add csfloat api usage

This commit is contained in:
bob
2026-05-29 22:08:32 -05:00
parent b51f1d9f5f
commit d1752b1b07
37 changed files with 6095 additions and 22 deletions

View File

@@ -0,0 +1,43 @@
using BlueLaminate.EFCore.Entities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace BlueLaminate.EFCore.Configurations;
public class ListingConfiguration : IEntityTypeConfiguration<Listing>
{
public void Configure(EntityTypeBuilder<Listing> entity)
{
// CSFloat's listing id is the natural key; the incremental sweep upserts
// against it and must never create duplicates.
entity.HasIndex(e => e.CsFloatListingId).IsUnique();
entity.Property(e => e.Price).HasPrecision(18, 2);
// Full precision to match SkinInstance for exact fingerprint joins.
entity.Property(e => e.FloatValue).HasColumnType("numeric(20,18)");
// Store the enum as text so the DB is self-describing (matches the
// project's readable-data leaning over opaque ints).
entity.Property(e => e.Status).HasConversion<string>();
// The sweep filters/sorts by item identity and by what's still active.
entity.HasIndex(e => new { e.DefIndex, e.PaintIndex });
entity.HasIndex(e => e.Status);
// Best-effort catalogue link: a global sweep sees items we may not have,
// so the FK is optional and set null if the skin is later removed.
entity.HasOne(e => e.Skin)
.WithMany()
.HasForeignKey(e => e.SkinId)
.OnDelete(DeleteBehavior.SetNull);
// Listings roll up to the physical item they represent.
entity.HasOne(e => e.SkinInstance)
.WithMany(i => i.Listings)
.HasForeignKey(e => e.SkinInstanceId)
.OnDelete(DeleteBehavior.SetNull);
// Dupe analysis groups a fingerprint's listings by asset id.
entity.HasIndex(e => e.AssetId);
}
}

View File

@@ -21,6 +21,18 @@ public class SkinConfiguration : IEntityTypeConfiguration<Skin>
// Slug is the natural key the sync upserts against.
entity.HasIndex(e => e.Slug).IsUnique();
// Market listings join back to a skin by (def_index, paint_index). Unique
// among populated rows; filtered so the many catalogue rows that predate
// these columns (null) don't collide. Postgres treats nulls as distinct
// anyway, but the filter makes the intent explicit and the index smaller.
entity.HasIndex(e => new { e.DefIndex, e.PaintIndex })
.IsUnique()
.HasFilter("def_index IS NOT NULL AND paint_index IS NOT NULL");
// The catalogue sweep orders skins by when they were last swept (nulls
// first) to resume across capped runs; index that ordering.
entity.HasIndex(e => e.ListingsSweptAt);
entity.HasOne(e => e.Weapon)
.WithMany(w => w.Skins)
.HasForeignKey(e => e.WeaponId);

View File

@@ -8,19 +8,34 @@ public class SkinInstanceConfiguration : IEntityTypeConfiguration<SkinInstance>
{
public void Configure(EntityTypeBuilder<SkinInstance> entity)
{
entity.Property(e => e.FloatValue).HasColumnType("numeric(10,9)");
// Full precision so exact-match dupe detection isn't defeated by rounding.
// CSFloat returns deterministic ~17-digit floats; numeric(20,18) holds them.
entity.Property(e => e.FloatValue).HasColumnType("numeric(20,18)");
// Primary lookup key for trade fingerprinting.
entity.HasIndex(e => e.FloatValue);
entity.HasIndex(e => e.PaintSeed);
// The fingerprint that identifies a physical item. NOT unique: duped items
// legitimately share a fingerprint, and detecting that collision is the
// point. Indexed for fast fingerprint resolution during the sweep.
entity.HasIndex(e => new
{
e.SkinId,
e.FloatValue,
e.PaintSeed,
e.StatTrak,
e.Souvenir,
});
// Surfacing fresh dupes is a hot query.
entity.HasIndex(e => e.SuspectedDupe);
entity.HasOne(e => e.Skin)
.WithMany(s => s.Instances)
.HasForeignKey(e => e.SkinId);
// Condition is optional now (derived from float later); set null on delete
// rather than restrict so condition rows can change without blocking.
entity.HasOne(e => e.Condition)
.WithMany(c => c.Instances)
.HasForeignKey(e => e.ConditionId)
.OnDelete(DeleteBehavior.Restrict);
.OnDelete(DeleteBehavior.SetNull);
}
}

View File

@@ -29,6 +29,7 @@ public class SkinTrackerDbContext : DbContext
public DbSet<Trade> Trades => Set<Trade>();
public DbSet<TradeItem> TradeItems => Set<TradeItem>();
public DbSet<PriceHistory> PriceHistories => Set<PriceHistory>();
public DbSet<Listing> Listings => Set<Listing>();
/// <summary>The PostgreSQL schema that owns all of this context's tables.</summary>
public const string Schema = "skintracker";
@@ -48,5 +49,6 @@ public class SkinTrackerDbContext : DbContext
modelBuilder.ApplyConfiguration(new TradeConfiguration());
modelBuilder.ApplyConfiguration(new TradeItemConfiguration());
modelBuilder.ApplyConfiguration(new PriceHistoryConfiguration());
modelBuilder.ApplyConfiguration(new ListingConfiguration());
}
}

View File

@@ -0,0 +1,86 @@
namespace BlueLaminate.EFCore.Entities;
/// <summary>Lifecycle of a CSFloat listing as observed across sweeps.</summary>
public enum ListingStatus
{
/// <summary>Seen in the most recent sweep that covered it.</summary>
Active = 0,
/// <summary>
/// Previously seen, then absent from a sweep that should have covered it —
/// i.e. sold or delisted. The disappearance is the signal; we can't tell sold
/// from delisted with certainty, but <see cref="Listing.LastSeenAt"/> bounds when.
/// </summary>
Removed = 1,
}
/// <summary>
/// One active-market listing observed on CSFloat via the official
/// <c>GET /api/v1/listings</c> endpoint. Rows are keyed by CSFloat's own listing
/// id and soft-tracked across sweeps: <see cref="FirstSeenAt"/>/<see cref="LastSeenAt"/>
/// bound the observation window and <see cref="Status"/> flips to
/// <see cref="ListingStatus.Removed"/> when a once-seen listing stops appearing,
/// which approximates a sale/delisting.
///
/// A global sweep returns items that may not be in our catalogue, so
/// <see cref="SkinId"/> is a best-effort nullable link (resolved by
/// def_index + paint_index); the listing stands on its own without it.
/// </summary>
public class Listing
{
public int Id { get; set; }
/// <summary>CSFloat's listing id (a snowflake string). Natural key for dedup.</summary>
public string CsFloatListingId { get; set; } = null!;
/// <summary>"buy_now" or "auction".</summary>
public string Type { get; set; } = null!;
/// <summary>Asking price in USD.</summary>
public decimal Price { get; set; }
/// <summary>When CSFloat says the listing was created.</summary>
public DateTimeOffset ListedAt { get; set; }
// Item identity. Stored directly (not only via the Skin link) so listings for
// items outside our catalogue are still fully described.
public int DefIndex { get; set; }
public int PaintIndex { get; set; }
public string MarketHashName { get; set; } = null!;
public string? WearName { get; set; }
public decimal FloatValue { get; set; }
public int PaintSeed { get; set; }
public bool IsStatTrak { get; set; }
public bool IsSouvenir { get; set; }
public int StickerCount { get; set; }
public string? SellerSteamId { get; set; }
public string? InspectLink { get; set; }
/// <summary>
/// Steam asset id of the listed copy. Changes on trade, so not a stable
/// identity — but the discriminator that distinguishes duped copies which
/// otherwise share an identical fingerprint.
/// </summary>
public string? AssetId { get; set; }
/// <summary>Best-effort catalogue link, resolved by def_index + paint_index. Null if unmatched.</summary>
public int? SkinId { get; set; }
public Skin? Skin { get; set; }
/// <summary>
/// The physical item (by fingerprint) this listing is for. Many listings over
/// time roll up to one instance, forming its market-movement history. Nullable
/// because catalogue-less items can't be fingerprinted to a known skin.
/// </summary>
public int? SkinInstanceId { get; set; }
public SkinInstance? SkinInstance { get; set; }
// Soft-tracking across sweeps.
public DateTimeOffset FirstSeenAt { get; set; }
public DateTimeOffset LastSeenAt { get; set; }
public ListingStatus Status { get; set; }
/// <summary>When the listing was marked Removed (absent from a sweep). Null while Active.</summary>
public DateTimeOffset? RemovedAt { get; set; }
}

