fix: Resolve EF Core shadow property issue and increase migration timeout
This commit is contained in:
@@ -148,7 +148,7 @@ public class AppDbContext : DbContext
|
|||||||
mb.Entity<TraderCategoryPerformance>(e =>
|
mb.Entity<TraderCategoryPerformance>(e =>
|
||||||
{
|
{
|
||||||
e.HasKey(tcp => tcp.Id);
|
e.HasKey(tcp => tcp.Id);
|
||||||
e.HasOne(tcp => tcp.Trader).WithMany().HasForeignKey(tcp => tcp.TraderId).OnDelete(DeleteBehavior.Cascade);
|
e.HasOne(tcp => tcp.Trader).WithMany(t => t.CategoryPerformances).HasForeignKey(tcp => tcp.TraderId).OnDelete(DeleteBehavior.Cascade);
|
||||||
e.Property(tcp => tcp.Category).HasConversion<string>().HasMaxLength(64);
|
e.Property(tcp => tcp.Category).HasConversion<string>().HasMaxLength(64);
|
||||||
e.Property(tcp => tcp.TotalVolume).HasPrecision(18, 4);
|
e.Property(tcp => tcp.TotalVolume).HasPrecision(18, 4);
|
||||||
e.Property(tcp => tcp.TotalPnL).HasPrecision(18, 4);
|
e.Property(tcp => tcp.TotalPnL).HasPrecision(18, 4);
|
||||||
|
|||||||
@@ -53,7 +53,11 @@ public static class DependencyInjection
|
|||||||
if (dbDebug) Serilog.Log.Warning("🛠️ EF: Configuring AppDbContext. Target DB: '{Database}'", csBuilder.Database);
|
if (dbDebug) Serilog.Log.Warning("🛠️ EF: Configuring AppDbContext. Target DB: '{Database}'", csBuilder.Database);
|
||||||
|
|
||||||
options.UseMySql(csBuilder.ConnectionString, new MySqlServerVersion(new Version(8, 0, 31)),
|
options.UseMySql(csBuilder.ConnectionString, new MySqlServerVersion(new Version(8, 0, 31)),
|
||||||
mysql => mysql.EnableRetryOnFailure(3, TimeSpan.FromSeconds(10), null));
|
mysql =>
|
||||||
|
{
|
||||||
|
mysql.EnableRetryOnFailure(3, TimeSpan.FromSeconds(10), null);
|
||||||
|
mysql.CommandTimeout(600); // 10 minutes timeout for large migrations (e.g. ALTER TABLE Trades)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Repositories
|
// Repositories
|
||||||
|
|||||||
Generated
+906
@@ -0,0 +1,906 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Predictalytics.Infrastructure.Data;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Predictalytics.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(AppDbContext))]
|
||||||
|
[Migration("20260705121925_FixTraderCategoryMapping")]
|
||||||
|
partial class FixTraderCategoryMapping
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "8.0.11")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Alert", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsRead")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Message")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("varchar(4096)");
|
||||||
|
|
||||||
|
b.Property<int>("Platform")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Severity")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("varchar(512)");
|
||||||
|
|
||||||
|
b.Property<int?>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Type")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("CreatedAt");
|
||||||
|
|
||||||
|
b.HasIndex("TraderId");
|
||||||
|
|
||||||
|
b.ToTable("Alerts");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Event", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DbCreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("varchar(4096)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EndDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsClosed")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("Platform")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<long>("PlatformEventId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("Slug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("varchar(512)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("StartDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Tags")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)");
|
||||||
|
|
||||||
|
b.Property<string>("Title")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Platform", "PlatformEventId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Events");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Category")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)");
|
||||||
|
|
||||||
|
b.Property<string>("ConditionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("DbCreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(4096)
|
||||||
|
.HasColumnType("varchar(4096)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("EndDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("EventId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("ImageUrl")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsResolved")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastTradesUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Liquidity")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<string>("MarketSlug")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("varchar(512)");
|
||||||
|
|
||||||
|
b.Property<int>("Platform")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<long>("PlatformMarketId")
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
b.Property<string>("Question")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)");
|
||||||
|
|
||||||
|
b.Property<string>("QuestionId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("ResolutionOutcome")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("StartDate")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("Subcategory")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Volume")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Volume24h")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("EventId");
|
||||||
|
|
||||||
|
b.HasIndex("Platform", "PlatformMarketId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Markets");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketAnalytics", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("MarketId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("AverageTradeSize")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("BotActivityScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastCalculatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("UniqueTradersCount")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("MarketId");
|
||||||
|
|
||||||
|
b.ToTable("MarketAnalytics");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcome", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("CurrentPrice")
|
||||||
|
.HasPrecision(18, 8)
|
||||||
|
.HasColumnType("decimal(18,8)");
|
||||||
|
|
||||||
|
b.Property<string>("Label")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<int>("MarketId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("OutcomeIndex")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TokenId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TokenId");
|
||||||
|
|
||||||
|
b.HasIndex("MarketId", "OutcomeIndex")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("MarketOutcomes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcomePriceSnapshot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<int>("MarketOutcomeId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("Price")
|
||||||
|
.HasPrecision(10, 6)
|
||||||
|
.HasColumnType("decimal(10,6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Timestamp")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MarketOutcomeId", "Timestamp");
|
||||||
|
|
||||||
|
b.ToTable("MarketOutcomePriceSnapshots");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.PlatformConfig", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("BaseUrl")
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsActive")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)");
|
||||||
|
|
||||||
|
b.Property<string>("SettingsJson")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PlatformConfigs");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Trade", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("Amount")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<string>("AssetId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(80)
|
||||||
|
.HasColumnType("varchar(80)");
|
||||||
|
|
||||||
|
b.Property<int?>("DbMarketId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExecutedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsContextEnriched")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("MarketId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(66)
|
||||||
|
.HasColumnType("varchar(66)");
|
||||||
|
|
||||||
|
b.Property<int?>("MarketOutcomeId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Outcome")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)");
|
||||||
|
|
||||||
|
b.Property<int>("Platform")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("PlatformTradeId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<decimal?>("PostTradePrice1m")
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal?>("PreTradePrice1m")
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("Price")
|
||||||
|
.HasPrecision(18, 6)
|
||||||
|
.HasColumnType("decimal(18,6)");
|
||||||
|
|
||||||
|
b.Property<int>("Side")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("Size")
|
||||||
|
.HasPrecision(14, 6)
|
||||||
|
.HasColumnType("decimal(14,6)");
|
||||||
|
|
||||||
|
b.Property<int>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("TransactionHash")
|
||||||
|
.HasMaxLength(66)
|
||||||
|
.HasColumnType("varchar(66)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("AssetId");
|
||||||
|
|
||||||
|
b.HasIndex("DbMarketId");
|
||||||
|
|
||||||
|
b.HasIndex("ExecutedAt");
|
||||||
|
|
||||||
|
b.HasIndex("MarketOutcomeId");
|
||||||
|
|
||||||
|
b.HasIndex("TraderId");
|
||||||
|
|
||||||
|
b.HasIndex("Platform", "PlatformTradeId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Trades");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Trader", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AiStrategySummary")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("AiStrategyUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAutoDiscovered")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsInitialImportComplete")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<bool>("IsSuspectedBot")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastApiErrorAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPolledAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastTradesUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int?>("ManualPriorityOverride")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("Platform")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<string>("PlatformUserId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)");
|
||||||
|
|
||||||
|
b.Property<int>("Strategy")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("Tier")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalPnl")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<int>("TotalTrades")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("WinRate")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Platform", "PlatformUserId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Traders");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderAnalytics", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastCalculatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<decimal>("OverallPnL")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("OverallWinRate")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("PnL24h")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("PnL30d")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("PnL7d")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("WinRate24h")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("WinRate30d")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("WinRate7d")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.HasKey("TraderId");
|
||||||
|
|
||||||
|
b.ToTable("TraderAnalytics");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("Category")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalPnL")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<int>("TotalTrades")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("TotalVolume")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<int>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<int>("WinningTrades")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TraderId", "Category")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("TraderCategoryPerformances");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("AvgCost")
|
||||||
|
.HasPrecision(10, 6)
|
||||||
|
.HasColumnType("decimal(10,6)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastUpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<int>("MarketOutcomeId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("RealizedPnl")
|
||||||
|
.HasPrecision(18, 4)
|
||||||
|
.HasColumnType("decimal(18,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("SharesHeld")
|
||||||
|
.HasPrecision(14, 6)
|
||||||
|
.HasColumnType("decimal(14,6)");
|
||||||
|
|
||||||
|
b.Property<int>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("MarketOutcomeId");
|
||||||
|
|
||||||
|
b.HasIndex("TraderId", "MarketOutcomeId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("TraderPositions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderScore", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<decimal>("ActivityScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CalculatedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<decimal>("CombinedScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("CopytradingScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<decimal>("QualityScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<int>("Rank")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("TimingScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.Property<int>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.Property<decimal>("VolumeScore")
|
||||||
|
.HasPrecision(8, 4)
|
||||||
|
.HasColumnType("decimal(8,4)");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TraderId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("TraderScores");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.WatchlistEntry", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("AddedAt")
|
||||||
|
.HasColumnType("datetime(6)");
|
||||||
|
|
||||||
|
b.Property<bool>("AlertsEnabled")
|
||||||
|
.HasColumnType("tinyint(1)");
|
||||||
|
|
||||||
|
b.Property<string>("Label")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(256)
|
||||||
|
.HasColumnType("varchar(256)");
|
||||||
|
|
||||||
|
b.Property<string>("Notes")
|
||||||
|
.HasColumnType("longtext");
|
||||||
|
|
||||||
|
b.Property<int>("TraderId")
|
||||||
|
.HasColumnType("int");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("TraderId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("WatchlistEntries");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Alert", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Event", "Event")
|
||||||
|
.WithMany("Markets")
|
||||||
|
.HasForeignKey("EventId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Event");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketAnalytics", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Market", "Market")
|
||||||
|
.WithOne("Analytics")
|
||||||
|
.HasForeignKey("Predictalytics.Domain.Entities.MarketAnalytics", "MarketId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Market");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcome", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Market", "Market")
|
||||||
|
.WithMany("Outcomes")
|
||||||
|
.HasForeignKey("MarketId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Market");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.MarketOutcomePriceSnapshot", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("MarketOutcomeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("MarketOutcome");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Trade", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Market", "DbMarket")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("DbMarketId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("MarketOutcomeId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithMany("Trades")
|
||||||
|
.HasForeignKey("TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("DbMarket");
|
||||||
|
|
||||||
|
b.Navigation("MarketOutcome");
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderAnalytics", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithOne("Analytics")
|
||||||
|
.HasForeignKey("Predictalytics.Domain.Entities.TraderAnalytics", "TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithMany("CategoryPerformances")
|
||||||
|
.HasForeignKey("TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderPosition", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.MarketOutcome", "MarketOutcome")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("MarketOutcomeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithMany("Positions")
|
||||||
|
.HasForeignKey("TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("MarketOutcome");
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderScore", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithOne("CurrentScore")
|
||||||
|
.HasForeignKey("Predictalytics.Domain.Entities.TraderScore", "TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.WatchlistEntry", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
|
.WithMany("WatchlistEntries")
|
||||||
|
.HasForeignKey("TraderId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("Trader");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Event", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Markets");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Market", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Analytics");
|
||||||
|
|
||||||
|
b.Navigation("Outcomes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Predictalytics.Domain.Entities.Trader", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Analytics");
|
||||||
|
|
||||||
|
b.Navigation("CategoryPerformances");
|
||||||
|
|
||||||
|
b.Navigation("CurrentScore");
|
||||||
|
|
||||||
|
b.Navigation("Positions");
|
||||||
|
|
||||||
|
b.Navigation("Trades");
|
||||||
|
|
||||||
|
b.Navigation("WatchlistEntries");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace Predictalytics.Infrastructure.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class FixTraderCategoryMapping : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -822,7 +822,7 @@ namespace Predictalytics.Infrastructure.Migrations
|
|||||||
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
modelBuilder.Entity("Predictalytics.Domain.Entities.TraderCategoryPerformance", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
b.HasOne("Predictalytics.Domain.Entities.Trader", "Trader")
|
||||||
.WithMany()
|
.WithMany("CategoryPerformances")
|
||||||
.HasForeignKey("TraderId")
|
.HasForeignKey("TraderId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
.IsRequired();
|
.IsRequired();
|
||||||
@@ -887,6 +887,8 @@ namespace Predictalytics.Infrastructure.Migrations
|
|||||||
{
|
{
|
||||||
b.Navigation("Analytics");
|
b.Navigation("Analytics");
|
||||||
|
|
||||||
|
b.Navigation("CategoryPerformances");
|
||||||
|
|
||||||
b.Navigation("CurrentScore");
|
b.Navigation("CurrentScore");
|
||||||
|
|
||||||
b.Navigation("Positions");
|
b.Navigation("Positions");
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Predictalytics.Infrastructure.Data;
|
||||||
|
using Predictalytics.Infrastructure.Data.Repositories;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
var dbVersion = new MySqlServerVersion(new Version(8, 0, 36));
|
||||||
|
services.AddDbContext<AppDbContext>(o => o.UseMySql("Server=localhost;Database=test;User=root;Password=test", dbVersion));
|
||||||
|
services.AddScoped<ITraderRepository, TraderRepository>();
|
||||||
|
var sp = services.BuildServiceProvider();
|
||||||
|
using var scope = sp.CreateScope();
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
|
var priorityCutoff = DateTime.UtcNow.AddHours(-1);
|
||||||
|
var normalCutoff = DateTime.UtcNow.AddHours(-12);
|
||||||
|
|
||||||
|
var query = db.Traders
|
||||||
|
.Include(t => t.WatchlistEntries)
|
||||||
|
.Where(t => t.LastTradesUpdatedAt == null ||
|
||||||
|
(!t.IsInitialImportComplete) ||
|
||||||
|
((!t.IsAutoDiscovered || t.WatchlistEntries.Any()) && t.LastTradesUpdatedAt < priorityCutoff) ||
|
||||||
|
(t.IsAutoDiscovered && t.LastTradesUpdatedAt < normalCutoff))
|
||||||
|
.OrderBy(t => t.IsAutoDiscovered)
|
||||||
|
.ThenByDescending(t => t.WatchlistEntries.Any())
|
||||||
|
.ThenBy(t => t.IsInitialImportComplete)
|
||||||
|
.ThenBy(t => t.LastTradesUpdatedAt ?? DateTime.MinValue)
|
||||||
|
.Take(20);
|
||||||
|
|
||||||
|
Console.WriteLine(query.ToQueryString());
|
||||||
Reference in New Issue
Block a user