diff --git a/aidocs/project_context.md b/aidocs/project_context.md index ab182ea..3bbbd18 100644 --- a/aidocs/project_context.md +++ b/aidocs/project_context.md @@ -86,4 +86,8 @@ - Each run follows only the group containing the globally newest source start time. It does not fall back or backfill older groups. Missing sources or no matching files produce `status=waiting` and a successful exit; storage API failures remain task failures. Multiple files from one source in a group select the latest source start time. - All workbook row start times must round into the target group. Result CSV and database rows use one normalized `metric_time` and the database result columns only; `hour_start` and `hour_end` were removed from summary CSV output. - Database time is checked before source ZIP and CellData downloads. A newer or equal database time skips source processing. Successful database output is archived through the Storage API under `干扰历史数据/YYYY-MM-DD/干扰数据处理结果_YYYYMMDDHHMMSS.csv`; an equal database time with a missing/empty history file is exported from the database and repaired. -- Eighteen tests pass on Windows and in the offline runtime image. Live read-only selection saw incomplete newest group `2026-08-06 15:00:00`, correctly waited for missing `5G下FDD干扰监控`, and did not produce output. Explicit read-only validation of complete group `2026-08-06 14:00:00` produced 1,092 rows with one normalized `metric_time` and the expected nine-column result schema. +- Nineteen tests pass on Windows and in the offline runtime image. Live read-only selection saw incomplete newest group `2026-08-06 15:00:00`, correctly waited for missing `5G下FDD干扰监控`, and did not produce output. Explicit read-only validation of complete group `2026-08-06 14:00:00` produced 1,092 rows with one normalized `metric_time` and the expected nine-column result schema. +- SSH deployment run `7a7c0bba3a0c41f5a5a9e83ad25ff2fd` processed the completed `2026-08-06 15:00:00` group successfully. It retained 1,082 high-interference rows, removed 853 lower-interference rows, matched coordinates for 1,074 rows, left 8 unmatched, and replaced 1,116 rows from the previous database time. +- Database verification found only `2026-08-06 15:00:00` and 1,082 rows. Of those, 887 have non-zero `nearby_count`, the maximum is 40, and all 8 rows without coordinates remain valid. The history CSV was uploaded with the expected nine columns and 1,082 rows to `干扰历史数据/2026-08-06/干扰数据处理结果_20260806150000.csv`. +- A second run `e1c81ada63234b3c85a377e1b46a1370` returned success with `status=skipped`, confirming that an existing database time plus a non-empty history file avoids duplicate work. +- `MetrixApiClient` uses a proxy-free opener because the API is an intranet service and the Windows system proxy previously converted direct requests into `502` responses. The verified Metrix API remains `http://188.5.127.115:18271`; external port `9082` currently serves CapacityReport and must not be used by this script. diff --git a/main.py b/main.py index 3db5d9f..c4c9134 100644 --- a/main.py +++ b/main.py @@ -19,7 +19,7 @@ import time from typing import Protocol from urllib.error import HTTPError, URLError from urllib.parse import quote, urlencode -from urllib.request import Request, urlopen +from urllib.request import ProxyHandler, Request, build_opener import warnings import zipfile @@ -225,6 +225,7 @@ class MetrixApiClient: raise ProcessingError("METRIX_API_TOKEN is required for Metrix API access") self.base_url = base_url.rstrip("/") self.token = token + self.opener = build_opener(ProxyHandler({})) def get_bytes(self, endpoint: str, query: dict[str, object] | None = None, timeout: int = 30) -> bytes: return self._request("GET", endpoint, query=query, timeout=timeout) @@ -285,7 +286,7 @@ class MetrixApiClient: last_error: Exception | None = None for attempt in range(3): try: - with urlopen(request, timeout=timeout) as response: + with self.opener.open(request, timeout=timeout) as response: return response.read() except HTTPError as exc: detail = exc.read().decode("utf-8", "replace")[:1000]