From fda9fcb4353b121baa3270df782991009b229092 Mon Sep 17 00:00:00 2001 From: Zuzanna Kaczorek Date: Mon, 6 Mar 2023 15:27:22 +0100 Subject: [PATCH 1/5] Squashed commit of the following: commit b7f1d0adeba8b4e4b9371edd94511be532021e38 Author: Zuzanna Kaczorek Date: Mon Mar 6 15:26:24 2023 +0100 Fixing what I just messed up with git add and branches commit f12cd3e1d5b59676b6ad9a51e45976c495fd7c5c Author: Zuzanna Kaczorek Date: Mon Mar 6 15:06:47 2023 +0100 Fixtures for teachers modified to include join_date commit 91b043a771d21d19bcf8b9cc82cb387691520430 Author: Zuzanna Kaczorek Date: Mon Jan 30 17:16:55 2023 +0100 (Partial solve for #94) Added timestamps (join date) for teachers. commit 54985243c5043d7ca6eef022002259488bb147cb Author: Zuzanna Kaczorek Date: Mon Jan 16 21:26:47 2023 +0100 Added search fields to admin's view on teachers, added more fields to show on teachers list --- .gitignore | 28 ----------------- oioioi/teachers/admin.py | 31 +++++++++++++++++-- oioioi/teachers/fixtures/teachers.json | 6 ++-- .../migrations/0002_teacher_join_date.py | 20 ++++++++++++ oioioi/teachers/models.py | 1 + 5 files changed, 54 insertions(+), 32 deletions(-) delete mode 100644 .gitignore create mode 100644 oioioi/teachers/migrations/0002_teacher_join_date.py diff --git a/.gitignore b/.gitignore deleted file mode 100644 index b8675bfc5..000000000 --- a/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# use syntax common to .gitignore and .dockerignore - -/rst/source/sections/modules.rst -/build/ -/dist/ -/test_report.html -/test_report/ -/test_log.txt -/test_screenshots.tar.gz -/.coverage -/cover/ -**/*.egg -**/*.egg-info -**/*.pid -**/.DS_Store -**/*.pyc -**/__pycache__/ -**/*.kdev* -**/*~ -/.tox/ -/.pytest_cache/ -/.eggs/ -**/*.swp -/deployment/ -/venv*/ -/.idea/ -/.vscode/ -/.fleet/ diff --git a/oioioi/teachers/admin.py b/oioioi/teachers/admin.py index 3d8099566..dddfa79fc 100644 --- a/oioioi/teachers/admin.py +++ b/oioioi/teachers/admin.py @@ -16,8 +16,10 @@ class TeacherAdmin(admin.ModelAdmin): - list_display = ['user', 'school', 'is_active'] - list_editable = ['is_active'] + list_display = ['teacher_login', 'teacher_email', 'teacher_first_name', 'teacher_last_name', 'school', 'is_active', 'join_date'] + list_editable = ['is_active'] + + search_fields = ['school', 'user__username', 'user__first_name', 'user__last_name', 'user__email', 'join_date'] def has_add_permission(self, request): return request.user.is_superuser @@ -38,6 +40,31 @@ def formfield_for_foreignkey(self, db_field, request, **kwargs): def get_custom_list_select_related(self): return super(TeacherAdmin, self).get_custom_list_select_related() + ['user'] + def teacher_first_name(self, instance): + if not instance.user: + return '' + return instance.user.first_name + + def teacher_last_name(self, instance): + if not instance.user: + return '' + return instance.user.last_name + + def teacher_email(self, instance): + if not instance.user: + return '' + return instance.user.email + + def teacher_login(self, instance): + if not instance.user: + return '' + return instance.user.get_username() + + teacher_first_name.short_description = _("First name") + teacher_last_name.short_description = _("Last name") + teacher_email.short_description = _("Email address") + teacher_login.short_description = _("Userame") + admin.site.register(Teacher, TeacherAdmin) diff --git a/oioioi/teachers/fixtures/teachers.json b/oioioi/teachers/fixtures/teachers.json index 4809f7d9f..f067f884c 100644 --- a/oioioi/teachers/fixtures/teachers.json +++ b/oioioi/teachers/fixtures/teachers.json @@ -5,7 +5,8 @@ "fields": { "user": 1001, "is_active": true, - "school": "School" + "school": "School", + "join_date": "1983-01-01" } }, { @@ -14,7 +15,8 @@ "fields": { "user": 1002, "is_active": false, - "school": "School" + "school": "School", + "join_date": "1983-01-01" } } ] \ No newline at end of file diff --git a/oioioi/teachers/migrations/0002_teacher_join_date.py b/oioioi/teachers/migrations/0002_teacher_join_date.py new file mode 100644 index 000000000..a4f37e650 --- /dev/null +++ b/oioioi/teachers/migrations/0002_teacher_join_date.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.16 on 2023-01-30 16:01 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('teachers', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='teacher', + name='join_date', + field=models.DateField(auto_now_add=True, default=datetime.date(1983, 1, 1)), + preserve_default=False, + ), + ] diff --git a/oioioi/teachers/models.py b/oioioi/teachers/models.py index 901653fa7..232c4d4e9 100644 --- a/oioioi/teachers/models.py +++ b/oioioi/teachers/models.py @@ -24,6 +24,7 @@ class Teacher(models.Model): ) is_active = models.BooleanField(default=False, verbose_name=_("active")) school = models.CharField(max_length=255, verbose_name=_("school")) + join_date = models.DateField(auto_now_add=True) class Meta(object): permissions = (('teacher', _("Is a teacher")),) From ec017ae25a8d451cf156bcd660c3cbdf01e4eb1a Mon Sep 17 00:00:00 2001 From: Zuzanna Kaczorek Date: Mon, 6 Mar 2023 15:41:48 +0100 Subject: [PATCH 2/5] Fixed typo in admin.py, Userame -> Username --- oioioi/teachers/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oioioi/teachers/admin.py b/oioioi/teachers/admin.py index dddfa79fc..568ee4e6e 100644 --- a/oioioi/teachers/admin.py +++ b/oioioi/teachers/admin.py @@ -63,7 +63,7 @@ def teacher_login(self, instance): teacher_first_name.short_description = _("First name") teacher_last_name.short_description = _("Last name") teacher_email.short_description = _("Email address") - teacher_login.short_description = _("Userame") + teacher_login.short_description = _("Username") admin.site.register(Teacher, TeacherAdmin) From 9476aeb89efe82fa751ac32c68eaadc46e962aba Mon Sep 17 00:00:00 2001 From: Zuzanna Kaczorek Date: Thu, 25 May 2023 18:39:10 +0200 Subject: [PATCH 3/5] Moved ContestPermissionAdminForm to forms, added autocomplete searchfield for adding permissions for teachers to a contest --- oioioi/contests/admin.py | 12 ++---------- oioioi/contests/forms.py | 14 +++++++++++++- oioioi/contests/urls.py | 1 + oioioi/contests/views.py | 4 ++++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/oioioi/contests/admin.py b/oioioi/contests/admin.py index c4c64cc4b..9b8fb25bf 100644 --- a/oioioi/contests/admin.py +++ b/oioioi/contests/admin.py @@ -9,7 +9,6 @@ from django.contrib.admin.utils import quote, unquote from django.db.models import Case, F, OuterRef, Q, Subquery, Value, When from django.db.models.functions import Coalesce -from django.forms import ModelForm from django.forms.models import modelform_factory from django.http import HttpResponseRedirect from django.shortcuts import redirect, render @@ -29,6 +28,7 @@ ProblemInstanceForm, SimpleContestForm, TestsSelectionForm, + ContestPermissionAdminForm, ) from oioioi.contests.menu import ( contest_admin_menu_registry, @@ -1016,19 +1016,11 @@ def get_custom_list_select_related(self): order=50, ) - -class ContestPermissionAdminForm(ModelForm): - user = UserSelectionField(label=_("Username")) - - class Meta(object): - model = ContestPermission - fields = ('user', 'contest', 'permission') - - class ContestPermissionAdmin(admin.ModelAdmin): list_display = ['permission', 'user', 'user_full_name'] list_display_links = ['user'] ordering = ['permission', 'user'] + form = ContestPermissionAdminForm def user_full_name(self, instance): diff --git a/oioioi/contests/forms.py b/oioioi/contests/forms.py index 64357e963..0ba00d446 100644 --- a/oioioi/contests/forms.py +++ b/oioioi/contests/forms.py @@ -1,6 +1,7 @@ import json from django import forms +from django.forms import ModelForm from django.contrib.admin import widgets from django.contrib.auth.models import User from django.forms import ValidationError @@ -11,11 +12,22 @@ from oioioi.base.utils.inputs import narrow_input_field, narrow_input_fields from oioioi.base.utils.user_selection import UserSelectionField -from oioioi.contests.models import Contest, ProblemInstance, Round +from oioioi.contests.models import Contest, ProblemInstance, Round, ContestPermission from oioioi.contests.utils import is_contest_basicadmin, submittable_problem_instances from oioioi.programs.models import Test +class ContestPermissionAdminForm(ModelForm): + user = UserSelectionField(label=_("Username")) + + def __init__(self, *args, **kwargs): + super(ContestPermissionAdminForm, self).__init__(*args, **kwargs) + self.fields['user'].hints_url = reverse('teacher_search') + + class Meta(object): + model = ContestPermission + fields = ('user', 'contest', 'permission') + class SimpleContestForm(forms.ModelForm): class Meta(object): model = Contest diff --git a/oioioi/contests/urls.py b/oioioi/contests/urls.py index 609c4864f..7d61eecce 100644 --- a/oioioi/contests/urls.py +++ b/oioioi/contests/urls.py @@ -133,6 +133,7 @@ def glob_namespaced_patterns(namespace): name='user_info_redirect', ), re_path(r'^admin/', admin.contest_site.urls), + re_path(r'^teacher-search/$', views.get_teacher_names, name='teacher_search'), ] nonc_patterns = [ diff --git a/oioioi/contests/views.py b/oioioi/contests/views.py index b30997570..84f437b24 100644 --- a/oioioi/contests/views.py +++ b/oioioi/contests/views.py @@ -695,3 +695,7 @@ def reattach_problem_confirm_view(request, problem_instance_id, contest_id): 'contests/reattach_problem_confirm.html', {'problem_instance': problem_instance, 'destination_contest': contest}, ) + +def get_teacher_names(request): + queryset = User.objects.filter(teacher__isnull=False) + return get_user_hints_view(request, 'substr', queryset) From c86e504487cd2de07b2f5859efa999820d98648a Mon Sep 17 00:00:00 2001 From: Zuzanna Kaczorek Date: Mon, 5 Jun 2023 14:29:10 +0200 Subject: [PATCH 4/5] .gitignore not deleted anymore --- .gitignore | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b8675bfc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# use syntax common to .gitignore and .dockerignore + +/rst/source/sections/modules.rst +/build/ +/dist/ +/test_report.html +/test_report/ +/test_log.txt +/test_screenshots.tar.gz +/.coverage +/cover/ +**/*.egg +**/*.egg-info +**/*.pid +**/.DS_Store +**/*.pyc +**/__pycache__/ +**/*.kdev* +**/*~ +/.tox/ +/.pytest_cache/ +/.eggs/ +**/*.swp +/deployment/ +/venv*/ +/.idea/ +/.vscode/ +/.fleet/ From fa69a27a2846b5adcbdd167d731d0df224dde100 Mon Sep 17 00:00:00 2001 From: Zuzanna Kaczorek Date: Thu, 15 Jun 2023 18:03:43 +0200 Subject: [PATCH 5/5] The test (test_teachers_list) was added, though it doesn't recognize a name (teacher_search) that it should know as far as I understand. --- oioioi/contests/tests/tests.py | 23 +++++++++++++++++++++++ oioioi/contests/views.py | 1 + 2 files changed, 24 insertions(+) diff --git a/oioioi/contests/tests/tests.py b/oioioi/contests/tests/tests.py index 0c7433f40..2e1b1f8b4 100644 --- a/oioioi/contests/tests/tests.py +++ b/oioioi/contests/tests/tests.py @@ -2137,6 +2137,29 @@ def test_show_info_about(self): response = self.client.get(url) self.assertEqual(response.status_code, 200) + def test_teachers_list(self): + self.assertTrue(self.client.login(username='test_admin')) + contest = Contest.objects.get() + url = reverse('teacher_search') + response = self.client.get(url, {'substr': ''}) + self.assertEqual(404, response.status_code) + response = self.client.get(url) + self.assertEqual(404, response.status_code) + + response = self.client.get(url, {'substr': 'te'}) + self.assertEqual(200, response.status_code) + response = response.json() + self.assertListEqual( + ['test_admin (Test Admin)', 'test_user (Test User)'], response + ) + + response = self.client.get(url, {'substr': 'test admin'}) + response = response.json() + self.assertListEqual(['test_admin (Test Admin)'], response) + + self.assertTrue(self.client.login(username='test_user')) + check_not_accessible(self, url) + class TestProblemsMenuWithQuizzes(TestCase): fixtures = [ diff --git a/oioioi/contests/views.py b/oioioi/contests/views.py index 84f437b24..e370c804e 100644 --- a/oioioi/contests/views.py +++ b/oioioi/contests/views.py @@ -696,6 +696,7 @@ def reattach_problem_confirm_view(request, problem_instance_id, contest_id): {'problem_instance': problem_instance, 'destination_contest': contest}, ) +@enforce_condition(is_contest_basicadmin) def get_teacher_names(request): queryset = User.objects.filter(teacher__isnull=False) return get_user_hints_view(request, 'substr', queryset)