Library schema semi-fixed
This commit is contained in:
parent
8f18b9ab9d
commit
f7aaa98000
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ test.*
|
||||
static
|
||||
teststore
|
||||
cache
|
||||
local_storage
|
||||
media
|
||||
4
interface/fields.py
Normal file
4
interface/fields.py
Normal file
@ -0,0 +1,4 @@
|
||||
from crispy_forms.layout import Field
|
||||
|
||||
class BulmaFileUpload(Field):
|
||||
template = 'bulma/file_upload.html'
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 3.2.7 on 2022-11-18 09:54
|
||||
# Generated by Django 3.2.7 on 2022-11-19 10:25
|
||||
|
||||
import byostorage.user
|
||||
from django.conf import settings
|
||||
@ -9,11 +9,13 @@ import interface.models
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('interface', '0001_initial'), ('interface', '0002_alter_module_name'), ('interface', '0003_alter_ensemble_slug'), ('interface', '0004_alter_project_event_date')]
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('byostorage', '0004_alter_userstorage_storage'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@ -22,7 +24,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='Display name', max_length=100)),
|
||||
('slug', models.SlugField(editable=False, help_text='Short name for the ensemble - used for folders', max_length=100)),
|
||||
('slug', models.SlugField(editable=False, help_text='Short name for the ensemble - used for folders', max_length=100, unique=True)),
|
||||
('code', models.CharField(default=interface.models.generate_code, help_text='Ensemble registration code', max_length=9)),
|
||||
('passphrase', models.CharField(help_text='Used to register ensembles', max_length=100)),
|
||||
('details', models.TextField(blank=True, help_text='Description of the ensemble (markdown)')),
|
||||
@ -37,7 +39,7 @@ class Migration(migrations.Migration):
|
||||
('name', models.CharField(max_length=100)),
|
||||
('description', models.TextField(blank=True, help_text='Markdown format')),
|
||||
('active', models.BooleanField(default=True)),
|
||||
('event_date', models.DateField(blank=True, null=True)),
|
||||
('event_date', models.DateTimeField(blank=True, null=True)),
|
||||
('owner', models.CharField(blank=True, max_length=255)),
|
||||
('ensemble', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='projects', to='interface.ensemble')),
|
||||
],
|
||||
@ -73,7 +75,7 @@ class Migration(migrations.Migration):
|
||||
name='Module',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.SlugField(max_length=20)),
|
||||
('name', models.SlugField(choices=[('library', 'Library'), ('submissions', 'Submissions')], max_length=20)),
|
||||
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='modules', to='interface.project')),
|
||||
],
|
||||
),
|
||||
@ -154,53 +154,4 @@ class WikiPage(models.Model):
|
||||
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='old_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 "<Unknown>"
|
||||
|
||||
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}"
|
||||
"""
|
||||
return self.title
|
||||
28
interface/templates/bulma/file_upload.html
Normal file
28
interface/templates/bulma/file_upload.html
Normal file
@ -0,0 +1,28 @@
|
||||
{% load crispy_forms_field %}
|
||||
|
||||
<div class="field">
|
||||
<div id="div_id_{{ field.name }}" class="file has-name is-fullwidth">
|
||||
<label class="file-label">
|
||||
{% crispy_field field 'class' 'file-input'%}
|
||||
<span class="file-cta">
|
||||
<span class="file-icon">
|
||||
<i class="fas fa-upload"></i>
|
||||
</span>
|
||||
<span class="file-label">
|
||||
Choose a file…
|
||||
</span>
|
||||
</span>
|
||||
<span class="file-name"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const fileInput = document.querySelector('#div_id_{{ field.name }} input[type=file]');
|
||||
fileInput.onchange = () => {
|
||||
if (fileInput.files.length > 0) {
|
||||
const fileName = document.querySelector('#div_id_{{ field.name }} .file-name');
|
||||
fileName.textContent = fileInput.files[0].name;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
41
interface/templates/interface/ensemble_list.html
Normal file
41
interface/templates/interface/ensemble_list.html
Normal file
@ -0,0 +1,41 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
{% load md2 %}
|
||||
|
||||
{% block page %}
|
||||
<div class="admin-tools is-pulled-right">
|
||||
<a class="button is-link" href="{% url 'register' %}">
|
||||
<span class="icon"><i class="fas fa-plus-circle"></i></span>
|
||||
<span>Register another</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<h3 class="title">My Ensembles</h3>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
{% for ensemble in object_list %}
|
||||
<div class="column is-half">
|
||||
<div class="card">
|
||||
<header class="card-header{% if ensemble.pk == ensemble_id %} has-background-link-light{% endif %}">
|
||||
<a class="card-header-title" href="{% url 'register' %}?code={{ ensemble.code }}">
|
||||
{{ ensemble.name }}
|
||||
</a>
|
||||
<a class="card-header-icon" href="{% url 'ensemble_forget' pk=ensemble.id %}">
|
||||
<span class="delete"></span>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
{% if ensemble.details %}
|
||||
<div class="content">
|
||||
{{ ensemble.details | markdown }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
{% with projects=ensemble.active_projects.count %}
|
||||
<p>{{ projects }} active project{{ projects|pluralize }}</p>
|
||||
{% endwith %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
14
interface/templatetags/polyphonic.py
Normal file
14
interface/templatetags/polyphonic.py
Normal file
@ -0,0 +1,14 @@
|
||||
from django import template
|
||||
from django.utils import timesince
|
||||
|
||||
register = template.Library()
|
||||
|
||||
def roughtimesince(value):
|
||||
return timesince.timesince(value, depth=1)
|
||||
|
||||
register.filter('roughtimesince', roughtimesince)
|
||||
|
||||
def roughtimeuntil(value):
|
||||
return timesince.timeuntil(value, depth=1)
|
||||
|
||||
register.filter('roughtimeuntil', roughtimeuntil)
|
||||
@ -1,53 +0,0 @@
|
||||
from django.test import TestCase, Client
|
||||
|
||||
from interface import models
|
||||
|
||||
class SubmissionTestCase(TestCase):
|
||||
|
||||
@staticmethod
|
||||
def setUpTestData():
|
||||
e1 = models.Ensemble.objects.create(name='The Be Sharps', code="1234", passphrase='Homer')
|
||||
e1.projects.create(name='Baby on Board')
|
||||
e2 = models.Ensemble.objects.create(name='Lisa and the Bleeding Gums', code="2345", passphrase="Maggie")
|
||||
e2.projects.create(name='Baker St')
|
||||
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
|
||||
def test_submission_upload(self):
|
||||
response = self.client.post('/register', {'code': '12-34', 'passphrase': 'Homer'})
|
||||
self.assertRedirects(response, '/')
|
||||
|
||||
response = self.client.post(f"/projects/1/submission", {'name': 'Ned', 'instrument': 'Harp', 'method': 'upload'})
|
||||
self.assertRedirects(response, '/projects/1/submission/1/upload')
|
||||
|
||||
response = self.client.get(response.url)
|
||||
upload = response.context['upload']
|
||||
self.assertEqual(upload['url'], f"http://localhost:9000/{models.BUCKET}")
|
||||
self.assertRegex(upload['fields']['key'], r'^baby-on-board\/submissions\/[0-9T\-]+_ned_harp_\$\{filename\}$')
|
||||
self.assertEqual(upload['fields']['success_action_redirect'], 'http://testserver/projects/1/submission/1/complete')
|
||||
|
||||
self.assertEqual(models.Submission.objects.count(), 1)
|
||||
self.assertRedirects(self.client.get(f"/projects/1/submission/1/cancel"), '/projects/1')
|
||||
self.assertEqual(models.Submission.objects.count(), 0)
|
||||
|
||||
def test_submission_link(self):
|
||||
response = self.client.post('/register', {'code': '12-34', 'passphrase': 'Homer'})
|
||||
self.assertRedirects(response, '/')
|
||||
|
||||
response = self.client.post(f"/projects/1/submission", {'name': 'Ned', 'instrument': 'Harp', 'method': 'link'})
|
||||
self.assertRedirects(response, '/projects/1/submission/1/link')
|
||||
|
||||
url = 'https://drive.google.com/a/path/to/a/video.mp4#g6e6e4a23'
|
||||
|
||||
response = self.client.post(f"/projects/1/submission/1/link", {'url': url})
|
||||
self.assertRedirects(response, '/projects/1/submission/1')
|
||||
|
||||
response = self.client.get('/projects/1/submission/1')
|
||||
self.assertContains(response, "Thankyou for your submission")
|
||||
|
||||
response = self.client.get('/projects/1')
|
||||
self.assertContains(response, 'Ned')
|
||||
|
||||
s = models.Submission.objects.get(pk=1)
|
||||
self.assertEqual(s.download_url, url)
|
||||
197
library/imslp.py
Normal file
197
library/imslp.py
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
from collections import namedtuple
|
||||
|
||||
# taken from https://imslp.org/wiki/IMSLP:Abbreviations_for_Instruments
|
||||
|
||||
ABBREVIATIONS = """
|
||||
score Score
|
||||
acc Accordion
|
||||
afl Alto flute
|
||||
alt Alto (voice) (contralto)
|
||||
arp Arpeggione
|
||||
bag Bagpipe
|
||||
bar Baritone (voice)
|
||||
bass Bass (voice)
|
||||
bbar Bass baritone (voice)
|
||||
bc Continuo (Basso continuo)
|
||||
bcl Bass clarinet
|
||||
bell Bell (Chimes)
|
||||
bfl Bass flute
|
||||
bgtr Bass guitar
|
||||
bjo Banjo
|
||||
bn Bassoon
|
||||
bob Bass oboe (Baritone oboe)
|
||||
br Brass instruments
|
||||
bryt Baryton
|
||||
bstcl Basset clarinet
|
||||
bsthn Basset horn
|
||||
bug Bugle
|
||||
cbcl Contrabass clarinet
|
||||
cbn Contrabassoon
|
||||
cch Children's chorus
|
||||
cel Celesta
|
||||
ch Mixed chorus
|
||||
cimb Cimbalom
|
||||
cit Cittern
|
||||
cl Clarinet
|
||||
clvd Clavichord
|
||||
cm Chalumeau
|
||||
conc Concertina
|
||||
crh Crumhorn
|
||||
crt Cornet
|
||||
crtt Cornett (Zink)
|
||||
cv Child's voice
|
||||
db Double Bass
|
||||
dlcn Dulcian
|
||||
dom Domra
|
||||
dulc Dulcimer
|
||||
egtr Electric guitar
|
||||
eh English horn (Cor anglais)
|
||||
elec Electronic Instruments
|
||||
epf Electric piano
|
||||
eq Equal voices
|
||||
erhu Erhu
|
||||
euph Euphonium
|
||||
fch Female chorus
|
||||
fda Flute d'amore (Tenor flute)
|
||||
fgh Flugelhorn
|
||||
fife Fife
|
||||
fl Flute
|
||||
flag Flageolet
|
||||
ghca Glass harmonica (Bowl organ)
|
||||
gl Glockenspiel
|
||||
gtr Guitar
|
||||
harm Harmonium
|
||||
hca Harmonica (Mouth Organ)
|
||||
heck Heckelphone
|
||||
hn Horn
|
||||
hp Harp
|
||||
hpd Harpsichord
|
||||
kbd Keyboard instrument
|
||||
lute Lute
|
||||
lyre Lyre
|
||||
mand Mandolin
|
||||
mar Marimba
|
||||
mch Male chorus
|
||||
mez Mezzo-soprano
|
||||
mus Musette
|
||||
nar Narrator (Reciter)
|
||||
ob Oboe
|
||||
oca Ocarina
|
||||
oda Oboe d'amore
|
||||
om Ondes Martenot
|
||||
oph Ophicleide
|
||||
orch Orchestra
|
||||
org Organ
|
||||
oud Oud
|
||||
pan Pan flute (Pan-pipes)
|
||||
perc Percussion
|
||||
pf Piano
|
||||
pf3h Piano 3 hands
|
||||
pf4h Piano 4 hands
|
||||
pf5h Piano 5 hands
|
||||
pf6h Piano 6 hands
|
||||
pflh Piano left hand
|
||||
pfped Pedal piano
|
||||
pfrh Piano right hand
|
||||
picc Piccolo
|
||||
pipa Pipa
|
||||
pk Timpani
|
||||
ptpt Piccolo trumpet
|
||||
reb Rebec
|
||||
rec Recorder
|
||||
sar Sarrusophone
|
||||
sax Saxophone
|
||||
sheng Sheng
|
||||
shw Shawm
|
||||
sit Sitar
|
||||
skbt Sackbut
|
||||
sop Soprano (voice)
|
||||
srp Serpent
|
||||
stpt Slide trumpet
|
||||
str String instruments
|
||||
sxh Saxhorn
|
||||
syn Synthesizer
|
||||
tba Tuba
|
||||
tbn Trombone
|
||||
ten Tenor
|
||||
thrm Theremin
|
||||
timp Timpani
|
||||
tpt Trumpet
|
||||
uch Unison chorus
|
||||
uke Ukelele (Ukulele)
|
||||
v Voice (solo)
|
||||
va Viola
|
||||
vap Viola pomposa
|
||||
vc Cello
|
||||
vda Viola d'amore
|
||||
vib Vibraphone
|
||||
vie Vielle (Hurdy-Gurdy)
|
||||
viol Viol (Viola da gamba)
|
||||
vlne Violone
|
||||
vn Violin
|
||||
vuv Vuvuzela
|
||||
vv Voices (multiple soloists)
|
||||
wag Wagner tuba
|
||||
ww Woodwind instruments
|
||||
xiao Xiao
|
||||
xyl Xylophone
|
||||
zith Zither
|
||||
"""
|
||||
|
||||
INSTRUMENTS = []
|
||||
for line in ABBREVIATIONS.split('\n'):
|
||||
parts = line.strip().split(maxsplit=1)
|
||||
if len(parts) < 2: continue
|
||||
name, _, _ = parts[1].partition('(')
|
||||
INSTRUMENTS.append((parts[0], name))
|
||||
|
||||
INSTRUMENT_LOOKUP = dict(INSTRUMENTS)
|
||||
TAG_LOOKUP = dict( ( (x[1].lower(), x[0]) for x in INSTRUMENTS ) )
|
||||
|
||||
class Instrument(namedtuple('Instrument', ('name', 'variant'), defaults=[None])):
|
||||
|
||||
@classmethod
|
||||
def from_tag(cls, tag):
|
||||
"""
|
||||
>>> Instrument.from_tag('vn-1')
|
||||
Instrument(name='Violin', variant='1')
|
||||
>>> Instrument.from_tag('db')
|
||||
Instrument(name='Double Bass', variant=None)
|
||||
>>> Instrument.from_tag('Jaws Harp')
|
||||
Instrument(name='Jaws Harp', variant=None)
|
||||
"""
|
||||
abbr, _, variant = tag.partition('-')
|
||||
name = INSTRUMENT_LOOKUP.get(abbr.lower(), abbr)
|
||||
|
||||
if variant:
|
||||
return cls(name, variant)
|
||||
return cls(name, None)
|
||||
|
||||
|
||||
def abbreviate(self):
|
||||
"""
|
||||
>>> Instrument('Violin', 1).abbreviate()
|
||||
'vn-1'
|
||||
>>> Instrument('Double Bass').abbreviate()
|
||||
'db'
|
||||
"""
|
||||
tag = TAG_LOOKUP.get(self.name.lower())
|
||||
if self.variant:
|
||||
tag = f"{tag}-{self.variant}"
|
||||
return tag
|
||||
|
||||
def __str__(self):
|
||||
"""
|
||||
>>> str(Instrument('Violin', 1))
|
||||
'Violin 1'
|
||||
>>> str(Instrument('Double Bass'))
|
||||
'Double Bass'
|
||||
"""
|
||||
if self.variant:
|
||||
return f"{self.name} {self.variant}"
|
||||
return self.name
|
||||
|
||||
if __name__ == "__main__":
|
||||
import doctest
|
||||
print(doctest.testmod())
|
||||
@ -1,4 +1,4 @@
|
||||
# Generated by Django 3.2.7 on 2022-11-18 09:54
|
||||
# Generated by Django 3.2.7 on 2022-11-19 10:24
|
||||
|
||||
import byostorage.user
|
||||
from django.conf import settings
|
||||
@ -9,12 +9,14 @@ import library.models
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
replaces = [('library', '0001_initial'), ('library', '0002_auto_20221118_2208'), ('library', '0003_work_composer'), ('library', '0004_auto_20221118_2223'), ('library', '0005_auto_20221118_2253'), ('library', '0006_auto_20221119_2121')]
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('byostorage', '0004_alter_userstorage_storage'),
|
||||
('interface', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@ -23,21 +25,11 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='Name of the collection', max_length=255)),
|
||||
('prefix', models.SlugField(default='default', max_length=30)),
|
||||
('location', models.CharField(help_text='Physical location (institution, town...)', max_length=100)),
|
||||
('notes', models.TextField(blank=True, help_text='Publicly visible notes about collection and loans policy')),
|
||||
('prefix', models.SlugField(default='default', help_text='Folder to store works in', max_length=30)),
|
||||
('location', models.CharField(blank=True, help_text='Physical location (institution, town...)', max_length=100)),
|
||||
('notes', models.TextField(blank=True, help_text='Publicly visible notes about collection and loans policy (markdown format)')),
|
||||
('administrators', models.ManyToManyField(help_text='Administrators for this collection', related_name='collections', to=settings.AUTH_USER_MODEL)),
|
||||
('storage', models.ForeignKey(blank=True, help_text='Storage for documents', null=True, on_delete=django.db.models.deletion.CASCADE, to='byostorage.userstorage')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Document',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('doctype', models.PositiveSmallIntegerField(choices=[(1, 'PDF'), (2, 'Audio'), (3, 'Video'), (4, 'Source')], default=1)),
|
||||
('upload', models.FileField(storage=byostorage.user.BYOStorage(), upload_to=library.models.doc_upload_filename)),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('version', models.CharField(blank=True, max_length=30)),
|
||||
('storage', models.ForeignKey(blank=True, help_text='User storage for documents', null=True, on_delete=django.db.models.deletion.CASCADE, to='byostorage.userstorage')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
@ -59,45 +51,21 @@ class Migration(migrations.Migration):
|
||||
name='Work',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('slug', models.SlugField(editable=False, max_length=100)),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('edition', models.CharField(blank=True, help_text='Edition details', max_length=255)),
|
||||
('composer', models.CharField(blank=True, max_length=255)),
|
||||
('orchestration', models.CharField(blank=True, help_text='IMDB format instrumentation', max_length=255)),
|
||||
('original_parts', models.JSONField(blank=True, help_text='Original printed parts', null=True)),
|
||||
('slug', models.SlugField(editable=False, help_text='Used as folder name', max_length=100)),
|
||||
('name', models.CharField(help_text='Original name of the work', max_length=255)),
|
||||
('original_parts', models.JSONField(blank=True, default=dict, help_text='Original printed parts (IMSLP format)')),
|
||||
('code', models.CharField(blank=True, help_text='Collection specific code or number', max_length=100)),
|
||||
('licence', models.PositiveSmallIntegerField(choices=[(2, 'Public Domain'), (4, 'Copyright Expired'), (6, 'Copyrighted'), (10, 'Internal use only')], default=6, help_text='Copyright status')),
|
||||
('max_projects', models.IntegerField(default=1, help_text='How many projects can this work be attached to')),
|
||||
('max_projects', models.IntegerField(default=1, help_text='How many active projects can this work be attached to')),
|
||||
('running_time', models.DurationField(blank=True, help_text='Running time in seconds', null=True)),
|
||||
('notes', models.TextField(blank=True)),
|
||||
('collection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='works', to='library.collection')),
|
||||
('parent', models.ForeignKey(blank=True, help_text='Arrangement of another work or part of an anthology', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='related_works', to='library.work')),
|
||||
('projects', models.ManyToManyField(help_text='Current usage', related_name='works', through='library.ProjectItem', to='interface.Project')),
|
||||
('composer', models.CharField(blank=True, help_text='Surname, First Name/Initials', max_length=255)),
|
||||
('edition', models.CharField(blank=True, help_text='Edition details to distinguish multiple versions', max_length=255)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='WorkMeta',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.SlugField(choices=[('tag', 'Tag'), ('arr', 'Arranger'), ('lyrics', 'Lyracist'), ('genre', 'Genre'), ('style', 'Style')], max_length=20)),
|
||||
('value', models.CharField(max_length=255)),
|
||||
('work', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_info', to='library.work')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Section',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('tag', models.SlugField(max_length=20)),
|
||||
('start', models.SmallIntegerField(blank=True, null=True)),
|
||||
('end', models.SmallIntegerField(blank=True, null=True)),
|
||||
('notes', models.TextField(blank=True)),
|
||||
('doc', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='library.document')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['doc', 'start', 'pk'],
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='projectitem',
|
||||
name='work',
|
||||
@ -115,9 +83,37 @@ class Migration(migrations.Migration):
|
||||
'verbose_name_plural': 'Ensemble access',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='document',
|
||||
name='work',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='docs', to='library.work'),
|
||||
migrations.CreateModel(
|
||||
name='Document',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('doctype', models.PositiveSmallIntegerField(choices=[(1, 'PDF'), (2, 'Audio'), (3, 'Video'), (4, 'Source')], default=1)),
|
||||
('upload', models.FileField(storage=byostorage.user.BYOStorage(), upload_to=library.models.doc_upload_filename)),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('version', models.CharField(blank=True, max_length=30)),
|
||||
('work', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='docs', to='library.work')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='WorkMeta',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.SlugField(choices=[('tag', 'Tag'), ('arr', 'Arranger'), ('lyrics', 'Lyracist'), ('genre', 'Genre'), ('style', 'Style'), ('orchestration', 'Orchestration')], max_length=20)),
|
||||
('value', models.CharField(max_length=255)),
|
||||
('work', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_info', to='library.work')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Section',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('tag', models.CharField(max_length=50)),
|
||||
('start', models.SmallIntegerField(blank=True, null=True)),
|
||||
('end', models.SmallIntegerField(blank=True, null=True)),
|
||||
('doc', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sections', to='library.document')),
|
||||
],
|
||||
options={
|
||||
'ordering': ['doc', 'start', 'pk'],
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -90,7 +90,7 @@ class ProjectItem(models.Model):
|
||||
returned = models.DateTimeField(null=True, blank=True)
|
||||
approved_by = models.ForeignKey('auth.User', on_delete=models.CASCADE)
|
||||
order = models.SmallIntegerField(default=0)
|
||||
section = models.SlugField
|
||||
section = models.CharField(max_length=100, blank=True)
|
||||
#version = models.CharField(max_length=30, blank=True, help_text="Limited to specific version tag")
|
||||
|
||||
class Meta:
|
||||
|
||||
26
library/templates/library/collection_list.html
Normal file
26
library/templates/library/collection_list.html
Normal file
@ -0,0 +1,26 @@
|
||||
{% extends "interface/project_base.html" %}
|
||||
|
||||
{% block page %}
|
||||
<h3 class="title">Library collections for {{ request.user }}</h3>
|
||||
|
||||
<div class="columns is-multiline">
|
||||
{% for collection in object_list %}
|
||||
<div class="column is-half">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<a class="" href="{% url 'collection_work_list' pk=collection.id %}">
|
||||
<p class="card-header-title">{{ collection.name }}</p>
|
||||
</a>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<p>{{ collection.location }}, {{ collection.works.count }} items.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<small>{{ ensemble.ensemble_code }}</small>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@ -31,8 +31,7 @@ ALLOWED_HOSTS = ['localhost']
|
||||
# Application definition
|
||||
|
||||
POLYPHONIC_MODULES = [
|
||||
'library',
|
||||
'submissions'
|
||||
'library'
|
||||
]
|
||||
|
||||
INSTALLED_APPS = [
|
||||
|
||||
@ -19,7 +19,7 @@ from django.urls import path, re_path, include
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include('interface.urls')),
|
||||
path('', include('submissions.urls')),
|
||||
#path('', include('submissions.urls')),
|
||||
path('', include('library.urls')),
|
||||
]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user