diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 41af1dcb..e5de47c2 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -504,7 +504,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): EmailMessage( subject="Nové řešení k " + seznam_do_subjectu, - body=f"Řešitel{ '' if resitel.pohlavi_muz else 'ka' } { resitel } právě nahrál{'' if resitel.pohlavi_muz else 'a' } nové řešení k { seznam }.\n\nHurá do opravování: { self.object.absolute_url() }", + body=f"{resitel} posílá nové řešení k { seznam }.\n\nHurá do opravování: { self.object.absolute_url() }", from_email="submitovatko@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? to=list(prijemci), ).send() diff --git a/personalni/forms.py b/personalni/forms.py index 3199a8a2..ea0891e7 100644 --- a/personalni/forms.py +++ b/personalni/forms.py @@ -32,7 +32,7 @@ class UdajeForm(forms.Form): jmeno = forms.CharField(label='Jméno', max_length=256, required=True) prezdivka_resitele = forms.CharField(label='Přezdívka (veřejná)', max_length=256, required=False) prijmeni = forms.CharField(label='Příjmení', max_length=256, required=True) - pohlavi_muz = forms.ChoiceField(label='Pohlaví', choices=((True, 'muž'), (False, 'žena')), required=True) + osloveni = forms.ChoiceField(label='Oslovení', choices=Osoba.OSLOVENI_CHOICES, required=False) email = forms.EmailField(label='E-mail', max_length=256, required=True) telefon = forms.CharField(widget=TelInput(), label='Telefon', max_length=256, required=False) datum_narozeni = forms.DateField(widget=DateInput(), label='Datum narození', required=False) diff --git a/personalni/templates/personalni/udaje/udaje.html b/personalni/templates/personalni/udaje/udaje.html index f39e8b47..894ddaf9 100644 --- a/personalni/templates/personalni/udaje/udaje.html +++ b/personalni/templates/personalni/udaje/udaje.html @@ -24,7 +24,7 @@ {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} {% include "personalni/udaje/prihlaska_field.html" with field=form.prezdivka_resitele %} {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} - {% include "personalni/udaje/prihlaska_field.html" with field=form.pohlavi_muz%} + {% include "personalni/udaje/prihlaska_field.html" with field=form.osloveni%} {% include "personalni/udaje/prihlaska_field.html" with field=form.email %} {% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %} {% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %} diff --git a/personalni/views.py b/personalni/views.py index 876cc7ec..5e5ad22c 100644 --- a/personalni/views.py +++ b/personalni/views.py @@ -139,7 +139,7 @@ def resitelEditView(request): form_logger.info("EDIT:" + str(fcd) + str(form_hash)) # TODO možná logovat jinak osoba_edit.jmeno = fcd['jmeno'] osoba_edit.prijmeni = fcd['prijmeni'] - osoba_edit.pohlavi_muz = fcd['pohlavi_muz'] + osoba_edit.osloveni = fcd['osloveni'] osoba_edit.email = fcd['email'] osoba_edit.telefon = fcd['telefon'] osoba_edit.ulice = fcd['ulice'] @@ -209,7 +209,7 @@ def prihlaskaView(request): o = s.Osoba( jmeno = fcd['jmeno'], prijmeni = fcd['prijmeni'], - pohlavi_muz = fcd['pohlavi_muz'], + osloveni = fcd['osloveni'], email = fcd['email'], telefon = fcd.get('telefon',''), datum_narozeni = fcd.get('datum_narozeni',None), @@ -242,7 +242,7 @@ def prihlaskaView(request): # Porovnání údajů assert orig_osoba.user is None, "Právě-registrující-se osoba už má Uživatele!" - osoba_attrs = ['jmeno', 'prijmeni', 'pohlavi_muz', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'stat', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_registrace'] + osoba_attrs = ['jmeno', 'prijmeni', 'osloveni', 'email', 'telefon', 'datum_narozeni', 'ulice', 'mesto', 'psc', 'stat', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'datum_registrace'] diffattrs = [] for attr in osoba_attrs: new = getattr(o, attr) @@ -339,7 +339,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): 'osoba__telefon', 'osoba__user__username', 'osoba__datum_narozeni', - 'osoba__pohlavi_muz', + 'osoba__osloveni', 'osoba__ulice', 'osoba__mesto', 'osoba__psc', @@ -367,7 +367,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): 'osoba__telefon': 'telefon', 'osoba__user__username': 'user', 'osoba__datum_narozeni': 'datum_narozeni', - 'osoba__pohlavi_muz': 'pohlavi_muz', + 'osoba__osloveni': 'osloveni', 'osoba__ulice': 'ulice', 'osoba__mesto': 'mesto', 'osoba__psc': 'psc', diff --git a/seminar/migrations/0115_reforma_pohlavi.py b/seminar/migrations/0115_reforma_pohlavi.py new file mode 100644 index 00000000..3a0f72fd --- /dev/null +++ b/seminar/migrations/0115_reforma_pohlavi.py @@ -0,0 +1,40 @@ +# Generated by Django 4.2.11 on 2024-04-12 14:03 + +from django.db import migrations, models + +# V migracích nemáme Osoba.OSLOVENI_*, tak si to sem nakopíruji. +OSLOVENI_MUZSKE = 'resitel' +OSLOVENI_ZENSKE = 'resitelka' +OSLOVENI_ZADNE = '' + +def pohlavi_to_osloveni(apps, schema_editor): + Osoba = apps.get_model('seminar', 'Osoba') + Osoba.objects.filter(pohlavi_muz=True).update(osloveni=OSLOVENI_MUZSKE) + Osoba.objects.filter(pohlavi_muz=False).update(osloveni=OSLOVENI_ZENSKE) + +def osloveni_to_pohlavi(apps, schema_editor): + Osoba = apps.get_model('seminar', 'Osoba') + nebinarni = Osoba.objects.filter(osloveni=OSLOVENI_ZADNE) + if nebinarni.count() > 0: + raise Exception("Nelze odmigrovat: v databázi jsou nebinární osoby, které starý model nereprezentuje správně.") + Osoba.objects.filter(osloveni=OSLOVENI_MUZSKE).update(pohlavi_muz=True) + Osoba.objects.filter(osloveni=OSLOVENI_MUZSKE).update(pohlavi_muz=False) + +class Migration(migrations.Migration): + + dependencies = [ + ('seminar', '0114_related_name_se_zmenilo_a_django_chce_migraci_tak_dostane_migraci'), + ] + + operations = [ + migrations.AddField( + model_name='osoba', + name='osloveni', + field=models.CharField(blank=True, choices=[('resitel', 'Řešitel'), ('resitelka', 'Řešitelka')], max_length=32, verbose_name='Oslovení'), + ), + migrations.RunPython(pohlavi_to_osloveni, osloveni_to_pohlavi), + migrations.RemoveField( + model_name='osoba', + name='pohlavi_muz', + ), + ] diff --git a/seminar/models/personalni.py b/seminar/models/personalni.py index 61313e87..f93e35b0 100644 --- a/seminar/models/personalni.py +++ b/seminar/models/personalni.py @@ -38,8 +38,16 @@ class Osoba(SeminarModelBase): user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True, verbose_name='uživatel', on_delete=models.DO_NOTHING) - # Pohlaví. Že ho neznáme se snad nestane (a ušetří to práci při programování) - pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False) + # Pohlaví nás prakticky nezajímá, reálně. + OSLOVENI_MUZSKE = 'resitel' + OSLOVENI_ZENSKE = 'resitelka' + OSLOVENI_ZADNE = '' + OSLOVENI_CHOICES = [ + (OSLOVENI_MUZSKE, 'Řešitel'), + (OSLOVENI_ZENSKE, 'Řešitelka'), + (OSLOVENI_ZADNE, 'Cokoliv jiného'), # Reálně nás u nikoho jiného oslovení nezajímá? (A pohlaví už vůbec) + ] + osloveni = models.CharField('Oslovení', choices=OSLOVENI_CHOICES, max_length=32, blank=True) email = models.EmailField('e-mail', max_length=256, blank=True, default='') @@ -246,11 +254,19 @@ class Resitel(SeminarModelBase): def export_row(self): "Slovnik pro pouziti v AESOP exportu" + # Ref: https://opmk.mff.cuni.cz/wiki/aesop/import#telo + + # FUJ: Oslovení nemusí souviset s genderem. + gender = { + Osoba.OSLOVENI_MUZSKE: 'M', + Osoba.OSLOVENI_ZENSKE: 'F', + Osoba.OSLOVENI_ZADNE: '', + }[self.osoba.osloveni] return { 'id': self.id, 'name': self.osoba.jmeno, 'surname': self.osoba.prijmeni, - 'gender': 'M' if self.osoba.pohlavi_muz else 'F', + 'gender': gender, 'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '', 'email': self.osoba.email, 'end-year': self.rok_maturity or '', diff --git a/seminar/testutils.py b/seminar/testutils.py index c3d64f56..0d90456a 100644 --- a/seminar/testutils.py +++ b/seminar/testutils.py @@ -58,17 +58,19 @@ def gen_osoby(rnd, size): # 30 je náhodná konstanta, size je použité na víc místech a # říká, jak velká asi chceme testovací data for i in range(30 * size): - pohlavi = rnd.randint(0,1) - jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) - prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) + pohlavi_idx = rnd.randint(0,2) # 2 = nebinární + osloveni = [Osoba.OSLOVENI_MUZSKE, Osoba.OSLOVENI_ZENSKE, Osoba.OSLOVENI_ZADNE][pohlavi_idx] + jmeno = rnd.choice([jmena_m, jmena_f, jmena_m + jmena_f][pohlavi_idx]) + prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) + if pohlavi_idx == 2: logger.debug(f'Testdata: nebinární osoba: {jmeno} {prijmeni}.') pokusy = 0 max_pokusy = 120*size while (not __unikatni_jmeno and pokusy < max_pokusy): # pokud jméno a příjmení není unikátní, zkoušíme generovat nová # do daného limitu (abychom se nezacyklili do nekonečna při málo jménech a příjmeních # ze kterých se generuje) - jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) - prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) + jmeno = rnd.choice([jmena_m, jmena_f, jmena_m + jmena_f][pohlavi_idx]) + prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) pokusy = pokusy + 1 if pokusy >= max_pokusy: print("Chyba, na danou velikost testovacích dat příliš málo možných" @@ -86,7 +88,7 @@ def gen_osoby(rnd, size): psc = "".join([str(rnd.choice([k for k in range(10)])) for i in range(5)]) osoby.append(Osoba.objects.create(jmeno = jmeno, prijmeni = prijmeni, - prezdivka = prezdivka, pohlavi_muz = pohlavi, email = email, + prezdivka = prezdivka, osloveni = osloveni, email = email, telefon = telefon, datum_narozeni = narozeni, ulice = ulice, mesto = mesto, psc = psc, datum_registrace = datetime.date(rnd.randint(2019, 2029), @@ -818,7 +820,7 @@ def create_test_data(size = 6, rnd = None): admin = User.objects.create_superuser(username='admin', email='', password='admin') os_admin = Osoba.objects.create( user=admin, jmeno='admin', prijmeni='admin', - prezdivka='admin', pohlavi_muz=1, email='admin@admin.admin', + prezdivka='admin', osloveni='', email='admin@admin.admin', telefon='123 456 789', datum_narozeni=datetime.date(2000, 1, 1), ulice='admin', mesto='admin', psc='100 00', datum_registrace=datetime.date(2020, 9, 6) diff --git a/seminar/utils.py b/seminar/utils.py index 891f8c15..ddce769c 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -337,7 +337,7 @@ def merge_osoby(cilova, zdrojova): # ID, User neřešíme, poznámku vyřešíme separátně. fieldy = ['datum_narozeni', 'datum_registrace', 'datum_souhlasu_udaje', 'datum_souhlasu_zasilani', 'email', 'foto', 'jmeno', 'mesto', - 'pohlavi_muz', 'prezdivka', 'prijmeni', 'psc', 'stat', 'telefon', 'ulice'] + 'osloveni', 'prezdivka', 'prijmeni', 'psc', 'stat', 'telefon', 'ulice'] for f in fieldy: zf = getattr(zdrojova, f) cf = getattr(cilova, f) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 662c5025..31cfbe17 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -674,8 +674,8 @@ class ClankyResitelView(generic.ListView): def StavDatabazeView(request): # nastaveni = Nastaveni.objects.get() problemy = utils.seznam_problemu() - muzi = Resitel.objects.filter(osoba__pohlavi_muz=True) - zeny = Resitel.objects.filter(osoba__pohlavi_muz=False) + muzi = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_MUZSKE) + zeny = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_ZENSKE) return render(request, 'seminar/stav_databaze.html', { # 'nastaveni': nastaveni,