First product
This commit is contained in:
parent
778da9e2fc
commit
4433d7d2c4
157 changed files with 23975 additions and 0 deletions
108
app/presentation/dashboard.py
Normal file
108
app/presentation/dashboard.py
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
from typing import Sequence
|
||||
|
||||
from app.db.models import CrawlRun
|
||||
from app.services.publications import UnreadPublicationItem
|
||||
|
||||
SECTION_TEMPLATES: tuple[str, ...] = (
|
||||
"dashboard/_run_controls.html",
|
||||
"dashboard/_unread_publications.html",
|
||||
"dashboard/_run_history.html",
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunControlsViewModel:
|
||||
request_delay_seconds: int
|
||||
queue_queued_count: int
|
||||
queue_retrying_count: int
|
||||
queue_dropped_count: int
|
||||
run_manual_action: str = "/runs/manual"
|
||||
mark_all_read_action: str = "/publications/mark-all-read"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class UnreadPublicationViewModel:
|
||||
title: str
|
||||
scholar_label: str
|
||||
year_display: str
|
||||
citation_count: int
|
||||
venue_text: str | None
|
||||
pub_url: str | None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RunHistoryItemViewModel:
|
||||
run_id: int | None
|
||||
detail_url: str | None
|
||||
started_at_display: str
|
||||
status_label: str
|
||||
status_badge: str
|
||||
scholar_count: int
|
||||
new_publication_count: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DashboardViewModel:
|
||||
section_templates: tuple[str, ...]
|
||||
run_controls: RunControlsViewModel
|
||||
unread_publications: list[UnreadPublicationViewModel]
|
||||
run_history: list[RunHistoryItemViewModel]
|
||||
|
||||
|
||||
def build_dashboard_view_model(
|
||||
*,
|
||||
unread_publications: Sequence[UnreadPublicationItem],
|
||||
recent_runs: Sequence[CrawlRun],
|
||||
request_delay_seconds: int,
|
||||
queue_counts: dict[str, int] | None = None,
|
||||
) -> DashboardViewModel:
|
||||
queue_counts = queue_counts or {}
|
||||
return DashboardViewModel(
|
||||
section_templates=SECTION_TEMPLATES,
|
||||
run_controls=RunControlsViewModel(
|
||||
request_delay_seconds=request_delay_seconds,
|
||||
queue_queued_count=int(queue_counts.get("queued", 0)),
|
||||
queue_retrying_count=int(queue_counts.get("retrying", 0)),
|
||||
queue_dropped_count=int(queue_counts.get("dropped", 0)),
|
||||
),
|
||||
unread_publications=[
|
||||
UnreadPublicationViewModel(
|
||||
title=item.title,
|
||||
scholar_label=item.scholar_label,
|
||||
year_display=str(item.year) if item.year is not None else "-",
|
||||
citation_count=item.citation_count,
|
||||
venue_text=item.venue_text,
|
||||
pub_url=item.pub_url,
|
||||
)
|
||||
for item in unread_publications
|
||||
],
|
||||
run_history=[
|
||||
RunHistoryItemViewModel(
|
||||
run_id=run.id,
|
||||
detail_url=f"/runs/{run.id}" if run.id is not None else None,
|
||||
started_at_display=_format_started_at(run.start_dt),
|
||||
status_label=run.status.value,
|
||||
status_badge=_status_badge(run.status.value),
|
||||
scholar_count=run.scholar_count,
|
||||
new_publication_count=run.new_pub_count,
|
||||
)
|
||||
for run in recent_runs
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def _status_badge(status: str) -> str:
|
||||
if status == "success":
|
||||
return "ok"
|
||||
if status == "failed":
|
||||
return "danger"
|
||||
return "warn"
|
||||
|
||||
|
||||
def _format_started_at(dt: datetime) -> str:
|
||||
local_aware = dt.astimezone(timezone.utc)
|
||||
return local_aware.strftime("%Y-%m-%d %H:%M UTC")
|
||||
Loading…
Add table
Add a link
Reference in a new issue