78 lines
2.5 KiB
C#
78 lines
2.5 KiB
C#
using BlueLaminate.EFCore.Data;
|
|
using BlueLaminate.EFCore.Entities;
|
|
using BlueLaminate.Scraper.Weapons;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace BlueLaminate.Cli;
|
|
|
|
/// <param name="Skipped">True when the monthly throttle suppressed the run.</param>
|
|
/// <param name="LastRanAt">When the previous successful run happened, if any.</param>
|
|
public sealed record WeaponSyncResult(
|
|
bool Skipped,
|
|
DateTimeOffset? LastRanAt,
|
|
int Scraped,
|
|
int Inserted,
|
|
int Updated);
|
|
|
|
/// <summary>
|
|
/// Fetches the CS2 weapon catalogue and upserts it into the database. The
|
|
/// catalogue changes rarely, so a run is throttled to at most once a month
|
|
/// unless explicitly forced.
|
|
/// </summary>
|
|
public sealed class WeaponSyncService
|
|
{
|
|
public const string Source = "weapons";
|
|
|
|
private readonly SkinTrackerDbContext _db;
|
|
private readonly WeaponWikiScraper _scraper;
|
|
|
|
public WeaponSyncService(SkinTrackerDbContext db, WeaponWikiScraper scraper)
|
|
{
|
|
_db = db;
|
|
_scraper = scraper;
|
|
}
|
|
|
|
public async Task<WeaponSyncResult> SyncAsync(bool force = false, CancellationToken ct = default)
|
|
{
|
|
var now = DateTimeOffset.UtcNow;
|
|
|
|
var lastRanAt = await _db.ScrapeRuns
|
|
.Where(r => r.Source == Source)
|
|
.OrderByDescending(r => r.RanAt)
|
|
.Select(r => (DateTimeOffset?)r.RanAt)
|
|
.FirstOrDefaultAsync(ct);
|
|
|
|
if (!force && lastRanAt is { } last && last.AddMonths(1) > now)
|
|
return new WeaponSyncResult(Skipped: true, last, Scraped: 0, Inserted: 0, Updated: 0);
|
|
|
|
var scraped = await _scraper.ScrapeAsync(ct);
|
|
|
|
var existing = await _db.Weapons.ToDictionaryAsync(w => w.Name, ct);
|
|
var inserted = 0;
|
|
var updated = 0;
|
|
|
|
foreach (var s in scraped)
|
|
{
|
|
if (existing.TryGetValue(s.Name, out var weapon))
|
|
{
|
|
if (weapon.Type != s.Type || weapon.Team != s.Team)
|
|
{
|
|
weapon.Type = s.Type;
|
|
weapon.Team = s.Team;
|
|
updated++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_db.Weapons.Add(new Weapon { Name = s.Name, Type = s.Type, Team = s.Team });
|
|
inserted++;
|
|
}
|
|
}
|
|
|
|
_db.ScrapeRuns.Add(new ScrapeRun { Source = Source, RanAt = now, ItemCount = scraped.Count });
|
|
await _db.SaveChangesAsync(ct);
|
|
|
|
return new WeaponSyncResult(Skipped: false, lastRanAt, scraped.Count, inserted, updated);
|
|
}
|
|
}
|