Merge branch 'data_migrations' into test
This commit is contained in:
		
						commit
						fb01c005d9
					
				
					 13 changed files with 136 additions and 15 deletions
				
			
		
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -782,5 +782,29 @@ | |||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 40 | ||||
| 	}, | ||||
| 	{ | ||||
| 		"fields": { | ||||
| 			"access_guest": false, | ||||
| 			"access_loggedin": false, | ||||
| 			"access_perm_type": 1, | ||||
| 			"access_permissions": [], | ||||
| 			"access_restricted": false, | ||||
| 			"alias": null, | ||||
| 			"description": "", | ||||
| 			"hidden": false, | ||||
| 			"hint": "", | ||||
| 			"inbreadcrumbs": true, | ||||
| 			"inmenu": true, | ||||
| 			"insitetree": true, | ||||
| 			"parent": 1, | ||||
| 			"sort_order": 41, | ||||
| 			"title": "Odměny", | ||||
| 			"tree": 1, | ||||
| 			"url": "/o-nas/odmeny/", | ||||
| 			"urlaspattern": false | ||||
| 		}, | ||||
| 		"model": "sitetree.treeitem", | ||||
| 		"pk": 41 | ||||
| 	} | ||||
| ] | ||||
|  | @ -601,7 +601,7 @@ class Cislo(SeminarModelBase): | |||
| 		if not self.titulka_nahled or os.path.getmtime(self.titulka_nahled.path) < os.path.getmtime(self.pdf.path): | ||||
| 			png_filename = pathlib.Path(tempfile.mkdtemp(), 'nahled.png') | ||||
| 
 | ||||
| 			subprocess.call([ | ||||
| 			subprocess.run([ | ||||
| 				"convert", | ||||
| 				"-density", "300x300", | ||||
| 				"-geometry", "{}x{}".format(VYSKA, sirka), | ||||
|  | @ -609,7 +609,10 @@ class Cislo(SeminarModelBase): | |||
| 				"-flatten", | ||||
| 				"{}[0]".format(self.pdf.path),  # titulní strana | ||||
| 				png_filename | ||||
| 			]) | ||||
| 				], | ||||
| 				check=True, | ||||
| 				capture_output=True | ||||
| 			) | ||||
| 
 | ||||
| 			with open(png_filename,'rb') as f: | ||||
| 				self.titulka_nahled.save('',f,True) | ||||
|  |  | |||
							
								
								
									
										
											BIN
										
									
								
								seminar/static/seminar/cross.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								seminar/static/seminar/cross.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 717 B | 
							
								
								
									
										
											BIN
										
									
								
								seminar/static/seminar/plus.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								seminar/static/seminar/plus.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 11 KiB | 
|  | @ -38,8 +38,8 @@ | |||
|         <h2> Orgovské odkazy </h2> | ||||
|         <ul> | ||||
|           <li><a href="obalky.pdf">Obálky (PDF)</a></li> | ||||
|           <li><a href="tituly.tex">Tituly (TeX)</a></li> | ||||
|           <li><a href="vysledkovka.tex">Výsledkovka (TeX)</a></li> | ||||
|           <li><a href="tituly.tex" download>Tituly (TeX)</a></li> | ||||
|           <li><a href="vysledkovka.tex" download>Výsledkovka (TeX)</a></li> | ||||
|           <li><a href="obalkovani">Obálkování</a></li> | ||||
| 	  <li><a href="odmeny/{{prevcislo.rocnik.rocnik}}.{{prevcislo.poradi}}/">Odměny</a></li> | ||||
|         </ul> | ||||
|  |  | |||
|  | @ -67,7 +67,7 @@ | |||
|   {% if vysledkovka %} | ||||
|     {% if user.je_org %} | ||||
|       <div class='mam-org-only'> | ||||
|         <a href='vysledkovka.tex'>Výsledkovka ročníku (LaTeX)</a> | ||||
|         <a href='vysledkovka.tex' download>Výsledkovka ročníku (LaTeX)</a> | ||||
|       </div> | ||||
|     {% endif %} | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| {% extends "base.html" %} | ||||
| {% load static %} | ||||
| 
 | ||||
| {% block content %} | ||||
| 
 | ||||
