|
@@ -25,6 +25,7 @@ from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
|
from pathlib import Path
|
|
from pathlib import Path
|
|
|
from urllib.parse import urlparse, parse_qs
|
|
from urllib.parse import urlparse, parse_qs
|
|
|
import urllib.request
|
|
import urllib.request
|
|
|
|
|
+import urllib.parse
|
|
|
import urllib.error
|
|
import urllib.error
|
|
|
|
|
|
|
|
try:
|
|
try:
|
|
@@ -49,6 +50,29 @@ def _matrix():
|
|
|
return _MATRIX_CACHE
|
|
return _MATRIX_CACHE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+CATEGORY_API = "https://library.aiddit.com/api/pattern/executions/401/category-tree"
|
|
|
|
|
+CAT_CACHE_DIR = HERE / ".cache" / "category_tree"
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def _category_tree(source_type):
|
|
|
|
|
+ """拉取并缓存 实质/形式 分类树(library.aiddit.com)。缓存命中直接读盘;
|
|
|
|
|
+ 上游不可达则抛异常(do_GET 转 500,前端降级)。"""
|
|
|
|
|
+ if source_type not in ("实质", "形式"):
|
|
|
|
|
+ raise ValueError("source_type 只能是 实质/形式")
|
|
|
|
|
+ CAT_CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
+ cache = CAT_CACHE_DIR / f"{source_type}.json"
|
|
|
|
|
+ if cache.is_file():
|
|
|
|
|
+ return json.loads(cache.read_text(encoding="utf-8"))
|
|
|
|
|
+ url = CATEGORY_API + "?" + urllib.parse.urlencode({"source_type": source_type})
|
|
|
|
|
+ req = urllib.request.Request(url, headers={"Accept": "application/json"})
|
|
|
|
|
+ with urllib.request.urlopen(req, timeout=20) as resp:
|
|
|
|
|
+ data = json.loads(resp.read().decode("utf-8"))
|
|
|
|
|
+ tmp = cache.with_suffix(".tmp")
|
|
|
|
|
+ tmp.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
|
|
|
|
|
+ tmp.replace(cache)
|
|
|
|
|
+ return data
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
LOG_DIR = HERE / "runs" / "logs"
|
|
LOG_DIR = HERE / "runs" / "logs"
|
|
|
# 工序步骤 via 字段的「无具体工具」占位符,不计入工序提及工具 TOP 榜
|
|
# 工序步骤 via 字段的「无具体工具」占位符,不计入工序提及工具 TOP 榜
|
|
|
_VIA_PLACEHOLDERS = {"-", "—", "-", "--", "/", "无", "n/a", "none"}
|
|
_VIA_PLACEHOLDERS = {"-", "—", "-", "--", "/", "无", "n/a", "none"}
|
|
@@ -435,6 +459,8 @@ class Handler(BaseHTTPRequestHandler):
|
|
|
self.wfile.write(body)
|
|
self.wfile.write(body)
|
|
|
elif u.path == "/api/query_matrix":
|
|
elif u.path == "/api/query_matrix":
|
|
|
self._json_etag(_matrix())
|
|
self._json_etag(_matrix())
|
|
|
|
|
+ elif u.path == "/api/category_tree":
|
|
|
|
|
+ self._json_etag(_category_tree(qs.get("source_type", "实质")))
|
|
|
elif u.path == "/api/dashboard":
|
|
elif u.path == "/api/dashboard":
|
|
|
self._json_etag(_dashboard_cached())
|
|
self._json_etag(_dashboard_cached())
|
|
|
elif u.path == "/api/queries":
|
|
elif u.path == "/api/queries":
|