research-workshop-quality-r13.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. """Bounded read-only retrieval for report revision. Never publishes or writes business data."""
  2. import concurrent.futures
  3. import datetime
  4. import hashlib
  5. import json
  6. import urllib.error
  7. import urllib.request
  8. from pathlib import Path
  9. ROOT = Path(__file__).resolve().parents[1]
  10. OUT = ROOT / 'work/workshop-report-quality-20261110/data'
  11. OUT.mkdir(parents=True, exist_ok=True)
  12. def read(name, body):
  13. target = OUT / (name + '.json')
  14. if target.exists():
  15. return json.loads(target.read_bytes())
  16. req = urllib.request.Request('https://kangshifu.fmode.cn/api/library',
  17. data=json.dumps(body, ensure_ascii=False).encode(),
  18. headers={'Content-Type': 'application/json'}, method='POST')
  19. try:
  20. response = urllib.request.urlopen(req, timeout=45)
  21. except urllib.error.HTTPError as e:
  22. response = e
  23. raw = response.read()
  24. target.write_bytes(raw)
  25. data = json.loads(raw)
  26. receipt = dict(name=name, url=req.full_url, request=body,
  27. checkedAt=datetime.datetime.now(datetime.timezone.utc).isoformat(),
  28. httpStatus=response.code, responseSha256=hashlib.sha256(raw).hexdigest(),
  29. readOnly=data.get('readOnly'), databaseWrites=data.get('databaseWrites'),
  30. ok=data.get('ok'), generation=data.get('generation'), error=data.get('error'))
  31. (OUT/(name+'.request.json')).write_text(json.dumps(receipt, ensure_ascii=False, indent=2), encoding='utf8')
  32. assert not data.get('ok') or data.get('readOnly') is True and data.get('databaseWrites') == 0
  33. return data
  34. def main():
  35. version = read('version', {'op': 'version'})
  36. assert version.get('ok'), version.get('error')
  37. gen = version['generation']
  38. jobs = [
  39. ('ksf-banmian-products', dict(op='browse', entity='products', filters={'brand':'ksf','category':'banmian'}, limit=50)),
  40. ('ksf-mixed', dict(op='browse', entity='products', filters={'brand':'ksf','category':'banmian'}, keyword='组合', limit=50)),
  41. ('flavour-switch', dict(op='browse', entity='reviews', filters={'category':'banmian'}, keyword='口味', limit=50)),
  42. ('flavour-fatigue', dict(op='browse', entity='reviews', filters={'category':'banmian'}, keyword='吃腻', limit=50)),
  43. ('small-pack', dict(op='browse', entity='products', filters={'category':'banmian'}, keyword='3', limit=50)),
  44. ]
  45. def job(kv):
  46. name, q = kv
  47. d = read(name, {**q, 'generation':gen})
  48. return dict(name=name, ok=d.get('ok'), matched=d.get('totalMatched'), returned=len(d.get('records',[])), error=d.get('error'))
  49. with concurrent.futures.ThreadPoolExecutor(max_workers=2) as pool:
  50. results = list(pool.map(job, jobs))
  51. print(json.dumps(dict(generation=gen, results=results), ensure_ascii=False))
  52. if __name__ == '__main__':
  53. main()