- 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
181 lines
6.0 KiB
Python
181 lines
6.0 KiB
Python
from django.db import models
|
|
from django.contrib.auth.models import User
|
|
from django.core.validators import FileExtensionValidator
|
|
from django.utils import timezone
|
|
|
|
|
|
def validate_file_size(value):
|
|
limit = 10 * 1024 * 1024 # 10 MB
|
|
if value.size > limit:
|
|
from django.core.exceptions import ValidationError
|
|
raise ValidationError('Il file non può superare i 10 MB.')
|
|
|
|
|
|
class Tag(models.Model):
|
|
nome = models.CharField(max_length=50, unique=True)
|
|
colore = models.CharField(max_length=7, default='#4361ee') # colore hex
|
|
|
|
class Meta:
|
|
ordering = ['nome']
|
|
verbose_name = 'Tag'
|
|
verbose_name_plural = 'Tag'
|
|
|
|
def __str__(self):
|
|
return self.nome
|
|
|
|
|
|
class Conversazione(models.Model):
|
|
titolo = models.CharField(max_length=200)
|
|
data = models.DateTimeField(default=timezone.now)
|
|
partecipanti = models.ManyToManyField(User, related_name='conversazioni_partecipate', blank=True)
|
|
contenuto = models.TextField()
|
|
registrato_da = models.ForeignKey(
|
|
User, on_delete=models.SET_NULL, null=True, related_name='conversazioni_registrate'
|
|
)
|
|
tags = models.ManyToManyField(Tag, blank=True, related_name='conversazioni')
|
|
|
|
class Meta:
|
|
ordering = ['-data']
|
|
verbose_name = 'Conversazione'
|
|
verbose_name_plural = 'Conversazioni'
|
|
|
|
def __str__(self):
|
|
return f"{self.titolo} ({self.data.strftime('%d/%m/%Y')})"
|
|
|
|
|
|
class Obiettivo(models.Model):
|
|
TIPO_CHOICES = [
|
|
('collettivo', 'Collettivo'),
|
|
('individuale', 'Individuale'),
|
|
]
|
|
STATO_CHOICES = [
|
|
('aperto', 'Aperto'),
|
|
('in_corso', 'In corso'),
|
|
('completato', 'Completato'),
|
|
('sospeso', 'Sospeso'),
|
|
]
|
|
|
|
titolo = models.CharField(max_length=200)
|
|
descrizione = models.TextField(blank=True)
|
|
avanzamento = models.PositiveSmallIntegerField(default=0) # 0-100
|
|
tipo = models.CharField(max_length=20, choices=TIPO_CHOICES, default='collettivo')
|
|
assegnato_a = models.ManyToManyField(
|
|
User, blank=True,
|
|
related_name='obiettivi_assegnati'
|
|
)
|
|
stato = models.CharField(max_length=20, choices=STATO_CHOICES, default='aperto')
|
|
data_scadenza = models.DateField(null=True, blank=True)
|
|
creato_da = models.ForeignKey(
|
|
User, on_delete=models.SET_NULL, null=True, related_name='obiettivi_creati'
|
|
)
|
|
data_creazione = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['-data_creazione']
|
|
verbose_name = 'Obiettivo'
|
|
verbose_name_plural = 'Obiettivi'
|
|
|
|
def __str__(self):
|
|
return self.titolo
|
|
|
|
@property
|
|
def giorni_rimanenti(self):
|
|
"""Restituisce i giorni rimanenti alla scadenza (None se non impostata)."""
|
|
if not self.data_scadenza:
|
|
return None
|
|
delta = self.data_scadenza - timezone.now().date()
|
|
return delta.days
|
|
|
|
|
|
class AggiornamentoObiettivo(models.Model):
|
|
obiettivo = models.ForeignKey(Obiettivo, on_delete=models.CASCADE, related_name='aggiornamenti')
|
|
testo = models.TextField()
|
|
autore = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='aggiornamenti')
|
|
data = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['-data']
|
|
verbose_name = 'Aggiornamento'
|
|
verbose_name_plural = 'Aggiornamenti'
|
|
|
|
def __str__(self):
|
|
return f"Aggiornamento su '{self.obiettivo}' del {self.data.strftime('%d/%m/%Y')}"
|
|
|
|
|
|
class CommentoConversazione(models.Model):
|
|
conversazione = models.ForeignKey(Conversazione, on_delete=models.CASCADE, related_name='commenti')
|
|
testo = models.TextField()
|
|
autore = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name='commenti_conversazione')
|
|
data = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['-data']
|
|
verbose_name = 'Commento'
|
|
verbose_name_plural = 'Commenti'
|
|
|
|
def __str__(self):
|
|
return f"Commento su '{self.conversazione}' del {self.data.strftime('%d/%m/%Y')}"
|
|
|
|
|
|
class Documento(models.Model):
|
|
file = models.FileField(
|
|
upload_to='documenti/%Y/%m/',
|
|
validators=[
|
|
FileExtensionValidator(allowed_extensions=['pdf']),
|
|
validate_file_size,
|
|
],
|
|
)
|
|
titolo = models.CharField(max_length=200)
|
|
descrizione = models.TextField(blank=True)
|
|
caricato_da = models.ForeignKey(
|
|
User, on_delete=models.SET_NULL, null=True, related_name='documenti_caricati'
|
|
)
|
|
data_caricamento = models.DateTimeField(auto_now_add=True)
|
|
conversazione = models.ForeignKey(
|
|
'Conversazione', on_delete=models.SET_NULL, null=True, blank=True, related_name='documenti'
|
|
)
|
|
obiettivo = models.ForeignKey(
|
|
'Obiettivo', on_delete=models.SET_NULL, null=True, blank=True, related_name='documenti'
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ['-data_caricamento']
|
|
verbose_name = 'Documento'
|
|
verbose_name_plural = 'Documenti'
|
|
|
|
def __str__(self):
|
|
return self.titolo
|
|
|
|
@property
|
|
def filename(self):
|
|
import os
|
|
return os.path.basename(self.file.name)
|
|
|
|
|
|
class Appuntamento(models.Model):
|
|
titolo = models.CharField(max_length=200)
|
|
data_ora = models.DateTimeField()
|
|
luogo = models.CharField(max_length=200, blank=True)
|
|
note = models.TextField(blank=True)
|
|
conversazione = models.ForeignKey(
|
|
Conversazione, on_delete=models.SET_NULL, null=True, blank=True, related_name='appuntamenti'
|
|
)
|
|
partecipanti = models.ManyToManyField(User, blank=True, related_name='appuntamenti')
|
|
creato_da = models.ForeignKey(
|
|
User, on_delete=models.SET_NULL, null=True, related_name='appuntamenti_creati'
|
|
)
|
|
data_creazione = models.DateTimeField(auto_now_add=True)
|
|
|
|
class Meta:
|
|
ordering = ['data_ora']
|
|
verbose_name = 'Appuntamento'
|
|
verbose_name_plural = 'Appuntamenti'
|
|
|
|
def __str__(self):
|
|
return f"{self.titolo} ({self.data_ora.strftime('%d/%m/%Y %H:%M')})"
|
|
|
|
@property
|
|
def is_passato(self):
|
|
return self.data_ora < timezone.now()
|
|
|