Cloud Connect: add scan-inbox to process existing inbox mails

- POST /cloud-connect/accounts/scan-inbox: queries all inbox mails with
  backup_type 'cloud connect report', calls upsert_cloud_connect_report()
  for each, flashes a summary of accounts upserted and runs created
- 'Scan inbox mails' button added to Cloud Connect Accounts page header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ivo Oskamp 2026-03-19 17:17:14 +01:00
parent ea134f49f3
commit 4eac589625
2 changed files with 61 additions and 4 deletions

View File

@ -2,12 +2,14 @@
Mirrors the Cove Accounts flow: Mirrors the Cove Accounts flow:
/cloud-connect/accounts list all accounts (unmatched first) /cloud-connect/accounts list all accounts (unmatched first)
/cloud-connect/accounts/scan-inbox process existing inbox mails
/cloud-connect/accounts/<id>/link link to existing job or create new job /cloud-connect/accounts/<id>/link link to existing job or create new job
/cloud-connect/accounts/<id>/unlink remove the job link /cloud-connect/accounts/<id>/unlink remove the job link
""" """
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 ..models import CloudConnectAccount, Customer, Job, JobRun from ..models import CloudConnectAccount, Customer, Job, JobRun, MailMessage
from ..cloud_connect_importer import upsert_cloud_connect_report
@main_bp.route("/cloud-connect/accounts") @main_bp.route("/cloud-connect/accounts")
@ -48,6 +50,58 @@ def cloud_connect_accounts():
) )
@main_bp.route("/cloud-connect/accounts/scan-inbox", methods=["POST"])
@login_required
@roles_required("admin", "operator")
def cloud_connect_scan_inbox():
"""Process all existing inbox mails that are Cloud Connect report emails."""
mails = (
MailMessage.query
.filter(
MailMessage.location == "inbox",
db.func.lower(MailMessage.backup_type) == "cloud connect report",
)
.all()
)
total = len(mails)
total_accounts = 0
total_created = 0
errors = 0
for mail in mails:
try:
result = upsert_cloud_connect_report(
mail_message_id=mail.id,
html_body=(mail.html_body or ""),
)
total_accounts += result.get("total", 0)
total_created += result.get("created", 0)
except Exception as exc:
errors += 1
_log_admin_event(
event_type="cloud_connect_scan_error",
message=f"Failed to process mail {mail.id}: {exc}",
)
db.session.commit()
_log_admin_event(
event_type="cloud_connect_scan_inbox",
message=(
f"Scanned {total} inbox mail(s): "
f"{total_accounts} accounts upserted, {total_created} new runs created, {errors} error(s)"
),
)
flash(
f"Scanned {total} mail(s): {total_accounts} accounts upserted, "
f"{total_created} new runs created."
+ (f" {errors} error(s)." if errors else ""),
"success" if errors == 0 else "warning",
)
return redirect(url_for("main.cloud_connect_accounts"))
@main_bp.route("/cloud-connect/accounts/<int:cc_account_db_id>/link", methods=["POST"]) @main_bp.route("/cloud-connect/accounts/<int:cc_account_db_id>/link", methods=["POST"])
@login_required @login_required
@roles_required("admin", "operator") @roles_required("admin", "operator")

View File

@ -2,6 +2,9 @@
{% block content %} {% block content %}
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="mb-0">Cloud Connect Accounts</h2> <h2 class="mb-0">Cloud Connect Accounts</h2>
<form method="post" action="{{ url_for('main.cloud_connect_scan_inbox') }}">
<button type="submit" class="btn btn-sm btn-outline-secondary">Scan inbox mails</button>
</form>
</div> </div>
{# ── Unmatched accounts ─────────────────────────────────────────────────── #} {# ── Unmatched accounts ─────────────────────────────────────────────────── #}