|  | @ -25,8 +26,9 @@ function deleteForm(prefix, btn) { | |||
|     if (total >= 1){ | ||||
|         btn.closest('tr').remove(); | ||||
|         var forms = $('.hodnoceni'); | ||||
|         $('#id_' + prefix + '-TOTAL_FORMS').val(forms.length); | ||||
|         for (var i=0, formCount=forms.length; i<formCount; i++) { | ||||
|         var formCount = forms.length - 1; // There is one extra such form hidden as template! | ||||
|         $('#id_' + prefix + '-TOTAL_FORMS').val(formCount); | ||||
|         for (var i=0; i<formCount; i++) { | ||||
|             $(forms.get(i)).find(':input').each(function() { | ||||
|                 updateElementIndex(this, prefix, i); | ||||
|             }); | ||||
|  | @ -91,21 +93,21 @@ $(document).ready(function(){ | |||
| 		<td>{{ subform.problem }}</td> | ||||
| 		<td>{{ subform.body }}</td> | ||||
| 		<td>{{ subform.cislo_body }}</td> | ||||
| 		<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{subform.prefix}}-jsremove"></td> | ||||
| 		<td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "seminar/cross.png" %}" alt="Smazat"></a></td> | ||||
| 	</tr> | ||||
| {% endfor %} | ||||
| </table> | ||||
| 
 | ||||
| 
 | ||||
| <input type=button id="pridat_hodnoceni" value="Přidat hodnocení"> | ||||
| <input type=submit></form> | ||||
| <a href="#"> <img src="{% static "seminar/plus.png" %}" id="pridat_hodnoceni" alt="Přidat hodnocení"></a> </br> | ||||
| <input type=submit value="Uložit"></form> | ||||
| 
 | ||||
| <table id="empty_form" style="display: none;"> | ||||
| 	<tr class="hodnoceni"> | ||||
| 		<td>{{ form.empty_form.problem }}</td> | ||||
| 		<td>{{ form.empty_form.body }}</td> | ||||
| 		<td>{{ form.empty_form.cislo_body }}</td> | ||||
| 		<td><input type=button class="smazat_hodnoceni" value="Smazat" id="id_{{form.empty_form.prefix}}-jsremove"></td> | ||||
| 		<td><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove"><img src="{% static "seminar/cross.png" %}" alt="Smazat"></a></td> | ||||
| 	</tr> | ||||
| </table> | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,8 @@ | |||
| Ahoj! | ||||
| 
 | ||||
| Obdrželi jsme od Tebe žádost o obnovu hesla uživatele {{ user }} na webu M&M. | ||||
| Pro zadání nového hesla přejdi na následující stránku: | ||||
| {{ protocol}}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} | ||||
| 
 | ||||
| S pozdravem, | ||||
| organizátoři M&M | ||||
|  | @ -0,0 +1 @@ | |||
| Změna hesla na webu M&M | ||||
|  | @ -11,6 +11,8 @@ from django.contrib.auth.models import AnonymousUser | |||
| from django.contrib.contenttypes.models import ContentType | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
| 
 | ||||
| from enum import Enum | ||||
| 
 | ||||
| import seminar.models as m | ||||
| import seminar.treelib as t | ||||
| 
 | ||||
|  | @ -282,3 +284,58 @@ def podproblemy_v_cislu(cislo, problemy=None, hlavni_problemy=None): | |||
| 			podproblemy[-1].append(problem) | ||||
| 
 | ||||
| 	return podproblemy | ||||
| 
 | ||||
| class TypDeadline(Enum): | ||||
| 	PredDeadline = auto() | ||||
| 	SousDeadline = auto() | ||||
| 	FinalDeadline = auto() | ||||
| 
 | ||||
| def deadline_v_rocniku(datum, rocnik): | ||||
| 	"""Funkce pro dohledání, ke kterému deadlinu daného ročníku se datum váže. | ||||
| 	 | ||||
| 	Vrací trojici (TypDeadline, Cislo, datumDeadline: date). | ||||
| 
 | ||||
| 	V případě nevalidního volání není aktuálně chování definováno(!) | ||||
| 	""" | ||||
| 	cisla = m.Cislo.objects.filter(rocnik=rocnik) | ||||
| 	deadliny = [] | ||||
| 	for c in cisla: | ||||
| 		if c.datum_preddeadline is not None: | ||||
| 			deadliny.append((TypDeadline.PredDeadline, c, c.datum_preddeadline)) | ||||
| 		if c.datum_deadline_soustredeni is not None: | ||||
| 			deadliny.append((TypDeadline.SousDeadline, c, c.datum_deadline_soustredeni)) | ||||
| 		if c.datum_deadline is not None: | ||||
| 			deadliny.append((TypDeadline.FinalDeadline, c, c.datum_deadline)) | ||||
| 	deadliny = sorted(deadliny, key=lambda x: x[2])	# podle data | ||||
| 	for dl in deadliny: | ||||
| 		if datum <= dl: | ||||
| 			# První takový deadline je ten nejtěsnější | ||||
| 			return dl | ||||
| 
 | ||||
| def deadline(datum): | ||||
| 	"""Funkce pro dohledání, ke kterému deadlinu se datum váže. | ||||
| 	 | ||||
| 	Vrací trojici (TypDeadline, Cislo, datumDeadline: date). | ||||
| 	""" | ||||
| 
 | ||||
| 	rok = datum.year | ||||
| 	# Dva ročníky podezřelé z obsahování dat | ||||
| 	pozdejsi_rocnik = m.Rocnik.filter(prvni_rok=rok) | ||||
| 	drivejsi_rocnik = m.Rocnik.filter(druhy_rok=rok) | ||||
| 	if any( | ||||
| 			pozdejsi_rocnik.count() > 1, | ||||
| 			drivejsi_rocnik.count() > 1, | ||||
| 			): | ||||
| 		raise ValueError(f"Více ročníků začíná/končí stejným rokem: {rok}") | ||||
| 	pozdejsi_rocnik = pozdejsi_rocnik.first() if pozdejsi_rocnik.count() > 0 else None | ||||
| 	drivejsi_rocnik = drivejsi_rocnik.first() if drivejsi_rocnik.count() > 0 else None | ||||
| 
 | ||||
| 	# Předpokládáme, že neexistuje číslo, které má deadline ale nemá finální deadline. | ||||
| 	posledni_deadline_drivejsiho_rocniku = m.Cislo.objects.get(rocnik=drivejsi_rocnik, datum_deadline__isnull=False).datum_deadline | ||||
| 
 | ||||
| 	if datum <= posledni_deadline_drivejsiho_rocniku: | ||||
| 		return deadline_v_rocniku(datum, drivejsi_rocnik) | ||||
| 	else: | ||||
| 		return deadline_v_rocniku(datum, pozdejsi_rocnik) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ from django.shortcuts import get_object_or_404, render, redirect | |||
| from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidden, JsonResponse | ||||
| from django.urls import reverse,reverse_lazy | ||||
| from django.core.exceptions import PermissionDenied, ObjectDoesNotExist | ||||
| from django.core.mail import send_mail | ||||
| from django.views import generic | ||||
| from django.utils.translation import ugettext as _ | ||||
| from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect | ||||
|  | @ -1058,6 +1059,21 @@ class NahrajReseniView(LoginRequiredMixin, CreateView): | |||
| 			prilohy.instance = self.object | ||||
| 			prilohy.save() | ||||
| 		 | ||||
| 		# Pošleme mail opravovatelům a garantovi | ||||
| 		# FIXME: Nechat spočítat databázi? Je to pár dotazů (pravděpodobně), takže to za to možná nestojí | ||||
| 		prijemci = set() | ||||
| 		for prob in form.cleaned_data['problem']: | ||||
| 			prijemci.update(prob.opravovatele.all()) | ||||
| 			prijemci.add(prob.garant) | ||||
| 			# FIXME: Možná poslat mail i relevantním orgům nadproblémů? | ||||
| 		# FIXME: Víc informativní obsah mailů, možná vč. příloh? | ||||
| 		send_mail( | ||||
| 			subject="Nové řešení k problému", | ||||
| 			message=f"Řešitel poslal řešení...", | ||||
| 			from_email="submitovatko@mam.mff.cuni.cz", # FIXME: Chceme to mít radši tady, nebo v nastavení? | ||||
| 			recipient_list=list(prijemci), | ||||
| 			) | ||||
| 
 | ||||
| 		return HttpResponseRedirect(self.get_success_url()) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1223,6 +1239,8 @@ class PasswordResetView(auth_views.PasswordResetView): | |||
| 	# TODO: vlastní email_template_name a subject_template_name a html_email_template_name | ||||
| 	success_url = reverse_lazy('reset_password_done') | ||||
| 	from_email = 'login@mam.mff.cuni.cz' | ||||
| 	email_template_name = 'seminar/registrace/password_reset_email.html' | ||||
| 	subject_template_name = 'seminar/registrace/password_reset_subject.txt' | ||||
| 
 | ||||
| class PasswordResetDoneView(auth_views.PasswordResetDoneView): | ||||
| 	""" Poslali jsme e-mail (pokud bylo kam)). """ | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ from seminar.utils import aktivniResitele, resi_v_rocniku, cisla_rocniku, hlavni | |||
| import time | ||||
| ### Výsledky | ||||
| 
 | ||||
| ROCNIK_ZRUSENI_TEMAT = 25 | ||||
| 
 | ||||
| def sloupec_s_poradim(setrizene_body): | ||||
| 	""" | ||||
| 	Ze seznamu obsahujícího sestupně setřízené body řešitelů za daný ročník  | ||||
|  | @ -255,7 +257,10 @@ def secti_body_za_cislo(cislo, aktivni_resitele, hlavni_problemy=None): | |||
| 		inst = problem.get_real_instance() | ||||
| 		return not(isinstance(inst, m.Clanek) or isinstance(inst, m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 	if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: | ||||
| 		temata_a_spol = hlavni_problemy | ||||
| 	else: | ||||
| 		temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	hlavni_problemy_slovnik = {} | ||||
| 	for hp in temata_a_spol: | ||||
|  | @ -410,7 +415,10 @@ def vysledkovka_cisla(cislo, context=None): | |||
| 		 | ||||
| 		return not(isinstance(problem.get_real_instance(), m.Clanek) or isinstance(problem.get_real_instance(), m.Konfera)) | ||||
| 
 | ||||
| 	temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 	if cislo.rocnik.rocnik < ROCNIK_ZRUSENI_TEMAT: | ||||
| 		temata_a_spol = hlavni_problemy | ||||
| 	else: | ||||
| 		temata_a_spol = list(filter(ne_clanek_ne_konfera, hlavni_problemy)) | ||||
| 
 | ||||
| 	# získáme body u jednotlivých témat | ||||
| 	podproblemy = podproblemy_v_cislu(cislo, problemy, temata_a_spol) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jonas Havelka
						Jonas Havelka