Add scrape/ingestion engine + validation, and 24h shift hour-range visualization

Scrape engine (Services/Scraping/): pluggable IListingSource (working sample + Telegram/Divar credential-ready stubs) → IngestionService (content-hash dedupe → parse → validate → review queue) → ListingValidator (completeness score + spam screen) → IngestionWorker (config-gated hosted service). RawListing gains ContentHash/Confidence/ValidationNotes; RawListingStatus.Flagged. Admin /Admin gets run-now, source list, confidence + flagged queue.

Hour-range viz: _HourBar 24h timeline bar (colored by type, overnight wrap) on shift cards, recommendation cards, and detail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-03 08:18:19 +03:30
parent 69fa921fbd
commit 931b7b6ffb
24 changed files with 1439 additions and 26 deletions
@@ -0,0 +1,20 @@
@model JobsMedical.Web.Models.RawListing
@{
var c = Model.Confidence;
var confClass = c >= 70 ? "badge-verified" : c >= 50 ? "badge-day" : "badge-type";
}
<div class="card card-pad" style="margin-bottom:12px;">
<div class="row" style="display:flex; justify-content:space-between; align-items:center; gap:8px; flex-wrap:wrap;">
<strong>@Model.SourceChannel</strong>
<span style="display:flex; gap:8px; align-items:center;">
<span class="badge @confClass">اطمینان @JalaliDate.ToPersianDigits(c.ToString())٪</span>
<span class="muted" style="font-size:12px;">@JalaliDate.ToLongDate(DateOnly.FromDateTime(Model.FetchedAt))</span>
</span>
</div>
<p style="margin:10px 0; white-space:pre-wrap;">@Model.RawText</p>
@if (!string.IsNullOrEmpty(Model.ValidationNotes))
{
<p class="muted" style="font-size:12.5px; margin:0 0 10px;">⚠ @Model.ValidationNotes</p>
}
<a class="btn btn-accent" asp-page="/Admin/Review" asp-route-id="@Model.Id">بررسی و انتشار ←</a>
</div>