Merge pull request 'Vylepšení hodnotítka fix #1354 fix #1237' (!20) from vylepseni_odevzdavatka into master
Reviewed-on: #20
This commit is contained in:
		
						commit
						2ab6e76fbe
					
				
					 7 changed files with 204 additions and 11 deletions
				
			
		|  | @ -1255,3 +1255,8 @@ div.gdpr { | ||||||
| label[for=id_skola] { | label[for=id_skola] { | ||||||
| 	font-weight: bold; | 	font-weight: bold; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | /* detail řešení */ | ||||||
|  | .bodovani>input { | ||||||
|  | 	width: 4em; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -119,6 +119,24 @@ class JednoHodnoceniForm(forms.ModelForm): | ||||||
| 			'feedback': forms.Textarea(attrs={'rows': 1, 'cols': 30, 'class': 'feedback'}), | 			'feedback': forms.Textarea(attrs={'rows': 1, 'cols': 30, 'class': 'feedback'}), | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
|  | 	body_celkem = forms.DecimalField(required=False, decimal_places=1) | ||||||
|  | 	body_neprepocitane = forms.DecimalField(required=False, decimal_places=1) | ||||||
|  | 	body_neprepocitane_celkem = forms.DecimalField(required=False, decimal_places=1) | ||||||
|  | 
 | ||||||
|  | 	def __init__(self, *args, initial=None, **kwargs): | ||||||
|  | 		if initial is not None: | ||||||
|  | 			body_max = initial["body_max"] | ||||||
|  | 			body_neprepocitane_max = initial["body_neprepocitane_max"] | ||||||
|  | 			del(initial["body_max"]) | ||||||
|  | 			del(initial["body_neprepocitane_max"]) | ||||||
|  | 		super().__init__(*args, initial=initial, **kwargs) | ||||||
|  | 		if initial is not None: | ||||||
|  | 			self.fields['body'].widget.attrs['placeholder'] = body_max | ||||||
|  | 			self.fields['body_celkem'].widget.attrs['placeholder'] = body_max | ||||||
|  | 			self.fields['body_neprepocitane'].widget.attrs['placeholder'] = body_neprepocitane_max | ||||||
|  | 			self.fields['body_neprepocitane_celkem'].widget.attrs['placeholder'] = body_neprepocitane_max | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | OhodnoceniReseniFormSet = formset_factory(JednoHodnoceniForm, | ||||||
| 		extra = 0, | 		extra = 0, | ||||||
| 		) | 		) | ||||||
|  |  | ||||||
|  | @ -49,8 +49,18 @@ $(document).ready(function(){ | ||||||
|             $('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value |             $('#id_form-' + form_idx + '-deadline_body')[0].value = $('#id_form-' + (form_idx - 1) + '-deadline_body')[0].value | ||||||
|         } |         } | ||||||
| 		$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); | 		$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1); | ||||||
|  | 
 | ||||||
|  | 		$('.bodovani').children().change(function(){ | ||||||
|  | 			$(this).parent().parent().children(".bodovani").children().attr("disabled", true); | ||||||
|  | 			$(this).attr("disabled", false); | ||||||
|  | 		}) | ||||||
| 	}); | 	}); | ||||||
| 	$('.smazat_hodnoceni').click(function(){ | 	$('.smazat_hodnoceni').click(function(){ | ||||||
| 		deleteForm("form",this); | 		deleteForm("form",this); | ||||||
| 	}); | 	}); | ||||||
|  | 
 | ||||||
|  | 	$('.bodovani').children().change(function(){ | ||||||
|  | 		$(this).parent().parent().children(".bodovani").children().attr("disabled", true); | ||||||
|  | 		$(this).attr("disabled", false); | ||||||
|  | 	}) | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -4,6 +4,13 @@ | ||||||
| {% load mail %} | {% load mail %} | ||||||
| {% load jmena %} | {% load jmena %} | ||||||
| 
 | 
 | ||||||
