114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
import base64
|
||
|
||
from odoo import api, fields, models
|
||
from odoo.exceptions import ValidationError
|
||
|
||
|
||
class CommunitySchoolAssignment(models.Model):
|
||
_name = 'community.school.assignment'
|
||
_description = 'School Assignment'
|
||
_order = 'due_date asc, id desc'
|
||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||
|
||
name = fields.Char(string='Title', required=True, tracking=True)
|
||
class_id = fields.Many2one(
|
||
'community.school.class', string='Class', required=True, ondelete='cascade',
|
||
)
|
||
term_id = fields.Many2one(related='class_id.term_id', store=True)
|
||
teacher_id = fields.Many2one(related='class_id.teacher_id', store=True)
|
||
description = fields.Html(string='Instructions')
|
||
due_date = fields.Datetime(string='Due Date', required=True, tracking=True)
|
||
max_score = fields.Integer(string='Max Score', default=100)
|
||
state = fields.Selection(
|
||
[('draft', 'Draft'), ('published', 'Published'), ('closed', 'Closed')],
|
||
default='draft', required=True, tracking=True,
|
||
)
|
||
submission_ids = fields.One2many('community.school.submission', 'assignment_id')
|
||
submission_count = fields.Integer(compute='_compute_submission_count', store=True)
|
||
|
||
@api.depends('submission_ids')
|
||
def _compute_submission_count(self):
|
||
for assignment in self:
|
||
assignment.submission_count = len(assignment.submission_ids)
|
||
|
||
def action_publish(self):
|
||
for rec in self:
|
||
rec.state = 'published'
|
||
|
||
def action_close(self):
|
||
for rec in self:
|
||
rec.state = 'closed'
|
||
|
||
def action_reset_draft(self):
|
||
for rec in self:
|
||
rec.state = 'draft'
|
||
|
||
def action_view_submissions(self):
|
||
self.ensure_one()
|
||
return {
|
||
'type': 'ir.actions.act_window',
|
||
'name': f'Submissions – {self.name}',
|
||
'res_model': 'community.school.submission',
|
||
'view_mode': 'list,form',
|
||
'domain': [('assignment_id', '=', self.id)],
|
||
'context': {'default_assignment_id': self.id},
|
||
}
|
||
|
||
|
||
class CommunitySchoolSubmission(models.Model):
|
||
_name = 'community.school.submission'
|
||
_description = 'Assignment Submission'
|
||
_order = 'submitted_on desc, id desc'
|
||
|
||
assignment_id = fields.Many2one(
|
||
'community.school.assignment', required=True, ondelete='cascade',
|
||
)
|
||
enrollment_id = fields.Many2one(
|
||
'community.school.enrollment', string='Student Enrollment', required=True, ondelete='cascade',
|
||
)
|
||
student_id = fields.Many2one(related='enrollment_id.student_id', store=True)
|
||
class_id = fields.Many2one(related='assignment_id.class_id', store=True)
|
||
|
||
submitted_on = fields.Datetime(string='Submitted On', readonly=True)
|
||
notes = fields.Text(string='Student Notes')
|
||
|
||
# File attachment stored as binary
|
||
attachment_name = fields.Char(string='File Name')
|
||
attachment_data = fields.Binary(string='Uploaded File', attachment=True)
|
||
|
||
# Grading
|
||
score = fields.Integer(string='Score')
|
||
feedback = fields.Text(string='Teacher Feedback')
|
||
state = fields.Selection(
|
||
[('submitted', 'Submitted'), ('graded', 'Graded')],
|
||
default='submitted', required=True,
|
||
)
|
||
is_late = fields.Boolean(compute='_compute_is_late', store=True)
|
||
|
||
_assignment_enrollment_uniq = models.Constraint(
|
||
'unique(assignment_id, enrollment_id)',
|
||
'A submission for this assignment by this student already exists.',
|
||
)
|
||
|
||
@api.depends('submitted_on', 'assignment_id.due_date')
|
||
def _compute_is_late(self):
|
||
for sub in self:
|
||
if sub.submitted_on and sub.assignment_id.due_date:
|
||
sub.is_late = sub.submitted_on > sub.assignment_id.due_date
|
||
else:
|
||
sub.is_late = False
|
||
|
||
@api.constrains('score', 'assignment_id')
|
||
def _check_score(self):
|
||
for sub in self:
|
||
if sub.score < 0:
|
||
raise ValidationError('Score cannot be negative.')
|
||
if sub.assignment_id and sub.score > sub.assignment_id.max_score:
|
||
raise ValidationError(
|
||
f'Score ({sub.score}) cannot exceed max score ({sub.assignment_id.max_score}).'
|
||
)
|
||
|
||
def action_grade(self):
|
||
for sub in self:
|
||
sub.state = 'graded'
|