149 lines
7.3 KiB
Python
149 lines
7.3 KiB
Python
"""Swagger Try-it-out 等效脚本:CMS 各角色概览大屏(第八章 CMS-STATS-*)。
|
|
|
|
CMS-STATS-SUP-1 平台总览 / HOS-1 医院驾驶舱 / CNT-1 内容概览 / TEA-1 教学概览。
|
|
为产出**真实出参示例**,先在示例机构内播种一份连贯数据(机构/4 类管理员/2 学生/师生关系/
|
|
2 病例/3 条训练记录),调用后把完整 JSON 落到 logs/swagger-cms-stats-examples.json,最后清理。
|
|
运行方式:.venv\\Scripts\\python.exe test/swagger_cms_stats.py
|
|
前提:Django dev server 已在 http://127.0.0.1:8000 运行,Redis 已启动。
|
|
"""
|
|
import sys
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import requests
|
|
|
|
sys.stdout.reconfigure(encoding='utf-8')
|
|
sys.stderr.reconfigure(encoding='utf-8')
|
|
|
|
BASE = 'http://127.0.0.1:8000'
|
|
PYTHON = r'D:\01Agent\medical_training\.venv\Scripts\python.exe'
|
|
CWD = r'D:\01Agent\medical_training'
|
|
PASS, FAIL = 'PASS', 'FAIL'
|
|
results = []
|
|
EXAMPLES = {}
|
|
|
|
OVERVIEW = '/api/cms/stats/overview/'
|
|
HOSPITAL = '/api/cms/stats/hospital/overview/'
|
|
CONTENT = '/api/cms/stats/content/overview/'
|
|
TEACHING = '/api/cms/stats/teaching/overview/'
|
|
|
|
PHONES = ['13700008001', '13700008002', '13700008003', '13700008004', '13700008005', '13700008006']
|
|
|
|
|
|
def log(api_id, method, url, expected, actual, detail=''):
|
|
exp = expected if isinstance(expected, (list, tuple)) else [expected]
|
|
status = PASS if actual in exp else FAIL
|
|
results.append((api_id, status))
|
|
print(f' {status} {api_id:<18} {method:<5} {url:<40} expect={str(expected):<9} got={actual} {detail}')
|
|
|
|
|
|
def django_eval(code):
|
|
pre = 'import django, os; os.environ.setdefault("DJANGO_SETTINGS_MODULE","config.settings"); django.setup()\n'
|
|
p = subprocess.run([PYTHON, '-c', pre + code], capture_output=True, text=True, cwd=CWD)
|
|
if p.returncode != 0:
|
|
print('[django_eval ERROR]', p.stderr[-1500:])
|
|
return p.stdout.strip()
|
|
|
|
|
|
def jb(r):
|
|
return r.json() if r.headers.get('content-type', '').startswith('application/json') else None
|
|
|
|
|
|
SEED = r'''
|
|
from apps.user.models import User, Institution, Department, TeacherStudentRelation
|
|
from apps.case.models import CaseBase
|
|
from rest_framework_simplejwt.tokens import RefreshToken
|
|
from django.db import connection
|
|
from django.utils import timezone
|
|
import json
|
|
PH = ["13700008001","13700008002","13700008003","13700008004","13700008005","13700008006"]
|
|
User.all_objects.filter(phone__in=PH).delete()
|
|
CaseBase.all_objects.filter(title__startswith="概览示例-").hard_delete()
|
|
with connection.cursor() as c:
|
|
c.execute("DELETE FROM training_record WHERE external_user_id='SWGSTAT'")
|
|
inst,_ = Institution.objects.get_or_create(code="SWG_STAT", defaults={"name":"概览示例院","type":"hospital","level":"三甲"})
|
|
dept,_ = Department.objects.get_or_create(name="心内科", defaults={"category":"临床"})
|
|
mk = lambda ph,nm,rt: User.objects.create_user(username=ph,password=None,phone=ph,real_name=nm,role_type=rt,institution=inst,status=1)
|
|
su=mk(PH[0],"示例超管","super_admin"); hu=mk(PH[1],"示例院管","hospital_admin")
|
|
cu=mk(PH[2],"示例内容","content_admin"); du=mk(PH[3],"示例带教","doctor")
|
|
s1=mk(PH[4],"学生甲","student"); s2=mk(PH[5],"学生乙","student")
|
|
s1.department=dept; s1.save(update_fields=["department"]); s2.department=dept; s2.save(update_fields=["department"])
|
|
TeacherStudentRelation.objects.create(teacher=du,student=s1,status=1)
|
|
TeacherStudentRelation.objects.create(teacher=du,student=s2,status=1)
|
|
c1=CaseBase.objects.create(title="概览示例-急性心梗",case_type="traditional",institution=inst,department=dept,publish_status=2,difficulty="medium",created_by=cu)
|
|
c2=CaseBase.objects.create(title="概览示例-稳定型心绞痛",case_type="traditional",institution=inst,department=dept,publish_status=1,difficulty="easy",created_by=cu)
|
|
DIMS=[{"dimension":"信息获取","score":18,"max_score":20},{"dimension":"分析推理","score":17,"max_score":20},{"dimension":"处置决策","score":8,"max_score":10},{"dimension":"沟通人文","score":7,"max_score":10},{"dimension":"临床整合","score":8,"max_score":10}]
|
|
now=timezone.now()
|
|
rows=[(s1.id,c1.id,88),(s2.id,c1.id,76),(s1.id,c2.id,92)]
|
|
with connection.cursor() as c:
|
|
for uid,cid,sc in rows:
|
|
c.execute("INSERT INTO training_record (user_id,case_id,training_mode,case_type,status,total_score,duration_seconds,end_time,start_time,score_type,evaluation_level,feedback,thinking_chain,diagnosis_path,wrong_points,missed_questions,recommendation_result,osce_station_score,emotion_analysis,ai_feedback_structured,external_user_id,created_at,updated_at) VALUES (%s,%s,'practice','traditional','completed',%s,1800,%s,%s,'percentage','good','','','','[]','[]','{}','{}','{}',%s,'SWGSTAT',%s,%s)",[uid,cid,sc,now,now,json.dumps({"dimension_scores":DIMS},ensure_ascii=False),now,now])
|
|
print("|".join(str(RefreshToken.for_user(x).access_token) for x in [su,hu,cu,du]))
|
|
'''
|
|
|
|
CLEANUP = r'''
|
|
from apps.user.models import User
|
|
from apps.case.models import CaseBase
|
|
from django.db import connection
|
|
PH = ["13700008001","13700008002","13700008003","13700008004","13700008005","13700008006"]
|
|
with connection.cursor() as c:
|
|
c.execute("DELETE FROM training_record WHERE external_user_id='SWGSTAT'")
|
|
CaseBase.all_objects.filter(title__startswith="概览示例-").hard_delete()
|
|
User.all_objects.filter(phone__in=PH).delete()
|
|
print("cleaned")
|
|
'''
|
|
|
|
print('\n[准备] 播种示例机构数据(机构/4 管理员/2 学生/师生关系/2 病例/3 训练记录)...')
|
|
out = django_eval(SEED)
|
|
su, hu, cu, du = out.split('|')
|
|
SU = {'Authorization': f'Bearer {su}'}
|
|
HU = {'Authorization': f'Bearer {hu}'}
|
|
CU = {'Authorization': f'Bearer {cu}'}
|
|
DU = {'Authorization': f'Bearer {du}'}
|
|
print('[准备] 完成\n')
|
|
|
|
print('=' * 100)
|
|
print(' CMS 各角色概览大屏 Swagger Try-it-out')
|
|
print('=' * 100)
|
|
|
|
# 权限
|
|
log('anon-401', 'GET', OVERVIEW, 401, requests.get(f'{BASE}{OVERVIEW}').status_code)
|
|
log('SUP-role403', 'GET', OVERVIEW, 403, requests.get(f'{BASE}{OVERVIEW}', headers=HU).status_code)
|
|
log('HOS-role403', 'GET', HOSPITAL, 403, requests.get(f'{BASE}{HOSPITAL}', headers=SU).status_code)
|
|
log('CNT-role403', 'GET', CONTENT, 403, requests.get(f'{BASE}{CONTENT}', headers=DU).status_code)
|
|
log('TEA-role403', 'GET', TEACHING, 403, requests.get(f'{BASE}{TEACHING}', headers=CU).status_code)
|
|
|
|
|
|
def call(api_id, url, headers):
|
|
r = requests.get(f'{BASE}{url}', headers=headers)
|
|
b = jb(r)
|
|
log(api_id, 'GET', url, 200, r.status_code)
|
|
if r.status_code == 200:
|
|
EXAMPLES[api_id] = {'request': {'method': 'GET', 'url': url,
|
|
'headers': {'Authorization': 'Bearer <access>'}},
|
|
'response': b}
|
|
return b
|
|
|
|
|
|
call('CMS-STATS-SUP-1', OVERVIEW, SU)
|
|
call('CMS-STATS-HOS-1', HOSPITAL, HU)
|
|
call('CMS-STATS-CNT-1', CONTENT, CU)
|
|
call('CMS-STATS-TEA-1', TEACHING, DU)
|
|
|
|
# 落盘完整示例
|
|
exf = Path(CWD) / 'logs' / 'swagger-cms-stats-examples.json'
|
|
exf.parent.mkdir(exist_ok=True)
|
|
exf.write_text(json.dumps(EXAMPLES, ensure_ascii=False, indent=2), encoding='utf-8')
|
|
print(f'\n[示例] 完整出参已写入 {exf}')
|
|
|
|
django_eval(CLEANUP)
|
|
|
|
print('=' * 100)
|
|
total = len(results); passed = sum(1 for _, s in results if s == PASS); failed = total - passed
|
|
print(f' 总计: {total} | 通过: {passed} | 失败: {failed}')
|
|
if failed:
|
|
print(' 失败:', [a for a, s in results if s == FAIL]); sys.exit(1)
|
|
print(' ALL PASSED — CMS 各角色概览大屏接口验证通过!')
|
|
sys.exit(0)
|