From 8b0630967b11cc36b4f011493cf95c69b3e46666 Mon Sep 17 00:00:00 2001 From: Alexey Grigorev Date: Wed, 23 Oct 2024 11:33:49 +0200 Subject: [PATCH] admin files reorganized --- courses/admin.py | 213 +------------------------------------- courses/admin/__init__.py | 22 ++++ courses/admin/course.py | 52 ++++++++++ courses/admin/forms.py | 0 courses/admin/homework.py | 106 +++++++++++++++++++ courses/admin/projects.py | 64 ++++++++++++ courses/scoring.py | 2 +- 7 files changed, 246 insertions(+), 213 deletions(-) create mode 100644 courses/admin/__init__.py create mode 100644 courses/admin/course.py create mode 100644 courses/admin/forms.py create mode 100644 courses/admin/homework.py create mode 100644 courses/admin/projects.py diff --git a/courses/admin.py b/courses/admin.py index 2b94ef2..df64c93 100644 --- a/courses/admin.py +++ b/courses/admin.py @@ -1,212 +1 @@ -from django import forms -from django.contrib import admin -from unfold.admin import ModelAdmin, TabularInline -from unfold.widgets import ( - UnfoldAdminTextInputWidget, - UnfoldAdminTextareaWidget, -) -from django.contrib import messages - -from .models import ( - Course, - Homework, - Question, - Project, - ReviewCriteria, - HomeworkState, -) - -from .scoring import ( - score_homework_submissions, - update_leaderboard, - fill_correct_answers, - calculate_homework_statistics, -) - -from .projects import ( - assign_peer_reviews_for_project, - score_project, - ProjectActionStatus, -) - - -class QuestionForm(forms.ModelForm): - class Meta: - model = Question - fields = "__all__" - widgets = { - "text": UnfoldAdminTextInputWidget(attrs={"size": "60"}), - "possible_answers": UnfoldAdminTextareaWidget( - attrs={"cols": 60, "rows": 4} - ), - "correct_answer": UnfoldAdminTextInputWidget( - attrs={"size": "20"} - ), - } - - -class QuestionInline(TabularInline): - model = Question - form = QuestionForm - extra = 0 - - -def score_selected_homeworks(modeladmin, request, queryset): - for homework in queryset: - status, message = score_homework_submissions(homework.id) - if status: - modeladmin.message_user( - request, message, level=messages.SUCCESS - ) - else: - modeladmin.message_user( - request, message, level=messages.WARNING - ) - - -score_selected_homeworks.short_description = "Score selected homeworks" - - -def set_most_popular_as_correct(modeladmin, request, queryset): - for homework in queryset: - fill_correct_answers(homework) - modeladmin.message_user( - request, - f"Correct answer for {homework} set to most popular", - level=messages.SUCCESS, - ) - - -set_most_popular_as_correct.short_description = ( - "Set correct answers to most popular" -) - - -def calculate_statistics_selected_homeworks( - modeladmin, request, queryset -): - for homework in queryset: - if homework.state != HomeworkState.SCORED.value: - modeladmin.message_user( - request, - f"Cannot calculate statistics for {homework} " - "because it has not been scored", - level=messages.WARNING, - ) - continue - - calculate_homework_statistics(homework, force=True) - - message = f"Statistics calculated for {homework}" - modeladmin.message_user( - request, message, level=messages.SUCCESS - ) - - -calculate_statistics_selected_homeworks.short_description = ( - "Calculate statistics" -) - - -@admin.register(Homework) -class HomeworkAdmin(ModelAdmin): - inlines = [QuestionInline] - actions = [ - score_selected_homeworks, - set_most_popular_as_correct, - calculate_statistics_selected_homeworks, - ] - list_display = ["title", "course", "due_date", "state"] - list_filter = ["course__slug"] - - -class CriteriaForm(forms.ModelForm): - class Meta: - model = ReviewCriteria - fields = "__all__" - widgets = { - "description": UnfoldAdminTextInputWidget( - attrs={"size": "60"} - ), - "options": UnfoldAdminTextareaWidget( - attrs={"cols": 60, "rows": 4} - ), - } - - -class CriteriaInline(TabularInline): - model = ReviewCriteria - form = CriteriaForm - extra = 0 - - -def update_leaderboard_admin(modeladmin, request, queryset): - for course in queryset: - update_leaderboard(course) - modeladmin.message_user( - request, - f"Leaderboard updated for course {course}", - level=messages.SUCCESS, - ) - - -update_leaderboard_admin.short_description = "Update leaderboard" - - -@admin.register(Course) -class CourseAdmin(ModelAdmin): - actions = [update_leaderboard_admin] - inlines = [CriteriaInline] - list_display = ["title"] - - -def assign_peer_reviews_for_project_admin( - modeladmin, request, queryset -): - for project in queryset: - status, message = assign_peer_reviews_for_project(project) - if status == ProjectActionStatus.OK: - modeladmin.message_user( - request, message, level=messages.SUCCESS - ) - else: - modeladmin.message_user( - request, message, level=messages.WARNING - ) - - -assign_peer_reviews_for_project_admin.short_description = ( - "Assign peer reviews" -) - - -def score_projects_admin(modeladmin, request, queryset): - for project in queryset: - status, message = score_project(project) - if status == ProjectActionStatus.OK: - modeladmin.message_user( - request, message, level=messages.SUCCESS - ) - else: - modeladmin.message_user( - request, message, level=messages.WARNING - ) - - -score_projects_admin.short_description = "Score projects" - - -@admin.register(Project) -class ProjectAdmin(ModelAdmin): - actions = [ - assign_peer_reviews_for_project_admin, - score_projects_admin, - ] - - list_display = ["title", "course", "state"] - list_filter = ["course__slug"] - - -@admin.register(ReviewCriteria) -class ReviewCriteriaAdmin(ModelAdmin): - pass +from .admin import * \ No newline at end of file diff --git a/courses/admin/__init__.py b/courses/admin/__init__.py new file mode 100644 index 0000000..199dfb8 --- /dev/null +++ b/courses/admin/__init__.py @@ -0,0 +1,22 @@ +from pathlib import Path +from importlib import import_module + + +def import_admin_modules(): + # we just want to import all the modules, + # so in admin.py we can use the star import + current_dir = Path(__file__).resolve().parent + + for python_file in current_dir.glob("*.py"): + if python_file.name == "__init__.py": + continue + + module_name = f"courses.admin.{python_file.stem}" + + try: + import_module(module_name) + except ImportError as e: + print(f"Failed to import {module_name}: {e}") + + +import_admin_modules() diff --git a/courses/admin/course.py b/courses/admin/course.py new file mode 100644 index 0000000..200f565 --- /dev/null +++ b/courses/admin/course.py @@ -0,0 +1,52 @@ +from django import forms +from django.contrib import admin +from unfold.admin import ModelAdmin, TabularInline +from unfold.widgets import ( + UnfoldAdminTextInputWidget, + UnfoldAdminTextareaWidget, +) + +from django.contrib import messages + +from courses.models import Course, ReviewCriteria +from courses.scoring import update_leaderboard + + +class CriteriaForm(forms.ModelForm): + class Meta: + model = ReviewCriteria + fields = "__all__" + widgets = { + "description": UnfoldAdminTextInputWidget( + attrs={"size": "60"} + ), + "options": UnfoldAdminTextareaWidget( + attrs={"cols": 60, "rows": 4} + ), + } + + +class CriteriaInline(TabularInline): + model = ReviewCriteria + form = CriteriaForm + extra = 0 + + +def update_leaderboard_admin(modeladmin, request, queryset): + for course in queryset: + update_leaderboard(course) + modeladmin.message_user( + request, + f"Leaderboard updated for course {course}", + level=messages.SUCCESS, + ) + + +update_leaderboard_admin.short_description = "Update leaderboard" + + +@admin.register(Course) +class CourseAdmin(ModelAdmin): + actions = [update_leaderboard_admin] + inlines = [CriteriaInline] + list_display = ["title"] diff --git a/courses/admin/forms.py b/courses/admin/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/courses/admin/homework.py b/courses/admin/homework.py new file mode 100644 index 0000000..4ba6114 --- /dev/null +++ b/courses/admin/homework.py @@ -0,0 +1,106 @@ +from django import forms +from django.contrib import admin +from unfold.admin import ModelAdmin, TabularInline +from unfold.widgets import ( + UnfoldAdminTextInputWidget, + UnfoldAdminTextareaWidget, +) +from django.contrib import messages + +from courses.models import Homework, Question, HomeworkState + +from courses.scoring import ( + score_homework_submissions, + fill_correct_answers, + calculate_homework_statistics, +) + + +class QuestionForm(forms.ModelForm): + class Meta: + model = Question + fields = "__all__" + widgets = { + "text": UnfoldAdminTextInputWidget(attrs={"size": "60"}), + "possible_answers": UnfoldAdminTextareaWidget( + attrs={"cols": 60, "rows": 4} + ), + "correct_answer": UnfoldAdminTextInputWidget( + attrs={"size": "20"} + ), + } + + +class QuestionInline(TabularInline): + model = Question + form = QuestionForm + extra = 0 + + +def score_selected_homeworks(modeladmin, request, queryset): + for homework in queryset: + status, message = score_homework_submissions(homework.id) + if status: + modeladmin.message_user( + request, message, level=messages.SUCCESS + ) + else: + modeladmin.message_user( + request, message, level=messages.WARNING + ) + + +score_selected_homeworks.short_description = "Score selected homeworks" + + +def set_most_popular_as_correct(modeladmin, request, queryset): + for homework in queryset: + fill_correct_answers(homework) + modeladmin.message_user( + request, + f"Correct answer for {homework} set to most popular", + level=messages.SUCCESS, + ) + + +set_most_popular_as_correct.short_description = ( + "Set correct answers to most popular" +) + + +def calculate_statistics_selected_homeworks( + modeladmin, request, queryset +): + for homework in queryset: + if homework.state != HomeworkState.SCORED.value: + modeladmin.message_user( + request, + f"Cannot calculate statistics for {homework} " + "because it has not been scored", + level=messages.WARNING, + ) + continue + + calculate_homework_statistics(homework, force=True) + + message = f"Statistics calculated for {homework}" + modeladmin.message_user( + request, message, level=messages.SUCCESS + ) + + +calculate_statistics_selected_homeworks.short_description = ( + "Calculate statistics" +) + + +@admin.register(Homework) +class HomeworkAdmin(ModelAdmin): + inlines = [QuestionInline] + actions = [ + score_selected_homeworks, + set_most_popular_as_correct, + calculate_statistics_selected_homeworks, + ] + list_display = ["title", "course", "due_date", "state"] + list_filter = ["course__slug"] diff --git a/courses/admin/projects.py b/courses/admin/projects.py new file mode 100644 index 0000000..685ebd2 --- /dev/null +++ b/courses/admin/projects.py @@ -0,0 +1,64 @@ +from django.contrib import admin +from unfold.admin import ModelAdmin + +from django.contrib import messages + +from courses.models import Project, ReviewCriteria + +from courses.projects import ( + assign_peer_reviews_for_project, + score_project, + ProjectActionStatus, +) + + +def assign_peer_reviews_for_project_admin( + modeladmin, request, queryset +): + for project in queryset: + status, message = assign_peer_reviews_for_project(project) + if status == ProjectActionStatus.OK: + modeladmin.message_user( + request, message, level=messages.SUCCESS + ) + else: + modeladmin.message_user( + request, message, level=messages.WARNING + ) + + +assign_peer_reviews_for_project_admin.short_description = ( + "Assign peer reviews" +) + + +def score_projects_admin(modeladmin, request, queryset): + for project in queryset: + status, message = score_project(project) + if status == ProjectActionStatus.OK: + modeladmin.message_user( + request, message, level=messages.SUCCESS + ) + else: + modeladmin.message_user( + request, message, level=messages.WARNING + ) + + +score_projects_admin.short_description = "Score projects" + + +@admin.register(Project) +class ProjectAdmin(ModelAdmin): + actions = [ + assign_peer_reviews_for_project_admin, + score_projects_admin, + ] + + list_display = ["title", "course", "state"] + list_filter = ["course__slug"] + + +@admin.register(ReviewCriteria) +class ReviewCriteriaAdmin(ModelAdmin): + pass diff --git a/courses/scoring.py b/courses/scoring.py index 935ddf0..e8a3f38 100644 --- a/courses/scoring.py +++ b/courses/scoring.py @@ -6,7 +6,7 @@ from collections import defaultdict from django.utils import timezone -from django.db.models import Sum, Count, Min, Max, Avg +from django.db.models import Sum, Count from django.db import transaction