from __future__ import annotations import re import urllib.parse from typing import Any PLATFORM_DOC_RE = re.compile(r"/db/(v(?:8|83|85)\d*doc)(?:/|$)", flags=re.IGNORECASE) BOOKMARK_PATH_RE = re.compile(r"/bookmark/([^/]+)/([^/?#]+)", flags=re.IGNORECASE) CONTENT_PATH_RE = re.compile(r"/content/(-?\d+)/(?:hdoc|\d+)", flags=re.IGNORECASE) HASH_BOOKMARK_RE = re.compile(r"^#?bookmark:([^:]+):([^:]+)$", flags=re.IGNORECASE) HASH_CONTENT_RE = re.compile(r"^#?content:([^:]+):([^:]+)$", flags=re.IGNORECASE) def platform_doc_id_from_url(url: str) -> str | None: match = PLATFORM_DOC_RE.search(urllib.parse.urlparse(url).path) return match.group(1).lower() if match else None def platform_version_from_doc_id(doc_id: str | None) -> str | None: if not doc_id: return None doc_id = doc_id.lower() if doc_id == "v8doc": return "8.x" match = re.fullmatch(r"v83(\d+)doc", doc_id) if match: patch = int(match.group(1)) return f"8.3.{patch}" match = re.fullmatch(r"v85(\d+)doc", doc_id) if match: patch = int(match.group(1)) return f"8.5.{patch}" match = re.fullmatch(r"v8(\d+)doc", doc_id) if match: patch = int(match.group(1)) return f"8.{patch}" return None def platform_version_from_url(url: str) -> str | None: return platform_version_from_doc_id(platform_doc_id_from_url(url)) def parse_doc_coordinate(url: str) -> dict[str, str | None]: parsed = urllib.parse.urlparse(url) path = parsed.path fragment = urllib.parse.unquote(parsed.fragment or "") coordinate: dict[str, str | None] = { "platform_doc_id": platform_doc_id_from_url(url), "platform_version": platform_version_from_url(url), "doc_book": None, "doc_bookmark": None, "doc_content_id": None, "doc_coordinate": None, } bookmark = BOOKMARK_PATH_RE.search(path) if bookmark: coordinate["doc_book"] = bookmark.group(1) coordinate["doc_bookmark"] = bookmark.group(2) else: hash_bookmark = HASH_BOOKMARK_RE.match(fragment) if hash_bookmark: coordinate["doc_book"] = hash_bookmark.group(1) coordinate["doc_bookmark"] = hash_bookmark.group(2) content = CONTENT_PATH_RE.search(path) if content: coordinate["doc_content_id"] = content.group(1) else: hash_content = HASH_CONTENT_RE.match(fragment) if hash_content: coordinate["doc_book"] = coordinate["doc_book"] or hash_content.group(1) coordinate["doc_content_id"] = hash_content.group(2) parts = [ coordinate.get("platform_doc_id"), coordinate.get("doc_book"), coordinate.get("doc_bookmark") or coordinate.get("doc_content_id"), ] if any(parts): coordinate["doc_coordinate"] = ":".join(part or "_" for part in parts) return coordinate def materialize_doc_url(url: str) -> str: parsed = urllib.parse.urlparse(url) fragment = urllib.parse.unquote(parsed.fragment or "") if not fragment: return url hash_bookmark = HASH_BOOKMARK_RE.match(fragment) if hash_bookmark: path = parsed.path.rstrip("/") + f"/bookmark/{hash_bookmark.group(1)}/{hash_bookmark.group(2)}" return urllib.parse.urlunparse(parsed._replace(path=path, fragment="")) hash_content = HASH_CONTENT_RE.match(fragment) if hash_content: content_id = hash_content.group(2) path = parsed.path.rstrip("/") + f"/content/{content_id}/hdoc" return urllib.parse.urlunparse(parsed._replace(path=path, fragment="")) return urllib.parse.urlunparse(parsed._replace(fragment="")) def merge_platform_metadata(record: dict[str, Any], url: str) -> dict[str, Any]: coordinate = parse_doc_coordinate(url) merged = dict(record) for key, value in coordinate.items(): if value and not merged.get(key): merged[key] = value if not merged.get("platform_family") and merged.get("platform_doc_id"): merged["platform_family"] = "1C:Enterprise 8" return merged