Skip to content
Snippets Groups Projects
models.py 6.85 KiB
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"