feat: add date-based OHIP data reporting alongside XML

This commit is contained in:
Wyndham ARR committed 2026-09-18 15:29:27 +08:00
1 parent 5ecd571e3b
commit ad2c59256f
259 files changed
+37326 -126

No files matched your search

+42
View File
@@ -39,6 +39,10 @@ from arr_web.repository import (
UnavailablePortalRepository,
)
from arr_web.downloads import ArtifactReader, UnavailableArtifactReader
from arr_web.arr_downloads import (
ARRDownloadCoordinator, UnavailableARRDownloads, default_report_date,
validate_report_date, validate_request_id,
)
from arr_web.company_jobs import (
COMPANY_NAMES,
CompanyReportCoordinator,
@@ -249,6 +253,7 @@ class PortalApplication:
credentials: Optional[LoginCredentials] = None,
login_attempts: Optional[LoginAttemptLedger] = None,
secure_cookies: bool = False,
arr_downloads: Optional[ARRDownloadCoordinator] = None,
) -> None:
self._repository = repository or UnavailablePortalRepository()
self._upload = upload or UnavailableUploadCoordinator()
@@ -267,6 +272,7 @@ class PortalApplication:
self._credentials = credentials
self._login_attempts = login_attempts or LoginAttemptLedger()
self._secure_cookies = secure_cookies
self._arr_downloads = arr_downloads or UnavailableARRDownloads()
def handle(
self,
@@ -345,6 +351,7 @@ class PortalApplication:
{
"database_ready": self._health.database_ready,
"processing_ready": self._health.upload_ready,
"arr_download_ready": self._arr_downloads.ready,
"monthly_ready": self._health.monthly_ready,
"download_ready": self._health.download_ready,
"company_reports_ready": self._health.company_reports_ready,
@@ -354,6 +361,31 @@ class PortalApplication:
}
),
)
if method == "GET" and route.path == "/api/arr-downloads":
return Response.json(200, success({
"ready": self._arr_downloads.ready,
"default_date": default_report_date(),
"business_time_zone": "Asia/Bangkok",
"latest_task": self._arr_download_snapshot(self._arr_downloads.latest()),
}))
if method == "POST" and route.path == "/api/arr-downloads":
self._require_csrf(normalized_headers)
payload = _strict_json(body)
if set(payload) != {"report_date", "request_id"}:
raise PortalError("ARR_DOWNLOAD_REQUEST_INVALID", "请仅提交一个报表日期和任务编号")
day = validate_report_date(payload["report_date"])
request_id = validate_request_id(payload["request_id"])
return Response.json(202, success(self._arr_downloads.create(day, request_id)))
if route.path.startswith("/api/arr-downloads/"):
parts = route.path[len("/api/arr-downloads/"):].split("/")
request_id = validate_request_id(parts[0])
if method == "GET" and len(parts) == 1:
return Response.json(200, success(self._arr_download_snapshot(self._arr_downloads.get(request_id))))
if method == "POST" and len(parts) == 2 and parts[1] == "retry":
self._require_csrf(normalized_headers)
if body and _strict_json(body):
raise PortalError("ARR_DOWNLOAD_REQUEST_INVALID", "重试沿用原任务日期")
return Response.json(202, success(self._arr_downloads.retry(request_id)))
if method == "GET" and route.path == "/api/jobs":
query = parse_qs(route.query, keep_blank_values=True)
month = validate_month(self._one(query, "month", self._current_month()))
@@ -830,6 +862,16 @@ class PortalApplication:
failure(PortalError("INTERNAL_ERROR", "服务暂时无法完成请求", 500)),
)
def _arr_download_snapshot(self, task: Optional[dict]) -> Optional[dict]:
if task and task.get("status") == "needs_review" and task.get("job_id"):
try:
status = self._repository.get_job_trace(task["job_id"])["job"]["status"]
except (PortalDataError, KeyError):
return task
if status in {"accepted", "succeeded", "failed", "rejected", "cancelled"}:
return {**task, "status": "succeeded" if status in {"accepted", "succeeded"} else "failed", "can_retry": False}
return task
def _readiness(self) -> Response:
ready = self._health.database_ready and self._health.upload_ready
return Response(