Library schema semi-fixed
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from crispy_forms.layout import Field
|
||||
|
||||
class BulmaFileUpload(Field):
|
||||
template = 'bulma/file_upload.html'
|
||||
+7
-5
@@ -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')),
|
||||
],
|
||||
),
|
||||
+1
-50
@@ -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
|
||||
@@ -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>
|
||||
@@ -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 %}
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user