Auto-commit local changes before build (2026-01-12 14:07:33) #100
@ -1 +1 @@
|
|||||||
v20260112-12-vspc-company-mapping-popup-visible
|
v20260112-13-vspc-company-mapping-popup-ui
|
||||||
|
|||||||
@ -4,6 +4,8 @@ from .routes_shared import _format_datetime, _log_admin_event, _send_mail_messag
|
|||||||
from ..email_utils import extract_best_html_from_eml, is_effectively_blank_html
|
from ..email_utils import extract_best_html_from_eml, is_effectively_blank_html
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import re
|
||||||
|
import html as _html
|
||||||
|
|
||||||
@main_bp.route("/inbox")
|
@main_bp.route("/inbox")
|
||||||
@login_required
|
@login_required
|
||||||
@ -149,34 +151,35 @@ 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()
|
||||||
]
|
]
|
||||||
|
|
||||||
# For VSPC Active Alarms summary messages, objects are typically not persisted until approval.
|
# VSPC multi-company emails (e.g. "Active alarms summary") may not store parsed objects yet.
|
||||||
# To enable multi-company mapping in the Inbox, we expose the company list derived from parsing
|
# Extract company names from the stored body so the UI can offer a dedicated mapping workflow.
|
||||||
# the stored HTML body (best-effort, without persisting objects).
|
vspc_companies: list[str] = []
|
||||||
bsw = (getattr(msg, "backup_software", "") or "").strip()
|
|
||||||
btype = (getattr(msg, "backup_type", "") or "").strip()
|
|
||||||
jname = (getattr(msg, "job_name", "") or "").strip()
|
|
||||||
if not objects and bsw == "Veeam" and btype == "Service Provider Console" and jname == "Active alarms summary":
|
|
||||||
try:
|
try:
|
||||||
from ..parsers.veeam import _parse_vspc_active_alarms_from_html
|
bsw = (getattr(msg, "backup_software", "") or "").strip().lower()
|
||||||
|
btype = (getattr(msg, "backup_type", "") or "").strip().lower()
|
||||||
|
jname = (getattr(msg, "job_name", "") or "").strip().lower()
|
||||||
|
|
||||||
parsed_objs, _status, _msg = _parse_vspc_active_alarms_from_html(body_html or "")
|
if bsw == "veeam" and btype == "service provider console" and jname == "active alarms summary":
|
||||||
companies: list[str] = []
|
raw = text_body if not _is_blank_text(text_body) else (html_body or "")
|
||||||
seen: set[str] = set()
|
if raw:
|
||||||
for o in parsed_objs or []:
|
txt = raw
|
||||||
name = str((o or {}).get("name") or "")
|
# Best-effort HTML to text
|
||||||
ix = name.find(" | ")
|
if "<" in txt and ">" in txt:
|
||||||
if ix > 0:
|
txt = re.sub(r"<[^>]+>", " ", txt)
|
||||||
c = name[:ix].strip()
|
txt = _html.unescape(txt)
|
||||||
if c and c not in seen:
|
txt = re.sub(r"\s+", " ", txt).strip()
|
||||||
seen.add(c)
|
|
||||||
companies.append(c)
|
seen = set()
|
||||||
if companies:
|
for m2 in re.finditer(r"\bCompany:\s*([^\(\r\n]+?)\s*\(\s*alarms?\s*:", txt, flags=re.IGNORECASE):
|
||||||
meta["vspc_companies"] = companies
|
cname = (m2.group(1) or "").strip()
|
||||||
|
if cname and cname not in seen:
|
||||||
|
seen.add(cname)
|
||||||
|
vspc_companies.append(cname)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Never fail the message detail endpoint due to best-effort parsing.
|
vspc_companies = []
|
||||||
pass
|
|
||||||
|
|
||||||
return jsonify({"status": "ok", "meta": meta, "body_html": body_html, "objects": objects})
|
|
||||||
|
return jsonify({"status": "ok", "meta": meta, "body_html": body_html, "objects": objects, "vspc_companies": vspc_companies})
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route("/inbox/message/<int:message_id>/eml")
|
@main_bp.route("/inbox/message/<int:message_id>/eml")
|
||||||
|
|||||||
@ -509,6 +509,11 @@ function findCustomerIdByName(name) {
|
|||||||
// reset
|
// reset
|
||||||
mapBtn.classList.add("d-none");
|
mapBtn.classList.add("d-none");
|
||||||
if (approveBtn) approveBtn.classList.remove("d-none");
|
if (approveBtn) approveBtn.classList.remove("d-none");
|
||||||
|
var ciReset = document.getElementById("msg_customer_input");
|
||||||
|
if (ciReset) {
|
||||||
|
ciReset.removeAttribute("disabled");
|
||||||
|
ciReset.placeholder = "Select customer";
|
||||||
|
}
|
||||||
|
|
||||||
var bsw = String(meta.backup_software || "").trim();
|
var bsw = String(meta.backup_software || "").trim();
|
||||||
var btype = String(meta.backup_type || "").trim();
|
var btype = String(meta.backup_type || "").trim();
|
||||||
@ -518,14 +523,11 @@ function findCustomerIdByName(name) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var companies = [];
|
var companies = (data.vspc_companies || meta.vspc_companies || []);
|
||||||
if (Array.isArray(meta.vspc_companies) && meta.vspc_companies.length) {
|
if (!Array.isArray(companies)) companies = [];
|
||||||
meta.vspc_companies.forEach(function (c) {
|
|
||||||
c = String(c || "").trim();
|
// Fallback for older stored messages where companies were embedded in object names.
|
||||||
if (c) companies.push(c);
|
if (!companies.length) {
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// fallback: derive from parsed objects (when available)
|
|
||||||
var objs = data.objects || [];
|
var objs = data.objects || [];
|
||||||
var seen = {};
|
var seen = {};
|
||||||
objs.forEach(function (o) {
|
objs.forEach(function (o) {
|
||||||
@ -543,6 +545,12 @@ function findCustomerIdByName(name) {
|
|||||||
// Show mapping button; hide regular approve
|
// Show mapping button; hide regular approve
|
||||||
mapBtn.classList.remove("d-none");
|
mapBtn.classList.remove("d-none");
|
||||||
if (approveBtn) approveBtn.classList.add("d-none");
|
if (approveBtn) approveBtn.classList.add("d-none");
|
||||||
|
var ci = document.getElementById("msg_customer_input");
|
||||||
|
if (ci) {
|
||||||
|
ci.value = "";
|
||||||
|
ci.setAttribute("disabled", "disabled");
|
||||||
|
ci.placeholder = "Use \"Map companies\"";
|
||||||
|
}
|
||||||
|
|
||||||
mapBtn.onclick = function () {
|
mapBtn.onclick = function () {
|
||||||
var tbody = document.getElementById("vspcCompanyMapTbody");
|
var tbody = document.getElementById("vspcCompanyMapTbody");
|
||||||
|
|||||||
@ -206,6 +206,13 @@
|
|||||||
- Exposed parsed VSPC company list via the inbox message detail endpoint.
|
- Exposed parsed VSPC company list via the inbox message detail endpoint.
|
||||||
- Updated inbox popup logic to trigger the dedicated VSPC company-mapping workflow based on detected companies instead of parsed objects.
|
- Updated inbox popup logic to trigger the dedicated VSPC company-mapping workflow based on detected companies instead of parsed objects.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v20260112-13-vspc-company-mapping-popup-ui
|
||||||
|
- Added VSPC company extraction to the inbox message detail response so multi-company summaries expose a company list even when no objects are stored.
|
||||||
|
- Updated the Inbox message modal to show the “Map companies” button based on the returned VSPC company list (with fallback to legacy object-name parsing).
|
||||||
|
- Disabled the standard Customer selector when VSPC company mapping is available to avoid using the wrong approval flow.
|
||||||
|
|
||||||
================================================================================================================================================
|
================================================================================================================================================
|
||||||
## v0.1.19
|
## v0.1.19
|
||||||
This release delivers a broad set of improvements focused on reliability, transparency, and operational control across mail processing, administrative auditing, and Run Checks workflows. The changes aim to make message handling more robust, provide better insight for administrators, and give operators clearer and more flexible control when reviewing backup runs.
|
This release delivers a broad set of improvements focused on reliability, transparency, and operational control across mail processing, administrative auditing, and Run Checks workflows. The changes aim to make message handling more robust, provide better insight for administrators, and give operators clearer and more flexible control when reviewing backup runs.
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user