初始化课评系统仓库:包含规则配置、学生档案、课程教案、班级汇总

This commit is contained in:
qiuyan
2026-05-08 14:15:00 +08:00
commit 16af260a11
300 changed files with 30338 additions and 0 deletions

311
src/evaluation_validator.py Normal file
View File

@@ -0,0 +1,311 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
课评质量自动验证脚本
用于检查生成的课评是否符合规范
使用方法:
python evaluation_validator.py <课评文件路径>
python evaluation_validator.py --batch <班级汇总文件路径>
python evaluation_validator.py --student <学生历史课评文件路径>
"""
import sys
import io
import re
import argparse
from pathlib import Path
from datetime import datetime
# 设置UTF-8输出编码
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
# ============ 配置项 ============
# 字数范围配置(与规则文件保持一致)
MIN_LENGTH = 180
MAX_LENGTH = 350
# 禁用词汇列表
FORBIDDEN_WORDS = [
"宝贝", "小天才", "最棒", "无敌", "超级厉害", "完美",
"在今天的课堂上", "通过本次活动",
"比其他小朋友", "补课", "补课上", "来补课", "本次补课",
"临时加入", "从其他班级过来"
]
# 必须包含的模块关键词(至少包含其中几类)
REQUIRED_MODULES = {
"课程知识点": ["学习了", "认识了", "探索了", "了解了"],
"掌握程度": ["完成", "掌握", "理解", "尝试", "独立"],
"课后建议": ["在家", "可以", "建议", "巩固"]
}
# Emoji检查配置
MIN_EMOJI = 3
MAX_EMOJI = 5
# ============ 核心验证类 ============
class EvaluationValidator:
def __init__(self, text: str, student_name: str = ""):
self.text = text
self.student_name = student_name
self.errors = []
self.warnings = []
self.stats = {}
def validate_all(self) -> dict:
"""执行所有验证检查"""
self.check_length()
self.check_forbidden_words()
self.check_emoji_count()
self.check_modules()
self.check_name_usage()
self.check_week_number()
self.check_paragraph_structure()
return {
"status": "通过" if not self.errors else "未通过",
"errors": self.errors,
"warnings": self.warnings,
"stats": self.stats
}
def check_length(self):
"""检查字数是否在弹性范围内"""
# 计算中文字符数(不含空格、换行、标点)
chinese_chars = len(re.findall(r'[\u4e00-\u9fff]', self.text))
# 计算总字符数(含英文单词)
total_chars = len(self.text.replace(' ', '').replace('\n', ''))
self.stats["中文字数"] = chinese_chars
self.stats["总字符数"] = total_chars
if chinese_chars < MIN_LENGTH:
self.errors.append(f"字数不足:当前{chinese_chars}字,最少要求{MIN_LENGTH}")
elif chinese_chars > MAX_LENGTH:
self.warnings.append(f"字数偏多:当前{chinese_chars}字,建议控制在{MAX_LENGTH}字以内")
else:
self.stats["字数检查"] = f"✅ 通过({chinese_chars}字)"
def check_forbidden_words(self):
"""检查是否包含禁用词汇"""
found = []
for word in FORBIDDEN_WORDS:
if word in self.text:
found.append(word)
if found:
self.errors.append(f"包含禁用词汇:{', '.join(found)}")
else:
self.stats["禁用词检查"] = "✅ 通过"
def check_emoji_count(self):
"""检查Emoji数量"""
# 只匹配真正的Emoji字符排除Markdown符号如✓、✅等
# 匹配Unicode Emoji范围
emojis = re.findall(r'[\U0001F300-\U0001F9FF]', self.text)
emoji_count = len(emojis)
self.stats["Emoji数量"] = emoji_count
if emoji_count < MIN_EMOJI:
self.warnings.append(f"Emoji较少当前{emoji_count}个,建议至少{MIN_EMOJI}")
elif emoji_count > MAX_EMOJI:
self.warnings.append(f"Emoji较多当前{emoji_count}个,建议不超过{MAX_EMOJI}")
else:
self.stats["Emoji检查"] = f"✅ 通过({emoji_count}个)"
def check_modules(self):
"""检查是否包含必要的模块内容"""
missing_modules = []
for module_name, keywords in REQUIRED_MODULES.items():
if not any(keyword in self.text for keyword in keywords):
missing_modules.append(module_name)
if missing_modules:
self.warnings.append(f"可能缺少以下模块:{', '.join(missing_modules)}")
else:
self.stats["模块检查"] = "✅ 通过"
def check_name_usage(self):
"""检查学生称呼使用是否正确"""
if not self.student_name:
return
# 检查是否使用了全名(应该使用小名/去姓/叠字)
if len(self.student_name) >= 3 and self.student_name in self.text:
self.warnings.append(f"可能使用了全名'{self.student_name}',建议使用小名或去姓称呼")
def check_week_number(self):
"""检查正文中是否出现了周数信息"""
week_patterns = [r'\d+周', r'本周第\d+节', r'\d+节课']
found = []
for pattern in week_patterns:
matches = re.findall(pattern, self.text)
found.extend(matches)
if found:
self.errors.append(f"课评正文禁止出现周数:{', '.join(found)}")
else:
self.stats["周数检查"] = "✅ 通过"
def check_paragraph_structure(self):
"""检查段落结构"""
paragraphs = [p.strip() for p in self.text.split('\n\n') if p.strip()]
if len(paragraphs) != 3:
self.warnings.append(f"段落数建议为3段当前{len(paragraphs)}")
else:
self.stats["段落检查"] = "✅ 通过3段"
self.stats["段落数"] = len(paragraphs)
# ============ 文件解析函数 ============
def parse_class_summary(file_path: str) -> list:
"""解析班级周汇总文件,提取各学生课评"""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
evaluations = []
# 匹配学生课评区块
pattern = r'###\s*(.+?)\n\n(.+?)(?=\n###|\Z)'
matches = re.findall(pattern, content, re.DOTALL)
for name_section, eval_text in matches:
# 提取学生姓名(去掉括号内容)
name = name_section.split('')[0].strip()
evaluations.append({
"name": name,
"text": eval_text.strip()
})
return evaluations
def parse_student_history(file_path: str) -> list:
"""解析学生历史课评文件,提取各条课评"""
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
evaluations = []
# 匹配课评记录
pattern = r'###\s*(\d{4}-\d{2}-\d{2})\s*\|\s*课程代码:(.+?)\n\n\*\*课评内容\*\*\n\n(.+?)(?=\n\*\*能力评估\*\*|\Z)'
matches = re.findall(pattern, content, re.DOTALL)
for date, course_code, eval_text in matches:
evaluations.append({
"date": date,
"course": course_code,
"text": eval_text.strip()
})
return evaluations
# ============ 输出格式化 ============
def print_result(result: dict, identifier: str = ""):
"""打印验证结果"""
print(f"\n{'='*60}")
if identifier:
print(f"📋 验证对象:{identifier}")
print(f"🎯 验证状态:{result['status']}")
print(f"{'='*60}")
if result['stats']:
print("\n📊 统计信息:")
for key, value in result['stats'].items():
print(f" {key}: {value}")
if result['errors']:
print("\n❌ 错误(必须修复):")
for error in result['errors']:
print(f"{error}")
if result['warnings']:
print("\n⚠️ 警告(建议优化):")
for warning in result['warnings']:
print(f"{warning}")
if not result['errors'] and not result['warnings']:
print("\n✅ 所有检查项通过!")
print(f"{'='*60}\n")
# ============ 主函数 ============
def main():
parser = argparse.ArgumentParser(description='课评质量自动验证工具')
parser.add_argument('file', help='要验证的文件路径')
parser.add_argument('--batch', action='store_true', help='批量验证班级汇总文件')
parser.add_argument('--student', action='store_true', help='验证学生历史课评文件')
parser.add_argument('--text', action='store_true', help='直接验证文本内容')
args = parser.parse_args()
if args.text:
# 直接验证文本
with open(args.file, 'r', encoding='utf-8') as f:
text = f.read()
validator = EvaluationValidator(text)
result = validator.validate_all()
print_result(result, "文本内容")
elif args.batch:
# 批量验证班级汇总
print(f"📁 正在解析班级汇总文件:{args.file}")
evaluations = parse_class_summary(args.file)
print(f"找到 {len(evaluations)} 条课评记录\n")
all_passed = True
for eval_data in evaluations:
validator = EvaluationValidator(eval_data['text'], eval_data['name'])
result = validator.validate_all()
print_result(result, f"学生:{eval_data['name']}")
if result['status'] != '通过':
all_passed = False
print(f"\n{'='*60}")
print(f"📊 批量验证完成:{len(evaluations)}条课评")
print(f"🎯 总体状态:{'✅ 全部通过' if all_passed else '❌ 存在未通过项'}")
print(f"{'='*60}")
elif args.student:
# 验证学生历史课评
print(f"📁 正在解析学生历史课评:{args.file}")
evaluations = parse_student_history(args.file)
print(f"找到 {len(evaluations)} 条历史记录\n")
for eval_data in evaluations:
validator = EvaluationValidator(eval_data['text'])
result = validator.validate_all()
print_result(result, f"日期:{eval_data['date']} | 课程:{eval_data['course']}")
else:
# 默认:尝试自动识别文件类型
with open(args.file, 'r', encoding='utf-8') as f:
content = f.read()
if '班级周汇总' in content or '学生课评列表' in content:
print("🔍 自动识别为:班级汇总文件")
evaluations = parse_class_summary(args.file)
for eval_data in evaluations:
validator = EvaluationValidator(eval_data['text'], eval_data['name'])
result = validator.validate_all()
print_result(result, f"学生:{eval_data['name']}")
else:
print("🔍 自动识别为:单条课评文本")
validator = EvaluationValidator(content)
result = validator.validate_all()
print_result(result, args.file)
if __name__ == '__main__':
main()