def attivita_scheda_turni_partecipa(request, me, pk=None, turno_pk=None): """ Mostra la scheda "Informazioni" di una attivita'. """ turno = get_object_or_404(Turno, pk=turno_pk) stato = turno.persona(me) if stato not in turno.TURNO_PUOI_PARTECIPARE: return errore_generico(request, me, titolo="Non puoi partecipare a questo turno", messaggio="Siamo spiacenti, ma ci risulta che tu non possa " "richiedere partecipazione a questo turno. Vai " "all'elenco dei turni per maggiori informazioni " "sulla motivazione. ", torna_titolo="Turni dell'attività", torna_url=turno.url, ) p = Partecipazione( turno=turno, persona=me, ) p.save() p.richiedi() return messaggio_generico(request, me, titolo="Ottimo! Richiesta inoltrata.", messaggio="La tua richiesta è stata inoltrata ai referenti di " "questa attività, che potranno confermarla o negarla. " "Ti manderemo una e-mail non appena risponderanno alla " "tua richiesta. Puoi sempre controllare lo stato delle tue" "richieste di partecipazione da 'Attivita' > 'I miei turni'. ", torna_titolo="Vai a 'I miei turni'", torna_url="/attivita/storico/")
def crea_partecipazione(persona, turno): p = Partecipazione( turno=turno, persona=persona, stato=Partecipazione.RICHIESTA, ) p.save() return p
def permessi_persona(persona): """ Permessi di ogni persona (nessuna delega). :param persona: La persona. :return: Lista di permessi. """ from anagrafica.models import Sede from attivita.models import Attivita, Partecipazione from base.utils import poco_fa # Limiti di tempo per la centrale operativa quindici_minuti_fa = poco_fa() - timedelta( minutes=Attivita.MINUTI_CENTRALE_OPERATIVA) tra_quindici_minuti = poco_fa() + timedelta( minutes=Attivita.MINUTI_CENTRALE_OPERATIVA) # Sedi per le quali sto effettuando un servizio di centrale operativa sede_centrale_operativa = Sede.objects.filter( Partecipazione.con_esito( Partecipazione.ESITO_OK, persona=persona, ).via("attivita__turni__partecipazioni"), Q( Q(attivita__centrale_operativa=Attivita.CO_AUTO) | Q(attivita__centrale_operativa=Attivita.CO_MANUALE, attivita__turni__partecipazioni__centrale_operativa=True)), attivita__turni__inizio__lte=tra_quindici_minuti, attivita__turni__fine__gte=quindici_minuti_fa, ) return [(GESTIONE_CENTRALE_OPERATIVA_SEDE, sede_centrale_operativa)]
def attivita_scheda_turni_rimuovi(request, me, pk=None, turno_pk=None, partecipante_pk=None): turno = get_object_or_404(Turno, pk=turno_pk) stato = turno.persona(me) if stato != turno.TURNO_PRENOTATO_PUOI_RITIRARTI: return errore_generico(request, me, titolo="Non puoi ritirare la tua partecipazione", messaggio="Una volta che la tua partecipazione è stata confermata, " "non puoi più ritirarla da Gaia. Se non puoi presentarti, " "scrivi a un referente dell'attività, che potrà valutare " "la situazione e rimuoverti dai partecipanti.", torna_titolo="Torna al turno", torna_url=turno.url) partecipazione = Partecipazione.con_esito_pending(turno=turno, persona=me).first() if not partecipazione: raise ValueError("TURNO_PRENOTATO_PUOI_RITIRARTI assegnato, ma nessuna partecipazione" "trovata. ") partecipazione.autorizzazioni_ritira() return messaggio_generico(request, me, titolo="Richiesta ritirata.", messaggio="La tua richiesta di partecipazione a questo turno " "è stata ritirata con successo.", torna_titolo="Torna al turno", torna_url=turno.url)
def attivita_scheda_turni_ritirati(request, me, pk=None, turno_pk=None): turno = get_object_or_404(Turno, pk=turno_pk) stato = turno.persona(me) if stato != turno.TURNO_PRENOTATO_PUOI_RITIRARTI: return errore_generico( request, me, titolo="Non puoi ritirare la tua partecipazione", messaggio="Una volta che la tua partecipazione è stata confermata, " "non puoi più ritirarla da Gaia. Se non puoi presentarti, " "scrivi a un referente dell'attività, che potrà valutare " "la situazione e rimuoverti dai partecipanti.", torna_titolo="Torna al turno", torna_url=turno.url) partecipazione = Partecipazione.con_esito_pending(turno=turno, persona=me).first() if not partecipazione: raise ValueError( "TURNO_PRENOTATO_PUOI_RITIRARTI assegnato, ma nessuna partecipazione" "trovata. ") partecipazione.autorizzazioni_ritira() return messaggio_generico( request, me, titolo="Richiesta ritirata.", messaggio="La tua richiesta di partecipazione a questo turno " "è stata ritirata con successo.", torna_titolo="Torna al turno", torna_url=turno.url)
def co_poteri(request, me): sedi = me.oggetti_permesso(GESTIONE_POTERI_CENTRALE_OPERATIVA_SEDE) modulo = ModuloPoteri(request.GET or None) giorno = date.today() if modulo.is_valid(): giorno = modulo.cleaned_data['giorno'] minuti = Attivita.MINUTI_CENTRALE_OPERATIVA # Limiti di tempo per la centrale operativa giorno_inizio = datetime.combine(giorno, time(0, 0)) giorno_fine = datetime.combine(giorno, time(23, 59)) url = "/centrale-operativa/poteri/" ieri = "%s?giorno=%s" % (url, (giorno - timedelta(days=1)).strftime("%d/%m/%Y")) domani = "%s?giorno=%s" % (url, (giorno + timedelta(days=1)).strftime("%d/%m/%Y")) partecipazioni = Partecipazione.con_esito_ok().filter( turno__inizio__lte=giorno_fine, turno__fine__gte=giorno_inizio, turno__attivita__centrale_operativa=Attivita.CO_MANUALE, turno__attivita__sede__in=sedi, ).order_by('turno__inizio') contesto = { "partecipazioni": partecipazioni, "minuti": minuti, "modulo": modulo, "ieri": ieri, "domani": domani, } return "centrale_operativa_poteri.html", contesto
def permessi_persona(persona): """ Permessi di ogni persona (nessuna delega). :param persona: La persona. :return: Lista di permessi. """ from anagrafica.models import Sede from attivita.models import Attivita, Partecipazione from base.utils import poco_fa # Limiti di tempo per la centrale operativa quindici_minuti_fa = poco_fa() - timedelta(minutes=Attivita.MINUTI_CENTRALE_OPERATIVA) tra_quindici_minuti = poco_fa() + timedelta(minutes=Attivita.MINUTI_CENTRALE_OPERATIVA) # Sedi per le quali sto effettuando un servizio di centrale operativa sede_centrale_operativa = Sede.objects.filter( Partecipazione.con_esito(Partecipazione.ESITO_OK, persona=persona, ).via("attivita__turni__partecipazioni"), Q( Q(attivita__centrale_operativa=Attivita.CO_AUTO) | Q(attivita__centrale_operativa=Attivita.CO_MANUALE, attivita__turni__partecipazioni__centrale_operativa=True) ), attivita__turni__inizio__lte=tra_quindici_minuti, attivita__turni__fine__gte=quindici_minuti_fa, ) return [ (GESTIONE_CENTRALE_OPERATIVA_SEDE, sede_centrale_operativa) ]
def test_autorizzazioni_automatiche_scadute(self): presidente = crea_persona() persona, sede, app = crea_persona_sede_appartenenza( presidente=presidente) persona.email_contatto = email_fittizzia() persona.save() ora = timezone.now() area, attivita = crea_area_attivita(sede) domani_inizio = ora + timedelta(days=24) domani_fine = ora + timedelta(days=180) t1 = crea_turno(attivita, inizio=domani_inizio, fine=domani_fine) partecipazione = crea_partecipazione(persona, t1) attivita.centrale_operativa = Attivita.CO_AUTO attivita.save() self.assertEqual(0, Autorizzazione.objects.count()) partecipazione.richiedi() self.assertNotIn(partecipazione, Partecipazione.con_esito_ok()) self.assertEqual(0, len(mail.outbox)) self.assertEqual(1, Autorizzazione.objects.count()) autorizzazione = Autorizzazione.objects.first() self.assertNotEqual(autorizzazione.scadenza, None) autorizzazione.scadenza = timezone.now() - timedelta(days=10) autorizzazione.save() self.assertFalse(autorizzazione.concessa) Autorizzazione.gestisci_automatiche() self.assertEqual(1, len(mail.outbox)) messaggio = mail.outbox[0] self.assertTrue( messaggio.subject.find( 'Richiesta di partecipazione attività RESPINTA') > -1) self.assertFalse( messaggio.subject.find( 'Richiesta di partecipazione attività APPROVATA') > -1) self.assertTrue( messaggio.body.find( 'una tua richiesta è rimasta in attesa per 30 giorni e come da policy' ) == -1) self.assertTrue(autorizzazione.oggetto.automatica) Autorizzazione.gestisci_automatiche() self.assertEqual(1, len(mail.outbox)) self.assertEqual(autorizzazione.concessa, None) self.assertIn(partecipazione, Partecipazione.con_esito_no())
def risultati(self): qs_turni = self.args[0] return Persona.objects.filter( Partecipazione.con_esito_ok(turno__in=qs_turni).via("partecipazioni") ).prefetch_related( 'appartenenze', 'appartenenze__sede', 'utenza', 'numeri_telefono' ).distinct('cognome', 'nome', 'codice_fiscale')
def risultati(self): qs_turni = self.args[0] return Persona.objects.filter( Partecipazione.con_esito_ok( turno__in=qs_turni).via("partecipazioni")).prefetch_related( 'appartenenze', 'appartenenze__sede', 'utenza', 'numeri_telefono').distinct('cognome', 'nome', 'codice_fiscale')
def risultati(self): qs_attivita = self.args[0] return Persona.objects.filter( Partecipazione.con_esito_ok(turno__attivita__in=qs_attivita).via("partecipazioni") ).prefetch_related( 'appartenenze', 'appartenenze__sede', 'utenza', 'numeri_telefono' ).annotate( partecipazioni__turno__nome=F('partecipazioni__turno__nome'), partecipazioni__turno__inizio=F('partecipazioni__turno__inizio'), partecipazioni__turno__fine=F('partecipazioni__turno__fine'), ) ## NO DISTINCT!
def risultati(self): qs_attivita = self.args[0] return Persona.objects.filter( Partecipazione.con_esito_ok( turno__attivita__in=qs_attivita).via("partecipazioni") ).prefetch_related( 'appartenenze', 'appartenenze__sede', 'utenza', 'numeri_telefono').annotate( partecipazioni__turno__nome=F('partecipazioni__turno__nome'), partecipazioni__turno__inizio=F( 'partecipazioni__turno__inizio'), partecipazioni__turno__fine=F('partecipazioni__turno__fine'), ) ## NO DISTINCT!
def test_autorizzazioni_automatiche_scadute(self): presidente = crea_persona() persona, sede, app = crea_persona_sede_appartenenza(presidente=presidente) persona.email_contatto = email_fittizzia() persona.save() ora = timezone.now() area, attivita = crea_area_attivita(sede) domani_inizio = ora + timedelta(days=24) domani_fine = ora + timedelta(days=180) t1 = crea_turno(attivita, inizio=domani_inizio, fine=domani_fine) partecipazione = crea_partecipazione(persona, t1) attivita.centrale_operativa = Attivita.CO_AUTO attivita.save() self.assertEqual(0, Autorizzazione.objects.count()) partecipazione.richiedi() self.assertNotIn(partecipazione, Partecipazione.con_esito_ok()) self.assertEqual(0, len(mail.outbox)) self.assertEqual(1, Autorizzazione.objects.count()) autorizzazione = Autorizzazione.objects.first() self.assertNotEqual(autorizzazione.scadenza, None) autorizzazione.scadenza = timezone.now() - timedelta(days=10) autorizzazione.save() self.assertFalse(autorizzazione.concessa) Autorizzazione.gestisci_automatiche() self.assertEqual(1, len(mail.outbox)) messaggio = mail.outbox[0] self.assertTrue(messaggio.subject.find('Richiesta di partecipazione attività RESPINTA') > -1) self.assertFalse(messaggio.subject.find('Richiesta di partecipazione attività APPROVATA') > -1) self.assertTrue(messaggio.body.find('una tua richiesta è rimasta in attesa per 30 giorni e come da policy') == -1) self.assertTrue(autorizzazione.oggetto.automatica) Autorizzazione.gestisci_automatiche() self.assertEqual(1, len(mail.outbox)) self.assertEqual(autorizzazione.concessa, None) self.assertIn(partecipazione, Partecipazione.con_esito_no())
def co_turni(request, me): sedi = me.oggetti_permesso(GESTIONE_CENTRALE_OPERATIVA_SEDE) tra_qualche_ora = poco_fa() + timedelta(hours=2) qualche_ora_fa = poco_fa() - timedelta(hours=2) partecipazioni = Partecipazione.con_esito_ok().filter( Q(turno__attivita__sede__in=sedi), # Nelle mie sedi di competenza Q(turno__inizio__lte=tra_qualche_ora, turno__fine__gte=qualche_ora_fa) # a) In corso, oppure | Q(turno__coturni__montato_da__isnull=False, turno__coturni__smontato_da__isnull=True) # d) Da smontare ).select_related('turno', 'turno__attivita', 'turno__attivita__sede')\ .order_by('turno__inizio', 'turno__fine', 'turno__id', 'persona__id')\ .distinct('turno__inizio', 'turno__fine', 'turno__id', 'persona__id') contesto = { "partecipazioni": partecipazioni, } return "centrale_operativa_turni.html", contesto
def co_poteri_switch(request, me, part_pk): sedi = me.oggetti_permesso(GESTIONE_POTERI_CENTRALE_OPERATIVA_SEDE) partecipazione = Partecipazione.con_esito_ok( turno__attivita__sede__in=sedi, pk=part_pk ).first() next = request.GET.get('next', default='/centrale-operativa/poteri/') if not partecipazione: return errore_generico(request, me, titolo="Partecipazione non trovata") partecipazione.centrale_operativa = not partecipazione.centrale_operativa partecipazione.save() return redirect(next)
def attivita_scheda_turni_partecipa(request, me, pk=None, turno_pk=None): """ Mostra la scheda "Informazioni" di una attivita'. """ turno = get_object_or_404(Turno, pk=turno_pk) stato = turno.persona(me) if stato not in turno.TURNO_PUOI_PARTECIPARE: return errore_generico( request, me, titolo="Non puoi partecipare a questo turno", messaggio="Siamo spiacenti, ma ci risulta che tu non possa " "richiedere partecipazione a questo turno. Vai " "all'elenco dei turni per maggiori informazioni " "sulla motivazione. ", torna_titolo="Turni dell'attività", torna_url=turno.url, ) p = Partecipazione( turno=turno, persona=me, ) p.save() p.richiedi() return messaggio_generico( request, me, titolo="Ottimo! Richiesta inoltrata.", messaggio="La tua richiesta è stata inoltrata ai referenti di " "questa attività, che potranno confermarla o negarla. " "Ti manderemo una e-mail non appena risponderanno alla " "tua richiesta. Puoi sempre controllare lo stato delle tue" "richieste di partecipazione da 'Attivita' > 'I miei turni'. ", torna_titolo="Vai a 'I miei turni'", torna_url="/attivita/storico/")
def attivita_statistiche(request, me): sedi = me.oggetti_permesso(GESTIONE_ATTIVITA_SEDE) modulo = ModuloStatisticheAttivita(request.POST or None, initial={"sedi": sedi}) modulo.fields['sedi'].queryset = sedi statistiche = [] chart = {} periodi = 12 if modulo.is_valid(): oggi = date.today() giorni = int(modulo.cleaned_data['periodo']) if giorni == modulo.SETTIMANA: etichetta = "sett." elif giorni == modulo.QUINDICI_GIORNI: etichetta = "fortn." elif giorni == modulo.MESE: etichetta = "mesi" else: raise ValueError("Etichetta mancante.") for periodo in range(periodi, 0, -1): dati = {} fine = oggi - timedelta(days=(giorni * periodo)) inizio = fine - timedelta(days=giorni - 1) fine = datetime.combine(fine, time(23, 59, 59)) inizio = datetime.combine(inizio, time(0, 0, 0)) dati['inizio'] = inizio dati['fine'] = fine # Prima, ottiene tutti i queryset. qs_attivita = Attivita.objects.filter(stato=Attivita.VISIBILE, sede__in=sedi) qs_turni = Turno.objects.filter(attivita__in=qs_attivita, inizio__lte=fine, fine__gte=inizio) qs_part = Partecipazione.con_esito_ok(turno__in=qs_turni) ore_di_servizio = qs_turni.annotate( durata=F('fine') - F('inizio')).aggregate( totale_ore=Sum('durata'))['totale_ore'] or timedelta() ore_uomo_di_servizio = qs_part.annotate( durata=F('turno__fine') - F('turno__inizio')).aggregate( totale_ore=Sum('durata'))['totale_ore'] or timedelta() # Poi, associa al dizionario statistiche. dati['etichetta'] = "%d %s fa" % ( periodo, etichetta, ) dati['num_turni'] = qs_turni.count() dati['ore_di_servizio'] = ore_di_servizio dati['ore_uomo_di_servizio'] = ore_uomo_di_servizio try: dati['rapporto'] = round( ore_uomo_di_servizio / ore_di_servizio, 3) except ZeroDivisionError: dati['rapporto'] = 0 statistiche.append(dati) chart['labels'] = json.dumps([x['etichetta'] for x in statistiche]) chart['num_turni'] = json.dumps([x['num_turni'] for x in statistiche]) chart['ore_di_servizio'] = json.dumps( [timedelta_ore(x['ore_di_servizio']) for x in statistiche]) chart['ore_uomo_di_servizio'] = json.dumps( [timedelta_ore(x['ore_uomo_di_servizio']) for x in statistiche]) chart['rapporto'] = json.dumps([x['rapporto'] for x in statistiche]) contesto = { "modulo": modulo, "statistiche": statistiche, "chart": chart, } return 'attivita_statistiche.html', contesto
def statistiche_attivita_persona(persona, modulo): """ Ritorna un dizionario di parametri, che puo' essere passato alla vista, con tutti i dati statistici relativi alle attivita' effettuate da una determinata persona. :param persona_id: ID della persona. :param modulo: Il form validato. :return: Un dizionario, o None se il form non e' valido. """ if not modulo.is_valid(): return None oggi = date.today() impostazioni = { # num_giorni: (nome, numero_periodi) modulo.SETTIMANA: ("sett.", 20), modulo.QUINDICI_GIORNI: ("fortn.", 20), modulo.MESE: ("mesi", 12), modulo.ANNO: ("anni", 4), } giorni = int(modulo.cleaned_data['periodo']) etichetta, periodi = impostazioni[giorni] statistiche = [] chart = {} for periodo in range(periodi, -1, -1): dati = {} fine = oggi - timedelta(days=(giorni * periodo)) inizio = fine - timedelta(days=giorni - 1) fine = datetime.combine(fine, time(23, 59, 59)) inizio = datetime.combine(inizio, time(0, 0, 0)) dati['inizio'] = inizio dati['fine'] = fine # Prima, ottiene tutti i queryset. qs_turni = Turno.objects.filter(inizio__lte=fine, fine__gte=inizio) qs_part = Partecipazione.con_esito_ok(turno__in=qs_turni, persona=persona) ore_di_servizio = qs_part.annotate(durata=F('turno__fine') - F('turno__inizio'))\ .aggregate(totale_ore=Sum('durata'))['totale_ore'] or timedelta() # Poi, associa al dizionario statistiche. dati['etichetta'] = "%d %s fa" % (periodo, etichetta,) dati['num_turni'] = qs_part.count() dati['ore_di_servizio'] = ore_di_servizio dati['ore_di_servizio_int'] = round(ore_di_servizio.total_seconds() / 3600, 3) statistiche.append(dati) chart['labels'] = json.dumps([x['etichetta'] for x in statistiche]) chart['num_turni'] = json.dumps([x['num_turni'] for x in statistiche]) chart['ore_di_servizio'] = json.dumps([timedelta_ore(x['ore_di_servizio']) for x in statistiche]) return {'statistiche': statistiche, 'chart': chart}
def attivita_statistiche(request, me): sedi = me.oggetti_permesso(GESTIONE_ATTIVITA_SEDE) modulo = ModuloStatisticheAttivita(request.POST or None, initial={"sedi": sedi}) modulo.fields['sedi'].queryset = sedi statistiche = [] chart = {} periodi = 12 if modulo.is_valid(): oggi = date.today() giorni = int(modulo.cleaned_data['periodo']) if giorni == modulo.SETTIMANA: etichetta = "sett." elif giorni == modulo.QUINDICI_GIORNI: etichetta = "fortn." elif giorni == modulo.MESE: etichetta = "mesi" else: raise ValueError("Etichetta mancante.") for periodo in range(periodi, 0, -1): dati = {} fine = oggi - timedelta(days=(giorni*periodo)) inizio = fine - timedelta(days=giorni-1) fine = datetime.combine(fine, time(23, 59, 59)) inizio = datetime.combine(inizio, time(0, 0, 0)) dati['inizio'] = inizio dati['fine'] = fine # Prima, ottiene tutti i queryset. qs_attivita = Attivita.objects.filter(stato=Attivita.VISIBILE, sede__in=sedi) qs_turni = Turno.objects.filter(attivita__in=qs_attivita, inizio__lte=fine, fine__gte=inizio) qs_part = Partecipazione.con_esito_ok(turno__in=qs_turni) ore_di_servizio = qs_turni.annotate(durata=F('fine') - F('inizio')).aggregate(totale_ore=Sum('durata'))['totale_ore'] or timedelta() ore_uomo_di_servizio = qs_part.annotate(durata=F('turno__fine') - F('turno__inizio')).aggregate(totale_ore=Sum('durata'))['totale_ore'] or timedelta() # Poi, associa al dizionario statistiche. dati['etichetta'] = "%d %s fa" % (periodo, etichetta,) dati['num_turni'] = qs_turni.count() dati['ore_di_servizio'] = ore_di_servizio dati['ore_uomo_di_servizio'] = ore_uomo_di_servizio try: dati['rapporto'] = round(ore_uomo_di_servizio / ore_di_servizio, 3) except ZeroDivisionError: dati['rapporto'] = 0 statistiche.append(dati) chart['labels'] = json.dumps([x['etichetta'] for x in statistiche]) chart['num_turni'] = json.dumps([x['num_turni'] for x in statistiche]) chart['ore_di_servizio'] = json.dumps([timedelta_ore(x['ore_di_servizio']) for x in statistiche]) chart['ore_uomo_di_servizio'] = json.dumps([timedelta_ore(x['ore_uomo_di_servizio']) for x in statistiche]) chart['rapporto'] = json.dumps([x['rapporto'] for x in statistiche]) contesto = { "modulo": modulo, "statistiche": statistiche, "chart": chart, } return 'attivita_statistiche.html', contesto