Browse Source

Submitovatko: pomerne funkcni verze.

export_seznamu_prednasek
parent
commit
62b0b4a62f
  1. 24
      seminar/forms.py
  2. 24
      seminar/migrations/0074_auto_20200228_1401.py
  3. 18
      seminar/migrations/0075_auto_20200228_2010.py
  4. 19
      seminar/migrations/0076_auto_20200228_2013.py
  5. 30
      seminar/models.py
  6. 49
      seminar/static/seminar/dynamic_formsets.js
  7. 44
      seminar/templates/seminar/nahraj_reseni.html
  8. 2
      seminar/urls.py
  9. 49
      seminar/views.py

24
seminar/forms.py

@ -2,6 +2,7 @@ from django import forms
from dal import autocomplete from dal import autocomplete
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.forms.models import inlineformset_factory
from .models import Skola, Resitel, Osoba, Problem from .models import Skola, Resitel, Osoba, Problem
import seminar.models as m import seminar.models as m
@ -252,6 +253,25 @@ class VlozReseniForm(forms.Form):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
#self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()]) #self.fields['favorite_color'] = forms.ChoiceField(choices=[(color.id, color.name) for color in Resitel.objects.all()])
class NahrajReseniForm(forms.ModelForm):
class Meta:
model = m.Reseni
fields = ('problem',)
widgets = {'problem':
autocomplete.ModelSelect2Multiple(
url='autocomplete_problem_odevzdatelny',
attrs = {'data-placeholder--id': '-1',
'data-placeholder--text' : '---',
'data-allow-clear': 'true'},
)
}
ReseniSPrilohamiFormSet = inlineformset_factory(m.Reseni,m.PrilohaReseni,
form = NahrajReseniForm,
fields = ('soubor','res_poznamka'),
widgets = {'res_poznamka':forms.TextInput()},
extra = 1,
)

24
seminar/migrations/0074_auto_20200228_1401.py

@ -0,0 +1,24 @@
# Generated by Django 2.2.9 on 2020-02-28 13:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('seminar', '0073_copy_osoba_email_to_user_email'),
]
operations = [
migrations.AddField(
model_name='prilohareseni',
name='res_poznamka',
field=models.TextField(blank=True, help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje', verbose_name='poznámka řešitele'),
),
migrations.AlterField(
model_name='hodnoceni',
name='problem',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.Problem', verbose_name='problém'),
),
]

18
seminar/migrations/0075_auto_20200228_2010.py

@ -0,0 +1,18 @@
# Generated by Django 2.2.9 on 2020-02-28 19:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('seminar', '0074_auto_20200228_1401'),
]
operations = [
migrations.AlterField(
model_name='hodnoceni',
name='body',
field=models.DecimalField(decimal_places=1, max_digits=8, null=True, verbose_name='body'),
),
]

19
seminar/migrations/0076_auto_20200228_2013.py

@ -0,0 +1,19 @@
# Generated by Django 2.2.9 on 2020-02-28 19:13
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('seminar', '0075_auto_20200228_2010'),
]
operations = [
migrations.AlterField(
model_name='hodnoceni',
name='cislo_body',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='hodnoceni', to='seminar.Cislo', verbose_name='číslo pro body'),
),
]

30
seminar/models.py

