补全9月11日至13日课评:为13名学生创建请假文档,为5个班级创建总结文档
This commit is contained in:
140
.claude/memory/temp/fetch_student_code.py
Normal file
140
.claude/memory/temp/fetch_student_code.py
Normal file
@@ -0,0 +1,140 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Fetch final submission code per (student, problem) for CSP04 lesson 01 (基础回顾).
|
||||
Targets both CSP04 sections (0830 & 1400). Saves to JSON.
|
||||
"""
|
||||
import io, sys, json, re, httpx
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace')
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent
|
||||
env = {}
|
||||
for line in (PROJECT_ROOT / '.env').read_text(encoding='utf-8').split('\n'):
|
||||
line = line.strip()
|
||||
if line and not line.startswith('#') and '=' in line:
|
||||
k, v = line.split('=', 1)
|
||||
env[k] = v
|
||||
|
||||
base = env.get('OJ_BASE_URL', 'https://oj.qonnwolf.com')
|
||||
TID = '6a8c6a8b8499477a04169f85' # CSP04 lesson 01 基础回顾 (A包)
|
||||
|
||||
# 数字题目 -> 展示名(参考课件 OJ ID)
|
||||
PID_MAP = {
|
||||
6775: 'S0401P1 小杨的回文串',
|
||||
6776: 'S0401P2 小杨有序去重',
|
||||
6777: 'S0401P3 小杨的进制数位',
|
||||
6968: 'S0401P4 小杨的幸运数字',
|
||||
6969: 'S0401P5 小杨的报数游戏',
|
||||
}
|
||||
|
||||
CLASSES = {
|
||||
'0830': ['欧俊宇', '汪子杰', '谢明泓', '王佳琳', '魏铭轩', '张立坤'],
|
||||
'1400': ['郑子煜', '张雨禾', '苏俊宇', '卢华琛', '鲍佳音'],
|
||||
}
|
||||
ALL_TARGETS = sorted({n for lst in CLASSES.values() for n in lst})
|
||||
|
||||
STATUS_NAMES = {1: 'AC', 2: 'WA', 3: 'CE', 4: 'RE', 5: 'TLE', 6: 'MLE', 7: 'PE', 8: 'OLE'}
|
||||
|
||||
client = httpx.Client(base_url=base, follow_redirects=True, timeout=30)
|
||||
r = client.post('/login', json={'uname': env.get('OJ_USERNAME'), 'password': env.get('OJ_PASSWORD')})
|
||||
ok = any(c.name == 'sid' for c in client.cookies.jar)
|
||||
print('LOGIN:', 'OK' if ok else 'FAIL')
|
||||
if not ok:
|
||||
raise SystemExit('login failed')
|
||||
|
||||
# 1. fetch all records for the homework
|
||||
records = []
|
||||
seen = set()
|
||||
for page in range(1, 40):
|
||||
r = client.get(f'/d/system/record?tid={TID}&page={page}', headers={'Accept': 'application/json'})
|
||||
if r.status_code != 200:
|
||||
break
|
||||
try:
|
||||
rdocs = r.json().get('rdocs', [])
|
||||
except Exception:
|
||||
break
|
||||
if not rdocs:
|
||||
break
|
||||
for rec in rdocs:
|
||||
rid = rec.get('_id')
|
||||
if rid in seen:
|
||||
continue
|
||||
seen.add(rid)
|
||||
records.append({
|
||||
'rid': rid, 'uid': rec.get('uid'), 'pid': rec.get('pid'),
|
||||
'status': rec.get('status'), 'score': rec.get('score', 0),
|
||||
'judgeAt': rec.get('judgeAt', ''), 'lang': rec.get('lang'),
|
||||
})
|
||||
if len(rdocs) < 100:
|
||||
break
|
||||
print('TOTAL RECORDS:', len(records))
|
||||
|
||||
# 2. build uid -> name
|
||||
uids = {rec['uid'] for rec in records}
|
||||
uid_name = {}
|
||||
for uid in sorted(uids):
|
||||
try:
|
||||
rr = client.get(f'/user/{uid}')
|
||||
m = re.search(r'<title>([^<]+)</title>', rr.text)
|
||||
if m:
|
||||
nm = m.group(1).split(' - ')[0].strip()
|
||||
uid_name[uid] = nm
|
||||
except Exception:
|
||||
pass
|
||||
print('UID->NAME mapped:', len(uid_name))
|
||||
for rec in records:
|
||||
rec['name'] = uid_name.get(rec['uid'], '')
|
||||
|
||||
# 3. for each target student, per problem pick the LAST submission (max judgeAt by iso)
|
||||
last_by_key = {}
|
||||
for rec in records:
|
||||
if rec['name'] not in ALL_TARGETS:
|
||||
continue
|
||||
if rec['pid'] not in PID_MAP:
|
||||
continue
|
||||
key = (rec['name'], rec['pid'])
|
||||
if key not in last_by_key or rec['judgeAt'] > last_by_key[key]['judgeAt']:
|
||||
last_by_key[key] = rec
|
||||
print('LAST-SUB keys:', len(last_by_key))
|
||||
|
||||
# 4. fetch code per (name, pid)
|
||||
result = {}
|
||||
sample_shown = 0
|
||||
for (name, pid), rec in last_by_key.items():
|
||||
try:
|
||||
rr = client.get(f"/d/system/record/{rec['rid']}", headers={'Accept': 'application/json'})
|
||||
j = rr.json()
|
||||
rdoc = j.get('rdoc', {})
|
||||
code = rdoc.get('code', '')
|
||||
lang = rdoc.get('lang') or rec.get('lang')
|
||||
except Exception as e:
|
||||
code = ''
|
||||
lang = None
|
||||
entry = {
|
||||
'key': PID_MAP[pid],
|
||||
'code': code,
|
||||
'lang': lang,
|
||||
'status': STATUS_NAMES.get(rec['status'], '?'),
|
||||
'score': rec.get('score'),
|
||||
'attempts': sum(1 for x in records if x['name'] == name and x['pid'] == pid),
|
||||
'judgeAt': rec.get('judgeAt'),
|
||||
}
|
||||
result.setdefault(name, {})[str(pid)] = entry
|
||||
if sample_shown < 2 and code:
|
||||
sample_shown += 1
|
||||
print(f"SAMPLE CODE [{name} / {PID_MAP[pid]}] lang={lang}:\n{code[:300]}")
|
||||
|
||||
client.close()
|
||||
|
||||
out = {
|
||||
'tid': TID,
|
||||
'classes': CLASSES,
|
||||
'pid_map': {str(k): v for k, v in PID_MAP.items()},
|
||||
'students': result,
|
||||
}
|
||||
out_path = PROJECT_ROOT / '.claude' / 'memory' / 'temp' / 'student_code_CSP04_L01.json'
|
||||
out_path.write_text(json.dumps(out, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
print('SAVED ->', out_path)
|
||||
print('students with code:', sorted(result.keys()))
|
||||
Reference in New Issue
Block a user