From edc820051eae6a9ce0218d71b54aa5d0922d8609 Mon Sep 17 00:00:00 2001 From: StyxX65 <150797939+StyxX65@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:58:24 +0200 Subject: [PATCH] Fix classification/ directory never matching where data files lived The code has always expected classification/m365_skus.json and classification/google_ou_roles.json (present since the initial commit in m365_connector.py, google_connector.py, and build_gdpr.py's own skus_dir variable) - but the actual data files lived in skus/ under a different name (education.json) or just the wrong directory. Both loaders degrade to a fallback on failure, so this was never fatal, but it meant: - M365 SKU-based role classification silently ran on fragment-matching only, never touching the real 13 student + 11 staff SKU list. - Google Workspace OU-based role classification silently used a hardcoded 4-prefix fallback missing real categories the actual config has - /Elev, /Pupils (student), /Teachers, /Admin (staff). _load_ou_roles() had a bare `except Exception: return ` with no logging at all, so this had zero visibility anywhere. Fix: moved both files to classification/ (git mv, so history is preserved), updated .gitignore's exception rule from !skus/*.json to !classification/*.json, fixed the stale "skus/google_ou_roles.json" docstring, and added a module logger + proper warning/error logging to _load_ou_roles() matching the pattern _load_sku_data() already used - so if this class of bug recurs, it won't be silent again. Verified: both loaders now log successful loads with the real counts (13+11 SKUs, 4+6 OU prefixes including the previously-missing categories), all 227 tests still pass, and a full app boot confirms the startup warning is gone and the app still serves normally. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 2 +- {skus => classification}/google_ou_roles.json | 0 skus/education.json => classification/m365_skus.json | 0 google_connector.py | 12 ++++++++++-- 4 files changed, 11 insertions(+), 3 deletions(-) rename {skus => classification}/google_ou_roles.json (100%) rename skus/education.json => classification/m365_skus.json (100%) diff --git a/.gitignore b/.gitignore index aa89886..80c7543 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ *.json !lang/*.json !keywords/*.json -!skus/*.json +!classification/*.json !package*.json # Be explicit about the most sensitive files diff --git a/skus/google_ou_roles.json b/classification/google_ou_roles.json similarity index 100% rename from skus/google_ou_roles.json rename to classification/google_ou_roles.json diff --git a/skus/education.json b/classification/m365_skus.json similarity index 100% rename from skus/education.json rename to classification/m365_skus.json diff --git a/google_connector.py b/google_connector.py index 5451ac6..06625a4 100644 --- a/google_connector.py +++ b/google_connector.py @@ -31,6 +31,8 @@ import threading from pathlib import Path from typing import Iterator, Optional +logger = logging.getLogger(__name__) + # ── google-auth / google-api-python-client ──────────────────────────────────── try: from google.oauth2 import service_account @@ -93,15 +95,21 @@ _MAX_EXPORT_BYTES = 20 * 1024 * 1024 # 20 MB _OU_ROLES_PATH = Path(__file__).parent / "classification" / "google_ou_roles.json" def _load_ou_roles() -> tuple[list, list]: - """Load student/staff OU prefix lists from skus/google_ou_roles.json. + """Load student/staff OU prefix lists from classification/google_ou_roles.json. Returns (student_prefixes, staff_prefixes) — both lowercased.""" try: import json as _j data = _j.loads(_OU_ROLES_PATH.read_text(encoding="utf-8")) students = [p.lower() for p in data.get("student_ou_prefixes", [])] staff = [p.lower() for p in data.get("staff_ou_prefixes", [])] + logger.info("[ou-roles] Loaded %d student + %d staff OU prefixes from classification/google_ou_roles.json", + len(students), len(staff)) return students, staff - except Exception: + except FileNotFoundError: + logger.warning("[ou-roles] classification/google_ou_roles.json not found — using hardcoded fallback prefixes only") + return ["/elever", "/students"], ["/personale", "/staff", "/lærere", "/ansatte"] + except Exception as e: + logger.error("[ou-roles] Failed to load classification/google_ou_roles.json: %s — using hardcoded fallback prefixes only", e) return ["/elever", "/students"], ["/personale", "/staff", "/lærere", "/ansatte"] def classify_ou_role(org_unit_path: str) -> str: