class TimedExam(db.Model, AnswerMixin): # See the `timed exam endpoint parameters <timed exam>` for documenation on these columns. correct = db.Column(db.Integer) incorrect = db.Column(db.Integer) skipped = db.Column(db.Integer) time_taken = db.Column(db.Integer) # True if the ``act`` endpoint parameter was ``'reset'``; otherwise, False. reset = db.Column(Web2PyBoolean) # Define a default query: the username if provided a string. Otherwise, automatically fall back to the id. @classmethod def default_query(cls, key): if isinstance(key, str): return cls.sid == key
class Courses(db.Model, IdMixin): # _`course_name`: The name of this course. course_name = db.Column(db.String(512), unique=True) term_start_date = db.Column(db.Date) # TODO: Why not use base_course_id instead? _`base_course`: the course from which this course was derived. TODO: If this is a base course, this field should be identical to the course_name_? base_course = db.Column(db.String(512), db.ForeignKey('courses.course_name')) # TODO: This should go in a different table. Not all courses have a Python/Skuplt component. python3 = db.Column(Web2PyBoolean) login_required = db.Column(Web2PyBoolean) # Create ``child_courses`` which all refer to a single ``parent_course``: children's ``base_course`` matches a parent's ``course_name``. See `adjacency list relationships <http://docs.sqlalchemy.org/en/latest/orm/self_referential.html#self-referential>`_. child_courses = db.relationship('Courses', backref=backref('parent_course', remote_side=[course_name])) # Define a default query: the username if provided a string. Otherwise, automatically fall back to the id. @classmethod def default_query(cls, key): if isinstance(key, str): return cls.course_name == key
class CorrectAnswerMixin(AnswerMixin): # _`correct`: True if this answer is correct. correct = db.Column(Web2PyBoolean) @classmethod def default_query(cls, key): if isinstance(key, bool): return key == cls.correct else: return super().default_query(key)
class LpAnswers(db.Model, AnswerMixin): # See answer_. A JSON string; see RunestoneComponents for details. TODO: The length seems too short to me. answer = db.Column(db.String(512)) # A grade between 0 and 100. correct = db.Column(db.Float()) @classmethod # To make `hsblog endpoint` code simple, accept a Boolean: True is correct (a grade of 100); False is anything else. Also support numeric queries. def default_query(cls, key): if isinstance(key, bool): if key: return cls.correct == 100 else: return cls.correct < 100 # Treat any `number <https://docs.python.org/3/library/numbers.html#numbers.Number>`_ as a query of ``correct``. elif isinstance(key, Number): return key == cls.correct else: return super().default_query(key)
class LogInfo(db.Model): __tablename__ = 'useinfo' id = db.Column(db.Integer, primary_key=True) timestamp = db.Column(db.DateTime) sid = db.Column(db.String) event = db.Column(db.String) act = db.Column(db.String) div_id = db.Column(db.String) course_id = db.Column(db.String)
class AnswerMixin(IdMixin): # TODO: these entries duplicate Useinfo.timestamp. Why not just have a timestamp_id field? # # See timestamp_. timestamp = db.Column(db.DateTime) # See div_id_. div_id = db.Column(db.String(512)) # See sid_. sid = db.Column(db.String(512)) # See course_name_. Mixins with foreign keys need `special treatment <http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/mixins.html#mixing-in-columns>`_. @declared_attr def course_name(cls): return db.Column(db.String(512), db.ForeignKey('courses.course_name')) @classmethod def default_query(cls, key): if isinstance(key, tuple): sid, div_id, course_name = key return (cls.sid == sid) and (cls.div_id == div_id) and (course_name == course_name)
class Course(db.Model): __tablename__ = 'courses' id = db.Column(db.Integer, primary_key=True) course_name = db.Column(db.String, unique=True) term_start_date = db.Column(db.Date) base_course = db.Column(db.String) python3 = db.Column(db.Boolean) login_required = db.Column(db.Boolean)
class Useinfo(db.Model, IdMixin): # _`timestamp`: when this entry was recorded by this webapp. timestamp = db.Column(db.DateTime) # _`sid`: TODO: The student id? (user) which produced this row. sid = db.Column(db.String(512)) # The type of question (timed exam, fill in the blank, etc.). event = db.Column(db.String(512)) # TODO: What is this? The action associated with this log entry? act = db.Column(db.String(512)) # _`div_id`: the ID of the question which produced this entry. div_id = db.Column(db.String(512)) # _`course_id`: the Courses ``course_name`` **NOT** the ``id`` this row refers to. TODO: Use the ``id`` instead! course_id = db.Column(db.String(512), db.ForeignKey('courses.course_name')) # Define a default query: the username if provided a string. Otherwise, automatically fall back to the id. @classmethod def default_query(cls, key): if isinstance(key, str): return cls.sid == key
class ParsonsAnswers(db.Model, CorrectAnswerMixin): # See answer_. TODO: what is the format? answer = db.Column(db.String(512)) # _`source`: The source code provided by a student? TODO. source = db.Column(db.String(512))
class MchoiceAnswers(db.Model, CorrectAnswerMixin): # _`answer`: The answer to this question. TODO: what is the format? answer = db.Column(db.String(50))
def course_name(cls): return db.Column(db.String(512), db.ForeignKey('courses.course_name'))
class IdMixin: id = db.Column(db.Integer, primary_key=True)
class AuthUser(db.Model, UserMixin, IdMixin): username = db.Column(db.String(512), nullable=False, unique=True) first_name = db.Column(db.String(512)) last_name = db.Column(db.String(512)) email = db.Column(db.String(512), unique=True) password = db.Column(db.String(512)) created_on = db.Column(db.DateTime()) modified_on = db.Column(db.DateTime()) registration_key = db.Column(db.String(512)) reset_password_key = db.Column(db.String(512)) registration_id = db.Column(db.String(512)) cohort_id = db.Column(db.String(512)) course_id = db.Column(db.String(512)) active = db.Column(Web2PyBoolean) # Define a default query: the username if provided a string. Otherwise, automatically fall back to the id. @classmethod def default_query(cls, key): if isinstance(key, str): return cls.username == key
class DragndropAnswers(db.Model, CorrectAnswerMixin): # See answer_. TODO: what is the format? answer = db.Column(db.String(512))
class ShortanswerAnswers(db.Model, AnswerMixin): # See answer_. TODO: what is the format? answer = db.Column(db.String(512))
class CodelensAnswers(db.Model, CorrectAnswerMixin): # See answer_. TODO: what is the format? answer = db.Column(db.String(512)) # See source_. source = db.Column(db.String(512))
class Questions(db.Model, IdMixin): # The base_course_ this question is in. base_course = db.Column(db.String(512), nullable=False) # The div_id_ for this question. TODO: Rename this! name = db.Column(db.String(512), nullable=False) # matches chapter_label, not name chapter = db.Column(db.String(512)) # matches sub_chapter_label, not name subchapter = db.Column(db.String(512)) author = db.Column(db.String(512)) difficulty = db.Column(db.Integer) question = db.Column(db.Text) timestamp = db.Column(db.DateTime), question_type = db.Column(db.String(512)) is_private = db.Column(Web2PyBoolean) htmlsrc = db.Column(db.Text) autograde = db.Column(db.String(512)) @classmethod def default_query(cls, key): if isinstance(key, str): return cls.name == key
class ClickableareaAnswers(db.Model, CorrectAnswerMixin): # See answer_. TODO: what is the format? answer = db.Column(db.String(512))