from django.db import models from django.utils.text import slugify from django.utils import timezone from django.conf import settings from django.shortcuts import resolve_url from byostorage.user import BYOStorage import random from urllib.parse import urlparse import os.path MEDIA_TYPES = [ ('audio', "Audio"), ('video', "Video"), ('general', "General"), ] def rough_date(d): if not d: return False, "sometime..." days = (d - timezone.now()).days in_past = days < 0 if in_past: days = abs(days) if days == 0: m = int((d-timezone.now()).seconds/60) if m > 60: return in_past, "{0:d} hours".format(int(m / 60)) return in_past, "{0:d} minutes!".format(int(m % 60)) if days >= 14: return in_past, "{0:d} weeks".format(int(days/7)) if days >= 7: return in_past, "{0:d} weeks, {1:d} days".format(int(days / 7), int(days % 7)) return in_past, f"{days} days" def generate_code(length=9): return "".join([ random.choice('0123456789') for _ in range(length) ]) class Ensemble(models.Model): ''' A group that plays together ''' name = models.CharField(max_length=100, help_text="Display name") slug = models.SlugField(max_length=100, editable=False, unique=True, help_text="Short name for the ensemble - used for folders") #code = models.CharField(max_length=9, default=generate_code, # help_text="Ensemble registration code") #passphrase = models.CharField(max_length=100, # help_text="Used to register ensembles") admins = models.ManyToManyField('auth.User', related_name='ensembles') details = models.TextField(blank=True, help_text="Description of the ensemble (markdown)") storage = models.ForeignKey('byostorage.UserStorage', null=True, on_delete=models.SET_NULL, help_text="Default storage for this ensemble") class Meta: ordering = ('slug', ) def active_projects(self): return self.projects.filter(active=True, event_date__gte=timezone.now()) def ensemble_code(self): code = str(self.code) return "{}-{}-{}".format(code[:3], code[3:6], code[6:]) def has_admin(self, user): if not user.is_authenticated: return False if user.is_superuser: return True return user.pk in self.admins.values_list('pk', flat=True) def save(self, **kwargs): if not self.slug: self.slug = slugify(self.name) super(Ensemble, self).save(**kwargs) def __str__(self): return self.name class Project(models.Model): ''' A Project linked to an ensemble ''' name = models.CharField(max_length=100) ensemble = models.ForeignKey(Ensemble, related_name='projects', on_delete=models.CASCADE, null=True) description = models.TextField(blank=True, help_text="Markdown format") active = models.BooleanField(default=True) event_date =models.DateTimeField(null=True, blank=True) owner = models.CharField(max_length=255, blank=True) class Meta: ordering = ['active', 'event_date'] @property def days(self): return (self.event_date - timezone.now().date()).days @property def has_happened(self): if not self.event_date: return False return self.event_date < timezone.now() @property def rough_date(self): in_past, s = rough_date(self.event_date) if in_past: return f"{s} ago" return f"In {s}" @property def folder(self): project = slugify(self.name) print(f"{self.ensemble.storage_id}:{self.ensemble.slug}/{project}") return f"{self.ensemble.storage_id}:{self.ensemble.slug}/{project}" @property def active_modules(self): return self.modules.values_list('name', flat=True) def __str__(self): return self.name class Module(models.Model): ''' Enable modules on a oriject ''' name = models.SlugField(max_length=20, choices=[ (x, x.title()) for x in settings.POLYPHONIC_MODULES ]) project = models.ForeignKey(Project, related_name="modules", on_delete=models.CASCADE) def resource_key(resource, filename): return f'{resource.project.folder}/resources/{filename}' class Resource(models.Model): ''' A viewable file resource attached to a project e.g PDF instructions, MP3 backing track ''' project = models.ForeignKey(Project, related_name='resources', on_delete=models.CASCADE) name = models.CharField(max_length=100) description = models.TextField(blank=True) file = models.FileField(storage=BYOStorage(), upload_to=resource_key) media_type = models.CharField(max_length=10, choices=MEDIA_TYPES, default='*') visible = models.BooleanField(default=True) class Meta: ordering = ['-visible', '-pk'] def accept(self): if self.media_type == 'general': return ".*" return f"{self.media_type}/*" def __str__(self): return self.name class WikiPage(models.Model): ''' An editable wiki page for the project in markdown format ''' project = models.ForeignKey(Project, related_name='wiki_pages', on_delete=models.CASCADE) title = models.CharField(max_length=255) markdown = models.TextField() def get_absolute_url(self): return resolve_url('wiki', project=self.project_id, pk=self.pk) def __str__(self): return self.title