Merge remote-tracking branch 'origin/data_migrations' into treenode_editor
This commit is contained in:
		
						commit
						e4818d28ea
					
				
					 4 changed files with 102 additions and 303 deletions
				
			
		|  | @ -8,10 +8,10 @@ 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): | ||||
| 
 | ||||
| 		ls = [] | ||||
| 		for r in Rocnik.objects.filter(exportovat = True): | ||||
| 	    		url = reverse('seminar_export_rocnik', kwargs={'prvni_rok': r.prvni_rok}) | ||||
|  | @ -63,7 +63,7 @@ class ExportSousView(generic.View): | |||
| 	 | ||||
| 		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): | ||||
|  | @ -76,30 +76,28 @@ class ExportRocnikView(generic.View): | |||
| 
 | ||||
| 		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() | ||||
| 		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.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 vi in range(len(vysledky)): | ||||
| 		for i in range(len(setrizeni_resitele)): | ||||
| 			rd = setrizeni_resitele[i].export_row() | ||||
| 
 | ||||
|             v = vysledky[vi] | ||||
|             rd = v.resitel.export_row() | ||||
| 
 | ||||
|             if posledni_body > v.body: | ||||
|                 posledni_body = v.body | ||||
|                 posledni_poradi = vi + 1 | ||||
| 			if posledni_body > body[i]: | ||||
| 				posledni_body = body[i] | ||||
| 				posledni_poradi = i + 1 | ||||
| 			rd['rank'] = posledni_poradi | ||||
|             rd['points'] = v.body | ||||
| 			rd['points'] = body[i] | ||||
| 
 | ||||
| 			of.rows.append(rd) | ||||
| 	 | ||||
| 		return of.to_HttpResponse() | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -94,17 +94,9 @@ urlpatterns = [ | |||
| 		staff_member_required(views.StavDatabazeView), name='stav_databaze'), | ||||
| 	path('cislo/<int:rocnik>.<int:cislo>/obalkovani', | ||||
| 		staff_member_required(views.ObalkovaniView.as_view()), name='seminar_cislo_resitel_obalkovani'), | ||||
| 	path('cislo/<int:rocnik>.<int:cislo>/tex-download.json', | ||||
| 		staff_member_required(views.texDownloadView), name='seminar_tex_download'), | ||||
| 	path('soustredeni/<int: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/<int:tema>/', | ||||
| 		staff_member_required(views.VlozBodyView.as_view()),name='seminar_org_vlozbody'), | ||||
| 	path('auth/prihlaska/',views.prihlaskaView, name='seminar_prihlaska'), | ||||
|  |  | |||
|  | @ -49,40 +49,40 @@ def seznam_problemu(): | |||
| 
 | ||||
| 	# Pomocna fce k formatovani problemovych hlasek | ||||
| 	def prb(cls, msg, objs=None): | ||||
| 		s = u'<b>%s:</b> %s' % (cls.__name__, msg) | ||||
| 		s = '<b>%s:</b> %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'<a href="%s">%s</a>, ' % (url, o.pk, ) | ||||
| 					s += '<a href="%s">%s</a>, ' % (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]) | ||||
| 
 | ||||
|  |  | |||
|  | @ -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) | ||||
|  | @ -980,6 +985,10 @@ def aktivniResitele(rocnik, cislo): | |||
|        		# 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)	 | ||||
| 	else: | ||||
|  | @ -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' | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue