import mimetypes import uuid from datetime import datetime, timedelta from django.contrib.auth.models import Group from django.core.exceptions import ValidationError from django.db import models from django.db.models.fields.files import FieldFile from django.urls import reverse from django.utils import timezone from django.utils.safestring import mark_safe from markdownx.models import MarkdownxField from shared.models import NameStrMixin from . import settings as app_settings # Create your models here. def get_file_location(instance, filename, path_prefix="public"): mimetypes_instance = mimetypes.MimeTypes() current_time = datetime.today() guessed_type = mimetypes_instance.guess_type(filename, strict=False)[0] extension = "" if guessed_type is not None: for mapper in mimetypes_instance.types_map_inv: if guessed_type not in mapper: continue extension = mapper[guessed_type] if isinstance(extension, list): extension = extension[0] break return ( f"{path_prefix}/" f"{current_time.year}/{current_time.month}/{current_time.day}/" f"{uuid.uuid4()}{extension}" ) class LectureGroup(NameStrMixin, models.Model): name = models.CharField( max_length=128, verbose_name="Jméno", ) description = MarkdownxField( null=True, blank=True, verbose_name="Popis", help_text=mark_safe( r'Můžeš použít <a href="' r"https://cs.wikipedia.org/wiki/Markdown" r'#P%C5%99%C3%ADklad_u%C5%BEit%C3%AD"' r">Markdown</a>." ), ) priority = models.IntegerField( verbose_name="Priorita", help_text="Čím nižší číslo, tím výš se skupina zobrazí.", ) user_groups = models.ManyToManyField( Group, blank=True, verbose_name="Uživatelské skupiny", help_text="Pokud žádné nedefinuješ, školení ve skupině jsou dostupná všem.", ) class Meta: verbose_name = "Výuková skupina" verbose_name_plural = "Výukové skupiny" ordering = ("priority", "name") class LectureGroupType(models.Model): lecture = models.ForeignKey( "Lecture", on_delete=models.CASCADE, related_name="lecture_group_types", verbose_name="Školení", ) group = models.ForeignKey( "LectureGroup", on_delete=models.CASCADE, related_name="lecture_group_types", verbose_name="Výuková skupina", ) class TypeChoices(models.TextChoices): REQUIRED = "required", "Silně doporučené školení" RECOMMENDED = "recommended", "Doporučené školení" VOLUNTARY = "voluntary", "Dobrovolné školení" type = models.CharField( choices=TypeChoices.choices, max_length=11, verbose_name="Požadovanost", ) def __str__(self) -> str: return f"{self.group} - {self.lecture}" class Meta: verbose_name = "Úroveň požadovanosti pro skupinu" verbose_name_plural = "Úroveň požadovanosti pro skupiny" class LectureCategory(models.Model): name = models.CharField( max_length=32, verbose_name="Jméno" ) def __str__(self) -> str: return self.name class Meta: verbose_name = "Kategorie školení" verbose_name_plural = verbose_name class Lecture(NameStrMixin, models.Model): is_current_starting_treshold = timedelta(hours=8) is_current_ending_treshold = timedelta(days=60) timestamp = models.DateTimeField( verbose_name="Datum a čas konání", blank=True, null=True, ) # If undefined, assume this event was in the past category = models.ForeignKey( "LectureCategory", null=True, on_delete=models.SET_NULL, related_name="lectures", verbose_name="Kategorie", ) name = models.CharField( max_length=128, verbose_name="Název", ) description = MarkdownxField( blank=True, null=True, verbose_name="Popis", help_text=mark_safe( r'Můžeš použít <a href="' r"https://cs.wikipedia.org/wiki/Markdown" r'#P%C5%99%C3%ADklad_u%C5%BEit%C3%AD"' r">Markdown</a>." ), ) lectors = models.ManyToManyField( "LectureLector", related_name="lectures", verbose_name="Lektoři" ) rsvp_users = models.ManyToManyField( "users.User", blank=True, related_name="rsvp_lectures", verbose_name="Zaregistrovaní uživatelé", ) # Settings settings = app_settings.LectureSettings("Nastavení") def get_current_group_type(self, request, group=None): if group is not None: return LectureGroupType.objects.filter(lecture=self, group=group).first() else: return LectureGroupType.objects.filter( lecture=self, group__in=( LectureGroup.filter( models.Q(user_groups__in=request.user.groups.all()) if not request.user.is_superuser else models.Q(id__isnull=False) # Always True ) .distinct() .all() ), ).first() class Meta: verbose_name = "Školení" verbose_name_plural = verbose_name ordering = ("-timestamp", "-name") class LectureLector(NameStrMixin, models.Model): name = models.CharField( max_length=128, verbose_name="Jméno", ) description = MarkdownxField( max_length=512, blank=True, null=True, verbose_name="Popis", help_text=mark_safe( r'Můžeš použít <a href="' r"https://cs.wikipedia.org/wiki/Markdown" r'#P%C5%99%C3%ADklad_u%C5%BEit%C3%AD"' r">Markdown</a>." ), ) url = models.URLField( max_length=256, blank=True, null=True, verbose_name="Odkaz", help_text=mark_safe( 'Běžně na <a href="https://lide.pirati.cz">aplikaci Lidé</a>.' ), ) avatar = models.ImageField( blank=True, null=True, upload_to=get_file_location, verbose_name="Profilový obrázek", help_text=( "Vložený soubor má prioritu nad obrázkem synchronizovaným z Chobotnice." ), ) username = models.CharField( max_length=128, blank=True, null=True, verbose_name="Uživatelské jméno", help_text=( "Např. na fóru, nebo v Chobotnici. Užívá se " "k synchronizaci profilového obrázku, v budoucnu " "i dalších informací." ), ) class Meta: verbose_name = "Lektor" verbose_name_plural = "Lektoři" class LectureRecording(NameStrMixin, models.Model): lecture = models.ForeignKey( "Lecture", on_delete=models.CASCADE, related_name="recordings", verbose_name="Školení", ) name = models.CharField( max_length=128, verbose_name="Název", ) link = models.URLField( max_length=256, blank=True, null=True, verbose_name="Odkaz", help_text=mark_safe( 'Běžně na <a href="https://tv.pirati.cz">Pirátskou TV</a>.' ), ) class Meta: verbose_name = "Nahrávka" verbose_name_plural = "Nahrávky" def get_lecture_material_file_location(instance, filename): return get_file_location(instance, filename, path_prefix="_private") class LectureMaterialFileProxy(FieldFile): @property def url(self) -> str: return reverse("lectures:download_material_file", args=(str(self.instance.id),)) class LectureMaterialFileField(models.FileField): attr_class = LectureMaterialFileProxy class LectureMaterial(NameStrMixin, models.Model): lecture = models.ForeignKey( "Lecture", on_delete=models.CASCADE, related_name="materials", verbose_name="Školení", ) name = models.CharField( max_length=128, verbose_name="Název", ) link = models.URLField( max_length=256, blank=True, null=True, verbose_name="Odkaz", help_text="Pokud máš zadaný odkaz, nemůžeš definovat soubor.", ) file = LectureMaterialFileField( blank=True, null=True, upload_to=get_lecture_material_file_location, verbose_name="Soubor", help_text="Pokud máš vložený soubor, nemůžeš definovat odkaz.", ) @property def protected_file_url(self) -> str: return reverse( "lectures:download_material_file", args=(self.id,), ) def clean(self) -> None: BOTH_FILE_AND_LINK_DEFINED = ( "Definuj prosím pouze odkaz, nebo soubor. Nemůžeš mít oboje najednou." ) NEITHER_FILE_NOR_LINK_DEFINED = ( "Definuj prosím odkaz, nebo soubor. Aspoň jedna hodnota musí být vyplněna." ) if not self.file and not self.link: raise ValidationError( { "link": NEITHER_FILE_NOR_LINK_DEFINED, "file": NEITHER_FILE_NOR_LINK_DEFINED, } ) if self.file and self.link: raise ValidationError( { "link": BOTH_FILE_AND_LINK_DEFINED, "file": BOTH_FILE_AND_LINK_DEFINED, } ) class Meta: verbose_name = "Materiál" verbose_name_plural = "Materiály"