View File

@@ -9,6 +9,19 @@ public class Skin
/// <summary>Stable id from the CSGO-API catalogue, e.g. "skin-e757fd7191f9". The natural key.</summary>
public string Slug { get; set; } = null!;
// CSFloat/CS item indexes, sourced from the static catalogue (weapon.weapon_id
// and paint_index). Together they identify a skin on CSFloat and let market
// listings join back to this catalogue row. Nullable until a sync populates
// them, since older catalogue rows predate these columns.
public int? DefIndex { get; set; }
public int? PaintIndex { get; set; }
// When the catalogue-driven listing sweep last fully covered this skin. The
// sweep processes least-recently-swept skins first (nulls = never swept), so
// capped runs chain across the whole catalogue and the stalest data refreshes
// first. Null until the first sweep reaches this skin.
public DateTimeOffset? ListingsSweptAt { get; set; }
public string Name { get; set; } = null!;
public string Rarity { get; set; } = null!;
public string? Description { get; set; }

View File

@@ -1,20 +1,50 @@
namespace BlueLaminate.EFCore.Entities;
/// <summary>
/// One physical CS2 item, identified by its fingerprint
/// (Skin + FloatValue + PaintSeed + StatTrak + Souvenir) rather than its Steam
/// asset id, which changes on every trade. Decoupled from Steam inventories on
/// purpose: an instance exists from market observation alone, and the optional
/// <see cref="InventoryItem"/> bridge ties it to a <c>SteamUser</c> only once we
/// crawl inventories.
///
/// Duping note: a duplicated item is a byte-for-byte copy with an identical
/// fingerprint, so a fingerprint is NOT guaranteed unique to one physical item.
/// We treat the fingerprint as the item, and flag <see cref="SuspectedDupe"/>
/// when the same fingerprint is seen live under two or more different asset ids
/// at once (see the sweep's dupe detection).
/// </summary>
public class SkinInstance
{
public int Id { get; set; }
public int SkinId { get; set; }
public Skin Skin { get; set; } = null!;
public int ConditionId { get; set; }
public SkinCondition Condition { get; set; } = null!;
// FloatValue + PaintSeed form a stable fingerprint across trades; the Steam
// asset_id changes on every trade but these do not.
// Nullable: market observation gives a float but not a derived wear bucket.
// Condition can be backfilled later from the float without blocking ingest.
public int? ConditionId { get; set; }
public SkinCondition? Condition { get; set; }
// The fingerprint. FloatValue is stored at full precision (see config) so
// that exact-match dupe detection isn't fooled by rounding.
public decimal FloatValue { get; set; }
public string PaintSeed { get; set; } = null!;
public bool StatTrak { get; set; }
public bool Souvenir { get; set; }
public DateTimeOffset FirstSeenAt { get; set; }
public DateTimeOffset LastSeenAt { get; set; }
/// <summary>
/// True once this fingerprint was observed live under 2+ distinct asset ids
/// simultaneously — the signature of duplication.
/// </summary>
public bool SuspectedDupe { get; set; }
/// <summary>When the dupe condition was first detected. Null until then.</summary>
public DateTimeOffset? DupeFirstSeenAt { get; set; }
/// <summary>Every market listing observed for this physical item over time.</summary>
public ICollection<Listing> Listings { get; set; } = new List<Listing>();
public ICollection<InventoryItem> InventoryItems { get; set; } = new List<InventoryItem>();
}

View File

