From 23b7f492b0c44cbe1676ec6ae3938567c9af5209 Mon Sep 17 00:00:00 2001 From: Tris Forster Date: Mon, 7 Sep 2020 14:25:06 +1000 Subject: [PATCH] Submissions working nicely --- .../migrations/0009_auto_20200907_0103.py | 27 +++++++ .../migrations/0010_auto_20200907_0148.py | 18 +++++ .../migrations/0011_auto_20200907_0234.py | 18 +++++ interface/models.py | 17 +++-- interface/static/interface/css/polyphonic.css | 16 +++- interface/templates/base.html | 9 ++- interface/templates/interface/project.html | 24 +++++- .../templates/interface/project_list.html | 3 +- interface/templates/interface/register.html | 8 +- interface/templates/interface/submission.html | 23 ------ .../interface/submission_create.html | 20 +++++ .../interface/submission_detail.html | 17 +++++ .../{upload.html => submission_upload.html} | 18 +++-- interface/urls.py | 3 +- interface/views.py | 74 ++++++++++++------- polyphonic/settings.py | 2 +- 16 files changed, 222 insertions(+), 75 deletions(-) create mode 100644 interface/migrations/0009_auto_20200907_0103.py create mode 100644 interface/migrations/0010_auto_20200907_0148.py create mode 100644 interface/migrations/0011_auto_20200907_0234.py delete mode 100644 interface/templates/interface/submission.html create mode 100644 interface/templates/interface/submission_create.html create mode 100644 interface/templates/interface/submission_detail.html rename interface/templates/interface/{upload.html => submission_upload.html} (87%) diff --git a/interface/migrations/0009_auto_20200907_0103.py b/interface/migrations/0009_auto_20200907_0103.py new file mode 100644 index 0000000..027531f --- /dev/null +++ b/interface/migrations/0009_auto_20200907_0103.py @@ -0,0 +1,27 @@ +# Generated by Django 3.1.1 on 2020-09-07 01:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('interface', '0008_auto_20200906_1122'), + ] + + operations = [ + migrations.RemoveField( + model_name='submission', + name='key', + ), + migrations.AddField( + model_name='submission', + name='location', + field=models.CharField(blank=True, max_length=512), + ), + migrations.AlterField( + model_name='ensemble', + name='bucket', + field=models.CharField(max_length=255), + ), + ] diff --git a/interface/migrations/0010_auto_20200907_0148.py b/interface/migrations/0010_auto_20200907_0148.py new file mode 100644 index 0000000..fb00411 --- /dev/null +++ b/interface/migrations/0010_auto_20200907_0148.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.1 on 2020-09-07 01:48 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('interface', '0009_auto_20200907_0103'), + ] + + operations = [ + migrations.RenameField( + model_name='submission', + old_name='location', + new_name='key', + ), + ] diff --git a/interface/migrations/0011_auto_20200907_0234.py b/interface/migrations/0011_auto_20200907_0234.py new file mode 100644 index 0000000..1738cc9 --- /dev/null +++ b/interface/migrations/0011_auto_20200907_0234.py @@ -0,0 +1,18 @@ +# Generated by Django 3.1.1 on 2020-09-07 02:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('interface', '0010_auto_20200907_0148'), + ] + + operations = [ + migrations.AlterField( + model_name='submission', + name='date', + field=models.DateTimeField(auto_now_add=True), + ), + ] diff --git a/interface/models.py b/interface/models.py index 1662da3..92832ef 100644 --- a/interface/models.py +++ b/interface/models.py @@ -1,5 +1,6 @@ from django.db import models from django.utils.text import slugify +from django.utils import timezone import random @@ -18,7 +19,7 @@ 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) - bucket = models.CharField(max_length=100) + bucket = models.CharField(max_length=255) def active_projects(self): return self.projects.filter(active=True) @@ -37,7 +38,7 @@ class Project(models.Model): deadline =models.DateField(null=True, blank=True) def submissions(self): - return self.all_submissions.filter(complete=True) + 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(slugify(self.name), object_name) @@ -61,16 +62,20 @@ class WikiPage(models.Model): class Submission(models.Model): project = models.ForeignKey(Project, related_name='all_submissions', on_delete=models.CASCADE) - date = models.DateField(auto_now_add=True) + date = models.DateTimeField(auto_now_add=True, ) name = models.CharField(max_length=255) instrument = models.CharField(max_length=100) notes = models.TextField(blank=True) complete = models.BooleanField(default=False) - key = models.CharField(max_length=255, blank=True) + key = models.CharField(max_length=512, blank=True) - def generate_key(self): + def presigned_url(self): + params = {'Bucket': self.project.ensemble.bucket, 'Key': self.key} + return s3client.generate_presigned_url('get_object', Params=params, ExpiresIn=3600) + + def key_template(self): return "{}_{}_{}_${{filename}}".format( - datetime.now().isoformat(timespec='seconds').replace(':', ''), + timezone.localtime(self.date).isoformat(timespec='seconds').replace(':', ''), slugify(self.name), slugify(self.instrument) ) diff --git a/interface/static/interface/css/polyphonic.css b/interface/static/interface/css/polyphonic.css index 948942d..3c7fc93 100644 --- a/interface/static/interface/css/polyphonic.css +++ b/interface/static/interface/css/polyphonic.css @@ -37,6 +37,7 @@ BODY { .content { margin: 20px; + flex-direction: column; } .narrow { @@ -178,7 +179,7 @@ TEXTAREA { } .progress-bar { - width: 5%; + width: 0%; height: 1.5em; background-color: var(--light-blue); border-radius: 5px; @@ -197,4 +198,17 @@ A:hover { H1 { text-align: center; +} + +TABLE { + width: 100%; +} + +TABLE.horizontal TH { + text-align: right; +} + +TABLE.horizontal TD, +TABLE.horizontal TH { + padding: 5px; } \ No newline at end of file diff --git a/interface/templates/base.html b/interface/templates/base.html index de005ee..62d5f13 100644 --- a/interface/templates/base.html +++ b/interface/templates/base.html @@ -25,13 +25,18 @@ Projects {% endif %} + {% if request.user.is_authenticated %} + + {% endif %} {% endblock %} diff --git a/interface/templates/interface/project.html b/interface/templates/interface/project.html index ce2ef80..4f493d5 100644 --- a/interface/templates/interface/project.html +++ b/interface/templates/interface/project.html @@ -1,15 +1,31 @@ {% extends "base.html" %} {% block content %} +

