Files
Mimante/Mimante/Data/PostgresStatsContext.cs
T
Alby96 61f0945db2 Supporto PostgreSQL, statistiche avanzate e nuova UI
Aggiornamento massivo: aggiunto backend PostgreSQL per statistiche aste con fallback SQLite, nuovi modelli e servizi, UI moderna con grafici interattivi, refactoring stato applicazione (ApplicationStateService), documentazione completa per deploy Docker/Unraid/Gitea, nuovi CSS e script JS per UX avanzata, template Unraid, test database, e workflow CI/CD estesi. Pronto per produzione e analisi avanzate.
2026-01-18 17:52:05 +01:00

212 lines
12 KiB
C#

using Microsoft.EntityFrameworkCore;
using AutoBidder.Models;
namespace AutoBidder.Data
{
/// <summary>
/// Context Entity Framework per PostgreSQL - Database Statistiche Aste
/// Gestisce aste concluse, metriche strategiche e analisi performance
/// </summary>
public class PostgresStatsContext : DbContext
{
public PostgresStatsContext(DbContextOptions<PostgresStatsContext> options)
: base(options)
{
}
// Tabelle principali
public DbSet<CompletedAuction> CompletedAuctions { get; set; }
public DbSet<BidderPerformance> BidderPerformances { get; set; }
public DbSet<ProductStatistic> ProductStatistics { get; set; }
public DbSet<DailyMetric> DailyMetrics { get; set; }
public DbSet<StrategicInsight> StrategicInsights { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// Configurazione CompletedAuction
modelBuilder.Entity<CompletedAuction>(entity =>
{
entity.ToTable("completed_auctions");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.AuctionId).HasColumnName("auction_id").IsRequired().HasMaxLength(100);
entity.Property(e => e.ProductName).HasColumnName("product_name").IsRequired().HasMaxLength(500);
entity.Property(e => e.FinalPrice).HasColumnName("final_price").HasColumnType("decimal(10,2)");
entity.Property(e => e.BuyNowPrice).HasColumnName("buy_now_price").HasColumnType("decimal(10,2)");
entity.Property(e => e.ShippingCost).HasColumnName("shipping_cost").HasColumnType("decimal(10,2)");
entity.Property(e => e.TotalBids).HasColumnName("total_bids");
entity.Property(e => e.MyBidsCount).HasColumnName("my_bids_count");
entity.Property(e => e.ResetCount).HasColumnName("reset_count");
entity.Property(e => e.Won).HasColumnName("won");
entity.Property(e => e.WinnerUsername).HasColumnName("winner_username").HasMaxLength(100);
entity.Property(e => e.CompletedAt).HasColumnName("completed_at");
entity.Property(e => e.DurationSeconds).HasColumnName("duration_seconds");
entity.Property(e => e.AverageLatency).HasColumnName("average_latency").HasColumnType("decimal(10,2)");
entity.Property(e => e.Savings).HasColumnName("savings").HasColumnType("decimal(10,2)");
entity.Property(e => e.TotalCost).HasColumnName("total_cost").HasColumnType("decimal(10,2)");
entity.Property(e => e.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("CURRENT_TIMESTAMP");
entity.HasIndex(e => e.AuctionId).HasDatabaseName("idx_auction_id");
entity.HasIndex(e => e.ProductName).HasDatabaseName("idx_product_name");
entity.HasIndex(e => e.CompletedAt).HasDatabaseName("idx_completed_at");
entity.HasIndex(e => e.Won).HasDatabaseName("idx_won");
});
// Configurazione BidderPerformance
modelBuilder.Entity<BidderPerformance>(entity =>
{
entity.ToTable("bidder_performances");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.Username).HasColumnName("username").IsRequired().HasMaxLength(100);
entity.Property(e => e.TotalAuctions).HasColumnName("total_auctions");
entity.Property(e => e.AuctionsWon).HasColumnName("auctions_won");
entity.Property(e => e.AuctionsLost).HasColumnName("auctions_lost");
entity.Property(e => e.TotalBidsPlaced).HasColumnName("total_bids_placed");
entity.Property(e => e.WinRate).HasColumnName("win_rate").HasColumnType("decimal(5,2)");
entity.Property(e => e.AverageBidsPerAuction).HasColumnName("average_bids_per_auction").HasColumnType("decimal(10,2)");
entity.Property(e => e.AverageCompetition).HasColumnName("average_competition").HasColumnType("decimal(10,2)");
entity.Property(e => e.IsAggressive).HasColumnName("is_aggressive");
entity.Property(e => e.LastSeenAt).HasColumnName("last_seen_at");
entity.Property(e => e.UpdatedAt).HasColumnName("updated_at").HasDefaultValueSql("CURRENT_TIMESTAMP");
entity.HasIndex(e => e.Username).IsUnique().HasDatabaseName("idx_username");
entity.HasIndex(e => e.WinRate).HasDatabaseName("idx_win_rate");
});
// Configurazione ProductStatistic
modelBuilder.Entity<ProductStatistic>(entity =>
{
entity.ToTable("product_statistics");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.ProductKey).HasColumnName("product_key").IsRequired().HasMaxLength(200);
entity.Property(e => e.ProductName).HasColumnName("product_name").IsRequired().HasMaxLength(500);
entity.Property(e => e.TotalAuctions).HasColumnName("total_auctions");
entity.Property(e => e.AverageWinningBids).HasColumnName("average_winning_bids").HasColumnType("decimal(10,2)");
entity.Property(e => e.AverageFinalPrice).HasColumnName("average_final_price").HasColumnType("decimal(10,2)");
entity.Property(e => e.AverageResets).HasColumnName("average_resets").HasColumnType("decimal(10,2)");
entity.Property(e => e.MinBidsSeen).HasColumnName("min_bids_seen");
entity.Property(e => e.MaxBidsSeen).HasColumnName("max_bids_seen");
entity.Property(e => e.RecommendedMaxBids).HasColumnName("recommended_max_bids");
entity.Property(e => e.RecommendedMaxPrice).HasColumnName("recommended_max_price").HasColumnType("decimal(10,2)");
entity.Property(e => e.CompetitionLevel).HasColumnName("competition_level").HasMaxLength(20);
entity.Property(e => e.LastUpdated).HasColumnName("last_updated").HasDefaultValueSql("CURRENT_TIMESTAMP");
entity.HasIndex(e => e.ProductKey).IsUnique().HasDatabaseName("idx_product_key");
entity.HasIndex(e => e.ProductName).HasDatabaseName("idx_product_name_stats");
});
// Configurazione DailyMetric
modelBuilder.Entity<DailyMetric>(entity =>
{
entity.ToTable("daily_metrics");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.Date).HasColumnName("date").HasColumnType("date");
entity.Property(e => e.TotalBidsUsed).HasColumnName("total_bids_used");
entity.Property(e => e.MoneySpent).HasColumnName("money_spent").HasColumnType("decimal(10,2)");
entity.Property(e => e.AuctionsWon).HasColumnName("auctions_won");
entity.Property(e => e.AuctionsLost).HasColumnName("auctions_lost");
entity.Property(e => e.TotalSavings).HasColumnName("total_savings").HasColumnType("decimal(10,2)");
entity.Property(e => e.AverageLatency).HasColumnName("average_latency").HasColumnType("decimal(10,2)");
entity.Property(e => e.WinRate).HasColumnName("win_rate").HasColumnType("decimal(5,2)");
entity.Property(e => e.ROI).HasColumnName("roi").HasColumnType("decimal(10,2)");
entity.HasIndex(e => e.Date).IsUnique().HasDatabaseName("idx_date");
});
// Configurazione StrategicInsight
modelBuilder.Entity<StrategicInsight>(entity =>
{
entity.ToTable("strategic_insights");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).HasColumnName("id");
entity.Property(e => e.InsightType).HasColumnName("insight_type").IsRequired().HasMaxLength(50);
entity.Property(e => e.ProductKey).HasColumnName("product_key").HasMaxLength(200);
entity.Property(e => e.RecommendedAction).HasColumnName("recommended_action").IsRequired();
entity.Property(e => e.ConfidenceLevel).HasColumnName("confidence_level").HasColumnType("decimal(5,2)");
entity.Property(e => e.DataPoints).HasColumnName("data_points");
entity.Property(e => e.Reasoning).HasColumnName("reasoning");
entity.Property(e => e.CreatedAt).HasColumnName("created_at").HasDefaultValueSql("CURRENT_TIMESTAMP");
entity.Property(e => e.IsActive).HasColumnName("is_active").HasDefaultValue(true);
entity.HasIndex(e => e.InsightType).HasDatabaseName("idx_insight_type");
entity.HasIndex(e => e.ProductKey).HasDatabaseName("idx_product_key_insight");
entity.HasIndex(e => e.CreatedAt).HasDatabaseName("idx_created_at");
});
}
/// <summary>
/// Verifica e crea lo schema del database
/// </summary>
public async Task<bool> EnsureSchemaAsync()
{
try
{
// Verifica connessione
if (!await Database.CanConnectAsync())
{
Console.WriteLine("[PostgreSQL] Cannot connect to database");
return false;
}
// Crea schema se non esistono le tabelle (senza migrations)
var created = await Database.EnsureCreatedAsync();
if (created)
{
Console.WriteLine("[PostgreSQL] Schema created successfully");
}
else
{
Console.WriteLine("[PostgreSQL] Schema already exists");
}
// Verifica che tutte le tabelle esistano
var hasCompletedAuctions = await CompletedAuctions.AnyAsync();
Console.WriteLine($"[PostgreSQL] Database verified - {(hasCompletedAuctions ? "has data" : "empty")}");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"[PostgreSQL ERROR] Schema creation failed: {ex.Message}");
Console.WriteLine($"[PostgreSQL ERROR] Stack trace: {ex.StackTrace}");
return false;
}
}
/// <summary>
/// Verifica che tutte le tabelle richieste esistano
/// </summary>
public async Task<bool> ValidateSchemaAsync()
{
try
{
// Prova a contare le righe di ogni tabella (forza check esistenza)
await CompletedAuctions.CountAsync();
await BidderPerformances.CountAsync();
await ProductStatistics.CountAsync();
await DailyMetrics.CountAsync();
await StrategicInsights.CountAsync();
Console.WriteLine("[PostgreSQL] All tables validated successfully");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"[PostgreSQL ERROR] Schema validation failed: {ex.Message}");
return false;
}
}
}
}