diff --git a/interface/admin.py b/interface/admin.py index 4f28ede..c65d0a9 100644 --- a/interface/admin.py +++ b/interface/admin.py @@ -4,6 +4,8 @@ from django.contrib import admin from . import models +class EnsembleAdmin(admin.ModelAdmin): + list_display = ['name', 'ensemble_code'] class ProjectAdmin(admin.ModelAdmin): @@ -18,7 +20,7 @@ class WikiPageAdmin(admin.ModelAdmin): list_display = ['title', 'project'] list_filter = ['project'] -admin.site.register(models.Ensemble) +admin.site.register(models.Ensemble, EnsembleAdmin) admin.site.register(models.Project, ProjectAdmin) admin.site.register(models.Submission, SubmissionAdmin) admin.site.register(models.Resource) diff --git a/interface/decorators.py b/interface/decorators.py index 32f7374..764f608 100644 --- a/interface/decorators.py +++ b/interface/decorators.py @@ -4,6 +4,16 @@ def check_allowed(view_func): def _view(request, *args, **kwargs): + code = request.GET.get('code') + if code: + # just change if we can + try: + ensemble = request.session.get('registered', {})[code.replace('-', '')] + request.session['ensemble'] = ensemble + except KeyError: + # need to register this code + return HttpResponseRedirect('/register?code=' + code) + request.ensemble_id = request.session.get('ensemble') if request.ensemble_id is None: diff --git a/interface/forms.py b/interface/forms.py index a403abc..4f6b578 100644 --- a/interface/forms.py +++ b/interface/forms.py @@ -1,8 +1,12 @@ -from django.forms import ModelForm - +from django import forms from .models import Submission -class SubmissionForm(ModelForm): +class CodeForm(forms.Form): + code = forms.CharField(max_length=14, + widget=forms.TextInput(attrs={'placeholder': 'xxx-xxx-xxx'})) + passphrase = forms.CharField(max_length=32) + +class SubmissionForm(forms.ModelForm): class Meta: model = Submission fields = ['name', 'instrument', 'notes'] \ No newline at end of file diff --git a/interface/migrations/0007_auto_20200906_1009.py b/interface/migrations/0007_auto_20200906_1009.py new file mode 100644 index 0000000..87f9acd --- /dev/null +++ b/interface/migrations/0007_auto_20200906_1009.py @@ -0,0 +1,35 @@ +# Generated by Django 3.1.1 on 2020-09-06 10:09 + +from django.db import migrations, models +import django.db.models.deletion +import interface.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('interface', '0006_submission_key'), + ] + + operations = [ + migrations.RemoveField( + model_name='project', + name='bucket', + ), + migrations.AddField( + model_name='ensemble', + name='bucket', + field=models.CharField(default='', max_length=100), + preserve_default=False, + ), + migrations.AlterField( + model_name='ensemble', + name='code', + field=models.CharField(default=interface.models.generate_code, max_length=12), + ), + migrations.AlterField( + model_name='submission', + name='project', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='all_submissions', to='interface.project'), + ), + ] diff --git a/interface/migrations/0008_auto_20200906_1122.py b/interface/migrations/0008_auto_20200906_1122.py new file mode 100644 index 0000000..e9f6a97 --- /dev/null +++ b/interface/migrations/0008_auto_20200906_1122.py @@ -0,0 +1,24 @@ +# Generated by Django 3.1.1 on 2020-09-06 11:22 + +from django.db import migrations, models +import interface.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('interface', '0007_auto_20200906_1009'), + ] + + operations = [ + migrations.RenameField( + model_name='ensemble', + old_name='password', + new_name='passphrase', + ), + migrations.AlterField( + model_name='ensemble', + name='code', + field=models.CharField(default=interface.models.generate_code, max_length=9), + ), + ] diff --git a/interface/models.py b/interface/models.py index 2ca8b33..d948497 100644 --- a/interface/models.py +++ b/interface/models.py @@ -1,6 +1,8 @@ from django.db import models from django.utils.text import slugify +import random + import boto3 from datetime import datetime @@ -9,14 +11,22 @@ import os.path s3client = boto3.client('s3') +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=12) - password = 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) 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 __str__(self): return self.name @@ -25,14 +35,13 @@ class Project(models.Model): ensemble = models.ForeignKey(Ensemble, related_name='projects', on_delete=models.CASCADE, null=True) active = models.BooleanField(default=True) deadline =models.DateField(null=True, blank=True) - bucket = models.CharField(max_length=100) def submissions(self): return self.all_submissions.filter(complete=True) def presigned_post(self, object_name, fields={}, conditions=[], expires=3600): key = os.path.join(slugify(self.name), object_name) - return s3client.generate_presigned_post(self.bucket, key, Fields=fields, Conditions=conditions, ExpiresIn=expires) + return s3client.generate_presigned_post(self.ensemble.bucket, key, Fields=fields, Conditions=conditions, ExpiresIn=expires) def __str__(self): return self.name diff --git a/interface/static/interface/css/polyphonic.css b/interface/static/interface/css/polyphonic.css index 2d6910b..38e6c5c 100644 --- a/interface/static/interface/css/polyphonic.css +++ b/interface/static/interface/css/polyphonic.css @@ -1,12 +1,183 @@ -.navbar { - margin-bottom: 50px; - background-color: #69C; + +:root { + --border-color: #292929; + --gray-blue: #667788; + --light-blue: #c5eff7; } -.form-actions { +@font-face { + font-family: 'DreamOrphans'; + src: url('../../fonts/dream orphans.ttf') format('truetype'); +} + +@font-face { + font-family: 'Quicksand'; + src: url('../../fonts/Quicksand_Book.otf'); +} + +@font-face { + font-family: 'QuicksandBold'; + src: url('../../fonts/Quicksand_Bold_Oblique.otf'); +} + +.debug DIV { + border: 1px dashed #DDD; +} + +BODY { +} + +.main { + max-width: 1000px; + margin: 10px auto; + border: 1px solid var(--border-color); + border-radius: 5px; + font-family: 'Quicksand', Arial, Helvetica, sans-serif; +} + +.content { + margin: 20px; +} + +.narrow { + max-width: 500px; + margin: 0px auto; +} + +@media all and (max-width: 900px) { + .mdhide { + display: none; + } +} + +@media all and (max-width: 700px) { + .smhide { + display: none; + } + UL.nav-buttons { + flex-direction: column; + } +} + +/* HEADER BAR */ + +.navigation { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 50px; + background-color: var(--gray-blue); + color: var(--light-blue) !important; +} + +.navigation > * { + white-space: nowrap; +} + +.navigation A, +.navigation A:visited { + color: var(--light-blue); + text-decoration: none; +} + +.navigation .brand { + font-family: 'QuicksandBold', 'Quicksand', Arial, Helvetica, sans-serif; + font-size: 1.5rem; + margin: auto 20px; +} + +UL.nav-buttons { + display: flex; + list-style: none; +} +UL.nav-buttons > LI { + margin: 2px 10px; +} + +/* FORMS */ + +FORM { + display: flex; + flex-direction: column; + max-width: 400px; + margin: 20px auto; +} + +LABEL { margin-top: 20px; } -#project H1 { +TEXTAREA { + height: 50px; +} + +.form-actions { + text-align: right; + margin-top: 20px; +} + +.btn { + background-color: var(--gray-blue); + display: inline-block; + border: none; + color: var(--light-blue); + text-decoration: none; + border-radius: 1em; + font-size: 1em; +} + +.btn:hover { + cursor: pointer; + color: white; +} + +.pills { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin-top: 40px; +} + +.pills A { + border: 1px solid var(--gray-blue); + padding: 4px 10px 2px 10px; + margin: 10px 5px; + border-radius: 10px; + white-space: nowrap; +} + +.pills A:hover { + background-color: var(--light-blue); + text-decoration: none +} + +.list-group { + display: flex; + flex-direction: column; +} + +.list-group A { + border: 1px solid var(--gray-blue); + border-radius: 10px; + padding: 2px 20px; + margin-top: 20px; +} + +.list-group A:hover { + background-color: var(--light-blue); + text-decoration: none; +} + +A, A:visited { + text-decoration: none; + color: var(--gray-blue); + font-weight: bold; +} + +A:hover { + text-decoration: underline; +} + +H1 { text-align: center; } \ No newline at end of file diff --git a/interface/templates/base.html b/interface/templates/base.html index 32530c2..9f9bc0a 100644 --- a/interface/templates/base.html +++ b/interface/templates/base.html @@ -6,49 +6,45 @@ - - - -