From ddf87afeaaf0aeee0200d0774519c4cb73bc36c5 Mon Sep 17 00:00:00 2001 From: dodo42 Date: Fri, 8 Jun 2018 19:44:32 +0200 Subject: [PATCH 1/9] Create UserSchool model and data migrations. --- .../migrations/0017_auto_20180607_1156.py | 32 +++++++++++++++++ .../migrations/0018_auto_20180607_1158.py | 36 +++++++++++++++++++ trojsten/people/models.py | 14 ++++++++ 3 files changed, 82 insertions(+) create mode 100644 trojsten/people/migrations/0017_auto_20180607_1156.py create mode 100644 trojsten/people/migrations/0018_auto_20180607_1158.py diff --git a/trojsten/people/migrations/0017_auto_20180607_1156.py b/trojsten/people/migrations/0017_auto_20180607_1156.py new file mode 100644 index 000000000..f6fd32608 --- /dev/null +++ b/trojsten/people/migrations/0017_auto_20180607_1156.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-06-07 09:56 +from __future__ import unicode_literals + +from django.conf import settings +import django.contrib.auth.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('schools', '0001_initial'), + ('people', '0016_merge_20180121_1616'), + ] + + operations = [ + migrations.CreateModel( + name='UserSchool', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('start_time', models.DateField(verbose_name='Start of study')), + ('school', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='schools.School', verbose_name='School')), + ], + ), + migrations.AddField( + model_name='userschool', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'), + ), + ] diff --git a/trojsten/people/migrations/0018_auto_20180607_1158.py b/trojsten/people/migrations/0018_auto_20180607_1158.py new file mode 100644 index 000000000..87ec8e07f --- /dev/null +++ b/trojsten/people/migrations/0018_auto_20180607_1158.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-06-07 09:58 +from __future__ import unicode_literals +from datetime import date + +from django.db import migrations + + +def migrate_schools(apps, schema_editor): + User = apps.get_model('people', 'User') + UserSchool = apps.get_model('people', 'UserSchool') + for user in User.objects.all(): + if user.school: + UserSchool.objects.create( + user=user, + school=user.school, + start_time=date(max(user.graduation - 8, 1) if user.graduation else 1, 9, 1) + ) + + +def reverse_migrate_schools(apps, schema_editor): + UserSchool = apps.get_model('people', 'UserSchool') + for row in UserSchool.objects.order_by('user', '-start_time').distinct('user'): + row.user.school = row.school + row.user.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('people', '0017_auto_20180607_1156'), + ] + + operations = [ + migrations.RunPython(migrate_schools, reverse_migrate_schools) + ] diff --git a/trojsten/people/models.py b/trojsten/people/models.py index 26595646f..d558586e2 100644 --- a/trojsten/people/models.py +++ b/trojsten/people/models.py @@ -13,6 +13,7 @@ from django.utils.translation import ugettext_lazy as _ from unidecode import unidecode +from trojsten.schools.models import School from . import constants @@ -109,6 +110,13 @@ class Meta: verbose_name = 'používateľ' verbose_name_plural = 'používatelia' + @property + def school_now(self): + return UserSchool.objects.filter(user=self).order_by('-start_time').first().school + + def school_at(self, date): + return UserSchool.objects.filter(user=self, start_time__lt=date).order_by('-start_time').first().school + @property def school_year(self): return self.school_year_at( @@ -160,6 +168,12 @@ def format_name(num): User._meta.get_field('username').blank = True +class UserSchool(models.Model): + user = models.ForeignKey(User, verbose_name=_('User')) + school = models.ForeignKey(School, verbose_name=_('School')) + start_time = models.DateField(verbose_name=_('Start of study')) + + class UserPropertyManager(models.Manager): def get_queryset(self): return super(UserPropertyManager, self).get_queryset().select_related('key') From 042f20860c758a8643ee3a64e98d3e66cdc6d481 Mon Sep 17 00:00:00 2001 From: dodo42 Date: Fri, 8 Jun 2018 21:26:49 +0200 Subject: [PATCH 2/9] Edit Trojsten User forms, admin interface and exports. --- trojsten/events/admin.py | 5 +++ trojsten/people/admin.py | 39 ++++++++++++++++--- trojsten/people/forms.py | 26 ++++++++----- .../migrations/0019_auto_20180608_2124.py | 26 +++++++++++++ trojsten/people/models.py | 16 +++----- 5 files changed, 86 insertions(+), 26 deletions(-) create mode 100644 trojsten/people/migrations/0019_auto_20180608_2124.py diff --git a/trojsten/events/admin.py b/trojsten/events/admin.py index f9d4a6883..00aa612c7 100644 --- a/trojsten/events/admin.py +++ b/trojsten/events/admin.py @@ -4,6 +4,7 @@ from django.contrib import admin from django.utils.encoding import force_text +from django.utils.translation import ugettext_lazy as _ from easy_select2 import select2_modelform from import_export import fields, resources from import_export.admin import ExportMixin @@ -67,6 +68,7 @@ def get_queryset(self, request): class EventParticipantExport(resources.ModelResource): + user__school__verbose_name = fields.Field() type = fields.Field() street = fields.Field() @@ -83,6 +85,9 @@ class Meta: ] widgets = {'user__birth_date': {'format': '%d.%m.%Y'}} + def dehydrate_user__school__verbose_name(selfs, obj): + return obj.user.school.verbose_name if obj.user.school else _('Other_school') + def dehydrate_type(self, obj): return obj.get_type_display() diff --git a/trojsten/people/admin.py b/trojsten/people/admin.py index 5410cc97c..78384332e 100644 --- a/trojsten/people/admin.py +++ b/trojsten/people/admin.py @@ -25,7 +25,7 @@ from .forms import MergeForm from .helpers import get_similar_users, merge_users from .models import (Address, DuplicateUser, User, UserProperty, - UserPropertyKey) + UserPropertyKey, UserSchool) class AddressAdmin(admin.ModelAdmin): @@ -38,6 +38,13 @@ class UserPropertyInLine(admin.TabularInline): extra = 0 +class UserSchoolInLine(admin.TabularInline): + model = UserSchool + form = select2_modelform(UserSchool) + ordering = ['start_time'] + extra = 1 + + class StaffFilter(admin.SimpleListFilter): title = 'postavenia' parameter_name = 'is_staff' @@ -95,6 +102,11 @@ def queryset(self, request, queryset): class UsersExport(resources.ModelResource): + school__verbose_name = fields.Field() + school__addr_name = fields.Field() + school__street = fields.Field() + school__city = fields.Field() + school__zip_code = fields.Field() street = fields.Field() town = fields.Field() postal_code = fields.Field() @@ -110,6 +122,21 @@ class Meta: ) widgets = {'birth_date': {'format': '%d.%m.%Y'}} + def dehydrate_school__verbose_name(self, obj): + return obj.school.verbose_name if obj.school else _('Other school') + + def dehydrate_school__addr_name(self, obj): + return obj.school.addr_name if obj.school else _('Other school') + + def dehydrate_school__street(self, obj): + return obj.school.street if obj.school else '' + + def dehydrate_school__city(self, obj): + return obj.school.city if obj.school else '' + + def dehydrate_school__zip_code(self, obj): + return obj.school.zip_code if obj.school else '' + def dehydrate_street(self, obj): address = obj.get_mailing_address() return '' if address is None else address.street @@ -131,7 +158,7 @@ class AdminUserAddForm(ModelForm): class Meta: model = User - fields = ['first_name', 'last_name', 'school', 'graduation'] + fields = ['first_name', 'last_name', 'graduation'] class UserAdmin(ExportMixin, DefaultUserAdmin): @@ -149,7 +176,7 @@ class UserAdmin(ExportMixin, DefaultUserAdmin): } add_fieldsets = ( - (None, {'fields': ('first_name', 'last_name', 'school', 'graduation')}), + (None, {'fields': ('first_name', 'last_name', 'graduation')}), ) fieldsets = ( @@ -158,7 +185,7 @@ class UserAdmin(ExportMixin, DefaultUserAdmin): 'first_name', 'last_name', 'email', 'gender', 'birth_date' )}), (_('Address'), {'fields': ('home_address', 'mail_to_school', 'mailing_address')}), - (_('School'), {'fields': ('school', 'graduation')}), + (_('School'), {'fields': ('graduation',)}), ) superuser_fieldsets = fieldsets + ( (_('Password'), {'fields': ('password',)}), @@ -168,13 +195,13 @@ class UserAdmin(ExportMixin, DefaultUserAdmin): (_('Important dates'), {'fields': ('last_login', 'date_joined')}), ) - inlines = (UserPropertyInLine,) + inlines = (UserSchoolInLine, UserPropertyInLine) resource_class = UsersExport def get_queryset(self, request): qs = super(UserAdmin, self).get_queryset(request) - return qs.select_related('school').prefetch_related( + return qs.prefetch_related( 'groups', 'properties__key' ) diff --git a/trojsten/people/forms.py b/trojsten/people/forms.py index a491b40a6..41380e7fe 100644 --- a/trojsten/people/forms.py +++ b/trojsten/people/forms.py @@ -14,6 +14,7 @@ from trojsten.contests.models import Competition, Round, Task from trojsten.people.models import Address, DuplicateUser, User, UserProperty +from trojsten.schools.models import School from trojsten.submit.constants import (SUBMIT_PAPER_FILEPATH, SUBMIT_STATUS_IN_QUEUE, SUBMIT_STATUS_REVIEWED, @@ -28,6 +29,17 @@ # TODO: reuse code from ksp_login class TrojstenUserBaseForm(forms.ModelForm): required_css_class = 'required' + school = forms.ModelChoiceField( + label='Škola', + help_text='Do políčka napíšte skratku, ' + 'časť názvu alebo adresy školy a následne ' + 'vyberte správnu možnosť zo zoznamu. ' + 'Pokiaľ vaša škola nie je ' + 'v zozname, vyberte "Iná škola" ' + 'a pošlite nám e-mail.', + queryset=School.objects.all(), + widget=forms.Select(attrs={'class': 'autocomplete'}), + ) street = forms.CharField(max_length=70, label=_('Street')) town = forms.CharField(max_length=64, label=_('Town')) postal_code = forms.CharField( @@ -57,10 +69,7 @@ class TrojstenUserBaseForm(forms.ModelForm): class Meta: model = User fields = ('first_name', 'last_name', 'email', - 'birth_date', 'gender', 'school', 'graduation',) - widgets = { - 'school': forms.Select(attrs={'class': 'autocomplete'}), - } + 'birth_date', 'gender', 'graduation',) def __init__(self, *args, **kwargs): super(TrojstenUserBaseForm, self).__init__(*args, **kwargs) @@ -72,7 +81,6 @@ def __init__(self, *args, **kwargs): label=_('Gender'), choices=User.GENDER_CHOICES, ) - self.fields['school'].initial = 1 def get_initial_from_pipeline(self, pipeline_state): return None if not pipeline_state else { @@ -186,6 +194,7 @@ def __init__(self, *args, **kwargs): kwargs['instance'] = self.user if self.user.home_address: kwargs['initial'] = { + 'school': user.school, 'street': user.home_address.street, 'town': user.home_address.town, 'postal_code': user.home_address.postal_code, @@ -258,6 +267,7 @@ def save(self, commit=True): corr_address.save() user.mailing_address = corr_address user.save() + user.add_school(self.cleaned_data.get('school')) if not DuplicateUser.objects.filter(user=user).exists(): similar_users = get_similar_users(user) if len(similar_users): @@ -277,10 +287,7 @@ class TrojstenUserCreationForm(TrojstenUserBaseForm): class Meta: model = User fields = ('username', 'first_name', 'last_name', 'email', - 'birth_date', 'gender', 'school', 'graduation',) - widgets = { - 'school': forms.Select(attrs={'class': 'autocomplete'}), - } + 'birth_date', 'gender', 'graduation',) def __init__(self, *args, **kwargs): try: @@ -365,6 +372,7 @@ def save(self, commit=True): corr_address.save() user.mailing_address = corr_address user.save() + user.add_school(self.cleaned_data.get('school')) similar_users = get_similar_users(user) if len(similar_users): DuplicateUser.objects.create(user=user) diff --git a/trojsten/people/migrations/0019_auto_20180608_2124.py b/trojsten/people/migrations/0019_auto_20180608_2124.py new file mode 100644 index 000000000..44b25a455 --- /dev/null +++ b/trojsten/people/migrations/0019_auto_20180608_2124.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.10 on 2018-06-08 19:24 +from __future__ import unicode_literals + +import django.contrib.auth.validators +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('people', '0018_auto_20180607_1158'), + ] + + operations = [ + migrations.RemoveField( + model_name='user', + name='school', + ), + migrations.AlterField( + model_name='userschool', + name='start_time', + field=models.DateField(default=django.utils.timezone.now, verbose_name='Start of study'), + ), + ] diff --git a/trojsten/people/models.py b/trojsten/people/models.py index d558586e2..072ab99c4 100644 --- a/trojsten/people/models.py +++ b/trojsten/people/models.py @@ -74,15 +74,6 @@ class User(AbstractUser): verbose_name='adresa korešpondencie') mail_to_school = models.BooleanField(default=False, verbose_name='posielať poštu do školy') - school = models.ForeignKey('schools.School', - null=True, - verbose_name='škola', - help_text='Do políčka napíšte skratku, ' - 'časť názvu alebo adresy školy a následne ' - 'vyberte správnu možnosť zo zoznamu. ' - 'Pokiaľ vaša škola nie je ' - 'v zozname, vyberte "Iná škola" ' - 'a pošlite nám e-mail.') graduation = models.IntegerField(null=True, verbose_name='rok maturity', help_text='Povinné pre žiakov.') @@ -111,12 +102,15 @@ class Meta: verbose_name_plural = 'používatelia' @property - def school_now(self): + def school(self): return UserSchool.objects.filter(user=self).order_by('-start_time').first().school def school_at(self, date): return UserSchool.objects.filter(user=self, start_time__lt=date).order_by('-start_time').first().school + def add_school(self, school, date=timezone.now()): + UserSchool.objects.create(user=self, school=school, start_time=date) + @property def school_year(self): return self.school_year_at( @@ -171,7 +165,7 @@ def format_name(num): class UserSchool(models.Model): user = models.ForeignKey(User, verbose_name=_('User')) school = models.ForeignKey(School, verbose_name=_('School')) - start_time = models.DateField(verbose_name=_('Start of study')) + start_time = models.DateField(verbose_name=_('Start of study'), default=timezone.now) class UserPropertyManager(models.Manager): From aca216bfbe484342d3d8c23e4ebe9376878c4a18 Mon Sep 17 00:00:00 2001 From: dodo42 Date: Tue, 12 Jun 2018 15:26:00 +0200 Subject: [PATCH 3/9] Changes across the code such as remove school from select_related. --- trojsten/events/views.py | 4 ++-- trojsten/people/forms.py | 3 ++- .../commands/migrate_people_from_kaspar.py | 2 +- trojsten/people/models.py | 3 ++- trojsten/people/tests.py | 5 ++++- trojsten/results/generator.py | 2 +- trojsten/results/helpers.py | 1 - trojsten/results/tests.py | 16 ++++++++-------- trojsten/rules/ksp.py | 2 +- trojsten/rules/old_ksp.py | 2 +- trojsten/submit/models.py | 2 +- 11 files changed, 23 insertions(+), 19 deletions(-) diff --git a/trojsten/events/views.py b/trojsten/events/views.py index 6df3da760..5e8f90078 100644 --- a/trojsten/events/views.py +++ b/trojsten/events/views.py @@ -19,12 +19,12 @@ class ParticipantsAndOrganizersListView(DetailView): def get_context_data(self, **kwargs): context = super(ParticipantsAndOrganizersListView, self).get_context_data(**kwargs) event = context['event'] - participants = event.participants.select_related('user__school') + participants = event.participants for participant in participants: participant.year_at_event = participant.user.school_year_at(event.start_time) context.update({ 'participants': participants, - 'organizers': event.organizers.select_related('user__school'), + 'organizers': event.organizers, }) return context diff --git a/trojsten/people/forms.py b/trojsten/people/forms.py index 41380e7fe..42e39e216 100644 --- a/trojsten/people/forms.py +++ b/trojsten/people/forms.py @@ -194,7 +194,6 @@ def __init__(self, *args, **kwargs): kwargs['instance'] = self.user if self.user.home_address: kwargs['initial'] = { - 'school': user.school, 'street': user.home_address.street, 'town': user.home_address.town, 'postal_code': user.home_address.postal_code, @@ -209,6 +208,8 @@ def __init__(self, *args, **kwargs): kwargs['initial'][ 'corr_country'] = user.mailing_address.country kwargs['initial']['has_correspondence_address'] = True + if self.user.school: + kwargs['initial']['school'] = user.school.pk super(TrojstenUserChangeForm, self).__init__(*args, **kwargs) diff --git a/trojsten/people/management/commands/migrate_people_from_kaspar.py b/trojsten/people/management/commands/migrate_people_from_kaspar.py index 4abeb5109..ef8c0c700 100644 --- a/trojsten/people/management/commands/migrate_people_from_kaspar.py +++ b/trojsten/people/management/commands/migrate_people_from_kaspar.py @@ -113,7 +113,6 @@ def process_person(self, man_id, first_name, last_name, school_id, # The username needs to be unique, thus the ID. 'username': '%s%s%d' % (first_name, last_name, man_id), 'is_active': False, - 'school': self.school_id_map[school_id] } if grad_year: @@ -140,6 +139,7 @@ def process_person(self, man_id, first_name, last_name, school_id, self.stdout.write("Creating user %s %s" % (first_name, last_name)) new_user = User.objects.create(**new_user_args) + new_user.add_school(self.school_id_map[school_id]) self.man_id_map[man_id] = new_user new_user.properties.create(key=self.kaspar_id_key, value=man_id) diff --git a/trojsten/people/models.py b/trojsten/people/models.py index 072ab99c4..3b8b915c5 100644 --- a/trojsten/people/models.py +++ b/trojsten/people/models.py @@ -103,7 +103,8 @@ class Meta: @property def school(self): - return UserSchool.objects.filter(user=self).order_by('-start_time').first().school + schools_queryset = UserSchool.objects.filter(user=self).order_by('-start_time') + return schools_queryset.first().school if len(schools_queryset) > 0 else None def school_at(self, date): return UserSchool.objects.filter(user=self, start_time__lt=date).order_by('-start_time').first().school diff --git a/trojsten/people/tests.py b/trojsten/people/tests.py index 82ef5200e..edc984b1b 100644 --- a/trojsten/people/tests.py +++ b/trojsten/people/tests.py @@ -206,9 +206,10 @@ def setUp(self): self.user = User.objects.create( username='janko4247', first_name='Janko', last_name='Hrasko', password='pass', gender='M', birth_date=datetime.date(1999, 9, 19), email='hrasko@example.com', - mail_to_school=True, school=self.school, graduation=2018, + mail_to_school=True, graduation=2018, home_address=address ) + self.user.add_school(self.school) self.form_data = { 'username': 'mrkva', 'password1': 'heslo', @@ -303,6 +304,8 @@ def test_user_creation_invalid_password(self): def test_valid_initial_data(self): form = TrojstenUserChangeForm({}, user=self.user) form.data = form.initial + form.is_valid() + print(form.errors) self.assertTrue(form.is_valid()) def test_valid_user_change(self): diff --git a/trojsten/results/generator.py b/trojsten/results/generator.py index 82526938e..e5ed05e9f 100644 --- a/trojsten/results/generator.py +++ b/trojsten/results/generator.py @@ -111,7 +111,7 @@ def get_submit_queryset(self, res_request): ).distinct( 'user', 'task', 'submit_type' ).select_related( - 'user', 'user__school', 'task' + 'user', 'task' ).prefetch_related('user__properties', 'user__ignored_competitions') def create_row(self, res_request, user, cols): diff --git a/trojsten/results/helpers.py b/trojsten/results/helpers.py index 1c53856fd..ff44fe7d7 100644 --- a/trojsten/results/helpers.py +++ b/trojsten/results/helpers.py @@ -187,7 +187,6 @@ def create_frozen_user(frozen_result): 'task_points__task', ).select_related( 'original_user', - 'school', ).order_by('rank') results = defaultdict(FrozenUserResult) diff --git a/trojsten/results/tests.py b/trojsten/results/tests.py index cdf0e8564..de1e3ef4c 100644 --- a/trojsten/results/tests.py +++ b/trojsten/results/tests.py @@ -280,8 +280,8 @@ def test_serialize_row_without_previous(self): school = School.objects.create(abbreviation='GJH', verbose_name='Gymnazium Jura Hronca') user = User.objects.create(username="TestUser", password="password", first_name="Jozko", last_name="Mrkvicka", - graduation=timezone.now().year + 2, - school=school) + graduation=timezone.now().year + 2) + user.add_school(school) school_dict = { 'id': school.id, @@ -315,8 +315,8 @@ def test_serialize_row_with_previous(self): school = School.objects.create(abbreviation='GJH', verbose_name='Gymnazium Jura Hronca') user = User.objects.create(username="TestUser", password="password", first_name="Jozko", last_name="Mrkvicka", - graduation=timezone.now().year + 2, - school=school) + graduation=timezone.now().year + 2) + user.add_school(school) school_dict = { 'id': school.id, @@ -369,8 +369,8 @@ def test_serialize_results(self): school = School.objects.create(abbreviation='GJH', verbose_name='Gymnazium Jura Hronca') user = User.objects.create(username="TestUser", password="password", first_name="Jozko", last_name="Mrkvicka", - graduation=timezone.now().year + 2, - school=school) + graduation=timezone.now().year + 2) + user.add_school(school) school_dict = { 'id': school.id, @@ -400,8 +400,8 @@ def test_serialize_results(self): user = User.objects.create(username="TestUser2", password="password", first_name="Ferko", last_name="Mrkvicka", - graduation=timezone.now().year + 3, - school=school) + graduation=timezone.now().year + 3) + user.add_school(school) user_dict = { 'id': user.id, diff --git a/trojsten/rules/ksp.py b/trojsten/rules/ksp.py index 1fa0d1c35..e75a40f95 100644 --- a/trojsten/rules/ksp.py +++ b/trojsten/rules/ksp.py @@ -60,7 +60,7 @@ def get_submit_queryset(self, res_request): 'user', 'task', '-time', '-id', ).distinct( 'user', 'task', - ).select_related('user', 'user__school', 'task', 'task__round') + ).select_related('user', 'task', 'task__round') return chain(submits, penalized_submits) def source_submit_points(self, previous_points, submit): diff --git a/trojsten/rules/old_ksp.py b/trojsten/rules/old_ksp.py index 9cb61819b..b8ca8c2c5 100644 --- a/trojsten/rules/old_ksp.py +++ b/trojsten/rules/old_ksp.py @@ -44,7 +44,7 @@ def get_submit_queryset(self, res_request): 'user', 'task', '-time', '-id', ).distinct( 'user', 'task', - ).select_related('user', 'user__school', 'task', 'task__round') + ).select_related('user', 'task', 'task__round') return chain(submits, penalized_submits) def source_submit_points(self, previous_points, submit): diff --git a/trojsten/submit/models.py b/trojsten/submit/models.py index 874ef94c0..55054cb9a 100644 --- a/trojsten/submit/models.py +++ b/trojsten/submit/models.py @@ -56,7 +56,7 @@ def for_tasks(self, tasks, include_staff=False): 'user', 'task', 'submit_type', '-time', '-id', ).distinct( 'user', 'task', 'submit_type' - ).select_related('user__school', 'task') + ).select_related('task') # @FIXME: This is used for rendering points for each task at task_list view. # Displaying scores for tasks should be implemented in results/rules. From e16e4675738b0dd9a8abccf9f9b1c5a45c0389b7 Mon Sep 17 00:00:00 2001 From: dodo42 Date: Wed, 13 Jun 2018 12:29:14 +0200 Subject: [PATCH 4/9] Allow users to edit their shool history in settings. --- trojsten/people/admin.py | 2 +- trojsten/people/forms.py | 11 ++- trojsten/people/models.py | 8 +- .../templates/trojsten/people/settings.html | 92 +++++++++++++++++-- trojsten/people/views.py | 25 ++++- 5 files changed, 121 insertions(+), 17 deletions(-) diff --git a/trojsten/people/admin.py b/trojsten/people/admin.py index 78384332e..3cefabfc5 100644 --- a/trojsten/people/admin.py +++ b/trojsten/people/admin.py @@ -42,7 +42,7 @@ class UserSchoolInLine(admin.TabularInline): model = UserSchool form = select2_modelform(UserSchool) ordering = ['start_time'] - extra = 1 + extra = 0 class StaffFilter(admin.SimpleListFilter): diff --git a/trojsten/people/forms.py b/trojsten/people/forms.py index 42e39e216..55685bdc0 100644 --- a/trojsten/people/forms.py +++ b/trojsten/people/forms.py @@ -13,7 +13,7 @@ from ksp_login.utils import get_partial_pipeline from trojsten.contests.models import Competition, Round, Task -from trojsten.people.models import Address, DuplicateUser, User, UserProperty +from trojsten.people.models import Address, DuplicateUser, User, UserProperty, UserSchool from trojsten.schools.models import School from trojsten.submit.constants import (SUBMIT_PAPER_FILEPATH, SUBMIT_STATUS_IN_QUEUE, @@ -519,6 +519,15 @@ def __init__(self, *args, **kwargs): ) +class UserSchoolFrom(forms.ModelForm): + class Meta: + model = UserSchool + exclude = () + widgets = { + 'school': forms.Select(attrs={'class': 'autocomplete form-control'}), + } + + class IgnoreCompetitionForm(forms.Form): def __init__(self, user, *args, **kwargs): diff --git a/trojsten/people/models.py b/trojsten/people/models.py index 3b8b915c5..c11b4d12a 100644 --- a/trojsten/people/models.py +++ b/trojsten/people/models.py @@ -110,7 +110,8 @@ def school_at(self, date): return UserSchool.objects.filter(user=self, start_time__lt=date).order_by('-start_time').first().school def add_school(self, school, date=timezone.now()): - UserSchool.objects.create(user=self, school=school, start_time=date) + if school != self.school: + UserSchool.objects.create(user=self, school=school, start_time=date) @property def school_year(self): @@ -168,6 +169,11 @@ class UserSchool(models.Model): school = models.ForeignKey(School, verbose_name=_('School')) start_time = models.DateField(verbose_name=_('Start of study'), default=timezone.now) + class Meta: + verbose_name = _('School of user') + verbose_name_plural = _('Schools of user') + ordering = ['start_time'] + class UserPropertyManager(models.Manager): def get_queryset(self): diff --git a/trojsten/people/templates/trojsten/people/settings.html b/trojsten/people/templates/trojsten/people/settings.html index d5d3d2ace..143859df2 100644 --- a/trojsten/people/templates/trojsten/people/settings.html +++ b/trojsten/people/templates/trojsten/people/settings.html @@ -19,6 +19,7 @@

{% trans "Account settings" %}