From fda9fcb4353b121baa3270df782991009b229092 Mon Sep 17 00:00:00 2001
From: Zuzanna Kaczorek <zk429289@students.mimuw.edu.pl>
Date: Mon, 6 Mar 2023 15:27:22 +0100
Subject: [PATCH 1/5] Squashed commit of the following:

commit b7f1d0adeba8b4e4b9371edd94511be532021e38
Author: Zuzanna Kaczorek <zk429289@students.mimuw.edu.pl>
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 <zk429289@students.mimuw.edu.pl>
Date:   Mon Mar 6 15:06:47 2023 +0100

    Fixtures for teachers modified to include join_date

commit 91b043a771d21d19bcf8b9cc82cb387691520430
Author: Zuzanna Kaczorek <zk429289@students.mimuw.edu.pl>
Date:   Mon Jan 30 17:16:55 2023 +0100

    (Partial solve for #94) Added timestamps (join date) for teachers.

commit 54985243c5043d7ca6eef022002259488bb147cb
Author: Zuzanna Kaczorek <zk429289@students.mimuw.edu.pl>
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 <zk429289@students.mimuw.edu.pl>
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 <zk429289@students.mimuw.edu.pl>
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 <zk429289@students.mimuw.edu.pl>
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 <zk429289@students.mimuw.edu.pl>
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)