[{'Text': '"""GBS (Global Broadband Statistics) tools for the PT Research App.\n\nGBS is a quarterly process: associates search the web for updated broadband\nstatistics from operators (most are required by law to report quarterly).\nThey find PDFs, images, or web data, extract subscriber numbers, and enter\nthem as **pending** records. Admins review and approve/publish.\n\nMongoDB Collections (pt-research-app Prisma schema):\n  Operator       — 1300+ operators globally (214 UK). Fields: name, country, technologies\n  Statistics     — Subscriber counts. Fields: type, operatorId, period, tech, channel, domain,\n                   subscribers, state (1=pending, 2=approved, 3=published)\n  Sources        — Archival records linking statistics to source URLs/files\n  GlobalVariables— Tracks currentStatsPeriod (e.g. "2025Q3") and currentTariffsPeriod\n\nLive data snapshot (2025Q3 — verified against production MongoDB):\n  - UK operators: 175 total, 55 with data (31.4%), 120 without\n  - Global records in 2025Q3: 1854 across 690 operators\n  - Valid types:    broadband, mobile, iptv\n  - Valid channels: Infrastructure, Retail\n  - Valid domains:  Residential, Business, Total\n\nTool workflow (agent-driven, no hardcoded flow):\n  1. gbs_get_status(country) → overview of what\'s covered this quarter\n  2. gbs_get_status(country, operator) → drill into a specific operator\'s records + gaps\n  3. gbs_add_statistic(...) → insert a pending record when numbers are found\n  4. gbs_create_source(...) → link source URLs/files to operator/period\n\nUses mongosh subprocess (not PyMongo) for reliable SSL.\nRequires: PT_RESEARCH_DATABASE_URI env var, mongosh on PATH.\n"""\n\nimport json\nfrom collections import defaultdict\nfrom typing import Optional\n\nfrom point_topic_mcp.core.utils import check_env_vars\nfrom point_topic_mcp.core import mongodb_utils as mongo\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nADMIN_BASE = "https://pt-research-app.vercel.app"\n\nif check_env_vars("gbs_tools", ["PT_RESEARCH_DATABASE_URI"]):\n\n    VALID_TYPES = ["broadband", "mobile", "iptv"]\n    VALID_CHANNELS = ["Infrastructure", "Retail"]\n    VALID_DOMAINS = ["Residential", "Business", "Total"]\n\n    def _valid_object_id(oid: str) -> bool:\n        """Check if string is valid 24-char hex ObjectId."""\n        if not oid or len(oid) != 24:\n            return False\n        return all(c in "0123456789abcdefABCDEF" for c in oid)\n\n    def _valid_period(p: str) -> bool:\n        """Check period matches format like \'2025Q1\'."""\n        import re\n        return bool(re.match(r"^\\d{4}Q[1-4]$", p))\n\n    def _gbs_admin_url(operator_id: str) -> str:\n        """Admin link to review/publish this operator\'s statistics."""\n        return f"{ADMIN_BASE}/operators/{operator_id}/statistics"\n\n    def _run_status_query(country: str, operator: str | None, period: str | None) -> dict:\n        """Single mongosh call: resolve current period + run $lookup aggregation."""\n        # Build the pipeline conditionally\n        match: dict = {"country": country, "isArchived": {"$ne": True}}\n        if operator:\n            match["name"] = operator\n\n        pipeline = [\n            {"$match": match},\n            {"$lookup": {\n                "from": "Statistics",\n                "let": {"oid": "$_id", "tp": "__PERIOD_PLACEHOLDER__"},\n                "pipeline": [\n                    {"$match": {"$expr": {\n                        "$and": [\n                            {"$eq": ["$operatorId", "$$oid"]},\n                            {"$eq": ["$period", "$$tp"]}\n                        ]\n                    }}}\n                ],\n                "as": "stats"\n            }},\n            {"$addFields": {"statCount": {"$size": "$stats"}}},\n            {"$project": {"name": 1, "techs": "$technologies", "statCount": 1, "stats": 1}},\n            {"$sort": {"name": 1}},\n        ]\n\n        period_expr = f\'"{period}"\' if period else "null"\n        pipeline_json = json.dumps(pipeline)\n\n        js = f"""\n        const gv = db.GlobalVariables.findOne({{isCurrent: true}});\n        const currentPeriod = gv ? gv.currentStatsPeriod : null;\n        const targetPeriod = {period_expr} || currentPeriod;\n\n        const pipeline = JSON.parse({json.dumps(pipeline_json)}.replace(\n            \'"__PERIOD_PLACEHOLDER__"\', JSON.stringify(targetPeriod)\n        ));\n\n        const docs = db.Operator.aggregate(pipeline).toArray();\n        EJSON.stringify({{currentPeriod, targetPeriod, docs}});\n        """\n        result = mongo._run_mongosh(js, timeout=60)\n        parsed = json.loads(result) if result else {}\n        if not parsed:\n            return {"error": "No data returned from MongoDB"}\n        return parsed\n\n'}]