Supporto per aste chiuse e miglioramenti UI
- Aggiornamento alla versione Microsoft.EntityFrameworkCore.Sqlite 8.0.0. - Aggiornamento alla versione Microsoft.Windows.SDK.BuildTools 10.0.26100.6584. - Migliorata l'interfaccia per l'inserimento di più URL/ID di aste. - Aggiunti pulsanti per "Aste Chiuse" e "Esporta" in MainWindow. - Creata finestra "Aste Chiuse" per visualizzare e gestire aste chiuse. - Implementato scraper per estrarre dati da aste chiuse. - Aggiunto supporto per esportazione dati in CSV, JSON e XML. - Introdotto contesto Entity Framework per statistiche delle aste. - Aggiunto servizio per calcolo e gestione delle statistiche. - Gestite preferenze di esportazione con salvataggio in file JSON.
This commit is contained in:
@@ -1,28 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Window x:Class="AutoBidder.Dialogs.AddAuctionSimpleDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Aggiungi Asta" Height="220" Width="600"
|
||||
Background="#0a0a0a" Foreground="#FFFFFF"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="pack://application:,,,/Icon/favicon.ico"
|
||||
ResizeMode="NoResize">
|
||||
<Border Background="#1a1a1a" CornerRadius="8" Padding="16" Margin="8">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<TextBlock Text="Inserire URL dell'asta" Foreground="#CCCCCC" FontSize="14" Margin="0,0,0,10" />
|
||||
<TextBox x:Name="AuctionUrlBox" Grid.Row="1" MinWidth="320" Margin="0,0,0,8"
|
||||
Background="#181818" Foreground="#00CCFF" BorderBrush="#444" BorderThickness="1"
|
||||
Padding="8" FontSize="13" ToolTip="Inserisci l'URL completo dell'asta" />
|
||||
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<Button x:Name="OkButton" Content="OK" Width="110" Margin="6" Padding="10,8"
|
||||
Style="{StaticResource SmallButtonStyle}" Background="#00CC66" Foreground="White" Click="OkButton_Click" />
|
||||
<Button x:Name="CancelButton" Content="Annulla" Width="110" Margin="6" Padding="10,8"
|
||||
Style="{StaticResource SmallButtonStyle}" Background="#666" Foreground="White" Click="CancelButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Aggiungi Asta" Height="320" Width="700"
|
||||
Background="#0a0a0a" Foreground="#FFFFFF"
|
||||
WindowStartupLocation="CenterOwner"
|
||||
Icon="pack://application:,,,/Icon/favicon.ico"
|
||||
ResizeMode="NoResize">
|
||||
<Border Background="#1a1a1a" CornerRadius="8" Padding="16" Margin="8">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Text="Inserire URL dell'asta (uno o più)" Foreground="#CCCCCC" FontSize="14" Margin="0,0,0,6" />
|
||||
<TextBlock Grid.Row="1" Text="Puoi aggiungere più link separandoli con 'a capo', 'spazio' o ';'" Foreground="#999999" FontSize="12" Margin="0,0,0,10" />
|
||||
|
||||
<TextBox x:Name="AuctionUrlBox" Grid.Row="2" MinWidth="560" Margin="0,0,0,8"
|
||||
Background="#181818" Foreground="#00CCFF" BorderBrush="#444" BorderThickness="1"
|
||||
Padding="8" FontSize="13" ToolTip="Inserisci uno o più URL/ID dell'asta. Separali con a capo, spazio o ';'"
|
||||
AcceptsReturn="True" TextWrapping="Wrap" VerticalScrollBarVisibility="Auto" Height="160" />
|
||||
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,8,0,0">
|
||||
<Button x:Name="OkButton" Content="OK" Width="110" Margin="6" Padding="10,8"
|
||||
Style="{StaticResource SmallButtonStyle}" Background="#00CC66" Foreground="White" Click="OkButton_Click" />
|
||||
<Button x:Name="CancelButton" Content="Annulla" Width="110" Margin="6" Padding="10,8"
|
||||
Style="{StaticResource SmallButtonStyle}" Background="#666" Foreground="White" Click="CancelButton_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Window>
|
||||
@@ -13,13 +13,15 @@ namespace AutoBidder.Dialogs
|
||||
|
||||
private void OkButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var text = AuctionUrlBox.Text?.Trim() ?? string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(text) || !text.StartsWith("http"))
|
||||
var text = AuctionUrlBox.Text ?? string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
MessageBox.Show("Inserisci un URL valido dell'asta.", "Errore", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
MessageBox.Show("Inserisci almeno un URL o ID dell'asta.", "Errore", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
return;
|
||||
}
|
||||
AuctionId = text;
|
||||
|
||||
// Return the raw text (may contain multiple entries); caller will parse it
|
||||
AuctionId = text.Trim();
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
89
Mimante/Dialogs/ClosedAuctionsWindow.xaml
Normal file
89
Mimante/Dialogs/ClosedAuctionsWindow.xaml
Normal file
@@ -0,0 +1,89 @@
|
||||
<Window x:Class="AutoBidder.Dialogs.ClosedAuctionsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Title="Aste Chiuse - Estrazione" Height="600" Width="1000"
|
||||
Background="#0a0a0a" Foreground="#FFFFFF" WindowStartupLocation="CenterOwner">
|
||||
<Grid Margin="10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="8" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border Background="#1a1a1a" Padding="10" CornerRadius="6" Grid.Row="0" BorderBrush="#333333" BorderThickness="1">
|
||||
<DockPanel>
|
||||
<TextBlock Text="Estrazione Aste Chiuse" FontSize="16" FontWeight="Bold" Foreground="#00CC66" VerticalAlignment="Center" DockPanel.Dock="Left" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" DockPanel.Dock="Right">
|
||||
<Button x:Name="StartExtractButton" Content="Avvia Estrazione" Click="StartExtractButton_Click" Width="140" Height="36" Margin="8,0,0,0" Background="#00CC66" Style="{StaticResource SmallButtonStyle}"/>
|
||||
<Button x:Name="ExportStatsButton" Content="Esporta Statistiche" Click="ExportStatsButton_Click" Width="160" Height="36" Margin="8,0,0,0" Background="#8B5CF6" Style="{StaticResource SmallButtonStyle}"/>
|
||||
<Button x:Name="CloseButton" Content="Chiudi" Click="CloseButton_Click" Width="80" Height="36" Margin="8,0,0,0" Background="#666" Style="{StaticResource SmallButtonStyle}"/>
|
||||
</StackPanel>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="2" Background="#1a1a1a" Padding="8" CornerRadius="6" BorderBrush="#333333" BorderThickness="1">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="2*" />
|
||||
<ColumnDefinition Width="8" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Products grid: styled like MainWindow -->
|
||||
<DataGrid x:Name="ProductsGrid" AutoGenerateColumns="False" Grid.Column="0" Background="#1a1a1a" Foreground="#FFFFFF" BorderBrush="#333333" BorderThickness="1"
|
||||
RowBackground="#1a1a1a" AlternatingRowBackground="#222222" GridLinesVisibility="Horizontal" HorizontalGridLinesBrush="#333333"
|
||||
IsReadOnly="True" SelectionUnit="CellOrRowHeader" SelectionMode="Extended" ClipboardCopyMode="IncludeHeader" CanUserAddRows="False" CanUserDeleteRows="False">
|
||||
<DataGrid.Resources>
|
||||
<Style TargetType="DataGridColumnHeader">
|
||||
<Setter Property="Background" Value="#2a2a2a" />
|
||||
<Setter Property="Foreground" Value="#FFFFFF" />
|
||||
<Setter Property="FontWeight" Value="Bold" />
|
||||
<Setter Property="Padding" Value="10,8" />
|
||||
<Setter Property="BorderThickness" Value="0,0,0,2" />
|
||||
<Setter Property="BorderBrush" Value="#00CC66" />
|
||||
</Style>
|
||||
<Style TargetType="DataGridRow">
|
||||
<Setter Property="Height" Value="36" />
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsSelected" Value="True">
|
||||
<Setter Property="Background" Value="#0099FF" />
|
||||
<Setter Property="Foreground" Value="White" />
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
<Style TargetType="DataGridCell">
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="10,6" />
|
||||
</Style>
|
||||
</DataGrid.Resources>
|
||||
|
||||
<DataGrid.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="ApplicationCommands.Copy" Header="Copia" />
|
||||
</ContextMenu>
|
||||
</DataGrid.ContextMenu>
|
||||
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Asta URL" Binding="{Binding AuctionUrl}" Width="2*" />
|
||||
<DataGridTextColumn Header="Nome" Binding="{Binding ProductName}" Width="3*"/>
|
||||
<DataGridTextColumn Header="Prezzo" Binding="{Binding FinalPrice}" Width="80"/>
|
||||
<DataGridTextColumn Header="Vincitore" Binding="{Binding Winner}" Width="120"/>
|
||||
<DataGridTextColumn Header="Puntate Usate" Binding="{Binding BidsUsed}" Width="100"/>
|
||||
<DataGridTextColumn Header="Scraped At" Binding="{Binding ScrapedAt}" Width="140"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="8" />
|
||||
|
||||
<!-- Log area styled like main window log -->
|
||||
<Border Grid.Column="2" Background="#0f0f0f" Padding="8" CornerRadius="6" BorderBrush="#333333" BorderThickness="1">
|
||||
<DockPanel>
|
||||
<TextBlock Text="Log Operazioni" FontWeight="Bold" Foreground="#00CC66" DockPanel.Dock="Top" Margin="0,0,0,8" />
|
||||
<RichTextBox x:Name="ExtractLogBox" IsReadOnly="True" VerticalScrollBarVisibility="Auto" FontFamily="Consolas" FontSize="11" Background="#0f0f0f" Foreground="#CCC" BorderBrush="#333333" BorderThickness="1" />
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
140
Mimante/Dialogs/ClosedAuctionsWindow.xaml.cs
Normal file
140
Mimante/Dialogs/ClosedAuctionsWindow.xaml.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Documents;
|
||||
using Microsoft.Win32;
|
||||
using AutoBidder.Models;
|
||||
using AutoBidder.Services;
|
||||
using AutoBidder.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace AutoBidder.Dialogs
|
||||
{
|
||||
public partial class ClosedAuctionsWindow : Window
|
||||
{
|
||||
private ObservableCollection<ClosedAuctionRecord> _products = new();
|
||||
|
||||
private bool _isRunning = false;
|
||||
|
||||
// StatsService using local DB. Create context with default sqlite file in app folder
|
||||
private readonly StatsService _statsService;
|
||||
|
||||
public ClosedAuctionsWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
ProductsGrid.ItemsSource = _products;
|
||||
|
||||
var optionsBuilder = new DbContextOptionsBuilder<StatisticsContext>();
|
||||
var dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "stats.db");
|
||||
optionsBuilder.UseSqlite($"Data Source={dbPath}");
|
||||
var ctx = new StatisticsContext(optionsBuilder.Options);
|
||||
_statsService = new StatsService(ctx);
|
||||
|
||||
Log("Finestra pronta");
|
||||
}
|
||||
|
||||
private void Log(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
var para = new Paragraph(new Run($"{DateTime.Now:HH:mm} - {message}"));
|
||||
ExtractLogBox.Document.Blocks.Add(para);
|
||||
// keep size manageable
|
||||
while (ExtractLogBox.Document.Blocks.Count > 500)
|
||||
ExtractLogBox.Document.Blocks.Remove(ExtractLogBox.Document.Blocks.FirstBlock);
|
||||
ExtractLogBox.ScrollToEnd();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private async void StartExtractButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (_isRunning)
|
||||
{
|
||||
Log("Estrazione già in corso");
|
||||
return;
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
StartExtractButton.IsEnabled = false;
|
||||
Log("Avvio procedura di estrazione da closed_auctions.php...");
|
||||
|
||||
try
|
||||
{
|
||||
var scraper = new ClosedAuctionsScraper(null, _statsService, Log);
|
||||
var closedUrl = "https://it.bidoo.com/closed_auctions.php";
|
||||
|
||||
Log($"Scarico: {closedUrl}");
|
||||
|
||||
int count = 0;
|
||||
await foreach (var rec in scraper.ScrapeYieldAsync(closedUrl))
|
||||
{
|
||||
// Filter out records without bids info (user requested)
|
||||
if (!rec.BidsUsed.HasValue)
|
||||
{
|
||||
Log($"Scartata asta (mancano puntate): {rec.AuctionUrl} - '{rec.ProductName ?? "?"}'");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add and log incrementally so user sees progress
|
||||
_products.Add(rec);
|
||||
count++;
|
||||
Log($"[{count}] {rec.ProductName} | Prezzo: {(rec.FinalPrice.HasValue?rec.FinalPrice.Value.ToString("F2")+"€":"--")} | Vincitore: {rec.Winner ?? "--"} | Puntate: {rec.BidsUsed.Value} | URL: {rec.AuctionUrl}");
|
||||
}
|
||||
|
||||
Log($"Estrazione completata: {count} record aggiunti.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Estrattore: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isRunning = false;
|
||||
StartExtractButton.IsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
}
|
||||
|
||||
private async void ExportStatsButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log("Preparazione esportazione statistiche...");
|
||||
var stats = await _statsService.GetAllStatsAsync();
|
||||
if (stats == null || stats.Count == 0)
|
||||
{
|
||||
Log("Nessuna statistica disponibile da esportare.");
|
||||
MessageBox.Show(this, "Nessuna statistica disponibile.", "Esporta Statistiche", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var dlg = new SaveFileDialog() { Filter = "CSV files|*.csv|All files|*.*", FileName = "auction_stats.csv" };
|
||||
if (dlg.ShowDialog(this) != true) return;
|
||||
|
||||
using var sw = new StreamWriter(dlg.FileName, false, System.Text.Encoding.UTF8);
|
||||
sw.WriteLine("ProductKey,ProductName,TotalAuctions,AverageBidsUsed,AverageFinalPrice,LastSeen");
|
||||
foreach (var s in stats)
|
||||
{
|
||||
var line = $"\"{s.ProductKey}\",\"{s.ProductName}\",{s.TotalAuctions},{s.AverageBidsUsed:F2},{s.AverageFinalPrice:F2},{s.LastSeen:O}";
|
||||
sw.WriteLine(line);
|
||||
}
|
||||
|
||||
Log($"Statistiche esportate su: {dlg.FileName}");
|
||||
MessageBox.Show(this, "Statistiche esportate con successo.", "Esporta Statistiche", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[ERRORE] Esporta: {ex.Message}");
|
||||
MessageBox.Show(this, "Errore durante esportazione: " + ex.Message, "Esporta Statistiche", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user