Auto-commit local changes before build (2026-03-20 11:04:11)
This commit is contained in:
parent
af84039daf
commit
44214cc2c6
@ -184,6 +184,54 @@ def inbox_message_detail(message_id: int):
|
|||||||
for obj in MailObject.query.filter_by(mail_message_id=msg.id).order_by(MailObject.object_name.asc()).all()
|
for obj in MailObject.query.filter_by(mail_message_id=msg.id).order_by(MailObject.object_name.asc()).all()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Optional run_id: if provided and the run is a Cloud Connect run, return
|
||||||
|
# per-run objects (from run_object_links) and a structured CC summary instead
|
||||||
|
# of the raw MailObject list which contains all tenants from the shared report email.
|
||||||
|
cloud_connect_summary = None
|
||||||
|
run_id_param = request.args.get("run_id", type=int)
|
||||||
|
if run_id_param:
|
||||||
|
try:
|
||||||
|
from ..models import JobRun, CloudConnectAccount
|
||||||
|
from ..database import db
|
||||||
|
from sqlalchemy import text as _sql_text
|
||||||
|
_run = JobRun.query.get(run_id_param)
|
||||||
|
if _run and getattr(_run, "source_type", None) == "cloud_connect" and _run.job_id:
|
||||||
|
_cc_acc = CloudConnectAccount.query.filter_by(job_id=_run.job_id).first()
|
||||||
|
if _cc_acc:
|
||||||
|
cloud_connect_summary = {
|
||||||
|
"user": _cc_acc.user or "",
|
||||||
|
"section": _cc_acc.section or "",
|
||||||
|
"repo_name": _cc_acc.repo_name or "",
|
||||||
|
"repo_type": _cc_acc.repo_type or "",
|
||||||
|
"used_space": _cc_acc.used_space or "",
|
||||||
|
"total_quota": _cc_acc.total_quota or "",
|
||||||
|
"free_space": _cc_acc.free_space or "",
|
||||||
|
"last_active": _cc_acc.last_active_raw or "",
|
||||||
|
"status": _cc_acc.last_status or "",
|
||||||
|
}
|
||||||
|
# Replace MailObject list with per-run objects from run_object_links
|
||||||
|
cc_rows = db.session.execute(
|
||||||
|
_sql_text("""
|
||||||
|
SELECT co.object_name AS name, rol.status, rol.error_message
|
||||||
|
FROM run_object_links rol
|
||||||
|
JOIN customer_objects co ON co.id = rol.customer_object_id
|
||||||
|
WHERE rol.run_id = :run_id
|
||||||
|
ORDER BY co.object_name ASC
|
||||||
|
"""),
|
||||||
|
{"run_id": run_id_param},
|
||||||
|
).mappings().all()
|
||||||
|
objects = [
|
||||||
|
{
|
||||||
|
"name": r["name"] or "",
|
||||||
|
"type": "",
|
||||||
|
"status": r["status"] or "",
|
||||||
|
"error_message": r["error_message"] or "",
|
||||||
|
}
|
||||||
|
for r in cc_rows
|
||||||
|
]
|
||||||
|
except Exception:
|
||||||
|
pass # keep MailObject objects as fallback
|
||||||
|
|
||||||
# VSPC multi-company emails (e.g. "Active alarms summary") may not store parsed objects yet.
|
# VSPC multi-company emails (e.g. "Active alarms summary") may not store parsed objects yet.
|
||||||
# Extract company names from the stored body so the UI can offer a dedicated mapping workflow.
|
# Extract company names from the stored body so the UI can offer a dedicated mapping workflow.
|
||||||
vspc_companies: list[str] = []
|
vspc_companies: list[str] = []
|
||||||
@ -227,6 +275,7 @@ def inbox_message_detail(message_id: int):
|
|||||||
"meta": meta,
|
"meta": meta,
|
||||||
"body_html": body_html,
|
"body_html": body_html,
|
||||||
"objects": objects,
|
"objects": objects,
|
||||||
|
"cloud_connect_summary": cloud_connect_summary,
|
||||||
"vspc_companies": vspc_companies,
|
"vspc_companies": vspc_companies,
|
||||||
"vspc_company_defaults": vspc_company_defaults,
|
"vspc_company_defaults": vspc_company_defaults,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -258,8 +258,36 @@
|
|||||||
<h6 class="mb-1">Details</h6>
|
<h6 class="mb-1">Details</h6>
|
||||||
<div id="run_msg_overall_message" class="border rounded p-2" style="white-space: pre-wrap; max-height: 20vh; overflow: auto;"></div>
|
<div id="run_msg_overall_message" class="border rounded p-2" style="white-space: pre-wrap; max-height: 20vh; overflow: auto;"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="border rounded p-2 p-0" style="overflow:hidden;">
|
|
||||||
<iframe id="run_msg_body_container_iframe" class="w-100" style="height:55vh; border:0; background:transparent;" sandbox="allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"></iframe>
|
<!-- Cloud Connect summary panel (shown instead of raw email for CC runs) -->
|
||||||
|
<div id="jdm_cc_summary_panel" class="mb-3" style="display:none;">
|
||||||
|
<h6>Cloud Connect</h6>
|
||||||
|
<dl class="row mb-0 dl-compact">
|
||||||
|
<dt class="col-4">User</dt> <dd class="col-8" id="jdm_cc_user"></dd>
|
||||||
|
<dt class="col-4">Section</dt> <dd class="col-8" id="jdm_cc_section"></dd>
|
||||||
|
<dt class="col-4">Repository</dt> <dd class="col-8" id="jdm_cc_repo"></dd>
|
||||||
|
<dt class="col-4">Used / Quota</dt><dd class="col-8" id="jdm_cc_used"></dd>
|
||||||
|
<dt class="col-4">Free</dt> <dd class="col-8" id="jdm_cc_free"></dd>
|
||||||
|
<dt class="col-4">Last active</dt> <dd class="col-8" id="jdm_cc_last_active"></dd>
|
||||||
|
<dt class="col-4">Status</dt> <dd class="col-8" id="jdm_cc_status"></dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex align-items-center gap-2 mb-1">
|
||||||
|
<h6 class="mb-0" id="jdm_mail_heading">Mail</h6>
|
||||||
|
<a href="#" class="small text-muted" id="jdm_mail_toggle" style="display:none;"
|
||||||
|
onclick="(function(){
|
||||||
|
var b=document.getElementById('jdm_mail_iframe_body');
|
||||||
|
var lnk=document.getElementById('jdm_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="jdm_mail_iframe_body" class="border rounded p-0" style="overflow:hidden;">
|
||||||
|
<iframe id="run_msg_body_container_iframe" class="w-100" style="height:55vh; border:0; background:transparent;" sandbox="allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"></iframe>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3">
|
<div class="mt-3">
|
||||||
@ -665,7 +693,9 @@ function renderObjects(objects) {
|
|||||||
if (!messageId) return;
|
if (!messageId) return;
|
||||||
currentRunId = runId ? parseInt(runId, 10) : null;
|
currentRunId = runId ? parseInt(runId, 10) : null;
|
||||||
|
|
||||||
fetch("{{ url_for('main.inbox_message_detail', message_id=0) }}".replace("0", messageId))
|
var detailUrl = "{{ url_for('main.inbox_message_detail', message_id=0) }}".replace("0", messageId);
|
||||||
|
if (runId) detailUrl += "?run_id=" + encodeURIComponent(runId);
|
||||||
|
fetch(detailUrl)
|
||||||
.then(function (resp) {
|
.then(function (resp) {
|
||||||
if (!resp.ok) throw new Error("Failed to load message details");
|
if (!resp.ok) throw new Error("Failed to load message details");
|
||||||
return resp.json();
|
return resp.json();
|
||||||
@ -727,8 +757,33 @@ function renderObjects(objects) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var bodyFrame = document.getElementById("run_msg_body_container_iframe");
|
var ccPanel = document.getElementById("jdm_cc_summary_panel");
|
||||||
if (bodyFrame) bodyFrame.srcdoc = wrapMailHtml(data.body_html || "");
|
var mailHeading = document.getElementById("jdm_mail_heading");
|
||||||
|
var mailToggle = document.getElementById("jdm_mail_toggle");
|
||||||
|
var mailBody = document.getElementById("jdm_mail_iframe_body");
|
||||||
|
var bodyFrame = document.getElementById("run_msg_body_container_iframe");
|
||||||
|
|
||||||
|
if (data.cloud_connect_summary) {
|
||||||
|
var s = data.cloud_connect_summary;
|
||||||
|
document.getElementById("jdm_cc_user").textContent = s.user || "";
|
||||||
|
document.getElementById("jdm_cc_section").textContent = s.section || "";
|
||||||
|
document.getElementById("jdm_cc_repo").textContent = s.repo_name + (s.repo_type ? " (" + s.repo_type + ")" : "");
|
||||||
|
document.getElementById("jdm_cc_used").textContent = (s.used_space || "—") + " / " + (s.total_quota || "—");
|
||||||
|
document.getElementById("jdm_cc_free").textContent = s.free_space || "—";
|
||||||
|
document.getElementById("jdm_cc_last_active").textContent = s.last_active || "—";
|
||||||
|
document.getElementById("jdm_cc_status").textContent = s.status || "—";
|
||||||
|
if (ccPanel) ccPanel.style.display = "";
|
||||||
|
if (mailHeading) mailHeading.textContent = "Source report email";
|
||||||
|
if (mailToggle) { mailToggle.style.display = ""; mailToggle.textContent = "show"; }
|
||||||
|
if (mailBody) mailBody.style.display = "none";
|
||||||
|
if (bodyFrame) bodyFrame.srcdoc = wrapMailHtml(data.body_html || "");
|
||||||
|
} else {
|
||||||
|
if (ccPanel) ccPanel.style.display = "none";
|
||||||
|
if (mailHeading) mailHeading.textContent = "Mail";
|
||||||
|
if (mailToggle) mailToggle.style.display = "none";
|
||||||
|
if (mailBody) mailBody.style.display = "";
|
||||||
|
if (bodyFrame) bodyFrame.srcdoc = wrapMailHtml(data.body_html || "");
|
||||||
|
}
|
||||||
|
|
||||||
renderObjects(data.objects || []);
|
renderObjects(data.objects || []);
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,10 @@ This file documents all changes made to this project via Claude Code.
|
|||||||
## [2026-03-20]
|
## [2026-03-20]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
- Cloud Connect runs in job detail page popup now show a structured CC summary instead of the raw report email with all tenants:
|
||||||
|
- `routes_inbox.py` (`inbox_message_detail`): accepts optional `?run_id=` parameter; when the run has `source_type = "cloud_connect"`, returns `cloud_connect_summary` dict and per-run objects from `run_object_links` instead of MailObjects
|
||||||
|
- `job_detail.html`: passes `run_id` to the detail API; if `cloud_connect_summary` is returned, shows the CC summary panel, collapses the raw email (accessible via "show" toggle), and shows only the single per-run repository object
|
||||||
|
|
||||||
- "Delete all jobs" in Settings → Maintenance no longer times out on large datasets:
|
- "Delete all jobs" in Settings → Maintenance no longer times out on large datasets:
|
||||||
- Replaced ORM-based deletion (loaded all jobs/runs into Python memory, deleted object by object) with direct SQL `DELETE FROM` statements in FK order — handles 650K+ rows in seconds
|
- Replaced ORM-based deletion (loaded all jobs/runs into Python memory, deleted object by object) with direct SQL `DELETE FROM` statements in FK order — handles 650K+ rows in seconds
|
||||||
- Added `job_run_review_events` to the FK cleanup sequence (was causing a FK violation)
|
- Added `job_run_review_events` to the FK cleanup sequence (was causing a FK violation)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user