Auto-commit local changes before build (2026-03-20 10:22:35)

This commit is contained in:
Ivo Oskamp 2026-03-20 10:22:35 +01:00
parent 461c46b0ff
commit af84039daf
3 changed files with 131 additions and 26 deletions

View File

@ -287,6 +287,10 @@ def upsert_cloud_connect_report(mail_message_id: int, html_body: str) -> dict:
external_id=external_id,
)
db.session.add(run)
db.session.flush() # need run.id for object links
_persist_cc_objects(row, job.customer_id, job.id, run.id, report_dt)
counters["created"] += 1
counters["linked"] += 1
@ -294,6 +298,86 @@ def upsert_cloud_connect_report(mail_message_id: int, html_body: str) -> dict:
return counters
def _persist_cc_objects(
row: dict,
customer_id: int,
job_id: int,
run_id: int,
observed_at: datetime,
) -> None:
"""Upsert repository as a customer_object and create a run_object_link.
This mirrors the Cove datasource object persistence so Cloud Connect runs
have per-run object records suitable for reporting.
"""
from sqlalchemy import text as _text
repo_name = (row.get("repo_name") or "").strip() or "Unknown Repository"
repo_type = (row.get("repo_type") or "").strip()
object_name = f"{repo_name} ({repo_type})" if repo_type else repo_name
used = row.get("used_space") or ""
quota = row.get("total_quota") or ""
free = row.get("free_space") or ""
last = row.get("last_active_raw") or "unknown"
detail = f"Used: {used} / {quota} — Free: {free} — Last active: {last}"
try:
customer_object_id = db.session.execute(
_text("""
INSERT INTO customer_objects
(customer_id, object_name, object_type, first_seen_at, last_seen_at)
VALUES
(:customer_id, :object_name, :object_type, NOW(), NOW())
ON CONFLICT (customer_id, object_name)
DO UPDATE SET
object_type = COALESCE(EXCLUDED.object_type, customer_objects.object_type),
last_seen_at = NOW()
RETURNING id
"""),
{
"customer_id": customer_id,
"object_name": object_name,
"object_type": "cloud_connect_repo",
},
).scalar()
db.session.execute(
_text("""
INSERT INTO job_object_links
(job_id, customer_object_id, first_seen_at, last_seen_at)
VALUES (:job_id, :customer_object_id, NOW(), NOW())
ON CONFLICT (job_id, customer_object_id)
DO UPDATE SET last_seen_at = NOW()
"""),
{"job_id": job_id, "customer_object_id": customer_object_id},
)
db.session.execute(
_text("""
INSERT INTO run_object_links
(run_id, customer_object_id, status, error_message, observed_at)
VALUES
(:run_id, :customer_object_id, :status, :error_message, :observed_at)
ON CONFLICT (run_id, customer_object_id)
DO UPDATE SET
status = EXCLUDED.status,
error_message = EXCLUDED.error_message,
observed_at = EXCLUDED.observed_at
"""),
{
"run_id": run_id,
"customer_object_id": customer_object_id,
"status": row["status"],
"error_message": detail,
"observed_at": observed_at,
},
)
except Exception as exc:
logger.warning("CC object persist failed for run %s: %s", run_id, exc)
def _build_error_message(row: dict) -> str:
"""Build a human-readable remark for a Cloud Connect run."""
parts = [

View File

@ -1494,7 +1494,7 @@ def run_checks_details():
"last_active": _cc_acc.last_active_raw or "",
"status": _cc_acc.last_status or "",
}
# Keep mail meta and EML link for audit trail; skip body HTML.
# Keep mail meta, EML link, and body for the collapsible source panel.
if msg:
mail_meta = {
"from_address": msg.from_address or "",
@ -1508,7 +1508,7 @@ def run_checks_details():
"subject": msg.subject or "",
"received_at": _format_datetime(msg.received_at),
}
if msg and cloud_connect_summary is None:
if msg:
def _is_blank_text(s):
return s is None or (isinstance(s, str) and s.strip() == "")
@ -1584,7 +1584,8 @@ def run_checks_details():
)
# If no run-linked objects exist yet, fall back to objects parsed/stored on the mail message.
if (not objects_payload) and msg:
# Skip this fallback for cloud_connect runs: the mail contains all tenants, not just this one.
if (not objects_payload) and msg and getattr(run, "source_type", None) != "cloud_connect":
try:
for mo in (
MailObject.query.filter_by(mail_message_id=msg.id)

View File

@ -318,9 +318,9 @@
</dd>
</dl>
<div class="mb-3 rcm-mail-panel" id="rcm_cc_summary_panel" style="display:none;">
<div class="mb-3" id="rcm_cc_summary_panel" style="display:none;">
<h6>Cloud Connect</h6>
<dl class="row mb-0 dl-compact" id="rcm_cc_summary_dl">
<dl class="row mb-0 dl-compact">
<dt class="col-4">User</dt> <dd class="col-8" id="rcc_user"></dd>
<dt class="col-4">Section</dt> <dd class="col-8" id="rcc_section"></dd>
<dt class="col-4">Repository</dt> <dd class="col-8" id="rcc_repo"></dd>
@ -332,7 +332,18 @@
</div>
<div class="mb-3 rcm-mail-panel" id="rcm_mail_iframe_panel">
<h6>Mail</h6>
<div class="d-flex align-items-center gap-2 mb-1">
<h6 class="mb-0" id="rcm_mail_heading">Mail</h6>
<a href="#" class="small text-muted" id="rcm_mail_toggle" style="display:none;"
onclick="(function(){
var b=document.getElementById('rcm_mail_iframe_body');
var lnk=document.getElementById('rcm_mail_toggle');
var collapsed=b.style.display==='none';
b.style.display=collapsed?'':'none';
lnk.textContent=collapsed?'hide':'show';
})();return false;">show</a>
</div>
<div id="rcm_mail_iframe_body">
<iframe
id="rcm_body_iframe"
class="border rounded"
@ -341,6 +352,7 @@
referrerpolicy="no-referrer"
></iframe>
</div>
</div>
<div>
<div class="table-responsive rcm-objects-scroll">
@ -1435,7 +1447,9 @@ table.addEventListener('change', function (e) {
}
var ccPanel = document.getElementById('rcm_cc_summary_panel');
var iframePanel = document.getElementById('rcm_mail_iframe_panel');
var mailHeading = document.getElementById('rcm_mail_heading');
var mailToggle = document.getElementById('rcm_mail_toggle');
var mailBody = document.getElementById('rcm_mail_iframe_body');
var bodyFrame = document.getElementById('rcm_body_iframe');
if (run.cloud_connect_summary) {
@ -1448,10 +1462,16 @@ table.addEventListener('change', function (e) {
document.getElementById('rcc_last_active').textContent = s.last_active || '—';
document.getElementById('rcc_status').textContent = s.status || '—';
if (ccPanel) ccPanel.style.display = '';
if (iframePanel) iframePanel.style.display = 'none';
if (mailHeading) mailHeading.textContent = 'Source report email';
if (mailToggle) mailToggle.style.display = '';
if (mailToggle) mailToggle.textContent = 'show';
if (mailBody) mailBody.style.display = 'none';
if (bodyFrame) bodyFrame.srcdoc = wrapMailHtml(run.body_html || '');
} else {
if (ccPanel) ccPanel.style.display = 'none';
if (iframePanel) iframePanel.style.display = '';
if (mailHeading) mailHeading.textContent = 'Mail';
if (mailToggle) mailToggle.style.display = 'none';
if (mailBody) mailBody.style.display = '';
if (bodyFrame) {
bodyFrame.srcdoc = wrapMailHtml(run.body_html || (run.missed ? '<div class="text-muted">No email for missed run.</div>' : ''));
}