parent
ac50b2596f
commit
327cd60bcc
Binary file not shown.
@ -0,0 +1,66 @@
|
||||
# file_handler.py
|
||||
|
||||
"""文件处理模块,负责加载、保存和查重。"""
|
||||
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
# 定义存储用户数据的基础目录
|
||||
_BASE_DATA_DIR = "quiz_data"
|
||||
|
||||
|
||||
def load_existing_questions(username):
|
||||
"""
|
||||
加载指定用户所有历史题目用于查重。
|
||||
|
||||
Args:
|
||||
username (str): 用户名。
|
||||
|
||||
Returns:
|
||||
set: 包含该用户所有历史题目的集合。
|
||||
"""
|
||||
user_dir = os.path.join(_BASE_DATA_DIR, username)
|
||||
existing_questions = set()
|
||||
|
||||
if not os.path.exists(user_dir):
|
||||
return existing_questions
|
||||
|
||||
for filename in os.listdir(user_dir):
|
||||
if filename.endswith(".txt"):
|
||||
filepath = os.path.join(user_dir, filename)
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
# 题目格式为 "1. 题目内容",我们只取题目内容
|
||||
if ". " in line:
|
||||
question = line.split(". ", 1)[1].strip()
|
||||
if question:
|
||||
existing_questions.add(question)
|
||||
|
||||
return existing_questions
|
||||
|
||||
|
||||
def save_quiz_to_file(username, questions):
|
||||
"""
|
||||
将生成的试卷保存到用户专属的文件夹中。
|
||||
|
||||
文件以 '年-月-日-时-分-秒.txt' 格式命名。
|
||||
|
||||
Args:
|
||||
username (str): 用户名。
|
||||
questions (list): 包含题目字符串的列表。
|
||||
|
||||
Returns:
|
||||
str: 保存成功后的文件绝对路径。
|
||||
"""
|
||||
user_dir = os.path.join(_BASE_DATA_DIR, username)
|
||||
os.makedirs(user_dir, exist_ok=True)
|
||||
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
|
||||
filename = f"{timestamp}.txt"
|
||||
filepath = os.path.join(user_dir, filename)
|
||||
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
for i, question in enumerate(questions, 1):
|
||||
f.write(f"{i}. {question}\n\n")
|
||||
|
||||
return os.path.abspath(filepath)
|
||||
@ -0,0 +1,121 @@
|
||||
# generator.py
|
||||
|
||||
import random
|
||||
import re
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
class QuestionGenerator(ABC):
|
||||
def generate_bunch(self, count, existing_questions):
|
||||
new_questions = []
|
||||
all_questions = existing_questions.copy()
|
||||
|
||||
while len(new_questions) < count:
|
||||
question = self._create_expression()
|
||||
if question not in all_questions:
|
||||
new_questions.append(question)
|
||||
all_questions.add(question)
|
||||
return new_questions
|
||||
|
||||
@abstractmethod
|
||||
def _create_expression(self):
|
||||
pass
|
||||
|
||||
def _get_random_operands(self, num_operands):
|
||||
return [random.randint(1, 100) for _ in range(num_operands)]
|
||||
|
||||
class PrimarySchoolGenerator(QuestionGenerator):
|
||||
def _get_factors(self, n):
|
||||
if n == 0:
|
||||
return []
|
||||
factors = set()
|
||||
for i in range(1, int(n**0.5) + 1):
|
||||
if n % i == 0:
|
||||
factors.add(i)
|
||||
factors.add(n // i)
|
||||
return list(factors)
|
||||
|
||||
def _create_expression(self):
|
||||
num_operations = random.randint(1, 4)
|
||||
current_value = random.randint(20, 100)
|
||||
expression_str = str(current_value)
|
||||
|
||||
for _ in range(num_operations):
|
||||
possible_ops = ['+', '*']
|
||||
if current_value > 1:
|
||||
possible_ops.append('-')
|
||||
|
||||
factors = self._get_factors(current_value)
|
||||
if len(factors) > 2:
|
||||
possible_ops.append('/')
|
||||
|
||||
op = random.choice(possible_ops)
|
||||
|
||||
if op == '+':
|
||||
next_operand = random.randint(1, 100)
|
||||
current_value += next_operand
|
||||
elif op == '*':
|
||||
next_operand = random.randint(1, 10)
|
||||
current_value *= next_operand
|
||||
elif op == '-':
|
||||
next_operand = random.randint(1, current_value)
|
||||
current_value -= next_operand
|
||||
elif op == '/':
|
||||
valid_divisors = [f for f in factors if f != current_value and f != 1]
|
||||
if not valid_divisors:
|
||||
op = '-'
|
||||
next_operand = random.randint(1, current_value)
|
||||
current_value -= next_operand
|
||||
else:
|
||||
next_operand = random.choice(valid_divisors)
|
||||
current_value //= next_operand
|
||||
|
||||
expression_str = f"({expression_str} {op} {next_operand})"
|
||||
|
||||
return expression_str[1:-1]
|
||||
|
||||
class MiddleSchoolGenerator(QuestionGenerator):
|
||||
def _create_expression(self):
|
||||
base_gen = PrimarySchoolGenerator()
|
||||
expression = base_gen._create_expression()
|
||||
|
||||
parts = re.findall(r'\d+', expression)
|
||||
if not parts:
|
||||
return self._create_expression()
|
||||
|
||||
target_num_str = random.choice(parts)
|
||||
|
||||
if random.choice([True, False]):
|
||||
replacement = f"{target_num_str} ** 2"
|
||||
else:
|
||||
n = random.randint(2, 10)
|
||||
original_num = n * n
|
||||
replacement = f"sqrt({original_num})"
|
||||
expression = expression.replace(target_num_str, str(original_num), 1)
|
||||
|
||||
return expression.replace(str(target_num_str), replacement, 1)
|
||||
|
||||
class HighSchoolGenerator(QuestionGenerator):
|
||||
def _create_expression(self):
|
||||
base_gen = MiddleSchoolGenerator()
|
||||
expression = base_gen._create_expression()
|
||||
|
||||
parts = re.findall(r'\d+', expression)
|
||||
if not parts:
|
||||
return self._create_expression()
|
||||
|
||||
target_num_str = random.choice(parts)
|
||||
trig_func = random.choice(['sin', 'cos', 'tan'])
|
||||
replacement = f"{trig_func}({target_num_str})"
|
||||
|
||||
return expression.replace(target_num_str, replacement, 1)
|
||||
|
||||
def get_generator(account_type):
|
||||
generators = {
|
||||
"小学": PrimarySchoolGenerator,
|
||||
"初中": MiddleSchoolGenerator,
|
||||
"高中": HighSchoolGenerator,
|
||||
}
|
||||
generator_class = generators.get(account_type)
|
||||
if generator_class:
|
||||
return generator_class()
|
||||
raise ValueError("无效的账户类型")
|
||||
@ -0,0 +1,94 @@
|
||||
# main.py
|
||||
|
||||
"""
|
||||
程序主入口。
|
||||
|
||||
负责处理用户登录、命令解析和调用其他模块完成核心功能。
|
||||
"""
|
||||
|
||||
import auth
|
||||
import file_handler
|
||||
from generator import get_generator
|
||||
|
||||
VALID_TYPES = {"小学", "初中", "高中"}
|
||||
|
||||
def session_loop(username, account_type):
|
||||
"""
|
||||
用户登录后的主会话循环。
|
||||
|
||||
处理生成题目、切换难度和退出登录的逻辑。
|
||||
|
||||
Args:
|
||||
username (str): 当前登录的用户名。
|
||||
account_type (str): 当前用户的账户类型。
|
||||
"""
|
||||
print(f"\n--- 欢迎, {username}!当前选择为 {account_type} 出题 ---")
|
||||
current_type = account_type
|
||||
|
||||
while True:
|
||||
prompt = (
|
||||
f"准备生成 {current_type} 数学题目, "
|
||||
"请输入生成题目数量 (10-30, 输入-1将退出当前用户): "
|
||||
)
|
||||
user_input = input(prompt).strip()
|
||||
|
||||
if user_input == "-1":
|
||||
print(f"用户 {username} 已退出。")
|
||||
break
|
||||
|
||||
if user_input.startswith("切换为"):
|
||||
parts = user_input.split()
|
||||
if len(parts) == 2 and parts[1] in VALID_TYPES:
|
||||
current_type = parts[1]
|
||||
print(f"--- 已成功切换为 {current_type} 出题 ---")
|
||||
continue
|
||||
else:
|
||||
print("无效的切换命令, 请输入 '切换为' + '小学'/'初中'/'高中' 中的一个。")
|
||||
continue
|
||||
|
||||
try:
|
||||
num_questions = int(user_input)
|
||||
if not 10 <= num_questions <= 30:
|
||||
print("输入错误, 题目数量必须在 10 到 30 之间。")
|
||||
continue
|
||||
|
||||
print("正在生成题目, 请稍候...")
|
||||
|
||||
generator = get_generator(current_type)
|
||||
existing_questions = file_handler.load_existing_questions(username)
|
||||
new_questions = generator.generate_bunch(
|
||||
num_questions, existing_questions
|
||||
)
|
||||
saved_path = file_handler.save_quiz_to_file(username, new_questions)
|
||||
|
||||
print("\n题目生成成功!试卷内容如下:")
|
||||
for i, q in enumerate(new_questions, 1):
|
||||
print(f"{i}. {q}")
|
||||
|
||||
print(f"\n试卷已保存至: {saved_path}")
|
||||
|
||||
except ValueError:
|
||||
print("输入无效, 请输入一个有效的数字或命令。")
|
||||
except Exception as e:
|
||||
print(f"发生未知错误: {e}")
|
||||
|
||||
def main():
|
||||
"""程序主函数,包含顶层登录循环。"""
|
||||
print("--- 中小学数学卷子自动生成程序 ---")
|
||||
while True:
|
||||
credentials = input("请输入用户名和密码 (用空格隔开): ").strip().split()
|
||||
|
||||
if len(credentials) != 2:
|
||||
print("输入格式错误, 请确保用户名和密码之间有且仅有一个空格。")
|
||||
continue
|
||||
|
||||
username, password = credentials
|
||||
account_type = auth.validate_user(username, password)
|
||||
|
||||
if account_type:
|
||||
session_loop(username, account_type)
|
||||
else:
|
||||
print("用户名或密码错误, 请重新输入。")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in new issue