From b42e1cd0910f74c4e8e2022f3ded61c4ab43d96d Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 12 May 2026 14:43:13 +0200 Subject: [PATCH] =?UTF-8?q?feat(cmp):=20timezone=E2=86=92geo=5Fcountry=20m?= =?UTF-8?q?apping=20+=20timezone=20parameter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add _resolve_geo_from_timezone() with 35-country IANA timezone map. Accept timezone field in ConsentCreate schema and pass through to service. Populate geo_country automatically from browser timezone. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../compliance/api/banner_routes.py | 1 + .../compliance/schemas/banner.py | 1 + .../services/banner_consent_service.py | 24 +++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/backend-compliance/compliance/api/banner_routes.py b/backend-compliance/compliance/api/banner_routes.py index d4c1b47..44234ae 100644 --- a/backend-compliance/compliance/api/banner_routes.py +++ b/backend-compliance/compliance/api/banner_routes.py @@ -90,6 +90,7 @@ async def record_consent( scripts_blocked=body.scripts_blocked, scripts_released=body.scripts_released, cookies_set=body.cookies_set, + tz_name=body.timezone, ) diff --git a/backend-compliance/compliance/schemas/banner.py b/backend-compliance/compliance/schemas/banner.py index ebc0b28..87e30db 100644 --- a/backend-compliance/compliance/schemas/banner.py +++ b/backend-compliance/compliance/schemas/banner.py @@ -30,6 +30,7 @@ class ConsentCreate(BaseModel): screen_resolution: Optional[str] = None session_id: Optional[str] = None consent_scope: Optional[str] = None + timezone: Optional[str] = None # Script/Cookie-Tracking (Migration 108) scripts_blocked: List[dict[str, Any]] = [] scripts_released: List[dict[str, Any]] = [] diff --git a/backend-compliance/compliance/services/banner_consent_service.py b/backend-compliance/compliance/services/banner_consent_service.py index 4c6e7ee..1f3b369 100644 --- a/backend-compliance/compliance/services/banner_consent_service.py +++ b/backend-compliance/compliance/services/banner_consent_service.py @@ -62,6 +62,28 @@ class BannerConsentService: return None return hashlib.sha256(ip.encode()).hexdigest()[:16] + @staticmethod + def _resolve_geo_from_timezone(tz_name: Optional[str]) -> Optional[str]: + """Map IANA timezone to ISO country code (best-effort, no external deps).""" + if not tz_name: + return None + tz_map: dict[str, str] = { + "Europe/Berlin": "DE", "Europe/Vienna": "AT", "Europe/Zurich": "CH", + "Europe/Amsterdam": "NL", "Europe/Brussels": "BE", "Europe/Paris": "FR", + "Europe/London": "GB", "Europe/Madrid": "ES", "Europe/Rome": "IT", + "Europe/Warsaw": "PL", "Europe/Prague": "CZ", "Europe/Stockholm": "SE", + "Europe/Copenhagen": "DK", "Europe/Helsinki": "FI", "Europe/Oslo": "NO", + "Europe/Dublin": "IE", "Europe/Lisbon": "PT", "Europe/Athens": "GR", + "Europe/Bucharest": "RO", "Europe/Budapest": "HU", "Europe/Sofia": "BG", + "Europe/Zagreb": "HR", "Europe/Ljubljana": "SI", "Europe/Bratislava": "SK", + "Europe/Tallinn": "EE", "Europe/Riga": "LV", "Europe/Vilnius": "LT", + "Europe/Luxembourg": "LU", "Europe/Valletta": "MT", "Europe/Nicosia": "CY", + "America/New_York": "US", "America/Chicago": "US", "America/Denver": "US", + "America/Los_Angeles": "US", "America/Toronto": "CA", "Asia/Tokyo": "JP", + "Asia/Shanghai": "CN", "Asia/Kolkata": "IN", "Australia/Sydney": "AU", + } + return tz_map.get(tz_name) + def _log( self, tenant_id: uuid.UUID, @@ -184,6 +206,7 @@ class BannerConsentService: scripts_blocked: Optional[list[dict]] = None, scripts_released: Optional[list[dict]] = None, cookies_set: Optional[list[dict]] = None, + tz_name: Optional[str] = None, ) -> dict[str, Any]: """Upsert a device consent row for (tenant, site, device_fingerprint). @@ -216,6 +239,7 @@ class BannerConsentService: "screen_resolution": screen_resolution, "session_id": session_id, "consent_scope": consent_scope or "domain", + "geo_country": self._resolve_geo_from_timezone(tz_name), "scripts_blocked": scripts_blocked or [], "scripts_released": scripts_released or [], "cookies_set": cookies_set or [],