From 6a64f57e45dc290ba8d5c09e3f1dffc29509f38b Mon Sep 17 00:00:00 2001 From: Andrea Carmisciano Date: Wed, 25 Sep 2019 15:55:48 +0200 Subject: [PATCH 01/79] bypass pdf generation --- base/files.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/base/files.py b/base/files.py index 155db5454..2145164e5 100755 --- a/base/files.py +++ b/base/files.py @@ -143,6 +143,13 @@ def genera_e_salva_con_python(self, nome='File.pdf', scadenza=None, corpo={}, def genera_e_salva(self, nome='File.pdf', scadenza=None, corpo={}, modello='pdf_vuoto.html', orientamento=ORIENTAMENTO_VERTICALE, formato=FORMATO_A4, posizione='allegati/'): + return self.genera_e_salva_con_python(nome=nome, scadenza=scadenza, corpo=corpo, + modello=modello, orientamento=orientamento, + formato=formato, posizione=posizione) + + def genera_e_salva_external(self, nome='File.pdf', scadenza=None, corpo={}, modello='pdf_vuoto.html', + orientamento=ORIENTAMENTO_VERTICALE, formato=FORMATO_A4, + posizione='allegati/'): """ Genera un file PDF con i parametri specificati e salva. :param nome: Il nome del file PDF da salvare. From 7a9db683b459e0affddae46febbacd1547eaee49 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 25 Sep 2019 15:21:12 +0200 Subject: [PATCH 02/79] =?UTF-8?q?FIXED:=20(related=20to=20GAIA-190)=20quer?= =?UTF-8?q?yset=20error=20perch=C3=A8=20nel=5Fraggio()=20restituiva=20empt?= =?UTF-8?q?y=20queryset=20di=20un=20altro=20tipo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base/geo.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/geo.py b/base/geo.py index 36d793861..1d8db8520 100755 --- a/base/geo.py +++ b/base/geo.py @@ -240,7 +240,13 @@ def nel_raggio(self, ricerca): Filtra una ricerca, per gli elementi nel raggio di questo elemento. :return: La ricerca filtrata """ + from formazione.models import CorsoBase + if not self.locazione: + # Check per restituire dello stesso tipo di oggetto + if ricerca.model is CorsoBase: + return CorsoBase.objects.none() + return self.__class__.objects.none() q = ricerca.filter(locazione__geo__distance_lte=(self.locazione.geo, D(km=self.raggio))) From b3c3a85891481eb443e8fb2b07f6a71f2d83a856 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 25 Sep 2019 15:41:02 +0200 Subject: [PATCH 03/79] =?UTF-8?q?FIXED:=20(GAIA-193)=20possibilit=C3=A0=20?= =?UTF-8?q?di=20attivare=20corso=20se=20presidente=20e=20direttore=20sono?= =?UTF-8?q?=20la=20stessa=20persona?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- formazione/models.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/formazione/models.py b/formazione/models.py index 7bf0e95ca..29e890931 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -1171,22 +1171,19 @@ def can_modify(self, me): return False def can_activate(self, me): - if me.is_presidente: - """ All'presidente deve sparire la sezione dell'attivazione corso se: - - ha caricato delibera - - ha impostato estensioni (/aspirante/corso-base//estensioni/) - - ha nominato almeno un direttore - """ - has_delibera = self.delibera_file is not None - has_extension = self.has_extensions() - has_directors = self.direttori_corso().count() > 0 - is_all_true = has_delibera, has_extension, has_directors - - # # Deve riapparire se: il direttore ha inserito la descrizione + if me.is_presidente or (me.is_presidente and me in self.direttori_corso()): + has_delibera = self.delibera_file is not None # ha caricato delibera + # has_extension = self.has_extensions() + has_directors = self.direttori_corso().count() > 0 # ha nominato almeno un direttore + is_all_true = has_delibera, has_directors # has_extension, + + # Deve riapparire se: il direttore ha inserito la descrizione # if self.descrizione: # return True - # else: - return True if False in is_all_true else False + + if False in is_all_true: + return False + return True else: """ Direttori del corso vedono sempre la sezione invece """ return True From 601c6e304af2ae8819408c3597a854f03b198f90 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 25 Sep 2019 15:49:57 +0200 Subject: [PATCH 04/79] FIXED: (GAIA-196) nuovo attestato mostra testo dal campo Titolo.scheda_competenze_in_uscita --- formazione/templates/pdf_corso_attestato.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/formazione/templates/pdf_corso_attestato.html b/formazione/templates/pdf_corso_attestato.html index c5504284c..6c06952fe 100644 --- a/formazione/templates/pdf_corso_attestato.html +++ b/formazione/templates/pdf_corso_attestato.html @@ -133,8 +133,7 @@

Contenuti:

Tipologia verifica finale:

-

La verifica individuale di fine corso dovrà prevedere:
- Test scritto per valutare le conoscenze acquisite; Un colloquio individuale volto all’autovalutazione, all’analisi condivisa delle performance e della partecipazione durante il percorso formativo, alla condivisione del vissuto personale e di gruppo; Una breve prova pratica con simulazione di intervento didattico e gestione d’aula; Tirocinio in affiancamento a personale esperto in didattica nelle prime attività da trainer (almeno 5 affiancamenti) e nella prima direzione di corso.

+

{{ corso.titolo_cri.scheda_competenze_in_uscita }}

{% if corso.tipo == Corso.BASE %} From 04e85f92796c55e437ca0d6db3fbbde9174df1e6 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 26 Sep 2019 09:49:32 +0200 Subject: [PATCH 05/79] ADDED: libreria django-prettyjson per facilitare il lavoro con il campo Titolo.scheda_lezioni --- curriculum/admin.py | 6 ++++++ jorvik/settings.py | 1 + requirements.txt | 1 + 3 files changed, 8 insertions(+) diff --git a/curriculum/admin.py b/curriculum/admin.py index 0a3447517..fb67c3940 100755 --- a/curriculum/admin.py +++ b/curriculum/admin.py @@ -1,4 +1,7 @@ from django.contrib import admin +from django.contrib.postgres.fields import JSONField + +from prettyjson import PrettyJSONWidget from base.admin import InlineAutorizzazione from gruppi.readonly_admin import ReadonlyAdminMixin @@ -12,6 +15,9 @@ class AdminTitolo(ReadonlyAdminMixin, admin.ModelAdmin): 'inseribile_in_autonomia', 'expires_after', 'scheda_prevede_esame',) list_filter = ('is_active', 'cdf_livello', 'area', "tipo", "richiede_conferma", "inseribile_in_autonomia", 'goal__unit_reference', 'scheda_prevede_esame',) + formfield_overrides = { + JSONField: {'widget': PrettyJSONWidget} + } def goal_obbiettivo_stragetico(self, obj): return obj.goal.unit_reference if hasattr(obj.goal, diff --git a/jorvik/settings.py b/jorvik/settings.py index 8c3b94759..5b407a233 100755 --- a/jorvik/settings.py +++ b/jorvik/settings.py @@ -70,6 +70,7 @@ 'filer', 'ckeditor', 'ckeditor_filebrowser_filer', + 'prettyjson', 'django_otp', 'django_otp.plugins.otp_static', diff --git a/requirements.txt b/requirements.txt index 79c94283c..98a7d1310 100755 --- a/requirements.txt +++ b/requirements.txt @@ -23,6 +23,7 @@ django_compressor==2.0 django-oidc-provider==0.6.2 django-loginas==0.3.1 django-otp==0.6.0 +django-prettyjson==0.4.1 djangorestframework==3.6 Markdown==2.6 phonenumbers==8.8.9 From 68d82bc2bd69f6eaf2d92aa023b6ff16b0a39145 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 26 Sep 2019 12:35:14 +0200 Subject: [PATCH 06/79] NEW: (GAIA-170) aggiungere corsi frequentati in Elenco corsi --- anagrafica/models.py | 16 +++++- ...e_corsi_base.html => aspirante_corsi.html} | 55 ++++++++++++++++--- ...irante_corso_base_scheda_informazioni.html | 2 - formazione/viste.py | 8 ++- 4 files changed, 66 insertions(+), 15 deletions(-) rename formazione/templates/{aspirante_corsi_base.html => aspirante_corsi.html} (56%) diff --git a/anagrafica/models.py b/anagrafica/models.py index 8e53e3368..412757ae5 100755 --- a/anagrafica/models.py +++ b/anagrafica/models.py @@ -676,15 +676,25 @@ def partecipazione_corso_base(self): from formazione.models import PartecipazioneCorsoBase, CorsoBase return PartecipazioneCorsoBase.con_esito_ok().filter(persona=self, corso__stato=CorsoBase.ATTIVO).first() - def richieste_di_partecipazione(self): - """ Restituisce richieste di partecipazione confermate e in attesa """ + def richieste_di_partecipazione(self, corso_stato=None): + """ Restituisce richieste di partecipazione ai corsi confermate e in attesa """ from formazione.models import PartecipazioneCorsoBase, CorsoBase + if corso_stato is None: + corso_stato = [CorsoBase.ATTIVO,] confirmed = PartecipazioneCorsoBase.con_esito_ok() pending = PartecipazioneCorsoBase.con_esito_pending() requests_to_courses = confirmed | pending - return requests_to_courses.filter(persona=self, corso__stato=CorsoBase.ATTIVO) + return requests_to_courses.filter(persona=self, corso__stato__in=corso_stato) + + @property + def corsi_frequentati(self): + from formazione.models import CorsoBase + return CorsoBase.objects.filter( + pk__in=[i.corso.pk for i in self.richieste_di_partecipazione( + corso_stato=[CorsoBase.TERMINATO,])] + ).order_by('-data_esame') @property def volontario_da_meno_di_un_anno(self): diff --git a/formazione/templates/aspirante_corsi_base.html b/formazione/templates/aspirante_corsi.html similarity index 56% rename from formazione/templates/aspirante_corsi_base.html rename to formazione/templates/aspirante_corsi.html index 2844f4b23..89f5d7da4 100644 --- a/formazione/templates/aspirante_corsi_base.html +++ b/formazione/templates/aspirante_corsi.html @@ -11,16 +11,14 @@ .aspirante-corsi-table thead tr th {vertical-align:top;} -

Corsi {% if me.volontario %}attivi{%else%}nelle tue vicinanze{%endif%}

{% if me.aspirante %} -
- - Questo è un elenco dei Corsi nel raggio di {{ me.aspirante.raggio|default:"0" }} km - da {{ me.aspirante.locazione|default:"n/a" }}. Puoi modificare la posizione dal menu "Aspirante" > "Impostazioni". -
- {%endif%} +
+ Questo è un elenco dei Corsi nel raggio di {{ me.aspirante.raggio|default:"0" }} km + da {{ me.aspirante.locazione|default:"n/a" }}. Puoi modificare la posizione dal menu "Aspirante" > "Impostazioni". +
+ {% endif %} @@ -31,7 +29,7 @@

Corsi {% if me.volontario %}attivi{%else%}n

- {% for corso in corsi %} + {% for corso in corsi_attivi %} {% endfor %}
{{ corso.link|safe }}
@@ -71,4 +69,45 @@

