Merge pull request 'Reforma pohlaví' (!49) from reforma_pohlavi into master
Reviewed-on: #49
This commit is contained in:
		
						commit
						e06080ae31
					
				
					 10 changed files with 93 additions and 21 deletions
				
			
		|  | @ -504,7 +504,7 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | ||||||
| 
 | 
 | ||||||
| 		EmailMessage( | 		EmailMessage( | ||||||
| 			subject="Nové řešení k " + seznam_do_subjectu, | 			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í? | 			from_email="submitovatko@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? | ||||||
| 			to=list(prijemci), | 			to=list(prijemci), | ||||||
| 		).send() | 		).send() | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ class UdajeForm(forms.Form): | ||||||
| 	jmeno = forms.CharField(label='Jméno', max_length=256, required=True) | 	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) | 	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) | 	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) | 	email = forms.EmailField(label='E-mail', max_length=256, required=True) | ||||||
| 	telefon = forms.CharField(widget=TelInput(), label='Telefon', max_length=256, required=False) | 	telefon = forms.CharField(widget=TelInput(), label='Telefon', max_length=256, required=False) | ||||||
| 	datum_narozeni = forms.DateField(widget=DateInput(), label='Datum narození', required=False) | 	datum_narozeni = forms.DateField(widget=DateInput(), label='Datum narození', required=False) | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								personalni/migrations/0007_post_split_soustredeni.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								personalni/migrations/0007_post_split_soustredeni.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | # Generated by Django 4.2.11 on 2024-04-30 21:53 | ||||||
|  | 
 | ||||||
|  | from django.db import migrations | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  | 
 | ||||||
|  |     dependencies = [ | ||||||
|  |         ('personalni', '0006_pre_split_soustredeni'), | ||||||
|  |         ('soustredeni', '0003_post_split_soustredeni'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     operations = [ | ||||||
|  |     ] | ||||||
							
								
								
									
										40
									
								
								personalni/migrations/0008_reforma_pohlavi.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								personalni/migrations/0008_reforma_pohlavi.py
									
									
									
									
									
										Normal file
									
								
							|  | @ -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('personalni', '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('personalni', '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 = [ | ||||||
|  |         ('personalni', '0007_post_split_soustredeni'), | ||||||
|  |     ] | ||||||
|  | 
 | ||||||
|  |     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', | ||||||
|  |         ), | ||||||
|  |     ] | ||||||
|  | @ -38,8 +38,16 @@ class Osoba(SeminarModelBase): | ||||||
| 	user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,  | 	user = models.OneToOneField(settings.AUTH_USER_MODEL, blank=True, null=True,  | ||||||
| 				verbose_name='uživatel', on_delete=models.DO_NOTHING) | 				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í) | 	# Pohlaví nás prakticky nezajímá, reálně. | ||||||
| 	pohlavi_muz = models.BooleanField('pohlaví (muž)', default=False) | 	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='') | 	email = models.EmailField('e-mail', max_length=256, blank=True, default='') | ||||||
| 
 | 
 | ||||||
