From f14e02992d20996068b13cb0207aeb8ed3d38461 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Tue, 6 Jan 2026 09:45:02 +0100 Subject: [PATCH] Auto-commit local changes before build (2026-01-06 09:45:02) --- .last-branch | 2 +- .../src/backend/app/parsers/veeam.py | 19 +++++++++++++++++++ docs/changelog.md | 5 +++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/.last-branch b/.last-branch index 9d288af..50618f4 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260104-20-changelog-0-1-16 +v20260106-01-m365-combined-job-name-merge diff --git a/containers/backupchecks/src/backend/app/parsers/veeam.py b/containers/backupchecks/src/backend/app/parsers/veeam.py index be9955b..71f8fd5 100644 --- a/containers/backupchecks/src/backend/app/parsers/veeam.py +++ b/containers/backupchecks/src/backend/app/parsers/veeam.py @@ -697,6 +697,20 @@ def _strip_retry_suffix(job_name: Optional[str]) -> Optional[str]: return cleaned or None +def _strip_m365_combined_suffix(job_name: Optional[str]) -> Optional[str]: + """Remove the trailing "(Combined)" suffix from a Veeam M365 job name. + + Veeam Backup for Microsoft 365 can send separate report emails where the + job name is suffixed with "(Combined)" (e.g. "Tenant OneDrive (Combined)"). + Those should be treated as the same logical job as the non-suffixed name. + """ + if not job_name: + return job_name + + cleaned = re.sub(r"\s*\(\s*Combined\s*\)\s*$", "", job_name, flags=re.IGNORECASE).strip() + return cleaned or None + + def try_parse_veeam(msg: MailMessage) -> Tuple[bool, Dict, List[Dict]]: """Try to parse a Veeam backup report mail. @@ -852,6 +866,11 @@ def try_parse_veeam(msg: MailMessage) -> Tuple[bool, Dict, List[Dict]]: # Do not let retry counters create distinct job names. job_name = _strip_retry_suffix(job_name) + # Veeam Backup for Microsoft 365 reports can add a "(Combined)" suffix. + # Strip it so combined/non-combined mails map to the same job. + if (backup_type or "") == "Veeam Backup for Microsoft 365": + job_name = _strip_m365_combined_suffix(job_name) + # Health Check reports should always map to a stable job name. if (backup_type or '').lower() == 'health check': job_name = 'Health Check' diff --git a/docs/changelog.md b/docs/changelog.md index 01b42a1..0b9151f 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,8 @@ +## v20260106-01-m365-combined-job-name-merge + +- Updated the Veeam Backup for Microsoft 365 mail parser to normalize job names. +- Job names containing the suffix "(Combined)" are now mapped to the same job as the corresponding job without this suffix. +- This ensures that combined and non-combined backup result emails are aggregated under a single job in reports and statistics. ================================================================================================================================================