Ancora nessun corso {% if puo_creare

+ + {% if corsi_frequentati %} +

Corsi frequentati

+ + + + + + + + + {% for corso in corsi_frequentati %} + + + + + {% endfor %} +
Corso e SedeInformazioni
+ {{ corso.link|safe }}
+ + {{ corso.sede.link|safe }}
+ {% if corso.locazione %} + + {{ corso.locazione }} + {% endif %} +
+ + {% if not corso.iniziato %} + Inizia: {{ corso.data_inizio|date:"SHORT_DATETIME_FORMAT" }} + {% else %} + Iniziato: {{ corso.data_inizio|date:"SHORT_DATETIME_FORMAT" }} + {% endif %} +
+ Esami: {{ corso.data_esame|date:"SHORT_DATETIME_FORMAT" }}
+ Direttore ({{ corso.deleghe.count }}): + {% for d in corso.deleghe.all %} + {{ d.persona.link|safe }} + {% endfor %} +
+ {% endif %} + {% endblock %} diff --git a/formazione/templates/aspirante_corso_base_scheda_informazioni.html b/formazione/templates/aspirante_corso_base_scheda_informazioni.html index 46cc60a9d..347b666b9 100644 --- a/formazione/templates/aspirante_corso_base_scheda_informazioni.html +++ b/formazione/templates/aspirante_corso_base_scheda_informazioni.html @@ -101,8 +101,6 @@

Sei iscritt{{ me.genere_o_a }} a questo c Non puoi partecipare perchè non hai titoli necessari. {% elif puoi_partecipare == corso.NON_PUOI_ISCRIVERTI_ESTENSIONI_NON_COINCIDONO %} Non puoi partecipare perchè non hai tutti i requisiti. - - {% elif puoi_partecipare == corso.NON_HAI_CARICATO_DOCUMENTI_PERSONALI %} Per iscriverti a questo corso inserisci la copia di un documento di riconoscimento in corso di validità (CDI, Passaporto o Patente Civile). diff --git a/formazione/viste.py b/formazione/viste.py index 1ab873e77..7741104cd 100644 --- a/formazione/viste.py +++ b/formazione/viste.py @@ -954,11 +954,15 @@ def aspirante_corsi(request, me): # Unisci 2 categorie di corsi corsi = corsi_confermati | corsi_da_partecipare | corsi_estensione_mia_appartenenze + corsi_frequentati = me.corsi_frequentati + corsi_attivi = corsi.exclude(pk__in=corsi_frequentati.values_list('pk', flat=True)) + context = { - 'corsi': corsi.order_by('data_inizio',), + 'corsi_attivi': corsi_attivi.order_by('data_inizio',), + 'corsi_frequentati': corsi_frequentati, 'puo_creare': True if me.ha_permesso(GESTIONE_CORSI_SEDE) else False } - return 'aspirante_corsi_base.html', context + return 'aspirante_corsi.html', context @pagina_privata From f1caa5aa31c46d913bcb3096b9e9a15c662acb4d Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 26 Sep 2019 15:51:59 +0200 Subject: [PATCH 07/79] FIXED: fix di ieri (:-P) aspirante non poteva iscriversi a corso base --- formazione/models.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/formazione/models.py b/formazione/models.py index 29e890931..da1a68d20 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -188,18 +188,19 @@ def persona(self, persona): if persona.ha_aspirante: return self.NON_PUOI_SEI_ASPIRANTE - # Controllo estensioni - if self.extension_type in [CorsoBase.EXT_MIA_SEDE, CorsoBase.EXT_LVL_REGIONALE]: - if not self.persona_verifica_estensioni(persona): - return self.NON_PUOI_ISCRIVERTI_ESTENSIONI_NON_COINCIDONO + # Non fare la verifica per gli aspiranti (non hanno appartenenze) + if not persona.ha_aspirante: + # Controllo estensioni + if self.extension_type in [CorsoBase.EXT_MIA_SEDE, CorsoBase.EXT_LVL_REGIONALE]: + if not self.persona_verifica_estensioni(persona): + return self.NON_PUOI_ISCRIVERTI_ESTENSIONI_NON_COINCIDONO # if not persona.has_required_titles_for_course(course=self): # return self.NON_PUOI_ISCRIVERTI_NON_HAI_TITOLI # Verifica presenza dei documenti personali aggiornati if persona.personal_identity_documents(): - esito_verifica = self.persona_verifica_documenti_personali( - persona) + esito_verifica = self.persona_verifica_documenti_personali(persona) if esito_verifica: return esito_verifica else: From b975b6449da507c1bc71c008903b9459d85d123e Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 26 Sep 2019 17:53:00 +0200 Subject: [PATCH 08/79] NEW: (GAIA-156) rielaborata lettura valore minuti dalla scheda lezioni e altre modifiche in merito --- formazione/forms.py | 11 +++++---- formazione/models.py | 23 ++++++++++++------- ...irante_corso_base_scheda_informazioni.html | 3 ++- .../aspirante_corso_base_scheda_lezioni.html | 3 ++- .../templatetags/formazione_templatetags.py | 22 ++++++++++++++++++ 5 files changed, 48 insertions(+), 14 deletions(-) diff --git a/formazione/forms.py b/formazione/forms.py index b7d275a29..a47fec7e6 100644 --- a/formazione/forms.py +++ b/formazione/forms.py @@ -1,3 +1,5 @@ +import datetime + from django import forms from django.core.exceptions import ValidationError from django.forms import ModelForm, modelformset_factory @@ -171,11 +173,12 @@ def clean(self): lezione_ore = self.instance.lezione_ore if lezione_ore: duration = fine-inizio - days, seconds = duration.days, duration.seconds - hours = days * 24 + seconds // 3600 - if hours > lezione_ore: + # days, seconds = duration.days, duration.seconds + # hours = days * 24 + seconds // 3600 + # hours = datetime.timedelta(hours=hours) + if duration.seconds > lezione_ore.seconds: self.add_error('fine', 'La durata della lezione non può essere ' - 'maggiore della durata impostata nella scheda per questa lezione (%s ore).' % lezione_ore) + 'maggiore della durata impostata nella scheda per questa lezione (%s).' % lezione_ore) self.clean_docente_fields() diff --git a/formazione/models.py b/formazione/models.py index da1a68d20..8916ebfb5 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -1,3 +1,4 @@ +import re import datetime from dateutil.relativedelta import relativedelta @@ -1769,7 +1770,6 @@ def avvisa_presidente_docente_nominato(self): }, destinatari=destinatari, **query_kwargs) - def get_full_scheda_lezioni(self): if hasattr(self, 'corso') and self.corso.titolo_cri and self.corso.titolo_cri.scheda_lezioni: return self.corso.titolo_cri.scheda_lezioni @@ -1794,12 +1794,19 @@ def lezione_argomento(self): @property def lezione_ore(self): ore = self.get_from_scheda('ore') - if ore: + if not ore: + return ore + + if "’" in ore: + # Elaborare i valori con apostrofo (minuti) + minutes = re.findall(r'^\d+', ore.strip()) + minutes = int(minutes[0]) if minutes else 60 + return datetime.timedelta(minutes=minutes) + else: try: - return int(ore) + return datetime.timedelta(hours=int(ore)) except ValueError: - return 1 - return ore + return datetime.timedelta(hours=1) @property def lezione_id_univoco(self): @@ -1813,9 +1820,9 @@ def precaricata(self): @property def non_revisionata(self): - """ Se la data è rimasta minore della data di inizio corso vuol dire - che il reponsabile non ha corretto il valore automatico. """ - return self.inizio < self.corso.data_inizio + """ La lezione risulta non revisionata se è rimasto il solo valore di + inizio impostato in automatico con la creazione del corso """ + return self.inizio and not self.fine @property def divisa(self): diff --git a/formazione/templates/aspirante_corso_base_scheda_informazioni.html b/formazione/templates/aspirante_corso_base_scheda_informazioni.html index 347b666b9..baf648cf0 100644 --- a/formazione/templates/aspirante_corso_base_scheda_informazioni.html +++ b/formazione/templates/aspirante_corso_base_scheda_informazioni.html @@ -4,6 +4,7 @@ {% load social %} {% load humanize %} {% load bootstrap3 %} +{% load formazione_templatetags %} {% block scheda_contenuto %} @@ -246,7 +247,7 @@

{{ lezione.inizio.date|naturalday:"DATE_FORMAT" }}, {{ lezione.inizio.time|date:"TIME_FORMAT" }} {% if lezione.fine %} — {{ lezione.fine.time|date:"TIME_FORMAT" }}{% endif %} - {% if lezione.lezione_ore and not lezione.divisa %}({{ lezione.lezione_ore }} ore){% endif %} + {% if lezione.lezione_ore and not lezione.divisa %}({% lezione_durata lezione %}){% endif %} {% if lezione.luogo %}
{{ lezione.luogo }}{% endif %} {% if lezione.docente %}
{{ lezione.docente.cognome }} {{ lezione.docente.nome }}{% endif %} diff --git a/formazione/templates/aspirante_corso_base_scheda_lezioni.html b/formazione/templates/aspirante_corso_base_scheda_lezioni.html index 6c148cb17..5e0c709ca 100644 --- a/formazione/templates/aspirante_corso_base_scheda_lezioni.html +++ b/formazione/templates/aspirante_corso_base_scheda_lezioni.html @@ -9,6 +9,7 @@ {% block scheda_contenuto %} {% get_url_for_staticfiles as SITE_URL %} @@ -91,17 +93,12 @@


- - - - - -
- Il Direttore del Corso - - Il Presidente del Comitato -
-
+ +

+ Il Direttore del Corso + Il Presidente del Comitato +

+

{{ corso.locazione.comune }}, li {{ corso.data_esame }} diff --git a/formazione/templates/pdf_firme_lezione.html b/formazione/templates/pdf_firme_lezione.html index 5d4e3aff4..6aa779a96 100644 --- a/formazione/templates/pdf_firme_lezione.html +++ b/formazione/templates/pdf_firme_lezione.html @@ -64,7 +64,7 @@

{{ lezione }} del {{ lezione.inizio|date:"SHORT_DATE_FORMAT" }}

{{ iscritto.cognome_nome_completo }}   - {% if forloop.last or forloop.counter|divisibleby:16 %} + {% if forloop.last or forloop.counter|divisibleby:14 %}
Firma Direttore
From d0fba0b0fffcde6af51d4ed0f85be3e38a0fa96d Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Mon, 28 Oct 2019 12:07:32 +0100 Subject: [PATCH 45/79] ADDED: (GAIA-223) colonna appartenenza nella lista iscritti corso --- anagrafica/models.py | 4 ++++ .../aspirante_corso_base_scheda_iscritti.html | 18 +++++------------- .../formazione_elenchi_inc_iscritti.html | 10 ++++++++++ 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/anagrafica/models.py b/anagrafica/models.py index 1520d24eb..b6b4ff2c3 100755 --- a/anagrafica/models.py +++ b/anagrafica/models.py @@ -310,6 +310,10 @@ def prima_appartenenza(self, **kwargs): """ return self.appartenenze.filter(confermata=True, **kwargs).order_by('inizio').first() + @property + def appartenenza_volontario(self): + return self.appartenenze_attuali(membro=Appartenenza.VOLONTARIO) + def appartenenze_per_presidente(self, presidente): """ Ottiene il queryset delle appartenenze attuali e confermate, in base al Presidente diff --git a/formazione/templates/aspirante_corso_base_scheda_iscritti.html b/formazione/templates/aspirante_corso_base_scheda_iscritti.html index 1abdea1b2..fb46f7cfe 100644 --- a/formazione/templates/aspirante_corso_base_scheda_iscritti.html +++ b/formazione/templates/aspirante_corso_base_scheda_iscritti.html @@ -1,8 +1,6 @@ {% extends 'aspirante_corso_base_scheda.html' %} -{% block scheda_titolo %} - Iscritti -{% endblock %} +{% block scheda_titolo %}Iscritti{% endblock %} {% load utils %} @@ -10,19 +8,13 @@ {% if in_attesa.exists %}
-

- - Ci sono {{ in_attesa.count }} richieste di iscrizione -

+

Ci sono {{ in_attesa.count }} richieste di iscrizione

- I direttori del corso e i - responsabili alla formazione del Comitato - possono confermare o negare le richieste - di iscrizione dalla + I direttori del corso e i responsabili alla formazione del Comitato + possono confermare o negare le richieste di iscrizione dalla sezione Richieste.

- {% endif %} {% if corso.possibile_aggiungere_iscritti %} @@ -36,4 +28,4 @@

{% elenco elenco %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/formazione/templates/formazione_elenchi_inc_iscritti.html b/formazione/templates/formazione_elenchi_inc_iscritti.html index 19dc7e743..80a8d2a36 100644 --- a/formazione/templates/formazione_elenchi_inc_iscritti.html +++ b/formazione/templates/formazione_elenchi_inc_iscritti.html @@ -3,6 +3,7 @@ {% block elenco_intestazione_extra %} Stato + Appartenenza {% endblock %} {% block elenco_riga_extra %} @@ -13,6 +14,15 @@ Invitato {% endif %} + + {% if persona.volontario %} + {% for app in persona.appartenenza_volontario %} + {{ app.sede }}
+ {% endfor %} + {% else %} + - + {% endif %} + {% endblock %} {% block elenco_riga_azioni %} From b48d37fa20fe29e4823f39c5954ea4a74e4ee01d Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 30 Oct 2019 14:39:16 +0100 Subject: [PATCH 46/79] NEW: (GAIA-206) IN SVILUPPO adeguamento questionario (mail del 23/10/19) (migr 0005, 0006, 0007, 0008) --- formazione/models.py | 9 +- survey/admin.py | 3 +- survey/forms.py | 167 ++++++++++++++---- .../0005_surveyresult_response_json.py | 21 +++ survey/migrations/0006_auto_20191024_1151.py | 21 +++ .../migrations/0007_question_question_type.py | 20 +++ survey/migrations/0008_question_order.py | 20 +++ survey/models.py | 28 ++- .../corso_questionario_di_gradimento.html | 89 +++++----- survey/templates/survey_step_0_inc.html | 38 ++++ survey/templates/survey_step_1_inc.html | 4 + survey/templates/survey_step_2_inc.html | 7 + survey/views.py | 121 +++++++++---- 13 files changed, 435 insertions(+), 113 deletions(-) create mode 100644 survey/migrations/0005_surveyresult_response_json.py create mode 100644 survey/migrations/0006_auto_20191024_1151.py create mode 100644 survey/migrations/0007_question_question_type.py create mode 100644 survey/migrations/0008_question_order.py create mode 100644 survey/templates/survey_step_0_inc.html create mode 100644 survey/templates/survey_step_1_inc.html create mode 100644 survey/templates/survey_step_2_inc.html diff --git a/formazione/models.py b/formazione/models.py index c86c2162b..c572837ad 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -1172,11 +1172,18 @@ def inform_presidency_with_delibera_file(self): allegati=[self.delibera_file,] ) - def direttori_corso(self): + def direttori_corso(self, as_delega=False): + """ + :param as_delega (True): return Delega + :param as_delega (False): return Persona + """ oggetto_tipo = ContentType.objects.get_for_model(self) deleghe = Delega.objects.filter(tipo=DIRETTORE_CORSO, oggetto_tipo=oggetto_tipo.pk, oggetto_id=self.pk) + if as_delega == True: + return deleghe + deleghe_persone_id = deleghe.values_list('persona__id', flat=True) persone_qs = Persona.objects.filter(id__in=deleghe_persone_id) return persone_qs diff --git a/survey/admin.py b/survey/admin.py index 003134815..13a36a629 100644 --- a/survey/admin.py +++ b/survey/admin.py @@ -9,7 +9,8 @@ class QuestionInline(admin.TabularInline): @admin.register(Question) class AdminQuestion(admin.ModelAdmin): - list_display = ['text', 'survey', 'is_active'] + list_display = ['text', 'survey', 'is_active', 'order', 'question_group', + 'question_type',] list_filter = ['is_active', ] diff --git a/survey/forms.py b/survey/forms.py index 7aa5f9df8..970bd2381 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -2,7 +2,112 @@ from django import forms from django.forms import ModelForm -from .models import Survey +from .models import Survey, Question # SurveyResult + + +class QuestionarioForm(forms.Form): + CHOICES_1_10 = [(i, i) for i in range(1, 11)] + + step = forms.CharField(widget=forms.HiddenInput()) + + def __init__(self, *args, **kwargs): + self.invalid_survey_forms = False + + for f in ['me', 'course', 'instance', 'step']: + setattr(self, f, kwargs.pop(f)) + super().__init__(*args, **kwargs) + + +class QuestionarioPaginaIniziale(QuestionarioForm): + # def process(self, result, next_step): + # super().process(result, next_step) + pass + + +class SelectDirettoreCorsoForm(QuestionarioForm): + direttore = forms.CharField(label="") + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + survey, course, step = self.instance, self.course, self.step + + direttori = course.direttori_corso() + direttori_choices = [(i.pk, i.nome_completo) for i in direttori] + self.fields['direttore'].widget = forms.RadioSelect(choices=direttori_choices) + + def validate_questionario(self, result, **kwargs): + form = self + if form.is_valid(): + cd = form.cleaned_data + direttore = cd['direttore'] + + # Nel caso di direttori multipli ogni direttore ha una sua key (persona.pk) + if direttore not in result.response_json['direttori']: + result.response_json['direttori'][direttore] = {} + + # (success): da salvare nella view + return True + + # Non da salvare + return False + + +class ValutazioneDirettoreCorsoForm(QuestionarioForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + survey, course, step = self.instance, self.course, self.step + + self.fields['step'].initial = self.step + + for question in survey.get_questions().filter(question_group__id=1): + if not question.is_active: + # skip inactive questions + continue + + qid = question.qid + self.fields[qid] = forms.CharField(label=question.text) + + if question.question_type == Question.RADIO: + self.fields[qid].widget = forms.RadioSelect(choices=self.CHOICES_1_10) + elif question.question_type == Question.TEXT: + pass + + # def clean(self): + # print(self.cleaned_data) + + def validate_questionario(self, result, **kwargs): + direttore = kwargs.pop('direttore_da_valutare') + + form = self + if form.is_valid(): + cd = form.cleaned_data + + risposte_valutazione_direttore = {k.replace('qid_', ''): v for k,v in cd.items() if k.startswith('qid_')} + result.response_json['direttori'][str(direttore.pk)] = risposte_valutazione_direttore + result.save() + + return True + + return False + + +class ValutazioneUtilitaLezioniForm(QuestionarioForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + survey, course = self.instance, self.course + lezioni = course.get_lezioni_precaricate() + + # print(lezioni) + + for lezione in lezioni: + lezione_pk = lezione.pk + + self.fields[lezione_pk] = forms.CharField(label=lezione.nome) + self.fields[lezione_pk].widget = forms.RadioSelect(choices=self.CHOICES_1_10) + + +class ValutazioneDocenteCorsoForm(QuestionarioForm): + pass class RespondToCourseSurveyForm(ModelForm): @@ -16,33 +121,33 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields.pop('text') - survey = self.instance - responses = survey.get_responses_dict(self.me) - for question in survey.get_questions(): - if not question.is_active: - # skip inactive questions - continue - - qid = question.qid - self.fields[qid] = forms.CharField(label=question.text) - field = self.fields[qid] - - if qid in responses: - # Set input values if user has already voted - response_for_question = responses[qid] - field.initial = response_for_question['response'] - - # Disable editing after N time passed since user voted - delta = datetime.now() - response_for_question['object'].created_at - if delta >= timedelta(days=2): - field.widget.attrs["disabled"] = "disabled" - - if survey.is_course_admin(self.me, self.course): - # Director of course can see questions but cannot vote - field.widget.attrs["disabled"] = "disabled" - - if question.required: - field.required = True - field.widget.attrs["class"] = "required" - else: - field.required = False + # survey = self.instance + # responses = survey.get_responses_dict(self.me) + # for question in survey.get_questions(): + # if not question.is_active: + # # skip inactive questions + # continue + # + # qid = question.qid + # self.fields[qid] = forms.CharField(label=question.text) + # field = self.fields[qid] + # + # if qid in responses: + # # Set input values if user has already voted + # response_for_question = responses[qid] + # field.initial = response_for_question['response'] + # + # # Disable editing after N time passed since user voted + # delta = datetime.now() - response_for_question['object'].created_at + # if delta >= timedelta(days=2): + # field.widget.attrs["disabled"] = "disabled" + # + # if survey.is_course_admin(self.me, self.course): + # # Director of course can see questions but cannot vote + # field.widget.attrs["disabled"] = "disabled" + # + # if question.required: + # field.required = True + # field.widget.attrs["class"] = "required" + # else: + # field.required = False diff --git a/survey/migrations/0005_surveyresult_response_json.py b/survey/migrations/0005_surveyresult_response_json.py new file mode 100644 index 000000000..d61a161f3 --- /dev/null +++ b/survey/migrations/0005_surveyresult_response_json.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-10-24 11:19 +from __future__ import unicode_literals + +import django.contrib.postgres.fields.jsonb +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0004_survey_survey_type'), + ] + + operations = [ + migrations.AddField( + model_name='surveyresult', + name='response_json', + field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True), + ), + ] diff --git a/survey/migrations/0006_auto_20191024_1151.py b/survey/migrations/0006_auto_20191024_1151.py new file mode 100644 index 000000000..a04a8e03e --- /dev/null +++ b/survey/migrations/0006_auto_20191024_1151.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-10-24 11:51 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0005_surveyresult_response_json'), + ] + + operations = [ + migrations.AlterField( + model_name='surveyresult', + name='question', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='survey.Question'), + ), + ] diff --git a/survey/migrations/0007_question_question_type.py b/survey/migrations/0007_question_question_type.py new file mode 100644 index 000000000..a09e66024 --- /dev/null +++ b/survey/migrations/0007_question_question_type.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-10-24 14:35 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0006_auto_20191024_1151'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='question_type', + field=models.CharField(blank=True, choices=[('text', 'text'), ('radio', 'radio'), ('select', 'select'), ('select-multiple', 'Select Multiple'), ('integer', 'integer')], default='text', max_length=100, null=True), + ), + ] diff --git a/survey/migrations/0008_question_order.py b/survey/migrations/0008_question_order.py new file mode 100644 index 000000000..0246dd596 --- /dev/null +++ b/survey/migrations/0008_question_order.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-10-24 14:41 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0007_question_question_type'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='order', + field=models.SmallIntegerField(blank=True, null=True), + ), + ] diff --git a/survey/models.py b/survey/models.py index 83309cb62..6ceccefa3 100644 --- a/survey/models.py +++ b/survey/models.py @@ -1,4 +1,7 @@ from django.db import models +from django.core.urlresolvers import reverse +from django.contrib.postgres.fields import JSONField + from anagrafica.models import Persona @@ -81,10 +84,10 @@ class Question(models.Model): survey = models.ForeignKey(Survey) is_active = models.BooleanField(default=True) required = models.BooleanField(default=True, verbose_name='Obbligatorio') + order = models.SmallIntegerField(null=True, blank=True) question_group = models.ForeignKey('QuestionGroup', null=True, blank=True) - - # question_type = models.CharField(max_length=100, choices=QUESTION_TYPES, - # default=TEXT, null=True, blank=True) + question_type = models.CharField(max_length=100, choices=QUESTION_TYPES, + default=TEXT, null=True, blank=True) @property def qid(self): @@ -99,11 +102,28 @@ def __str__(self): class SurveyResult(models.Model): + from .forms import (SelectDirettoreCorsoForm, ValutazioneDirettoreCorsoForm, + ValutazioneDocenteCorsoForm, QuestionarioPaginaIniziale) + + INIZIO = 'in' + SELEZIONA_DIRETTORE = 'sd' + VALUTAZIONE_DIRETTORE = 'vd' + VALUTAZIONE_DOCENTE = 'dv' + + # Not for model field choices use + STEPS = { + INIZIO: (0, QuestionarioPaginaIniziale, SELEZIONA_DIRETTORE ), + SELEZIONA_DIRETTORE: (1, SelectDirettoreCorsoForm, VALUTAZIONE_DIRETTORE), + VALUTAZIONE_DIRETTORE: (2, ValutazioneDirettoreCorsoForm, VALUTAZIONE_DOCENTE), + VALUTAZIONE_DOCENTE: (3, ValutazioneDocenteCorsoForm, None), + } + user = models.ForeignKey(Persona) course = models.ForeignKey('formazione.CorsoBase', blank=True, null=True) survey = models.ForeignKey(Survey) - question = models.ForeignKey(Question) + question = models.ForeignKey(Question, blank=True, null=True) response = models.TextField(max_length=1000, blank=True, null=True) + response_json = JSONField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) diff --git a/survey/templates/corso_questionario_di_gradimento.html b/survey/templates/corso_questionario_di_gradimento.html index e0c3c44f0..fb9b33bec 100644 --- a/survey/templates/corso_questionario_di_gradimento.html +++ b/survey/templates/corso_questionario_di_gradimento.html @@ -5,60 +5,61 @@ {% block scheda_titolo %}Questionario di gradimento{% endblock %} {% block scheda_contenuto %} + +
-

Questionario di gradimento

+

{{ survey.text }}

-

Introduzione

-

- Ti chiediamo un parere sull’intervento di formazione cui hai partecipato, per conoscerne i punti di forza e le aree di miglioramento. Compilando il questionario, in forma anonima, ci consentirai di migliorare - continuamente la qualità della formazione della Croce Rossa Italiana. Ti preghiamo quindi di completarlo entro una settimana dalla fine della formazione. - Attraverso il questionario ti chiediamo di esprimere il tuo grado di soddisfazione sul corso, con particolare attenzione ai seguenti aspetti: -

-

-

    -
  • utilità percepita, interesse e partecipazione
  • -
  • didattica: docenza e materiale didattico
  • -
  • organizzazione e servizi
  • -
-

-

- Le domande sono nella maggior parte dei casi a risposta chiusa con richiesta di attribuzione del voto da - 1(poco) a 10 (molto), alcune domande sono a risposta aperta (campo descrittivo). - La tua opinione è per noi preziosa, per questo ti saremo grati se vorrai dedicare pochi minuti alla - compilazione del questionario che segue.
- Grazie! -

+ {% if puo_modificare and has_responses and False %} +

+ Scaricare il Report con le risposte dei partecipanti +


+

+ {% endif %} + + {% if continue_step and not step %} +

Vuoi continuare con la compilazione del questionario? Continua

-

{{ survey.text }}

+ {% else %} + {% if not step %} + {% include 'survey_step_0_inc.html' %} + {% endif %} - {% if puo_modificare and has_responses %} -

- Scaricare il Report con le risposte dei partecipanti -

-
- {% endif %} + {% if step %} +
+ {% csrf_token %} - - {% csrf_token %} + {% if step == 1 %} + {% include 'survey_step_1_inc.html' %} + {% elif step == 2 %} + {% include 'survey_step_2_inc.html' %} + {% endif %} - {% bootstrap_form form as bootstrap_form %} - {% add_questions_groups_to_survey_form bootstrap_form survey %} + +
+ {% endif %} - {% if puo_modificare %} - {# Director of course can only read form data #} - {% else %} - - {% endif %} - + {% endif %}
- - {% endblock %} diff --git a/survey/templates/survey_step_0_inc.html b/survey/templates/survey_step_0_inc.html new file mode 100644 index 000000000..273ca7ee4 --- /dev/null +++ b/survey/templates/survey_step_0_inc.html @@ -0,0 +1,38 @@ + + +

Introduzione

+

+ Ti chiediamo un parere sull’intervento di formazione cui hai partecipato, per conoscerne i punti di forza e le aree di miglioramento. Compilando il questionario, in forma anonima, ci consentirai di migliorare + continuamente la qualità della formazione della Croce Rossa Italiana. Ti preghiamo quindi di completarlo entro una settimana dalla fine della formazione. + Attraverso il questionario ti chiediamo di esprimere il tuo grado di soddisfazione sul corso, con particolare attenzione ai seguenti aspetti: +

+

+

    +
  • utilità percepita, interesse e partecipazione
  • +
  • didattica: docenza e materiale didattico
  • +
  • organizzazione e servizi
  • +
+

+

+ Le domande sono nella maggior parte dei casi a risposta chiusa con richiesta di attribuzione del voto da + 1 (poco) a 10 (molto), alcune domande sono a risposta aperta (campo descrittivo). + La tua opinione è per noi preziosa, per questo ti saremo grati se vorrai dedicare pochi minuti alla + compilazione del questionario che segue.
+ Grazie! +

+ +Avanti diff --git a/survey/templates/survey_step_1_inc.html b/survey/templates/survey_step_1_inc.html new file mode 100644 index 000000000..909596291 --- /dev/null +++ b/survey/templates/survey_step_1_inc.html @@ -0,0 +1,4 @@ +{% load bootstrap3 %} + +

Chi era il tuo Direttore di Corso?

+{% bootstrap_form form %} diff --git a/survey/templates/survey_step_2_inc.html b/survey/templates/survey_step_2_inc.html new file mode 100644 index 000000000..2eb2e83f4 --- /dev/null +++ b/survey/templates/survey_step_2_inc.html @@ -0,0 +1,7 @@ +{% load bootstrap3 %} + +

Dai un voto

+{% bootstrap_form form %} + +

Valuta da 1 (poco) a 10 (molto) l'utilità del singolo Modulo Formativo (non il docente)

+{ % bootstrap_form forms % } diff --git a/survey/views.py b/survey/views.py index 392d828eb..1b970fbd9 100644 --- a/survey/views.py +++ b/survey/views.py @@ -3,60 +3,117 @@ from django.contrib import messages from autenticazione.funzioni import pagina_privata - from anagrafica.permessi.costanti import ERRORE_PERMESSI, MODIFICA +from anagrafica.models import Persona from formazione.models import CorsoBase -from .models import Question, Survey, SurveyResult -from .forms import RespondToCourseSurveyForm +from .models import Survey, SurveyResult @pagina_privata def course_survey(request, me, pk): + redir_to_course = redirect(reverse('aspirante:info', args=[pk])) + course = get_object_or_404(CorsoBase, pk=pk) - course_info_page_redirect = redirect(reverse('aspirante:info', args=[pk])) + + if not course.concluso: + messages.error(request, "Il corso non è ancora terminato (non è superata la data di esame)") + return redir_to_course try: survey = Survey.objects.get(corsobase=course) except Survey.DoesNotExist: + # Se il corso non ha agganciato un questionario messages.error(request, "Il corso non ha un questionario impostato. Contattare l'amministrazione.") - return course_info_page_redirect - - if not course.concluso: - messages.error(request, "Il corso non è ancora terminato (non è superata la data di esame)") - return course_info_page_redirect + return redir_to_course if not survey.can_vote(me, course): return redirect(ERRORE_PERMESSI) - form = RespondToCourseSurveyForm(request.POST or None, instance=survey, - me=me, course=course) - if form.is_valid(): - cd = form.cleaned_data - for question in survey.get_questions(): - if question.qid in cd: - response = cd.get(question.qid) - result, created = SurveyResult.objects.get_or_create( - course=course, - user=me, - survey=survey, - question=question - ) - if result: - result.response = response - if created: - pass - result.save() - - messages.success(request, 'Grazie, abbiamo salvato le tue risposte.') - return redirect(reverse('survey:course', args=[course.pk])) + survey_url = reverse("survey:course", args=[pk,]) context = { 'corso': course, 'survey': survey, - 'form': form, 'puo_modificare': survey.is_course_admin(me, course), 'has_responses': survey.has_user_responses(course), + 'forms': list(), } + + # Scegli le form sulla base di step indicato in http-request + step_in_request = request.POST.get('step') or request.GET.get('step') + step_in_dict = SurveyResult.STEPS.get(step_in_request, [None, None, None]) + step, form, next_step = step_in_dict + + # Crea/trova oggetto per le risposte + result, created = SurveyResult.objects.get_or_create(course=course, + user=me, + survey=survey) + # Valorizza il campo json senza dati (keys vuoti) + if result and not result.response_json: + result.response_json = { + 'direttori': dict(), + 'docenti': dict(), + 'step': None, + } + + if step_in_request: + form_kwargs = dict(instance=survey, me=me, course=course, step=step_in_request) + + # Instanziare form per step + form = form(request.POST or None, **form_kwargs) + + context.update({ + 'form': form, + 'step': step, + }) + + # Variabili + direttore_persona = None + invalid_forms = False + + continue_step = result.response_json['step'] + if continue_step: + context['continue_step'] = survey_url + "?step=%s" % continue_step + + direttore_selezionato = list(result.response_json['direttori'].keys()) + if direttore_selezionato: + if len(direttore_selezionato): + direttore_persona = Persona.objects.get(pk=int(direttore_selezionato[0])) + else: + direttore_persona = Persona.objects.filter(pk__in=[int(i) for i in direttore_selezionato]) + + # Steo (0): Inizio + if request.GET.get('from') and request.GET.get('from') == 'start': + result.response_json['step'] = SurveyResult.SELEZIONA_DIRETTORE + + # Argumenti che servono per + kwargs_per_questionario = dict() + if step_in_request == SurveyResult.VALUTAZIONE_DIRETTORE: + kwargs_per_questionario['direttore_da_valutare'] = direttore_persona + + # Arriva form da validare e salvare + if request.method == 'POST' and form is not None: + """ Possibili steps: + (1): Seleziona direttore + (2): Valutazione direttore, valutazione lezioni + """ + + # Valida form collegaga allo step attuale + is_questionario_valid = form.validate_questionario(result, **kwargs_per_questionario) + if not is_questionario_valid: + # Segna che ci sono form invalide per non procede al salvataggio + invalid_forms = True + + # Non ci sono form invalide. La form è tutta compilata. + # Imposta prossimo step -> Salva risultato - > Rindirizza pagina prox.step + if not invalid_forms: + result.response_json['step'] = next_step + result.save() + + next_step_reverse = survey_url + "?step=%s" % next_step + return redirect(next_step_reverse) + + print(result.pk, continue_step, step) return 'corso_questionario_di_gradimento.html', context @@ -67,4 +124,4 @@ def course_survey_download_results(request, me, pk): return redirect(ERRORE_PERMESSI) report = SurveyResult.generate_report_for_course(course) - return report \ No newline at end of file + return report From 5b1565bc7afd0521498adae33e569af0502ef83f Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 30 Oct 2019 16:30:08 +0100 Subject: [PATCH 47/79] FIXED: (GAIA-236) esame verbale idoneo/non idoneo nell'anteprima --- formazione/models.py | 11 +++++++++-- formazione/templates/pdf_corso_esame_verbale.html | 8 +++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/formazione/models.py b/formazione/models.py index c572837ad..ba5a2e5a5 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -1078,6 +1078,13 @@ def key_cognome(elem): pdf_template = "pdf_corso_%sesame_verbale.html" pdf_template = pdf_template % "base_" if self.corso_vecchio else pdf_template % "" + if anteprima: + numero_idonei = len([p.pk for p in partecipazioni if p.idoneo]) + numero_non_idonei = len([p.pk for p in partecipazioni if not p.idoneo]) + else: + numero_idonei = self.idonei().count() + numero_non_idonei = self.non_idonei().count() + pdf = PDF(oggetto=self) pdf.genera_e_salva_con_python( nome="Verbale Esame del Corso Base %d-%d.pdf" % (self.progressivo, self.anno), @@ -1086,8 +1093,8 @@ def key_cognome(elem): 'titolo': "Anteprima " if anteprima else "", 'secondo_verbale': verbale_per_seconda_data_esame, "partecipazioni": sorted(partecipazioni, key=key_cognome), - "numero_idonei": self.idonei().count(), - "numero_non_idonei": self.non_idonei().count(), + "numero_idonei": numero_idonei, + "numero_non_idonei": numero_non_idonei, "numero_aspiranti": self.partecipazioni_confermate().count(), 'request': request, }, diff --git a/formazione/templates/pdf_corso_esame_verbale.html b/formazione/templates/pdf_corso_esame_verbale.html index 9135298e6..747a3f6d6 100644 --- a/formazione/templates/pdf_corso_esame_verbale.html +++ b/formazione/templates/pdf_corso_esame_verbale.html @@ -192,7 +192,13 @@ {{ p.persona.codice_fiscale }} {% if corso.titolo_cri.scheda_prevede_esame %} - {{ p.get_esito_esame_display|default:"N/D" }} + + {% if not p.get_esito_esame_display %} + {% if p.idoneo %}IDONEO{% else %}NON IDONEO{% endif %} + {% else %} + {{ p.get_esito_esame_display|default:"N/D" }} + {% endif %} + {% else %} {{ p.get_ammissione_display|default:"Esame non previsto" }} {% endif %} From 796408c25a267a863e4c38227f392e6f805cca45 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 31 Oct 2019 17:56:57 +0100 Subject: [PATCH 48/79] FIXED: (GAIA-206) IN SVILUPPO elaborazione nuovi step questionario --- survey/admin.py | 8 +- survey/forms.py | 175 +++++++++++++++--- survey/models.py | 29 ++- .../corso_questionario_di_gradimento.html | 16 +- survey/templates/survey_step_2_inc.html | 3 - survey/templates/survey_step_3_inc.html | 4 + survey/templates/survey_step_4_inc.html | 5 + survey/templates/survey_step_5_inc.html | 2 + survey/views.py | 53 ++++-- 9 files changed, 235 insertions(+), 60 deletions(-) create mode 100644 survey/templates/survey_step_3_inc.html create mode 100644 survey/templates/survey_step_4_inc.html create mode 100644 survey/templates/survey_step_5_inc.html diff --git a/survey/admin.py b/survey/admin.py index 13a36a629..05dc82625 100644 --- a/survey/admin.py +++ b/survey/admin.py @@ -1,5 +1,8 @@ from django.contrib import admin -from .models import * +from django.contrib.postgres.fields import JSONField +from prettyjson import PrettyJSONWidget + +from .models import (Question, QuestionGroup, Survey, SurveyResult) class QuestionInline(admin.TabularInline): @@ -30,3 +33,6 @@ class AdminSurvey(admin.ModelAdmin): class AdminSurveyResult(admin.ModelAdmin): list_display = ['course', 'user', 'question', 'response', 'created_at', 'updated_at'] raw_id_fields = ['user', 'survey', 'question', 'course'] + formfield_overrides = { + JSONField: {'widget': PrettyJSONWidget} + } diff --git a/survey/forms.py b/survey/forms.py index 970bd2381..7d4e720c4 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -6,6 +6,11 @@ class QuestionarioForm(forms.Form): + QGROUP_UTILITA_PERCEPITA = 1 + QGROUP_DOCENTI = 2 + QGROUP_ORGANIZZAZIONE = 3 + QGROUP_SERVIZI = 4 + CHOICES_1_10 = [(i, i) for i in range(1, 11)] step = forms.CharField(widget=forms.HiddenInput()) @@ -13,10 +18,34 @@ class QuestionarioForm(forms.Form): def __init__(self, *args, **kwargs): self.invalid_survey_forms = False - for f in ['me', 'course', 'instance', 'step']: + for f in ['me', 'course', 'instance', 'step', 'survey_result']: setattr(self, f, kwargs.pop(f)) super().__init__(*args, **kwargs) + @property + def response_direttori(self): + return list(self.survey_result.response_json['direttori'].keys()) + + def populate_questions_inputs(self, survey, question_group_id, **kwargs): + for question in survey.get_questions().filter(question_group__id=question_group_id): + if not question.is_active: + # skip inactive questions + continue + + qid = question.qid + self.fields[qid] = forms.CharField(label=question.text) + + if question.question_type == Question.RADIO: + self.fields[qid].widget = forms.RadioSelect(choices=self.CHOICES_1_10) + elif question.question_type == Question.TEXT: + pass + + def process_qid(self, cd): + QID_PREFIX = 'qid_' + return { + k.replace(QID_PREFIX, ''): v for k,v in cd.items() if k.startswith(QID_PREFIX) + } + class QuestionarioPaginaIniziale(QuestionarioForm): # def process(self, result, next_step): @@ -29,12 +58,16 @@ class SelectDirettoreCorsoForm(QuestionarioForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - survey, course, step = self.instance, self.course, self.step - direttori = course.direttori_corso() - direttori_choices = [(i.pk, i.nome_completo) for i in direttori] + direttori_choices = [(i.pk, i.nome_completo) for i in self.course.direttori_corso()] self.fields['direttore'].widget = forms.RadioSelect(choices=direttori_choices) + response_direttori = self.response_direttori + if response_direttori and len(response_direttori) >= 1: + self.fields['direttore'].initial = response_direttori[0] + + self.fields['step'].initial = self.step + def validate_questionario(self, result, **kwargs): form = self if form.is_valid(): @@ -44,6 +77,7 @@ def validate_questionario(self, result, **kwargs): # Nel caso di direttori multipli ogni direttore ha una sua key (persona.pk) if direttore not in result.response_json['direttori']: result.response_json['direttori'][direttore] = {} + result.save() # (success): da salvare nella view return True @@ -55,25 +89,12 @@ def validate_questionario(self, result, **kwargs): class ValutazioneDirettoreCorsoForm(QuestionarioForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - survey, course, step = self.instance, self.course, self.step + survey, course = self.instance, self.course + # Per indicare con request.POST con che form/step stiamo lavorando self.fields['step'].initial = self.step - for question in survey.get_questions().filter(question_group__id=1): - if not question.is_active: - # skip inactive questions - continue - - qid = question.qid - self.fields[qid] = forms.CharField(label=question.text) - - if question.question_type == Question.RADIO: - self.fields[qid].widget = forms.RadioSelect(choices=self.CHOICES_1_10) - elif question.question_type == Question.TEXT: - pass - - # def clean(self): - # print(self.cleaned_data) + self.populate_questions_inputs(self.instance, QuestionarioForm.QGROUP_UTILITA_PERCEPITA) def validate_questionario(self, result, **kwargs): direttore = kwargs.pop('direttore_da_valutare') @@ -94,20 +115,122 @@ def validate_questionario(self, result, **kwargs): class ValutazioneUtilitaLezioniForm(QuestionarioForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - survey, course = self.instance, self.course - lezioni = course.get_lezioni_precaricate() - # print(lezioni) + # Per indicare con request.POST con che form/step stiamo lavorando + self.fields['step'].initial = self.step - for lezione in lezioni: - lezione_pk = lezione.pk + # Risposte precedentemente salvate + response_lezioni = self.survey_result.response_json['lezioni'] + for lezione in self.course.get_lezioni_precaricate(): + lezione_pk = 'lezioni_pk_%s' % lezione.pk + + # Crea i campi con 10 radio-button (0-10) self.fields[lezione_pk] = forms.CharField(label=lezione.nome) self.fields[lezione_pk].widget = forms.RadioSelect(choices=self.CHOICES_1_10) + # Valorizzare gli input se ci sono le risposte + lezione_pk_str = str(lezione.pk) + if lezione_pk_str in response_lezioni: + self.fields[lezione_pk].initial = response_lezioni[lezione_pk_str] + + def validate_questionario(self, result, **kwargs): + form = self + if form.is_valid(): + cd = form.cleaned_data + + if 'lezioni' not in result.response_json: + result.response_json['lezioni'] = dict() + + lezioni_valutate = {k.replace('lezioni_pk_', ''): v for k, v in cd.items() if k.startswith('lezioni_pk_')} + result.response_json['lezioni'] = lezioni_valutate + result.save() + + return True + return False + class ValutazioneDocenteCorsoForm(QuestionarioForm): - pass + docente_lezione_pk = forms.CharField(widget=forms.HiddenInput()) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + # Per indicare con request.POST con che form/step stiamo lavorando + self.fields['step'].initial = self.step + + # Prima generare la struttura + self.generate_json_structure() + + # Creare gli input per la valutazione di ogni docente per ogni singola lezione + self.populate_questions_inputs(self.instance, QuestionarioForm.QGROUP_DOCENTI) + + # Per indicare con request.POST che docente/lezione valutiamo + docente_lezione = self.survey_result.get_uncompleted_valutazione_docente_lezione() + self.fields['docente_lezione_pk'].initial = "%s_%s" % docente_lezione + + def generate_json_structure(self): + """ + Questo metodo genera la struttura delle chiavi delle lezioni (JSON). + Formato: + { + 'lezioni': { + 'docente.pk': { + 'lezione.pk': { + "completed": true|false, + "domanda.pk": "risposta 1-10", + "domanda.pk": "risposta 1-10", + ... + }, + ... + }, + ... + } + } + + :return: None + """ + + lezioni_struttura = self.survey_result.response_json['lezioni'] + for lezione in self.course.lezioni.all(): + + # Per docenti (Persona) + for docente_pk in lezione.docente.all().values_list('pk', flat=True): + docente_pk = str(docente_pk) + lezione_pk = str(lezione.pk) + + # Crea dict vuoti + if docente_pk not in lezioni_struttura: + lezioni_struttura[docente_pk] = dict() + + if lezione_pk not in lezioni_struttura[docente_pk]: + lezioni_struttura[docente_pk][lezione_pk] = dict(completed=False) + + # # Per docenti esterni + # docente_esterno = lezione.docente_esterno + # if docente_esterno: + # if docente_esterno not in lezioni_struttura: + # lezioni_struttura[docente_esterno] = dict() + # lezioni_struttura[docente_esterno][lezione.pk] = dict(completed=False) + + # Salva la struttura + self.survey_result.save() + + def validate_questionario(self, result, **kwargs): + form = self + if form.is_valid(): + cd = form.cleaned_data + + docente, lezione = cd['docente_lezione_pk'].split('_') + + risposte = self.process_qid(cd) + risposte['completed'] = True + + result.response_json['lezioni'][docente][lezione] = risposte + result.save() + + return True + return False class RespondToCourseSurveyForm(ModelForm): diff --git a/survey/models.py b/survey/models.py index 6ceccefa3..65ea242de 100644 --- a/survey/models.py +++ b/survey/models.py @@ -102,20 +102,25 @@ def __str__(self): class SurveyResult(models.Model): - from .forms import (SelectDirettoreCorsoForm, ValutazioneDirettoreCorsoForm, - ValutazioneDocenteCorsoForm, QuestionarioPaginaIniziale) + from .forms import (QuestionarioPaginaIniziale, SelectDirettoreCorsoForm, + ValutazioneDirettoreCorsoForm, ValutazioneUtilitaLezioniForm, + ValutazioneDocenteCorsoForm,) INIZIO = 'in' SELEZIONA_DIRETTORE = 'sd' VALUTAZIONE_DIRETTORE = 'vd' + VALUTAZIONE_LEZIONI = 'vl' VALUTAZIONE_DOCENTE = 'dv' + GRAZIE = 'gr' # Not for model field choices use STEPS = { INIZIO: (0, QuestionarioPaginaIniziale, SELEZIONA_DIRETTORE ), SELEZIONA_DIRETTORE: (1, SelectDirettoreCorsoForm, VALUTAZIONE_DIRETTORE), - VALUTAZIONE_DIRETTORE: (2, ValutazioneDirettoreCorsoForm, VALUTAZIONE_DOCENTE), - VALUTAZIONE_DOCENTE: (3, ValutazioneDocenteCorsoForm, None), + VALUTAZIONE_DIRETTORE: (2, ValutazioneDirettoreCorsoForm, VALUTAZIONE_LEZIONI), + VALUTAZIONE_LEZIONI: (3, ValutazioneUtilitaLezioniForm, VALUTAZIONE_DOCENTE), + VALUTAZIONE_DOCENTE: (4, ValutazioneDocenteCorsoForm, GRAZIE), + GRAZIE: (5, None, None), } user = models.ForeignKey(Persona) @@ -154,6 +159,22 @@ def generate_report_for_course(cls, course): return response + def get_uncompleted_valutazione_docente_lezione(self): + """ + Restituisce docente -> lezione da valutare (completed=False). + Restituisce None, None: Se non ci sono gruppo docente->lezione da + valutare (assenti completed=False). + + :return: docente.pk or None, lezione.pk or None + """ + docenti = self.response_json['lezioni'] + for docente_pk, lezione_data in docenti.items(): + for lezione_pk, lezione_data in lezione_data.items(): + if lezione_data['completed']: + continue + return int(docente_pk), int(lezione_pk) + return None, None + class Meta: verbose_name = "Risposta dell'utente" verbose_name_plural = "Risposte degli utenti" diff --git a/survey/templates/corso_questionario_di_gradimento.html b/survey/templates/corso_questionario_di_gradimento.html index fb9b33bec..17f386fb8 100644 --- a/survey/templates/corso_questionario_di_gradimento.html +++ b/survey/templates/corso_questionario_di_gradimento.html @@ -40,22 +40,14 @@

Vuoi continuare con la compilazione del questionario? Continua

{% else %} - {% if not step %} - {% include 'survey_step_0_inc.html' %} - {% endif %} - - {% if step %} + {% if step and form %}
{% csrf_token %} - - {% if step == 1 %} - {% include 'survey_step_1_inc.html' %} - {% elif step == 2 %} - {% include 'survey_step_2_inc.html' %} - {% endif %} - + {% include template %}
+ {% else %} + {% include 'survey_step_0_inc.html' %} {% endif %} {% endif %} diff --git a/survey/templates/survey_step_2_inc.html b/survey/templates/survey_step_2_inc.html index 2eb2e83f4..480f3b319 100644 --- a/survey/templates/survey_step_2_inc.html +++ b/survey/templates/survey_step_2_inc.html @@ -2,6 +2,3 @@

Dai un voto

{% bootstrap_form form %} - -

Valuta da 1 (poco) a 10 (molto) l'utilità del singolo Modulo Formativo (non il docente)

-{ % bootstrap_form forms % } diff --git a/survey/templates/survey_step_3_inc.html b/survey/templates/survey_step_3_inc.html new file mode 100644 index 000000000..d3e302667 --- /dev/null +++ b/survey/templates/survey_step_3_inc.html @@ -0,0 +1,4 @@ +{% load bootstrap3 %} + +

Valuta da 1 (poco) a 10 (molto) l'utilità del singolo Modulo Formativo (non il docente)

+{% bootstrap_form form %} diff --git a/survey/templates/survey_step_4_inc.html b/survey/templates/survey_step_4_inc.html new file mode 100644 index 000000000..7e13a4eef --- /dev/null +++ b/survey/templates/survey_step_4_inc.html @@ -0,0 +1,5 @@ +{% load bootstrap3 %} + +

Valuta l'utilità del singolo Modulo {{ valutazione_lezione.nome }} (docente {{ valutazione_docente }})

+
da 1 (poco) a 10 (molto)
+{% bootstrap_form form %} diff --git a/survey/templates/survey_step_5_inc.html b/survey/templates/survey_step_5_inc.html new file mode 100644 index 000000000..938a129e2 --- /dev/null +++ b/survey/templates/survey_step_5_inc.html @@ -0,0 +1,2 @@ +

Grazie.

+

Grazie per la compilazione di tutto il questionario.

diff --git a/survey/views.py b/survey/views.py index 1b970fbd9..5e8de70b0 100644 --- a/survey/views.py +++ b/survey/views.py @@ -5,7 +5,7 @@ from autenticazione.funzioni import pagina_privata from anagrafica.permessi.costanti import ERRORE_PERMESSI, MODIFICA from anagrafica.models import Persona -from formazione.models import CorsoBase +from formazione.models import CorsoBase, LezioneCorsoBase from .models import Survey, SurveyResult @@ -44,10 +44,13 @@ def course_survey(request, me, pk): step_in_dict = SurveyResult.STEPS.get(step_in_request, [None, None, None]) step, form, next_step = step_in_dict + # print(step) + # Crea/trova oggetto per le risposte result, created = SurveyResult.objects.get_or_create(course=course, user=me, survey=survey) + # Valorizza il campo json senza dati (keys vuoti) if result and not result.response_json: result.response_json = { @@ -55,17 +58,18 @@ def course_survey(request, me, pk): 'docenti': dict(), 'step': None, } + result.save() if step_in_request: - form_kwargs = dict(instance=survey, me=me, course=course, step=step_in_request) + form_kwargs = dict(instance=survey, course=course, + step=step_in_request, survey_result=result, me=me) + if step: + context['step'] = step - # Instanziare form per step - form = form(request.POST or None, **form_kwargs) - - context.update({ - 'form': form, - 'step': step, - }) + if form: + # Instanziare form per step + form = form(request.POST or None, **form_kwargs) + context['form'] = form # Variabili direttore_persona = None @@ -85,20 +89,30 @@ def course_survey(request, me, pk): # Steo (0): Inizio if request.GET.get('from') and request.GET.get('from') == 'start': result.response_json['step'] = SurveyResult.SELEZIONA_DIRETTORE + result.save() - # Argumenti che servono per + # Argomenti kwargs_per_questionario = dict() if step_in_request == SurveyResult.VALUTAZIONE_DIRETTORE: kwargs_per_questionario['direttore_da_valutare'] = direttore_persona + elif step_in_request == SurveyResult.VALUTAZIONE_DOCENTE: + docente, lezione = result.get_uncompleted_valutazione_docente_lezione() + context['valutazione_docente'] = Persona.objects.get(pk=docente) + context['valutazione_lezione'] = LezioneCorsoBase.objects.get(pk=lezione) + # Arriva form da validare e salvare - if request.method == 'POST' and form is not None: + if (request.method == 'POST') and (form is not None): """ Possibili steps: (1): Seleziona direttore - (2): Valutazione direttore, valutazione lezioni + (2): Valutazione direttore + (3): Valutazione lezioni + (4): Valutazione di ogni docente di ogni lezione + (5): Grazie. """ # Valida form collegaga allo step attuale + # Importante restituire [True o False] is_questionario_valid = form.validate_questionario(result, **kwargs_per_questionario) if not is_questionario_valid: # Segna che ci sono form invalide per non procede al salvataggio @@ -107,13 +121,24 @@ def course_survey(request, me, pk): # Non ci sono form invalide. La form è tutta compilata. # Imposta prossimo step -> Salva risultato - > Rindirizza pagina prox.step if not invalid_forms: + # Valutazione docente -> lezione + # Prossimo step rimane sempre lo stesso finchè non sono state + # compilate tutte le combinazioni docente\lezione. + + # print(result.get_uncompleted_valutazione_docente_lezione()) + if step == 4 and result.get_uncompleted_valutazione_docente_lezione()[0] is not None: + return redirect(survey_url + "?step=%s" % SurveyResult.VALUTAZIONE_DOCENTE) + result.response_json['step'] = next_step result.save() - next_step_reverse = survey_url + "?step=%s" % next_step + # Rindirizza + next_step_qs = "?step=%s" % next_step if next_step is not None else '' + next_step_reverse = survey_url + next_step_qs + return redirect(next_step_reverse) - print(result.pk, continue_step, step) + context['template'] = 'survey_step_%s_inc.html' % step return 'corso_questionario_di_gradimento.html', context From f7061194fab2b814c49ec9eedaf530083d902d88 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Mon, 4 Nov 2019 11:00:43 +0100 Subject: [PATCH 49/79] FIXED: (GAIA-206) IN SVILUPPO. Sistemati gli step; templates; valorazzazione degli input che hanno risposta nel json --- survey/forms.py | 19 ++++++++++---- survey/models.py | 4 +++ .../corso_questionario_di_gradimento.html | 10 +++++++- survey/templates/survey_step_5_inc.html | 6 +++-- survey/views.py | 25 +++++++++++++------ 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/survey/forms.py b/survey/forms.py index 7d4e720c4..ee0ffe3f4 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -89,12 +89,20 @@ def validate_questionario(self, result, **kwargs): class ValutazioneDirettoreCorsoForm(QuestionarioForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - survey, course = self.instance, self.course # Per indicare con request.POST con che form/step stiamo lavorando self.fields['step'].initial = self.step self.populate_questions_inputs(self.instance, QuestionarioForm.QGROUP_UTILITA_PERCEPITA) + self.set_initial_values() + + def set_initial_values(self): + # Risposte precedentemente salvate + direttori = self.survey_result.response_json['direttori'] + for direttore_pk, responses in direttori.items(): + for question_id, response in responses.items(): + qid = 'qid_%s' % question_id + self.fields[qid].initial = response def validate_questionario(self, result, **kwargs): direttore = kwargs.pop('direttore_da_valutare') @@ -120,7 +128,7 @@ def __init__(self, *args, **kwargs): self.fields['step'].initial = self.step # Risposte precedentemente salvate - response_lezioni = self.survey_result.response_json['lezioni'] + response_lezioni = self.survey_result.response_json['utilita_lezioni'] for lezione in self.course.get_lezioni_precaricate(): lezione_pk = 'lezioni_pk_%s' % lezione.pk @@ -139,11 +147,12 @@ def validate_questionario(self, result, **kwargs): if form.is_valid(): cd = form.cleaned_data - if 'lezioni' not in result.response_json: - result.response_json['lezioni'] = dict() + if 'utilita_lezioni' not in result.response_json: + result.response_json['utilita_lezioni'] = dict() lezioni_valutate = {k.replace('lezioni_pk_', ''): v for k, v in cd.items() if k.startswith('lezioni_pk_')} - result.response_json['lezioni'] = lezioni_valutate + + result.response_json['utilita_lezioni'] = lezioni_valutate result.save() return True diff --git a/survey/models.py b/survey/models.py index 65ea242de..0eeb286c3 100644 --- a/survey/models.py +++ b/survey/models.py @@ -175,6 +175,10 @@ def get_uncompleted_valutazione_docente_lezione(self): return int(docente_pk), int(lezione_pk) return None, None + @property + def current_step(self): + return self.response_json.get('step') + class Meta: verbose_name = "Risposta dell'utente" verbose_name_plural = "Risposte degli utenti" diff --git a/survey/templates/corso_questionario_di_gradimento.html b/survey/templates/corso_questionario_di_gradimento.html index 17f386fb8..d0cae7470 100644 --- a/survey/templates/corso_questionario_di_gradimento.html +++ b/survey/templates/corso_questionario_di_gradimento.html @@ -37,7 +37,13 @@ {% endif %} {% if continue_step and not step %} -

Vuoi continuare con la compilazione del questionario? Continua

+

+ {% if survey_result.current_step == survey_result.GRAZIE %} + Il questionario è già stato completato. + {% else %} + Vuoi continuare con la compilazione del questionario? Continua + {% endif %} +

{% else %} {% if step and form %} @@ -46,6 +52,8 @@ {% include template %} + {% elif step == 5 %} + {% include 'survey_step_5_inc.html' %} {% else %} {% include 'survey_step_0_inc.html' %} {% endif %} diff --git a/survey/templates/survey_step_5_inc.html b/survey/templates/survey_step_5_inc.html index 938a129e2..a14802872 100644 --- a/survey/templates/survey_step_5_inc.html +++ b/survey/templates/survey_step_5_inc.html @@ -1,2 +1,4 @@ -

Grazie.

-

Grazie per la compilazione di tutto il questionario.

+
+

Grazie.

+

Grazie per la compilazione di tutto il questionario.

+
diff --git a/survey/views.py b/survey/views.py index 5e8de70b0..8a9e8e57f 100644 --- a/survey/views.py +++ b/survey/views.py @@ -44,25 +44,28 @@ def course_survey(request, me, pk): step_in_dict = SurveyResult.STEPS.get(step_in_request, [None, None, None]) step, form, next_step = step_in_dict - # print(step) - # Crea/trova oggetto per le risposte result, created = SurveyResult.objects.get_or_create(course=course, user=me, survey=survey) + context['survey_result'] = result # Valorizza il campo json senza dati (keys vuoti) if result and not result.response_json: result.response_json = { 'direttori': dict(), - 'docenti': dict(), + 'utilita_lezioni': dict(), + 'lezioni': dict(), 'step': None, } result.save() if step_in_request: - form_kwargs = dict(instance=survey, course=course, - step=step_in_request, survey_result=result, me=me) + form_kwargs = dict(instance=survey, + course=course, + step=step_in_request, + survey_result=result, + me=me) if step: context['step'] = step @@ -98,8 +101,12 @@ def course_survey(request, me, pk): elif step_in_request == SurveyResult.VALUTAZIONE_DOCENTE: docente, lezione = result.get_uncompleted_valutazione_docente_lezione() - context['valutazione_docente'] = Persona.objects.get(pk=docente) - context['valutazione_lezione'] = LezioneCorsoBase.objects.get(pk=lezione) + if docente and lezione: + context['valutazione_docente'] = Persona.objects.get(pk=docente) + context['valutazione_lezione'] = LezioneCorsoBase.objects.get(pk=lezione) + else: + messages.warning(request, "Questo step del questionario è stato già completato.") + return redirect(survey_url) # Arriva form da validare e salvare if (request.method == 'POST') and (form is not None): @@ -132,12 +139,16 @@ def course_survey(request, me, pk): result.response_json['step'] = next_step result.save() + # print(1, step, form, next_step) + # Rindirizza next_step_qs = "?step=%s" % next_step if next_step is not None else '' next_step_reverse = survey_url + next_step_qs return redirect(next_step_reverse) + # print(0, step, form, next_step) + context['template'] = 'survey_step_%s_inc.html' % step return 'corso_questionario_di_gradimento.html', context From 452f64ee8181693ab48fd688fa8da8f06eea2c5e Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Tue, 5 Nov 2019 12:15:40 +0100 Subject: [PATCH 50/79] FIXED: (GAIA-206) IN SVILUPPO (migrazioni 0009, 0010); logica/validazione step org e servizi --- survey/admin.py | 2 +- survey/forms.py | 133 +++++++++++++----- survey/migrations/0009_auto_20191104_1435.py | 20 +++ survey/migrations/0010_question_anchor.py | 20 +++ survey/models.py | 23 ++- .../corso_questionario_di_gradimento.html | 5 +- survey/templates/survey_step_5_inc.html | 8 +- survey/templates/survey_step_6_inc.html | 4 + survey/views.py | 3 +- 9 files changed, 169 insertions(+), 49 deletions(-) create mode 100644 survey/migrations/0009_auto_20191104_1435.py create mode 100644 survey/migrations/0010_question_anchor.py create mode 100644 survey/templates/survey_step_6_inc.html diff --git a/survey/admin.py b/survey/admin.py index 05dc82625..271884b3b 100644 --- a/survey/admin.py +++ b/survey/admin.py @@ -13,7 +13,7 @@ class QuestionInline(admin.TabularInline): @admin.register(Question) class AdminQuestion(admin.ModelAdmin): list_display = ['text', 'survey', 'is_active', 'order', 'question_group', - 'question_type',] + 'question_type', 'anchor',] list_filter = ['is_active', ] diff --git a/survey/forms.py b/survey/forms.py index ee0ffe3f4..ade8aeb2e 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -8,8 +8,7 @@ class QuestionarioForm(forms.Form): QGROUP_UTILITA_PERCEPITA = 1 QGROUP_DOCENTI = 2 - QGROUP_ORGANIZZAZIONE = 3 - QGROUP_SERVIZI = 4 + QGROUP_ORG_SERVIZI = 3 CHOICES_1_10 = [(i, i) for i in range(1, 11)] @@ -27,7 +26,11 @@ def response_direttori(self): return list(self.survey_result.response_json['direttori'].keys()) def populate_questions_inputs(self, survey, question_group_id, **kwargs): - for question in survey.get_questions().filter(question_group__id=question_group_id): + questions_qs = survey.get_questions().filter( + question_group__id=question_group_id + ).order_by('order',) + + for question in questions_qs: if not question.is_active: # skip inactive questions continue @@ -37,8 +40,12 @@ def populate_questions_inputs(self, survey, question_group_id, **kwargs): if question.question_type == Question.RADIO: self.fields[qid].widget = forms.RadioSelect(choices=self.CHOICES_1_10) + + elif question.question_type == Question.BOOLEAN: + self.fields[qid] = forms.BooleanField(label=question.text) + elif question.question_type == Question.TEXT: - pass + pass # non modifica niente def process_qid(self, cd): QID_PREFIX = 'qid_' @@ -242,6 +249,64 @@ def validate_questionario(self, result, **kwargs): return False +class ValutazioneOrganizzazioneServiziForm(QuestionarioForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + survey = self.instance + + # Per indicare con request.POST con che form/step stiamo lavorando + self.fields['step'].initial = self.step + + self.populate_questions_inputs(survey, QuestionarioForm.QGROUP_ORG_SERVIZI) + + boolean_non_required_fields = survey.get_questions().filter( + question_group__id=QuestionarioForm.QGROUP_ORG_SERVIZI, + ) + + for field in boolean_non_required_fields: + self.fields[field.qid].required = False + + def clean(self): + cd = self.cleaned_data + + questions_to_validate = self.instance.get_questions().filter( + question_group__id=QuestionarioForm.QGROUP_ORG_SERVIZI, + question_type=Question.RADIO, + anchor__isnull=False, + ).values_list('pk', 'anchor',) + questions_to_validate = list(filter(lambda x: x[1], questions_to_validate)) + + for i in questions_to_validate: + pk, anchor = i + risposta_parente = cd.get('qid_%s' % anchor) + if risposta_parente is None: + continue + + rispost_qid = 'qid_%s' % pk + risposta = cd.get(rispost_qid) + if risposta and not risposta_parente: + msg = 'Questo campo è valorazzare solo nel caso di risposta "Si" nella domanda precedente' + self.add_error(rispost_qid, msg) + + return cd + + def validate_questionario(self, result, **kwargs): + form = self + if form.is_valid(): + cd = form.cleaned_data + + if 'org_servizi' not in result.response_json: + result.response_json['org_servizi'] = dict() + + risposte = {k.replace('qid_', ''): v for k, v in cd.items() if k.startswith('qid')} + result.response_json['org_servizi'] = risposte + result.save() + return True + + return False + + class RespondToCourseSurveyForm(ModelForm): class Meta: model = Survey @@ -253,33 +318,33 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields.pop('text') - # survey = self.instance - # responses = survey.get_responses_dict(self.me) - # for question in survey.get_questions(): - # if not question.is_active: - # # skip inactive questions - # continue - # - # qid = question.qid - # self.fields[qid] = forms.CharField(label=question.text) - # field = self.fields[qid] - # - # if qid in responses: - # # Set input values if user has already voted - # response_for_question = responses[qid] - # field.initial = response_for_question['response'] - # - # # Disable editing after N time passed since user voted - # delta = datetime.now() - response_for_question['object'].created_at - # if delta >= timedelta(days=2): - # field.widget.attrs["disabled"] = "disabled" - # - # if survey.is_course_admin(self.me, self.course): - # # Director of course can see questions but cannot vote - # field.widget.attrs["disabled"] = "disabled" - # - # if question.required: - # field.required = True - # field.widget.attrs["class"] = "required" - # else: - # field.required = False + survey = self.instance + responses = survey.get_responses_dict(self.me, self.course) + for question in survey.get_questions(): + if not question.is_active: + # skip inactive questions + continue + + qid = question.qid + self.fields[qid] = forms.CharField(label=question.text) + field = self.fields[qid] + + if qid in responses: + # Set input values if user has already voted + response_for_question = responses[qid] + field.initial = response_for_question['response'] + + # Disable editing after N time passed since user voted + delta = datetime.now() - response_for_question['object'].created_at + if delta >= timedelta(days=2): + field.widget.attrs["disabled"] = "disabled" + + if survey.is_course_admin(self.me, self.course): + # Director of course can see questions but cannot vote + field.widget.attrs["disabled"] = "disabled" + + if question.required: + field.required = True + field.widget.attrs["class"] = "required" + else: + field.required = False diff --git a/survey/migrations/0009_auto_20191104_1435.py b/survey/migrations/0009_auto_20191104_1435.py new file mode 100644 index 000000000..dc9e416a3 --- /dev/null +++ b/survey/migrations/0009_auto_20191104_1435.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-11-04 14:35 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0008_question_order'), + ] + + operations = [ + migrations.AlterField( + model_name='question', + name='question_type', + field=models.CharField(blank=True, choices=[('text', 'text'), ('radio', 'radio'), ('select', 'select'), ('select-multiple', 'Select Multiple'), ('integer', 'integer'), ('boolean', 'boolean')], default='text', max_length=100, null=True), + ), + ] diff --git a/survey/migrations/0010_question_anchor.py b/survey/migrations/0010_question_anchor.py new file mode 100644 index 000000000..73558286c --- /dev/null +++ b/survey/migrations/0010_question_anchor.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-11-04 15:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0009_auto_20191104_1435'), + ] + + operations = [ + migrations.AddField( + model_name='question', + name='anchor', + field=models.CharField(blank=True, max_length=100, null=True), + ), + ] diff --git a/survey/models.py b/survey/models.py index 0eeb286c3..f915ee086 100644 --- a/survey/models.py +++ b/survey/models.py @@ -19,12 +19,12 @@ class Survey(models.Model): def get_questions(self): return self.question_set.all() - def get_my_responses(self, user): - return SurveyResult.objects.filter(user=user, survey=self) + def get_my_responses(self, user, course): + return SurveyResult.objects.filter(survey=self, user=user, course=course) - def get_responses_dict(self, me): + def get_responses_dict(self, me, course): d = dict() - for r in self.get_my_responses(me): + for r in self.get_my_responses(me, course): qid = r.question.qid if qid not in d: d[qid] = dict(response=r.response, object=r) @@ -71,6 +71,7 @@ class Question(models.Model): SELECT = 'select' SELECT_MULTIPLE = 'select-multiple' INTEGER = 'integer' + BOOLEAN = 'boolean' QUESTION_TYPES = ( (TEXT, 'text'), @@ -78,6 +79,7 @@ class Question(models.Model): (SELECT, 'select'), (SELECT_MULTIPLE, 'Select Multiple'), (INTEGER, 'integer'), + (BOOLEAN, 'boolean'), ) text = models.CharField(max_length=255) @@ -88,6 +90,7 @@ class Question(models.Model): question_group = models.ForeignKey('QuestionGroup', null=True, blank=True) question_type = models.CharField(max_length=100, choices=QUESTION_TYPES, default=TEXT, null=True, blank=True) + anchor = models.CharField(max_length=100, null=True, blank=True) @property def qid(self): @@ -104,13 +107,14 @@ def __str__(self): class SurveyResult(models.Model): from .forms import (QuestionarioPaginaIniziale, SelectDirettoreCorsoForm, ValutazioneDirettoreCorsoForm, ValutazioneUtilitaLezioniForm, - ValutazioneDocenteCorsoForm,) + ValutazioneDocenteCorsoForm, ValutazioneOrganizzazioneServiziForm,) INIZIO = 'in' SELEZIONA_DIRETTORE = 'sd' VALUTAZIONE_DIRETTORE = 'vd' VALUTAZIONE_LEZIONI = 'vl' VALUTAZIONE_DOCENTE = 'dv' + VALUTAZIONE_ORG_SERVIZI = 'os' GRAZIE = 'gr' # Not for model field choices use @@ -119,8 +123,9 @@ class SurveyResult(models.Model): SELEZIONA_DIRETTORE: (1, SelectDirettoreCorsoForm, VALUTAZIONE_DIRETTORE), VALUTAZIONE_DIRETTORE: (2, ValutazioneDirettoreCorsoForm, VALUTAZIONE_LEZIONI), VALUTAZIONE_LEZIONI: (3, ValutazioneUtilitaLezioniForm, VALUTAZIONE_DOCENTE), - VALUTAZIONE_DOCENTE: (4, ValutazioneDocenteCorsoForm, GRAZIE), - GRAZIE: (5, None, None), + VALUTAZIONE_DOCENTE: (4, ValutazioneDocenteCorsoForm, VALUTAZIONE_ORG_SERVIZI), + VALUTAZIONE_ORG_SERVIZI: (5, ValutazioneOrganizzazioneServiziForm, GRAZIE), + GRAZIE: (6, None, None), } user = models.ForeignKey(Persona) @@ -179,6 +184,10 @@ def get_uncompleted_valutazione_docente_lezione(self): def current_step(self): return self.response_json.get('step') + @property + def final_step_id(self): + return SurveyResult.STEPS[SurveyResult.GRAZIE][0] + class Meta: verbose_name = "Risposta dell'utente" verbose_name_plural = "Risposte degli utenti" diff --git a/survey/templates/corso_questionario_di_gradimento.html b/survey/templates/corso_questionario_di_gradimento.html index d0cae7470..7cc2f36e0 100644 --- a/survey/templates/corso_questionario_di_gradimento.html +++ b/survey/templates/corso_questionario_di_gradimento.html @@ -52,8 +52,9 @@ {% include template %} - {% elif step == 5 %} - {% include 'survey_step_5_inc.html' %} + + {% elif step == survey_result.final_step_id %} + {% include template %} {% else %} {% include 'survey_step_0_inc.html' %} {% endif %} diff --git a/survey/templates/survey_step_5_inc.html b/survey/templates/survey_step_5_inc.html index a14802872..7f119ba77 100644 --- a/survey/templates/survey_step_5_inc.html +++ b/survey/templates/survey_step_5_inc.html @@ -1,4 +1,4 @@ -
-

Grazie.

-

Grazie per la compilazione di tutto il questionario.

-
+{% load bootstrap3 %} + +

Organizzazione e servizi

+{% bootstrap_form form %} diff --git a/survey/templates/survey_step_6_inc.html b/survey/templates/survey_step_6_inc.html new file mode 100644 index 000000000..a14802872 --- /dev/null +++ b/survey/templates/survey_step_6_inc.html @@ -0,0 +1,4 @@ +
+

Grazie.

+

Grazie per la compilazione di tutto il questionario.

+
diff --git a/survey/views.py b/survey/views.py index 8a9e8e57f..3868f128c 100644 --- a/survey/views.py +++ b/survey/views.py @@ -115,7 +115,8 @@ def course_survey(request, me, pk): (2): Valutazione direttore (3): Valutazione lezioni (4): Valutazione di ogni docente di ogni lezione - (5): Grazie. + (5): Valutazione organizzazione e servizi + (6): Grazie. """ # Valida form collegaga allo step attuale From 36586c1621960b8f66b84e6f6962b7a23a0348d8 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Tue, 5 Nov 2019 12:23:16 +0100 Subject: [PATCH 51/79] =?UTF-8?q?FIXED:=20(GAIA-206)=20blocco=20logica=20s?= =?UTF-8?q?ottostante=20se=20il=20questionario=20=C3=A8=20gi=C3=A0=20concl?= =?UTF-8?q?uso.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- survey/models.py | 4 ++++ survey/views.py | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/survey/models.py b/survey/models.py index f915ee086..d8be70d1e 100644 --- a/survey/models.py +++ b/survey/models.py @@ -184,6 +184,10 @@ def get_uncompleted_valutazione_docente_lezione(self): def current_step(self): return self.response_json.get('step') + @property + def concluso(self): + return True if self.current_step == SurveyResult.GRAZIE else False + @property def final_step_id(self): return SurveyResult.STEPS[SurveyResult.GRAZIE][0] diff --git a/survey/views.py b/survey/views.py index 3868f128c..83594a58d 100644 --- a/survey/views.py +++ b/survey/views.py @@ -48,6 +48,11 @@ def course_survey(request, me, pk): result, created = SurveyResult.objects.get_or_create(course=course, user=me, survey=survey) + + if result.concluso: + messages.success(request, "Grazie per la compilazione di tutto il questionario.") + return redir_to_course + context['survey_result'] = result # Valorizza il campo json senza dati (keys vuoti) From 8c83c6f9837400dfd78e90284f475e2be8f75367 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Tue, 5 Nov 2019 16:48:44 +0100 Subject: [PATCH 52/79] ADDED: (GAIA-206) nuovo report per questionario --- .../aspirante_corso_base_scheda.html | 1 + survey/models.py | 98 +++++++++++++++++-- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/formazione/templates/aspirante_corso_base_scheda.html b/formazione/templates/aspirante_corso_base_scheda.html index b154bc98e..39af6f8c3 100644 --- a/formazione/templates/aspirante_corso_base_scheda.html +++ b/formazione/templates/aspirante_corso_base_scheda.html @@ -98,6 +98,7 @@

  • Compila il Questionario di gradimento
  • {% if puo_modificare %}
  • Invia il Questionario di gradimento
  • +
  • Scarica i risultati
  • {% endif %}

    diff --git a/survey/models.py b/survey/models.py index d8be70d1e..6aa57be53 100644 --- a/survey/models.py +++ b/survey/models.py @@ -145,22 +145,100 @@ def get_responses_for_course(cls, course): def generate_report_for_course(cls, course): import csv from django.shortcuts import HttpResponse + from formazione.models import LezioneCorsoBase filename = "Questionario di gradimento [%s].csv" % course.nome response = HttpResponse(content_type='text/csv') response['Content-Disposition'] = 'attachment; filename="%s"' % filename writer = csv.writer(response, delimiter=';') - writer.writerow(['Corso', 'Domanda', 'Risposta', 'Creato', 'Modificato']) - - for result in cls.get_responses_for_course(course): - writer.writerow([ - course.nome, - result.question, - result.response, - result.created_at, - result.updated_at - ]) + + responses_to_q = cls.get_responses_for_course(course) + responses_with_json = responses_to_q.values_list('response_json', flat=True) + + def _direttori(result): + rows = list() + direttori = result.response_json['direttori'] + for persona_pk, response_data in direttori.items(): + p = Persona.objects.get(pk=persona_pk) + for domanda_pk, risposta in response_data.items(): + domanda = Question.objects.get(pk=domanda_pk) + rows.append([p, domanda.text, risposta]) + return rows + + def _utilita_lezioni_rows(result): + rows = list() + utilita_lezioni_rows = result.response_json['utilita_lezioni'] + for lezione_pk, voto in utilita_lezioni_rows.items(): + lezione = LezioneCorsoBase.objects.get(pk=lezione_pk) + rows.append([lezione.nome, voto]) + return rows + + def _org_servizi(result): + rows = list() + org_servizi_rows = result.response_json['org_servizi'] + for domanda_pk, voto in org_servizi_rows.items(): + domanda = Question.objects.get(pk=domanda_pk) + rows.append([domanda.text, voto]) + return rows + + def _valutazione_docenti(result): + rows = list() + lezioni_rows = result.response_json['lezioni'] + + for docente_pk, lezione_data in lezioni_rows.items(): + docente = Persona.objects.get(pk=docente_pk) + + for lezione_pk, data in lezione_data.items(): + lezione = LezioneCorsoBase.objects.get(pk=lezione_pk) + + for domanda_pk, risposta in data.items(): + if domanda_pk == 'completed': continue + domanda = Question.objects.get(pk=domanda_pk) + + rows.append([docente, lezione, domanda, risposta]) + + return rows + + if responses_with_json: + columns = [ + '1. Valutazione direttore (nome)', 'Domanda', 'Risposta', + '2. Utilita lezione', 'Voto', + '3. Valutazione docente (nome)', 'Lezione', 'Domanda', 'Voto', + '4. Org. e servizi (domanda)', 'Risposta', + 'Creato', 'Modificato', + ] + + writer.writerow(columns) + for result in responses_to_q: + direttori_rows = _direttori(result) + utilita_lezioni_rows = _utilita_lezioni_rows(result) + valutazione_docente_rows = _valutazione_docenti(result) + org_servizi_rows = _org_servizi(result) + + for row in direttori_rows: + writer.writerow(row) + + for row in utilita_lezioni_rows: + writer.writerow([''] * 3 + row) + + for row in valutazione_docente_rows: + writer.writerow([''] * 5 + row) + + for row in org_servizi_rows: + writer.writerow([''] * 9 + row) + + else: + columns = ['Corso', 'Domanda', 'Risposta', 'Creato', 'Modificato'] + writer.writerow(columns) + for result in responses_to_q: + writer.writerow([ + course.nome, + result.question, + result.response, + result.created_at, + result.updated_at + ]) return response From 0c13b96a8deb1fa9d48c8f98d828276ba5ac65ad Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 10:06:58 +0100 Subject: [PATCH 53/79] FIXED: (GAIA-206) new field (migration 0011) --- .../0011_surveyresult_new_version.py | 20 +++++++++++++++++++ survey/models.py | 13 +++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 survey/migrations/0011_surveyresult_new_version.py diff --git a/survey/migrations/0011_surveyresult_new_version.py b/survey/migrations/0011_surveyresult_new_version.py new file mode 100644 index 000000000..15f24b24f --- /dev/null +++ b/survey/migrations/0011_surveyresult_new_version.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-11-06 09:37 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('survey', '0010_question_anchor'), + ] + + operations = [ + migrations.AddField( + model_name='surveyresult', + name='new_version', + field=models.BooleanField(default=False), + ), + ] diff --git a/survey/models.py b/survey/models.py index 6aa57be53..749aa4c38 100644 --- a/survey/models.py +++ b/survey/models.py @@ -132,6 +132,7 @@ class SurveyResult(models.Model): course = models.ForeignKey('formazione.CorsoBase', blank=True, null=True) survey = models.ForeignKey(Survey) question = models.ForeignKey(Question, blank=True, null=True) + new_version = models.BooleanField(default=False, blank=True) response = models.TextField(max_length=1000, blank=True, null=True) response_json = JSONField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) @@ -153,9 +154,6 @@ def generate_report_for_course(cls, course): writer = csv.writer(response, delimiter=';') - responses_to_q = cls.get_responses_for_course(course) - responses_with_json = responses_to_q.values_list('response_json', flat=True) - def _direttori(result): rows = list() direttori = result.response_json['direttori'] @@ -200,7 +198,14 @@ def _valutazione_docenti(result): return rows + # Trova le risposte per il questionario di questo corso + responses_to_q = cls.get_responses_for_course(course) + + # Verifica se le risposte sono JSON (nuova modalità) + responses_with_json = responses_to_q.filter(new_version=True).exists() if responses_with_json: + + # Report per le risposte in JSON columns = [ '1. Valutazione direttore (nome)', 'Domanda', 'Risposta', '2. Utilita lezione', 'Voto', @@ -229,6 +234,8 @@ def _valutazione_docenti(result): writer.writerow([''] * 9 + row) else: + # Report per le risposte vecchio formato (prima del rilascio) + # Ogni risposta -> record db columns = ['Corso', 'Domanda', 'Risposta', 'Creato', 'Modificato'] writer.writerow(columns) for result in responses_to_q: From 89aca1701514700b322d86d1670981197700fc51 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 10:14:54 +0100 Subject: [PATCH 54/79] FIXED: (GAIA-206) rimosso il link sul questionario dal nav-tab sulla pagina del corso --- formazione/templates/aspirante_corso_base_scheda.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formazione/templates/aspirante_corso_base_scheda.html b/formazione/templates/aspirante_corso_base_scheda.html index 39af6f8c3..a28531fff 100644 --- a/formazione/templates/aspirante_corso_base_scheda.html +++ b/formazione/templates/aspirante_corso_base_scheda.html @@ -46,7 +46,7 @@

    {% can_show_tab_questionario as show_tab_questionario %} {% if show_tab_questionario %} -
  • Questionario
  • +
  • Questionario
  • {% endif %} From 1936e213260bf8927220f9726abe6b1d04337d16 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 10:45:27 +0100 Subject: [PATCH 55/79] FIXED: (GAIA-206) verifiche vecchio/nuovo questionario --- survey/models.py | 4 +++- survey/views.py | 17 ++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/survey/models.py b/survey/models.py index 749aa4c38..a6dfa3830 100644 --- a/survey/models.py +++ b/survey/models.py @@ -267,7 +267,9 @@ def get_uncompleted_valutazione_docente_lezione(self): @property def current_step(self): - return self.response_json.get('step') + if self.response_json is not None: + return self.response_json.get('step') + return None @property def concluso(self): diff --git a/survey/views.py b/survey/views.py index 83594a58d..ed8d74fa0 100644 --- a/survey/views.py +++ b/survey/views.py @@ -45,9 +45,14 @@ def course_survey(request, me, pk): step, form, next_step = step_in_dict # Crea/trova oggetto per le risposte - result, created = SurveyResult.objects.get_or_create(course=course, - user=me, - survey=survey) + try: + result, created = SurveyResult.objects.get_or_create(course=course, + user=me, + survey=survey) + except SurveyResult.MultipleObjectsReturned: + if SurveyResult.get_responses_for_course(course).filter(new_version=False).exists(): + messages.error(request, "Il questionario è stato già compilato.") + return redir_to_course if result.concluso: messages.success(request, "Grazie per la compilazione di tutto il questionario.") @@ -137,24 +142,18 @@ def course_survey(request, me, pk): # Valutazione docente -> lezione # Prossimo step rimane sempre lo stesso finchè non sono state # compilate tutte le combinazioni docente\lezione. - - # print(result.get_uncompleted_valutazione_docente_lezione()) if step == 4 and result.get_uncompleted_valutazione_docente_lezione()[0] is not None: return redirect(survey_url + "?step=%s" % SurveyResult.VALUTAZIONE_DOCENTE) result.response_json['step'] = next_step result.save() - # print(1, step, form, next_step) - # Rindirizza next_step_qs = "?step=%s" % next_step if next_step is not None else '' next_step_reverse = survey_url + next_step_qs return redirect(next_step_reverse) - # print(0, step, form, next_step) - context['template'] = 'survey_step_%s_inc.html' % step return 'corso_questionario_di_gradimento.html', context From 213165e095afae471333502f9ba0972d77dfabe0 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 10:50:18 +0100 Subject: [PATCH 56/79] FIXED: (GAIA-206) aggiungere colonna persona al csv --- survey/models.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/survey/models.py b/survey/models.py index a6dfa3830..c6714ab5b 100644 --- a/survey/models.py +++ b/survey/models.py @@ -207,6 +207,7 @@ def _valutazione_docenti(result): # Report per le risposte in JSON columns = [ + 'Utente', '1. Valutazione direttore (nome)', 'Domanda', 'Risposta', '2. Utilita lezione', 'Voto', '3. Valutazione docente (nome)', 'Lezione', 'Domanda', 'Voto', @@ -221,17 +222,19 @@ def _valutazione_docenti(result): valutazione_docente_rows = _valutazione_docenti(result) org_servizi_rows = _org_servizi(result) + persona_in_list = [str(result.user)] + for row in direttori_rows: - writer.writerow(row) + writer.writerow(persona_in_list + row) for row in utilita_lezioni_rows: - writer.writerow([''] * 3 + row) + writer.writerow(persona_in_list + [''] * 3 + row) for row in valutazione_docente_rows: - writer.writerow([''] * 5 + row) + writer.writerow(persona_in_list + [''] * 5 + row) for row in org_servizi_rows: - writer.writerow([''] * 9 + row) + writer.writerow(persona_in_list + [''] * 9 + row) else: # Report per le risposte vecchio formato (prima del rilascio) From 635982ccd0de42f061575d0962e6faf7f8a1d3c7 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 11:49:55 +0100 Subject: [PATCH 57/79] FIXED: (GAIA-206) varie modifiche --- survey/forms.py | 3 ++- survey/models.py | 18 +++++++++--------- survey/views.py | 11 +++++++++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/survey/forms.py b/survey/forms.py index ade8aeb2e..c9784e7fe 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -137,7 +137,8 @@ def __init__(self, *args, **kwargs): # Risposte precedentemente salvate response_lezioni = self.survey_result.response_json['utilita_lezioni'] - for lezione in self.course.get_lezioni_precaricate(): + # for lezione in self.course.get_lezioni_precaricate(): + for lezione in self.course.lezioni.all(): lezione_pk = 'lezioni_pk_%s' % lezione.pk # Crea i campi con 10 radio-button (0-10) diff --git a/survey/models.py b/survey/models.py index c6714ab5b..80ec08eae 100644 --- a/survey/models.py +++ b/survey/models.py @@ -202,12 +202,12 @@ def _valutazione_docenti(result): responses_to_q = cls.get_responses_for_course(course) # Verifica se le risposte sono JSON (nuova modalità) - responses_with_json = responses_to_q.filter(new_version=True).exists() - if responses_with_json: + responses_with_json = responses_to_q.filter(new_version=True) + if responses_with_json.exists(): + responses_to_q = responses_with_json # Report per le risposte in JSON columns = [ - 'Utente', '1. Valutazione direttore (nome)', 'Domanda', 'Risposta', '2. Utilita lezione', 'Voto', '3. Valutazione docente (nome)', 'Lezione', 'Domanda', 'Voto', @@ -222,25 +222,25 @@ def _valutazione_docenti(result): valutazione_docente_rows = _valutazione_docenti(result) org_servizi_rows = _org_servizi(result) - persona_in_list = [str(result.user)] - for row in direttori_rows: - writer.writerow(persona_in_list + row) + writer.writerow(row) for row in utilita_lezioni_rows: - writer.writerow(persona_in_list + [''] * 3 + row) + writer.writerow([''] * 3 + row) for row in valutazione_docente_rows: - writer.writerow(persona_in_list + [''] * 5 + row) + writer.writerow([''] * 5 + row) for row in org_servizi_rows: - writer.writerow(persona_in_list + [''] * 9 + row) + writer.writerow([''] * 9 + row) else: # Report per le risposte vecchio formato (prima del rilascio) # Ogni risposta -> record db columns = ['Corso', 'Domanda', 'Risposta', 'Creato', 'Modificato'] writer.writerow(columns) + + responses_to_q = responses_to_q.exclude(new_version=True) for result in responses_to_q: writer.writerow([ course.nome, diff --git a/survey/views.py b/survey/views.py index ed8d74fa0..105f42297 100644 --- a/survey/views.py +++ b/survey/views.py @@ -49,12 +49,15 @@ def course_survey(request, me, pk): result, created = SurveyResult.objects.get_or_create(course=course, user=me, survey=survey) + result.new_version = True + result.save() + except SurveyResult.MultipleObjectsReturned: if SurveyResult.get_responses_for_course(course).filter(new_version=False).exists(): messages.error(request, "Il questionario è stato già compilato.") return redir_to_course - if result.concluso: + if result.concluso and step != 6: messages.success(request, "Grazie per la compilazione di tutto il questionario.") return redir_to_course @@ -115,7 +118,11 @@ def course_survey(request, me, pk): context['valutazione_docente'] = Persona.objects.get(pk=docente) context['valutazione_lezione'] = LezioneCorsoBase.objects.get(pk=lezione) else: - messages.warning(request, "Questo step del questionario è stato già completato.") + result.response_json['step'] = next_step + result.save() + + messages.warning(request, "Non ci sono docenti da valutare o " + "questo step è stato già completato.") return redirect(survey_url) # Arriva form da validare e salvare From 3373b835e92d25d4f4e4762b8a7c7551a903e185 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 12:03:13 +0100 Subject: [PATCH 58/79] FIXED: (GAIA-206) blocco compilazione questionario per direttori/altre relative deleghe --- survey/views.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/survey/views.py b/survey/views.py index 105f42297..810f94c76 100644 --- a/survey/views.py +++ b/survey/views.py @@ -29,6 +29,10 @@ def course_survey(request, me, pk): if not survey.can_vote(me, course): return redirect(ERRORE_PERMESSI) + if survey.is_course_admin(me, course): + messages.error(request, 'Direttori e amministratori del corso non possono compilare il questionario.') + return redir_to_course + survey_url = reverse("survey:course", args=[pk,]) context = { From 4957bc728070764ce6102cf2b780369195ae1412 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 15:34:22 +0100 Subject: [PATCH 59/79] FIXED: (GAIA-206) inserito docenti esterni nella compilazione --- formazione/models.py | 2 +- survey/forms.py | 37 +++++++++++++++++++++++++++---------- survey/models.py | 7 ++++++- survey/views.py | 6 +++++- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/formazione/models.py b/formazione/models.py index ba5a2e5a5..f138ef4a8 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -1231,7 +1231,7 @@ def docenti_esterni_corso(self): return list(set(filter(bool, docenti_esterni_result))) except: - # per qualsiasi problema. fatto in fretta, per evitare 500. + # per qualsiasi problema, per evitare 500. return list() def can_modify(self, me): diff --git a/survey/forms.py b/survey/forms.py index c9784e7fe..dbe6c1e59 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -55,8 +55,6 @@ def process_qid(self, cd): class QuestionarioPaginaIniziale(QuestionarioForm): - # def process(self, result, next_step): - # super().process(result, next_step) pass @@ -223,27 +221,46 @@ def generate_json_structure(self): if lezione_pk not in lezioni_struttura[docente_pk]: lezioni_struttura[docente_pk][lezione_pk] = dict(completed=False) - # # Per docenti esterni - # docente_esterno = lezione.docente_esterno - # if docente_esterno: - # if docente_esterno not in lezioni_struttura: - # lezioni_struttura[docente_esterno] = dict() - # lezioni_struttura[docente_esterno][lezione.pk] = dict(completed=False) + for docente_esterno in self.course.docenti_esterni_corso(): + docente_esterno_prefix = 'de_%s' % docente_esterno + + for lezione in self.course.lezioni.all(): + if not lezione.docente_esterno: + continue + if docente_esterno not in lezione.docente_esterno: + continue + + if docente_esterno not in lezioni_struttura: + lezioni_struttura[docente_esterno_prefix] = dict() + + lezioni_struttura[docente_esterno_prefix][lezione.pk] = dict(completed=False) # Salva la struttura self.survey_result.save() def validate_questionario(self, result, **kwargs): + prefix = 'de' form = self if form.is_valid(): cd = form.cleaned_data - docente, lezione = cd['docente_lezione_pk'].split('_') + if cd['docente_lezione_pk'].startswith('%s_' % prefix): + docente, lezione = [i for i in cd['docente_lezione_pk'].split('_') if i != prefix] + docente = "%s_%s" % (prefix, docente) + else: + docente, lezione = cd['docente_lezione_pk'].split('_') risposte = self.process_qid(cd) risposte['completed'] = True - result.response_json['lezioni'][docente][lezione] = risposte + docente_key = result.response_json['lezioni'][docente] + if int(lezione) in docente_key: + lezione = int(lezione) + + elif str(lezione) in docente_key: + lezione = str(lezione) + + result.response_json['lezioni'][docente][lezione].update(risposte) result.save() return True diff --git a/survey/models.py b/survey/models.py index 80ec08eae..42024fe37 100644 --- a/survey/models.py +++ b/survey/models.py @@ -265,7 +265,12 @@ def get_uncompleted_valutazione_docente_lezione(self): for lezione_pk, lezione_data in lezione_data.items(): if lezione_data['completed']: continue - return int(docente_pk), int(lezione_pk) + + if docente_pk.startswith('de_'): + return docente_pk, int(lezione_pk) + else: + return int(docente_pk), int(lezione_pk) + return None, None @property diff --git a/survey/views.py b/survey/views.py index 810f94c76..9b2efda23 100644 --- a/survey/views.py +++ b/survey/views.py @@ -119,7 +119,11 @@ def course_survey(request, me, pk): elif step_in_request == SurveyResult.VALUTAZIONE_DOCENTE: docente, lezione = result.get_uncompleted_valutazione_docente_lezione() if docente and lezione: - context['valutazione_docente'] = Persona.objects.get(pk=docente) + if isinstance(docente, int): + context['valutazione_docente'] = Persona.objects.get(pk=docente) + else: + context['valutazione_docente'] = docente.replace('de_', '') + context['valutazione_lezione'] = LezioneCorsoBase.objects.get(pk=lezione) else: result.response_json['step'] = next_step From b4bff0d52f02a39b5c3ca58b80c4439d801e4114 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 15:37:56 +0100 Subject: [PATCH 60/79] FIXED: (GAIA-206) inserito gestione docenti esterni nel report --- survey/models.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/survey/models.py b/survey/models.py index 42024fe37..6db259d2a 100644 --- a/survey/models.py +++ b/survey/models.py @@ -185,7 +185,10 @@ def _valutazione_docenti(result): lezioni_rows = result.response_json['lezioni'] for docente_pk, lezione_data in lezioni_rows.items(): - docente = Persona.objects.get(pk=docente_pk) + if docente_pk.startswith('de_'): + docente = docente_pk + else: + docente = Persona.objects.get(pk=docente_pk) for lezione_pk, data in lezione_data.items(): lezione = LezioneCorsoBase.objects.get(pk=lezione_pk) From 13bb4fbe927c59323bf7c15f859efab00f5051af Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 15:56:41 +0100 Subject: [PATCH 61/79] FIXED: (GAIA-206) inserito data creazione/ultima modifica del risultato nel report --- survey/models.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/survey/models.py b/survey/models.py index 6db259d2a..4d33fa071 100644 --- a/survey/models.py +++ b/survey/models.py @@ -226,16 +226,16 @@ def _valutazione_docenti(result): org_servizi_rows = _org_servizi(result) for row in direttori_rows: - writer.writerow(row) + writer.writerow(row + [''] * 8 + [result.created_at, result.updated_at]) for row in utilita_lezioni_rows: - writer.writerow([''] * 3 + row) + writer.writerow([''] * 3 + row + [''] * 6 + [result.created_at, result.updated_at]) for row in valutazione_docente_rows: - writer.writerow([''] * 5 + row) + writer.writerow([''] * 5 + row + [''] * 2 + [result.created_at, result.updated_at]) for row in org_servizi_rows: - writer.writerow([''] * 9 + row) + writer.writerow([''] * 9 + row + [result.created_at, result.updated_at]) else: # Report per le risposte vecchio formato (prima del rilascio) From d99c395afc685e6c66dda5508a7ae6f479c3a2ed Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 16:52:35 +0100 Subject: [PATCH 62/79] FIXED: (GAIA-206) verifica amministratore (RF, DI, PR, DC) puo fare quest. se partecipante --- survey/views.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/survey/views.py b/survey/views.py index 9b2efda23..13fd004e6 100644 --- a/survey/views.py +++ b/survey/views.py @@ -30,8 +30,9 @@ def course_survey(request, me, pk): return redirect(ERRORE_PERMESSI) if survey.is_course_admin(me, course): - messages.error(request, 'Direttori e amministratori del corso non possono compilare il questionario.') - return redir_to_course + if me.pk not in course.partecipazioni_confermate().values_list('persona__pk', flat=True): + messages.error(request, 'Direttori e amministratori del corso non possono compilare il questionario.') + return redir_to_course survey_url = reverse("survey:course", args=[pk,]) From ffa999b86740172b6f0bd9248f24dac7f2637f7f Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 17:02:07 +0100 Subject: [PATCH 63/79] FIXED: (GAIA-206) aggiungere voce Nessuna valutazione agli radio input 1-10 --- survey/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/survey/forms.py b/survey/forms.py index dbe6c1e59..b9ed2e42d 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -10,7 +10,7 @@ class QuestionarioForm(forms.Form): QGROUP_DOCENTI = 2 QGROUP_ORG_SERVIZI = 3 - CHOICES_1_10 = [(i, i) for i in range(1, 11)] + CHOICES_1_10 = [(0,'Nessuna valutazione')] + [(i, i) for i in range(1, 11)] step = forms.CharField(widget=forms.HiddenInput()) From fff784c14f4aa1bd21ce3b9b33cd4374d863de7a Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 17:10:34 +0100 Subject: [PATCH 64/79] FIXED: (GAIA-224) foglio firme (num righe) --- formazione/templates/pdf_firme_lezione.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formazione/templates/pdf_firme_lezione.html b/formazione/templates/pdf_firme_lezione.html index 6aa779a96..3067c0cdd 100644 --- a/formazione/templates/pdf_firme_lezione.html +++ b/formazione/templates/pdf_firme_lezione.html @@ -64,7 +64,7 @@

    {{ lezione }} del {{ lezione.inizio|date:"SHORT_DATE_FORMAT" }}

    {{ iscritto.cognome_nome_completo }}   - {% if forloop.last or forloop.counter|divisibleby:14 %} + {% if forloop.last or forloop.counter|divisibleby:12 %}
    Firma Direttore
    From 4890b3e481a8febe52a984939707ab407759e79d Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Wed, 6 Nov 2019 17:45:03 +0100 Subject: [PATCH 65/79] FIXED: (GAIA-236) mostra p.ammissione nel template pdf_corso_esame_verbale.html --- formazione/templates/pdf_corso_esame_verbale.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formazione/templates/pdf_corso_esame_verbale.html b/formazione/templates/pdf_corso_esame_verbale.html index 747a3f6d6..e61530494 100644 --- a/formazione/templates/pdf_corso_esame_verbale.html +++ b/formazione/templates/pdf_corso_esame_verbale.html @@ -194,7 +194,7 @@ {% if corso.titolo_cri.scheda_prevede_esame %} {% if not p.get_esito_esame_display %} - {% if p.idoneo %}IDONEO{% else %}NON IDONEO{% endif %} + {{ p.get_ammissione_display }} {% else %} {{ p.get_esito_esame_display|default:"N/D" }} {% endif %} From ab0a1d8a987f2a79655bd4a7beda660082e1e166 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 7 Nov 2019 10:37:53 +0100 Subject: [PATCH 66/79] FIXED: (GAIA-231) messaggio per autoinvitarsi al corso agli admin --- formazione/templates/aspirante_corso_base_scheda.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/formazione/templates/aspirante_corso_base_scheda.html b/formazione/templates/aspirante_corso_base_scheda.html index a28531fff..3930fdcd6 100644 --- a/formazione/templates/aspirante_corso_base_scheda.html +++ b/formazione/templates/aspirante_corso_base_scheda.html @@ -161,6 +161,16 @@

    Il corso non è ancora attivo

    {% endif %} + {% if puo_modificare and corso.stato == corso.ATTIVO and not corso.terminabile %} +
    +

    +

    Vuoi partecipare a questo corso?

    + Sei amministratore del corso (Responsabile della formazione, Direttore del corso, Comissario o Presidente) e vorresti partecipare a questo corso? + Per partecipare dovresti invitare se stesso sulla pagina "Iscrizioni" inserendo il tuo Codice Fiscale. +

    +
    + {% endif %} + {% if puo_modificare and corso.terminabile %}

    Terminazione corso

    From 181fa2b84c3e2cf85b45d83e8c2d105d8eaf1c9b Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 7 Nov 2019 10:45:56 +0100 Subject: [PATCH 67/79] FIXED: (GAIA-231) messaggio per autoinvitarsi al corso agli admin --- formazione/templates/aspirante_corso_base_scheda.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formazione/templates/aspirante_corso_base_scheda.html b/formazione/templates/aspirante_corso_base_scheda.html index 3930fdcd6..2bdf1427a 100644 --- a/formazione/templates/aspirante_corso_base_scheda.html +++ b/formazione/templates/aspirante_corso_base_scheda.html @@ -166,7 +166,7 @@

    Il corso non è ancora attivo

    Vuoi partecipare a questo corso?

    Sei amministratore del corso (Responsabile della formazione, Direttore del corso, Comissario o Presidente) e vorresti partecipare a questo corso? - Per partecipare dovresti invitare se stesso sulla pagina "Iscrizioni" inserendo il tuo Codice Fiscale. + Per partecipare puoi invitarti cliccando sulla pagina iscrizioni ed inserendo il tuo Codice Fiscale.

    {% endif %} From 565eec997a0659bbb908a2e66f0edd4a548cf49e Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 7 Nov 2019 14:41:18 +0100 Subject: [PATCH 68/79] FIXED: (GAIA-235) visibilita' sezioni menu profilo sistemato (con sessioni) --- anagrafica/profile/menu.py | 27 ++++++++++++++++++++ anagrafica/templates/anagrafica_profilo.html | 3 ++- anagrafica/templatetags/utils.py | 10 ++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/anagrafica/profile/menu.py b/anagrafica/profile/menu.py index d223b407e..cd700c1e4 100644 --- a/anagrafica/profile/menu.py +++ b/anagrafica/profile/menu.py @@ -17,11 +17,38 @@ def filter_per_role(request, me, persona, sezioni): corsi_persona = persona.corsi(corso_stato=[Corso.ATTIVO, Corso.PREPARAZIONE]) corsi_in_comune = corsi_direttore & corsi_persona + def delete_sessions(request): + session_names = ['us', 'ea',] + for n in session_names: + if n in request.session.keys(): + # print(n) + del request.session[n] + + def elenco_shortname_in_get(): + for i in [ElencoPartecipantiCorsiBase.SHORT_NAME,]: + if i in request.GET and not corsi_in_comune: + return False + return True + sezioni = OrderedDict(sezioni) if 'us' in request.GET or 'ea' in request.GET: + if 'us' in request.GET: + request.session['us'] = '' + elif 'ea' in request.GET: + request.session['ea'] = '' + return sezioni + + elif 'us' in request.session.keys() and not elenco_shortname_in_get(): + delete_sessions(request) + return sezioni + + elif 'ea' in request.session.keys() and not elenco_shortname_in_get(): + delete_sessions(request) return sezioni elif ElencoPartecipantiCorsiBase.SHORT_NAME in request.GET or corsi_in_comune: + delete_sessions(request) + SEZIONI_VISIBILI_PER_DIRETTORE = ['appartenenze', 'curriculum',] # Trova corsi del direttore e corsi della persona diff --git a/anagrafica/templates/anagrafica_profilo.html b/anagrafica/templates/anagrafica_profilo.html index 33ebef28d..ffbfc42bd 100644 --- a/anagrafica/templates/anagrafica_profilo.html +++ b/anagrafica/templates/anagrafica_profilo.html @@ -1,6 +1,7 @@ {% extends "base_vuota.html" %} {% load bootstrap3 %} +{% load utils %} {% block pagina_titolo %}{{ persona.nome_completo }}{% endblock %} @@ -17,7 +18,7 @@

    {{ persona.nome_completo }}

    {% for chiave, sezione in sezioni.items %} {% if sezione|length_is:4 or sezione.3 %}
  • - + {{ sezione.0 }}
  • diff --git a/anagrafica/templatetags/utils.py b/anagrafica/templatetags/utils.py index 13fa4a226..804e87699 100755 --- a/anagrafica/templatetags/utils.py +++ b/anagrafica/templatetags/utils.py @@ -354,8 +354,18 @@ def get_top_navbar(context): return "" + @register.filter def is_rifiutato(id): from ufficio_soci.models import Tesserino return False if Tesserino.objects.filter(persona=id, stato_richiesta=Tesserino.RIFIUTATO).count() else True +@register.simple_tag(takes_context=True) +def add_session_flag_to_profile_urls(context): + request = context['request'] + + if 'us' in request.session.keys(): + return '?us' + elif 'ea' in request.session.keys(): + return '?ea' + return '' From d0ecd1d317e1d4e86a3a922b4d806db734dd83a6 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 7 Nov 2019 16:19:28 +0100 Subject: [PATCH 69/79] FIXED: (GAIA-233) num links/allegati pagina modifica corso --- formazione/formsets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/formazione/formsets.py b/formazione/formsets.py index 4ad4d5362..27a6b8805 100644 --- a/formazione/formsets.py +++ b/formazione/formsets.py @@ -5,10 +5,10 @@ CorsoFileFormSet = modelformset_factory(CorsoFile, - form=CorsoLinkForm, extra=1, max_num=4) + form=CorsoLinkForm, extra=1, max_num=5) CorsoLinkFormSet = modelformset_factory(CorsoLink, - fields=('link',), extra=1, max_num=2) + fields=('link',), extra=1, max_num=5) CorsoSelectExtensionFormSet = modelformset_factory(CorsoEstensione, extra=1, max_num=3, form=CorsoExtensionForm, can_delete=True) From 66ca1d90a267558eed90a941fa0d8246c6188ad2 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Thu, 7 Nov 2019 16:59:11 +0100 Subject: [PATCH 70/79] FIXED: (GAIA-225) inviare mail con delibera solo a regionale --- formazione/models.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/formazione/models.py b/formazione/models.py index f138ef4a8..59e788772 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -1156,14 +1156,15 @@ def inform_presidency_with_delibera_file(self): elif self.livello in [Titolo.CDF_LIVELLO_III, Titolo.CDF_LIVELLO_IV]: pass - # Invia e-mail - Messaggio.invia_raw( - oggetto=oggetto, - corpo_html="""

    E' stato attivato un nuovo corso. La delibera si trova in allegato.

    """, - email_mittente=Messaggio.NOREPLY_EMAIL, - lista_email_destinatari=email_destinatari, - allegati=self.delibera_file - ) + if sede == REGIONALE: + # Invia e-mail + Messaggio.invia_raw( + oggetto=oggetto, + corpo_html="""

    E' stato attivato un nuovo corso. La delibera si trova in allegato.

    """, + email_mittente=Messaggio.NOREPLY_EMAIL, + lista_email_destinatari=email_destinatari, + allegati=self.delibera_file + ) if not sede == NAZIONALE: email_to = self.sede.sede_regionale.presidente() From 44ee7326bab98e610c74ef1aa953a9b258afc925 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Fri, 8 Nov 2019 12:22:06 +0100 Subject: [PATCH 71/79] FIXED: (GAIA-206) bug generazione struttura json docenti esterni che sovrascriveva dict con risultati --- survey/forms.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/survey/forms.py b/survey/forms.py index b9ed2e42d..95fc9d439 100644 --- a/survey/forms.py +++ b/survey/forms.py @@ -208,11 +208,11 @@ def generate_json_structure(self): lezioni_struttura = self.survey_result.response_json['lezioni'] for lezione in self.course.lezioni.all(): + lezione_pk = str(lezione.pk) # Per docenti (Persona) for docente_pk in lezione.docente.all().values_list('pk', flat=True): docente_pk = str(docente_pk) - lezione_pk = str(lezione.pk) # Crea dict vuoti if docente_pk not in lezioni_struttura: @@ -225,15 +225,18 @@ def generate_json_structure(self): docente_esterno_prefix = 'de_%s' % docente_esterno for lezione in self.course.lezioni.all(): + lezione_pk = str(lezione.pk) + if not lezione.docente_esterno: continue if docente_esterno not in lezione.docente_esterno: continue - if docente_esterno not in lezioni_struttura: + if docente_esterno_prefix not in lezioni_struttura: lezioni_struttura[docente_esterno_prefix] = dict() - lezioni_struttura[docente_esterno_prefix][lezione.pk] = dict(completed=False) + if lezione_pk not in lezioni_struttura[docente_esterno_prefix]: + lezioni_struttura[docente_esterno_prefix][lezione_pk] = dict(completed=False) # Salva la struttura self.survey_result.save() @@ -253,14 +256,7 @@ def validate_questionario(self, result, **kwargs): risposte = self.process_qid(cd) risposte['completed'] = True - docente_key = result.response_json['lezioni'][docente] - if int(lezione) in docente_key: - lezione = int(lezione) - - elif str(lezione) in docente_key: - lezione = str(lezione) - - result.response_json['lezioni'][docente][lezione].update(risposte) + result.response_json['lezioni'][docente][lezione] = risposte result.save() return True From adfac820b161675632d79372acb5e0a55828f19d Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Fri, 8 Nov 2019 14:50:22 +0100 Subject: [PATCH 72/79] FIXED: (GAIA-237) mostrare docenti sulla pagina del corso (ai volontari) --- .../templates/aspirante_corso_base_scheda_informazioni.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/formazione/templates/aspirante_corso_base_scheda_informazioni.html b/formazione/templates/aspirante_corso_base_scheda_informazioni.html index baf648cf0..bd563103a 100644 --- a/formazione/templates/aspirante_corso_base_scheda_informazioni.html +++ b/formazione/templates/aspirante_corso_base_scheda_informazioni.html @@ -249,7 +249,11 @@

    {% if lezione.fine %} — {{ lezione.fine.time|date:"TIME_FORMAT" }}{% endif %} {% if lezione.lezione_ore and not lezione.divisa %}({% lezione_durata lezione %}){% endif %} {% if lezione.luogo %}
    {{ lezione.luogo }}{% endif %} - {% if lezione.docente %}
    {{ lezione.docente.cognome }} {{ lezione.docente.nome }}{% endif %} + {% if lezione.docente.all %}
    + {% for docente in lezione.docente.all %} + {{ docente.nome_completo }}{% if not forloop.last %}, {% endif %} + {% endfor %} + {% endif %} {% empty %} From 63b549c87b4c6be188ba49c3e343bb30cb491b3e Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Fri, 8 Nov 2019 15:04:31 +0100 Subject: [PATCH 73/79] FIXED: (GAIA-234) count corsi per aspirante su /aspirante/corsi (modificato metodo Aspirante.corsi()) --- formazione/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/formazione/models.py b/formazione/models.py index 59e788772..1f8605c50 100755 --- a/formazione/models.py +++ b/formazione/models.py @@ -2057,7 +2057,7 @@ def corsi(self, **kwargs): :return: Un elenco di Corsi. """ corsi = CorsoBase.pubblici().filter(**kwargs) - return self.nel_raggio(corsi) + return self.nel_raggio(corsi.exclude(tipo=CorsoBase.CORSO_NUOVO)) def corso(self): partecipazione = PartecipazioneCorsoBase.con_esito_ok(persona=self.persona) From db515f234f3b5f94c86571ec385bf2fdc55ebea4 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Fri, 8 Nov 2019 15:35:17 +0100 Subject: [PATCH 74/79] FIXED: (GAIA-232) osserva corsi aggiusta logica per sede Lazio --- formazione/viste.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/formazione/viste.py b/formazione/viste.py index 6adaacdbf..f4f0dc66c 100644 --- a/formazione/viste.py +++ b/formazione/viste.py @@ -82,6 +82,10 @@ def formazione_osserva_corsi(request, me): for sede in sedi: comitati = sede.comitati_sottostanti() for comitato in comitati: + + if comitato.pk == 524: # Lazio + comitato = Sede.objects.get(pk=1638) # Area Metropolitana di Roma Capitale Coordinamento + corsi = CorsoBase.objects.filter(sede=comitato).count() if corsi: if sede not in results: From 6c4dc1ef1483dd28c4de13753aa5cee06551bb19 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Fri, 8 Nov 2019 16:49:48 +0100 Subject: [PATCH 75/79] FIXED: (GAIA-240) cambia nome file (cognome nome) nel zip di --- formazione/classes.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/formazione/classes.py b/formazione/classes.py index 931c9077f..8af94ca86 100644 --- a/formazione/classes.py +++ b/formazione/classes.py @@ -83,8 +83,8 @@ def _verifica_presenze(self, partecipanti): class GeneraReport: - ATTESTATO_FILENAME = "%s - Attestato.pdf" - SCHEDA_FILENAME = "%s - Scheda di Valutazione.pdf" + ATTESTATO_FILENAME = "%s %s - Attestato.pdf" + SCHEDA_FILENAME = "%s %s - Scheda di Valutazione.pdf" def __init__(self, request, corso, single_attestato=False): self.request = request @@ -116,7 +116,7 @@ def _download_single_attestato(self): return redirect(reverse('utente:cv_tipo', args=[Titolo.TITOLO_CRI,])) attestato = self._attestato(partecipazione) - filename = self.ATTESTATO_FILENAME % partecipazione.titolo_ottenuto.last() + filename = self.ATTESTATO_FILENAME % (partecipazione.titolo_ottenuto.last(), '') with open(attestato.file.path, 'rb') as f: pdf = f.read() @@ -137,18 +137,20 @@ def _schede(self, partecipante): """ Genera la scheda di valutazione """ scheda = partecipante.genera_scheda_valutazione(request=self.request) + persona = partecipante.persona self.archive.aggiungi_file( scheda.file.path, - self.SCHEDA_FILENAME % partecipante.persona.nome_completo + self.SCHEDA_FILENAME % (persona.cognome, persona.nome) ) return scheda def _attestato(self, partecipante): attestato = partecipante.genera_attestato(request=self.request) + persona = partecipante.persona self.archive.aggiungi_file( attestato.file.path, - self.ATTESTATO_FILENAME % partecipante.persona.nome_completo + self.ATTESTATO_FILENAME % (persona.cognome, persona.nome) ) return attestato From f4c796e9f86ae387988081cbe3a6e4acefdd63f6 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Mon, 11 Nov 2019 09:27:20 +0100 Subject: [PATCH 76/79] FIXED: (GAIA-243) blocco max 1 direttore per corsi di livello 1,2,3 --- anagrafica/viste.py | 1 - formazione/admin.py | 2 +- formazione/forms.py | 10 ++++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/anagrafica/viste.py b/anagrafica/viste.py index 13ce0baf4..50c70fac0 100755 --- a/anagrafica/viste.py +++ b/anagrafica/viste.py @@ -1069,7 +1069,6 @@ def strumenti_delegati(request, me): } form = ModuloCreazioneDelega(request.POST or None, **form_data) if model == 'corsobase': - # if oggetto.is_nuovo_corso: form = FormCreateDirettoreDelega(request.POST or None, **form_data) # Check form is valid diff --git a/formazione/admin.py b/formazione/admin.py index e7d957490..34f59f4f9 100755 --- a/formazione/admin.py +++ b/formazione/admin.py @@ -70,7 +70,7 @@ class AdminCorsoBase(ReadonlyAdminMixin, admin.ModelAdmin): search_fields = ['sede__nome', 'sede__genitore__nome', 'progressivo', 'anno', ] list_display = ['progressivo', 'tipo', 'stato', 'anno', 'sede', 'data_inizio', 'data_esame', 'delibera_file_col',] - list_filter = ['tipo', 'anno', 'creazione', 'stato', 'data_inizio', ] + list_filter = ['tipo', 'stato', 'titolo_cri__cdf_livello', 'anno', 'creazione', 'data_inizio',] raw_id_fields = RAW_ID_FIELDS_CORSOBASE inlines = [InlineDelegaCorsoBase, InlinePartecipazioneCorsoBase, InlineInvitoCorsoBase, InlineLezioneCorsoBase, InlineEstensioneCorso] diff --git a/formazione/forms.py b/formazione/forms.py index 7fd773018..f0f21f6ea 100644 --- a/formazione/forms.py +++ b/formazione/forms.py @@ -609,6 +609,16 @@ class FormCreateDirettoreDelega(ModelForm): has_nulla_osta = forms.BooleanField(label='Responsabilità di aver ricevuto nulla osta dal presidente del comitato di appartenenza', initial=True) + def clean(self): + cd = self.cleaned_data + corso = self.oggetto + + if corso.titolo_cri and corso.titolo_cri.cdf_livello != Titolo.CDF_LIVELLO_IV: + if corso.direttori_corso().count() >= 1: + self.add_error('persona', 'I corsi di questo livello possono avere uno ed un solo direttore') + + return cd + class Meta: model = Delega fields = ['persona', 'has_nulla_osta',] From 24ad7d7202578991f48965abe8453c50842f9073 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Mon, 11 Nov 2019 10:27:02 +0100 Subject: [PATCH 77/79] FIXED: (GAIA-243) blocco num massimo (30) di partecipazioni corso confermabili --- base/classes/autorizzazione.py | 19 +++++++++++++++++-- formazione/forms.py | 17 +++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/base/classes/autorizzazione.py b/base/classes/autorizzazione.py index 975e1503d..43e956dc9 100644 --- a/base/classes/autorizzazione.py +++ b/base/classes/autorizzazione.py @@ -53,9 +53,24 @@ def _process(self, concedi): return self._template, context + def _form_kwargs(self): + """ + kwargs da passare nella form da validare. + :return: empty dict (default) or dict with kwargs. + """ + + try: + if self.richiesta.oggetto is not None: + return dict(instance=self.richiesta.oggetto) + except Exception as e: + return dict() + return dict() + def _process_form(self, concedi): + form_kwargs = self._form_kwargs() + if self.request.POST: - self.form = self._autorizzazione_form(self.request.POST) + self.form = self._autorizzazione_form(self.request.POST, **form_kwargs) if self.form.is_valid(): # Accetta la richiesta con modulo if concedi: @@ -63,7 +78,7 @@ def _process_form(self, concedi): else: self.richiesta.nega(self.me, modulo=self.form) else: - self.form = self._autorizzazione_form() + self.form = self._autorizzazione_form(**form_kwargs) def _validate(self): self.torna_url = self.request.session.get('autorizzazioni_torna_url', diff --git a/formazione/forms.py b/formazione/forms.py index f0f21f6ea..d6c95ad3f 100644 --- a/formazione/forms.py +++ b/formazione/forms.py @@ -407,6 +407,23 @@ def __init__(self, *args, **kwargs): class ModuloConfermaIscrizioneCorso(forms.Form): IS_CORSO_NUOVO = True + MAX_ISCRIZIONI_CONFERMABILI = 30 + + def clean(self): + cd = self.cleaned_data + + corso = self.instance.corso + max_iscrizioni = ModuloConfermaIscrizioneCorso.MAX_ISCRIZIONI_CONFERMABILI + + if corso.partecipazioni_confermate().count() >= max_iscrizioni: + raise ValidationError('Come da regolamento non si possono iscrivere più di %s partecipanti. ' + 'É raggiunto il limite massimo.' % max_iscrizioni) + return cd + + def __init__(self, *args, **kwargs): + self.instance = kwargs.pop('instance') + + super().__init__(*args, *kwargs) class ModuloConfermaIscrizioneCorsoBase(forms.Form): From 0dd430bbe26de82d1b0b1583744ee91427812e75 Mon Sep 17 00:00:00 2001 From: Arkady Karlkvist Date: Tue, 12 Nov 2019 16:50:39 +0100 Subject: [PATCH 78/79] NEW: (GAIA-246) nuova appartenenza OPERATORE VILLA MARAINI (migr 0054) --- .../migrations/0054_auto_20191112_1619.py | 20 +++++++++++++ anagrafica/models.py | 12 ++++++++ .../templates/anagrafica_utente_home.html | 17 +++++++---- base/menus/utente.py | 28 +++++++++++-------- 4 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 anagrafica/migrations/0054_auto_20191112_1619.py diff --git a/anagrafica/migrations/0054_auto_20191112_1619.py b/anagrafica/migrations/0054_auto_20191112_1619.py new file mode 100644 index 000000000..82bfdccd1 --- /dev/null +++ b/anagrafica/migrations/0054_auto_20191112_1619.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2019-11-12 16:19 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('anagrafica', '0053_merge'), + ] + + operations = [ + migrations.AlterField( + model_name='appartenenza', + name='membro', + field=models.CharField(choices=[('VO', 'Volontario'), ('ES', 'Volontario in Estensione'), ('OR', 'Socio Ordinario'), ('SO', 'Sostenitore'), ('DI', 'Dipendente'), ('VM', 'Operatore Villa Maraini')], db_index=True, default='VO', max_length=2, verbose_name='Tipo membro'), + ), + ] diff --git a/anagrafica/models.py b/anagrafica/models.py index b6b4ff2c3..d3fe945e9 100755 --- a/anagrafica/models.py +++ b/anagrafica/models.py @@ -497,6 +497,10 @@ def sostenitore(self, **kwargs): """ return self.membro(Appartenenza.SOSTENITORE, **kwargs) + @property + def operatore_villa_maraini(self): + return self.appartenenze_attuali(membro=Appartenenza.OPERATORE_VILLA_MARAINI) + @property def applicazioni_disponibili(self): from formazione.models import CorsoBase @@ -504,6 +508,12 @@ def applicazioni_disponibili(self): # Personalizzare il menu "Utente" secondo il suo ruolo utente_url, utente_label, utente_count = ('/utente/', 'Volontario', None) + # GAIA-246 + if self.operatore_villa_maraini and not self.volontario: + return [ + (utente_url, 'Operatore Villa Maraini', 'fa-user', utente_count), + ] + if not self.volontario: utente_label = 'Utente' @@ -1490,6 +1500,7 @@ class Meta: MILITARE = 'MI' DONATORE = 'DO' SOSTENITORE = 'SO' + OPERATORE_VILLA_MARAINI = 'VM' # GAIA-246 # Quale tipo di membro puo' partecipare alle attivita'? MEMBRO_ATTIVITA = (VOLONTARIO, ESTESO,) @@ -1524,6 +1535,7 @@ class Meta: (ORDINARIO, 'Socio Ordinario'), (SOSTENITORE, 'Sostenitore'), (DIPENDENTE, 'Dipendente'), + (OPERATORE_VILLA_MARAINI, 'Operatore Villa Maraini'), #(INFERMIERA, 'Infermiera Volontaria'), #(MILITARE, 'Membro Militare'), #(DONATORE, 'Donatore Finanziario'), diff --git a/anagrafica/templates/anagrafica_utente_home.html b/anagrafica/templates/anagrafica_utente_home.html index 6ab503913..296d3321e 100644 --- a/anagrafica/templates/anagrafica_utente_home.html +++ b/anagrafica/templates/anagrafica_utente_home.html @@ -28,7 +28,7 @@

    Vuoi trovare Corsi Base vicino a te? {% endif %} - {% if me.dipendente or me.volontario %} + {% if me.dipendente or me.volontario or me.operatore_villa_maraini %}
    {% endif %} + {% if not me.operatore_villa_maraini %}
    + {% endif %} + {% if me.dipendente %}
    @@ -177,11 +180,13 @@

    {% endif %} -
    - {% for articolo in articoli %} - {% include "includes/elemento_lista.html" %} - {% endfor %} -
    + {% if not me.operatore_villa_maraini %} +
    + {% for articolo in articoli %} + {% include "includes/elemento_lista.html" %} + {% endfor %} +
    + {% endif %} {% include "base_privacy_policy_popup.html" %} diff --git a/base/menus/utente.py b/base/menus/utente.py index c1c26ccc7..977177e3c 100644 --- a/base/menus/utente.py +++ b/base/menus/utente.py @@ -79,14 +79,20 @@ def get_menu(self): me = self.me - return (( - self.menu_persona(), - self.menu_volontario() if me.volontario else None, - self.menu_formazione() if me.volontario or me.dipendente else None, - menu_rubrica_base(me), - self.menu_curriculum(), - self.menu_donatore() if self.is_volontario else None, - self.menu_sicurezza(), - self.menu_links(), - # menu_monitoraggio(me), - )) + if me.operatore_villa_maraini and not me.volontario: + return (( + self.menu_persona(), + self.menu_sicurezza(), + )) + else: + return (( + self.menu_persona(), + self.menu_volontario() if me.volontario else None, + self.menu_formazione() if me.volontario or me.dipendente else None, + menu_rubrica_base(me), + self.menu_curriculum(), + self.menu_donatore() if self.is_volontario else None, + self.menu_sicurezza(), + self.menu_links(), + # menu_monitoraggio(me), + )) From c6b7f2536c08a55bbda67bff71b9e04cba314a77 Mon Sep 17 00:00:00 2001 From: Andrea Carmisciano Date: Wed, 13 Nov 2019 23:48:18 +0100 Subject: [PATCH 79/79] fix --- base/files.py | 16 +--------------- config/pgsql.cnf.sample | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/base/files.py b/base/files.py index 2145164e5..363d2668a 100755 --- a/base/files.py +++ b/base/files.py @@ -164,21 +164,7 @@ def genera_e_salva_external(self, nome='File.pdf', scadenza=None, corpo={}, mode scadenza = scadenza or domani() - url = DOMPDF_ENDPOINT - corpo.update({"timestamp": datetime.now()}) - html = get_template(modello).render(corpo) - values = { - 'paper': formato, - 'orientation': orientamento, - 'html': html - } - - data = urllib.parse.urlencode(values) - data = data.encode('UTF-8') - req = urllib.request.Request(url, data) - response = urllib.request.urlopen(req) - - self.salva(posizione, nome, response, scadenza) + self.genera_e_salva_con_python(nome, scadenza, corpo, modello, posizione) class Meta: proxy = True diff --git a/config/pgsql.cnf.sample b/config/pgsql.cnf.sample index 2eeb12613..60439273c 100755 --- a/config/pgsql.cnf.sample +++ b/config/pgsql.cnf.sample @@ -1,6 +1,6 @@ [client] host = db port = 5432 -database = qq +database = postgres user = postgres password =