Making OData Refresh a background process
This commit is contained in:
1 parent
2e7976e382
commit
cf8d72f73e
4 files changed
+157
-34
No files matched your search
@@ -29,11 +29,9 @@
|
||||
<button class="btn btn-primary"
|
||||
hx-post="/admin/imported-parts/refresh"
|
||||
hx-target="#imported-parts-section"
|
||||
hx-swap="outerHTML"
|
||||
hx-indicator="#refresh-indicator">
|
||||
hx-swap="outerHTML">
|
||||
Refresh from OData
|
||||
</button>
|
||||
<span id="refresh-indicator" class="htmx-indicator muted"> Loading…</span>
|
||||
</div>
|
||||
|
||||
{% include "admin/_imported_parts_table.html" %}
|
||||
|
||||
+92
-30
@@ -1366,6 +1366,19 @@ async def save_imported_part_config(
|
||||
return HTMLResponse("Configuration saved.", status_code=200)
|
||||
|
||||
|
||||
_IMPORTED_PARTS_REFRESH_TARGET_ID = "imported-parts-section"
|
||||
|
||||
|
||||
async def _imported_parts_table_ctx(
|
||||
db: AsyncSession, *, sync_result: str | None = None, sync_error: str | None = None
|
||||
) -> dict:
|
||||
return {
|
||||
**await _default_imported_parts_table_ctx(db),
|
||||
"sync_result": sync_result,
|
||||
"sync_error": sync_error,
|
||||
}
|
||||
|
||||
|
||||
@router.post("/imported-parts/refresh", response_class=HTMLResponse)
|
||||
async def refresh_imported_parts(
|
||||
request: Request,
|
||||
@@ -1373,46 +1386,95 @@ async def refresh_imported_parts(
|
||||
actor: User = Depends(web_require("imported_parts:write")),
|
||||
):
|
||||
config = await imported_part_config_crud.get(db)
|
||||
|
||||
async def _imported_table_ctx(sync_result=None, sync_error=None):
|
||||
return {
|
||||
**await _default_imported_parts_table_ctx(db),
|
||||
"sync_result": sync_result,
|
||||
"sync_error": sync_error,
|
||||
}
|
||||
|
||||
if not config or not config.odata_url:
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_imported_parts_table.html",
|
||||
await _imported_table_ctx(sync_error="OData URL is not configured."),
|
||||
)
|
||||
try:
|
||||
records = await odata_service.fetch(
|
||||
config.odata_url, config.username, config.password
|
||||
)
|
||||
created, updated = await imported_part_crud.upsert_bulk(db, records)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor,
|
||||
action="update",
|
||||
resource_type="ImportedPart",
|
||||
resource_id=None,
|
||||
description=f"OData sync: {created} created, {updated} updated ({len(records)} total records)",
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_imported_parts_table.html",
|
||||
await _imported_table_ctx(
|
||||
sync_result=f"Sync complete: {created} created, {updated} updated."
|
||||
await _imported_parts_table_ctx(
|
||||
db, sync_error="OData URL is not configured."
|
||||
),
|
||||
)
|
||||
except Exception as exc:
|
||||
|
||||
actor_id = actor.id
|
||||
|
||||
async def _run(job_id: str) -> None:
|
||||
async with AsyncSessionLocal() as db:
|
||||
config = await imported_part_config_crud.get(db)
|
||||
if not config or not config.odata_url:
|
||||
await jobs.mark_error(job_id, "OData URL is not configured.")
|
||||
return
|
||||
try:
|
||||
records = await odata_service.fetch(
|
||||
config.odata_url, config.username, config.password
|
||||
)
|
||||
created, updated = await imported_part_crud.upsert_bulk(db, records)
|
||||
except Exception as exc:
|
||||
await jobs.mark_error(job_id, f"Sync failed: {exc}")
|
||||
return
|
||||
actor_row = await user_crud.get(db, actor_id)
|
||||
await audit_crud.log(
|
||||
db,
|
||||
actor=actor_row,
|
||||
action="update",
|
||||
resource_type="ImportedPart",
|
||||
resource_id=None,
|
||||
description=(
|
||||
f"OData sync: {created} created, {updated} updated "
|
||||
f"({len(records)} total records)"
|
||||
),
|
||||
)
|
||||
await jobs.mark_done(
|
||||
job_id,
|
||||
message=f"Sync complete: {created} created, {updated} updated.",
|
||||
)
|
||||
|
||||
job = await jobs.start("imported_parts_refresh", _run)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_job_progress.html",
|
||||
{
|
||||
"container_id": _IMPORTED_PARTS_REFRESH_TARGET_ID,
|
||||
"poll_url": f"/admin/imported-parts/refresh/jobs/{job.id}",
|
||||
"job": job,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/imported-parts/refresh/jobs/{job_id}", response_class=HTMLResponse)
|
||||
async def imported_parts_refresh_job_status(
|
||||
request: Request,
|
||||
job_id: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
_: User = Depends(web_require("imported_parts:write")),
|
||||
):
|
||||
job = await jobs.get(db, job_id)
|
||||
if job is None:
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_imported_parts_table.html",
|
||||
await _imported_table_ctx(sync_error=f"Sync failed: {exc}"),
|
||||
await _imported_parts_table_ctx(
|
||||
db, sync_error="Refresh job not found — it may have expired."
|
||||
),
|
||||
)
|
||||
if job.status == "running":
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_job_progress.html",
|
||||
{
|
||||
"container_id": _IMPORTED_PARTS_REFRESH_TARGET_ID,
|
||||
"poll_url": f"/admin/imported-parts/refresh/jobs/{job_id}",
|
||||
"job": job,
|
||||
},
|
||||
)
|
||||
return templates.TemplateResponse(
|
||||
request,
|
||||
"admin/_imported_parts_table.html",
|
||||
await _imported_parts_table_ctx(
|
||||
db,
|
||||
sync_result=job.message if job.status == "done" else None,
|
||||
sync_error=job.message if job.status == "error" else None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@router.get("/imported-parts/filter", response_class=HTMLResponse)
|
||||
|
||||
Reference in new issue
Block a user