Tag progetto, @menzioni, appuntamenti da conversazioni
- Modello Tag con nome e colore, M2M su Conversazione - Modello Appuntamento con luogo, note, partecipanti, link a Conversazione - @menzioni nei commenti e aggiornamenti: @username → link al profilo - Autocomplete JS per @menzioni nelle textarea - Auto-data conversazioni (default=now) - CRUD completo appuntamenti con permessi autore - Appuntamenti in agenda, dashboard, dettaglio conversazione - Crea riunione direttamente da una conversazione (pre-compila titolo e partecipanti) - Admin: Tag, Appuntamento registrati
This commit is contained in:
136
diario/views.py
136
diario/views.py
@@ -9,8 +9,8 @@ from django.utils import timezone
|
||||
from itertools import chain
|
||||
from datetime import timedelta
|
||||
|
||||
from .models import Conversazione, Obiettivo, AggiornamentoObiettivo, CommentoConversazione, Documento
|
||||
from .forms import ConversazioneForm, ObiettivoForm, AggiornamentoObiettivoForm, CommentoConversazioneForm, DocumentoForm
|
||||
from .models import Conversazione, Obiettivo, AggiornamentoObiettivo, CommentoConversazione, Documento, Tag, Appuntamento
|
||||
from .forms import ConversazioneForm, ObiettivoForm, AggiornamentoObiettivoForm, CommentoConversazioneForm, DocumentoForm, AppuntamentoForm
|
||||
|
||||
|
||||
def _can_edit(user, obj):
|
||||
@@ -51,11 +51,17 @@ def dashboard(request):
|
||||
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,
|
||||
})
|
||||
|
||||
@@ -66,7 +72,7 @@ def dashboard(request):
|
||||
def agenda(request):
|
||||
oggi = timezone.now().date()
|
||||
|
||||
# Prossimi eventi: scadenze obiettivi, conversazioni programmate nel futuro
|
||||
# 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')
|
||||
@@ -75,11 +81,16 @@ def agenda(request):
|
||||
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'],
|
||||
)
|
||||
@@ -96,10 +107,16 @@ def agenda(request):
|
||||
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,
|
||||
@@ -141,6 +158,7 @@ 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)
|
||||
@@ -159,6 +177,7 @@ def conversazione_dettaglio(request, pk):
|
||||
'commenti': commenti,
|
||||
'comment_form': comment_form,
|
||||
'documenti': documenti,
|
||||
'appuntamenti': appuntamenti,
|
||||
'can_edit': _can_edit(request.user, conv),
|
||||
})
|
||||
|
||||
@@ -515,3 +534,114 @@ def persona_dettaglio(request, pk):
|
||||
'documenti': documenti,
|
||||
})
|
||||
|
||||
|
||||
# ── 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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user