129 lines
5.1 KiB
Python
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)
|