class Grade(Serializable, db.Model): __tablename__ = 'grades' course_id = db.Column('course_id', db.Integer, primary_key=True) assignment_id = db.Column('assignment_id', db.Integer, primary_key=True) grade_component_id = db.Column('grade_component_id', db.Unicode, primary_key=True) team_id = db.Column('team_id', db.Integer, primary_key=True) points = db.Column(db.Float, nullable=False) grade_component = db.relationship('GradeComponent') team = db.relationship('AssignmentsTeams', backref="grades") default_fields = ['points', 'grade_component_id'] readonly_fields = ['course_id', 'team_id', 'grade_component'] __table_args__ = (db.ForeignKeyConstraint( [team_id, assignment_id, course_id], [ AssignmentsTeams.team_id, AssignmentsTeams.assignment_id, AssignmentsTeams.course_id ]), db.ForeignKeyConstraint( [grade_component_id, assignment_id, course_id], [ GradeComponent.id, GradeComponent.assignment_id, GradeComponent.course_id ]), {})
class Course(ExposedModel, Serializable): __tablename__ = 'courses' id = db.Column(db.Unicode, primary_key=True, unique=True) name = db.Column(db.Unicode) options = db.Column(JSONEncodedDict, default={}) graders = association_proxy('courses_graders', 'grader') students = association_proxy('courses_students', 'student') instructors = association_proxy('courses_instructors', 'instructor') assignments = db.relationship("Assignment", backref="course") teams = db.relationship("Team", backref="course") default_fields = ['name', 'assignments', 'options'] readonly_fields = ['id', 'options', 'graders', 'students', 'instructors', 'assignments', 'teams'] @staticmethod def from_id(course_id): return Course.query.filter_by(id=course_id).first()
class CoursesInstructors(Serializable, db.Model): __tablename__ = 'courses_instructors' repo_info = db.Column(JSONEncodedDict, default={}) instructor_id = db.Column('instructor_id', db.Unicode, db.ForeignKey('users.id'), primary_key=True) course_id = db.Column('course_id', db.Integer, db.ForeignKey('courses.id'), primary_key=True) instructor = db.relationship("User") default_fields = ['instructor', 'repo_info'] readonly_fields = ['instructor_id', 'course_id', 'repo_info'] course = db.relationship("Course", backref=db.backref("courses_instructors", cascade="all, delete-orphan"))
class AssignmentsTeams(Serializable, db.Model): __tablename__ = 'assignments_teams' assignment_id = db.Column('assignment_id', db.Integer, primary_key=True) team_id = db.Column('team_id', db.Unicode, primary_key=True) course_id = db.Column('course_id', db.Integer, primary_key=True) extensions_used = db.Column(db.Integer, default=0) commit_sha = db.Column(db.Unicode) submitted_at = db.Column(UTCDateTime) grader_id = db.Column('grader_id', db.Integer, db.ForeignKey('users.id')) penalties = db.Column(JSONEncodedDict, default={}) team = db.relationship("Team", backref=db.backref("assignments_teams", cascade="all, delete-orphan")) assignment = db.relationship("Assignment") grader = db.relationship("User") default_fields = [ 'extensions_used', 'commit_sha', 'submitted_at', 'assignment_id', 'grades', 'penalties', 'grader' ] readonly_fields = ['team', 'grader', 'grades'] __table_args__ = (db.ForeignKeyConstraint([team_id, course_id], [Team.id, Team.course_id]), db.ForeignKeyConstraint( [assignment_id, course_id], ["assignments.id", "assignments.course_id"]), {}) @staticmethod def from_id(course_id, team_id, assignment_id): return AssignmentsTeams.query.filter_by( course_id=course_id, team_id=team_id, assignment_id=assignment_id).first() def get_grade(self, gc_id): grades = [g for g in self.grades if g.grade_component_id == gc_id] if len(grades) == 0: return None else: return grades[0] def is_ready_for_grading(self): if self.submitted_at is None: return False else: now = get_datetime_now_utc() deadline = self.assignment.deadline + timedelta( days=self.extensions_used) if now > deadline: return True else: return False
class StudentsTeams(Serializable, db.Model): STATUS_UNCONFIRMED = 0 STATUS_CONFIRMED = 1 __tablename__ = 'students_teams' status = db.Column(db.Integer, nullable=False, server_default='0') student_id = db.Column('student_id', db.Unicode, db.ForeignKey('users.id'), primary_key=True) team_id = db.Column('team_id', db.Integer, primary_key=True) course_id = db.Column('course_id', db.Integer, primary_key=True) student = db.relationship("User") default_fields = ['status', 'student'] readonly_fields = ['student', 'team'] team = db.relationship("Team", backref=db.backref("students_teams", cascade="all, delete-orphan")) __table_args__ = (db.ForeignKeyConstraint([team_id, course_id], [Team.id, Team.course_id]), {})
class Assignment(Serializable): __tablename__ = 'assignments' id = db.Column(db.Unicode, primary_key=True) course_id = db.Column('course_id', db.Integer, db.ForeignKey('courses.id'), primary_key=True) name = db.Column(db.Unicode) deadline = db.Column(UTCDateTime) grade_components = db.relationship("GradeComponent", backref="assignment") default_fields = [ 'id', 'name', 'deadline', 'course_id', 'grade_components' ] readonly_fields = ['id', 'grade_components', 'course_id'] @staticmethod def from_id(course_id, assignment_id): return Assignment.query.filter_by(id=assignment_id, course_id=course_id).first()
class CoursesStudents(Serializable, db.Model): __tablename__ = 'courses_students' repo_info = db.Column(JSONEncodedDict, default={}) dropped = db.Column('dropped', db.Boolean, server_default='0', nullable=False) extensions = db.Column(db.Integer, default=0) student_id = db.Column('student_id', db.Unicode, db.ForeignKey('users.id'), primary_key=True) course_id = db.Column('course_id', db.Integer, db.ForeignKey('courses.id'), primary_key=True) student = db.relationship("User") default_fields = ['student', 'dropped', 'repo_info'] readonly_fields = ['student_id', 'course_id', 'repo_info'] course = db.relationship("Course", backref=db.backref("courses_students", cascade="all, delete-orphan")) @staticmethod def from_id(course_id, student_id): return CoursesStudents.query.filter_by(student_id=student_id, course_id=course_id).first() def get_extensions_available(self): from chisubmit.backend.webapp.api.teams.models import Team extensions_used = 0 student_teams = Team.find_teams_with_students(self.course_id, [self.student]) for student_team in student_teams: for assignment_team in student_team.assignments_teams: extensions_used += assignment_team.extensions_used return self.extensions - extensions_used
class Team(Serializable, db.Model): __tablename__ = 'teams' id = db.Column(db.Unicode, primary_key=True) extensions = db.Column(db.Integer, default=0) repo_info = db.Column(JSONEncodedDict, default={}) extras = db.Column(JSONEncodedDict, default={}) active = db.Column('active', db.Boolean, server_default='1', nullable=False) course_id = db.Column('course_id', db.Integer, db.ForeignKey('courses.id'), primary_key=True) students = association_proxy('students_teams', 'student') assignments = association_proxy('assignments_teams', 'assignment') default_fields = [ 'extensions', 'repo_info', 'active', 'course_id', 'extras', 'students_teams', 'assignments_teams', 'grades' ] readonly_fields = ['students', 'assignments', 'grades', 'extras'] @staticmethod def from_id(course_id, team_id): return Team.query.filter_by(course_id=course_id, id=team_id).first() @staticmethod def find_teams_with_students(course_id, students): q = Team.query.filter( Team.course_id == course_id, Team.students.any( StudentsTeams.student_id.in_([s.id for s in students]))) return q.all() def get_extensions_used(self): extensions = 0 for at in self.assignments_teams: extensions += at.extensions_used return extensions def get_extensions_available(self, extension_policy): from chisubmit.backend.webapp.api.courses.models import CoursesStudents if extension_policy == "per_team": return self.extensions - self.get_extensions_used() elif extension_policy == "per_student": student_extensions_available = [] for student in self.students: cs = CoursesStudents.from_id(self.course_id, student.id) a = cs.get_extensions_available() student_extensions_available.append(a) return min(student_extensions_available) else: return 0
class GradeComponent(Serializable, db.Model): __tablename__ = 'grade_components' id = db.Column(db.Unicode, primary_key=True) course_id = db.Column('course_id', db.Integer, primary_key=True) assignment_id = db.Column('assignment_id', db.Integer, primary_key=True) order = db.Column(db.Integer) description = db.Column(db.Unicode) points = db.Column('points', db.Float) default_fields = ['id', 'description', 'order', 'points', 'assignment_id'] readonly_fields = ['id', 'course_id', 'assignment_id'] __table_args__ = (db.ForeignKeyConstraint( [assignment_id, course_id], [Assignment.id, Assignment.course_id]), {})
class User(UniqueModel, Serializable, db.Model): __tablename__ = 'users' id = db.Column(db.Unicode, primary_key=True) first_name = db.Column(db.Unicode) last_name = db.Column(db.Unicode) email = db.Column(db.Unicode) api_key = db.Column(db.Unicode) admin = db.Column('admin', db.Boolean, server_default='0', nullable=False) default_fields = ['id', 'first_name', 'last_name', 'email'] readonly_fields = ['id'] @classmethod def unique_hash(cls, **kws): return kws['id'] @classmethod def unique_filter(cls, query, **kws): return query.filter(User.id == kws['id']) @staticmethod def from_id(user_id): return User.query.filter_by(id=user_id).first() def is_instructor_in(self, course): return self in course.instructors def is_student_in(self, course): return self in course.students def is_grader_in(self, course): return self in course.graders def has_instructor_or_grader_permissions(self, course): return self.is_instructor_in(course) or self.is_grader_in( course) or self.admin def is_in_course(self, course): return self.is_instructor_in(course) or self.is_student_in( course) or self.is_grader_in(course)