blender-studio/training/models/sections.py

126 lines
3.9 KiB
Python

import uuid
from django.contrib.auth import get_user_model
from django.db import models
from django.urls.base import reverse
from taggit.managers import TaggableManager
from typing import Optional
import looper.model_mixins
from comments.models import Comment
from common import mixins
from training.models import chapters
import common.help_texts
import static_assets.models as models_static_assets
User = get_user_model()
class Section(
mixins.CreatedUpdatedMixin,
mixins.StaticThumbnailURLMixin,
looper.model_mixins.RecordModificationMixin,
mixins.SaveAndRecordChangesMixin,
models.Model,
):
class Meta:
ordering = ['index', 'name']
chapter = models.ForeignKey(chapters.Chapter, on_delete=models.CASCADE, related_name='sections')
index = models.IntegerField()
name = models.CharField(max_length=512)
slug = models.SlugField(unique=True, null=False)
text = models.TextField(blank=True, help_text=common.help_texts.markdown_with_html)
is_free = models.BooleanField(default=False)
is_published = models.BooleanField(default=False)
is_featured = models.BooleanField(default=False)
# Can be a video, an image or a file
# If the static asset is a video, a video player will be shown
static_asset = models.OneToOneField(
'static_assets.StaticAsset',
on_delete=models.CASCADE,
related_name='section',
blank=True,
null=True,
)
# If set, a YouTube embedded player will be displayed instead of the static_asset preview
preview_youtube_link = models.URLField(null=True, blank=True)
user = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
comments = models.ManyToManyField(Comment, through='SectionComment', related_name='section')
attachments = models.ManyToManyField(
models_static_assets.StaticAsset, blank=True, related_name='+'
)
tags = TaggableManager(blank=True)
record_modification_fields = {
'static_asset_id',
'is_free',
'is_published',
'is_featured',
'preview_youtube_link',
'name',
}
def clean(self) -> None:
super().clean()
# TODO(fsiddi) Add background job to update file metadata for static_asset on the bucket
if not self.slug:
# TODO(fsiddi) Look into alphaid for a shorter slug
self.slug = uuid.uuid4().hex
def save(self, *args, **kwargs):
"""Record changes before saving."""
self.save_and_record_changes(*args, **kwargs)
def __str__(self) -> str:
return (
f'{self.chapter.training.name} > {self.chapter.index:02.0f}. {self.chapter.name} > '
f'{self.index:02.0f}. {self.name}'
)
@property
def thumbnail(self) -> Optional[str]:
# Try to use asset thumbnail
if self.static_asset and self.static_asset.thumbnail:
return self.static_asset.thumbnail
# Try to use chapter thumbnail
if self.chapter.thumbnail:
return None
def get_absolute_url(self) -> str:
return self.url
@property
def url(self) -> str:
return reverse(
'section',
kwargs={'training_slug': self.chapter.training.slug, 'section_slug': self.slug},
)
@property
def comment_url(self) -> str:
return reverse('section-comment', kwargs={'section_pk': self.pk})
@property
def progress_url(self) -> str:
return reverse('section-progress', kwargs={'section_pk': self.pk})
@property
def admin_url(self) -> str:
return reverse('admin:training_section_change', args=[self.pk])
class SectionComment(models.Model):
class Meta:
constraints = [
models.UniqueConstraint(fields=['comment'], name='unique_section_per_comment')
]
section = models.ForeignKey(Section, on_delete=models.CASCADE)
comment = models.ForeignKey(Comment, on_delete=models.CASCADE)