alexei.dolgolyov c2934b2c8d chore(med): mapping culture-safe parse, dead-code, scope comparer, UA rotator, parser cache
Six MEDIUM-tier review items:

* Mapping.cs — DateTimeOffset.Parse now passes CultureInfo.InvariantCulture
  + DateTimeStyles.RoundtripKind so a non-en-US thread culture cannot
  corrupt round-tripped ScheduledAt / CapturedAt / DetectedAt / CompletedAt.
  Also replaces the magic 0/1 BetScope discriminator with named constants.

* Delete dead Placeholder.cs files in Marathon.Application and
  Marathon.Infrastructure — they were stubs from Phase 1 to satisfy
  "non-empty project" and have been dead since Phase 2/3.

* EventBrowsingService — drop the bespoke ScopeEqualityComparer; BetScope
  is a record hierarchy, .GroupBy uses value equality natively.

* UserAgentRotatorHandler — counter promoted to private static int with
  Interlocked.Increment so rotation is round-robin across the process.
  HttpClientFactory builds the handler Transient, so the previous instance
  field reset to zero on every new client and broke rotation.

* EventOddsParser — added a parallel "selection-key → IElement" index
  alongside the existing price index. Handicap extraction (6 call sites
  per event detail page) used to do a fresh document.QuerySelector("span[
  data-selection-key='...']") for every key — full-document CSS traversal.
  Now it's a dictionary lookup, with the pair-emit logic factored into a
  shared TryEmitHandicapPair helper.
2026-05-09 15:45:18 +03:00

maraphon-app

Sports betting odds analyzer for marathonbet.by.

Scrapes pre-match (/su) and live (/su/live) sports events, tracks coefficient changes over time, and detects anomalies — in particular the "odds-flip" pattern where the bookmaker freezes betting and then inverts underdog/favorite odds.

Tech stack

  • .NET 8 + C# 12
  • Blazor Hybrid — WPF shell hosting BlazorWebView (designed to migrate to ASP.NET Core Blazor Server with no UI rewrite)
  • EF Core + SQLite (WAL mode) for local storage
  • ClosedXML for Excel export
  • AngleSharp for HTML scraping (with Playwright fallback for JS-rendered pages)
  • Polly v8 for retry / circuit breaker / rate limiting
  • MudBlazor UI components, Plotly.Blazor for charts
  • Serilog structured logging
  • xUnit / FluentAssertions / NSubstitute for tests

Project layout

src/
  Marathon.Domain/         entities, value objects, no dependencies
  Marathon.Application/    use cases, abstractions (IOddsScraper, IRepository, ...)
  Marathon.Infrastructure/ EF Core, scraping, Polly, Excel, Playwright
  Marathon.UI/             Razor Class Library — all Blazor components live here
  Marathon.Hosts.WpfBlazor/ WPF + BlazorWebView host (replaceable for web)
tests/
  Marathon.*.Tests/        unit + integration tests per layer

Build & run

dotnet build Marathon.sln
dotnet test  Marathon.sln
dotnet run   --project src/Marathon.Hosts.WpfBlazor

Configuration

All variable parameters (polling intervals, concurrency, user-agents, retry policy, snapshot retention, locale) are exposed via appsettings.json and live-editable via the in-app Settings page.

Status

🟡 In active development. See plans/initial-implementation/PLAN.md for the current phase plan and progress.

License

Private — customer project.

S
Description
No description provided
Readme 1.8 MiB
Languages
HTML 54.3%
C# 45%
CSS 0.7%