diff --git a/seminar/export.py b/seminar/export.py index 0143101e..b2339786 100644 --- a/seminar/export.py +++ b/seminar/export.py @@ -8,98 +8,96 @@ from django.utils.encoding import force_text from .models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustredeni #from .models import VysledkyZaCislo, VysledkyKCisluZaRocnik, VysledkyKCisluOdjakziva from .ovvpfile import OvvpFile +from seminar import views class ExportIndexView(generic.View): - def get(self, request): + def get(self, request): + ls = [] + for r in Rocnik.objects.filter(exportovat = True): + url = reverse('seminar_export_rocnik', kwargs={'prvni_rok': r.prvni_rok}) + ls.append(url.split('/')[-1]) + for s in Soustredeni.objects.filter(exportovat = True): + url = reverse('seminar_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()}) + ls.append(url.split('/')[-1]) - ls = [] - for r in Rocnik.objects.filter(exportovat = True): - url = reverse('seminar_export_rocnik', kwargs={'prvni_rok': r.prvni_rok}) - ls.append(url.split('/')[-1]) - for s in Soustredeni.objects.filter(exportovat = True): - url = reverse('seminar_export_sous', kwargs={'datum_zacatku': s.datum_zacatku.isoformat()}) - ls.append(url.split('/')[-1]) - - return HttpResponse('\n'.join(ls) + '\n', content_type='text/plain; charset=utf-8') + return HttpResponse('\n'.join(ls) + '\n', content_type='text/plain; charset=utf-8') def default_ovvpfile(event, rocnik): - of = OvvpFile() - of.headers['version'] = '1' - of.headers['event'] = event - of.headers['year'] = force_text(rocnik.prvni_rok) - of.headers['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") - of.headers['id-scope'] = 'mam' - of.headers['id-generation'] = '1' - return of + of = OvvpFile() + of.headers['version'] = '1' + of.headers['event'] = event + of.headers['year'] = force_text(rocnik.prvni_rok) + of.headers['date'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + of.headers['id-scope'] = 'mam' + of.headers['id-generation'] = '1' + return of class ExportSousView(generic.View): - def get(self, request, datum_zacatku=None): - try: - dz = django.utils.dateparse.parse_date(datum_zacatku) - except: - dz = None - if dz is None: - raise django.http.Http404() - - s = get_object_or_404(Soustredeni, datum_zacatku=dz, exportovat=True) - - akce = {Soustredeni.TYP_JARNI: 'MaM.sous.jaro', - Soustredeni.TYP_PODZIMNI: 'MaM.sous.podzim', - Soustredeni.TYP_VIKEND: 'MaM.vikend', - }[s.typ] - - of = default_ovvpfile(akce, s.rocnik) - of.headers['x-event-begin'] = s.datum_zacatku.isoformat() - of.headers['x-event-end'] = s.datum_konce.isoformat() - of.headers['x-event-location'] = s.misto - of.headers['comment'] = u'MaM-Web export ucastniku soustredeni v {x-event-location} od {x-event-begin} do {x-event-end}'.format(**of.headers) - of.columns = ['id', 'name', 'surname', 'gender', 'email', 'end-year', 'school', 'school-name'] - - for u in s.ucastnici.all(): - of.rows.append(u.export_row()) - - return of.to_HttpResponse() - - + def get(self, request, datum_zacatku=None): + try: + dz = django.utils.dateparse.parse_date(datum_zacatku) + except: + dz = None + if dz is None: + raise django.http.Http404() + + s = get_object_or_404(Soustredeni, datum_zacatku=dz, exportovat=True) + + akce = {Soustredeni.TYP_JARNI: 'MaM.sous.jaro', + Soustredeni.TYP_PODZIMNI: 'MaM.sous.podzim', + Soustredeni.TYP_VIKEND: 'MaM.vikend', + }[s.typ] + + of = default_ovvpfile(akce, s.rocnik) + of.headers['x-event-begin'] = s.datum_zacatku.isoformat() + of.headers['x-event-end'] = s.datum_konce.isoformat() + of.headers['x-event-location'] = s.misto + of.headers['comment'] = u'MaM-Web export ucastniku soustredeni v {x-event-location} od {x-event-begin} do {x-event-end}'.format(**of.headers) + of.columns = ['id', 'name', 'surname', 'gender', 'email', 'end-year', 'school', 'school-name'] + + for u in s.ucastnici.all(): + of.rows.append(u.export_row()) + + return of.to_HttpResponse() + +# POZOR! Předělání na nový model neotestováno v reálu (ale zase jen drobné změny) class ExportRocnikView(generic.View): - def get(self, request, prvni_rok=None): - try: - pr = int(prvni_rok) - except: - pr = None - if pr is None: - raise django.http.Http404() - - rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True) - cislo = rocnik.posledni_zverejnena_vysledkovka_cislo() - vysledky = VysledkyKCisluZaRocnik.objects.filter(cislo = cislo).select_related("resitel").order_by('-body').all() - - of = default_ovvpfile('MaM.rocnik', rocnik) - of.headers['comment'] = u'MaM-Web export aktivnich resitelu rocniku {rocnik} do cisla {cislo}'.format( - rocnik=rocnik, cislo=cislo) - of.columns = ['id', 'name', 'surname', 'gender', 'born', 'email', 'end-year', - 'street', 'town', 'postcode', 'country', 'spam-flag', 'spam-date', - 'school', 'school-name', 'points', 'rank',] - - posledni_body = 100000 - posledni_poradi = 0 - for vi in range(len(vysledky)): - - v = vysledky[vi] - rd = v.resitel.export_row() - - if posledni_body > v.body: - posledni_body = v.body - posledni_poradi = vi + 1 - rd['rank'] = posledni_poradi - rd['points'] = v.body - - of.rows.append(rd) - - return of.to_HttpResponse() - - + def get(self, request, prvni_rok=None): + try: + pr = int(prvni_rok) + except: + pr = None + if pr is None: + raise django.http.Http404() + + rocnik = get_object_or_404(Rocnik, prvni_rok=pr, exportovat=True) + cislo = rocnik.posledni_zverejnena_vysledkovka_cislo() + resitele = views.aktivniResitele(cislo.rocnik.rocnik, cislo.poradi, True) + slovnik_body = views.secti_body_za_rocnik(cislo, resitele) + _, setrizeni_resitele, setrizene_body = views.setrid_resitele_a_body(slovnik_body) + + of = default_ovvpfile('MaM.rocnik', rocnik) + of.headers['comment'] = u'MaM-Web export aktivnich resitelu rocniku {rocnik} do cisla {cislo}'.format(rocnik=rocnik, cislo=cislo) + of.columns = ['id', 'name', 'surname', 'gender', 'born', 'email', 'end-year', + 'street', 'town', 'postcode', 'country', 'spam-flag', 'spam-date', + 'school', 'school-name', 'points', 'rank',] + + # počítání pořadí řešitelů + posledni_body = 100000 + posledni_poradi = 0 + for i in range(len(setrizeni_resitele)): + rd = setrizeni_resitele[i].export_row() + + if posledni_body > body[i]: + posledni_body = body[i] + posledni_poradi = i + 1 + rd['rank'] = posledni_poradi + rd['points'] = body[i] + + of.rows.append(rd) + + return of.to_HttpResponse() diff --git a/seminar/urls.py b/seminar/urls.py index fcaf8144..2b93f00b 100644 --- a/seminar/urls.py +++ b/seminar/urls.py @@ -94,17 +94,9 @@ urlpatterns = [ staff_member_required(views.StavDatabazeView), name='stav_databaze'), path('cislo/./obalkovani', staff_member_required(views.ObalkovaniView.as_view()), name='seminar_cislo_resitel_obalkovani'), - path('cislo/./tex-download.json', - staff_member_required(views.texDownloadView), name='seminar_tex_download'), path('soustredeni//obalky.pdf', staff_member_required(views.soustredeniObalkyView), name='seminar_soustredeni_obalky'), - path('tex-upload/login/', views.TeXUploadLoginView, name='seminar_login'), - path( - 'tex-upload/', - staff_member_required(views.texUploadView), - name='seminar_tex_upload' - ), path('org/vloz_body//', staff_member_required(views.VlozBodyView.as_view()),name='seminar_org_vlozbody'), path('auth/prihlaska/',views.prihlaskaView, name='seminar_prihlaska'), diff --git a/seminar/utils.py b/seminar/utils.py index 3869ffd4..19223b9b 100644 --- a/seminar/utils.py +++ b/seminar/utils.py @@ -49,40 +49,40 @@ def seznam_problemu(): # Pomocna fce k formatovani problemovych hlasek def prb(cls, msg, objs=None): - s = u'%s: %s' % (cls.__name__, msg) + s = '%s: %s' % (cls.__name__, msg) if objs: - s += u' [' + s += ' [' for o in objs: try: url = o.admin_url() except: url = None if url: - s += u'%s, ' % (url, o.pk, ) + s += '%s, ' % (url, o.pk, ) else: - s += u'%s, ' % (o.pk, ) - s = s[:-2] + u']' + s += '%s, ' % (o.pk, ) + s = s[:-2] + ']' problemy.append(s) # Duplicita jmen jmena = {} for r in m.Resitel.objects.all(): - j = r.plne_jmeno() + j = r.osoba.plne_jmeno() if j not in jmena: jmena[j] = [] jmena[j].append(r) for j in jmena: if len(jmena[j]) > 1: - prb(m.Resitel, u'Duplicitní jméno "%s"' % (j, ), jmena[j]) + prb(m.Resitel, 'Duplicitní jméno "%s"' % (j, ), jmena[j]) # Data maturity a narození for r in m.Resitel.objects.all(): if not r.rok_maturity: - prb(m.Resitel, u'Neznámý rok maturity', [r]) + prb(m.Resitel, 'Neznámý rok maturity', [r]) if r.rok_maturity and (r.rok_maturity < 1990 or r.rok_maturity > datetime.date.today().year + 10): - prb(m.Resitel, u'Podezřelé datum maturity', [r]) - if r.datum_narozeni and (r.datum_narozeni.year < 1970 or r.datum_narozeni.year > datetime.date.today().year - 12): - prb(m.Resitel, u'Podezřelé datum narození', [r]) + prb(m.Resitel, 'Podezřelé datum maturity', [r]) + if r.osoba.datum_narozeni and (r.osoba.datum_narozeni.year < 1970 or r.osoba.datum_narozeni.year > datetime.date.today().year - 12): + prb(m.Resitel, 'Podezřelé datum narození', [r]) # if not r.email: # prb(Resitel, u'Neznámý email', [r]) diff --git a/seminar/views/views_all.py b/seminar/views/views_all.py index 69832744..9089b63b 100644 --- a/seminar/views/views_all.py +++ b/seminar/views/views_all.py @@ -645,6 +645,12 @@ class RadekVysledkovkyRocniku(object): self.body_cisla_sezn = body_cisla_sezn self.titul = resitel.get_titul(body_odjakziva) +def setrid_resitele_a_body(slov_resitel_body): + setrizeni_resitele_id = [dvojice[0] for dvojice in slov_resitel_body] + setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] + setrizene_body = [dvojice[1] for dvojice in slov_resitel_body] + return setrizeni_resitele_id, setrizeni_resitele, setrizene_body + def vysledkovka_rocniku(rocnik, jen_verejne=True): """ Přebírá ročník (např. context["rocnik"]) a vrací výsledkovou listinu ve formě vhodné pro šablonu "seminar/vysledkovka_rocniku.html" @@ -665,9 +671,7 @@ def vysledkovka_rocniku(rocnik, jen_verejne=True): resitel_rocnikbody_sezn = secti_body_za_rocnik(rocnik, aktivni_resitele) # setřídíme řešitele podle počtu bodů a získáme seznam s body od nejvyšších po nenižší - setrizeni_resitele_id = [dvojice[0] for dvojice in resitel_rocnikbody_sezn] - setrizeni_resitele = [Resitel.objects.get(id=i) for i in setrizeni_resitele_id] - setrizene_body = [dvojice[1] for dvojice in resitel_rocnikbody_sezn] + setrizeni_resitele_id, setrizeni_resitele, setrizene_body = setrid_resitele_a_body(resitel_rocnikbody_sezn) poradi = sloupec_s_poradim(setrizene_body) # získáme body odjakživa @@ -958,12 +962,13 @@ def resi_v_rocniku(rocnik, cislo=None): return letosni_resitele.distinct() -def aktivniResitele(rocnik, cislo): +def aktivniResitele(rocnik, cislo, pouze_realni=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: rocnik (typu int) číslo ročníku, o který se jedná cislo (typu int) pořadí čísla, o které se jedná + pouze_realni jen řešitelé, kteří tento rok něco poslali """ letos = Rocnik.objects.get(rocnik = rocnik) @@ -979,6 +984,10 @@ def aktivniResitele(rocnik, cislo): except ValueError: # pravděpodobně se jedná o číslo 7-8 zacatek_rocniku = False + + # nehledě na číslo chceme jen řešitele, kteří letos něco odevzdali + if pouze_realni: + zacatek_rocniku = False if not zacatek_rocniku: return resi_v_rocniku(letos) @@ -1123,8 +1132,8 @@ class ClankyResitelView(generic.ListView): def StavDatabazeView(request): # nastaveni = Nastaveni.objects.get() problemy = utils.seznam_problemu() - muzi = Resitel.objects.filter(pohlavi_muz=True) - zeny = Resitel.objects.filter(pohlavi_muz=False) + muzi = Resitel.objects.filter(osoba__pohlavi_muz=True) + zeny = Resitel.objects.filter(osoba__pohlavi_muz=False) return render(request, 'seminar/stav_databaze.html', { # 'nastaveni': nastaveni, @@ -1133,211 +1142,11 @@ def StavDatabazeView(request): 'resitele': Resitel.objects.all(), 'muzi': muzi, 'zeny': zeny, - 'jmena_muzu': utils.histogram([r.jmeno for r in muzi]), - 'jmena_zen': utils.histogram([r.jmeno for r in zeny]), + 'jmena_muzu': utils.histogram([r.osoba.jmeno for r in muzi]), + 'jmena_zen': utils.histogram([r.osoba.jmeno for r in zeny]), }) - -@ensure_csrf_cookie -def TeXUploadLoginView(request): - """Pro přihlášení při nahrávání z texu""" - q = request.POST - # nastavení cookie csrftoken - if not q: - return JsonResponse({"ok": 1}) - - if "username" in q: - username = q["username"] - password = q["password"] - user = authenticate(username=username, password=password) - if user is not None and user.is_staff: - login(request, user) - return JsonResponse({"ok": 1}) - else: - return JsonResponse({"error": "Neplatné uživatelské jméno nebo heslo"}) - - -@ensure_csrf_cookie -def texUploadView(request): - - def uloz_soubory(files, rocnik, cislo): - for filename, f in files: - path = os.path.join( - settings.MEDIA_ROOT, - settings.CISLO_IMG_DIR, - rocnik, - cislo, - filename - ) - - adresar = os.path.dirname(path) - if not os.path.exists(adresar): - os.makedirs(adresar) - - with open(path, "wb+") as fout: - for chunk in f.chunks(): - fout.write(chunk) - - q = request.POST - # nastavení cookie csrftoken - if not q: - return JsonResponse({"ok": 1}) - - # Odchytíme všechny výjimky a traceback pošleme v odpovědi - try: - meta = json.loads(q["meta"]) - html = q["html"] - - if meta["typ"] in ["uloha", "serial", "reseni", "tema"]: - - # Uložíme soubory - if meta["typ"] != "reseni": - c = meta["cislo"] - else: - # Řešení má nastavené číslo svojí úlohy, ale obrázky jsou - # ukládány do čísla, kde řešení vyšlo - c = meta["cislo_reseni"] - - # Zjistíme typ ukládaného problému - typy = { - "uloha": Problem.TYP_ULOHA, - "serial": Problem.TYP_SERIAL, - "reseni": Problem.TYP_ULOHA, - "tema": Problem.TYP_TEMA, - } - problem_typ = typy[meta["typ"]] - - # Pokud už problém existuje, vytáhneme jej z db a upravíme. - # Pokud neexistuje, vytvoříme jej jedině pokud je to vynucené. - - # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> - # číslo/ročník se musí založit ručně v adminu. - rocnik = Rocnik.objects.get(rocnik=meta["rocnik"]) - cislo = Cislo.objects.get(rocnik=rocnik, cislo=meta["cislo"]) - - existujici = Problem.objects.filter( - typ=problem_typ, - stav=Problem.STAV_ZADANY, - cislo_zadani=cislo, - kod=meta["kod"] - ) - - problem = None - if existujici: - problem = existujici[0] - elif "vytvor" in q: - # vytvoříme nový - problem = Problem( - typ=problem_typ, - stav=Problem.STAV_ZADANY, - kod=meta["kod"], - cislo_zadani=cislo - ) - else: - return JsonResponse({ - "error": "Problém neexistuje: {} {}.{} kód {}".format( - meta["typ"], meta["rocnik"], meta["cislo"], meta["kod"] - ) - }) - - uloz_soubory(request.FILES.items(), meta["rocnik"], c) - - if meta["typ"] == "reseni": - problem.text_reseni = html - - # Pokud ročník/číslo ještě neexistuje, vyhodí to výjimku -> - # číslo/ročník se musí založit ručně v adminu - problem.cislo_reseni = Cislo.objects.get( - rocnik=rocnik, - cislo=meta["cislo_reseni"] - ) - # při nahrávání řešení už původní zadání atd. neměníme - else: - problem.text_zadani = html - problem.nazev = meta["nazev"] - if meta["typ"] != "tema": - problem.body = meta["body"] - - problem.save() - cislo.faze = cislo.FAZE_NAHRANO - cislo.save() - - # Vrátíme id dané úlohy, aby se k ní dala případně připojit pohádka - return JsonResponse({"db_id": problem.id}) - - elif meta["typ"] == "pohadka": - uloha = Problem.objects.get(typ=Problem.TYP_ULOHA, pk=meta["uloha"]) - - # Pokud už příslušná pohádka existuje, jen ji upravíme - existujici = Pohadka.objects.filter(uloha=uloha, pred=meta["pred"]) - pohadka = None - if existujici: - pohadka = existujici[0] - else: - pohadka = Pohadka(uloha=uloha, pred=meta["pred"]) - pohadka.text = q["html"] - pohadka.save() - - return JsonResponse({"db_id": pohadka.id}) - - except Exception as e: - # Pošleme zpátky traceback, ať uživatel ví, v čem je problém - tb = "".join(traceback.format_exception(type(e), e, sys.exc_info()[2])) - return JsonResponse({"error": tb}) - - -def texDownloadView(request, rocnik, cislo): - """View posílající JSON se zadanými a řešenými problémy pro založení čísla - """ - cislo = Cislo.objects.get(rocnik__rocnik=rocnik, cislo=cislo) - if cislo.faze == cislo.FAZE_NAHRANO: - # obsah byl nahrán z TeXu na web, už je příliš složitý - return JsonResponse( - {"error": "Obsah čísla už byl nahrán z TeXu na web."} - ) - - zadane = Problem.objects.filter( - cislo_zadani=cislo, - stav=Problem.STAV_ZADANY - ) - resene = Problem.objects.filter( - cislo_reseni=cislo, - stav=Problem.STAV_ZADANY, - typ=Problem.TYP_ULOHA - ) - pred_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=True) - po_pohadky = Pohadka.objects.filter(uloha__cislo_zadani=cislo, pred=False) - - response = { - "zadane": [ - { - "nazev": p.nazev, - "typ": p.typ, - "kod": p.kod, - "body": p.body, - "zadani": p.text_zadani, - "pred_pohadky": [x.text for x in pred_pohadky.filter(uloha=p)], - "po_pohadky": [x.text for x in po_pohadky.filter(uloha=p)], - } for p in zadane - ], - "resene": [ - { - "nazev": p.nazev, - "typ": p.typ, - "kod": p.kod, - "body": p.body, - "zadani": p.text_zadani, - "reseni": p.text_reseni, - "cislo_zadani": p.cislo_zadani.poradi, - } for p in resene - ], - } - - cislo.faze = Cislo.FAZE_TEX - cislo.save() - return JsonResponse(response) - class ResitelView(LoginRequiredMixin,generic.DetailView): model = Resitel template_name = 'seminar/resitel.html'