From 5fcf9bac15210a74b16b18ec9b2c94119962850d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=C3=A1=C5=A1=20Havelka?= Date: Thu, 10 Aug 2023 13:20:18 +0200 Subject: [PATCH] =?UTF-8?q?Vypreparov=C3=A1n=C3=AD=20person=C3=A1ln=C3=ADh?= =?UTF-8?q?o=20ze=20seminar.utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/tests/test_skola_autocomplete.py | 2 +- api/urls.py | 2 +- galerie/urls.py | 2 +- korektury/urls.py | 2 +- odevzdavatko/urls.py | 3 +- odevzdavatko/views.py | 2 +- personalni/urls.py | 2 +- personalni/utils.py | 233 +++++++++++++++++++++++++- prednasky/urls.py | 2 +- seminar/urls.py | 2 +- seminar/utils.py | 236 +-------------------------- seminar/views/views_all.py | 2 +- soustredeni/urls.py | 2 +- tvorba/models.py | 2 +- vyroci/urls.py | 2 +- vysledkovky/utils.py | 2 +- 16 files changed, 249 insertions(+), 249 deletions(-) diff --git a/api/tests/test_skola_autocomplete.py b/api/tests/test_skola_autocomplete.py index c552ad00..12cb878d 100644 --- a/api/tests/test_skola_autocomplete.py +++ b/api/tests/test_skola_autocomplete.py @@ -2,7 +2,7 @@ from django.test import TestCase from django.urls import reverse from personalni.models import Skola import seminar.views as v -from seminar.utils import sync_skoly +from personalni.utils import sync_skoly class OrgSkolyAutocompleteTestCase(TestCase): @classmethod diff --git a/api/urls.py b/api/urls.py index 9ff38424..be58d3f9 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,6 +1,6 @@ from django.urls import path from . import views -from seminar.utils import org_required +from personalni.utils import org_required urlpatterns = [ # Export škol diff --git a/galerie/urls.py b/galerie/urls.py index 32824248..28b43a22 100644 --- a/galerie/urls.py +++ b/galerie/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from seminar.utils import org_required +from personalni.utils import org_required from . import views urlpatterns = [ diff --git a/korektury/urls.py b/korektury/urls.py index dcd1d965..cf45ea8f 100644 --- a/korektury/urls.py +++ b/korektury/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from seminar.utils import org_required +from personalni.utils import org_required from . import views urlpatterns = [ diff --git a/odevzdavatko/urls.py b/odevzdavatko/urls.py index e41b9c14..d1ac91fa 100644 --- a/odevzdavatko/urls.py +++ b/odevzdavatko/urls.py @@ -1,7 +1,8 @@ from django.urls import path -from seminar.utils import org_required, resitel_required, viewMethodSwitch, \ +from personalni.utils import org_required, resitel_required, \ resitel_or_org_required +from seminar.utils import viewMethodSwitch from . import views urlpatterns = [ diff --git a/odevzdavatko/views.py b/odevzdavatko/views.py index 27a060fe..8b454e6c 100644 --- a/odevzdavatko/views.py +++ b/odevzdavatko/views.py @@ -22,7 +22,7 @@ from seminar.models.nastaveni import Nastaveni from personalni.models import Resitel, Organizator, Osoba from . import forms as f from .forms import OdevzdavatkoTabulkaFiltrForm as FiltrForm -from seminar.utils import resi_v_rocniku +from personalni.utils import resi_v_rocniku from seminar.views import formularOKView logger = logging.getLogger(__name__) diff --git a/personalni/urls.py b/personalni/urls.py index 73a6f720..d0f642a8 100644 --- a/personalni/urls.py +++ b/personalni/urls.py @@ -1,7 +1,7 @@ from django.urls import path from django.contrib.auth.decorators import login_required from . import views -from seminar.utils import org_required +from personalni.utils import org_required urlpatterns = [ path( diff --git a/personalni/utils.py b/personalni/utils.py index 248f539a..ca17c6ee 100644 --- a/personalni/utils.py +++ b/personalni/utils.py @@ -1,6 +1,18 @@ +import re + +from django.contrib.auth.models import AnonymousUser +from django.contrib.auth import get_user_model +from django.contrib.auth.decorators import permission_required, \ + user_passes_test +from django.core.exceptions import ObjectDoesNotExist +from django.db import transaction + +from tvorba.models import Rocnik from .models import * +from soustredeni.models import Konfery_Ucastnici, Soustredeni_Ucastnici +from odevzdavatko.models import Reseni_Resitele from various.utils import bez_diakritiky_translate -import re + def normalizuj_jmeno(o: Osoba) -> str: # FIXME: Možná není potřeba vázat na model? @@ -9,3 +21,222 @@ def normalizuj_jmeno(o: Osoba) -> str: cele_jmeno = re.sub(r'[^a-zA-Z- ]', '', cele_jmeno) return cele_jmeno +def resi_v_rocniku(rocnik, cislo=None): + """ Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla. + Parametry: + rocnik (typu Rocnik) ročník, ze kterého chci řešitele, co něco odevzdali + cislo (typu Cislo) číslo, do kterého včetně se počítá, že v daném + ročníku řešitel něco poslal. + Pokud není zadané, počítají se všechna řešení z daného ročníku. + Výstup: + QuerySet objektů typu Resitel """ + + if cislo is None: + # filtrujeme pouze podle ročníku + return Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), + reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik).distinct() + else: # filtrujeme podle ročníku i čísla + return Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), + reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik, + reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi).distinct() + + +def aktivniResitele(cislo, pouze_letosni=False): + """ Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali + a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla). + Parametry: + cislo (typu Cislo) číslo, o které se jedná + pouze_letosni jen řešitelé, kteří tento rok něco poslali + + """ + letos = cislo.rocnik + + # detekujeme, zda jde o první tři čísla či nikoli (tj. zda spamovat řešitele z minulého roku) + zacatek_rocniku = True + try: + if int(cislo.poradi) > 3: + zacatek_rocniku = False + except ValueError: + # if cislo.poradi != '7-8': + # raise ValueError(f'{cislo} je neplatné číslo čísla (není int a není 7-8)') + zacatek_rocniku = False + + # nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali + if pouze_letosni: + zacatek_rocniku = False + + try: + loni = Rocnik.objects.get(rocnik=letos.rocnik - 1) + except ObjectDoesNotExist: + # Pro první ročník neexistuje ročník předchozí + zacatek_rocniku = False + + if not zacatek_rocniku: + return resi_v_rocniku(letos, cislo).filter(rok_maturity__gte=letos.druhy_rok()) + else: + # spojíme querysety s řešiteli loni a letos do daného čísla + return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct().filter(rok_maturity__gte=letos.druhy_rok()) + + +org_required = permission_required('auth.org') +resitel_required = permission_required('auth.resitel') + + +# inspirováno django.contrib.auth.decorators permission_required +def check_perms(user): + if user.has_perms(('auth.resitel',)): + return True + if user.has_perms(('auth.org',)): + return True + return False + + +resitel_or_org_required = user_passes_test(check_perms) + +User = get_user_model() +# Není to úplně hezké, ale budeme doufat, že to je funkční... +User.je_org = property(lambda self: self.has_perm('auth.org')) +User.je_resitel = property(lambda self: self.has_perm('auth.resitel')) +AnonymousUser.je_org = False +AnonymousUser.je_resitel = False + + +def sync_skoly(base_url): + """Stáhne všechny školy z mamwebu na adrese a uloží je do databáze""" + from django.urls import reverse + full_url = base_url.rstrip('/') + reverse('export_skoly') + import requests + from django.core import serializers + json = requests.get(full_url, stream=True).content + for skola in serializers.deserialize('json', json): + skola.save() + +@transaction.atomic +def merge_resitele(cilovy, zdrojovy): + """Spojí dva řešitelské objekty do cílového. + + Pojmenování "zdrojový" je silně nepřiléhající, ale co už…""" + + # Postup: + # Sjednotit / upravit informace cílového řešitele + print('Upravuji data modelu') + fieldy_shoda = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] + + for f in fieldy_shoda: + zf = getattr(zdrojovy, f) + cf = getattr(cilovy, f) + if cf == zf: + print(f' Údaj {f} je shodný ({zf})') + else: + if zf is None: + print(f' Údaj {f} je pouze v cílovém, používám') + continue + if cf is None: + setattr(cilovy, f, zf) + cilovy.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojového: {zf}' + print(f" Přiřazuji {f} ze zdrojového: {zf}") + continue + # Jsou fakt různé… + # FIXME: chybí možnost na vlastní úpravu… + verdikt = input(f"\n\n Údaj {f} se u řešitele {cilovy} ({cilovy.id}) liší:\n Zdrojový: {zf}\n Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") + verdikt = verdikt[0].casefold() + if verdikt == 'z': + setattr(cilovy, f, zf) + cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojový), nepoužit {cf} (cílový)' + elif verdikt == 'c': + cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílový), nepoužit {zf} (zdrojový)' + else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') + # poznámku chceme nezahodit… + cilovy.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojovy.poznamka}' + print(f' Výsledný řešitel: {cilovy.__dict__}, ukládám') + cilovy.save() + + + # Přepojit všechny vazby ze zdrojového na cílového + print('Přepojuji vazby') + # Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit) + ct = Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) + print(f' Přepojeno {ct} řešení') + ct = Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) + print(f' Přepojeno {ct} konfer') + ct = Soustredeni_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) + print(f' Přepojeno {ct} sousů') + + # Teď by na zdrojovém řešiteli nemělo nic viset, smazat ho, pamatujíce si jeho Osobu + zdrosoba = zdrojovy.osoba + print(f'Mažu zdrojového řešitele {zdrojovy.__dict__}') + zdrojovy.delete() + # Spojit osoby (separátní funkce). + merge_osoby(cilovy.osoba, zdrosoba) + + input("Potvrdit transakci řešitelů (^C pro zrušení) ") + +@transaction.atomic +def merge_osoby(cilova, zdrojova): + """ Spojí dvě osoby do cílové + + Nehlídá omezení typu "max 1 řešitel na osobu", to by měla hlídat databáze (OneToOneField).""" + # Sjednocení dat + print('Sjednocuji data osob') + # 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'] + for f in fieldy: + zf = getattr(zdrojova, f) + cf = getattr(cilova, f) + if cf == zf: + print(f' Údaj {f} je shodný ({zf})') + else: + if zf is None: + print(f' Údaj {f} je pouze v cílové, používám') + continue + if cf is None: + setattr(cilova, f, zf) + cilova.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojové: {zf}' + print(f" Přiřazuji {f} ze zdrojové: {zf}") + continue + # Jsou fakt různé… + # FIXME: chybí možnost na vlastní úpravu… + verdikt = input(f"\n\n Údaj {f} se u osoby {cilova} ({cilova.id}) liší:\n Zdrojový: {zf}\n Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") + verdikt = verdikt[0].casefold() + if verdikt == 'z': + setattr(cilova, f, zf) + cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojová), nepoužit {cf} (cílová)' + elif verdikt == 'c': + cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílová), nepoužit {zf} (zdrojová)' + else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') + # poznámku chceme nezahodit… + cilova.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojova.poznamka}' + print(f' Výsledná osoba: {cilova.__dict__}, ukládám') + cilova.save() + + # Vazby: Řešitel, User, Příjemce, Organizátor, Škola.kontaktní_osoba + print('Přepojuji vazby') + ct = Skola.objects.filter(kontaktni_osoba=zdrojova).update(kontaktni_osoba=cilova) + print(f' Přepojeno {ct} kontaktních osob') + # Ostatní vazby vyřeší OneToOneFieldy, ale někdy nemusí existovat… + ct = Resitel.objects.filter(osoba=zdrojova).update(osoba=cilova) + print(f' Přepojeno {ct} řešitelů') + ct = Prijemce.objects.filter(osoba=zdrojova).update(osoba=cilova) + print(f' Přepojeno {ct} příjemců') + ct = Organizator.objects.filter(osoba=zdrojova).update(osoba=cilova) + print(f' Přepojeno {ct} organizátorů') + # Uživatelé vedou opačným směrem, radši chceme zkontrolovat, že jsou různí ručně: + if zdrojova.user != cilova.user: + # Jeden z nich může být nenastavený… + if zdrojova.user is None: + print('Uživatel je již v cílové osobě') + elif cilova.user is None: + print('Používám uživatele zdrojové osoby') + cilova.user = zdrojova.user + # Teď nemůžeme uložit, protože kolize uživatelů. Ukládat cílovou budeme až po smazání zdrojové. + else: raise ValueError('Osoby mají obě uživatele, radši padám') + + # Uložení a mazání + print(f'Mažu zdrojovou osobu {zdrojova.__dict__}') + zdrojova.delete() + print(f'Ukládám cílovou osobu {cilova.__dict__}') + cilova.save() + + input("Potvrdit transakci osob (^C pro zrušení) ") diff --git a/prednasky/urls.py b/prednasky/urls.py index 6b455163..eecc45ad 100644 --- a/prednasky/urls.py +++ b/prednasky/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from seminar.utils import org_required, resitel_or_org_required +from personalni.utils import org_required, resitel_or_org_required from . import views urlpatterns = [ diff --git a/seminar/urls.py b/seminar/urls.py index f740e6a4..777b40a4 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -1,6 +1,6 @@ from django.urls import path, include, re_path from . import views -from .utils import org_required +from personalni.utils import org_required urlpatterns = [ # path('aktualni/temata/', views.TemataRozcestnikView), diff --git a/seminar/utils.py b/seminar/utils.py index 349b0f01..869cac09 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -1,52 +1,20 @@ import datetime import decimal - -from django.contrib.auth import get_user_model -from django.contrib.auth.decorators import permission_required, \ - user_passes_test from html.parser import HTMLParser from django import views as DjangoViews -from django.db import transaction - -from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist import logging -from personalni.models import Organizator, Resitel, Skola, Prijemce -from tvorba.models import Clanek, Rocnik +from personalni.models import Resitel +from tvorba.models import Clanek from treenode.models import CisloNode -from soustredeni.models import Konfery_Ucastnici, Soustredeni_Ucastnici -from odevzdavatko.models import Reseni_Resitele import treenode.treelib as t logger = logging.getLogger(__name__) -org_required = permission_required('auth.org') -resitel_required = permission_required('auth.resitel') - - -# inspirováno django.contrib.auth.decorators permission_required -def check_perms(user): - if user.has_perms(('auth.resitel',)): - return True - if user.has_perms(('auth.org',)): - return True - return False - - -resitel_or_org_required = user_passes_test(check_perms) - -User = get_user_model() -# Není to úplně hezké, ale budeme doufat, že to je funkční... -User.je_org = property(lambda self: self.has_perm('auth.org')) -User.je_resitel = property(lambda self: self.has_perm('auth.resitel')) -AnonymousUser.je_org = False -AnonymousUser.je_resitel = False - def vzorecek_na_prepocet(body, resitelu): """ Vzoreček na přepočet plných bodů na parciálni, když má řešení více řešitelů. """ @@ -176,63 +144,6 @@ def seznam_problemu(): return problemy -### Generovani obalek -def resi_v_rocniku(rocnik, cislo=None): - """ Vrátí seznam řešitelů, co vyřešili nějaký problém v daném ročníku, do daného čísla. - Parametry: - rocnik (typu Rocnik) ročník, ze kterého chci řešitele, co něco odevzdali - cislo (typu Cislo) číslo, do kterého včetně se počítá, že v daném - ročníku řešitel něco poslal. - Pokud není zadané, počítají se všechna řešení z daného ročníku. - Výstup: - QuerySet objektů typu Resitel """ - - if cislo is None: - # filtrujeme pouze podle ročníku - return Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), - reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik).distinct() - else: # filtrujeme podle ročníku i čísla - return Resitel.objects.filter(rok_maturity__gte=rocnik.druhy_rok(), - reseni__hodnoceni__deadline_body__cislo__rocnik=rocnik, - reseni__hodnoceni__deadline_body__cislo__poradi__lte=cislo.poradi).distinct() - - -def aktivniResitele(cislo, pouze_letosni=False): - """ Vrací QuerySet aktivních řešitelů, což jsou ti, co ještě neodmaturovali - a letos něco poslali (anebo loni něco poslali, pokud jde o první tři čísla). - Parametry: - cislo (typu Cislo) číslo, o které se jedná - pouze_letosni jen řešitelé, kteří tento rok něco poslali - - """ - letos = cislo.rocnik - - # detekujeme, zda jde o první tři čísla či nikoli (tj. zda spamovat řešitele z minulého roku) - zacatek_rocniku = True - try: - if int(cislo.poradi) > 3: - zacatek_rocniku = False - except ValueError: - # if cislo.poradi != '7-8': - # raise ValueError(f'{cislo} je neplatné číslo čísla (není int a není 7-8)') - zacatek_rocniku = False - - # nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali - if pouze_letosni: - zacatek_rocniku = False - - try: - loni = Rocnik.objects.get(rocnik=letos.rocnik - 1) - except ObjectDoesNotExist: - # Pro první ročník neexistuje ročník předchozí - zacatek_rocniku = False - - if not zacatek_rocniku: - return resi_v_rocniku(letos, cislo).filter(rok_maturity__gte=letos.druhy_rok()) - else: - # spojíme querysety s řešiteli loni a letos do daného čísla - return (resi_v_rocniku(loni) | resi_v_rocniku(letos, cislo)).distinct().filter(rok_maturity__gte=letos.druhy_rok()) - def viewMethodSwitch(get, post): """ Vrátí view, který zavolá různé jiné views podle toho, kterou metodou je zavolán. @@ -258,146 +169,3 @@ def viewMethodSwitch(get, post): return thePostView(request, *args, **kwargs) return NewView.as_view() - - -def sync_skoly(base_url): - """Stáhne všechny školy z mamwebu na adrese a uloží je do databáze""" - from django.urls import reverse - full_url = base_url.rstrip('/') + reverse('export_skoly') - import requests - from django.core import serializers - json = requests.get(full_url, stream=True).content - for skola in serializers.deserialize('json', json): - skola.save() - -@transaction.atomic -def merge_resitele(cilovy, zdrojovy): - """Spojí dva řešitelské objekty do cílového. - - Pojmenování "zdrojový" je silně nepřiléhající, ale co už…""" - - # Postup: - # Sjednotit / upravit informace cílového řešitele - print('Upravuji data modelu') - fieldy_shoda = ['skola', 'rok_maturity', 'zasilat', 'zasilat_cislo_emailem', 'zasilat_cislo_papirove'] - - for f in fieldy_shoda: - zf = getattr(zdrojovy, f) - cf = getattr(cilovy, f) - if cf == zf: - print(f' Údaj {f} je shodný ({zf})') - else: - if zf is None: - print(f' Údaj {f} je pouze v cílovém, používám') - continue - if cf is None: - setattr(cilovy, f, zf) - cilovy.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojového: {zf}' - print(f" Přiřazuji {f} ze zdrojového: {zf}") - continue - # Jsou fakt různé… - # FIXME: chybí možnost na vlastní úpravu… - verdikt = input(f"\n\n Údaj {f} se u řešitele {cilovy} ({cilovy.id}) liší:\n Zdrojový: {zf}\n Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") - verdikt = verdikt[0].casefold() - if verdikt == 'z': - setattr(cilovy, f, zf) - cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojový), nepoužit {cf} (cílový)' - elif verdikt == 'c': - cilovy.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílový), nepoužit {zf} (zdrojový)' - else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') - # poznámku chceme nezahodit… - cilovy.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojovy.poznamka}' - print(f' Výsledný řešitel: {cilovy.__dict__}, ukládám') - cilovy.save() - - - # Přepojit všechny vazby ze zdrojového na cílového - print('Přepojuji vazby') - # Vazby: Škola (hotovo), Řešení_Řešitelé, Konfery_Účastníci, Soustředění_Účastníci, Osoba (vyřeší se později, nejde přepojit) - ct = Reseni_Resitele.objects.filter(resitele=zdrojovy).update(resitele=cilovy) - print(f' Přepojeno {ct} řešení') - ct = Konfery_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) - print(f' Přepojeno {ct} konfer') - ct = Soustredeni_Ucastnici.objects.filter(resitel=zdrojovy).update(resitel=cilovy) - print(f' Přepojeno {ct} sousů') - - # Teď by na zdrojovém řešiteli nemělo nic viset, smazat ho, pamatujíce si jeho Osobu - zdrosoba = zdrojovy.osoba - print(f'Mažu zdrojového řešitele {zdrojovy.__dict__}') - zdrojovy.delete() - # Spojit osoby (separátní funkce). - merge_osoby(cilovy.osoba, zdrosoba) - - input("Potvrdit transakci řešitelů (^C pro zrušení) ") - -@transaction.atomic -def merge_osoby(cilova, zdrojova): - """ Spojí dvě osoby do cílové - - Nehlídá omezení typu "max 1 řešitel na osobu", to by měla hlídat databáze (OneToOneField).""" - # Sjednocení dat - print('Sjednocuji data osob') - # 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'] - for f in fieldy: - zf = getattr(zdrojova, f) - cf = getattr(cilova, f) - if cf == zf: - print(f' Údaj {f} je shodný ({zf})') - else: - if zf is None: - print(f' Údaj {f} je pouze v cílové, používám') - continue - if cf is None: - setattr(cilova, f, zf) - cilova.poznamka += f'\nDEBUG: Merge: doplnéný údaj {f} ze zdrojové: {zf}' - print(f" Přiřazuji {f} ze zdrojové: {zf}") - continue - # Jsou fakt různé… - # FIXME: chybí možnost na vlastní úpravu… - verdikt = input(f"\n\n Údaj {f} se u osoby {cilova} ({cilova.id}) liší:\n Zdrojový: {zf}\n Cílový: {cf}\n Který použít, [z]drojový, [c]ílový? ") - verdikt = verdikt[0].casefold() - if verdikt == 'z': - setattr(cilova, f, zf) - cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {zf} (zdrojová), nepoužit {cf} (cílová)' - elif verdikt == 'c': - cilova.poznamka += f'\nDEBUG: Merge: pro {f} použit údaj {cf} (cílová), nepoužit {zf} (zdrojová)' - else: raise ValueError('Špatná odpověď, řešitel pravděpodobně neuložen') - # poznámku chceme nezahodit… - cilova.poznamka += f'\nDEBUG: Merge: Původní poznámka: {zdrojova.poznamka}' - print(f' Výsledná osoba: {cilova.__dict__}, ukládám') - cilova.save() - - # Vazby: Řešitel, User, Příjemce, Organizátor, Škola.kontaktní_osoba - print('Přepojuji vazby') - ct = Skola.objects.filter(kontaktni_osoba=zdrojova).update(kontaktni_osoba=cilova) - print(f' Přepojeno {ct} kontaktních osob') - # Ostatní vazby vyřeší OneToOneFieldy, ale někdy nemusí existovat… - ct = Resitel.objects.filter(osoba=zdrojova).update(osoba=cilova) - print(f' Přepojeno {ct} řešitelů') - ct = Prijemce.objects.filter(osoba=zdrojova).update(osoba=cilova) - print(f' Přepojeno {ct} příjemců') - ct = Organizator.objects.filter(osoba=zdrojova).update(osoba=cilova) - print(f' Přepojeno {ct} organizátorů') - # Uživatelé vedou opačným směrem, radši chceme zkontrolovat, že jsou různí ručně: - if zdrojova.user != cilova.user: - # Jeden z nich může být nenastavený… - if zdrojova.user is None: - print('Uživatel je již v cílové osobě') - elif cilova.user is None: - print('Používám uživatele zdrojové osoby') - cilova.user = zdrojova.user - # Teď nemůžeme uložit, protože kolize uživatelů. Ukládat cílovou budeme až po smazání zdrojové. - else: raise ValueError('Osoby mají obě uživatele, radši padám') - - # Uložení a mazání - print(f'Mažu zdrojovou osobu {zdrojova.__dict__}') - zdrojova.delete() - print(f'Ukládám cílovou osobu {cilova.__dict__}') - cilova.save() - - input("Potvrdit transakci osob (^C pro zrušení) ") - - diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 3e574155..10ab3116 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -36,7 +36,7 @@ import logging import time from collections.abc import Sequence -from seminar.utils import aktivniResitele +from personalni.utils import aktivniResitele # ze starého modelu #def verejna_temata(rocnik): diff --git a/soustredeni/urls.py b/soustredeni/urls.py index 2e5a6136..92cfad18 100644 --- a/soustredeni/urls.py +++ b/soustredeni/urls.py @@ -1,6 +1,6 @@ from django.urls import path, include from . import views -from seminar.utils import org_required +from personalni.utils import org_required urlpatterns = [ path( diff --git a/tvorba/models.py b/tvorba/models.py index f06edfe1..9f21287e 100644 --- a/tvorba/models.py +++ b/tvorba/models.py @@ -279,7 +279,7 @@ class Cislo(SeminarModelBase): # Prijemci e-mailu - from seminar.utils import aktivniResitele + from personalni.utils import aktivniResitele resitele_vsichni = aktivniResitele(self).filter(zasilat_cislo_emailem=True) def posli(subject, text, resitele): diff --git a/vyroci/urls.py b/vyroci/urls.py index 69132f45..44215a46 100644 --- a/vyroci/urls.py +++ b/vyroci/urls.py @@ -1,6 +1,6 @@ from django.urls import path -from seminar.utils import org_required +from personalni.utils import org_required from .views import VyrociView, VyrociListView urlpatterns = [ diff --git a/vysledkovky/utils.py b/vysledkovky/utils.py index 5afccab0..4e691e85 100644 --- a/vysledkovky/utils.py +++ b/vysledkovky/utils.py @@ -7,7 +7,7 @@ from odevzdavatko.models import Hodnoceni from personalni.models import Resitel from soustredeni.models import Konfera from django.db.models import Q, Sum -from seminar.utils import resi_v_rocniku +from personalni.utils import resi_v_rocniku ROCNIK_ZRUSENI_TEMAT = 25