diff --git a/course_management/settings.py b/course_management/settings.py index 333201e..13ac1b4 100644 --- a/course_management/settings.py +++ b/course_management/settings.py @@ -16,7 +16,6 @@ from pathlib import Path import dj_database_url -from django.templatetags.static import static from django.utils.translation import gettext_lazy as _ @@ -35,13 +34,10 @@ EXTRA_ALLOWED_HOSTS = os.getenv("EXTRA_ALLOWED_HOSTS", "") extra_allowed_hosts_parsed = EXTRA_ALLOWED_HOSTS.split(",") -ALLOWED_HOSTS = [ - "localhost", - "127.0.0.1" -] + extra_allowed_hosts_parsed +ALLOWED_HOSTS = ["localhost", "127.0.0.1"] + extra_allowed_hosts_parsed IS_LOCAL = os.getenv("IS_LOCAL", "0") == "1" -print(f'IS_LOCAL={IS_LOCAL}') +print(f"IS_LOCAL={IS_LOCAL}") CSRF_TRUSTED_ORIGINS = [] @@ -62,17 +58,14 @@ "django.contrib.staticfiles", "django.contrib.sites", "loginas", - "accounts.apps.AccountsConfig", "courses.apps.CoursesConfig", - "allauth", "allauth.account", "allauth.socialaccount", "allauth.socialaccount.providers.google", "allauth.socialaccount.providers.github", "allauth.socialaccount.providers.slack", - ] MIDDLEWARE = [ @@ -215,12 +208,10 @@ "disable_existing_loggers": False, "formatters": { "json": { - "()": "pythonjsonlogger.jsonlogger.JsonFormatter", + "()": "pythonjsonlogger.json.JsonFormatter", "format": "%(asctime)s %(levelname)s %(name)s %(message)s", }, - "simple": { - "format": "%(levelname)s %(message)s" - }, + "simple": {"format": "%(levelname)s %(message)s"}, }, "handlers": { "console": { @@ -320,4 +311,7 @@ "SITE_HEADER": _("Course Management"), "SITE_TITLE": _("Course Management"), "SITE_SYMBOL": "school", -} \ No newline at end of file +} + +# Fix Django 6.0 URLField deprecation warning +FORMS_URLFIELD_ASSUME_HTTPS = True diff --git a/courses/models/course.py b/courses/models/course.py index f0be4c5..96acdee 100644 --- a/courses/models/course.py +++ b/courses/models/course.py @@ -78,8 +78,10 @@ class Meta: enrollment_date = models.DateTimeField(auto_now_add=True) display_name = models.CharField( - verbose_name="Leaderboard name", max_length=255, blank=True, - help_text="Name on the leaderboard" + verbose_name="Leaderboard name", + max_length=255, + blank=True, + help_text="Name on the leaderboard", ) display_on_leaderboard = models.BooleanField(default=True) @@ -92,7 +94,7 @@ class Meta: max_length=255, blank=True, null=True, - help_text="Your actual name that will appear on your certificate" + help_text="Your actual name that will appear on your certificate", ) total_score = models.IntegerField(default=0) @@ -129,15 +131,20 @@ class Meta: def save(self, *args, **kwargs): if not self.display_name: self.display_name = generate_random_name() - + # If certificate_name is being set, update the user's certificate_name - if self.certificate_name and self.certificate_name != self.student.certificate_name: + if ( + self.certificate_name + and self.certificate_name != self.student.certificate_name + ): self.student.certificate_name = self.certificate_name self.student.save() # If certificate_name is not set but user has one, use the user's certificate_name - elif not self.certificate_name and self.student.certificate_name: + elif ( + not self.certificate_name and self.student.certificate_name + ): self.certificate_name = self.student.certificate_name - + super().save(*args, **kwargs) def __str__(self): diff --git a/courses/models/homework.py b/courses/models/homework.py index 5f980d7..bb9645e 100644 --- a/courses/models/homework.py +++ b/courses/models/homework.py @@ -6,7 +6,6 @@ from .course import Course, Enrollment from courses.validators import validate_url_200 -from courses.validators.validating_json_field import ValidatingJSONField User = get_user_model() @@ -227,18 +226,32 @@ class HomeworkStatistics(models.Model): q3_total_score = models.FloatField(null=True, blank=True) # Fields for learning_in_public_score - min_learning_in_public_score = models.IntegerField(null=True, blank=True) - max_learning_in_public_score = models.IntegerField(null=True, blank=True) - avg_learning_in_public_score = models.FloatField(null=True, blank=True) - median_learning_in_public_score = models.FloatField(null=True, blank=True) - q1_learning_in_public_score = models.FloatField(null=True, blank=True) - q3_learning_in_public_score = models.FloatField(null=True, blank=True) + min_learning_in_public_score = models.IntegerField( + null=True, blank=True + ) + max_learning_in_public_score = models.IntegerField( + null=True, blank=True + ) + avg_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + median_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + q1_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + q3_learning_in_public_score = models.FloatField( + null=True, blank=True + ) # Fields for time_spent_lectures min_time_spent_lectures = models.FloatField(null=True, blank=True) max_time_spent_lectures = models.FloatField(null=True, blank=True) avg_time_spent_lectures = models.FloatField(null=True, blank=True) - median_time_spent_lectures = models.FloatField(null=True, blank=True) + median_time_spent_lectures = models.FloatField( + null=True, blank=True + ) q1_time_spent_lectures = models.FloatField(null=True, blank=True) q3_time_spent_lectures = models.FloatField(null=True, blank=True) @@ -246,7 +259,9 @@ class HomeworkStatistics(models.Model): min_time_spent_homework = models.FloatField(null=True, blank=True) max_time_spent_homework = models.FloatField(null=True, blank=True) avg_time_spent_homework = models.FloatField(null=True, blank=True) - median_time_spent_homework = models.FloatField(null=True, blank=True) + median_time_spent_homework = models.FloatField( + null=True, blank=True + ) q1_time_spent_homework = models.FloatField(null=True, blank=True) q3_time_spent_homework = models.FloatField(null=True, blank=True) @@ -260,58 +275,194 @@ def get_stat_fields(self): results = [] results.append( - ("Questions score", [ - (self.min_questions_score, "Minimum", "fas fa-arrow-down"), - (self.max_questions_score, "Maximum", "fas fa-arrow-up"), - (self.avg_questions_score, "Average", "fas fa-equals"), - (self.q1_questions_score, "25th Percentile", "fas fa-percentage"), - (self.median_questions_score, "Median", "fas fa-percentage"), - (self.q3_questions_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-question-circle') + ( + "Questions score", + [ + ( + self.min_questions_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_questions_score, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_questions_score, + "Average", + "fas fa-equals", + ), + ( + self.q1_questions_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_questions_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_questions_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-question-circle", + ) ) results.append( - ("Total score", [ - (self.min_total_score, "Minimum", "fas fa-arrow-down"), - (self.max_total_score, "Maximum", "fas fa-arrow-up"), - (self.avg_total_score, "Average", "fas fa-equals"), - (self.q1_total_score, "25th Percentile", "fas fa-percentage"), - (self.median_total_score, "Median", "fas fa-percentage"), - (self.q3_total_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-star') + ( + "Total score", + [ + ( + self.min_total_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_total_score, + "Maximum", + "fas fa-arrow-up", + ), + (self.avg_total_score, "Average", "fas fa-equals"), + ( + self.q1_total_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_total_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_total_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-star", + ) ) results.append( - ("Time spent on lectures", [ - (self.min_time_spent_lectures, "Minimum", "fas fa-arrow-down"), - (self.max_time_spent_lectures, "Maximum", "fas fa-arrow-up"), - (self.avg_time_spent_lectures, "Average", "fas fa-equals"), - (self.q1_time_spent_lectures, "25th Percentile", "fas fa-percentage"), - (self.median_time_spent_lectures, "Median", "fas fa-percentage"), - (self.q3_time_spent_lectures, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-book-reader') + ( + "Time spent on lectures", + [ + ( + self.min_time_spent_lectures, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_time_spent_lectures, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_time_spent_lectures, + "Average", + "fas fa-equals", + ), + ( + self.q1_time_spent_lectures, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_time_spent_lectures, + "Median", + "fas fa-percentage", + ), + ( + self.q3_time_spent_lectures, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-book-reader", + ) ) results.append( - ("Time spent on homework", [ - (self.min_time_spent_homework, "Minimum", "fas fa-arrow-down"), - (self.max_time_spent_homework, "Maximum", "fas fa-arrow-up"), - (self.avg_time_spent_homework, "Average", "fas fa-equals"), - (self.q1_time_spent_homework, "25th Percentile", "fas fa-percentage"), - (self.median_time_spent_homework, "Median", "fas fa-percentage"), - (self.q3_time_spent_homework, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-clock') + ( + "Time spent on homework", + [ + ( + self.min_time_spent_homework, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_time_spent_homework, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_time_spent_homework, + "Average", + "fas fa-equals", + ), + ( + self.q1_time_spent_homework, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_time_spent_homework, + "Median", + "fas fa-percentage", + ), + ( + self.q3_time_spent_homework, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-clock", + ) ) results.append( - ("Learning in public score", [ - (self.min_learning_in_public_score, "Minimum", "fas fa-arrow-down"), - (self.max_learning_in_public_score, "Maximum", "fas fa-arrow-up"), - (self.avg_learning_in_public_score, "Average", "fas fa-equals"), - (self.q1_learning_in_public_score, "25th Percentile", "fas fa-percentage"), - (self.median_learning_in_public_score, "Median", "fas fa-percentage"), - (self.q3_learning_in_public_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-globe') + ( + "Learning in public score", + [ + ( + self.min_learning_in_public_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_learning_in_public_score, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_learning_in_public_score, + "Average", + "fas fa-equals", + ), + ( + self.q1_learning_in_public_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_learning_in_public_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_learning_in_public_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-globe", + ) ) return results diff --git a/courses/models/project.py b/courses/models/project.py index e0dddc1..b3c344b 100644 --- a/courses/models/project.py +++ b/courses/models/project.py @@ -65,7 +65,9 @@ class ProjectSubmission(models.Model): student = models.ForeignKey(User, on_delete=models.CASCADE) enrollment = models.ForeignKey(Enrollment, on_delete=models.CASCADE) - github_link = models.URLField(validators=[URLValidator(), validate_url_200]) + github_link = models.URLField( + validators=[URLValidator(), validate_url_200], + ) commit_id = models.CharField(max_length=40) learning_in_public_links = models.JSONField(blank=True, null=True) @@ -81,7 +83,9 @@ class ProjectSubmission(models.Model): project_learning_in_public_score = models.IntegerField(default=0) peer_review_score = models.IntegerField(default=0) - peer_review_learning_in_public_score = models.IntegerField(default=0) + peer_review_learning_in_public_score = models.IntegerField( + default=0 + ) total_score = models.IntegerField(default=0) @@ -123,12 +127,18 @@ def median_score(self) -> int: result = 0 scores = [option["score"] for option in self.options] - if self.review_criteria_type == ReviewCriteriaTypes.RADIO_BUTTONS.value: + if ( + self.review_criteria_type + == ReviewCriteriaTypes.RADIO_BUTTONS.value + ): result = statistics.median(scores) - if self.review_criteria_type == ReviewCriteriaTypes.CHECKBOXES.value: - result = sum(scores) / 2 # just give the middle score - + if ( + self.review_criteria_type + == ReviewCriteriaTypes.CHECKBOXES.value + ): + result = sum(scores) / 2 # just give the middle score + return math.ceil(result) def __str__(self): @@ -238,12 +248,24 @@ class ProjectStatistics(models.Model): q3_project_score = models.FloatField(null=True, blank=True) # Fields for project_learning_in_public_score - min_project_learning_in_public_score = models.IntegerField(null=True, blank=True) - max_project_learning_in_public_score = models.IntegerField(null=True, blank=True) - avg_project_learning_in_public_score = models.FloatField(null=True, blank=True) - median_project_learning_in_public_score = models.FloatField(null=True, blank=True) - q1_project_learning_in_public_score = models.FloatField(null=True, blank=True) - q3_project_learning_in_public_score = models.FloatField(null=True, blank=True) + min_project_learning_in_public_score = models.IntegerField( + null=True, blank=True + ) + max_project_learning_in_public_score = models.IntegerField( + null=True, blank=True + ) + avg_project_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + median_project_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + q1_project_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + q3_project_learning_in_public_score = models.FloatField( + null=True, blank=True + ) # Fields for peer_review_score min_peer_review_score = models.IntegerField(null=True, blank=True) @@ -254,12 +276,24 @@ class ProjectStatistics(models.Model): q3_peer_review_score = models.FloatField(null=True, blank=True) # Fields for peer_review_learning_in_public_score - min_peer_review_learning_in_public_score = models.IntegerField(null=True, blank=True) - max_peer_review_learning_in_public_score = models.IntegerField(null=True, blank=True) - avg_peer_review_learning_in_public_score = models.FloatField(null=True, blank=True) - median_peer_review_learning_in_public_score = models.FloatField(null=True, blank=True) - q1_peer_review_learning_in_public_score = models.FloatField(null=True, blank=True) - q3_peer_review_learning_in_public_score = models.FloatField(null=True, blank=True) + min_peer_review_learning_in_public_score = models.IntegerField( + null=True, blank=True + ) + max_peer_review_learning_in_public_score = models.IntegerField( + null=True, blank=True + ) + avg_peer_review_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + median_peer_review_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + q1_peer_review_learning_in_public_score = models.FloatField( + null=True, blank=True + ) + q3_peer_review_learning_in_public_score = models.FloatField( + null=True, blank=True + ) # Fields for total_score min_total_score = models.IntegerField(null=True, blank=True) @@ -287,69 +321,225 @@ def get_stat_fields(self): results = [] results.append( - ("Project score", [ - (self.min_project_score, "Minimum", "fas fa-arrow-down"), - (self.max_project_score, "Maximum", "fas fa-arrow-up"), - (self.avg_project_score, "Average", "fas fa-equals"), - (self.q1_project_score, "25th Percentile", "fas fa-percentage"), - (self.median_project_score, "Median", "fas fa-percentage"), - (self.q3_project_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-project-diagram') + ( + "Project score", + [ + ( + self.min_project_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_project_score, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_project_score, + "Average", + "fas fa-equals", + ), + ( + self.q1_project_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_project_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_project_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-project-diagram", + ) ) results.append( - ("Project learning in public score", [ - (self.min_project_learning_in_public_score, "Minimum", "fas fa-arrow-down"), - (self.max_project_learning_in_public_score, "Maximum", "fas fa-arrow-up"), - (self.avg_project_learning_in_public_score, "Average", "fas fa-equals"), - (self.q1_project_learning_in_public_score, "25th Percentile", "fas fa-percentage"), - (self.median_project_learning_in_public_score, "Median", "fas fa-percentage"), - (self.q3_project_learning_in_public_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-globe') + ( + "Project learning in public score", + [ + ( + self.min_project_learning_in_public_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_project_learning_in_public_score, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_project_learning_in_public_score, + "Average", + "fas fa-equals", + ), + ( + self.q1_project_learning_in_public_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_project_learning_in_public_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_project_learning_in_public_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-globe", + ) ) results.append( - ("Peer review score", [ - (self.min_peer_review_score, "Minimum", "fas fa-arrow-down"), - (self.max_peer_review_score, "Maximum", "fas fa-arrow-up"), - (self.avg_peer_review_score, "Average", "fas fa-equals"), - (self.q1_peer_review_score, "25th Percentile", "fas fa-percentage"), - (self.median_peer_review_score, "Median", "fas fa-percentage"), - (self.q3_peer_review_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-users') + ( + "Peer review score", + [ + ( + self.min_peer_review_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_peer_review_score, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_peer_review_score, + "Average", + "fas fa-equals", + ), + ( + self.q1_peer_review_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_peer_review_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_peer_review_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-users", + ) ) results.append( - ("Peer review learning in public score", [ - (self.min_peer_review_learning_in_public_score, "Minimum", "fas fa-arrow-down"), - (self.max_peer_review_learning_in_public_score, "Maximum", "fas fa-arrow-up"), - (self.avg_peer_review_learning_in_public_score, "Average", "fas fa-equals"), - (self.q1_peer_review_learning_in_public_score, "25th Percentile", "fas fa-percentage"), - (self.median_peer_review_learning_in_public_score, "Median", "fas fa-percentage"), - (self.q3_peer_review_learning_in_public_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-share-alt') + ( + "Peer review learning in public score", + [ + ( + self.min_peer_review_learning_in_public_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_peer_review_learning_in_public_score, + "Maximum", + "fas fa-arrow-up", + ), + ( + self.avg_peer_review_learning_in_public_score, + "Average", + "fas fa-equals", + ), + ( + self.q1_peer_review_learning_in_public_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_peer_review_learning_in_public_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_peer_review_learning_in_public_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-share-alt", + ) ) results.append( - ("Total score", [ - (self.min_total_score, "Minimum", "fas fa-arrow-down"), - (self.max_total_score, "Maximum", "fas fa-arrow-up"), - (self.avg_total_score, "Average", "fas fa-equals"), - (self.q1_total_score, "25th Percentile", "fas fa-percentage"), - (self.median_total_score, "Median", "fas fa-percentage"), - (self.q3_total_score, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-star') + ( + "Total score", + [ + ( + self.min_total_score, + "Minimum", + "fas fa-arrow-down", + ), + ( + self.max_total_score, + "Maximum", + "fas fa-arrow-up", + ), + (self.avg_total_score, "Average", "fas fa-equals"), + ( + self.q1_total_score, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_total_score, + "Median", + "fas fa-percentage", + ), + ( + self.q3_total_score, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-star", + ) ) results.append( - ("Time spent on project", [ - (self.min_time_spent, "Minimum", "fas fa-arrow-down"), - (self.max_time_spent, "Maximum", "fas fa-arrow-up"), - (self.avg_time_spent, "Average", "fas fa-equals"), - (self.q1_time_spent, "25th Percentile", "fas fa-percentage"), - (self.median_time_spent, "Median", "fas fa-percentage"), - (self.q3_time_spent, "75th Percentile", "fas fa-percentage"), - ], 'fas fa-clock') + ( + "Time spent on project", + [ + ( + self.min_time_spent, + "Minimum", + "fas fa-arrow-down", + ), + (self.max_time_spent, "Maximum", "fas fa-arrow-up"), + (self.avg_time_spent, "Average", "fas fa-equals"), + ( + self.q1_time_spent, + "25th Percentile", + "fas fa-percentage", + ), + ( + self.median_time_spent, + "Median", + "fas fa-percentage", + ), + ( + self.q3_time_spent, + "75th Percentile", + "fas fa-percentage", + ), + ], + "fas fa-clock", + ) ) return results