Enhance UI, add AI integration, improve logging and database stats
This commit is contained in:
@@ -14,8 +14,8 @@ public class MarketRepository : IMarketRepository
|
||||
public MarketRepository(AppDbContext db) => _db = db;
|
||||
|
||||
public async Task<Market?> GetByPlatformIdAsync(PlatformType platform, string platformMarketId, CancellationToken ct = default)
|
||||
=> await _db.Markets.Include(m => m.Outcomes)
|
||||
.FirstOrDefaultAsync(m => m.Platform == platform && m.PlatformMarketId == platformMarketId, ct);
|
||||
=> await _db.Markets.Include(m => m.Outcomes).Include(m => m.Event)
|
||||
.FirstOrDefaultAsync(m => m.Platform == platform && m.ConditionId == platformMarketId, ct);
|
||||
|
||||
public async Task<MarketOutcome?> GetOutcomeByTokenIdAsync(string tokenId, CancellationToken ct = default)
|
||||
=> await _db.MarketOutcomes.Include(o => o.Market)
|
||||
@@ -34,7 +34,7 @@ public class MarketRepository : IMarketRepository
|
||||
TruncateMarketStrings(market);
|
||||
|
||||
var existing = await _db.Markets.Include(m => m.Outcomes)
|
||||
.FirstOrDefaultAsync(m => m.Platform == market.Platform && m.PlatformMarketId == market.PlatformMarketId, ct);
|
||||
.FirstOrDefaultAsync(m => m.Platform == market.Platform && m.ConditionId == market.ConditionId, ct);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
@@ -42,6 +42,11 @@ public class MarketRepository : IMarketRepository
|
||||
}
|
||||
else
|
||||
{
|
||||
if (market.Event == null && market.EventId == 0)
|
||||
{
|
||||
// Fallback to avoid foreign key exceptions if event is entirely missing
|
||||
market.Event = new Event { Platform = market.Platform, PlatformEventId = market.PlatformMarketId, Slug = "unknown", Title = "Unknown" };
|
||||
}
|
||||
_db.Markets.Add(market);
|
||||
}
|
||||
|
||||
@@ -55,9 +60,9 @@ public class MarketRepository : IMarketRepository
|
||||
|
||||
public async Task AddOrUpdateRangeAsync(IEnumerable<Market> markets, CancellationToken ct = default)
|
||||
{
|
||||
// Deduplicate input by PlatformMarketId to avoid processing the same ID twice in one call
|
||||
// Deduplicate input by ConditionId to avoid processing the same ID twice in one call
|
||||
var marketList = markets
|
||||
.GroupBy(m => new { m.Platform, m.PlatformMarketId })
|
||||
.GroupBy(m => new { m.Platform, m.ConditionId })
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
@@ -72,25 +77,28 @@ public class MarketRepository : IMarketRepository
|
||||
{
|
||||
var currentBatch = marketList.Skip(i).Take(subBatchSize).ToList();
|
||||
var platform = currentBatch.First().Platform;
|
||||
var ids = currentBatch.Select(m => m.PlatformMarketId).ToList();
|
||||
var ids = currentBatch.Select(m => m.ConditionId).ToList();
|
||||
|
||||
// Fetch all existing markets in this batch at once
|
||||
var existingMarkets = await _db.Markets.Include(m => m.Outcomes)
|
||||
.Where(m => m.Platform == platform && ids.Contains(m.PlatformMarketId))
|
||||
.Where(m => m.Platform == platform && ids.Contains(m.ConditionId))
|
||||
.ToListAsync(ct);
|
||||
|
||||
var existingMap = existingMarkets.ToDictionary(m => m.PlatformMarketId);
|
||||
var existingMap = existingMarkets.ToDictionary(m => m.ConditionId);
|
||||
|
||||
foreach (var market in currentBatch)
|
||||
{
|
||||
TruncateMarketStrings(market);
|
||||
|
||||
if (existingMap.TryGetValue(market.PlatformMarketId, out var existing))
|
||||
if (existingMap.TryGetValue(market.ConditionId, out var existing))
|
||||
{
|
||||
UpdateMarketFields(existing, market);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (market.Event == null && market.EventId == 0)
|
||||
{
|
||||
market.Event = new Event { Platform = market.Platform, PlatformEventId = market.PlatformMarketId, Slug = "unknown", Title = "Unknown" };
|
||||
}
|
||||
_db.Markets.Add(market);
|
||||
}
|
||||
}
|
||||
@@ -104,21 +112,96 @@ public class MarketRepository : IMarketRepository
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AddOrUpdateEventsAsync(IEnumerable<Event> events, CancellationToken ct = default)
|
||||
{
|
||||
var eventList = events.GroupBy(e => new { e.Platform, e.PlatformEventId }).Select(g => g.First()).ToList();
|
||||
if (!eventList.Any()) return;
|
||||
|
||||
await _syncSemaphore.WaitAsync(ct);
|
||||
try
|
||||
{
|
||||
const int subBatchSize = 100;
|
||||
for (int i = 0; i < eventList.Count; i += subBatchSize)
|
||||
{
|
||||
var currentBatch = eventList.Skip(i).Take(subBatchSize).ToList();
|
||||
var platform = currentBatch.First().Platform;
|
||||
var eventIds = currentBatch.Select(e => e.PlatformEventId).ToList();
|
||||
|
||||
var existingEvents = await _db.Events
|
||||
.Include(e => e.Markets).ThenInclude(m => m.Outcomes)
|
||||
.Where(e => e.Platform == platform && eventIds.Contains(e.PlatformEventId))
|
||||
.ToListAsync(ct);
|
||||
|
||||
var existingEventsMap = existingEvents.ToDictionary(e => e.PlatformEventId);
|
||||
|
||||
foreach (var ev in currentBatch)
|
||||
{
|
||||
if (ev.Slug != null && ev.Slug.Length > 512) ev.Slug = ev.Slug[..512];
|
||||
if (ev.Title != null && ev.Title.Length > 1024) ev.Title = ev.Title[..1024];
|
||||
|
||||
if (existingEventsMap.TryGetValue(ev.PlatformEventId, out var existing))
|
||||
{
|
||||
existing.Slug = ev.Slug;
|
||||
existing.Title = ev.Title;
|
||||
existing.Description = ev.Description;
|
||||
existing.ImageUrl = ev.ImageUrl;
|
||||
existing.Tags = ev.Tags;
|
||||
existing.StartDate = ev.StartDate;
|
||||
existing.EndDate = ev.EndDate;
|
||||
existing.IsActive = ev.IsActive;
|
||||
existing.IsClosed = ev.IsClosed;
|
||||
existing.LastUpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Upsert markets inside event
|
||||
foreach (var market in ev.Markets)
|
||||
{
|
||||
TruncateMarketStrings(market);
|
||||
var existingMarket = existing.Markets.FirstOrDefault(m => m.ConditionId == market.ConditionId);
|
||||
if (existingMarket != null)
|
||||
{
|
||||
UpdateMarketFields(existingMarket, market);
|
||||
}
|
||||
else
|
||||
{
|
||||
market.EventId = existing.Id;
|
||||
market.Event = null; // Prevent EF tracking issue
|
||||
existing.Markets.Add(market);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var m in ev.Markets) TruncateMarketStrings(m);
|
||||
_db.Events.Add(ev);
|
||||
}
|
||||
}
|
||||
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_syncSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateMarketFields(Market existing, Market updated)
|
||||
{
|
||||
existing.Question = updated.Question;
|
||||
existing.MarketSlug = updated.MarketSlug;
|
||||
existing.EventSlug = updated.EventSlug;
|
||||
existing.PlatformMarketId = updated.PlatformMarketId;
|
||||
existing.QuestionId = updated.QuestionId;
|
||||
existing.Description = updated.Description;
|
||||
existing.ImageUrl = updated.ImageUrl;
|
||||
existing.Category = updated.Category;
|
||||
existing.Volume = updated.Volume;
|
||||
existing.Volume24h = updated.Volume24h;
|
||||
existing.Liquidity = updated.Liquidity;
|
||||
existing.StartDate = updated.StartDate;
|
||||
existing.EndDate = updated.EndDate;
|
||||
existing.IsResolved = updated.IsResolved;
|
||||
existing.ResolutionOutcome = updated.ResolutionOutcome;
|
||||
existing.CreatedAt = updated.CreatedAt; // Platform creation date
|
||||
existing.CreatedAt = updated.CreatedAt;
|
||||
existing.LastUpdatedAt = DateTime.UtcNow;
|
||||
|
||||
// Upsert outcomes
|
||||
@@ -146,7 +229,6 @@ public class MarketRepository : IMarketRepository
|
||||
market.Question = StringHelper.Truncate(market.Question, 1024) ?? "";
|
||||
market.Description = StringHelper.Truncate(market.Description, 4096);
|
||||
market.MarketSlug = StringHelper.Truncate(market.MarketSlug, 512) ?? "";
|
||||
market.EventSlug = StringHelper.Truncate(market.EventSlug, 512) ?? "";
|
||||
market.ImageUrl = StringHelper.Truncate(market.ImageUrl, 1024);
|
||||
market.Category = StringHelper.Truncate(market.Category, 128) ?? "";
|
||||
|
||||
@@ -192,7 +274,7 @@ public class MarketRepository : IMarketRepository
|
||||
|
||||
return await _db.Markets.Include(m => m.Outcomes)
|
||||
.Where(m => m.Question.Contains(query) ||
|
||||
m.PlatformMarketId.Contains(query) ||
|
||||
m.ConditionId.Contains(query) ||
|
||||
m.Id.ToString() == query)
|
||||
.OrderByDescending(m => m.Volume)
|
||||
.Take(take)
|
||||
|
||||
Reference in New Issue
Block a user