Trigger immediate Cove import on link and enrich run details
This commit is contained in:
parent
06abd8c7a3
commit
f68f92e63a
@ -59,6 +59,21 @@ STATUS_MAP: dict[int, str] = {
|
|||||||
12: "Warning", # Restarted
|
12: "Warning", # Restarted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Mapping from Cove status code to readable label
|
||||||
|
STATUS_LABELS: dict[int, str] = {
|
||||||
|
1: "In process",
|
||||||
|
2: "Failed",
|
||||||
|
3: "Aborted",
|
||||||
|
5: "Completed",
|
||||||
|
6: "Interrupted",
|
||||||
|
7: "Not started",
|
||||||
|
8: "Completed with errors",
|
||||||
|
9: "In progress with faults",
|
||||||
|
10: "Over quota",
|
||||||
|
11: "No selection",
|
||||||
|
12: "Restarted",
|
||||||
|
}
|
||||||
|
|
||||||
# Datasource label mapping (column prefix → human-readable label)
|
# Datasource label mapping (column prefix → human-readable label)
|
||||||
DATASOURCE_LABELS: dict[str, str] = {
|
DATASOURCE_LABELS: dict[str, str] = {
|
||||||
"D1": "Files & Folders",
|
"D1": "Files & Folders",
|
||||||
@ -210,6 +225,16 @@ def _map_status(code: Any) -> str:
|
|||||||
return "Warning"
|
return "Warning"
|
||||||
|
|
||||||
|
|
||||||
|
def _status_label(code: Any) -> str:
|
||||||
|
"""Map a Cove status code (int) to a human-readable label."""
|
||||||
|
if code is None:
|
||||||
|
return "Unknown"
|
||||||
|
try:
|
||||||
|
return STATUS_LABELS.get(int(code), f"Code {int(code)}")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return "Unknown"
|
||||||
|
|
||||||
|
|
||||||
def _ts_to_dt(value: Any) -> datetime | None:
|
def _ts_to_dt(value: Any) -> datetime | None:
|
||||||
"""Convert a Unix timestamp (int or str) to a naive UTC datetime."""
|
"""Convert a Unix timestamp (int or str) to a naive UTC datetime."""
|
||||||
if value is None:
|
if value is None:
|
||||||
@ -223,6 +248,13 @@ def _ts_to_dt(value: Any) -> datetime | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _fmt_utc(dt: datetime | None) -> str:
|
||||||
|
"""Format a naive UTC datetime to readable text for run object messages."""
|
||||||
|
if not dt:
|
||||||
|
return "unknown"
|
||||||
|
return dt.strftime("%Y-%m-%d %H:%M UTC")
|
||||||
|
|
||||||
|
|
||||||
def run_cove_import(settings) -> tuple[int, int, int, int]:
|
def run_cove_import(settings) -> tuple[int, int, int, int]:
|
||||||
"""Fetch Cove account statistics and update the staging table + JobRuns.
|
"""Fetch Cove account statistics and update the staging table + JobRuns.
|
||||||
|
|
||||||
@ -381,13 +413,20 @@ def _process_account(account: dict) -> bool:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
status = _map_status(last_status_code)
|
status = _map_status(last_status_code)
|
||||||
|
run_remark = (
|
||||||
|
f"Cove account: {account_name or account_id} | "
|
||||||
|
f"Computer: {computer_name or '-'} | "
|
||||||
|
f"Customer: {customer_name or '-'} | "
|
||||||
|
f"Last status: {_status_label(last_status_code)} ({last_status_code if last_status_code is not None else '-'}) | "
|
||||||
|
f"Last run: {_fmt_utc(last_run_at)}"
|
||||||
|
)
|
||||||
|
|
||||||
run = JobRun(
|
run = JobRun(
|
||||||
job_id=job.id,
|
job_id=job.id,
|
||||||
mail_message_id=None,
|
mail_message_id=None,
|
||||||
run_at=last_run_at,
|
run_at=last_run_at,
|
||||||
status=status,
|
status=status,
|
||||||
remark=None,
|
remark=run_remark,
|
||||||
missed=False,
|
missed=False,
|
||||||
override_applied=False,
|
override_applied=False,
|
||||||
source_type="cove_api",
|
source_type="cove_api",
|
||||||
@ -422,6 +461,11 @@ def _persist_datasource_objects(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
status = _map_status(status_code)
|
status = _map_status(status_code)
|
||||||
|
ds_last_ts = _ts_to_dt(flat.get(f"{ds_prefix}F15"))
|
||||||
|
status_msg = (
|
||||||
|
f"Cove datasource status: {_status_label(status_code)} "
|
||||||
|
f"({status_code}); last session: {_fmt_utc(ds_last_ts)}"
|
||||||
|
)
|
||||||
|
|
||||||
# Upsert customer_objects
|
# Upsert customer_objects
|
||||||
customer_object_id = conn.execute(
|
customer_object_id = conn.execute(
|
||||||
@ -461,10 +505,11 @@ def _persist_datasource_objects(
|
|||||||
text(
|
text(
|
||||||
"""
|
"""
|
||||||
INSERT INTO run_object_links (run_id, customer_object_id, status, error_message, observed_at)
|
INSERT INTO run_object_links (run_id, customer_object_id, status, error_message, observed_at)
|
||||||
VALUES (:run_id, :customer_object_id, :status, NULL, :observed_at)
|
VALUES (:run_id, :customer_object_id, :status, :error_message, :observed_at)
|
||||||
ON CONFLICT (run_id, customer_object_id)
|
ON CONFLICT (run_id, customer_object_id)
|
||||||
DO UPDATE SET
|
DO UPDATE SET
|
||||||
status = EXCLUDED.status,
|
status = EXCLUDED.status,
|
||||||
|
error_message = EXCLUDED.error_message,
|
||||||
observed_at = EXCLUDED.observed_at
|
observed_at = EXCLUDED.observed_at
|
||||||
"""
|
"""
|
||||||
),
|
),
|
||||||
@ -472,6 +517,7 @@ def _persist_datasource_objects(
|
|||||||
"run_id": run_id,
|
"run_id": run_id,
|
||||||
"customer_object_id": customer_object_id,
|
"customer_object_id": customer_object_id,
|
||||||
"status": status,
|
"status": status,
|
||||||
"observed_at": observed_at,
|
"error_message": status_msg,
|
||||||
|
"observed_at": ds_last_ts or observed_at,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import re
|
|||||||
|
|
||||||
from .routes_shared import * # noqa: F401,F403
|
from .routes_shared import * # noqa: F401,F403
|
||||||
from .routes_shared import _log_admin_event
|
from .routes_shared import _log_admin_event
|
||||||
|
from ..cove_importer import CoveImportError, run_cove_import
|
||||||
|
|
||||||
from ..models import CoveAccount, Customer, Job, SystemSettings
|
from ..models import CoveAccount, Customer, Job, SystemSettings
|
||||||
|
|
||||||
@ -129,6 +130,8 @@ def cove_account_link(cove_account_db_id: int):
|
|||||||
|
|
||||||
action = (request.form.get("action") or "").strip() # "create" or "link"
|
action = (request.form.get("action") or "").strip() # "create" or "link"
|
||||||
|
|
||||||
|
linked_job_name = ""
|
||||||
|
|
||||||
if action == "create":
|
if action == "create":
|
||||||
# Create a new job from the Cove account data
|
# Create a new job from the Cove account data
|
||||||
customer_id_raw = (request.form.get("customer_id") or "").strip()
|
customer_id_raw = (request.form.get("customer_id") or "").strip()
|
||||||
@ -171,11 +174,8 @@ def cove_account_link(cove_account_db_id: int):
|
|||||||
f"Created job {job.id} and linked Cove account {cove_acc.account_id} ({cove_acc.account_name})",
|
f"Created job {job.id} and linked Cove account {cove_acc.account_id} ({cove_acc.account_name})",
|
||||||
details=f"customer={customer.name}, job_name={job_name}",
|
details=f"customer={customer.name}, job_name={job_name}",
|
||||||
)
|
)
|
||||||
flash(
|
linked_job_name = job_name
|
||||||
f"Job '{job_name}' created for customer '{customer.name}'. "
|
flash(f"Job '{job_name}' created for customer '{customer.name}'.", "success")
|
||||||
"Runs will appear after the next Cove import.",
|
|
||||||
"success",
|
|
||||||
)
|
|
||||||
|
|
||||||
elif action == "link":
|
elif action == "link":
|
||||||
# Link to an existing job
|
# Link to an existing job
|
||||||
@ -204,14 +204,62 @@ def cove_account_link(cove_account_db_id: int):
|
|||||||
f"Linked Cove account {cove_acc.account_id} ({cove_acc.account_name}) to existing job {job.id}",
|
f"Linked Cove account {cove_acc.account_id} ({cove_acc.account_name}) to existing job {job.id}",
|
||||||
details=f"job_name={job.job_name}",
|
details=f"job_name={job.job_name}",
|
||||||
)
|
)
|
||||||
flash(
|
linked_job_name = job.job_name or ""
|
||||||
f"Cove account linked to job '{job.job_name}'. "
|
flash(f"Cove account linked to job '{job.job_name}'.", "success")
|
||||||
"Runs will appear after the next Cove import.",
|
|
||||||
"success",
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
flash("Unknown action.", "warning")
|
flash("Unknown action.", "warning")
|
||||||
|
return redirect(url_for("main.cove_accounts"))
|
||||||
|
|
||||||
|
# Trigger an immediate import so the latest Cove run appears right away
|
||||||
|
# after linking (instead of waiting for the next scheduled/manual import).
|
||||||
|
settings = SystemSettings.query.first()
|
||||||
|
if settings and getattr(settings, "cove_enabled", False):
|
||||||
|
try:
|
||||||
|
total, created, skipped, errors = run_cove_import(settings)
|
||||||
|
_log_admin_event(
|
||||||
|
"cove_import_after_link",
|
||||||
|
(
|
||||||
|
"Triggered immediate Cove import after account link. "
|
||||||
|
f"accounts={total}, created={created}, skipped={skipped}, errors={errors}"
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if created > 0:
|
||||||
|
flash(
|
||||||
|
(
|
||||||
|
f"Immediate import complete for '{linked_job_name}'. "
|
||||||
|
f"New runs: {created} (accounts: {total}, skipped: {skipped}, errors: {errors})."
|
||||||
|
),
|
||||||
|
"success" if errors == 0 else "warning",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
flash(
|
||||||
|
(
|
||||||
|
f"Immediate import complete for '{linked_job_name}', but no new run was found yet. "
|
||||||
|
f"(accounts: {total}, skipped: {skipped}, errors: {errors})"
|
||||||
|
),
|
||||||
|
"info" if errors == 0 else "warning",
|
||||||
|
)
|
||||||
|
except CoveImportError as exc:
|
||||||
|
_log_admin_event(
|
||||||
|
"cove_import_after_link_error",
|
||||||
|
f"Immediate Cove import after account link failed: {exc}",
|
||||||
|
)
|
||||||
|
flash(
|
||||||
|
"Account linked, but immediate import failed. "
|
||||||
|
"You can run import again from Cove settings.",
|
||||||
|
"warning",
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
_log_admin_event(
|
||||||
|
"cove_import_after_link_error",
|
||||||
|
f"Unexpected immediate Cove import error after account link: {exc}",
|
||||||
|
)
|
||||||
|
flash(
|
||||||
|
"Account linked, but immediate import encountered an unexpected error. "
|
||||||
|
"You can run import again from Cove settings.",
|
||||||
|
"warning",
|
||||||
|
)
|
||||||
|
|
||||||
return redirect(url_for("main.cove_accounts"))
|
return redirect(url_for("main.cove_accounts"))
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user