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 import random import boto3 from datetime import datetime from urllib.parse import urlparse import os.path s3client = boto3.client('s3', **getattr(settings, 'S3_CREDENTIALS', {})) BUCKET = settings.AWS_BUCKET MEDIA_TYPES = [ ('audio', "Audio"), ('video', "Video"), ('general', "General"), ] def rough_date(d): days = (self.event_date - timezone.now().date()).days in_past = days < 0 if in_past: days = abs(days) if days ==0: return "today!" if days >= 7: return in_past, "{0:d} weeks, {1:d} days".format(days / 7, 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): name = models.CharField(max_length=100) code = models.CharField(max_length=9, default=generate_code) passphrase = models.CharField(max_length=100) admins = models.ManyToManyField('auth.User', related_name='ensembles') slug = models.SlugField(max_length=100, editable=False) details = models.TextField(blank=True) storage = models.ForeignKey('byostorage.UserStorage', null=True, on_delete=models.SET_NULL) def active_projects(self): return self.projects.filter(active=True) def ensemble_code(self): code = str(self.code) return "{}-{}-{}".format(code[:3], code[3:6], code[6:]) 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): name = models.CharField(max_length=100) ensemble = models.ForeignKey(Ensemble, related_name='projects', on_delete=models.CASCADE, null=True) description = models.TextField(blank=True) active = models.BooleanField(default=True) event_date =models.DateField(null=True, blank=True) owner = models.CharField(max_length=255, blank=True) slug = models.SlugField(max_length=100, editable=False) enable_library = models.BooleanField(default=True, help_text="Enable items to be added from the library") enable_submissions = models.BooleanField(default=False, help_text="Allow media submissions from participants") @property def submissions(self): return self.all_submissions.filter(complete=True).order_by('-pk') def presigned_post(self, object_name, fields=None, conditions=None, expires=3600): key = os.path.join(self.slug, object_name) return s3client.generate_presigned_post(BUCKET, key, Fields=fields or {}, Conditions=conditions or [], ExpiresIn=expires) @property def days(self): return (self.event_date - timezone.now().date()).days @property def has_happened(self): return self.event_date < timezone.now().date() def save(self): if not self.slug: self.slug = slugify(self.name) super(Project, self).save() def __str__(self): return self.name class Resource(models.Model): project = models.ForeignKey(Project, related_name='resources', on_delete=models.CASCADE) name = models.CharField(max_length=100) description = models.TextField(blank=True) key = models.CharField(max_length=255, blank=True) media_type = models.CharField(max_length=10, choices=MEDIA_TYPES, default='*') visible = models.BooleanField(default=True) def key_template(self): return "{}/${{filename}}".format(slugify(self.name)) def accept(self): if self.media_type == 'general': return ".*" return f"{self.media_type}/*" def presigned_url(self): if not self.key: return "" params = {'Bucket': BUCKET, 'Key': self.key} return s3client.generate_presigned_url('get_object', Params=params, ExpiresIn=3600*24) def __str__(self): return self.name class WikiPage(models.Model): 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 class Submission(models.Model): project = models.ForeignKey(Project, related_name='all_submissions', on_delete=models.CASCADE) date = models.DateTimeField(auto_now_add=True, ) name = models.CharField(max_length=255) instrument = models.CharField(max_length=100, verbose_name="Instrument / Voice") notes = models.TextField(blank=True) complete = models.BooleanField(default=False) url = models.CharField(max_length=512, blank=True) private = models.BooleanField(default=False) @property def download_url(self): if not self.complete: raise RuntimeError("Submission not complete") if self.private: return self.url params = {'Bucket': BUCKET, 'Key': self.url} return s3client.generate_presigned_url('get_object', Params=params, ExpiresIn=3600) @property def download_name(self): uri = urlparse(self.download_url) _, name = os.path.split(uri.path) return name or "" def key_template(self): return "submissions/{}_{}_{}_${{filename}}".format( timezone.localtime(self.date).isoformat(timespec='seconds').replace(':', '')[:17], slugify(self.name), slugify(self.instrument) ) @property def short_name(self): _, ext = os.path.splitext(self.download_name) return "{}_{}_{}{}".format( #timezone.localtime(self.date).strftime("%Y%m%d%H%M%S"), slugify(self.name), slugify(self.instrument), self.pk, ext ) def __str__(self): return f"{self.name}: {self.date}"