Files
shcool/college/models/exam_import_wizard.py
T
2026-08-27 19:53:56 +08:00

129 lines
5.1 KiB
Python

from odoo import api, fields, models, _
from odoo.exceptions import UserError
from .exam import _QUESTION_TYPES
class ExamImportWizard(models.TransientModel):
"""按课程/章节从题库批量导入题目到考试。"""
_name = 'yuthon.school.exam.import.wizard'
_description = '从题库批量导入题目'
exam_id = fields.Many2one(
'yuthon.school.exam', string='考试', required=True, readonly=True,
default=lambda self: self.env.context.get('active_id'))
course_id = fields.Many2one(
'yuthon.school.course', string='所属课程',
related='exam_id.course_id', readonly=True)
import_all_course = fields.Boolean(
string='导入整门课程题目', default=False,
help='勾选后,将忽略章节选择并导入本课程所有题目。')
chapter_ids = fields.Many2many(
'yuthon.school.course.chapter', 'yuthon_exam_import_wizard_chapter_rel',
string='所属章节',
domain="[('course_id', '=', course_id)]",
help='选择要导入的章节;不勾选“导入整门课程题目”时生效。')
question_type = fields.Selection(
_QUESTION_TYPES, string='题型筛选',
help='只导入指定题型;留空则导入全部题型。')
exclude_existing = fields.Boolean(
string='排除已存在题目', default=True,
help='试卷中已有的题目不会重复导入。')
line_ids = fields.One2many(
'yuthon.school.exam.import.wizard.line', 'wizard_id',
string='待导入题目')
@api.onchange('import_all_course', 'chapter_ids', 'question_type', 'exclude_existing')
def _onchange_load_questions(self):
"""根据筛选条件实时生成待导入题目列表。"""
self.ensure_one()
if not self.exam_id or not self.course_id:
self.line_ids = [(5, 0, 0)]
return
# 草稿状态的考试才允许重组题目;非草稿仅做预览,导入时会再次校验
question_obj = self.env['yuthon.school.exam.question']
domain = [
('course_id', '=', self.course_id.id),
('active', '=', True),
]
if not self.import_all_course and self.chapter_ids:
domain.append(('chapter_id', 'in', self.chapter_ids.ids))
if self.question_type:
domain.append(('question_type', '=', self.question_type))
existing_qids = set()
if self.exclude_existing:
existing_qids = set(self.exam_id.line_ids.mapped('question_id').ids)
questions = question_obj.search(domain, order='chapter_id, sequence, id')
questions = questions.filtered(lambda q: q.id not in existing_qids)
base_seq = 10
commands = [(5, 0, 0)]
for idx, question in enumerate(questions):
commands.append((0, 0, {
'sequence': base_seq + idx * 10,
'question_id': question.id,
'score': question.score,
'selected': True,
}))
self.line_ids = commands
def action_confirm_import(self):
"""将选中的题目写入考试组卷明细。"""
self.ensure_one()
exam = self.exam_id
if not exam:
raise UserError(_('未找到目标考试。'))
if exam.state != 'draft':
raise UserError(_('仅草稿状态的考试可以批量导入题目。'))
existing_qids = set(exam.line_ids.mapped('question_id').ids)
line_vals = []
for line in self.line_ids.filtered('selected'):
if line.question_id.id in existing_qids:
continue
line_vals.append({
'exam_id': exam.id,
'question_id': line.question_id.id,
'sequence': line.sequence,
'score': line.score,
})
if not line_vals:
raise UserError(_('没有需要导入的新题目。'))
self.env['yuthon.school.exam.line'].create(line_vals)
return {'type': 'ir.actions.act_window_close'}
class ExamImportWizardLine(models.TransientModel):
"""向导中的待导入题目明细行。"""
_name = 'yuthon.school.exam.import.wizard.line'
_description = '待导入题目明细'
_order = 'sequence, id'
wizard_id = fields.Many2one(
'yuthon.school.exam.import.wizard', string='向导',
required=True, ondelete='cascade')
selected = fields.Boolean(string='导入', default=True)
sequence = fields.Integer(string='排序', default=10)
question_id = fields.Many2one(
'yuthon.school.exam.question', string='题目', required=True, readonly=True)
question_type = fields.Selection(
related='question_id.question_type', string='题型', readonly=True)
question_text_short = fields.Char(
related='question_id.question_text_short', string='题干', readonly=True)
chapter_id = fields.Many2one(
related='question_id.chapter_id', string='所属章节', readonly=True)
visibility = fields.Selection(
related='question_id.visibility', string='可见范围', readonly=True)
score = fields.Float(string='本题分值', default=1.0)