import httpx, json, re from pathlib import Path from collections import defaultdict env = {} for line in Path('E:/workSpace/02-ClassFeedback/.env').read_text(encoding='utf-8').split('\n'): if '=' in line and not line.startswith('#'): k, v = line.split('=', 1) env[k.strip()] = v.strip() client = httpx.Client(base_url=env['OJ_BASE_URL'], follow_redirects=True, timeout=30) client.post('/login', json={'uname': env['OJ_USERNAME'], 'password': env['OJ_PASSWORD']}) hw_id = '6a35eadbc2880e90d8609759' # Step 1: Get problem names r = client.get(f'/homework/{hw_id}') problems_html = re.findall(r'href=\"/p/([^\"]+)\?tid=' + hw_id + r'\"[^>]*>([^<]+)(?: |\s)*([^<]*)', r.text) pid_map = {} for pid, pid_display, pname in problems_html: pname_clean = pname.strip() if pname.strip() else pid_display pid_map[pid] = pname_clean print(f'Problem: {pid} -> {pname_clean}') # Step 2: Try to get problem IDs from API # First get all records to find actual pids all_records = [] for page in range(1, 20): r = client.get(f'/d/system/record?tid={hw_id}&page={page}', headers={'Accept': 'application/json'}) data = r.json() rdocs = data.get('rdocs', []) if not rdocs: break all_records.extend(rdocs) print(f'Page {page}: {len(rdocs)} records') print(f'\nTotal records: {len(all_records)}') # Step 3: Collect unique pids and build lookup # Get problem info from API unique_pids = set(r['pid'] for r in all_records) print(f'Unique pids: {unique_pids}') # Map pid -> problem name using pdict # The pid_map from homework page uses string IDs, API uses numeric pids # Try to get problem info from the homework page's pdict r = client.get(f'/homework/{hw_id}') pdict_match = re.search(r'pdict\s*=\s*({[^;]+})', r.text) if pdict_match: try: pdict = json.loads(pdict_match.group(1)) print(f'\npdict found: {len(pdict)} entries') for k, v in list(pdict.items())[:5]: print(f' {k}: {v}') except: pass # Also try to get problem title from problem page pid_name_map = {} for pid in unique_pids: try: r = client.get(f'/p/{pid}') title_match = re.search(r'([^<]+)', r.text) if title_match: title = title_match.group(1).split(' - ')[0].strip() pid_name_map[pid] = title print(f' pid {pid} -> {title}') except: pass # Step 4: Map uid -> name using user API uid_names = {} unique_uids = set(r['uid'] for r in all_records) for uid in unique_uids: try: r = client.get(f'/user/{uid}') name_match = re.search(r'([^<]+)', r.text) if name_match: name = name_match.group(1).split(' - ')[0].strip() uid_names[uid] = name print(f' uid {uid} -> {name}') except: pass # Step 5: Status codes STATUS_MAP = {1: '通过', 2: '答案错误', 3: '编译错误', 4: '运行错误', 5: '超时', 6: '超内存', 7: '输出格式错误', 8: '运行时错误'} # Step 6: Organize per student per problem targets = ['杨林轩', '欧俊宇', '汪子杰', '谢明泓'] print('\n' + '='*60) print('CSP03-17 OJ 做题数据 (A包 - GESP真题训练)') print('='*60) # Build reverse map: name -> uid name_uid = {v: k for k, v in uid_names.items()} for name in targets: uid = name_uid.get(name) if not uid: print(f'\n【{name}】: 未找到匹配的OJ用户') continue records = [r for r in all_records if r['uid'] == uid] if not records: print(f'\n【{name}】: 无提交记录') continue # Group by pid by_pid = defaultdict(list) for rec in records: by_pid[rec['pid']].append(rec) print(f'\n【{name}】共 {len(records)} 次提交:') total_score = 0 for pid, recs in sorted(by_pid.items()): pname = pid_name_map.get(pid, f'题目{pid}') # Get best score best = max(recs, key=lambda r: r['score']) status_text = STATUS_MAP.get(best['status'], f'状态{best["status"]}') # Check if any AC has_ac = any(r['status'] == 1 for r in recs) ac_mark = '✅' if has_ac else '❌' print(f' {ac_mark} {pname}: {status_text} | 最高分: {best["score"]} | 提交 {len(recs)} 次') # Show score progression if multiple attempts if len(recs) > 1: scores = [r['score'] for r in sorted(recs, key=lambda r: r['judgeAt'])] print(f' 分数变化: {scores}') total_score += best['score'] print(f' 📊 总分: {total_score}') # Also show per-problem summary print('\n' + '='*60) print('按题目汇总') print('='*60) for pid in sorted(unique_pids): pname = pid_name_map.get(pid, f'题目{pid}') print(f'\n{pname}:') for name in targets: uid = name_uid.get(name) if not uid: continue records = [r for r in all_records if r['uid'] == uid and r['pid'] == pid] if not records: print(f' {name}: 未提交') else: best = max(records, key=lambda r: r['score']) status_text = STATUS_MAP.get(best['status'], f'状态{best["status"]}') ac = '✅' if best['status'] == 1 else '❌' print(f' {ac} {name}: {best["score"]}分 ({len(records)}次提交) - {status_text}')