diff --git a/add_more_test_data.py b/add_more_test_data.py index 45ecab1..2934d0d 100644 --- a/add_more_test_data.py +++ b/add_more_test_data.py @@ -167,6 +167,8 @@ def create_answers_for_student(submission): homework=homework, student=user, defaults={"enrollment": enrollment}, + time_spent_lectures=random.randint(0, 10), + time_spent_homework=random.randint(0, 10), ) if created: diff --git a/courses/migrations/0017_alter_projectsubmission_learning_in_public_links_and_more.py b/courses/migrations/0017_alter_projectsubmission_learning_in_public_links_and_more.py new file mode 100644 index 0000000..669806c --- /dev/null +++ b/courses/migrations/0017_alter_projectsubmission_learning_in_public_links_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 4.2.14 on 2024-10-10 12:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('courses', '0016_enrollment_about_me_enrollment_github_url_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='projectsubmission', + name='learning_in_public_links', + field=models.JSONField(blank=True, null=True), + ), + migrations.AlterField( + model_name='submission', + name='learning_in_public_links', + field=models.JSONField(blank=True, help_text='Links where students talk about the course', null=True), + ), + migrations.CreateModel( + name='HomeworkStatistics', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('total_submissions', models.IntegerField(default=0)), + ('min_questions_score', models.IntegerField(blank=True, null=True)), + ('max_questions_score', models.IntegerField(blank=True, null=True)), + ('avg_questions_score', models.FloatField(blank=True, null=True)), + ('median_questions_score', models.FloatField(blank=True, null=True)), + ('q1_questions_score', models.FloatField(blank=True, null=True)), + ('q3_questions_score', models.FloatField(blank=True, null=True)), + ('min_total_score', models.IntegerField(blank=True, null=True)), + ('max_total_score', models.IntegerField(blank=True, null=True)), + ('avg_total_score', models.FloatField(blank=True, null=True)), + ('median_total_score', models.FloatField(blank=True, null=True)), + ('q1_total_score', models.FloatField(blank=True, null=True)), + ('q3_total_score', models.FloatField(blank=True, null=True)), + ('min_learning_in_public_score', models.IntegerField(blank=True, null=True)), + ('max_learning_in_public_score', models.IntegerField(blank=True, null=True)), + ('avg_learning_in_public_score', models.FloatField(blank=True, null=True)), + ('median_learning_in_public_score', models.FloatField(blank=True, null=True)), + ('q1_learning_in_public_score', models.FloatField(blank=True, null=True)), + ('q3_learning_in_public_score', models.FloatField(blank=True, null=True)), + ('min_time_spent_lectures', models.FloatField(blank=True, null=True)), + ('max_time_spent_lectures', models.FloatField(blank=True, null=True)), + ('avg_time_spent_lectures', models.FloatField(blank=True, null=True)), + ('median_time_spent_lectures', models.FloatField(blank=True, null=True)), + ('q1_time_spent_lectures', models.FloatField(blank=True, null=True)), + ('q3_time_spent_lectures', models.FloatField(blank=True, null=True)), + ('min_time_spent_homework', models.FloatField(blank=True, null=True)), + ('max_time_spent_homework', models.FloatField(blank=True, null=True)), + ('avg_time_spent_homework', models.FloatField(blank=True, null=True)), + ('median_time_spent_homework', models.FloatField(blank=True, null=True)), + ('q1_time_spent_homework', models.FloatField(blank=True, null=True)), + ('q3_time_spent_homework', models.FloatField(blank=True, null=True)), + ('last_calculated', models.DateTimeField(auto_now=True)), + ('homework', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='statistics', to='courses.homework')), + ], + ), + ] diff --git a/courses/models/homework.py b/courses/models/homework.py index 6204831..0fec3b9 100644 --- a/courses/models/homework.py +++ b/courses/models/homework.py @@ -155,7 +155,10 @@ class Submission(models.Model): homework_link = models.URLField( blank=True, null=True, - validators=[URLValidator(schemes=["http", "https", "git"]), validate_url_200], + validators=[ + URLValidator(schemes=["http", "https", "git"]), + validate_url_200, + ], ) learning_in_public_links = models.JSONField( blank=True, @@ -199,3 +202,120 @@ class Answer(models.Model): def __str__(self): return f"Answer id={self.id} for {self.question}" + + +class HomeworkStatistics(models.Model): + homework = models.OneToOneField( + Homework, on_delete=models.CASCADE, related_name="statistics" + ) + + total_submissions = models.IntegerField(default=0) + + # Fields for questions_score + min_questions_score = models.IntegerField(null=True, blank=True) + max_questions_score = models.IntegerField(null=True, blank=True) + avg_questions_score = models.FloatField(null=True, blank=True) + median_questions_score = models.FloatField(null=True, blank=True) + q1_questions_score = models.FloatField(null=True, blank=True) + q3_questions_score = models.FloatField(null=True, blank=True) + + # Fields for total_score + min_total_score = models.IntegerField(null=True, blank=True) + max_total_score = models.IntegerField(null=True, blank=True) + avg_total_score = models.FloatField(null=True, blank=True) + median_total_score = models.FloatField(null=True, blank=True) + q1_total_score = models.FloatField(null=True, blank=True) + 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) + + # 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) + q1_time_spent_lectures = models.FloatField(null=True, blank=True) + q3_time_spent_lectures = models.FloatField(null=True, blank=True) + + # Fields for time_spent_homework + 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) + q1_time_spent_homework = models.FloatField(null=True, blank=True) + q3_time_spent_homework = models.FloatField(null=True, blank=True) + + last_calculated = models.DateTimeField(auto_now=True) + + def get_value(self, field_name, stats_type): + attribute_name = f"{stats_type}_{field_name}" + return getattr(self, attribute_name) + + 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') + ) + + 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') + ) + + 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') + ) + + 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') + ) + + 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') + ) + + return results + + def __str__(self): + return f"Statistics for {self.homework.slug}" diff --git a/courses/templates/homework/homework.html b/courses/templates/homework/homework.html index 120f180..ac867ab 100644 --- a/courses/templates/homework/homework.html +++ b/courses/templates/homework/homework.html @@ -41,6 +41,10 @@
This homework is already scored. You didn't submit your answers.
{% endif %} + + {% endif %}{{ stats.total_submissions }}
+{{ value|floatformat:0 }}
++ Calculated: {{ stats.last_calculated }} +
+ + +{% endblock %} \ No newline at end of file diff --git a/courses/urls.py b/courses/urls.py index d125d2b..5848762 100644 --- a/courses/urls.py +++ b/courses/urls.py @@ -28,6 +28,8 @@ course.enrollment_view, name="enrollment", ), + + # project path( "