661 lines
26 KiB
Python
661 lines
26 KiB
Python
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib.auth.models import User
|
|
from django.contrib import messages
|
|
from django.http import JsonResponse, HttpResponseForbidden
|
|
from django.views.decorators.http import require_POST
|
|
from django.db.models import Q, Count
|
|
from django.utils import timezone
|
|
from itertools import chain
|
|
from datetime import timedelta
|
|
|
|
from .models import Conversazione, Obiettivo, AggiornamentoObiettivo, CommentoConversazione, Documento, Tag, Appuntamento
|
|
from .forms import ConversazioneForm, ObiettivoForm, AggiornamentoObiettivoForm, CommentoConversazioneForm, DocumentoForm, AppuntamentoForm
|
|
|
|
|
|
def _can_edit(user, obj):
|
|
"""Autore o superuser possono modificare/eliminare."""
|
|
owner = getattr(obj, 'registrato_da', None) or getattr(obj, 'creato_da', None) or getattr(obj, 'caricato_da', None) or getattr(obj, 'autore', None)
|
|
return user == owner or user.is_superuser
|
|
|
|
|
|
# ── Dashboard ──────────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def dashboard(request):
|
|
conversazioni = Conversazione.objects.select_related('registrato_da').order_by('-data')[:20]
|
|
aggiornamenti = AggiornamentoObiettivo.objects.select_related('autore', 'obiettivo').order_by('-data')[:20]
|
|
documenti_recenti = Documento.objects.select_related('caricato_da').order_by('-data_caricamento')[:10]
|
|
|
|
eventi = sorted(
|
|
chain(
|
|
[{'tipo': 'conversazione', 'data': c.data, 'obj': c} for c in conversazioni],
|
|
[{'tipo': 'aggiornamento', 'data': a.data, 'obj': a} for a in aggiornamenti],
|
|
[{'tipo': 'documento', 'data': d.data_caricamento, 'obj': d} for d in documenti_recenti],
|
|
),
|
|
key=lambda x: x['data'],
|
|
reverse=True,
|
|
)[:30]
|
|
|
|
obiettivi_aperti = Obiettivo.objects.exclude(stato='completato').order_by('-data_creazione')[:5]
|
|
|
|
# Agenda: prossime scadenze obiettivi (prossimi 30 giorni)
|
|
oggi = timezone.now().date()
|
|
scadenze_prossime = Obiettivo.objects.filter(
|
|
data_scadenza__gte=oggi,
|
|
data_scadenza__lte=oggi + timedelta(days=30),
|
|
).exclude(stato='completato').order_by('data_scadenza')[:8]
|
|
|
|
# Scadenze passate non completate
|
|
scaduti = Obiettivo.objects.filter(
|
|
data_scadenza__lt=oggi,
|
|
).exclude(stato='completato').order_by('-data_scadenza')[:5]
|
|
|
|
# Prossimi appuntamenti
|
|
prossimi_appuntamenti = Appuntamento.objects.filter(
|
|
data_ora__gte=timezone.now(),
|
|
).select_related('creato_da', 'conversazione').order_by('data_ora')[:5]
|
|
|
|
return render(request, 'diario/dashboard.html', {
|
|
'eventi': eventi,
|
|
'obiettivi_aperti': obiettivi_aperti,
|
|
'scadenze_prossime': scadenze_prossime,
|
|
'scaduti': scaduti,
|
|
'prossimi_appuntamenti': prossimi_appuntamenti,
|
|
'oggi': oggi,
|
|
})
|
|
|
|
|
|
# ── Agenda ─────────────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def agenda(request):
|
|
oggi = timezone.now().date()
|
|
|
|
# Prossimi eventi: scadenze obiettivi, conversazioni programmate nel futuro, appuntamenti
|
|
scadenze_future = Obiettivo.objects.filter(
|
|
data_scadenza__gte=oggi,
|
|
).exclude(stato='completato').order_by('data_scadenza')
|
|
|
|
conversazioni_future = Conversazione.objects.filter(
|
|
data__date__gte=oggi,
|
|
).select_related('registrato_da').order_by('data')
|
|
|
|
appuntamenti_futuri = Appuntamento.objects.filter(
|
|
data_ora__date__gte=oggi,
|
|
).select_related('creato_da', 'conversazione').order_by('data_ora')
|
|
|
|
# Eventi futuri unificati
|
|
eventi_futuri = sorted(
|
|
chain(
|
|
[{'tipo': 'scadenza', 'data': o.data_scadenza, 'obj': o} for o in scadenze_future],
|
|
[{'tipo': 'conversazione', 'data': c.data.date(), 'obj': c} for c in conversazioni_future],
|
|
[{'tipo': 'appuntamento', 'data': a.data_ora.date(), 'obj': a} for a in appuntamenti_futuri],
|
|
),
|
|
key=lambda x: x['data'],
|
|
)
|
|
|
|
# Eventi passati (ultimi 30 giorni)
|
|
data_inizio = oggi - timedelta(days=30)
|
|
conversazioni_passate = Conversazione.objects.filter(
|
|
data__date__gte=data_inizio,
|
|
data__date__lt=oggi,
|
|
).select_related('registrato_da').order_by('-data')
|
|
|
|
scadenze_passate = Obiettivo.objects.filter(
|
|
data_scadenza__gte=data_inizio,
|
|
data_scadenza__lt=oggi,
|
|
).order_by('-data_scadenza')
|
|
|
|
appuntamenti_passati = Appuntamento.objects.filter(
|
|
data_ora__date__gte=data_inizio,
|
|
data_ora__date__lt=oggi,
|
|
).select_related('creato_da', 'conversazione').order_by('-data_ora')
|
|
|
|
eventi_passati = sorted(
|
|
chain(
|
|
[{'tipo': 'scadenza', 'data': o.data_scadenza, 'obj': o, 'scaduto': o.stato != 'completato'} for o in scadenze_passate],
|
|
[{'tipo': 'conversazione', 'data': c.data.date(), 'obj': c, 'scaduto': False} for c in conversazioni_passate],
|
|
[{'tipo': 'appuntamento', 'data': a.data_ora.date(), 'obj': a, 'scaduto': False} for a in appuntamenti_passati],
|
|
),
|
|
key=lambda x: x['data'],
|
|
reverse=True,
|
|
)
|
|
|
|
return render(request, 'diario/agenda.html', {
|
|
'eventi_futuri': eventi_futuri,
|
|
'eventi_passati': eventi_passati,
|
|
'oggi': oggi,
|
|
})
|
|
|
|
|
|
# ── Conversazioni ──────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def conversazioni_lista(request):
|
|
qs = Conversazione.objects.select_related('registrato_da').order_by('-data')
|
|
return render(request, 'diario/conversazioni/lista.html', {'conversazioni': qs})
|
|
|
|
|
|
@login_required
|
|
def conversazione_nuova(request):
|
|
if request.method == 'POST':
|
|
form = ConversazioneForm(request.POST)
|
|
if form.is_valid():
|
|
conv = form.save(commit=False)
|
|
conv.registrato_da = request.user
|
|
conv.save()
|
|
form.save_m2m()
|
|
messages.success(request, 'Conversazione registrata.')
|
|
return redirect('conversazione_dettaglio', pk=conv.pk)
|
|
else:
|
|
form = ConversazioneForm()
|
|
return render(request, 'diario/conversazioni/form.html', {'form': form, 'titolo_pagina': 'Nuova conversazione'})
|
|
|
|
|
|
@login_required
|
|
def conversazione_dettaglio(request, pk):
|
|
conv = get_object_or_404(Conversazione, pk=pk)
|
|
commenti = conv.commenti.select_related('autore').order_by('-data')
|
|
documenti = conv.documenti.select_related('caricato_da').all()
|
|
appuntamenti = conv.appuntamenti.select_related('creato_da').all()
|
|
|
|
if request.method == 'POST':
|
|
comment_form = CommentoConversazioneForm(request.POST)
|
|
if comment_form.is_valid():
|
|
c = comment_form.save(commit=False)
|
|
c.conversazione = conv
|
|
c.autore = request.user
|
|
c.save()
|
|
messages.success(request, 'Commento aggiunto.')
|
|
return redirect('conversazione_dettaglio', pk=conv.pk)
|
|
else:
|
|
comment_form = CommentoConversazioneForm()
|
|
|
|
return render(request, 'diario/conversazioni/dettaglio.html', {
|
|
'conv': conv,
|
|
'commenti': commenti,
|
|
'comment_form': comment_form,
|
|
'documenti': documenti,
|
|
'appuntamenti': appuntamenti,
|
|
'can_edit': _can_edit(request.user, conv),
|
|
})
|
|
|
|
|
|
@login_required
|
|
def conversazione_modifica(request, pk):
|
|
conv = get_object_or_404(Conversazione, pk=pk)
|
|
if not _can_edit(request.user, conv):
|
|
return HttpResponseForbidden('Non hai i permessi per modificare questa conversazione.')
|
|
if request.method == 'POST':
|
|
form = ConversazioneForm(request.POST, instance=conv)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Conversazione aggiornata.')
|
|
return redirect('conversazione_dettaglio', pk=conv.pk)
|
|
else:
|
|
form = ConversazioneForm(instance=conv)
|
|
return render(request, 'diario/conversazioni/form.html', {'form': form, 'titolo_pagina': 'Modifica conversazione', 'conv': conv})
|
|
|
|
|
|
@login_required
|
|
def conversazione_elimina(request, pk):
|
|
conv = get_object_or_404(Conversazione, pk=pk)
|
|
if not _can_edit(request.user, conv):
|
|
return HttpResponseForbidden('Non hai i permessi per eliminare questa conversazione.')
|
|
if request.method == 'POST':
|
|
conv.delete()
|
|
messages.success(request, 'Conversazione eliminata.')
|
|
return redirect('conversazioni_lista')
|
|
return render(request, 'diario/conferma_elimina.html', {
|
|
'oggetto': conv,
|
|
'tipo': 'conversazione',
|
|
'cancel_url': 'conversazione_dettaglio',
|
|
})
|
|
|
|
|
|
# ── Commenti conversazione — modifica/elimina ─────────────────────────────────
|
|
|
|
@login_required
|
|
def commento_modifica(request, pk):
|
|
commento = get_object_or_404(CommentoConversazione, pk=pk)
|
|
if not _can_edit(request.user, commento):
|
|
return HttpResponseForbidden('Non puoi modificare questo commento.')
|
|
if request.method == 'POST':
|
|
form = CommentoConversazioneForm(request.POST, instance=commento)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Commento aggiornato.')
|
|
return redirect('conversazione_dettaglio', pk=commento.conversazione.pk)
|
|
else:
|
|
form = CommentoConversazioneForm(instance=commento)
|
|
return render(request, 'diario/commento_modifica.html', {
|
|
'form': form,
|
|
'commento': commento,
|
|
'tipo': 'commento',
|
|
'back_url': redirect('conversazione_dettaglio', pk=commento.conversazione.pk).url,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def commento_elimina(request, pk):
|
|
commento = get_object_or_404(CommentoConversazione, pk=pk)
|
|
if not _can_edit(request.user, commento):
|
|
return HttpResponseForbidden('Non puoi eliminare questo commento.')
|
|
conv_pk = commento.conversazione.pk
|
|
if request.method == 'POST':
|
|
commento.delete()
|
|
messages.success(request, 'Commento eliminato.')
|
|
return redirect('conversazione_dettaglio', pk=conv_pk)
|
|
return render(request, 'diario/conferma_elimina.html', {
|
|
'oggetto': commento,
|
|
'tipo': 'commento',
|
|
'cancel_url': 'conversazione_dettaglio',
|
|
'cancel_pk': conv_pk,
|
|
})
|
|
|
|
|
|
# ── Aggiornamenti obiettivo — modifica/elimina ────────────────────────────────
|
|
|
|
@login_required
|
|
def aggiornamento_modifica(request, pk):
|
|
agg = get_object_or_404(AggiornamentoObiettivo, pk=pk)
|
|
if not _can_edit(request.user, agg):
|
|
return HttpResponseForbidden('Non puoi modificare questo aggiornamento.')
|
|
if request.method == 'POST':
|
|
form = AggiornamentoObiettivoForm(request.POST, instance=agg)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Aggiornamento modificato.')
|
|
return redirect('obiettivo_dettaglio', pk=agg.obiettivo.pk)
|
|
else:
|
|
form = AggiornamentoObiettivoForm(instance=agg)
|
|
return render(request, 'diario/commento_modifica.html', {
|
|
'form': form,
|
|
'commento': agg,
|
|
'tipo': 'aggiornamento',
|
|
'back_url': redirect('obiettivo_dettaglio', pk=agg.obiettivo.pk).url,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def aggiornamento_elimina(request, pk):
|
|
agg = get_object_or_404(AggiornamentoObiettivo, pk=pk)
|
|
if not _can_edit(request.user, agg):
|
|
return HttpResponseForbidden('Non puoi eliminare questo aggiornamento.')
|
|
obj_pk = agg.obiettivo.pk
|
|
if request.method == 'POST':
|
|
agg.delete()
|
|
messages.success(request, 'Aggiornamento eliminato.')
|
|
return redirect('obiettivo_dettaglio', pk=obj_pk)
|
|
return render(request, 'diario/conferma_elimina.html', {
|
|
'oggetto': agg,
|
|
'tipo': 'aggiornamento',
|
|
'cancel_url': 'obiettivo_dettaglio',
|
|
'cancel_pk': obj_pk,
|
|
})
|
|
|
|
|
|
# ── Obiettivi ──────────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def obiettivi_lista(request):
|
|
filtro = request.GET.get('filtro', 'tutti')
|
|
qs = Obiettivo.objects.prefetch_related('assegnato_a').select_related('creato_da')
|
|
|
|
if filtro == 'collettivi':
|
|
qs = qs.filter(tipo='collettivo')
|
|
elif filtro == 'individuali':
|
|
qs = qs.filter(tipo='individuale')
|
|
elif filtro == 'miei':
|
|
qs = qs.filter(assegnato_a=request.user)
|
|
|
|
return render(request, 'diario/obiettivi/lista.html', {'obiettivi': qs, 'filtro': filtro})
|
|
|
|
|
|
@login_required
|
|
def obiettivo_nuovo(request):
|
|
if request.method == 'POST':
|
|
form = ObiettivoForm(request.POST)
|
|
if form.is_valid():
|
|
obj = form.save(commit=False)
|
|
obj.creato_da = request.user
|
|
obj.save()
|
|
form.save_m2m()
|
|
messages.success(request, 'Obiettivo creato.')
|
|
return redirect('obiettivo_dettaglio', pk=obj.pk)
|
|
else:
|
|
form = ObiettivoForm()
|
|
return render(request, 'diario/obiettivi/form.html', {'form': form, 'titolo_pagina': 'Nuovo obiettivo'})
|
|
|
|
|
|
@login_required
|
|
def obiettivo_dettaglio(request, pk):
|
|
obj = get_object_or_404(Obiettivo, pk=pk)
|
|
aggiornamenti = obj.aggiornamenti.select_related('autore').order_by('-data')
|
|
documenti = obj.documenti.select_related('caricato_da').all()
|
|
|
|
if request.method == 'POST':
|
|
agg_form = AggiornamentoObiettivoForm(request.POST)
|
|
if agg_form.is_valid():
|
|
agg = agg_form.save(commit=False)
|
|
agg.obiettivo = obj
|
|
agg.autore = request.user
|
|
agg.save()
|
|
messages.success(request, 'Aggiornamento aggiunto.')
|
|
return redirect('obiettivo_dettaglio', pk=obj.pk)
|
|
else:
|
|
agg_form = AggiornamentoObiettivoForm()
|
|
|
|
return render(request, 'diario/obiettivi/dettaglio.html', {
|
|
'obj': obj,
|
|
'aggiornamenti': aggiornamenti,
|
|
'documenti': documenti,
|
|
'agg_form': agg_form,
|
|
'can_edit': _can_edit(request.user, obj),
|
|
})
|
|
|
|
|
|
@login_required
|
|
def obiettivo_modifica(request, pk):
|
|
obj = get_object_or_404(Obiettivo, pk=pk)
|
|
if not _can_edit(request.user, obj):
|
|
return HttpResponseForbidden('Non hai i permessi per modificare questo obiettivo.')
|
|
if request.method == 'POST':
|
|
form = ObiettivoForm(request.POST, instance=obj)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Obiettivo aggiornato.')
|
|
return redirect('obiettivo_dettaglio', pk=obj.pk)
|
|
else:
|
|
form = ObiettivoForm(instance=obj)
|
|
return render(request, 'diario/obiettivi/form.html', {'form': form, 'titolo_pagina': 'Modifica obiettivo', 'obj': obj})
|
|
|
|
|
|
@login_required
|
|
def obiettivo_elimina(request, pk):
|
|
obj = get_object_or_404(Obiettivo, pk=pk)
|
|
if not _can_edit(request.user, obj):
|
|
return HttpResponseForbidden('Non hai i permessi per eliminare questo obiettivo.')
|
|
if request.method == 'POST':
|
|
obj.delete()
|
|
messages.success(request, 'Obiettivo eliminato.')
|
|
return redirect('obiettivi_lista')
|
|
return render(request, 'diario/conferma_elimina.html', {
|
|
'oggetto': obj,
|
|
'tipo': 'obiettivo',
|
|
'cancel_url': 'obiettivo_dettaglio',
|
|
})
|
|
|
|
|
|
@login_required
|
|
@require_POST
|
|
def obiettivo_avanzamento_ajax(request, pk):
|
|
obj = get_object_or_404(Obiettivo, pk=pk)
|
|
try:
|
|
valore = int(request.POST.get('avanzamento', 0))
|
|
if not 0 <= valore <= 100:
|
|
return JsonResponse({'ok': False, 'error': 'Valore fuori range'}, status=400)
|
|
obj.avanzamento = valore
|
|
obj.save(update_fields=['avanzamento'])
|
|
return JsonResponse({'ok': True, 'avanzamento': obj.avanzamento})
|
|
except (ValueError, TypeError):
|
|
return JsonResponse({'ok': False, 'error': 'Valore non valido'}, status=400)
|
|
|
|
|
|
# ── Documenti ──────────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def documenti_lista(request):
|
|
qs = Documento.objects.select_related('caricato_da', 'conversazione', 'obiettivo').order_by('-data_caricamento')
|
|
return render(request, 'diario/documenti/lista.html', {'documenti': qs})
|
|
|
|
|
|
@login_required
|
|
def documento_nuovo(request):
|
|
conversazione_pk = request.GET.get('conversazione')
|
|
obiettivo_pk = request.GET.get('obiettivo')
|
|
initial = {}
|
|
if conversazione_pk:
|
|
initial['conversazione'] = conversazione_pk
|
|
if obiettivo_pk:
|
|
initial['obiettivo'] = obiettivo_pk
|
|
|
|
if request.method == 'POST':
|
|
form = DocumentoForm(request.POST, request.FILES)
|
|
if form.is_valid():
|
|
doc = form.save(commit=False)
|
|
doc.caricato_da = request.user
|
|
doc.save()
|
|
messages.success(request, 'Documento caricato.')
|
|
if doc.conversazione:
|
|
return redirect('conversazione_dettaglio', pk=doc.conversazione.pk)
|
|
if doc.obiettivo:
|
|
return redirect('obiettivo_dettaglio', pk=doc.obiettivo.pk)
|
|
return redirect('documento_dettaglio', pk=doc.pk)
|
|
else:
|
|
form = DocumentoForm(initial=initial)
|
|
return render(request, 'diario/documenti/form.html', {'form': form, 'titolo_pagina': 'Carica documento'})
|
|
|
|
|
|
@login_required
|
|
def documento_dettaglio(request, pk):
|
|
doc = get_object_or_404(Documento.objects.select_related('caricato_da', 'conversazione', 'obiettivo'), pk=pk)
|
|
return render(request, 'diario/documenti/dettaglio.html', {
|
|
'doc': doc,
|
|
'can_edit': _can_edit(request.user, doc),
|
|
})
|
|
|
|
|
|
@login_required
|
|
def documento_elimina(request, pk):
|
|
doc = get_object_or_404(Documento, pk=pk)
|
|
if not _can_edit(request.user, doc):
|
|
return HttpResponseForbidden('Non hai i permessi per eliminare questo documento.')
|
|
if request.method == 'POST':
|
|
doc.file.delete(save=False)
|
|
doc.delete()
|
|
messages.success(request, 'Documento eliminato.')
|
|
return redirect('documenti_lista')
|
|
return render(request, 'diario/conferma_elimina.html', {
|
|
'oggetto': doc,
|
|
'tipo': 'documento',
|
|
'cancel_url': 'documento_dettaglio',
|
|
})
|
|
|
|
|
|
# ── Ricerca ────────────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def ricerca(request):
|
|
q = request.GET.get('q', '').strip()
|
|
risultati = {'conversazioni': [], 'obiettivi': [], 'documenti': [], 'persone': []}
|
|
total = 0
|
|
|
|
if len(q) >= 2:
|
|
risultati['conversazioni'] = Conversazione.objects.filter(
|
|
Q(titolo__icontains=q) | Q(contenuto__icontains=q)
|
|
).select_related('registrato_da')[:20]
|
|
|
|
risultati['obiettivi'] = Obiettivo.objects.filter(
|
|
Q(titolo__icontains=q) | Q(descrizione__icontains=q)
|
|
).select_related('creato_da')[:20]
|
|
|
|
risultati['documenti'] = Documento.objects.filter(
|
|
Q(titolo__icontains=q) | Q(descrizione__icontains=q)
|
|
).select_related('caricato_da')[:20]
|
|
|
|
risultati['persone'] = User.objects.filter(
|
|
Q(first_name__icontains=q) | Q(last_name__icontains=q) | Q(username__icontains=q),
|
|
is_active=True,
|
|
)[:20]
|
|
|
|
total = sum(len(v) if hasattr(v, '__len__') else v.count() for v in risultati.values())
|
|
|
|
ctx = {'q': q, 'risultati': risultati, 'total': total}
|
|
|
|
if getattr(request, 'htmx', False):
|
|
return render(request, 'diario/ricerca_risultati.html', ctx)
|
|
|
|
return render(request, 'diario/ricerca.html', ctx)
|
|
|
|
|
|
# ── Persone ────────────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def persone_lista(request):
|
|
persone = User.objects.filter(is_active=True).annotate(
|
|
num_conversazioni=Count('conversazioni_registrate', distinct=True),
|
|
num_obiettivi=Count('obiettivi_creati', distinct=True),
|
|
num_documenti=Count('documenti_caricati', distinct=True),
|
|
).order_by('first_name', 'username')
|
|
return render(request, 'diario/persone/lista.html', {'persone': persone})
|
|
|
|
|
|
@login_required
|
|
def persona_dettaglio(request, pk):
|
|
persona = get_object_or_404(User, pk=pk, is_active=True)
|
|
conversazioni = Conversazione.objects.filter(
|
|
Q(registrato_da=persona) | Q(partecipanti=persona)
|
|
).distinct().select_related('registrato_da').order_by('-data')[:20]
|
|
|
|
obiettivi = Obiettivo.objects.filter(
|
|
Q(creato_da=persona) | Q(assegnato_a=persona)
|
|
).distinct().select_related('creato_da').prefetch_related('assegnato_a').order_by('-data_creazione')[:20]
|
|
|
|
documenti = Documento.objects.filter(
|
|
caricato_da=persona
|
|
).select_related('conversazione', 'obiettivo').order_by('-data_caricamento')[:20]
|
|
|
|
# Menzioni: commenti e aggiornamenti dove @username appare nel testo
|
|
mention_pattern = f'@{persona.username}'
|
|
menzioni_commenti = CommentoConversazione.objects.filter(
|
|
testo__icontains=mention_pattern
|
|
).select_related('autore', 'conversazione').order_by('-data')[:20]
|
|
|
|
menzioni_aggiornamenti = AggiornamentoObiettivo.objects.filter(
|
|
testo__icontains=mention_pattern
|
|
).select_related('autore', 'obiettivo').order_by('-data')[:20]
|
|
|
|
return render(request, 'diario/persone/dettaglio.html', {
|
|
'persona': persona,
|
|
'conversazioni': conversazioni,
|
|
'obiettivi': obiettivi,
|
|
'documenti': documenti,
|
|
'menzioni_commenti': menzioni_commenti,
|
|
'menzioni_aggiornamenti': menzioni_aggiornamenti,
|
|
'totale_menzioni': len(menzioni_commenti) + len(menzioni_aggiornamenti),
|
|
})
|
|
|
|
|
|
# ── Appuntamenti ───────────────────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def appuntamenti_lista(request):
|
|
oggi = timezone.now()
|
|
futuri = Appuntamento.objects.filter(data_ora__gte=oggi).select_related('creato_da', 'conversazione').order_by('data_ora')
|
|
passati = Appuntamento.objects.filter(data_ora__lt=oggi).select_related('creato_da', 'conversazione').order_by('-data_ora')[:30]
|
|
return render(request, 'diario/appuntamenti/lista.html', {
|
|
'futuri': futuri,
|
|
'passati': passati,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def appuntamento_nuovo(request):
|
|
conversazione_pk = request.GET.get('conversazione')
|
|
initial = {}
|
|
if conversazione_pk:
|
|
conv = get_object_or_404(Conversazione, pk=conversazione_pk)
|
|
initial['titolo'] = f"Riunione: {conv.titolo}"
|
|
initial['partecipanti'] = conv.partecipanti.all()
|
|
|
|
if request.method == 'POST':
|
|
form = AppuntamentoForm(request.POST)
|
|
if form.is_valid():
|
|
app = form.save(commit=False)
|
|
app.creato_da = request.user
|
|
if conversazione_pk:
|
|
app.conversazione_id = conversazione_pk
|
|
app.save()
|
|
form.save_m2m()
|
|
messages.success(request, 'Appuntamento creato.')
|
|
if conversazione_pk:
|
|
return redirect('conversazione_dettaglio', pk=conversazione_pk)
|
|
return redirect('appuntamento_dettaglio', pk=app.pk)
|
|
else:
|
|
form = AppuntamentoForm(initial=initial)
|
|
return render(request, 'diario/appuntamenti/form.html', {
|
|
'form': form,
|
|
'titolo_pagina': 'Nuovo appuntamento',
|
|
'conversazione_pk': conversazione_pk,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def appuntamento_dettaglio(request, pk):
|
|
app = get_object_or_404(Appuntamento.objects.select_related('creato_da', 'conversazione'), pk=pk)
|
|
return render(request, 'diario/appuntamenti/dettaglio.html', {
|
|
'app': app,
|
|
'can_edit': _can_edit(request.user, app),
|
|
})
|
|
|
|
|
|
@login_required
|
|
def appuntamento_modifica(request, pk):
|
|
app = get_object_or_404(Appuntamento, pk=pk)
|
|
if not _can_edit(request.user, app):
|
|
return HttpResponseForbidden('Non hai i permessi per modificare questo appuntamento.')
|
|
if request.method == 'POST':
|
|
form = AppuntamentoForm(request.POST, instance=app)
|
|
if form.is_valid():
|
|
form.save()
|
|
messages.success(request, 'Appuntamento aggiornato.')
|
|
return redirect('appuntamento_dettaglio', pk=app.pk)
|
|
else:
|
|
form = AppuntamentoForm(instance=app)
|
|
return render(request, 'diario/appuntamenti/form.html', {
|
|
'form': form,
|
|
'titolo_pagina': 'Modifica appuntamento',
|
|
'app': app,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def appuntamento_elimina(request, pk):
|
|
app = get_object_or_404(Appuntamento, pk=pk)
|
|
if not _can_edit(request.user, app):
|
|
return HttpResponseForbidden('Non hai i permessi per eliminare questo appuntamento.')
|
|
if request.method == 'POST':
|
|
app.delete()
|
|
messages.success(request, 'Appuntamento eliminato.')
|
|
return redirect('appuntamenti_lista')
|
|
return render(request, 'diario/conferma_elimina.html', {
|
|
'oggetto': app,
|
|
'tipo': 'appuntamento',
|
|
'cancel_url': 'appuntamento_dettaglio',
|
|
})
|
|
|
|
|
|
# ── API: utenti per @menzioni ──────────────────────────────────────────────────
|
|
|
|
@login_required
|
|
def api_utenti(request):
|
|
"""Restituisce lista utenti per autocomplete @menzioni."""
|
|
q = request.GET.get('q', '').strip()
|
|
utenti = User.objects.filter(is_active=True)
|
|
if q:
|
|
utenti = utenti.filter(
|
|
Q(username__icontains=q) | Q(first_name__icontains=q) | Q(last_name__icontains=q)
|
|
)
|
|
data = [
|
|
{
|
|
'username': u.username,
|
|
'nome': u.get_full_name() or u.username,
|
|
'pk': u.pk,
|
|
}
|
|
for u in utenti.order_by('first_name', 'username')[:15]
|
|
]
|
|
return JsonResponse(data, safe=False)
|
|
|