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. 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="Můžeš použít Markdown.", ) 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 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 class TypeChoices(models.TextChoices): RECOMMENDED = "recommended", "Doporučené školení" OPTIONAL = "optional", "Volitelné školení" groups = models.ManyToManyField( LectureGroup, blank=True, related_name="lectures", verbose_name="Výukové skupiny", help_text="Pokud nevybereš žádné skupiny, školení je dostupné všem.", ) type = models.CharField( choices=TypeChoices.choices, max_length=11, verbose_name="Typ", ) name = models.CharField( max_length=128, verbose_name="Název", ) description = MarkdownxField( blank=True, null=True, verbose_name="Popis", help_text="Můžeš použít Markdown.", ) # Settings settings = app_settings.LectureSettings("Nastavení") class Meta: verbose_name = "Školení" verbose_name_plural = verbose_name ordering = ("-timestamp", "-name") class LectureLector(NameStrMixin, models.Model): lecture = models.ForeignKey( "Lecture", on_delete=models.CASCADE, related_name="lectors", verbose_name="Školení", ) name = models.CharField( max_length=128, verbose_name="Jméno", ) 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>.") ) 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." ), ) 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): 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 ( "_private/" f"{current_time.year}/{current_time.month}/{current_time.day}/" f"{uuid.uuid4()}{extension}" ) 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"