@@ -0,0 +1,829 @@
// <auto-generated />
using System;
using BlueLaminate.EFCore.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BlueLaminate.EFCore.Migrations
{
[DbContext(typeof(SkinTrackerDbContext))]
[Migration("20260530014903_AddListingsAndSkinIndexes")]
partial class AddListingsAndSkinIndexes
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("skintracker")
.HasAnnotation("ProductVersion", "10.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Collection", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_collections");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_collections_slug");
b.ToTable("collections", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("AcquiredAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("acquired_at");
b.Property<string>("AssetId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("asset_id");
b.Property<int>("SkinInstanceId")
.HasColumnType("integer")
.HasColumnName("skin_instance_id");
b.Property<int>("UserId")
.HasColumnType("integer")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("pk_inventory_items");
b.HasIndex("AssetId")
.HasDatabaseName("ix_inventory_items_asset_id");
b.HasIndex("SkinInstanceId")
.HasDatabaseName("ix_inventory_items_skin_instance_id");
b.HasIndex("UserId")
.HasDatabaseName("ix_inventory_items_user_id");
b.ToTable("inventory_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("CsFloatListingId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("cs_float_listing_id");
b.Property<int>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_value");
b.Property<string>("InspectLink")
.HasColumnType("text")
.HasColumnName("inspect_link");
b.Property<bool>("IsSouvenir")
.HasColumnType("boolean")
.HasColumnName("is_souvenir");
b.Property<bool>("IsStatTrak")
.HasColumnType("boolean")
.HasColumnName("is_stat_trak");
b.Property<DateTimeOffset>("LastSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_at");
b.Property<DateTimeOffset>("ListedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listed_at");
b.Property<string>("MarketHashName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("market_hash_name");
b.Property<int>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<int>("PaintSeed")
.HasColumnType("integer")
.HasColumnName("paint_seed");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset?>("RemovedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("removed_at");
b.Property<string>("SellerSteamId")
.HasColumnType("text")
.HasColumnName("seller_steam_id");
b.Property<int?>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("text")
.HasColumnName("status");
b.Property<int>("StickerCount")
.HasColumnType("integer")
.HasColumnName("sticker_count");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.Property<string>("WearName")
.HasColumnType("text")
.HasColumnName("wear_name");
b.HasKey("Id")
.HasName("pk_listings");
b.HasIndex("CsFloatListingId")
.IsUnique()
.HasDatabaseName("ix_listings_cs_float_listing_id");
b.HasIndex("SkinId")
.HasDatabaseName("ix_listings_skin_id");
b.HasIndex("Status")
.HasDatabaseName("ix_listings_status");
b.HasIndex("DefIndex", "PaintIndex")
.HasDatabaseName("ix_listings_def_index_paint_index");
b.ToTable("listings", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("text")
.HasColumnName("currency");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset>("RecordedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("recorded_at");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<string>("Source")
.IsRequired()
.HasColumnType("text")
.HasColumnName("source");
b.HasKey("Id")
.HasName("pk_price_histories");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_price_histories_condition_id");
b.HasIndex("SkinId", "ConditionId", "RecordedAt")
.HasDatabaseName("ix_price_histories_skin_id_condition_id_recorded_at");
b.ToTable("price_histories", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.ScrapeRun", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ItemCount")
.HasColumnType("integer")
.HasColumnName("item_count");
b.Property<DateTimeOffset>("RanAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("ran_at");
b.Property<string>("Source")
.IsRequired()
.HasColumnType("text")
.HasColumnName("source");
b.HasKey("Id")
.HasName("pk_scrape_runs");
b.HasIndex("Source", "RanAt")
.HasDatabaseName("ix_scrape_runs_source_ran_at");
b.ToTable("scrape_runs", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("description");
b.Property<decimal?>("FloatMax")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_max");
b.Property<decimal?>("FloatMin")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_min");
b.Property<string>("ImageUrl")
.HasColumnType("text")
.HasColumnName("image_url");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<int?>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<string>("Rarity")
.IsRequired()
.HasColumnType("text")
.HasColumnName("rarity");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
b.Property<bool>("SouvenirAvailable")
.HasColumnType("boolean")
.HasColumnName("souvenir_available");
b.Property<bool>("StatTrakAvailable")
.HasColumnType("boolean")
.HasColumnName("stat_trak_available");
b.Property<bool?>("TrueFloat")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("boolean")
.HasColumnName("true_float")
.HasComputedColumnSql("float_min = 0.0 AND float_max = 1.0", true);
b.Property<int>("WeaponId")
.HasColumnType("integer")
.HasColumnName("weapon_id");
b.HasKey("Id")
.HasName("pk_skins");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_skins_slug");
b.HasIndex("TrueFloat")
.HasDatabaseName("ix_skins_true_float");
b.HasIndex("WeaponId")
.HasDatabaseName("ix_skins_weapon_id");
b.HasIndex("DefIndex", "PaintIndex")
.IsUnique()
.HasDatabaseName("ix_skins_def_index_paint_index")
.HasFilter("def_index IS NOT NULL AND paint_index IS NOT NULL");
b.ToTable("skins", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Condition")
.IsRequired()
.HasColumnType("text")
.HasColumnName("condition");
b.Property<decimal>("MaxFloat")
.HasColumnType("numeric(10,9)")
.HasColumnName("max_float");
b.Property<decimal>("MinFloat")
.HasColumnType("numeric(10,9)")
.HasColumnName("min_float");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.HasKey("Id")
.HasName("pk_skin_conditions");
b.HasIndex("SkinId")
.HasDatabaseName("ix_skin_conditions_skin_id");
b.ToTable("skin_conditions", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_value");
b.Property<string>("PaintSeed")
.IsRequired()
.HasColumnType("text")
.HasColumnName("paint_seed");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<bool>("Souvenir")
.HasColumnType("boolean")
.HasColumnName("souvenir");
b.Property<bool>("StatTrak")
.HasColumnType("boolean")
.HasColumnName("stat_trak");
b.HasKey("Id")
.HasName("pk_skin_instances");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_skin_instances_condition_id");
b.HasIndex("FloatValue")
.HasDatabaseName("ix_skin_instances_float_value");
b.HasIndex("PaintSeed")
.HasDatabaseName("ix_skin_instances_paint_seed");
b.HasIndex("SkinId")
.HasDatabaseName("ix_skin_instances_skin_id");
b.ToTable("skin_instances", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("DisplayName")
.HasColumnType("text")
.HasColumnName("display_name");
b.Property<DateTimeOffset>("LastSyncedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_synced_at");
b.Property<string>("SteamId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("steam_id");
b.HasKey("Id")
.HasName("pk_steam_users");
b.HasIndex("SteamId")
.IsUnique()
.HasDatabaseName("ix_steam_users_steam_id");
b.ToTable("steam_users", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("FromUserId")
.HasColumnType("integer")
.HasColumnName("from_user_id");
b.Property<string>("SteamTradeId")
.HasColumnType("text")
.HasColumnName("steam_trade_id");
b.Property<int>("ToUserId")
.HasColumnType("integer")
.HasColumnName("to_user_id");
b.Property<DateTimeOffset>("TradedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("traded_at");
b.HasKey("Id")
.HasName("pk_trades");
b.HasIndex("FromUserId")
.HasDatabaseName("ix_trades_from_user_id");
b.HasIndex("ToUserId")
.HasDatabaseName("ix_trades_to_user_id");
b.ToTable("trades", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.TradeItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("InventoryItemId")
.HasColumnType("integer")
.HasColumnName("inventory_item_id");
b.Property<int>("TradeId")
.HasColumnType("integer")
.HasColumnName("trade_id");
b.HasKey("Id")
.HasName("pk_trade_items");
b.HasIndex("InventoryItemId")
.HasDatabaseName("ix_trade_items_inventory_item_id");
b.HasIndex("TradeId")
.HasDatabaseName("ix_trade_items_trade_id");
b.ToTable("trade_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Weapon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Team")
.IsRequired()
.HasColumnType("text")
.HasColumnName("team");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_weapons");
b.HasIndex("Name")
.IsUnique()
.HasDatabaseName("ix_weapons_name");
b.ToTable("weapons", "skintracker");
});
modelBuilder.Entity("CollectionSkin", b =>
{
b.Property<int>("CollectionsId")
.HasColumnType("integer")
.HasColumnName("collections_id");
b.Property<int>("SkinsId")
.HasColumnType("integer")
.HasColumnName("skins_id");
b.HasKey("CollectionsId", "SkinsId")
.HasName("pk_skin_collections");
b.HasIndex("SkinsId")
.HasDatabaseName("ix_skin_collections_skins_id");
b.ToTable("skin_collections", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinInstance", "SkinInstance")
.WithMany("InventoryItems")
.HasForeignKey("SkinInstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_inventory_items_skin_instances_skin_instance_id");
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "User")
.WithMany("InventoryItems")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_inventory_items_steam_users_user_id");
b.Navigation("SkinInstance");
b.Navigation("User");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany()
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_listings_skins_skin_id");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("PriceHistories")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_price_histories_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("PriceHistories")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_price_histories_skins_skin_id");
b.Navigation("Condition");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Weapon", "Weapon")
.WithMany("Skins")
.HasForeignKey("WeaponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skins_weapons_weapon_id");
b.Navigation("Weapon");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("Conditions")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_conditions_skins_skin_id");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("Instances")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_skin_instances_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("Instances")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_instances_skins_skin_id");
b.Navigation("Condition");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "FromUser")
.WithMany("TradesSent")
.HasForeignKey("FromUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_trades_steam_users_from_user_id");
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "ToUser")
.WithMany("TradesReceived")
.HasForeignKey("ToUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_trades_steam_users_to_user_id");
b.Navigation("FromUser");
b.Navigation("ToUser");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.TradeItem", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.InventoryItem", "InventoryItem")
.WithMany("TradeItems")
.HasForeignKey("InventoryItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_trade_items_inventory_items_inventory_item_id");
b.HasOne("BlueLaminate.EFCore.Entities.Trade", "Trade")
.WithMany("TradeItems")
.HasForeignKey("TradeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_trade_items_trades_trade_id");
b.Navigation("InventoryItem");
b.Navigation("Trade");
});
modelBuilder.Entity("CollectionSkin", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Collection", null)
.WithMany()
.HasForeignKey("CollectionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_collections_collections_collections_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", null)
.WithMany()
.HasForeignKey("SkinsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_collections_skins_skins_id");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.Navigation("TradeItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.Navigation("Conditions");
b.Navigation("Instances");
b.Navigation("PriceHistories");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.Navigation("Instances");
b.Navigation("PriceHistories");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Navigation("InventoryItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>
{
b.Navigation("InventoryItems");
b.Navigation("TradesReceived");
b.Navigation("TradesSent");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.Navigation("TradeItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Weapon", b =>
{
b.Navigation("Skins");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,126 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BlueLaminate.EFCore.Migrations
{
/// <inheritdoc />
public partial class AddListingsAndSkinIndexes : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "def_index",
schema: "skintracker",
table: "skins",
type: "integer",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "paint_index",
schema: "skintracker",
table: "skins",
type: "integer",
nullable: true);
migrationBuilder.CreateTable(
name: "listings",
schema: "skintracker",
columns: table => new
{
id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
cs_float_listing_id = table.Column<string>(type: "text", nullable: false),
type = table.Column<string>(type: "text", nullable: false),
price = table.Column<decimal>(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false),
listed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
def_index = table.Column<int>(type: "integer", nullable: false),
paint_index = table.Column<int>(type: "integer", nullable: false),
market_hash_name = table.Column<string>(type: "text", nullable: false),
wear_name = table.Column<string>(type: "text", nullable: true),
float_value = table.Column<decimal>(type: "numeric(10,9)", nullable: false),
paint_seed = table.Column<int>(type: "integer", nullable: false),
is_stat_trak = table.Column<bool>(type: "boolean", nullable: false),
is_souvenir = table.Column<bool>(type: "boolean", nullable: false),
sticker_count = table.Column<int>(type: "integer", nullable: false),
seller_steam_id = table.Column<string>(type: "text", nullable: true),
inspect_link = table.Column<string>(type: "text", nullable: true),
skin_id = table.Column<int>(type: "integer", nullable: true),
first_seen_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
last_seen_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
status = table.Column<string>(type: "text", nullable: false),
removed_at = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("pk_listings", x => x.id);
table.ForeignKey(
name: "fk_listings_skins_skin_id",
column: x => x.skin_id,
principalSchema: "skintracker",
principalTable: "skins",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
});
migrationBuilder.CreateIndex(
name: "ix_skins_def_index_paint_index",
schema: "skintracker",
table: "skins",
columns: new[] { "def_index", "paint_index" },
unique: true,
filter: "def_index IS NOT NULL AND paint_index IS NOT NULL");
migrationBuilder.CreateIndex(
name: "ix_listings_cs_float_listing_id",
schema: "skintracker",
table: "listings",
column: "cs_float_listing_id",
unique: true);
migrationBuilder.CreateIndex(
name: "ix_listings_def_index_paint_index",
schema: "skintracker",
table: "listings",
columns: new[] { "def_index", "paint_index" });
migrationBuilder.CreateIndex(
name: "ix_listings_skin_id",
schema: "skintracker",
table: "listings",
column: "skin_id");
migrationBuilder.CreateIndex(
name: "ix_listings_status",
schema: "skintracker",
table: "listings",
column: "status");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "listings",
schema: "skintracker");
migrationBuilder.DropIndex(
name: "ix_skins_def_index_paint_index",
schema: "skintracker",
table: "skins");
migrationBuilder.DropColumn(
name: "def_index",
schema: "skintracker",
table: "skins");
migrationBuilder.DropColumn(
name: "paint_index",
schema: "skintracker",
table: "skins");
}
}
}

View File

@@ -0,0 +1,836 @@
// <auto-generated />
using System;
using BlueLaminate.EFCore.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BlueLaminate.EFCore.Migrations
{
[DbContext(typeof(SkinTrackerDbContext))]
[Migration("20260530023217_AddSkinListingsSweptAt")]
partial class AddSkinListingsSweptAt
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("skintracker")
.HasAnnotation("ProductVersion", "10.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Collection", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_collections");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_collections_slug");
b.ToTable("collections", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("AcquiredAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("acquired_at");
b.Property<string>("AssetId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("asset_id");
b.Property<int>("SkinInstanceId")
.HasColumnType("integer")
.HasColumnName("skin_instance_id");
b.Property<int>("UserId")
.HasColumnType("integer")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("pk_inventory_items");
b.HasIndex("AssetId")
.HasDatabaseName("ix_inventory_items_asset_id");
b.HasIndex("SkinInstanceId")
.HasDatabaseName("ix_inventory_items_skin_instance_id");
b.HasIndex("UserId")
.HasDatabaseName("ix_inventory_items_user_id");
b.ToTable("inventory_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("CsFloatListingId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("cs_float_listing_id");
b.Property<int>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_value");
b.Property<string>("InspectLink")
.HasColumnType("text")
.HasColumnName("inspect_link");
b.Property<bool>("IsSouvenir")
.HasColumnType("boolean")
.HasColumnName("is_souvenir");
b.Property<bool>("IsStatTrak")
.HasColumnType("boolean")
.HasColumnName("is_stat_trak");
b.Property<DateTimeOffset>("LastSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_at");
b.Property<DateTimeOffset>("ListedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listed_at");
b.Property<string>("MarketHashName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("market_hash_name");
b.Property<int>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<int>("PaintSeed")
.HasColumnType("integer")
.HasColumnName("paint_seed");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset?>("RemovedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("removed_at");
b.Property<string>("SellerSteamId")
.HasColumnType("text")
.HasColumnName("seller_steam_id");
b.Property<int?>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("text")
.HasColumnName("status");
b.Property<int>("StickerCount")
.HasColumnType("integer")
.HasColumnName("sticker_count");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.Property<string>("WearName")
.HasColumnType("text")
.HasColumnName("wear_name");
b.HasKey("Id")
.HasName("pk_listings");
b.HasIndex("CsFloatListingId")
.IsUnique()
.HasDatabaseName("ix_listings_cs_float_listing_id");
b.HasIndex("SkinId")
.HasDatabaseName("ix_listings_skin_id");
b.HasIndex("Status")
.HasDatabaseName("ix_listings_status");
b.HasIndex("DefIndex", "PaintIndex")
.HasDatabaseName("ix_listings_def_index_paint_index");
b.ToTable("listings", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("text")
.HasColumnName("currency");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset>("RecordedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("recorded_at");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<string>("Source")
.IsRequired()
.HasColumnType("text")
.HasColumnName("source");
b.HasKey("Id")
.HasName("pk_price_histories");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_price_histories_condition_id");
b.HasIndex("SkinId", "ConditionId", "RecordedAt")
.HasDatabaseName("ix_price_histories_skin_id_condition_id_recorded_at");
b.ToTable("price_histories", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.ScrapeRun", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ItemCount")
.HasColumnType("integer")
.HasColumnName("item_count");
b.Property<DateTimeOffset>("RanAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("ran_at");
b.Property<string>("Source")
.IsRequired()
.HasColumnType("text")
.HasColumnName("source");
b.HasKey("Id")
.HasName("pk_scrape_runs");
b.HasIndex("Source", "RanAt")
.HasDatabaseName("ix_scrape_runs_source_ran_at");
b.ToTable("scrape_runs", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("description");
b.Property<decimal?>("FloatMax")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_max");
b.Property<decimal?>("FloatMin")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_min");
b.Property<string>("ImageUrl")
.HasColumnType("text")
.HasColumnName("image_url");
b.Property<DateTimeOffset?>("ListingsSweptAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listings_swept_at");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<int?>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<string>("Rarity")
.IsRequired()
.HasColumnType("text")
.HasColumnName("rarity");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
b.Property<bool>("SouvenirAvailable")
.HasColumnType("boolean")
.HasColumnName("souvenir_available");
b.Property<bool>("StatTrakAvailable")
.HasColumnType("boolean")
.HasColumnName("stat_trak_available");
b.Property<bool?>("TrueFloat")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("boolean")
.HasColumnName("true_float")
.HasComputedColumnSql("float_min = 0.0 AND float_max = 1.0", true);
b.Property<int>("WeaponId")
.HasColumnType("integer")
.HasColumnName("weapon_id");
b.HasKey("Id")
.HasName("pk_skins");
b.HasIndex("ListingsSweptAt")
.HasDatabaseName("ix_skins_listings_swept_at");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_skins_slug");
b.HasIndex("TrueFloat")
.HasDatabaseName("ix_skins_true_float");
b.HasIndex("WeaponId")
.HasDatabaseName("ix_skins_weapon_id");
b.HasIndex("DefIndex", "PaintIndex")
.IsUnique()
.HasDatabaseName("ix_skins_def_index_paint_index")
.HasFilter("def_index IS NOT NULL AND paint_index IS NOT NULL");
b.ToTable("skins", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Condition")
.IsRequired()
.HasColumnType("text")
.HasColumnName("condition");
b.Property<decimal>("MaxFloat")
.HasColumnType("numeric(10,9)")
.HasColumnName("max_float");
b.Property<decimal>("MinFloat")
.HasColumnType("numeric(10,9)")
.HasColumnName("min_float");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.HasKey("Id")
.HasName("pk_skin_conditions");
b.HasIndex("SkinId")
.HasDatabaseName("ix_skin_conditions_skin_id");
b.ToTable("skin_conditions", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_value");
b.Property<string>("PaintSeed")
.IsRequired()
.HasColumnType("text")
.HasColumnName("paint_seed");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<bool>("Souvenir")
.HasColumnType("boolean")
.HasColumnName("souvenir");
b.Property<bool>("StatTrak")
.HasColumnType("boolean")
.HasColumnName("stat_trak");
b.HasKey("Id")
.HasName("pk_skin_instances");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_skin_instances_condition_id");
b.HasIndex("FloatValue")
.HasDatabaseName("ix_skin_instances_float_value");
b.HasIndex("PaintSeed")
.HasDatabaseName("ix_skin_instances_paint_seed");
b.HasIndex("SkinId")
.HasDatabaseName("ix_skin_instances_skin_id");
b.ToTable("skin_instances", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("DisplayName")
.HasColumnType("text")
.HasColumnName("display_name");
b.Property<DateTimeOffset>("LastSyncedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_synced_at");
b.Property<string>("SteamId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("steam_id");
b.HasKey("Id")
.HasName("pk_steam_users");
b.HasIndex("SteamId")
.IsUnique()
.HasDatabaseName("ix_steam_users_steam_id");
b.ToTable("steam_users", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("FromUserId")
.HasColumnType("integer")
.HasColumnName("from_user_id");
b.Property<string>("SteamTradeId")
.HasColumnType("text")
.HasColumnName("steam_trade_id");
b.Property<int>("ToUserId")
.HasColumnType("integer")
.HasColumnName("to_user_id");
b.Property<DateTimeOffset>("TradedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("traded_at");
b.HasKey("Id")
.HasName("pk_trades");
b.HasIndex("FromUserId")
.HasDatabaseName("ix_trades_from_user_id");
b.HasIndex("ToUserId")
.HasDatabaseName("ix_trades_to_user_id");
b.ToTable("trades", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.TradeItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("InventoryItemId")
.HasColumnType("integer")
.HasColumnName("inventory_item_id");
b.Property<int>("TradeId")
.HasColumnType("integer")
.HasColumnName("trade_id");
b.HasKey("Id")
.HasName("pk_trade_items");
b.HasIndex("InventoryItemId")
.HasDatabaseName("ix_trade_items_inventory_item_id");
b.HasIndex("TradeId")
.HasDatabaseName("ix_trade_items_trade_id");
b.ToTable("trade_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Weapon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Team")
.IsRequired()
.HasColumnType("text")
.HasColumnName("team");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_weapons");
b.HasIndex("Name")
.IsUnique()
.HasDatabaseName("ix_weapons_name");
b.ToTable("weapons", "skintracker");
});
modelBuilder.Entity("CollectionSkin", b =>
{
b.Property<int>("CollectionsId")
.HasColumnType("integer")
.HasColumnName("collections_id");
b.Property<int>("SkinsId")
.HasColumnType("integer")
.HasColumnName("skins_id");
b.HasKey("CollectionsId", "SkinsId")
.HasName("pk_skin_collections");
b.HasIndex("SkinsId")
.HasDatabaseName("ix_skin_collections_skins_id");
b.ToTable("skin_collections", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinInstance", "SkinInstance")
.WithMany("InventoryItems")
.HasForeignKey("SkinInstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_inventory_items_skin_instances_skin_instance_id");
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "User")
.WithMany("InventoryItems")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_inventory_items_steam_users_user_id");
b.Navigation("SkinInstance");
b.Navigation("User");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany()
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_listings_skins_skin_id");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("PriceHistories")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_price_histories_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("PriceHistories")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_price_histories_skins_skin_id");
b.Navigation("Condition");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Weapon", "Weapon")
.WithMany("Skins")
.HasForeignKey("WeaponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skins_weapons_weapon_id");
b.Navigation("Weapon");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("Conditions")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_conditions_skins_skin_id");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("Instances")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_skin_instances_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("Instances")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_instances_skins_skin_id");
b.Navigation("Condition");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "FromUser")
.WithMany("TradesSent")
.HasForeignKey("FromUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_trades_steam_users_from_user_id");
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "ToUser")
.WithMany("TradesReceived")
.HasForeignKey("ToUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_trades_steam_users_to_user_id");
b.Navigation("FromUser");
b.Navigation("ToUser");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.TradeItem", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.InventoryItem", "InventoryItem")
.WithMany("TradeItems")
.HasForeignKey("InventoryItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_trade_items_inventory_items_inventory_item_id");
b.HasOne("BlueLaminate.EFCore.Entities.Trade", "Trade")
.WithMany("TradeItems")
.HasForeignKey("TradeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_trade_items_trades_trade_id");
b.Navigation("InventoryItem");
b.Navigation("Trade");
});
modelBuilder.Entity("CollectionSkin", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Collection", null)
.WithMany()
.HasForeignKey("CollectionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_collections_collections_collections_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", null)
.WithMany()
.HasForeignKey("SkinsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_collections_skins_skins_id");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.Navigation("TradeItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.Navigation("Conditions");
b.Navigation("Instances");
b.Navigation("PriceHistories");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.Navigation("Instances");
b.Navigation("PriceHistories");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Navigation("InventoryItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>
{
b.Navigation("InventoryItems");
b.Navigation("TradesReceived");
b.Navigation("TradesSent");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.Navigation("TradeItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Weapon", b =>
{
b.Navigation("Skins");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,42 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BlueLaminate.EFCore.Migrations
{
/// <inheritdoc />
public partial class AddSkinListingsSweptAt : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTimeOffset>(
name: "listings_swept_at",
schema: "skintracker",
table: "skins",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.CreateIndex(
name: "ix_skins_listings_swept_at",
schema: "skintracker",
table: "skins",
column: "listings_swept_at");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropIndex(
name: "ix_skins_listings_swept_at",
schema: "skintracker",
table: "skins");
migrationBuilder.DropColumn(
name: "listings_swept_at",
schema: "skintracker",
table: "skins");
}
}
}

View File

@@ -0,0 +1,868 @@
// <auto-generated />
using System;
using BlueLaminate.EFCore.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace BlueLaminate.EFCore.Migrations
{
[DbContext(typeof(SkinTrackerDbContext))]
[Migration("20260530030305_AddSkinInstanceDupeTrackingModelB")]
partial class AddSkinInstanceDupeTrackingModelB
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("skintracker")
.HasAnnotation("ProductVersion", "10.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Collection", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_collections");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_collections_slug");
b.ToTable("collections", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<DateTimeOffset>("AcquiredAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("acquired_at");
b.Property<string>("AssetId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("asset_id");
b.Property<int>("SkinInstanceId")
.HasColumnType("integer")
.HasColumnName("skin_instance_id");
b.Property<int>("UserId")
.HasColumnType("integer")
.HasColumnName("user_id");
b.HasKey("Id")
.HasName("pk_inventory_items");
b.HasIndex("AssetId")
.HasDatabaseName("ix_inventory_items_asset_id");
b.HasIndex("SkinInstanceId")
.HasDatabaseName("ix_inventory_items_skin_instance_id");
b.HasIndex("UserId")
.HasDatabaseName("ix_inventory_items_user_id");
b.ToTable("inventory_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("AssetId")
.HasColumnType("text")
.HasColumnName("asset_id");
b.Property<string>("CsFloatListingId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("cs_float_listing_id");
b.Property<int>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(20,18)")
.HasColumnName("float_value");
b.Property<string>("InspectLink")
.HasColumnType("text")
.HasColumnName("inspect_link");
b.Property<bool>("IsSouvenir")
.HasColumnType("boolean")
.HasColumnName("is_souvenir");
b.Property<bool>("IsStatTrak")
.HasColumnType("boolean")
.HasColumnName("is_stat_trak");
b.Property<DateTimeOffset>("LastSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_at");
b.Property<DateTimeOffset>("ListedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listed_at");
b.Property<string>("MarketHashName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("market_hash_name");
b.Property<int>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<int>("PaintSeed")
.HasColumnType("integer")
.HasColumnName("paint_seed");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset?>("RemovedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("removed_at");
b.Property<string>("SellerSteamId")
.HasColumnType("text")
.HasColumnName("seller_steam_id");
b.Property<int?>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<int?>("SkinInstanceId")
.HasColumnType("integer")
.HasColumnName("skin_instance_id");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("text")
.HasColumnName("status");
b.Property<int>("StickerCount")
.HasColumnType("integer")
.HasColumnName("sticker_count");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.Property<string>("WearName")
.HasColumnType("text")
.HasColumnName("wear_name");
b.HasKey("Id")
.HasName("pk_listings");
b.HasIndex("AssetId")
.HasDatabaseName("ix_listings_asset_id");
b.HasIndex("CsFloatListingId")
.IsUnique()
.HasDatabaseName("ix_listings_cs_float_listing_id");
b.HasIndex("SkinId")
.HasDatabaseName("ix_listings_skin_id");
b.HasIndex("SkinInstanceId")
.HasDatabaseName("ix_listings_skin_instance_id");
b.HasIndex("Status")
.HasDatabaseName("ix_listings_status");
b.HasIndex("DefIndex", "PaintIndex")
.HasDatabaseName("ix_listings_def_index_paint_index");
b.ToTable("listings", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<string>("Currency")
.IsRequired()
.HasColumnType("text")
.HasColumnName("currency");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset>("RecordedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("recorded_at");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<string>("Source")
.IsRequired()
.HasColumnType("text")
.HasColumnName("source");
b.HasKey("Id")
.HasName("pk_price_histories");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_price_histories_condition_id");
b.HasIndex("SkinId", "ConditionId", "RecordedAt")
.HasDatabaseName("ix_price_histories_skin_id_condition_id_recorded_at");
b.ToTable("price_histories", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.ScrapeRun", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ItemCount")
.HasColumnType("integer")
.HasColumnName("item_count");
b.Property<DateTimeOffset>("RanAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("ran_at");
b.Property<string>("Source")
.IsRequired()
.HasColumnType("text")
.HasColumnName("source");
b.HasKey("Id")
.HasName("pk_scrape_runs");
b.HasIndex("Source", "RanAt")
.HasDatabaseName("ix_scrape_runs_source_ran_at");
b.ToTable("scrape_runs", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("description");
b.Property<decimal?>("FloatMax")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_max");
b.Property<decimal?>("FloatMin")
.HasColumnType("numeric(10,9)")
.HasColumnName("float_min");
b.Property<string>("ImageUrl")
.HasColumnType("text")
.HasColumnName("image_url");
b.Property<DateTimeOffset?>("ListingsSweptAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listings_swept_at");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<int?>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<string>("Rarity")
.IsRequired()
.HasColumnType("text")
.HasColumnName("rarity");
b.Property<string>("Slug")
.IsRequired()
.HasColumnType("text")
.HasColumnName("slug");
b.Property<bool>("SouvenirAvailable")
.HasColumnType("boolean")
.HasColumnName("souvenir_available");
b.Property<bool>("StatTrakAvailable")
.HasColumnType("boolean")
.HasColumnName("stat_trak_available");
b.Property<bool?>("TrueFloat")
.ValueGeneratedOnAddOrUpdate()
.HasColumnType("boolean")
.HasColumnName("true_float")
.HasComputedColumnSql("float_min = 0.0 AND float_max = 1.0", true);
b.Property<int>("WeaponId")
.HasColumnType("integer")
.HasColumnName("weapon_id");
b.HasKey("Id")
.HasName("pk_skins");
b.HasIndex("ListingsSweptAt")
.HasDatabaseName("ix_skins_listings_swept_at");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_skins_slug");
b.HasIndex("TrueFloat")
.HasDatabaseName("ix_skins_true_float");
b.HasIndex("WeaponId")
.HasDatabaseName("ix_skins_weapon_id");
b.HasIndex("DefIndex", "PaintIndex")
.IsUnique()
.HasDatabaseName("ix_skins_def_index_paint_index")
.HasFilter("def_index IS NOT NULL AND paint_index IS NOT NULL");
b.ToTable("skins", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Condition")
.IsRequired()
.HasColumnType("text")
.HasColumnName("condition");
b.Property<decimal>("MaxFloat")
.HasColumnType("numeric(10,9)")
.HasColumnName("max_float");
b.Property<decimal>("MinFloat")
.HasColumnType("numeric(10,9)")
.HasColumnName("min_float");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.HasKey("Id")
.HasName("pk_skin_conditions");
b.HasIndex("SkinId")
.HasDatabaseName("ix_skin_conditions_skin_id");
b.ToTable("skin_conditions", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<DateTimeOffset?>("DupeFirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("dupe_first_seen_at");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(20,18)")
.HasColumnName("float_value");
b.Property<DateTimeOffset>("LastSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_at");
b.Property<string>("PaintSeed")
.IsRequired()
.HasColumnType("text")
.HasColumnName("paint_seed");
b.Property<int>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<bool>("Souvenir")
.HasColumnType("boolean")
.HasColumnName("souvenir");
b.Property<bool>("StatTrak")
.HasColumnType("boolean")
.HasColumnName("stat_trak");
b.Property<bool>("SuspectedDupe")
.HasColumnType("boolean")
.HasColumnName("suspected_dupe");
b.HasKey("Id")
.HasName("pk_skin_instances");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_skin_instances_condition_id");
b.HasIndex("SuspectedDupe")
.HasDatabaseName("ix_skin_instances_suspected_dupe");
b.HasIndex("SkinId", "FloatValue", "PaintSeed", "StatTrak", "Souvenir")
.HasDatabaseName("ix_skin_instances_skin_id_float_value_paint_seed_stat_trak_sou");
b.ToTable("skin_instances", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("DisplayName")
.HasColumnType("text")
.HasColumnName("display_name");
b.Property<DateTimeOffset>("LastSyncedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_synced_at");
b.Property<string>("SteamId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("steam_id");
b.HasKey("Id")
.HasName("pk_steam_users");
b.HasIndex("SteamId")
.IsUnique()
.HasDatabaseName("ix_steam_users_steam_id");
b.ToTable("steam_users", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("FromUserId")
.HasColumnType("integer")
.HasColumnName("from_user_id");
b.Property<string>("SteamTradeId")
.HasColumnType("text")
.HasColumnName("steam_trade_id");
b.Property<int>("ToUserId")
.HasColumnType("integer")
.HasColumnName("to_user_id");
b.Property<DateTimeOffset>("TradedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("traded_at");
b.HasKey("Id")
.HasName("pk_trades");
b.HasIndex("FromUserId")
.HasDatabaseName("ix_trades_from_user_id");
b.HasIndex("ToUserId")
.HasDatabaseName("ix_trades_to_user_id");
b.ToTable("trades", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.TradeItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("InventoryItemId")
.HasColumnType("integer")
.HasColumnName("inventory_item_id");
b.Property<int>("TradeId")
.HasColumnType("integer")
.HasColumnName("trade_id");
b.HasKey("Id")
.HasName("pk_trade_items");
b.HasIndex("InventoryItemId")
.HasDatabaseName("ix_trade_items_inventory_item_id");
b.HasIndex("TradeId")
.HasDatabaseName("ix_trade_items_trade_id");
b.ToTable("trade_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Weapon", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<string>("Team")
.IsRequired()
.HasColumnType("text")
.HasColumnName("team");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.HasKey("Id")
.HasName("pk_weapons");
b.HasIndex("Name")
.IsUnique()
.HasDatabaseName("ix_weapons_name");
b.ToTable("weapons", "skintracker");
});
modelBuilder.Entity("CollectionSkin", b =>
{
b.Property<int>("CollectionsId")
.HasColumnType("integer")
.HasColumnName("collections_id");
b.Property<int>("SkinsId")
.HasColumnType("integer")
.HasColumnName("skins_id");
b.HasKey("CollectionsId", "SkinsId")
.HasName("pk_skin_collections");
b.HasIndex("SkinsId")
.HasDatabaseName("ix_skin_collections_skins_id");
b.ToTable("skin_collections", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinInstance", "SkinInstance")
.WithMany("InventoryItems")
.HasForeignKey("SkinInstanceId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_inventory_items_skin_instances_skin_instance_id");
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "User")
.WithMany("InventoryItems")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_inventory_items_steam_users_user_id");
b.Navigation("SkinInstance");
b.Navigation("User");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany()
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_listings_skins_skin_id");
b.HasOne("BlueLaminate.EFCore.Entities.SkinInstance", "SkinInstance")
.WithMany("Listings")
.HasForeignKey("SkinInstanceId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_listings_skin_instances_skin_instance_id");
b.Navigation("Skin");
b.Navigation("SkinInstance");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("PriceHistories")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_price_histories_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("PriceHistories")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_price_histories_skins_skin_id");
b.Navigation("Condition");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Weapon", "Weapon")
.WithMany("Skins")
.HasForeignKey("WeaponId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skins_weapons_weapon_id");
b.Navigation("Weapon");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("Conditions")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_conditions_skins_skin_id");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("Instances")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_skin_instances_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany("Instances")
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_instances_skins_skin_id");
b.Navigation("Condition");
b.Navigation("Skin");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "FromUser")
.WithMany("TradesSent")
.HasForeignKey("FromUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_trades_steam_users_from_user_id");
b.HasOne("BlueLaminate.EFCore.Entities.SteamUser", "ToUser")
.WithMany("TradesReceived")
.HasForeignKey("ToUserId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.HasConstraintName("fk_trades_steam_users_to_user_id");
b.Navigation("FromUser");
b.Navigation("ToUser");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.TradeItem", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.InventoryItem", "InventoryItem")
.WithMany("TradeItems")
.HasForeignKey("InventoryItemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_trade_items_inventory_items_inventory_item_id");
b.HasOne("BlueLaminate.EFCore.Entities.Trade", "Trade")
.WithMany("TradeItems")
.HasForeignKey("TradeId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_trade_items_trades_trade_id");
b.Navigation("InventoryItem");
b.Navigation("Trade");
});
modelBuilder.Entity("CollectionSkin", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Collection", null)
.WithMany()
.HasForeignKey("CollectionsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_collections_collections_collections_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", null)
.WithMany()
.HasForeignKey("SkinsId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("fk_skin_collections_skins_skins_id");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.InventoryItem", b =>
{
b.Navigation("TradeItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Skin", b =>
{
b.Navigation("Conditions");
b.Navigation("Instances");
b.Navigation("PriceHistories");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinCondition", b =>
{
b.Navigation("Instances");
b.Navigation("PriceHistories");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Navigation("InventoryItems");
b.Navigation("Listings");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>
{
b.Navigation("InventoryItems");
b.Navigation("TradesReceived");
b.Navigation("TradesSent");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Trade", b =>
{
b.Navigation("TradeItems");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Weapon", b =>
{
b.Navigation("Skins");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,259 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace BlueLaminate.EFCore.Migrations
{
/// <inheritdoc />
public partial class AddSkinInstanceDupeTrackingModelB : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_skin_instances_skin_conditions_condition_id",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropIndex(
name: "ix_skin_instances_float_value",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropIndex(
name: "ix_skin_instances_paint_seed",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropIndex(
name: "ix_skin_instances_skin_id",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.AlterColumn<decimal>(
name: "float_value",
schema: "skintracker",
table: "skin_instances",
type: "numeric(20,18)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "numeric(10,9)");
migrationBuilder.AlterColumn<int>(
name: "condition_id",
schema: "skintracker",
table: "skin_instances",
type: "integer",
nullable: true,
oldClrType: typeof(int),
oldType: "integer");
migrationBuilder.AddColumn<DateTimeOffset>(
name: "dupe_first_seen_at",
schema: "skintracker",
table: "skin_instances",
type: "timestamp with time zone",
nullable: true);
migrationBuilder.AddColumn<DateTimeOffset>(
name: "last_seen_at",
schema: "skintracker",
table: "skin_instances",
type: "timestamp with time zone",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
migrationBuilder.AddColumn<bool>(
name: "suspected_dupe",
schema: "skintracker",
table: "skin_instances",
type: "boolean",
nullable: false,
defaultValue: false);
migrationBuilder.AlterColumn<decimal>(
name: "float_value",
schema: "skintracker",
table: "listings",
type: "numeric(20,18)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "numeric(10,9)");
migrationBuilder.AddColumn<string>(
name: "asset_id",
schema: "skintracker",
table: "listings",
type: "text",
nullable: true);
migrationBuilder.AddColumn<int>(
name: "skin_instance_id",
schema: "skintracker",
table: "listings",
type: "integer",
nullable: true);
migrationBuilder.CreateIndex(
name: "ix_skin_instances_skin_id_float_value_paint_seed_stat_trak_sou",
schema: "skintracker",
table: "skin_instances",
columns: new[] { "skin_id", "float_value", "paint_seed", "stat_trak", "souvenir" });
migrationBuilder.CreateIndex(
name: "ix_skin_instances_suspected_dupe",
schema: "skintracker",
table: "skin_instances",
column: "suspected_dupe");
migrationBuilder.CreateIndex(
name: "ix_listings_asset_id",
schema: "skintracker",
table: "listings",
column: "asset_id");
migrationBuilder.CreateIndex(
name: "ix_listings_skin_instance_id",
schema: "skintracker",
table: "listings",
column: "skin_instance_id");
migrationBuilder.AddForeignKey(
name: "fk_listings_skin_instances_skin_instance_id",
schema: "skintracker",
table: "listings",
column: "skin_instance_id",
principalSchema: "skintracker",
principalTable: "skin_instances",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
migrationBuilder.AddForeignKey(
name: "fk_skin_instances_skin_conditions_condition_id",
schema: "skintracker",
table: "skin_instances",
column: "condition_id",
principalSchema: "skintracker",
principalTable: "skin_conditions",
principalColumn: "id",
onDelete: ReferentialAction.SetNull);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "fk_listings_skin_instances_skin_instance_id",
schema: "skintracker",
table: "listings");
migrationBuilder.DropForeignKey(
name: "fk_skin_instances_skin_conditions_condition_id",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropIndex(
name: "ix_skin_instances_skin_id_float_value_paint_seed_stat_trak_sou",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropIndex(
name: "ix_skin_instances_suspected_dupe",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropIndex(
name: "ix_listings_asset_id",
schema: "skintracker",
table: "listings");
migrationBuilder.DropIndex(
name: "ix_listings_skin_instance_id",
schema: "skintracker",
table: "listings");
migrationBuilder.DropColumn(
name: "dupe_first_seen_at",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropColumn(
name: "last_seen_at",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropColumn(
name: "suspected_dupe",
schema: "skintracker",
table: "skin_instances");
migrationBuilder.DropColumn(
name: "asset_id",
schema: "skintracker",
table: "listings");
migrationBuilder.DropColumn(
name: "skin_instance_id",
schema: "skintracker",
table: "listings");
migrationBuilder.AlterColumn<decimal>(
name: "float_value",
schema: "skintracker",
table: "skin_instances",
type: "numeric(10,9)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "numeric(20,18)");
migrationBuilder.AlterColumn<int>(
name: "condition_id",
schema: "skintracker",
table: "skin_instances",
type: "integer",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "integer",
oldNullable: true);
migrationBuilder.AlterColumn<decimal>(
name: "float_value",
schema: "skintracker",
table: "listings",
type: "numeric(10,9)",
nullable: false,
oldClrType: typeof(decimal),
oldType: "numeric(20,18)");
migrationBuilder.CreateIndex(
name: "ix_skin_instances_float_value",
schema: "skintracker",
table: "skin_instances",
column: "float_value");
migrationBuilder.CreateIndex(
name: "ix_skin_instances_paint_seed",
schema: "skintracker",
table: "skin_instances",
column: "paint_seed");
migrationBuilder.CreateIndex(
name: "ix_skin_instances_skin_id",
schema: "skintracker",
table: "skin_instances",
column: "skin_id");
migrationBuilder.AddForeignKey(
name: "fk_skin_instances_skin_conditions_condition_id",
schema: "skintracker",
table: "skin_instances",
column: "condition_id",
principalSchema: "skintracker",
principalTable: "skin_conditions",
principalColumn: "id",
onDelete: ReferentialAction.Restrict);
}
}
}

View File

@@ -98,6 +98,133 @@ namespace BlueLaminate.EFCore.Migrations
b.ToTable("inventory_items", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer")
.HasColumnName("id");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("AssetId")
.HasColumnType("text")
.HasColumnName("asset_id");
b.Property<string>("CsFloatListingId")
.IsRequired()
.HasColumnType("text")
.HasColumnName("cs_float_listing_id");
b.Property<int>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(20,18)")
.HasColumnName("float_value");
b.Property<string>("InspectLink")
.HasColumnType("text")
.HasColumnName("inspect_link");
b.Property<bool>("IsSouvenir")
.HasColumnType("boolean")
.HasColumnName("is_souvenir");
b.Property<bool>("IsStatTrak")
.HasColumnType("boolean")
.HasColumnName("is_stat_trak");
b.Property<DateTimeOffset>("LastSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_at");
b.Property<DateTimeOffset>("ListedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listed_at");
b.Property<string>("MarketHashName")
.IsRequired()
.HasColumnType("text")
.HasColumnName("market_hash_name");
b.Property<int>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<int>("PaintSeed")
.HasColumnType("integer")
.HasColumnName("paint_seed");
b.Property<decimal>("Price")
.HasPrecision(18, 2)
.HasColumnType("numeric(18,2)")
.HasColumnName("price");
b.Property<DateTimeOffset?>("RemovedAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("removed_at");
b.Property<string>("SellerSteamId")
.HasColumnType("text")
.HasColumnName("seller_steam_id");
b.Property<int?>("SkinId")
.HasColumnType("integer")
.HasColumnName("skin_id");
b.Property<int?>("SkinInstanceId")
.HasColumnType("integer")
.HasColumnName("skin_instance_id");
b.Property<string>("Status")
.IsRequired()
.HasColumnType("text")
.HasColumnName("status");
b.Property<int>("StickerCount")
.HasColumnType("integer")
.HasColumnName("sticker_count");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text")
.HasColumnName("type");
b.Property<string>("WearName")
.HasColumnType("text")
.HasColumnName("wear_name");
b.HasKey("Id")
.HasName("pk_listings");
b.HasIndex("AssetId")
.HasDatabaseName("ix_listings_asset_id");
b.HasIndex("CsFloatListingId")
.IsUnique()
.HasDatabaseName("ix_listings_cs_float_listing_id");
b.HasIndex("SkinId")
.HasDatabaseName("ix_listings_skin_id");
b.HasIndex("SkinInstanceId")
.HasDatabaseName("ix_listings_skin_instance_id");
b.HasIndex("Status")
.HasDatabaseName("ix_listings_status");
b.HasIndex("DefIndex", "PaintIndex")
.HasDatabaseName("ix_listings_def_index_paint_index");
b.ToTable("listings", "skintracker");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.Property<int>("Id")
@@ -186,6 +313,10 @@ namespace BlueLaminate.EFCore.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int?>("DefIndex")
.HasColumnType("integer")
.HasColumnName("def_index");
b.Property<string>("Description")
.HasColumnType("text")
.HasColumnName("description");
@@ -202,11 +333,19 @@ namespace BlueLaminate.EFCore.Migrations
.HasColumnType("text")
.HasColumnName("image_url");
b.Property<DateTimeOffset?>("ListingsSweptAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("listings_swept_at");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text")
.HasColumnName("name");
b.Property<int?>("PaintIndex")
.HasColumnType("integer")
.HasColumnName("paint_index");
b.Property<string>("Rarity")
.IsRequired()
.HasColumnType("text")
@@ -238,6 +377,9 @@ namespace BlueLaminate.EFCore.Migrations
b.HasKey("Id")
.HasName("pk_skins");
b.HasIndex("ListingsSweptAt")
.HasDatabaseName("ix_skins_listings_swept_at");
b.HasIndex("Slug")
.IsUnique()
.HasDatabaseName("ix_skins_slug");
@@ -248,6 +390,11 @@ namespace BlueLaminate.EFCore.Migrations
b.HasIndex("WeaponId")
.HasDatabaseName("ix_skins_weapon_id");
b.HasIndex("DefIndex", "PaintIndex")
.IsUnique()
.HasDatabaseName("ix_skins_def_index_paint_index")
.HasFilter("def_index IS NOT NULL AND paint_index IS NOT NULL");
b.ToTable("skins", "skintracker");
});
@@ -295,18 +442,26 @@ namespace BlueLaminate.EFCore.Migrations
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("ConditionId")
b.Property<int?>("ConditionId")
.HasColumnType("integer")
.HasColumnName("condition_id");
b.Property<DateTimeOffset?>("DupeFirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("dupe_first_seen_at");
b.Property<DateTimeOffset>("FirstSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("first_seen_at");
b.Property<decimal>("FloatValue")
.HasColumnType("numeric(10,9)")
.HasColumnType("numeric(20,18)")
.HasColumnName("float_value");
b.Property<DateTimeOffset>("LastSeenAt")
.HasColumnType("timestamp with time zone")
.HasColumnName("last_seen_at");
b.Property<string>("PaintSeed")
.IsRequired()
.HasColumnType("text")
@@ -324,20 +479,21 @@ namespace BlueLaminate.EFCore.Migrations
.HasColumnType("boolean")
.HasColumnName("stat_trak");
b.Property<bool>("SuspectedDupe")
.HasColumnType("boolean")
.HasColumnName("suspected_dupe");
b.HasKey("Id")
.HasName("pk_skin_instances");
b.HasIndex("ConditionId")
.HasDatabaseName("ix_skin_instances_condition_id");
b.HasIndex("FloatValue")
.HasDatabaseName("ix_skin_instances_float_value");
b.HasIndex("SuspectedDupe")
.HasDatabaseName("ix_skin_instances_suspected_dupe");
b.HasIndex("PaintSeed")
.HasDatabaseName("ix_skin_instances_paint_seed");
b.HasIndex("SkinId")
.HasDatabaseName("ix_skin_instances_skin_id");
b.HasIndex("SkinId", "FloatValue", "PaintSeed", "StatTrak", "Souvenir")
.HasDatabaseName("ix_skin_instances_skin_id_float_value_paint_seed_stat_trak_sou");
b.ToTable("skin_instances", "skintracker");
});
@@ -514,6 +670,25 @@ namespace BlueLaminate.EFCore.Migrations
b.Navigation("User");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.Listing", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
.WithMany()
.HasForeignKey("SkinId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_listings_skins_skin_id");
b.HasOne("BlueLaminate.EFCore.Entities.SkinInstance", "SkinInstance")
.WithMany("Listings")
.HasForeignKey("SkinInstanceId")
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_listings_skin_instances_skin_instance_id");
b.Navigation("Skin");
b.Navigation("SkinInstance");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.PriceHistory", b =>
{
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
@@ -564,8 +739,7 @@ namespace BlueLaminate.EFCore.Migrations
b.HasOne("BlueLaminate.EFCore.Entities.SkinCondition", "Condition")
.WithMany("Instances")
.HasForeignKey("ConditionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired()
.OnDelete(DeleteBehavior.SetNull)
.HasConstraintName("fk_skin_instances_skin_conditions_condition_id");
b.HasOne("BlueLaminate.EFCore.Entities.Skin", "Skin")
@@ -663,6 +837,8 @@ namespace BlueLaminate.EFCore.Migrations
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SkinInstance", b =>
{
b.Navigation("InventoryItems");
b.Navigation("Listings");
});
modelBuilder.Entity("BlueLaminate.EFCore.Entities.SteamUser", b =>