167 lines
5.6 KiB
Python
167 lines
5.6 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from fastapi import APIRouter, Depends, Request
|
|
from fastapi.responses import HTMLResponse
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.db.models import RunTriggerType
|
|
from app.db.session import get_db_session
|
|
from app.presentation import dashboard as dashboard_presenter
|
|
from app.services import ingestion as ingestion_service
|
|
from app.services import publications as publication_service
|
|
from app.services import runs as run_service
|
|
from app.services import user_settings as user_settings_service
|
|
from app.settings import settings
|
|
from app.theme import resolve_theme
|
|
from app.web import common
|
|
from app.web.deps import get_ingestion_service
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
async def _render_dashboard_page(
|
|
request: Request,
|
|
*,
|
|
db_session: AsyncSession,
|
|
current_user,
|
|
theme_name: str,
|
|
notice: str | None = None,
|
|
page_error: str | None = None,
|
|
) -> HTMLResponse:
|
|
unread_publications = await publication_service.list_new_for_latest_run_for_user(
|
|
db_session,
|
|
user_id=current_user.id,
|
|
limit=50,
|
|
)
|
|
recent_runs = await run_service.list_recent_runs_for_user(
|
|
db_session,
|
|
user_id=current_user.id,
|
|
limit=20,
|
|
)
|
|
user_settings = await user_settings_service.get_or_create_settings(
|
|
db_session,
|
|
user_id=current_user.id,
|
|
)
|
|
queue_counts = await run_service.queue_status_counts_for_user(
|
|
db_session,
|
|
user_id=current_user.id,
|
|
)
|
|
context = common.build_template_context(
|
|
request,
|
|
page_title="Dashboard",
|
|
active_nav="home",
|
|
theme_name=theme_name,
|
|
session_user=common.to_session_user(current_user),
|
|
notice=notice,
|
|
page_error=page_error,
|
|
)
|
|
context["dashboard"] = dashboard_presenter.build_dashboard_view_model(
|
|
unread_publications=unread_publications,
|
|
recent_runs=recent_runs,
|
|
request_delay_seconds=user_settings.request_delay_seconds,
|
|
queue_counts=queue_counts,
|
|
)
|
|
return common.templates.TemplateResponse(
|
|
request=request,
|
|
name="index.html",
|
|
context=context,
|
|
)
|
|
|
|
|
|
@router.get("/", response_class=HTMLResponse)
|
|
async def home(
|
|
request: Request,
|
|
theme: str | None = None,
|
|
db_session: AsyncSession = Depends(get_db_session),
|
|
) -> HTMLResponse:
|
|
current_user = await common.get_authenticated_user(request, db_session)
|
|
if current_user is None:
|
|
return common.redirect_to_login()
|
|
return await _render_dashboard_page(
|
|
request,
|
|
db_session=db_session,
|
|
current_user=current_user,
|
|
theme_name=resolve_theme(theme),
|
|
notice=request.query_params.get("notice"),
|
|
page_error=request.query_params.get("error"),
|
|
)
|
|
|
|
|
|
@router.post("/runs/manual")
|
|
async def run_manual_ingestion(
|
|
request: Request,
|
|
db_session: AsyncSession = Depends(get_db_session),
|
|
ingest_service: ingestion_service.ScholarIngestionService = Depends(get_ingestion_service),
|
|
):
|
|
current_user = await common.get_authenticated_user(request, db_session)
|
|
if current_user is None:
|
|
return common.redirect_to_login()
|
|
|
|
user_settings = await user_settings_service.get_or_create_settings(
|
|
db_session,
|
|
user_id=current_user.id,
|
|
)
|
|
logger.info(
|
|
"runs.manual_started",
|
|
extra={
|
|
"event": "runs.manual_started",
|
|
"user_id": current_user.id,
|
|
"request_delay_seconds": user_settings.request_delay_seconds,
|
|
"network_error_retries": settings.ingestion_network_error_retries,
|
|
"max_pages_per_scholar": settings.ingestion_max_pages_per_scholar,
|
|
"page_size": settings.ingestion_page_size,
|
|
},
|
|
)
|
|
try:
|
|
run_summary = await ingest_service.run_for_user(
|
|
db_session,
|
|
user_id=current_user.id,
|
|
trigger_type=RunTriggerType.MANUAL,
|
|
request_delay_seconds=user_settings.request_delay_seconds,
|
|
network_error_retries=settings.ingestion_network_error_retries,
|
|
retry_backoff_seconds=settings.ingestion_retry_backoff_seconds,
|
|
max_pages_per_scholar=settings.ingestion_max_pages_per_scholar,
|
|
page_size=settings.ingestion_page_size,
|
|
auto_queue_continuations=settings.ingestion_continuation_queue_enabled,
|
|
queue_delay_seconds=settings.ingestion_continuation_base_delay_seconds,
|
|
)
|
|
except ingestion_service.RunAlreadyInProgressError:
|
|
await db_session.rollback()
|
|
return common.redirect_with_message(
|
|
"/",
|
|
error="A run is already in progress for this account.",
|
|
)
|
|
except Exception:
|
|
await db_session.rollback()
|
|
logger.exception(
|
|
"runs.manual_failed",
|
|
extra={
|
|
"event": "runs.manual_failed",
|
|
"user_id": current_user.id,
|
|
},
|
|
)
|
|
return common.redirect_with_message("/", error="Manual run failed. Check logs for details.")
|
|
|
|
logger.info(
|
|
"runs.manual_completed",
|
|
extra={
|
|
"event": "runs.manual_completed",
|
|
"user_id": current_user.id,
|
|
"run_id": run_summary.crawl_run_id,
|
|
"status": run_summary.status.value,
|
|
"scholar_count": run_summary.scholar_count,
|
|
"new_publication_count": run_summary.new_publication_count,
|
|
},
|
|
)
|
|
return common.redirect_with_message(
|
|
"/",
|
|
notice=(
|
|
f"Run #{run_summary.crawl_run_id} complete ({run_summary.status.value}). "
|
|
f"Scholars: {run_summary.scholar_count}, "
|
|
f"new publications: {run_summary.new_publication_count}."
|
|
),
|
|
)
|