|  | @ -246,11 +254,19 @@ class Resitel(SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 	def export_row(self): | 	def export_row(self): | ||||||
| 		"Slovnik pro pouziti v AESOP exportu" | 		"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 { | 		return { | ||||||
| 			'id': self.id, | 			'id': self.id, | ||||||
| 			'name': self.osoba.jmeno, | 			'name': self.osoba.jmeno, | ||||||
| 			'surname': self.osoba.prijmeni, | 			'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 '', | 			'born': self.osoba.datum_narozeni.isoformat() if self.osoba.datum_narozeni else '', | ||||||
| 			'email': self.osoba.email, | 			'email': self.osoba.email, | ||||||
| 			'end-year': self.rok_maturity or '', | 			'end-year': self.rok_maturity or '', | ||||||
|  |  | ||||||
|  | @ -24,7 +24,7 @@ | ||||||
|   {% include "personalni/udaje/prihlaska_field.html" with field=form.jmeno %} |   {% 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.prezdivka_resitele %} | ||||||
|   {% include "personalni/udaje/prihlaska_field.html" with field=form.prijmeni %} |   {% 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.email %} | ||||||
|   {% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %} |   {% include "personalni/udaje/prihlaska_field.html" with field=form.telefon %} | ||||||
|   {% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %} |   {% include "personalni/udaje/prihlaska_field.html" with field=form.datum_narozeni %} | ||||||
|  |  | ||||||
|  | @ -139,7 +139,7 @@ def resitelEditView(request): | ||||||
| 			form_logger.info("EDIT:" + str(fcd) + str(form_hash))  # TODO možná logovat jinak | 			form_logger.info("EDIT:" + str(fcd) + str(form_hash))  # TODO možná logovat jinak | ||||||
| 			osoba_edit.jmeno = fcd['jmeno'] | 			osoba_edit.jmeno = fcd['jmeno'] | ||||||
| 			osoba_edit.prijmeni = fcd['prijmeni'] | 			osoba_edit.prijmeni = fcd['prijmeni'] | ||||||
| 			osoba_edit.pohlavi_muz = fcd['pohlavi_muz'] | 			osoba_edit.osloveni = fcd['osloveni'] | ||||||
| 			osoba_edit.email = fcd['email'] | 			osoba_edit.email = fcd['email'] | ||||||
| 			osoba_edit.telefon = fcd['telefon'] | 			osoba_edit.telefon = fcd['telefon'] | ||||||
| 			osoba_edit.ulice = fcd['ulice'] | 			osoba_edit.ulice = fcd['ulice'] | ||||||
|  | @ -209,7 +209,7 @@ def prihlaskaView(request): | ||||||
| 				o = s.Osoba( | 				o = s.Osoba( | ||||||
| 					jmeno = fcd['jmeno'], | 					jmeno = fcd['jmeno'], | ||||||
| 					prijmeni = fcd['prijmeni'], | 					prijmeni = fcd['prijmeni'], | ||||||
| 					pohlavi_muz = fcd['pohlavi_muz'], | 					osloveni = fcd['osloveni'], | ||||||
| 					email = fcd['email'], | 					email = fcd['email'], | ||||||
| 					telefon = fcd.get('telefon',''), | 					telefon = fcd.get('telefon',''), | ||||||
| 					datum_narozeni = fcd.get('datum_narozeni',None), | 					datum_narozeni = fcd.get('datum_narozeni',None), | ||||||
|  | @ -242,7 +242,7 @@ def prihlaskaView(request): | ||||||
| 
 | 
 | ||||||
| 				# Porovnání údajů | 				# Porovnání údajů | ||||||
| 				assert orig_osoba.user is None, "Právě-registrující-se osoba už má Uživatele!" | 				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 = [] | 				diffattrs = [] | ||||||
| 				for attr in osoba_attrs: | 				for attr in osoba_attrs: | ||||||
| 					new = getattr(o, attr) | 					new = getattr(o, attr) | ||||||
|  | @ -339,7 +339,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): | ||||||
| 		'osoba__telefon', | 		'osoba__telefon', | ||||||
| 		'osoba__user__username', | 		'osoba__user__username', | ||||||
| 		'osoba__datum_narozeni', | 		'osoba__datum_narozeni', | ||||||
| 		'osoba__pohlavi_muz', | 		'osoba__osloveni', | ||||||
| 		'osoba__ulice', | 		'osoba__ulice', | ||||||
| 		'osoba__mesto', | 		'osoba__mesto', | ||||||
| 		'osoba__psc', | 		'osoba__psc', | ||||||
|  | @ -367,7 +367,7 @@ def dataResiteluCsvResponse(queryset, columns=None, with_header=True): | ||||||
| 		'osoba__telefon':                'telefon', | 		'osoba__telefon':                'telefon', | ||||||
| 		'osoba__user__username':         'user', | 		'osoba__user__username':         'user', | ||||||
| 		'osoba__datum_narozeni':         'datum_narozeni', | 		'osoba__datum_narozeni':         'datum_narozeni', | ||||||
| 		'osoba__pohlavi_muz':            'pohlavi_muz', | 		'osoba__osloveni':               'osloveni', | ||||||
| 		'osoba__ulice':                  'ulice', | 		'osoba__ulice':                  'ulice', | ||||||
| 		'osoba__mesto':                  'mesto', | 		'osoba__mesto':                  'mesto', | ||||||
| 		'osoba__psc':                    'psc', | 		'osoba__psc':                    'psc', | ||||||
|  |  | ||||||
|  | @ -58,17 +58,19 @@ def gen_osoby(rnd, size): | ||||||
| 	# 30 je náhodná konstanta, size je použité na víc místech a | 	# 30 je náhodná konstanta, size je použité na víc místech a | ||||||
| 	# říká, jak velká asi chceme testovací data | 	# říká, jak velká asi chceme testovací data | ||||||
| 	for i in range(30 * size): | 	for i in range(30 * size): | ||||||
| 		pohlavi = rnd.randint(0,1) | 		pohlavi_idx = rnd.randint(0,2) # 2 = nebinární | ||||||
| 		jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) | 		osloveni = [Osoba.OSLOVENI_MUZSKE, Osoba.OSLOVENI_ZENSKE, Osoba.OSLOVENI_ZADNE][pohlavi_idx] | ||||||
| 		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]) | ||||||
|  | 		if pohlavi_idx == 2: logger.debug(f'Testdata: nebinární osoba: {jmeno} {prijmeni}.') | ||||||
| 		pokusy = 0 | 		pokusy = 0 | ||||||
| 		max_pokusy = 120*size | 		max_pokusy = 120*size | ||||||
| 		while (not __unikatni_jmeno and pokusy < max_pokusy): | 		while (not __unikatni_jmeno and pokusy < max_pokusy): | ||||||
| 		# pokud jméno a příjmení není unikátní, zkoušíme generovat nová | 		# 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 | 		# 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) | 		# ze kterých se generuje) | ||||||
| 			jmeno = rnd.choice([jmena_m, jmena_f][pohlavi]) | 			jmeno = rnd.choice([jmena_m, jmena_f, jmena_m + jmena_f][pohlavi_idx]) | ||||||
| 			prijmeni = rnd.choice([prijmeni_m, prijmeni_f][pohlavi]) | 			prijmeni = rnd.choice([prijmeni_m, prijmeni_f, prijmeni_m + prijmeni_f][pohlavi_idx]) | ||||||
| 			pokusy = pokusy + 1 | 			pokusy = pokusy + 1 | ||||||
| 		if pokusy >= max_pokusy: | 		if pokusy >= max_pokusy: | ||||||
| 			print("Chyba, na danou velikost testovacích dat příliš málo možných" | 			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)]) | 		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, | 		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, | 				telefon = telefon, datum_narozeni = narozeni, ulice = ulice, | ||||||
| 				mesto = mesto, psc = psc, | 				mesto = mesto, psc = psc, | ||||||
| 				datum_registrace = datetime.date(rnd.randint(2019, 2029), | 				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') | 	admin = User.objects.create_superuser(username='admin', email='', password='admin') | ||||||
| 	os_admin = Osoba.objects.create( | 	os_admin = Osoba.objects.create( | ||||||
| 		user=admin, jmeno='admin', prijmeni='admin', | 		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), | 		telefon='123 456 789', datum_narozeni=datetime.date(2000, 1, 1), | ||||||
| 		ulice='admin', mesto='admin', psc='100 00', | 		ulice='admin', mesto='admin', psc='100 00', | ||||||
| 		datum_registrace=datetime.date(2020, 9, 6) | 		datum_registrace=datetime.date(2020, 9, 6) | ||||||
|  |  | ||||||
|  | @ -337,7 +337,7 @@ def merge_osoby(cilova, zdrojova): | ||||||
| 	# ID, User neřešíme, poznámku vyřešíme separátně. | 	# ID, User neřešíme, poznámku vyřešíme separátně. | ||||||
| 	fieldy = ['datum_narozeni', 'datum_registrace', 'datum_souhlasu_udaje', | 	fieldy = ['datum_narozeni', 'datum_registrace', 'datum_souhlasu_udaje', | ||||||
| 			'datum_souhlasu_zasilani', 'email', 'foto', 'jmeno', 'mesto', | 			'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: | 	for f in fieldy: | ||||||
| 		zf = getattr(zdrojova, f) | 		zf = getattr(zdrojova, f) | ||||||
| 		cf = getattr(cilova, f) | 		cf = getattr(cilova, f) | ||||||
|  |  | ||||||
|  | @ -674,8 +674,8 @@ class ClankyResitelView(generic.ListView): | ||||||
| def StavDatabazeView(request): | def StavDatabazeView(request): | ||||||
| #	nastaveni = Nastaveni.objects.get() | #	nastaveni = Nastaveni.objects.get() | ||||||
| 	problemy = utils.seznam_problemu() | 	problemy = utils.seznam_problemu() | ||||||
| 	muzi = Resitel.objects.filter(osoba__pohlavi_muz=True) | 	muzi = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_MUZSKE) | ||||||
| 	zeny = Resitel.objects.filter(osoba__pohlavi_muz=False) | 	zeny = Resitel.objects.filter(osoba__osloveni=m.Osoba.OSLOVENI_ZENSKE) | ||||||
| 	return render(request, 'seminar/stav_databaze.html', | 	return render(request, 'seminar/stav_databaze.html', | ||||||
| 			{ | 			{ | ||||||
| #				'nastaveni': nastaveni, | #				'nastaveni': nastaveni, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue