From b9f4735cae6db2b2cc683ac4c44b66169458911e Mon Sep 17 00:00:00 2001 From: Or Ronai Date: Wed, 29 Sep 2021 10:28:50 +0300 Subject: [PATCH 01/15] feat: Add grades mark - Added grades table - Added backend and frontend implementaion --- lms/lmsdb/bootstrap.py | 38 ++++++++++++++++++++++++++++++++++++++ lms/lmsdb/models.py | 19 +++++++++++++++++++ lms/static/my.css | 4 ++++ lms/templates/view.html | 18 +++++++++++++++++- 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/lms/lmsdb/bootstrap.py b/lms/lmsdb/bootstrap.py index c4472f33..4284be50 100644 --- a/lms/lmsdb/bootstrap.py +++ b/lms/lmsdb/bootstrap.py @@ -240,10 +240,41 @@ def _api_keys_migration() -> bool: return True +<<<<<<< Updated upstream +======= +def _last_course_viewed_migration() -> bool: + User = models.User + _add_not_null_column(User, User.last_course_viewed) + return True + + +def _exercise_course_migration(course: models.Course) -> bool: + Exercise = models.Exercise + _create_usercourses_objects(models.User, course) + _add_course_and_numbers_to_exercises_table(Exercise, course) + return True + + +def _add_exercise_course_id_and_number_columns_constraint() -> bool: + Exercise = models.Exercise + migrator = db_config.get_migrator_instance() + with db_config.database.transaction(): + course_not_exists = _add_not_null_column(Exercise, Exercise.course) + number_not_exists = _add_not_null_column(Exercise, Exercise.number) + if course_not_exists and number_not_exists: + migrate( + migrator.add_index('exercise', ('course_id', 'number'), True), + ) + db_config.database.commit() + return True + + +>>>>>>> Stashed changes def _last_status_view_migration() -> bool: Solution = models.Solution _migrate_column_in_table_if_needed(Solution, Solution.last_status_view) _migrate_column_in_table_if_needed(Solution, Solution.last_time_view) + return True def _uuid_migration() -> bool: @@ -252,10 +283,17 @@ def _uuid_migration() -> bool: return True +def _grade_mark_migration() -> bool: + Solution = models.Solution + _migrate_column_in_table_if_needed(Solution, Solution.grade_mark) + return True + + def main(): with models.database.connection_context(): if models.database.table_exists(models.Solution.__name__.lower()): _last_status_view_migration() + _grade_mark_migration() if models.database.table_exists(models.User.__name__.lower()): _api_keys_migration() diff --git a/lms/lmsdb/models.py b/lms/lmsdb/models.py index 35c111d1..e144d422 100644 --- a/lms/lmsdb/models.py +++ b/lms/lmsdb/models.py @@ -365,9 +365,23 @@ def to_choices(cls: enum.EnumMeta) -> Tuple[Tuple[str, str], ...]: return tuple((choice.name, choice.value) for choice in choices) +class SolutionGradeMark(enum.Enum): + EXCELLENT = 'Excellent' + NICE = 'Nice' + FAIL = 'Fail' + PLAGIARISM = 'Plagiarism' + UNMARKED = 'Unmarked' + + @classmethod + def to_choices(cls: enum.EnumMeta) -> Tuple[Tuple[str, str], ...]: + choices = cast(Iterable[enum.Enum], tuple(cls)) + return tuple((choice.name, choice.value) for choice in choices) + + class Solution(BaseModel): STATES = SolutionState STATUS_VIEW = SolutionStatusView + GRADES = SolutionGradeMark MAX_CHECK_TIME_SECONDS = 60 * 10 exercise = ForeignKeyField(Exercise, backref='solutions') @@ -389,6 +403,11 @@ class Solution(BaseModel): index=True, ) last_time_view = DateTimeField(default=datetime.now, null=True, index=True) + grade_mark = CharField( + choices=GRADES.to_choices(), + default=GRADES.UNMARKED.name, + index=True, + ) @property def solution_files( diff --git a/lms/static/my.css b/lms/static/my.css index 2de91442..fd2b9195 100644 --- a/lms/static/my.css +++ b/lms/static/my.css @@ -632,6 +632,10 @@ code { margin-bottom: 1em; } +#exercise-comments { + margin-top: 1em; +} + ol.comments { list-style-type: none; padding: 1em; diff --git a/lms/templates/view.html b/lms/templates/view.html index 8504abf5..7b205033 100644 --- a/lms/templates/view.html +++ b/lms/templates/view.html @@ -97,7 +97,23 @@
{% if is_manager and not shared_url %}