|  | {# Přišlo mi to hezčí, než psát všude if. #} | ||||||
|  | {% block custom_css %} | ||||||
|  |   {% if object.resitele.count == 1 %} | ||||||
|  |     <style>.teamovaCast {display: none}</style> | ||||||
|  |   {% endif %} | ||||||
|  | {% endblock %} | ||||||
|  | 
 | ||||||
| {% block content %} | {% block content %} | ||||||
| 
 | 
 | ||||||
|   {% if edit %} |   {% if edit %} | ||||||
|  | @ -76,6 +83,22 @@ | ||||||
| <h3>Neveřejná poznámka:</h3> | <h3>Neveřejná poznámka:</h3> | ||||||
| <p>{{ poznamka_form.poznamka }}</p> | <p>{{ poznamka_form.poznamka }}</p> | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | <script>vporadku=true;</script> | ||||||
|  | {% for h in hodnoceni %}{% if h.body < 0.0 %} | ||||||
|  |   <script> | ||||||
|  |     if(vporadku){ | ||||||
|  |       vporadku=false; | ||||||
|  |       alert( | ||||||
|  |         "Pozor! Některé hodnocení má záporné body.\n\n" + | ||||||
|  |         "Buď jde o záměr, nebo o špatné zadáný počet bodů (např. součet bodů za úlohu) nebo se něco pokazilo.\n\n" + | ||||||
|  |         "Pokud se to děje neočekávaně a opakovaně, napiš webařům :)" | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |   </script> | ||||||
|  | {% endif %}{% endfor %} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| {# Hodnocení: #} | {# Hodnocení: #} | ||||||
| <h3>Hodnocení:</h3> | <h3>Hodnocení:</h3> | ||||||
| <table> | <table> | ||||||
|  | @ -83,12 +106,15 @@ | ||||||
| {{ form.management_form }} | {{ form.management_form }} | ||||||
| </table> | </table> | ||||||
| <table id="form_set"> | <table id="form_set"> | ||||||
| <tr><th>Problém</th><th>Body</th><th>Deadline pro body</th><th>Zpětná vazba pro řešitele</th></tr> | <tr><th>Problém</th><th>{# 📖 #}🧍</th><th>{# 🔵 #}🧍∑</th><th class="teamovaCast">{# 💪 #}🧑🤝🧑</th><th class="teamovaCast">{# ❤ #}🧑🤝🧑∑</th><th>Deadline pro body</th><th>Zpětná vazba pro řešitele</th></tr> | ||||||
| {% for subform in form %} | {% for subform in form %} | ||||||
|     <tbody> |     <tbody> | ||||||
| 	<tr class="hodnoceni"> | 	<tr class="hodnoceni"> | ||||||
| 		<td>{{ subform.problem }}</td> | 		<td>{{ subform.problem }}</td> | ||||||
| 		<td>{{ subform.body }}</td> | 		<td class="bodovani">{{ subform.body }}</td> | ||||||
|  | 		<td class="bodovani">{{ subform.body_celkem }}</td> | ||||||
|  | 		<td class="bodovani teamovaCast">{{ subform.body_neprepocitane }}</td> | ||||||
|  | 		<td class="bodovani teamovaCast">{{ subform.body_neprepocitane_celkem }}</td> | ||||||
| 		<td>{{ subform.deadline_body }}</td> | 		<td>{{ subform.deadline_body }}</td> | ||||||
| 		<td>{{ subform.feedback }}</td> | 		<td>{{ subform.feedback }}</td> | ||||||
| 		<td class="has_smazat_hodnoceni"><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove" title="Smazat hodnocení"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | 		<td class="has_smazat_hodnoceni"><a href="#" class="smazat_hodnoceni" id="id_{{subform.prefix}}-jsremove" title="Smazat hodnocení"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | ||||||
|  | @ -104,7 +130,10 @@ | ||||||
| <table id="empty_form" style="display: none;"> | <table id="empty_form" style="display: none;"> | ||||||
| 	<tr class="hodnoceni"> | 	<tr class="hodnoceni"> | ||||||
| 		<td>{{ form.empty_form.problem }}</td> | 		<td>{{ form.empty_form.problem }}</td> | ||||||
| 		<td>{{ form.empty_form.body }}</td> | 		<td class="bodovani">{{ form.empty_form.body }}</td> | ||||||
|  | 		<td class="bodovani">{{ form.empty_form.body_celkem }}</td> | ||||||
|  | 		<td class="bodovani teamovaCast">{{ form.empty_form.body_neprepocitane }}</td> | ||||||
|  | 		<td class="bodovani teamovaCast">{{ form.empty_form.body_neprepocitane_celkem }}</td> | ||||||
| 		<td>{{ form.empty_form.deadline_body }}</td> | 		<td>{{ form.empty_form.deadline_body }}</td> | ||||||
| 		<td>{{ form.empty_form.feedback }}</td> | 		<td>{{ form.empty_form.feedback }}</td> | ||||||
| 		<td class="has_smazat_hodnoceni"><a href="#" class="smazat_hodnoceni" id="id_{{form.empty_form.prefix}}-jsremove" title="Smazat hodnocení"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | 		<td class="has_smazat_hodnoceni"><a href="#" class="smazat_hodnoceni" id="id_{{form.empty_form.prefix}}-jsremove" title="Smazat hodnocení"><img src="{% static "odevzdavatko/cross.png" %}" alt="Smazat"></a></td> | ||||||
|  | @ -114,16 +143,61 @@ | ||||||
|   {% else %} |   {% else %} | ||||||
| <h3>Hodnocení:</h3> | <h3>Hodnocení:</h3> | ||||||
| <table class="dosla_reseni"> | <table class="dosla_reseni"> | ||||||
| <tr><th>Problém</th><th>Body</th><th>Zpětná vazba od opravovatele</th></tr> | <tr><th>Problém</th><th>{# 📖 #}🧍</th><th>{# 🔵 #}🧍∑</th><th class="teamovaCast">{# 💪 #}🧑🤝🧑</th><th class="teamovaCast">{# ❤ #}🧑🤝🧑∑</th><th>Zpětná vazba od opravovatele</th></tr> | ||||||
| {% for h in hodnoceni %} | {% for h in hodnoceni %} | ||||||
| 	<tr class="hodnoceni"> | 	<tr class="hodnoceni"> | ||||||
| 		<td>{{ h.problem }}</td> | 		<td>{{ h.problem }}</td> | ||||||
| 		<td>{{ h.body }}</td> | 		<td class="bodovani">{{ h.body }}</td> | ||||||
|  | 		<td class="bodovani">{{ h.body_celkem }}</td> | ||||||
|  | 		<td class="bodovani teamovaCast">{{ h.body_neprepocitane }}</td> | ||||||
|  | 		<td class="bodovani teamovaCast">{{ h.body_neprepocitane_celkem }}</td> | ||||||
| 		<td>{{ h.feedback | linebreaks }}</td> | 		<td>{{ h.feedback | linebreaks }}</td> | ||||||
| 	</tr> | 	</tr> | ||||||
| {% endfor %} | {% endfor %} | ||||||
| </table> | </table> | ||||||
|   {% endif %} |   {% endif %} | ||||||
| 
 | 
 | ||||||
|  | <h3>Vysvětlivky:</h3> | ||||||
|  | <dl> | ||||||
|  |   <dt>{# 📖 #}🧍</dt> | ||||||
|  |   <dd>Body za toto řešení.</dd> | ||||||
|  | 
 | ||||||
|  |   <dt>{# 🔵 #}🧍∑</dt> | ||||||
|  |   <dd>Body za tento problém/úlohu (součet za všechna řešení).</dd> | ||||||
|  | 
 | ||||||
|  |   <dt class="teamovaCast">{# 💪 #}🧑🤝🧑</dt> | ||||||
|  |   <dd class="teamovaCast">Body, které by dostal tým, kdyby to řešil jako jeden řešitel, za toto řešení.</dd> | ||||||
|  | 
 | ||||||
|  |   <dt class="teamovaCast">{# ❤ #}🧑🤝🧑∑</dt> | ||||||
|  |   <dd class="teamovaCast">Body, které by dostal tým, kdyby to řešil jako jeden řešitel, za tento problém/úlohu (součet za všechna řešení).</dd> | ||||||
|  | </dl> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | {% if edit %} | ||||||
|  | <h3>Návod pro hodnocení:</h3> | ||||||
|  | Sloupce: | ||||||
|  | <ol> | ||||||
|  |   <li>Pokud to neudělal řešitel, je třeba pomocí pluska přidat řádky (případně křížkem smazat) a vyplnit problémy tak, aby zde byly všechny, které řešení řeší (body zadáváme přímo k úlohám, ne k témátku samotnému).</li> | ||||||
|  |   <li>Pak je třeba do jednoho ze 2 nebo 4 sloupců vyplnit body (lze udělovat desetiny, setiny už udělovat nejde): | ||||||
|  |     <ul> | ||||||
|  |       <li>TLDR: pokud si počítáš a kontroluješ vše sám, vyplňuj do nejlevějšího. Pokud naopak vždy vyplňuješ to, kolik řešení má dostat bodů (bez ohledu na počet řešitelů a předchozí odevzdání), vyplňuj nejpravější.</li> | ||||||
|  |       <li>Zaprvé je třeba dávat pozor, že řešitel už mohl dostat body za danou úlohu (to je rozdíl mezi lichými a sudými sloupci).</li> | ||||||
|  |       <li>Zadruhé řešení, na kterém se spolupracovalo, dostává body přepočítané podle vzorečku <a href="https://mam.matfyz.cz/jak-resit/podrobneji/">zde dole</a>. To dělá rozdíl mezi prvními a druhými dvěma sloupci, pokud se oboje zobrazují.</li> | ||||||
|  |       <li>Co který sloupec znamená, je napsáno výše ve vysvětlivkách. | ||||||
|  |     </ul> | ||||||
|  |   </li> | ||||||
|  |   <li>Pokud nemáš důvod, deadline neměň. Sloupeček s deadlinem znamená, do kterého deadlinu se započítají body (nemusí se shodovat s deadlinem řešení).</li> | ||||||
|  |   <li>Poslední sloupec je na zpětnou vazbu řešiteli, tedy (na rozdíl od Neveřejné poznámky, která je určena pro synchronizaci orgů) ji uvidí řešitelé. Zatím jen pasivně (nechodí e-mail). Pohled řešitele si můžete prohlédnout <a href="{% url 'odevzdavatko_resitel_reseni' reseni.id %}">zde</a>. Pokud chcete z nějakého důvodu napsat řešitelům e-mail, klikněte na „Poslat mail všem řešitelům“.</li> | ||||||
|  | </ol> | ||||||
|  | 
 | ||||||
|  | Další poznámky | ||||||
|  | <ul> | ||||||
|  |   <li>Pokud chceš zadané body smazat (rozmyslel sis to a ohodnotíš to později), smaž body v libovolném sloupeci.</li> | ||||||
|  |   <li>Ne, soubory si zatím nejde stáhnout lépe než proklikáním všech řešeních. Stejně tak nejde hromadně bodovat. Třeba někdy půjde.</li> | ||||||
|  |   <li>Pokud řešitel odevzdal něco nesouvisejícího, nebo něco duplicitně, tak mu za to dejte nulu a jako problém nastavte něco, co odevzdal (ať se mu ve výsledkovce nezobrazuje 0 na špatném místě). A upozorni ho.</li> | ||||||
|  |   <li>Ano, lze zadávat záporné body (např. za podvádění), web vás bude silně upozorňovat, ale jinak mu to nevadí.</li> | ||||||
|  |   <li>Libovolné problémy s hodnotítkem řeš s {% maillink 'webaři' to='web@mam.mff.cuni.cz' subject='Hodnotítko' %}.</li> | ||||||
|  | </ul> | ||||||
|  | {% endif %} | ||||||
| 
 | 
 | ||||||
| {% endblock %} | {% endblock %} | ||||||
|  |  | ||||||
|  | @ -224,12 +224,18 @@ class DetailReseniView(DetailView): | ||||||
| 		self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | 		self.reseni = get_object_or_404(m.Reseni, id=self.kwargs['pk']) | ||||||
| 		result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet | 		result = [] # Slovníky s klíči problem, body, deadline_body -- initial data pro f.OhodnoceniReseniFormSet | ||||||
| 		for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): | 		for hodn in m.Hodnoceni.objects.filter(reseni=self.reseni): | ||||||
| 			result.append({ | 			seznam_atributu = [ | ||||||
| 				"problem": hodn.problem, | 				"problem", | ||||||
| 				"body": hodn.body, | 				"body", | ||||||
| 				"deadline_body": hodn.deadline_body, | 				"body_celkem", | ||||||
| 				"feedback": hodn.feedback, | 				"body_neprepocitane", | ||||||
| 				}) | 				"body_neprepocitane_celkem", | ||||||
|  | 				"body_max", | ||||||
|  | 				"body_neprepocitane_max", | ||||||
|  | 				"deadline_body", | ||||||
|  | 				"feedback", | ||||||
|  | 			] | ||||||
|  | 			result.append({attr: getattr(hodn, attr) for attr in seznam_atributu}) | ||||||
| 		return result | 		return result | ||||||
| 
 | 
 | ||||||
| 	def get_context_data(self, **kw): | 	def get_context_data(self, **kw): | ||||||
|  | @ -296,11 +302,23 @@ def hodnoceniReseniView(request, pk, *args, **kwargs): | ||||||
| 
 | 
 | ||||||
| 		# Vyrobíme nová podle formsetu | 		# Vyrobíme nová podle formsetu | ||||||
| 		for form in formset: | 		for form in formset: | ||||||
|  | 			data_for_hodnoceni = form.cleaned_data | ||||||
|  | 			data_for_body = data_for_hodnoceni.copy() | ||||||
|  | 			del(data_for_hodnoceni["body_celkem"]) | ||||||
|  | 			del(data_for_hodnoceni["body_neprepocitane"]) | ||||||
|  | 			del(data_for_hodnoceni["body_neprepocitane_celkem"]) | ||||||
| 			hodnoceni = m.Hodnoceni( | 			hodnoceni = m.Hodnoceni( | ||||||
| 					reseni=reseni, | 					reseni=reseni, | ||||||
| 					**form.cleaned_data, | 					**form.cleaned_data, | ||||||
| 					) | 					) | ||||||
| 			logger.info(f"Creating Hodnoceni: {hodnoceni}") | 			logger.info(f"Creating Hodnoceni: {hodnoceni}") | ||||||
|  | 			zmeny_bodu = [it for it in form.changed_data if it.startswith("body")] | ||||||
|  | 			if len(zmeny_bodu) == 1: | ||||||
|  | 				hodnoceni.__setattr__(zmeny_bodu[0], data_for_body[zmeny_bodu[0]]) | ||||||
|  | 			# > jedna změna je špatně, ale 4 "změny" znamenají že nebylo nic zadáno | ||||||
|  | 			if len(zmeny_bodu) > 1 and len(zmeny_bodu) != 4: | ||||||
|  | 				logger.warning(f"Hodnocení {hodnoceni} mělo mít nastavené víc různých bodů: {zmeny_bodu}. Nastavuji -0.1.") | ||||||
|  | 				hodnoceni.body = -0.1 | ||||||
| 			hodnoceni.save() | 			hodnoceni.save() | ||||||
| 
 | 
 | ||||||
| 	return redirect(success_url) | 	return redirect(success_url) | ||||||
|  |  | ||||||
|  | @ -14,6 +14,8 @@ from seminar.models import personalni as pm | ||||||
| from seminar.models import treenode as tm | from seminar.models import treenode as tm | ||||||
| from seminar.models import base as bm | from seminar.models import base as bm | ||||||
| 
 | 
 | ||||||
|  | from seminar.utils import vzorecek_na_prepocet, inverze_vzorecku_na_prepocet | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| @reversion.register(ignore_duplicates=True) | @reversion.register(ignore_duplicates=True) | ||||||
| class Reseni(bm.SeminarModelBase): | class Reseni(bm.SeminarModelBase): | ||||||
|  | @ -115,6 +117,61 @@ class Hodnoceni(bm.SeminarModelBase): | ||||||
| 
 | 
 | ||||||
| 	feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') | 	feedback = models.TextField('zpětná vazba', blank=True, default='', help_text='Zpětná vazba řešiteli (plain text)') | ||||||
| 
 | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_celkem(self): | ||||||
|  | 		# FIXME řeším jen prvního řešitele. | ||||||
|  | 		return Hodnoceni.objects.filter(problem=self.problem, reseni__resitele=self.reseni.resitele.first(), body__isnull=False).aggregate(Sum("body"))["body__sum"] | ||||||
|  | 
 | ||||||
|  | 	@body_celkem.setter | ||||||
|  | 	def body_celkem(self, value): | ||||||
|  | 		if value is None: | ||||||
|  | 			self.body = None | ||||||
|  | 		else: | ||||||
|  | 			if self.body is None: | ||||||
|  | 				self.body = 0 | ||||||
|  | 			if self.body_celkem is None: | ||||||
|  | 				self.body += value | ||||||
|  | 			else: | ||||||
|  | 				self.body += value - self.body_celkem | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_neprepocitane(self): | ||||||
|  | 		if self.body is None: | ||||||
|  | 			return None | ||||||
|  | 		return inverze_vzorecku_na_prepocet(self.body, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@body_neprepocitane.setter | ||||||
|  | 	def body_neprepocitane(self, value): | ||||||
|  | 		if value is None: | ||||||
|  | 			self.body = None | ||||||
|  | 		else: | ||||||
|  | 			self.body = vzorecek_na_prepocet(value, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_neprepocitane_celkem(self): | ||||||
|  | 		if self.body_celkem is None: | ||||||
|  | 			return None | ||||||
|  | 		return inverze_vzorecku_na_prepocet(self.body_celkem, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@body_neprepocitane_celkem.setter | ||||||
|  | 	def body_neprepocitane_celkem(self, value): | ||||||
|  | 		if value is None: | ||||||
|  | 			self.body = None | ||||||
|  | 		else: | ||||||
|  | 			self.body_celkem = vzorecek_na_prepocet(value, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_max(self): | ||||||
|  | 		if self.body_neprepocitane_max is None: | ||||||
|  | 			return None | ||||||
|  | 		return vzorecek_na_prepocet(self.body_neprepocitane_max, self.reseni.resitele.count()) | ||||||
|  | 
 | ||||||
|  | 	@property | ||||||
|  | 	def body_neprepocitane_max(self): | ||||||
|  | 		if not isinstance(self.problem.get_real_instance(), am.Uloha): | ||||||
|  | 			return None | ||||||
|  | 		return self.problem.uloha.max_body | ||||||
|  | 
 | ||||||
| 	def __str__(self): | 	def __str__(self): | ||||||
| 		return "{}, {}, {}".format(self.problem, self.reseni, self.body) | 		return "{}, {}, {}".format(self.problem, self.reseni, self.body) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| 
 | 
 | ||||||
| import datetime | import datetime | ||||||
|  | import decimal | ||||||
| 
 | 
 | ||||||
| from django.contrib.auth import get_user_model | from django.contrib.auth import get_user_model | ||||||
| from django.contrib.auth.decorators import permission_required, \ | from django.contrib.auth.decorators import permission_required, \ | ||||||
|  | @ -44,6 +45,16 @@ AnonymousUser.je_org = False | ||||||
| AnonymousUser.je_resitel = 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ů. """ | ||||||
|  | 	return body * 3 / (resitelu + 2) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def inverze_vzorecku_na_prepocet(body: decimal.Decimal, resitelu) -> decimal.Decimal: | ||||||
|  | 	""" Vzoreček na přepočet parciálních bodů na plné, když má řešení více řešitelů. """ | ||||||
|  | 	return round(body * (resitelu + 2) / 3, 1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class FirstTagParser(HTMLParser): | class FirstTagParser(HTMLParser): | ||||||
| 	def __init__(self, *args, **kwargs): | 	def __init__(self, *args, **kwargs): | ||||||
| 		self.firstTag = None | 		self.firstTag = None | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue