160 lines
4.8 KiB
Python
160 lines
4.8 KiB
Python
from django.conf import settings
|
|
from django.db import models
|
|
from django.utils.text import slugify
|
|
from django.core.files.storage import get_storage_class
|
|
|
|
import re
|
|
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
try:
|
|
library_storage = get_storage_class(settings.LIBRARY_STORAGE)()
|
|
except (ImportError, AttributeError):
|
|
library_storage = get_storage_class()()
|
|
logger.info("Library storage: %s", library_storage.__class__.__name__)
|
|
|
|
INSTRUMENTS = [
|
|
('Score', 'Score'),
|
|
('S', 'Soprano'),
|
|
('A', 'Alto'),
|
|
('T', 'Tenor'),
|
|
('B', 'Bass'),
|
|
('V', 'Vocals'),
|
|
('Vln', 'Violin'),
|
|
('Vla', 'Viola'),
|
|
('Vc', 'Violoncello'),
|
|
('Cb', 'Contrabass'),
|
|
('Fl', 'Flute'),
|
|
('Picc', 'Piccolo'),
|
|
('Cl', 'Clarinet'),
|
|
('Ob', 'Oboe'),
|
|
('Hn', 'Horn'),
|
|
('Tpt', 'Trumpet'),
|
|
('Tbn', 'Trombone'),
|
|
('Tuba', 'Tuba'),
|
|
('Timp', 'Timpani'),
|
|
('Drum', 'Drumset'),
|
|
('Perc', 'Percussion'),
|
|
('Pno', 'Piano'),
|
|
('Hp', 'Harp'),
|
|
]
|
|
|
|
ORCHESTRATIONS = {
|
|
'SATB': ('S', 'A', 'T', 'B'),
|
|
'String Quartet': ('Vln1', 'Vln2', 'Vla', 'Vc'),
|
|
'Chamber Orchestra': ('Vln1', 'Vln2', 'Vla', 'Vc', 'Cb',
|
|
'Fl1', 'Fl2', 'Cl1', 'Cl2', 'Hn1', 'Hn2',
|
|
'Tpt1', 'Tpt2', 'Tbn1', 'Tbn2', 'Tuba',
|
|
'Timp', 'Drum', 'Perc'),
|
|
'RWE': ('Fl1', 'Fl2', 'Cl', 'Tbn', 'Vln1', 'Vln2', 'Vla', 'Vc'),
|
|
'Custom': ()
|
|
}
|
|
|
|
def tag_to_instrument(tag):
|
|
m = re.match(r'([A-Za-z]+)(\d*)', tag)
|
|
if not m:
|
|
return tag
|
|
l = m.groups()
|
|
return "{0} {1}".format(dict(INSTRUMENTS).get(l[0],l[0]), l[1]).strip()
|
|
|
|
class Orchestration(models.Model):
|
|
ensemble = models.ForeignKey('interface.Ensemble', on_delete=models.CASCADE, related_name="orchestrations", null=True, blank=True)
|
|
name = models.CharField(max_length=100)
|
|
instruments = models.TextField()
|
|
|
|
def as_list(self):
|
|
tags = [ t.strip() for t in self.instruments.split(',') ]
|
|
return [ (t, tag_to_instrument(t)) for t in tags if t ]
|
|
|
|
def save(self):
|
|
self.as_list()
|
|
super(Orchestration, self).save()
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Work(models.Model):
|
|
ensemble = models.ForeignKey('interface.Ensemble', on_delete=models.CASCADE, related_name="works")
|
|
slug = models.SlugField(max_length=100, editable=False)
|
|
name = models.CharField(max_length=255)
|
|
orchestration = models.ForeignKey(Orchestration, null=True, on_delete=models.SET_NULL, related_name='works')
|
|
running_time = models.IntegerField(null=True, blank=True)
|
|
notes = models.TextField(blank=True)
|
|
projects = models.ManyToManyField('interface.Project', through='Item', related_name="works")
|
|
|
|
class Meta:
|
|
unique_together = ['ensemble', 'slug']
|
|
|
|
@property
|
|
def parts(self):
|
|
return Part.objects.filter(doc__work=self.pk)
|
|
|
|
@property
|
|
def instruments(self):
|
|
return self.orchestration.as_list()
|
|
|
|
def save(self):
|
|
if not self.slug:
|
|
self.slug = slugify(self.name)
|
|
super(Work, self).save()
|
|
|
|
def __str__(self):
|
|
return self.name
|
|
|
|
class Item(models.Model):
|
|
"""
|
|
Item represents a Work in a Project e.g. item in set list or programme
|
|
It also allows works to be shared from one ensemble to another on a per-project basis.
|
|
"""
|
|
project = models.ForeignKey('interface.Project', on_delete=models.CASCADE)
|
|
work = models.ForeignKey('Work', on_delete=models.CASCADE)
|
|
order = models.SmallIntegerField(default=0)
|
|
|
|
class Meta:
|
|
ordering = ['order', 'work']
|
|
|
|
def __str__(self):
|
|
return f"<{self.project.slug}:{self.work.slug}>"
|
|
|
|
def doc_upload_filename(doc, filename):
|
|
storage = doc.work.ensemble.storage
|
|
if not storage:
|
|
raise RuntimeError("Storage not set")
|
|
return f'{storage}:{doc.work.ensemble.slug}/works/{doc.work.slug}/{filename}'
|
|
|
|
class Document(models.Model):
|
|
work = models.ForeignKey('Work', on_delete=models.CASCADE, related_name="docs")
|
|
upload = models.FileField(upload_to=doc_upload_filename, storage=library_storage)
|
|
|
|
def __str__(self):
|
|
return self.upload.name
|
|
|
|
class Part(models.Model):
|
|
doc = models.ForeignKey(Document, on_delete=models.CASCADE, related_name="parts")
|
|
tag = models.SlugField(max_length=20)
|
|
start = models.SmallIntegerField(null=True, blank=True)
|
|
end = models.SmallIntegerField(null=True, blank=True)
|
|
notes = models.TextField(blank=True)
|
|
|
|
class Meta:
|
|
ordering = ['doc', 'start', 'pk']
|
|
|
|
@property
|
|
def instrument(self):
|
|
return tag_to_instrument(self.tag)
|
|
|
|
@property
|
|
def filename(self):
|
|
return slugify(f'{self.doc.work.name}_{self.instrument}') + '.pdf'
|
|
|
|
@property
|
|
def pagerange(self):
|
|
if self.start:
|
|
if self.end:
|
|
return f"{self.start}-{self.end}"
|
|
return str(self.start)
|
|
return "all"
|
|
|
|
def __str__(self):
|
|
return f'{self.doc.upload} [{self.pagerange}]' |