{{ project.name }}

-
{% block page %}

Due in {{ project.deadline|timeuntil }}!

There have been {{ project.submissions.count }} submissions so far...

+ + + {% for submission in project.submissions %} + + + + {% if request.user.is_authenticated %} + + {% endif %} + + {% endfor %} + +
{{ submission.date|timesince }} ago{{ submission.name }} ({{ submission.instrument }}) + +   + +
{% endblock %} -
{% endblock %} \ No newline at end of file diff --git a/interface/templates/interface/project_list.html b/interface/templates/interface/project_list.html index 7df96f0..e04e68f 100644 --- a/interface/templates/interface/project_list.html +++ b/interface/templates/interface/project_list.html @@ -1,7 +1,7 @@ {% extends "base.html" %} {% block content %} - +

Projects for {{ ensemble.name }}

{% for project in ensemble.active_projects %} @@ -11,4 +11,5 @@ {% endfor %}
+
{% endblock %} \ No newline at end of file diff --git a/interface/templates/interface/register.html b/interface/templates/interface/register.html index c05a00a..2e1426e 100644 --- a/interface/templates/interface/register.html +++ b/interface/templates/interface/register.html @@ -1,7 +1,8 @@ {% extends "base.html" %} {% block content %} -
+
+ {% if current %}

My Ensembles

-
-
+

Add a new ensemble

{% csrf_token %} diff --git a/interface/templates/interface/submission.html b/interface/templates/interface/submission.html deleted file mode 100644 index db7de8d..0000000 --- a/interface/templates/interface/submission.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "interface/project.html" %} - -{% block page %} -
-
Make a submission
-
-

- Some instructions about how to submit the file - {{ url }} -

- -
- - {% csrf_token %} - {{ form }} -
- -
- -
-
-
-{% endblock %} \ No newline at end of file diff --git a/interface/templates/interface/submission_create.html b/interface/templates/interface/submission_create.html new file mode 100644 index 0000000..29ba0ab --- /dev/null +++ b/interface/templates/interface/submission_create.html @@ -0,0 +1,20 @@ +{% extends "interface/project.html" %} + +{% block page %} +
+

Excellent, you are ready to make a submission!

+

+ Please enter some basic information so we can identify your submission and + note anything that might be relevant. +

+
+
+
+ {% csrf_token %} + {{ form }} +
+ +
+
+
+{% endblock %} \ No newline at end of file diff --git a/interface/templates/interface/submission_detail.html b/interface/templates/interface/submission_detail.html new file mode 100644 index 0000000..06a98a7 --- /dev/null +++ b/interface/templates/interface/submission_detail.html @@ -0,0 +1,17 @@ +{% extends "interface/project.html" %} + +{% block page %} +
+

Thankyou for your submission!

