diff --git a/containers/backupchecks/src/backend/app/cove_importer.py b/containers/backupchecks/src/backend/app/cove_importer.py index 27371d8..2f3b06a 100644 --- a/containers/backupchecks/src/backend/app/cove_importer.py +++ b/containers/backupchecks/src/backend/app/cove_importer.py @@ -360,7 +360,13 @@ def _process_account(account: dict) -> bool: computer_name = (flat.get("I18") or "").strip() or None customer_name = (flat.get("I8") or "").strip() or None datasource_types = (flat.get("I78") or "").strip() or None - last_run_at = _ts_to_dt(flat.get("D09F15")) + # Prefer "last session end" (D09F15); fallback to "last successful session" (D09F09) + # so accounts without D09F15 can still produce an initial run. + last_run_ts_raw = flat.get("D09F15") + last_run_at = _ts_to_dt(last_run_ts_raw) + if last_run_at is None: + last_run_ts_raw = flat.get("D09F09") + last_run_at = _ts_to_dt(last_run_ts_raw) colorbar_28d = (flat.get("D09F08") or "").strip() or None try: last_status_code = int(flat["D09F00"]) if flat.get("D09F00") is not None else None @@ -397,7 +403,10 @@ def _process_account(account: dict) -> bool: db.session.commit() return False - run_ts = int(flat.get("D09F15", 0)) + try: + run_ts = int(last_run_ts_raw or 0) + except (TypeError, ValueError): + run_ts = 0 external_id = f"cove-{account_id}-{run_ts}" existing = JobRun.query.filter_by(external_id=external_id).first() diff --git a/containers/backupchecks/src/backend/app/main/routes_cove.py b/containers/backupchecks/src/backend/app/main/routes_cove.py index 9dfa459..25905a2 100644 --- a/containers/backupchecks/src/backend/app/main/routes_cove.py +++ b/containers/backupchecks/src/backend/app/main/routes_cove.py @@ -11,7 +11,7 @@ from .routes_shared import * # noqa: F401,F403 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, JobRun, SystemSettings _COVE_DATASOURCE_LABELS = { @@ -215,8 +215,25 @@ def cove_account_link(cove_account_db_id: int): # after linking (instead of waiting for the next scheduled/manual import). settings = SystemSettings.query.first() if settings and getattr(settings, "cove_enabled", False): + linked_job_id = cove_acc.job_id + before_count = 0 + if linked_job_id: + before_count = ( + JobRun.query + .filter_by(job_id=linked_job_id, source_type="cove_api") + .count() + ) try: total, created, skipped, errors = run_cove_import(settings) + after_count = 0 + if linked_job_id: + after_count = ( + JobRun.query + .filter_by(job_id=linked_job_id, source_type="cove_api") + .count() + ) + linked_created = max(after_count - before_count, 0) + _log_admin_event( "cove_import_after_link", ( @@ -224,19 +241,27 @@ def cove_account_link(cove_account_db_id: int): f"accounts={total}, created={created}, skipped={skipped}, errors={errors}" ), ) - if created > 0: + if linked_created > 0: flash( ( f"Immediate import complete for '{linked_job_name}'. " - f"New runs: {created} (accounts: {total}, skipped: {skipped}, errors: {errors})." + f"New linked runs: {linked_created} (accounts: {total}, skipped: {skipped}, errors: {errors})." ), "success" if errors == 0 else "warning", ) else: + latest_cove = CoveAccount.query.get(cove_acc.id) + if latest_cove and latest_cove.last_run_at: + reason = ( + "latest run seems unchanged (already imported) " + "or Cove has not published a newer session yet" + ) + else: + reason = "Cove returned no usable last-session timestamp yet for this account" flash( ( f"Immediate import complete for '{linked_job_name}', but no new run was found yet. " - f"(accounts: {total}, skipped: {skipped}, errors: {errors})" + f"Reason: {reason}. (accounts: {total}, skipped: {skipped}, errors: {errors})" ), "info" if errors == 0 else "warning", )