summarize-product-review-samples.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. """Summarize exact product identity, observed likes and task-specific bills."""
  2. from collections import Counter
  3. import json
  4. from pathlib import Path
  5. import re
  6. PLACEHOLDER = re.compile(r'未填写评价|该用户觉得商品|此用户没有填写|系统默认')
  7. def payload(response):
  8. return response.get('structuredContent', {})
  9. def sample(folder):
  10. reservation = json.loads((folder / 'reservation.json').read_text(encoding='utf-8'))
  11. result_file = folder / 'mcp-result.json'
  12. if not result_file.is_file():
  13. return {'label': folder.name, 'status': 'pending', 'reservationCny': 0.30}
  14. result = payload(json.loads(result_file.read_text(encoding='utf-8'))['response'])
  15. family = reservation['endpoint'].split('.')[0]
  16. data = result.get('data', {}).get('result', {})
  17. rows = data.get('data', []) if family == 'jd' else data.get('comments', [])
  18. matched = []
  19. for index, row in enumerate(rows):
  20. iid = str(row.get('productId')) if family == 'jd' else str(row.get('auction', {}).get('aucNumId'))
  21. rid = str(row.get('commentId')) if family == 'jd' else str(row.get('rateId'))
  22. text = row.get('commentData', '') if family == 'jd' else row.get('content', '')
  23. likes = row.get('praiseCnt') if family == 'jd' else row.get('likeCount')
  24. matched.append({'itemId': iid, 'reviewId': rid, 'content': text, 'likes': likes,
  25. 'isPlaceholder': bool(PLACEHOLDER.search(text)), 'index': index,
  26. 'matchesRequestedProduct': iid == reservation['params']['itemId']})
  27. usages = []
  28. for file in sorted(folder.glob('usage-*.json')):
  29. data_usage = payload(json.loads(file.read_text(encoding='utf-8'))['response'])
  30. if data_usage.get('status') == 'ok':
  31. usages.append(data_usage)
  32. bill = usages[-1].get('summary') if usages else None
  33. billed = bool(bill and bill.get('billedCalls', 0))
  34. return {'label': folder.name, 'productId': family + ':' + reservation['params']['itemId'],
  35. 'page': reservation['params']['page'], 'order': reservation['params'].get('orderType', 'upstream_default'),
  36. 'status': result.get('status'), 'httpStatus': result.get('summary', {}).get('httpStatus'),
  37. 'sourceTime': result.get('summary', {}).get('recordTime'),
  38. 'historicalCountText': reservation.get('historicalCountDisplay'),
  39. 'upstreamCounts': data.get('pageInfo') if family == 'jd' else {k: data.get(k) for k in
  40. ('total', 'totalFuzzy', 'maxPage', 'hasNextPage')},
  41. 'returnedReviews': len(rows), 'matchingReviews': sum(r['matchesRequestedProduct'] for r in matched),
  42. 'meaningfulMatchingReviews': sum(r['matchesRequestedProduct'] and not r['isPlaceholder'] for r in matched),
  43. 'positiveLikeMatchingReviews': sum(r['matchesRequestedProduct'] and not r['isPlaceholder'] and
  44. type(r['likes']) is int and r['likes'] > 0 for r in matched),
  45. 'likeDistribution': dict(Counter(str(r['likes']) for r in matched)),
  46. 'maxObservedLikes': max((r['likes'] for r in matched if type(r['likes']) is int), default=None),
  47. 'billing': bill, 'confirmedCostCny': bill.get('costCny', 0) if bill else 0,
  48. 'unsettledReservationCny': 0 if billed else 0.30,
  49. 'usageTraceId': reservation['usageTraceId'], 'records': matched}
  50. def summarize(root):
  51. records = [sample(folder) for folder in sorted(Path(root).iterdir()) if (folder / 'reservation.json').is_file()]
  52. return {'collectionCalls': len(records), 'successfulCalls': sum(r['status'] == 'ok' for r in records),
  53. 'confirmedCostCny': round(sum(r.get('confirmedCostCny', 0) for r in records), 2),
  54. 'unsettledReservationCny': round(sum(r.get('unsettledReservationCny', r.get('reservationCny', 0)) for r in records), 2),
  55. 'records': records}
  56. if __name__ == '__main__':
  57. report = summarize('work/product-reviews/live-r1')
  58. print(json.dumps({**report, 'records': [{k: v for k, v in row.items() if k != 'records'} for row in report['records']]},
  59. ensure_ascii=False, indent=2))