fix
This commit is contained in:
parent
871b4baadc
commit
d1d786078c
40
add_phone_column.py
Normal file
40
add_phone_column.py
Normal file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
|
||||
def add_phone_column():
|
||||
"""为现有的students表添加phone列"""
|
||||
db_path = '/Users/moshui/Documents/survey/data/survey.db'
|
||||
|
||||
if not os.path.exists(db_path):
|
||||
print(f"数据库不存在: {db_path}")
|
||||
return
|
||||
|
||||
conn = sqlite3.connect(db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
try:
|
||||
# 检查phone列是否已存在
|
||||
cursor.execute("PRAGMA table_info(students)")
|
||||
columns = [column[1] for column in cursor.fetchall()]
|
||||
|
||||
if 'phone' not in columns:
|
||||
# 添加phone列
|
||||
cursor.execute("ALTER TABLE students ADD COLUMN phone TEXT")
|
||||
print("已成功添加phone列到students表")
|
||||
|
||||
# 提交更改
|
||||
conn.commit()
|
||||
else:
|
||||
print("phone列已存在,无需添加")
|
||||
|
||||
except Exception as e:
|
||||
print(f"添加phone列时出错: {e}")
|
||||
conn.rollback()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
add_phone_column()
|
||||
@ -5,13 +5,13 @@ meta {
|
||||
}
|
||||
|
||||
post {
|
||||
url: http://120.26.23.172:5678/webhook/survey_report
|
||||
url: http://120.26.23.172:5678/webhook-test/survey_report
|
||||
body: json
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
body:json {
|
||||
{"analysis_text":"#\n 考试答题情况分析\n\n| 题目 | 题型 | 用户答案 | 正确答案 |\n 是否正确\n |\n|------|------|----------|----------|----------|\n| 姓名 |\n 填空题 | 小王 | 无标准答案 | 无法判断 |\n| 学校 | 填空题 |\n 时代小学 | 无标准答案 | 无法判断 |\n|\n 测量书本长度时,读数视线应与刻度尺: | 单选题 | 垂直 | 垂直 |\n 正确 |\n| 制作过山车时,连接处不牢固会导致? | 单选题 |\n 轨道倒塌 | 小球运动受阻 | 错误 |\n|\n 光从空气斜射入水中时,传播方向会? | 单选题 | 发生偏折 |\n 发生偏折 | 正确 |\n| 血液循环为身体细胞提供? | 单选题 |\n 情绪和感觉 | 氧气和营养物质 | 错误 |\n|\n 用气球驱动小车时,小车运动的方向是? | 单选题 |\n 与气球喷气方向相反 | 与气球喷气方向相反 | 正确 |\n|\n 工程设计过程的第一步通常是: | 单选题 | 明确问题和限制条件 |\n 明确问题和限制条件 | 正确 |\n| 用来判断一个物体是否运动的另一\n 个物体,叫做参照物。下列有关参照物的说法正确的是: | 单选题 |\n 参照物的选择是任意的 | 参照物的选择是任意的 | 正确 |\n|\n 食物在胃里会发生什么变化? | 单选题 | 被吸收大部分营养 |\n 被胃液分解 | 错误 |\n| 要使弦发出的声音变高,应该怎么做? |\n 单选题 | 加粗弦 | 拉紧弦 | 错误 |\n|\n 下列水污染源中,属于点污染源的是: | 单选题 | 城市街道的雨水 |\n 某化工厂的排污口 | 错误 |\n|\n 地球板块构造理论认为,全球岩石圈主要由多大板块组成? | 单选题\n | 六大板块 | 六大板块 | 正确 |\n| 我们吃花生是吃植物的哪部分?\n | 单选题 | 种子 | 种子 | 正确 |\n|\n 如果我们想提高作物产量,可以采取什么措施? | 单选题 | 适时灌溉\n | 以上都是 | 错误 |\n| 植物进行光合作用需要什么? | 单选题 |\n 水、土壤、肥料 | 阳光、水、二氧化碳 | 错误 |\n|\n 同一斜面,哪种情况机械效率最高? | 单选题 | 粗糙表面 |\n 光滑表面 | 错误 |\n| 为什么结晶是重要的分离提纯方法? | 单选题\n | 操作相对简单 | 以上都是 | 错误 |\n"}
|
||||
{"analysis_text":"#\n 考试答题情况分析\n\n| 题目 | 题型 | 用户答案 | 正确答案 |\n 是否正确\n |\n|------|------|----------|----------|----------|\n| 姓名 |\n 填空题 | 小王 | 无标准答案 | 无法判断 |\n| 学校 | 填空题 |\n 时代小学 | 无标准答案 | 无法判断 |\n| 班级 | 填空题 |\n 三年级一班 | 无标准答案 | 无法判断 |\n| 手机号 | 填空题 |\n 13800138000 | 无标准答案 | 无法判断 |\n|\n 测量书本长度时,读数视线应与刻度尺: | 单选题 | 垂直 | 垂直 |\n 正确 |\n| 制作过山车时,连接处不牢固会导致? | 单选题 |\n 轨道倒塌 | 小球运动受阻 | 错误 |\n|\n 光从空气斜射入水中时,传播方向会? | 单选题 | 发生偏折 |\n 发生偏折 | 正确 |\n| 血液循环为身体细胞提供? | 单选题 |\n 情绪和感觉 | 氧气和营养物质 | 错误 |\n|\n 用气球驱动小车时,小车运动的方向是? | 单选题 |\n 与气球喷气方向相反 | 与气球喷气方向相反 | 正确 |\n|\n 工程设计过程的第一步通常是: | 单选题 | 明确问题和限制条件 |\n 明确问题和限制条件 | 正确 |\n| 用来判断一个物体是否运动的另一\n 个物体,叫做参照物。下列有关参照物的说法正确的是: | 单选题 |\n 参照物的选择是任意的 | 参照物的选择是任意的 | 正确 |\n|\n 食物在胃里会发生什么变化? | 单选题 | 被吸收大部分营养 |\n 被胃液分解 | 错误 |\n| 要使弦发出的声音变高,应该怎么做? |\n 单选题 | 加粗弦 | 拉紧弦 | 错误 |\n|\n 下列水污染源中,属于点污染源的是: | 单选题 | 城市街道的雨水 |\n 某化工厂的排污口 | 错误 |\n|\n 地球板块构造理论认为,全球岩石圈主要由多大板块组成? | 单选题\n | 六大板块 | 六大板块 | 正确 |\n| 我们吃花生是吃植物的哪部分?\n | 单选题 | 种子 | 种子 | 正确 |\n|\n 如果我们想提高作物产量,可以采取什么措施? | 单选题 | 适时灌溉\n | 以上都是 | 错误 |\n| 植物进行光合作用需要什么? | 单选题 |\n 水、土壤、肥料 | 阳光、水、二氧化碳 | 错误 |\n|\n 同一斜面,哪种情况机械效率最高? | 单选题 | 粗糙表面 |\n 光滑表面 | 错误 |\n| 为什么结晶是重要的分离提纯方法? | 单选题\n | 操作相对简单 | 以上都是 | 错误 |\n","session_id":"session_id"}
|
||||
}
|
||||
|
||||
settings {
|
||||
|
||||
142
excel_reader.py
142
excel_reader.py
@ -68,7 +68,12 @@ class ExcelQuestionReader:
|
||||
# 添加额外的字段以保持兼容性
|
||||
if "标签" in question:
|
||||
question["题目标签"] = question["标签"]
|
||||
question["题目类型"] = self._determine_question_type(question["标签"])
|
||||
|
||||
# 使用题型字段确定题目类型,如果题型字段为空则使用标签
|
||||
question_type = question.get("题型", "")
|
||||
if not question_type and "标签" in question:
|
||||
question_type = self._determine_question_type(question["标签"])
|
||||
question["题目类型"] = question_type or "基础题"
|
||||
|
||||
# 根据题目类型分类
|
||||
q_type = question.get("题目类型", "基础题")
|
||||
@ -122,11 +127,13 @@ class ExcelQuestionReader:
|
||||
|
||||
return sorted(list(all_tags))
|
||||
|
||||
def get_questions_by_tag(self, selected_tag: str) -> Dict[str, Any]:
|
||||
"""根据标签筛选题目"""
|
||||
if not selected_tag or selected_tag == "全部题目":
|
||||
return self.get_questions()
|
||||
|
||||
def get_questions_by_filters(
|
||||
self,
|
||||
subject: str = "",
|
||||
grade: str = "", # 完整的年级册次信息(如:一年级上册)
|
||||
unit: str = ""
|
||||
) -> Dict[str, Any]:
|
||||
"""根据学科、年级册次、单元筛选题目"""
|
||||
questions = self.get_questions()
|
||||
filtered_questions = {
|
||||
"基础题": [],
|
||||
@ -134,14 +141,68 @@ class ExcelQuestionReader:
|
||||
"竞赛题": []
|
||||
}
|
||||
|
||||
for category, category_questions in questions.items():
|
||||
# 如果没有提供任何筛选条件,返回全部题目
|
||||
if not any([subject, grade, unit]):
|
||||
return questions
|
||||
|
||||
for category_name, category_questions in questions.items():
|
||||
for question in category_questions:
|
||||
question_tag = question.get("标签", "")
|
||||
if selected_tag in question_tag:
|
||||
filtered_questions[category].append(question)
|
||||
# 检查是否符合所有筛选条件
|
||||
match = True
|
||||
|
||||
# 学科筛选
|
||||
if subject and question.get("学科", "") != subject:
|
||||
match = False
|
||||
|
||||
# 年级册次筛选
|
||||
if grade and question.get("年级", "") != grade:
|
||||
match = False
|
||||
|
||||
# 单元筛选(包括期中、期末的特殊处理)
|
||||
if unit:
|
||||
if unit == "期中":
|
||||
# 期中:选择前一半的单元
|
||||
if not self._is_in_midterm_units(question, subject, grade):
|
||||
match = False
|
||||
elif unit == "期末":
|
||||
# 期末:选择所有单元(已经通过了年级册次筛选)
|
||||
pass # 不需要额外筛选
|
||||
elif question.get("单元", "") != unit:
|
||||
# 具体单元筛选
|
||||
match = False
|
||||
|
||||
if match:
|
||||
filtered_questions[category_name].append(question)
|
||||
|
||||
return filtered_questions
|
||||
|
||||
def _is_in_midterm_units(self, question: Dict[str, Any], subject: str, grade: str) -> bool:
|
||||
"""判断题目是否属于期中考试范围(前一半单元)"""
|
||||
unit = question.get("单元", "")
|
||||
if not unit:
|
||||
return False
|
||||
|
||||
# 获取当前年级册次的所有单元
|
||||
all_questions = self.get_questions()
|
||||
grade_units = set()
|
||||
|
||||
for category_questions in all_questions.values():
|
||||
for q in category_questions:
|
||||
if q.get("学科", "") == subject and q.get("年级", "") == grade:
|
||||
unit_name = q.get("单元", "")
|
||||
if unit_name:
|
||||
grade_units.add(unit_name)
|
||||
|
||||
# 按单元数字排序
|
||||
sorted_units = sorted(list(grade_units), key=lambda x: int(x.split('-')[0]) if x.split('-')[0].isdigit() else 0)
|
||||
|
||||
# 确定期中包含的单元(前一半)
|
||||
mid_term_count = (len(sorted_units) + 1) // 2 # 向上取整
|
||||
mid_term_units = set(sorted_units[:mid_term_count])
|
||||
|
||||
# 检查当前题目的单元是否在期中范围内
|
||||
return unit in mid_term_units
|
||||
|
||||
def clear_cache(self):
|
||||
"""清除缓存"""
|
||||
self._cache = None
|
||||
@ -162,6 +223,67 @@ def get_questions_by_tag(selected_tag: str) -> Dict[str, Any]:
|
||||
"""根据标签筛选题目(全局函数)"""
|
||||
return _excel_reader.get_questions_by_tag(selected_tag)
|
||||
|
||||
def get_questions_by_filters(
|
||||
subject: str = "",
|
||||
grade: str = "", # 完整的年级册次信息(如:一年级上册)
|
||||
unit: str = ""
|
||||
) -> Dict[str, Any]:
|
||||
"""根据学科、年级册次、单元筛选题目(全局函数)"""
|
||||
return _excel_reader.get_questions_by_filters(
|
||||
subject=subject,
|
||||
grade=grade,
|
||||
unit=unit
|
||||
)
|
||||
|
||||
def get_available_filters() -> Dict[str, List[str]]:
|
||||
"""获取所有可用的筛选条件(全局函数)"""
|
||||
questions = _excel_reader.get_questions()
|
||||
|
||||
subjects = set()
|
||||
grades = set()
|
||||
units = set()
|
||||
|
||||
for category_questions in questions.values():
|
||||
for question in category_questions:
|
||||
# 收集所有学科
|
||||
subject = question.get("学科", "")
|
||||
if subject:
|
||||
subjects.add(subject)
|
||||
|
||||
# 收集所有年级
|
||||
grade = question.get("年级", "")
|
||||
if grade:
|
||||
grades.add(grade)
|
||||
|
||||
# 收集所有单元
|
||||
unit = question.get("单元", "")
|
||||
if unit:
|
||||
units.add(unit)
|
||||
|
||||
return {
|
||||
"subjects": sorted(list(subjects)),
|
||||
"grades": _sort_grades_by_chinese_numbers(list(grades)),
|
||||
"units": sorted(list(units))
|
||||
}
|
||||
|
||||
def _sort_grades_by_chinese_numbers(grades):
|
||||
"""按中文数字顺序排序年级"""
|
||||
chinese_number_map = {
|
||||
'一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8,
|
||||
'九': 9, '十': 10, '十一': 11, '十二': 12
|
||||
}
|
||||
|
||||
import re
|
||||
|
||||
def extract_chinese_number(grade_text):
|
||||
"""从年级文本中提取中文数字"""
|
||||
match = re.match(r'^([一二三四五六七八九十]+)', grade_text)
|
||||
if match:
|
||||
return chinese_number_map.get(match.group(1), 999)
|
||||
return 999
|
||||
|
||||
return sorted(grades, key=extract_chinese_number)
|
||||
|
||||
def clear_cache():
|
||||
"""清除缓存(全局函数)"""
|
||||
_excel_reader.clear_cache()
|
||||
@ -26,6 +26,7 @@ def init_database():
|
||||
name TEXT NOT NULL,
|
||||
school TEXT NOT NULL,
|
||||
grade TEXT NOT NULL,
|
||||
phone TEXT NOT NULL,
|
||||
selected_tag TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
|
||||
115
main.py
115
main.py
@ -15,7 +15,7 @@ from typing import Dict, List, Optional, Any
|
||||
import asyncio
|
||||
import threading
|
||||
from enhanced_survey_system import enhanced_system, get_east8_time_string, get_east8_time
|
||||
from excel_reader import get_questions_data, get_all_tags, get_questions_by_tag
|
||||
from excel_reader import get_questions_data, get_questions_by_tag, get_questions_by_filters, get_available_filters
|
||||
|
||||
app = FastAPI(title="Enhanced Survey System")
|
||||
|
||||
@ -40,7 +40,15 @@ class CreateSessionRequest(BaseModel):
|
||||
name: str
|
||||
school: str
|
||||
grade: str
|
||||
phone: str = ""
|
||||
selectedSubject: str = "" # 学科
|
||||
selectedSemester: str = "" # 册次(保留兼容性)
|
||||
selectedExamType: str = "" # 考试类型(保留兼容性)
|
||||
selectedUnit: str = "" # 单元
|
||||
selectedCategory: str = "" # 分类
|
||||
selectedQuestionType: str = "" # 题型
|
||||
selectedTag: str = ""
|
||||
selectedTagsList: List[str] = []
|
||||
questionsConfig: Dict[str, int]
|
||||
|
||||
class SaveAnswersRequest(BaseModel):
|
||||
@ -75,11 +83,24 @@ async def create_session(request: CreateSessionRequest):
|
||||
name = request.name.strip()
|
||||
school = request.school.strip()
|
||||
grade = request.grade.strip()
|
||||
selected_tag = request.selectedTag.strip()
|
||||
phone = request.phone.strip()
|
||||
selected_subject = request.selectedSubject.strip()
|
||||
selected_semester = request.selectedSemester.strip()
|
||||
selected_unit = request.selectedUnit.strip()
|
||||
|
||||
# 构建描述性标签
|
||||
selected_tag = ""
|
||||
filters = []
|
||||
if selected_subject: filters.append(f"学科:{selected_subject}")
|
||||
if selected_semester: filters.append(f"年级:{selected_semester}") # 这里实际是完整年级信息
|
||||
if selected_unit: filters.append(f"单元:{selected_unit}")
|
||||
|
||||
selected_tag = " | ".join(filters) if filters else "全部题目"
|
||||
|
||||
questions_config = request.questionsConfig
|
||||
|
||||
if not name or not school or not grade:
|
||||
raise HTTPException(status_code=400, detail="姓名、学校和年级不能为空")
|
||||
if not name or not school or not grade or not phone:
|
||||
raise HTTPException(status_code=400, detail="姓名、学校、年级和手机号不能为空")
|
||||
|
||||
# 创建学员记录
|
||||
student_id = str(uuid.uuid4())
|
||||
@ -87,9 +108,9 @@ async def create_session(request: CreateSessionRequest):
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
INSERT INTO students (id, name, school, grade, selected_tag)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
''', (student_id, name, school, grade, selected_tag))
|
||||
INSERT INTO students (id, name, school, grade, phone, selected_tag)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', (student_id, name, school, grade, phone, selected_tag))
|
||||
|
||||
# 创建答题会话
|
||||
session_id = str(uuid.uuid4())
|
||||
@ -322,29 +343,40 @@ async def get_questions():
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"加载题库失败: {str(e)}")
|
||||
|
||||
@app.get("/api/tags")
|
||||
async def get_tags():
|
||||
"""获取标签数据"""
|
||||
@app.get("/api/filters")
|
||||
async def get_filters():
|
||||
"""获取所有可用的筛选条件"""
|
||||
try:
|
||||
with open('public/tags.json', 'r', encoding='utf-8') as f:
|
||||
tags_data = json.load(f)
|
||||
return tags_data
|
||||
except FileNotFoundError:
|
||||
# 如果tags.json不存在,从Excel生成备用标签数据
|
||||
try:
|
||||
all_tags = get_all_tags()
|
||||
|
||||
backup_tags = {
|
||||
"tags": all_tags,
|
||||
"tag_counts": {},
|
||||
"total_unique_tags": len(all_tags)
|
||||
}
|
||||
|
||||
return backup_tags
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"生成标签数据失败: {str(e)}")
|
||||
filters_data = get_available_filters()
|
||||
return {
|
||||
"success": True,
|
||||
"data": filters_data
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"加载标签数据失败: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"获取筛选条件失败: {str(e)}")
|
||||
|
||||
@app.get("/api/questions-by-filters")
|
||||
async def get_questions_by_filters_api(
|
||||
subject: str = "",
|
||||
grade: str = "",
|
||||
unit: str = ""
|
||||
):
|
||||
"""根据筛选条件获取题目"""
|
||||
try:
|
||||
# 根据筛选条件获取题目
|
||||
filtered_questions = get_questions_by_filters(
|
||||
subject=subject,
|
||||
grade=grade,
|
||||
unit=unit
|
||||
)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"data": filtered_questions
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"获取筛选题目失败: {str(e)}")
|
||||
|
||||
@app.get("/api/questions/{session_id}")
|
||||
async def get_filtered_questions(session_id: str):
|
||||
@ -356,7 +388,7 @@ async def get_filtered_questions(session_id: str):
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
SELECT s.selected_tag, qs.questions_config
|
||||
SELECT s.*, qs.questions_config
|
||||
FROM students s
|
||||
JOIN quiz_sessions qs ON s.id = qs.student_id
|
||||
WHERE qs.id = ?
|
||||
@ -371,8 +403,29 @@ async def get_filtered_questions(session_id: str):
|
||||
selected_tag = session_data['selected_tag'] or ''
|
||||
questions_config = json.loads(session_data['questions_config'])
|
||||
|
||||
# 根据标签筛选题目(直接从Excel读取)
|
||||
filtered_questions = get_questions_by_tag(selected_tag)
|
||||
# 解析标签,提取筛选条件
|
||||
subject = ""
|
||||
grade = ""
|
||||
unit = ""
|
||||
|
||||
# 如果标签包含筛选条件格式,提取它们
|
||||
if selected_tag and "学科:" in selected_tag:
|
||||
# 格式: "学科:科学 | 年级:一年级上册 | 单元:1-周围的植物"
|
||||
filters = selected_tag.split(' | ')
|
||||
for filter_item in filters:
|
||||
if filter_item.startswith("学科:"):
|
||||
subject = filter_item[3:]
|
||||
elif filter_item.startswith("年级:"):
|
||||
grade = filter_item[3:]
|
||||
elif filter_item.startswith("单元:"):
|
||||
unit = filter_item[3:]
|
||||
|
||||
# 根据筛选条件获取题目
|
||||
filtered_questions = get_questions_by_filters(
|
||||
subject=subject,
|
||||
grade=grade,
|
||||
unit=unit
|
||||
)
|
||||
|
||||
# 根据配置选择题目
|
||||
selected_questions = select_questions_by_config(filtered_questions, questions_config)
|
||||
|
||||
BIN
public/.~questions.xlsx
Normal file
BIN
public/.~questions.xlsx
Normal file
Binary file not shown.
@ -760,7 +760,7 @@
|
||||
const createBtn = document.getElementById('create-report-btn');
|
||||
if (createBtn) {
|
||||
createBtn.addEventListener('click', function() {
|
||||
window.location.href = '/survey.html';
|
||||
window.location.href = '/survey.html?admin=true';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
1178
public/survey.html
1178
public/survey.html
File diff suppressed because it is too large
Load Diff
@ -23,16 +23,16 @@ class SurveyAPI:
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
|
||||
def create_student(self, name, school, grade, selected_tag=''):
|
||||
def create_student(self, name, school, grade, phone, selected_tag=''):
|
||||
"""创建学员记录"""
|
||||
student_id = str(uuid.uuid4())
|
||||
conn = self.get_connection()
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute('''
|
||||
INSERT INTO students (id, name, school, grade, selected_tag)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
''', (student_id, name, school, grade, selected_tag))
|
||||
INSERT INTO students (id, name, school, grade, phone, selected_tag)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
''', (student_id, name, school, grade, phone, selected_tag))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
@ -131,15 +131,30 @@ class SurveyHandler(BaseHTTPRequestHandler):
|
||||
name = data.get('name', '').strip()
|
||||
school = data.get('school', '').strip()
|
||||
grade = data.get('grade', '').strip()
|
||||
selected_tag = data.get('selectedTag', '').strip()
|
||||
phone = data.get('phone', '').strip()
|
||||
selected_subject = data.get('selectedSubject', '').strip()
|
||||
selected_semester = data.get('selectedSemester', '').strip()
|
||||
selected_exam_type = data.get('selectedExamType', '').strip()
|
||||
|
||||
# 构建描述性标签
|
||||
selected_tag = ""
|
||||
if selected_subject and grade and selected_semester and selected_exam_type:
|
||||
selected_tag = f"{selected_subject}-{grade}{selected_semester}-{selected_exam_type}考试"
|
||||
elif selected_subject and grade and selected_semester:
|
||||
selected_tag = f"{selected_subject}-{grade}{selected_semester}"
|
||||
elif selected_subject and grade:
|
||||
selected_tag = f"{selected_subject}-{grade}"
|
||||
elif selected_subject:
|
||||
selected_tag = selected_subject
|
||||
|
||||
questions_config = data.get('questionsConfig', {})
|
||||
|
||||
if not name or not school or not grade:
|
||||
self.send_error(400, "姓名、学校和年级不能为空")
|
||||
if not name or not school or not grade or not phone:
|
||||
self.send_error(400, "姓名、学校、年级和手机号不能为空")
|
||||
return
|
||||
|
||||
# 创建学员记录
|
||||
student_id = self.api.create_student(name, school, grade, selected_tag)
|
||||
student_id = self.api.create_student(name, school, grade, phone, selected_tag)
|
||||
|
||||
# 创建答题会话
|
||||
session_id = self.api.create_quiz_session(student_id, questions_config)
|
||||
@ -240,6 +255,7 @@ class SurveyHandler(BaseHTTPRequestHandler):
|
||||
student_name = session_data['name']
|
||||
school = session_data['school']
|
||||
grade = session_data['grade']
|
||||
phone = session_data['phone']
|
||||
|
||||
return f'''<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
@ -473,6 +489,10 @@ class SurveyHandler(BaseHTTPRequestHandler):
|
||||
<div class="info-label">年级</div>
|
||||
<div class="info-value">{grade}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">手机号</div>
|
||||
<div class="info-value">{phone}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
77
test_filter.html
Normal file
77
test_filter.html
Normal file
@ -0,0 +1,77 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>测试筛选功能</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>测试筛选功能</h1>
|
||||
<button onclick="testFiltering()">开始测试</button>
|
||||
<div id="results"></div>
|
||||
|
||||
<script>
|
||||
async function testFiltering() {
|
||||
const resultsDiv = document.getElementById('results');
|
||||
resultsDiv.innerHTML = '<p>正在测试...</p>';
|
||||
|
||||
try {
|
||||
// 测试获取筛选条件
|
||||
console.log('=== 测试获取筛选条件 ===');
|
||||
const filtersResponse = await fetch('/api/filters');
|
||||
const filtersResult = await filtersResponse.json();
|
||||
console.log('筛选条件:', filtersResult);
|
||||
|
||||
// 测试获取题目
|
||||
console.log('=== 测试获取题目 ===');
|
||||
const questionsResponse = await fetch('/api/questions');
|
||||
const questionsResult = await questionsResponse.json();
|
||||
console.log('题目总数:', Object.keys(questionsResult).map(type => `${type}: ${questionsResult[type].length}`).join(', '));
|
||||
|
||||
// 测试筛选功能
|
||||
console.log('=== 测试筛选功能 ===');
|
||||
if (filtersResult.success) {
|
||||
const subject = filtersResult.data.subjects[0] || '';
|
||||
const grade = filtersResult.data.grades[0] || '';
|
||||
const unit = filtersResult.data.units[0] || '';
|
||||
|
||||
console.log(`测试筛选: 学科=${subject}, 年级=${grade}, 单元=${unit}`);
|
||||
|
||||
// 构建筛选URL
|
||||
const filterParams = new URLSearchParams({
|
||||
subject: subject,
|
||||
grade: grade,
|
||||
unit: unit
|
||||
});
|
||||
|
||||
const filteredResponse = await fetch(`/api/questions-by-filters?${filterParams}`);
|
||||
const filteredResult = await filteredResponse.json();
|
||||
console.log('筛选结果:', filteredResult);
|
||||
|
||||
if (filteredResult.success) {
|
||||
const filteredQuestions = filteredResult.data;
|
||||
const totalFiltered = Object.keys(filteredQuestions).reduce((sum, type) => sum + filteredQuestions[type].length, 0);
|
||||
|
||||
resultsDiv.innerHTML = `
|
||||
<h2>测试结果</h2>
|
||||
<p>原始题目总数: ${Object.keys(questionsResult).reduce((sum, type) => sum + questionsResult[type].length, 0)}</p>
|
||||
<p>筛选条件: 学科=${subject}, 年级=${grade}, 单元=${unit}</p>
|
||||
<p>筛选后题目总数: ${totalFiltered}</p>
|
||||
<p>筛选后分布:</p>
|
||||
<ul>
|
||||
${Object.keys(filteredQuestions).map(type => `<li>${type}: ${filteredQuestions[type].length}题</li>`).join('')}
|
||||
</ul>
|
||||
<p>查看浏览器控制台获取详细信息</p>
|
||||
`;
|
||||
} else {
|
||||
resultsDiv.innerHTML = `<p>筛选测试失败</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试失败:', error);
|
||||
resultsDiv.innerHTML = `<p>测试失败: ${error.message}</p>`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
90
test_grade_sorting.html
Normal file
90
test_grade_sorting.html
Normal file
@ -0,0 +1,90 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>年级册次排序测试</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.test-container { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
|
||||
.success { border-color: #28a745; background-color: #f8fff9; }
|
||||
select { width: 200px; padding: 8px; margin: 5px 0; }
|
||||
label { display: block; margin: 10px 0 5px 0; font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>年级册次排序测试</h1>
|
||||
|
||||
<div class="test-container">
|
||||
<h2>排序验证</h2>
|
||||
<div id="sortResult"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-container">
|
||||
<h2>前端选择器预览</h2>
|
||||
<label for="gradeSelect">年级册次选择:</label>
|
||||
<select id="gradeSelect"></select>
|
||||
<div id="selectorResult"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function testSorting() {
|
||||
const sortResult = document.getElementById('sortResult');
|
||||
const gradeSelect = document.getElementById('gradeSelect');
|
||||
const selectorResult = document.getElementById('selectorResult');
|
||||
|
||||
try {
|
||||
// 获取筛选条件
|
||||
const response = await fetch('/api/filters');
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
const grades = result.data.grades;
|
||||
const correctOrder = ['一年级上册', '二年级上册', '三年级上册', '四年级上册', '五年级上册', '六年级上册', '七年级上册', '八年级上册'];
|
||||
|
||||
// 验证排序是否正确
|
||||
const isCorrect = JSON.stringify(grades) === JSON.stringify(correctOrder);
|
||||
|
||||
if (isCorrect) {
|
||||
sortResult.innerHTML = '<div class="success">✓ 年级册次排序正确!</div>';
|
||||
sortResult.innerHTML += '<h3>正确的排序顺序:</h3>';
|
||||
correctOrder.forEach((grade, index) => {
|
||||
sortResult.innerHTML += `<div>${index + 1}. ${grade}</div>`;
|
||||
});
|
||||
} else {
|
||||
sortResult.innerHTML = '<div>✗ 年级册次排序不正确</div>';
|
||||
sortResult.innerHTML += '<h3>当前排序:</h3>';
|
||||
grades.forEach((grade, index) => {
|
||||
sortResult.innerHTML += `<div>${index + 1}. ${grade}</div>`;
|
||||
});
|
||||
sortResult.innerHTML += '<h3>正确排序:</h3>';
|
||||
correctOrder.forEach((grade, index) => {
|
||||
sortResult.innerHTML += `<div>${index + 1}. ${grade}</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
// 填充选择器
|
||||
gradeSelect.innerHTML = '<option value="">选择年级册次...</option>';
|
||||
grades.forEach(grade => {
|
||||
const option = document.createElement('option');
|
||||
option.value = grade;
|
||||
option.textContent = grade;
|
||||
gradeSelect.appendChild(option);
|
||||
});
|
||||
|
||||
selectorResult.innerHTML = '<div class="success">✓ 前端选择器已正确填充</div>';
|
||||
|
||||
} else {
|
||||
sortResult.innerHTML = '<div>✗ 获取筛选条件失败</div>';
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试失败:', error);
|
||||
sortResult.innerHTML = `<div>测试失败: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后运行测试
|
||||
window.onload = testSorting;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
103
test_midterm_final.html
Normal file
103
test_midterm_final.html
Normal file
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>期中期末功能测试</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.test-result { margin: 10px 0; padding: 10px; border: 1px solid #ccc; }
|
||||
.success { background-color: #d4edda; }
|
||||
.error { background-color: #f8d7da; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>期中期末功能测试</h1>
|
||||
|
||||
<h2>测试结果:</h2>
|
||||
<div id="testResults"></div>
|
||||
|
||||
<script>
|
||||
async function runTests() {
|
||||
const resultsDiv = document.getElementById('testResults');
|
||||
resultsDiv.innerHTML = '<p>正在测试...</p>';
|
||||
|
||||
try {
|
||||
// 测试1: 获取筛选条件
|
||||
console.log('测试1: 获取筛选条件');
|
||||
const filtersResponse = await fetch('/api/filters');
|
||||
const filtersResult = await filtersResponse.json();
|
||||
|
||||
if (filtersResult.success) {
|
||||
resultsDiv.innerHTML += '<div class="test-result success">✓ 筛选条件API正常</div>';
|
||||
} else {
|
||||
resultsDiv.innerHTML += '<div class="test-result error">✗ 筛选条件API失败</div>';
|
||||
}
|
||||
|
||||
// 测试2: 期中考试筛选
|
||||
console.log('测试2: 期中考试筛选');
|
||||
const midtermResponse = await fetch('/api/questions-by-filters?subject=科学&grade=一年级上册&unit=期中');
|
||||
const midtermResult = await midtermResponse.json();
|
||||
|
||||
if (midtermResult.success) {
|
||||
const midtermTotal = Object.values(midtermResult.data).reduce((sum, questions) => sum + questions.length, 0);
|
||||
resultsDiv.innerHTML += `<div class="test-result success">✓ 期中考试筛选正常: ${midtermTotal}题</div>`;
|
||||
resultsDiv.innerHTML += `<div class="test-result"> - 基础题: ${midtermResult.data['基础题'].length}题</div>`;
|
||||
resultsDiv.innerHTML += `<div class="test-result"> - 进阶题: ${midtermResult.data['进阶题'].length}题</div>`;
|
||||
resultsDiv.innerHTML += `<div class="test-result"> - 竞赛题: ${midtermResult.data['竞赛题'].length}题</div>`;
|
||||
} else {
|
||||
resultsDiv.innerHTML += '<div class="test-result error">✗ 期中考试筛选失败</div>';
|
||||
}
|
||||
|
||||
// 测试3: 期末考试筛选
|
||||
console.log('测试3: 期末考试筛选');
|
||||
const finalResponse = await fetch('/api/questions-by-filters?subject=科学&grade=一年级上册&unit=期末');
|
||||
const finalResult = await finalResponse.json();
|
||||
|
||||
if (finalResult.success) {
|
||||
const finalTotal = Object.values(finalResult.data).reduce((sum, questions) => sum + questions.length, 0);
|
||||
resultsDiv.innerHTML += `<div class="test-result success">✓ 期末考试筛选正常: ${finalTotal}题</div>`;
|
||||
resultsDiv.innerHTML += `<div class="test-result"> - 基础题: ${finalResult.data['基础题'].length}题</div>`;
|
||||
resultsDiv.innerHTML += `<div class="test-result"> - 进阶题: ${finalResult.data['进阶题'].length}题</div>`;
|
||||
resultsDiv.innerHTML += `<div class="test-result"> - 竞赛题: ${finalResult.data['竞赛题'].length}题</div>`;
|
||||
} else {
|
||||
resultsDiv.innerHTML += '<div class="test-result error">✗ 期末考试筛选失败</div>';
|
||||
}
|
||||
|
||||
// 测试4: 对比期中期末题目数量
|
||||
console.log('测试4: 对比期中期末题目数量');
|
||||
if (midtermResult.success && finalResult.success) {
|
||||
const midtermCount = Object.values(midtermResult.data).reduce((sum, questions) => sum + questions.length, 0);
|
||||
const finalCount = Object.values(finalResult.data).reduce((sum, questions) => sum + questions.length, 0);
|
||||
|
||||
if (finalCount > midtermCount) {
|
||||
resultsDiv.innerHTML += '<div class="test-result success">✓ 期末题目数量大于期中,符合预期</div>';
|
||||
} else {
|
||||
resultsDiv.innerHTML += '<div class="test-result error">✗ 期末题目数量应该大于期中</div>';
|
||||
}
|
||||
}
|
||||
|
||||
// 测试5: 具体单元筛选
|
||||
console.log('测试5: 具体单元筛选');
|
||||
const unitResponse = await fetch('/api/questions-by-filters?subject=科学&grade=一年级上册&unit=1-周围的植物');
|
||||
const unitResult = await unitResponse.json();
|
||||
|
||||
if (unitResult.success) {
|
||||
const unitTotal = Object.values(unitResult.data).reduce((sum, questions) => sum + questions.length, 0);
|
||||
resultsDiv.innerHTML += `<div class="test-result success">✓ 具体单元筛选正常: ${unitTotal}题</div>`;
|
||||
} else {
|
||||
resultsDiv.innerHTML += '<div class="test-result error">✗ 具体单元筛选失败</div>';
|
||||
}
|
||||
|
||||
resultsDiv.innerHTML += '<div class="test-result success"><strong>所有测试完成!</strong></div>';
|
||||
|
||||
} catch (error) {
|
||||
console.error('测试失败:', error);
|
||||
resultsDiv.innerHTML = `<div class="test-result error">测试失败: ${error.message}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
// 页面加载完成后运行测试
|
||||
window.onload = runTests;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user