@ -920,10 +920,10 @@ class Hodnoceni(SeminarModelBase):
body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body', body = models.DecimalField(max_digits=8, decimal_places=1, verbose_name='body',
blank=False, null=False) blank=False, null=True)
cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body', cislo_body = models.ForeignKey(Cislo, verbose_name='číslo pro body',
related_name='hodnoceni', blank=False, null=False, on_delete=models.PROTECT) related_name='hodnoceni', blank=False, null=True, on_delete=models.PROTECT)
reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE) reseni = models.ForeignKey(Reseni, verbose_name='řešení', on_delete=models.CASCADE)
@ -935,17 +935,16 @@ class Hodnoceni(SeminarModelBase):
## FIXME: Budeme řešit později, pokud to bude potřeba. def aux_generate_filename(self, filename):
#def aux_generate_filename(self, filename): """Pomocná funkce generující ošetřený název souboru v adresáři s datem"""
# """Pomocná funkce generující ošetřený název souboru v adresáři s datem""" clean = get_valid_filename(
# clean = get_valid_filename( unidecode(filename.replace('/', '-').replace('\0', ''))
# unidecode(filename.replace('/', '-').replace('\0', '')) )
# ) datedir = timezone.now().strftime('%Y-%m')
# datedir = timezone.now().strftime('%Y-%m') fname = "{}_{}".format(
# fname = "%s_%s" % ( timezone.now().strftime('%Y-%m-%d-%H:%M'),
# timezone.now().strftime('%Y-%m-%d-%H:%M'), clean)
# clean) return os.path.join(datedir, fname)
# return os.path.join(datedir, fname)
# Django neumí jednoduše serializovat partial nebo třídu s __call__ # Django neumí jednoduše serializovat partial nebo třídu s __call__
# (https://docs.djangoproject.com/en/1.8/topics/migrations/), # (https://docs.djangoproject.com/en/1.8/topics/migrations/),
@ -988,9 +987,12 @@ class PrilohaReseni(SeminarModelBase):
poznamka = models.TextField('neveřejná poznámka', blank=True, poznamka = models.TextField('neveřejná poznámka', blank=True,
help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu') help_text='Neveřejná poznámka k příloze řešení (plain text), např. o původu')
res_poznamka = models.TextField('poznámka řešitele', blank=True,
help_text='Poznámka k příloze řešení, např. co daný soubor obsahuje')
def __str__(self): def __str__(self):
return self.soubor return str(self.soubor)
class Pohadka(SeminarModelBase): class Pohadka(SeminarModelBase):

49
seminar/static/seminar/dynamic_formsets.js

@ -0,0 +1,49 @@
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
function updateElementIndex(el, prefix, ndx) {
var id_regex = new RegExp('(' + prefix + '-\\d+)');
var replacement = prefix + '-' + ndx;
if ($(el).attr("for")) {
$(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
}
if (el.id) {
el.id = el.id.replace(id_regex, replacement);
}
if (el.name) {
el.name = el.name.replace(id_regex, replacement);
}
}
// Credit https://medium.com/all-about-django/adding-forms-dynamically-to-a-django-formset-375f1090c2b0
function deleteForm(prefix, btn) {
var total = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
if (total >= 1){
btn.closest('div').remove();
var forms = $('.attachment');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
$(forms.get(i)).find(':input').each(function() {
updateElementIndex(this, prefix, i);
});
}
}
return false;
}
// Credit: https://simpleit.rocks/python/django/dynamic-add-form-with-add-button-in-django-modelformset-template/
$(document).ready(function(){
$('#add_attachment').click(function() {
var form_idx = $('#id_prilohy-TOTAL_FORMS').val();
var new_form = $('#empty_form').html().replace(/__prefix__/g, form_idx);
$('#form_set').append(new_form);
// Newly created form has not the binding between remove button and remove function
// We need to add it manually
$('.remove_attachment').click(function(){
deleteForm("prilohy",this);
});
$('#id_prilohy-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});
$('.remove_attachment').click(function(){
deleteForm("prilohy",this);
});
});

44
seminar/templates/seminar/nahraj_reseni.html

@ -0,0 +1,44 @@
{% extends "seminar/zadani/base.html" %}
{% load staticfiles %}
{% block script %}
<!--script type="text/javascript" src="{% static 'admin/js/vendor/jquery/jquery.js' %}"></script!-->
{{form.media}}
<script src="{% static 'seminar/dynamic_formsets.js' %}"></script>
{% endblock %}
{% block content %}
<h1>
{% block nadpis1a %}{% block nadpis1b %}
Vložit řešení
{% endblock %}{% endblock %}
</h1>
<form enctype="multipart/form-data" action="{% url 'seminar_nahraj_reseni' %}" method="post">
{% csrf_token %}
{{form}}
{{prilohy.management_form}}
<div id="form_set">
{% for form in prilohy.forms %}
<div class="attachment">
{{form.non_field_errors}}
{{form.errors}}
<table class='no_error'>
{{ form }}
</table>
<input type="button" value="Odebrat" class="remove_attachment" id="{{form.prefix}}-jsremove">
</div>
{% endfor %}
</div>
<input type="button" value="Přidat přílohu" id="add_attachment">
<div id="empty_form" style="display:none">
<div class="attachment">
<table class='no_error'>
{{ prilohy.empty_form }}
</table>
<input type="button" value="Odebrat" class="remove_attachment" id="id_prilohy-__prefix__-jsremove">
</div>
</div>
<input type="submit" value="Odevzdat">
</form>
{% endblock %}

2
seminar/urls.py

@ -109,6 +109,7 @@ urlpatterns = [
path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'), path('auth/resitel/', views.ResitelView.as_view(), name='seminar_resitel'),
path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'), path('autocomplete/skola/',views.SkolaAutocomplete.as_view(), name='autocomplete_skola'),
path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'), path('autocomplete/resitel/',views.ResitelAutocomplete.as_view(), name='autocomplete_resitel'),
path('autocomplete/problem/odevzdatelny',views.OdevzdatelnyProblemAutocomplete.as_view(), name='autocomplete_problem_odevzdatelny'),
path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'), path('auth/reset_password/', views.PasswordResetView.as_view(), name='reset_password'),
path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'), path('auth/change_password/', views.PasswordChangeView.as_view(), name='change_password'),
path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'), path('auth/reset_password_done/', views.PasswordResetDoneView.as_view(), name='reset_password_done'),
@ -118,6 +119,7 @@ urlpatterns = [
path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'), path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'),
path('temp/submit_solution', views.SubmitSolutionView.as_view(),name='seminar_nahraj_reseni'),
path('', views.TitulniStranaView.as_view(), name='titulni_strana'), path('', views.TitulniStranaView.as_view(), name='titulni_strana'),

49
seminar/views.py

@ -9,7 +9,7 @@ from django.utils.translation import ugettext as _
from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect from django.http import Http404,HttpResponseBadRequest,HttpResponseRedirect
from django.db.models import Q, Sum, Count from django.db.models import Q, Sum, Count
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.generic.edit import FormView from django.views.generic.edit import FormView, CreateView
from django.contrib.auth import authenticate, login, get_user_model, logout from django.contrib.auth import authenticate, login, get_user_model, logout
from django.contrib.auth import views as auth_views from django.contrib.auth import views as auth_views
from django.contrib.auth.models import User from django.contrib.auth.models import User
@ -1157,6 +1157,40 @@ class AddSolutionView(LoginRequiredMixin, FormView):
form_class = f.VlozReseniForm form_class = f.VlozReseniForm
success_url = '/' success_url = '/'
class SubmitSolutionView(LoginRequiredMixin, CreateView):
model = s.Reseni
template_name = 'seminar/nahraj_reseni.html'
form_class = f.NahrajReseniForm
def get_context_data(self,**kwargs):
data = super().get_context_data(**kwargs)
if self.request.POST:
data['prilohy'] = f.ReseniSPrilohamiFormSet(self.request.POST,self.request.FILES)
else:
data['prilohy'] = f.ReseniSPrilohamiFormSet()
return data
# FIXME prepsat tak, aby form_valid se volalo jen tehdy, kdyz je form i formset validni
# Inspirace: https://stackoverflow.com/questions/41599809/using-a-django-filefield-in-an-inline-formset
def form_valid(self,form):
context = self.get_context_data()
prilohy = context['prilohy']
with transaction.atomic():
self.object = form.save()
self.object.resitele.add(Resitel.objects.get(osoba__user = self.request.user))
self.object.cas_doruceni = datetime.now()
self.object.forma = s.Reseni.FORMA_UPLOAD
if prilohy.is_valid():
prilohy.instance = self.object
prilohy.save()
else:
raise Exception("Uploadovane soubory nebyly validni")
return HttpResponseRedirect(self.get_success_url())
def resetPasswordView(request): def resetPasswordView(request):
pass pass
@ -1349,6 +1383,19 @@ class ResitelAutocomplete(LoginRequiredAjaxMixin,autocomplete.Select2QuerySetVie
) )
return qs return qs
class OdevzdatelnyProblemAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
nastaveni = get_object_or_404(Nastaveni)
rocnik = nastaveni.aktualni_rocnik
temata = s.Tema.objects.filter(rocnik=rocnik, stav=s.Problem.STAV_ZADANY)
ulohy = s.Uloha.objects.filter(cislo_deadline__rocnik = rocnik)
ulohy.union(temata)
qs = ulohy
if self.q:
qs = qs.filter(
Q(nazev__startswith=self.q))
return qs
# Ceka na autocomplete v3 # Ceka na autocomplete v3
# class OrganizatorAutocomplete(autocomplete.Select2QuerySetView): # class OrganizatorAutocomplete(autocomplete.Select2QuerySetView):

Loading…
Cancel
Save