+ + + + + + {% if download %} + + {% endif %} + +
From:{{ submission.name }}
Instrument:{{ submission.instrument }}
Notes:{{ submission.notes }}
Download:{{ submission.key }}
+
+{% endblock %} \ No newline at end of file diff --git a/interface/templates/interface/upload.html b/interface/templates/interface/submission_upload.html similarity index 87% rename from interface/templates/interface/upload.html rename to interface/templates/interface/submission_upload.html index f69d56c..cdaf7d3 100644 --- a/interface/templates/interface/upload.html +++ b/interface/templates/interface/submission_upload.html @@ -35,7 +35,7 @@ diff --git a/interface/urls.py b/interface/urls.py index bbb7ea1..758b2cf 100644 --- a/interface/urls.py +++ b/interface/urls.py @@ -7,7 +7,8 @@ urlpatterns = [ path('register', views.register, name="register"), path('projects/', views.project_page, name="project"), path('projects//page/', views.wiki_page, name="wiki"), - path('projects//submission', views.submission, name="submission"), + path('projects//submission', views.create_submission, name="create_submission"), + path('projects//submission/', views.submission, name="submission"), path('projects//submission//complete', views.complete_submission, name="complete_submission"), path('projects//submission//cancel', views.cancel_submission, name="cancel_submission"), ] \ No newline at end of file diff --git a/interface/views.py b/interface/views.py index 5eedc95..f49d870 100644 --- a/interface/views.py +++ b/interface/views.py @@ -2,6 +2,7 @@ from django.shortcuts import render, get_object_or_404, redirect, resolve_url from markdown2 import markdown from datetime import datetime +from urllib.parse import urlparse from . import models, forms from .decorators import check_allowed @@ -25,14 +26,17 @@ def register(request): if form.is_valid(): - data = form.cleaned_data; - ensemble = models.Ensemble.objects.get(code=data['code'].replace('-', '')) - + data = form.cleaned_data + try: + ensemble = models.Ensemble.objects.get(code=data['code'].replace('-', '')) + if ensemble.passphrase == data['passphrase']: + request.session['ensemble'] = ensemble.pk + registered[ensemble.code] = ensemble.pk + return redirect('my_projects') + except models.Ensemble.DoesNotExist: + form.add_error(None, "Incorrect code or passphrase") + - if ensemble.passphrase == data['passphrase']: - request.session['ensemble'] = ensemble.pk - registered[ensemble.code] = ensemble.pk - return redirect('my_projects') else: form = forms.CodeForm(initial=request.GET) @@ -61,7 +65,7 @@ def wiki_page(request, project_id, wiki_id): return render(request, 'interface/wiki.html', context) @check_allowed -def submission(request, project_id): +def create_submission(request, project_id): project = get_object_or_404(models.Project, pk=project_id, ensemble=request.ensemble_id) if request.method == 'POST': @@ -72,28 +76,43 @@ def submission(request, project_id): s.project_id = project_id s.save() - data = form.cleaned_data - request.session['name'] = data['name'] - request.session['instrument'] = data['instrument'] + # cache details for next time + request.session['name'] = s.name + request.session['instrument'] = s.instrument - redirect = request.build_absolute_uri(resolve_url('complete_submission', project_id=project.pk, submission_id=s.pk)) - - key = s.generate_key() - #print("KEY:", key) - upload = project.presigned_post(key, - fields={'success_action_redirect': redirect}, - conditions=[["starts-with", "$success_action_redirect", ""]]) - ajax_post = project.presigned_post(key) - #print(b64decode(ajax_post['fields']['policy'])) - context = {'upload': upload, 'ajax_post': ajax_post, 'project': project, 'submission': s} - - return render(request, 'interface/upload.html', context) + return redirect('submission', project_id=project_id, submission_id=s.pk) else: initial = { k: request.session.get(k) for k in ('name', 'instrument') } form = forms.SubmissionForm(initial=initial) context = {'project': project, 'form': form} - return render(request, 'interface/submission.html', context) + return render(request, 'interface/submission_create.html', context) + +@check_allowed +def submission(request, project_id, submission_id): + project = get_object_or_404(models.Project, pk=project_id, ensemble=request.ensemble_id) + submission = project.all_submissions.get(pk=submission_id) + + if submission.complete: + context = {'project': project, 'submission': submission} + if request.user.is_authenticated: + context['download'] = submission.presigned_url() + return render(request, 'interface/submission_detail.html', context) + + + # Need to do an upload + redirect = request.build_absolute_uri(resolve_url('complete_submission', project_id=project.pk, submission_id=submission.pk)) + + key = submission.key_template() + upload = project.presigned_post(key, + fields={'success_action_redirect': redirect}, + conditions=[["starts-with", "$success_action_redirect", ""]]) + + # need an additional presigned without the redirect for ajax submission + ajax_post = project.presigned_post(key) + context = {'upload': upload, 'ajax_post': ajax_post, 'project': project, 'submission': submission} + + return render(request, 'interface/submission_upload.html', context) @check_allowed def cancel_submission(request, project_id, submission_id): @@ -107,6 +126,9 @@ def complete_submission(request, project_id, submission_id): project = get_object_or_404(models.Project, pk=project_id, ensemble=request.ensemble_id) s = project.all_submissions.get(pk=submission_id) s.complete = True - s.key = request.GET['key'] + + uri = urlparse(request.GET['location']) + s.key = uri.path[1:] + s.save() - return redirect('project', project_id=project_id) \ No newline at end of file + return redirect('submission', project_id=project_id, submission_id=submission_id) \ No newline at end of file diff --git a/polyphonic/settings.py b/polyphonic/settings.py index c262604..74ff7f2 100644 --- a/polyphonic/settings.py +++ b/polyphonic/settings.py @@ -106,7 +106,7 @@ AUTH_PASSWORD_VALIDATORS = [ LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +TIME_ZONE = 'Australia/Melbourne' USE_I18N = True