diff --git a/containers/backupchecks/src/backend/app/main/routes_cove.py b/containers/backupchecks/src/backend/app/main/routes_cove.py index 7da261e..2c44e21 100644 --- a/containers/backupchecks/src/backend/app/main/routes_cove.py +++ b/containers/backupchecks/src/backend/app/main/routes_cove.py @@ -5,12 +5,57 @@ Mirrors the Inbox flow for mail messages: /cove/accounts//link – link an account to an existing or new job /cove/accounts//unlink – remove the job link """ +import re + from .routes_shared import * # noqa: F401,F403 from .routes_shared import _log_admin_event from ..models import CoveAccount, Customer, Job, SystemSettings +_COVE_DATASOURCE_LABELS = { + "D01": "Files & Folders", + "D1": "Files & Folders", + "D02": "System State", + "D2": "System State", + "D10": "VssMsSql", + "D11": "VssSharePoint", + "D19": "M365 Exchange", + "D20": "M365 OneDrive", + "D05": "M365 SharePoint", + "D5": "M365 SharePoint", + "D23": "M365 Teams", +} + +_COVE_M365_CODES = {"D19", "D20", "D05", "D5", "D23"} + + +def _parse_cove_datasource_codes(raw: str | None) -> list[str]: + """Extract datasource codes from Cove I78 strings like 'D01D02D10'.""" + text = (raw or "").strip().upper() + if not text: + return [] + return re.findall(r"D\d{1,2}", text) + + +def _derive_backup_type_for_account(cove_acc: CoveAccount) -> str: + """Return Backupchecks-style backup type for a Cove account.""" + codes = set(_parse_cove_datasource_codes(getattr(cove_acc, "datasource_types", None))) + if codes.intersection(_COVE_M365_CODES): + return "Microsoft 365" + return "Servers/Workstations" + + +def _humanize_datasources(raw: str | None) -> str: + """Return readable datasource labels from Cove I78 code string.""" + labels: list[str] = [] + for code in _parse_cove_datasource_codes(raw): + label = _COVE_DATASOURCE_LABELS.get(code, code) + if label not in labels: + labels.append(label) + return ", ".join(labels) + + @main_bp.route("/cove/accounts") @login_required @roles_required("admin", "operator") @@ -39,6 +84,12 @@ def cove_accounts(): customers = Customer.query.filter_by(active=True).order_by(Customer.name.asc()).all() jobs = Job.query.filter_by(archived=False).order_by(Job.job_name.asc()).all() + for acc in unmatched + matched: + acc.derived_backup_software = "Cove Data Protection" + acc.derived_backup_type = _derive_backup_type_for_account(acc) + acc.derived_job_name = (acc.account_name or acc.computer_name or f"Cove account {acc.account_id}").strip() + acc.datasource_display = _humanize_datasources(acc.datasource_types) or "—" + return render_template( "main/cove_accounts.html", unmatched=unmatched, @@ -87,8 +138,9 @@ def cove_account_link(cove_account_db_id: int): flash("Customer not found.", "danger") return redirect(url_for("main.cove_accounts")) - job_name = (request.form.get("job_name") or cove_acc.account_name or "").strip() - backup_type = (request.form.get("backup_type") or cove_acc.datasource_types or "Backup").strip() + default_job_name = (cove_acc.account_name or cove_acc.computer_name or f"Cove account {cove_acc.account_id}").strip() + job_name = (request.form.get("job_name") or default_job_name).strip() + backup_type = (request.form.get("backup_type") or _derive_backup_type_for_account(cove_acc)).strip() job = Job( customer_id=customer.id, diff --git a/containers/backupchecks/src/templates/main/cove_accounts.html b/containers/backupchecks/src/templates/main/cove_accounts.html index 9cfc4e4..3df1bd1 100644 --- a/containers/backupchecks/src/templates/main/cove_accounts.html +++ b/containers/backupchecks/src/templates/main/cove_accounts.html @@ -27,10 +27,12 @@ - + + + - + @@ -40,10 +42,12 @@ {% for acc in unmatched %} - + + + - +
Account nameBackup softwareTypeJob name Computer Customer (Cove)DatasourceDatasources Last status Last run First seen
{{ acc.account_name or '—' }}{{ acc.derived_backup_software }}{{ acc.derived_backup_type }}{{ acc.derived_job_name }} {{ acc.computer_name or '—' }} {{ acc.customer_name or '—' }}{{ acc.datasource_types or '—' }}{{ acc.datasource_display }} {% if acc.last_status_code is not none %} @@ -112,14 +116,14 @@
+ value="{{ acc.derived_job_name }}" />
Defaults to the Cove account name.
-
From Cove datasource info.
+ value="{{ acc.derived_backup_type }}" /> +
Derived from Cove datasource profile.
@@ -177,9 +181,11 @@ - + + + - + @@ -189,9 +195,11 @@ {% for acc in matched %} - + + + - +
Account nameBackup softwareTypeJob name Customer (Cove)DatasourceDatasources Last status Last run Linked job
{{ acc.account_name or '—' }}{{ acc.derived_backup_software }}{{ acc.derived_backup_type }}{{ acc.derived_job_name }} {{ acc.customer_name or '—' }}{{ acc.datasource_types or '—' }}{{ acc.datasource_display }} {% if acc.last_status_code is not none %}