Ditched bootstrap
This commit is contained in:
parent
a1d10ea30a
commit
8a8bccd850
@ -4,6 +4,8 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from . import models
|
from . import models
|
||||||
|
|
||||||
|
class EnsembleAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['name', 'ensemble_code']
|
||||||
|
|
||||||
class ProjectAdmin(admin.ModelAdmin):
|
class ProjectAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@ -18,7 +20,7 @@ class WikiPageAdmin(admin.ModelAdmin):
|
|||||||
list_display = ['title', 'project']
|
list_display = ['title', 'project']
|
||||||
list_filter = ['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.Project, ProjectAdmin)
|
||||||
admin.site.register(models.Submission, SubmissionAdmin)
|
admin.site.register(models.Submission, SubmissionAdmin)
|
||||||
admin.site.register(models.Resource)
|
admin.site.register(models.Resource)
|
||||||
|
|||||||
@ -4,6 +4,16 @@ def check_allowed(view_func):
|
|||||||
|
|
||||||
def _view(request, *args, **kwargs):
|
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')
|
request.ensemble_id = request.session.get('ensemble')
|
||||||
|
|
||||||
if request.ensemble_id is None:
|
if request.ensemble_id is None:
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
from django.forms import ModelForm
|
from django import forms
|
||||||
|
|
||||||
from .models import Submission
|
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:
|
class Meta:
|
||||||
model = Submission
|
model = Submission
|
||||||
fields = ['name', 'instrument', 'notes']
|
fields = ['name', 'instrument', 'notes']
|
||||||
35
interface/migrations/0007_auto_20200906_1009.py
Normal file
35
interface/migrations/0007_auto_20200906_1009.py
Normal file
@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
24
interface/migrations/0008_auto_20200906_1122.py
Normal file
24
interface/migrations/0008_auto_20200906_1122.py
Normal file
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,6 +1,8 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -9,14 +11,22 @@ import os.path
|
|||||||
|
|
||||||
s3client = boto3.client('s3')
|
s3client = boto3.client('s3')
|
||||||
|
|
||||||
|
def generate_code(length=9):
|
||||||
|
return "".join([ random.choice('0123456789') for _ in range(length) ])
|
||||||
|
|
||||||
class Ensemble(models.Model):
|
class Ensemble(models.Model):
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
code = models.CharField(max_length=12)
|
code = models.CharField(max_length=9, default=generate_code)
|
||||||
password = models.CharField(max_length=100)
|
passphrase = models.CharField(max_length=100)
|
||||||
|
bucket = models.CharField(max_length=100)
|
||||||
|
|
||||||
def active_projects(self):
|
def active_projects(self):
|
||||||
return self.projects.filter(active=True)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@ -25,14 +35,13 @@ class Project(models.Model):
|
|||||||
ensemble = models.ForeignKey(Ensemble, related_name='projects', on_delete=models.CASCADE, null=True)
|
ensemble = models.ForeignKey(Ensemble, related_name='projects', on_delete=models.CASCADE, null=True)
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
deadline =models.DateField(null=True, blank=True)
|
deadline =models.DateField(null=True, blank=True)
|
||||||
bucket = models.CharField(max_length=100)
|
|
||||||
|
|
||||||
def submissions(self):
|
def submissions(self):
|
||||||
return self.all_submissions.filter(complete=True)
|
return self.all_submissions.filter(complete=True)
|
||||||
|
|
||||||
def presigned_post(self, object_name, fields={}, conditions=[], expires=3600):
|
def presigned_post(self, object_name, fields={}, conditions=[], expires=3600):
|
||||||
key = os.path.join(slugify(self.name), object_name)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@ -1,12 +1,183 @@
|
|||||||
.navbar {
|
|
||||||
margin-bottom: 50px;
|
:root {
|
||||||
background-color: #69C;
|
--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;
|
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;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -6,49 +6,45 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||||
|
|
||||||
<!-- Bootstrap CSS -->
|
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="{% static 'interface/css/polyphonic.css' %}"></link>
|
<link rel="stylesheet" href="{% static 'interface/css/polyphonic.css' %}"></link>
|
||||||
<script src="https://kit.fontawesome.com/c837098e5b.js" crossorigin="anonymous"></script>
|
|
||||||
<title>{% block title %}Polyphonic{% endblock %}</title>
|
<title>{% block title %}Polyphonic{% endblock %}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
{% block header %}
|
<div class="main">
|
||||||
<nav class="navbar navbar-expand-lg navbar-dark">
|
{% block navigation %}
|
||||||
<a class="navbar-brand" href="/"><i class="fas fa-random"></i> Polyphonic</a>
|
<nav class="navigation">
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<div>
|
||||||
<span class="navbar-toggler-icon"></span>
|
<a class="brand" href="/"><i class="fas fa-random smhide"></i> Polyphonic</a>
|
||||||
</button>
|
<span class="mdhide">Virtual Ensemble Manager</span>
|
||||||
<span class="navbar-text">Virtual Ensemble Manager</span>
|
</div>
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent"></div>
|
<ul class="nav-buttons">
|
||||||
<ul class="navbar-nav ml-auto">
|
|
||||||
{% if request.ensemble_id %}
|
{% if request.ensemble_id %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'my_projects' %}"><i class="fas fa-music"></i> My Projects</a>
|
<a class="nav-link" href="{% url 'my_projects' %}"><i class="fas fa-music"></i> <span class="">My
|
||||||
|
Projects</span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'register' %}"><i class="fas fa-users"></i> Change Ensemble</a>
|
<a class="nav-link" href="{% url 'register' %}"><i class="fas fa-users"></i> <span class="">Change
|
||||||
|
Ensemble</span></a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href=""><i class="fas fa-question"></i> About</a>
|
<a class="nav-link" href=""><i class="fas fa-question"></i> <span class="">About</span></a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
<div class="content">
|
||||||
|
{% block content %}
|
||||||
{% block content %}
|
<h1>No content!</h1>
|
||||||
<h1>No content!</h1>
|
{% endblock %}
|
||||||
{% endblock %}
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Optional JavaScript -->
|
<!-- late load scripts -->
|
||||||
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
<script src="https://kit.fontawesome.com/c837098e5b.js" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@ -1,29 +0,0 @@
|
|||||||
<form class="form-horizontal" method="post">{% csrf_token %}
|
|
||||||
<fieldset>
|
|
||||||
<legend>{{ title }}</legend>
|
|
||||||
{% for field in form %}
|
|
||||||
{% if field.errors %}
|
|
||||||
<div class="control-group error">
|
|
||||||
<label class="control-label">{{ field.label }}</label>
|
|
||||||
<div class="controls">{{ field }}
|
|
||||||
<span class="help-inline">
|
|
||||||
{% for error in field.errors %}{{ error }}{% endfor %}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% else %}
|
|
||||||
<div class="control-group">
|
|
||||||
<label class="control-label">{{ field.label }}</label>
|
|
||||||
<div class="controls">{{ field }}
|
|
||||||
{% if field.help_text %}
|
|
||||||
<p class="help-inline"><small>{{ field.help_text }}</small></p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</fieldset>
|
|
||||||
<div class="form-actions text-right">
|
|
||||||
<button type="submit" class="btn btn-primary" >Submit</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
@ -1,31 +1,24 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container" id="project">
|
<h1>{{ project.name }}</h1>
|
||||||
|
<div>
|
||||||
<h1>{{ project.name }}</h1>
|
{% block page %}
|
||||||
<div class="row">
|
<div class="narrow">
|
||||||
<div class="col-9">
|
<h3 class="text-center">Due in {{ project.deadline|timeuntil }}!</h3>
|
||||||
{% block page %}
|
<p>There have been {{ project.submissions.count }} submissions so far...</p>
|
||||||
<h3 class="text-center">Due in {{ project.deadline|timeuntil }}</h3>
|
</div>
|
||||||
<p>There have been {{ project.submissions.count }} submissions so far...</p>
|
{% endblock %}
|
||||||
{% endblock %}
|
</div>
|
||||||
</div>
|
<div class="project-links">
|
||||||
<div class="col-3">
|
<div class="pills" role="tablist">
|
||||||
<div class="nav flex-column nav-pills" role="tablist">
|
<a role=tab" href="{% url 'project' project_id=project.id %}">Project info</a>
|
||||||
<a class="nav-link" role=tab"
|
{% for page in project.wiki_pages.all %}
|
||||||
href="{% url 'project' project_id=project.id %}">Project info</a>
|
<a class="nav-link {% if page.id == wiki_id %}active{% endif %}"
|
||||||
{% for page in project.wiki_pages.all %}
|
href="{% url 'wiki' project_id=project.id wiki_id=page.id %}">{{ page.title }}</a>
|
||||||
<a class="nav-link {% if page.id == wiki_id %}active{% endif %}"
|
{% endfor %}
|
||||||
href="{% url 'wiki' project_id=project.id wiki_id=page.id %}"
|
<a role="tab" href="">Record a submission</a>
|
||||||
role="tab">{{ page.title }}</a>
|
<a role="tab" href="{% url 'submission' project_id=project.id %}">Send a file</a>
|
||||||
{% endfor %}
|
|
||||||
<a class="nav-link" role="tab"
|
|
||||||
href="">Record a submission</a>
|
|
||||||
<a class="nav-link" role="tab"
|
|
||||||
href="{% url 'submission' project_id=project.id %}">Send a file</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -2,17 +2,13 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<h1>Projects for {{ ensemble.name }}</h1>
|
<h1>Projects for {{ ensemble.name }}</h1>
|
||||||
<div class="col-md-8 offset-md-2">
|
<div class="list-group narrow">
|
||||||
<div class="list-group">
|
|
||||||
{% for project in ensemble.active_projects %}
|
{% for project in ensemble.active_projects %}
|
||||||
<a class="list-group-item list-group-item-action" href="{% url 'project' project_id=project.id %}">
|
<a class="" href="{% url 'project' project_id=project.id %}">
|
||||||
<h4>{{ project.name }}</h4>
|
<h3>{{ project.name }}</h3>
|
||||||
<p><small>Due in {{ project.deadline|timeuntil }}</small></p>
|
<p><small>Due in {{ project.deadline|timeuntil }}</small></p>
|
||||||
</a>
|
</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,25 +1,24 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="col-md-4 offset-md-4">
|
<div class="narrow">
|
||||||
<div class="card">
|
<div>
|
||||||
<div class="card-header">Enter the ensemble details given to you</div>
|
<h3>My Ensembles</h3>
|
||||||
<div class="card-body">
|
<ul>
|
||||||
|
{% for ensemble in current %}
|
||||||
|
<li><a href="/?code={{ ensemble.ensemble_code}}">{{ ensemble.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
<form action="" method="POST">
|
<form action="" method="POST">
|
||||||
|
<h3>Add a new ensemble</h3>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="form-group">
|
{{ form }}
|
||||||
<label for="code">Ensemble Code</label>
|
<div class="form-actions">
|
||||||
<input type="text" class="form-control" placeholder="xxxx-xxxx-xxxx" name="code" value="{{ code }}"/>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="passphrase">Passphrase</label>
|
|
||||||
<input type="password" class="form-control" name="passphrase"/>
|
|
||||||
</div>
|
|
||||||
<div class="text-right">
|
|
||||||
<button class="btn btn-primary">Enter</button>
|
<button class="btn btn-primary">Enter</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -10,7 +10,13 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="">
|
<div class="">
|
||||||
{% include "interface/bootstrap-form.html" %}
|
<form action="" method="POST" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<div class="form-actions">
|
||||||
|
<button type="submit" class="btn-primary">Continue</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -4,44 +4,24 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="narrow">
|
||||||
<div class="col-md-4 offset-md-4">
|
<h3>Ready to upload file</h3>
|
||||||
<div class="card" id="legacy-upload">
|
<form method="POST" action="{{ upload.url }}" enctype="multipart/form-data" id="video-upload">
|
||||||
<div class="card-header">Ready to upload file</div>
|
{% for field, value in upload.fields.items %}
|
||||||
<div class="card-body">
|
<input type="hidden" name="{{ field }}" value="{{ value }}" />
|
||||||
<form method="POST" action="{{ upload.url }}" enctype="multipart/form-data" id="video-upload">
|
{% endfor %}
|
||||||
{% for field, value in upload.fields.items %}
|
|
||||||
<input type="hidden" name="{{ field }}" value="{{ value }}" />
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<input name="file" type="file" accept="video/*"/>
|
|
||||||
|
|
||||||
<div class="form-actions text-right">
|
<input name="file" type="file" accept="video/*" />
|
||||||
<a class="btn btn-secondary" href="{% url 'cancel_submission' project_id=project.pk submission_id=submission.pk %}">Cancel</a>
|
|
||||||
<button class="btn btn-primary">Upload</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="form-actions text-right">
|
||||||
|
<a class=""
|
||||||
|
href="{% url 'cancel_submission' project_id=project.pk submission_id=submission.pk %}">Cancel</a>
|
||||||
|
|
||||||
|
<button class="">Upload</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="{% static 'dropzone/dropzone.js' %}"></script>
|
</div>
|
||||||
<script>
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
//document.getElementById('fallback-file').remove();
|
|
||||||
|
|
||||||
Dropzone.options.videoUpload = {
|
|
||||||
paramName: "file", // The name that will be used to transfer the file
|
|
||||||
maxFilesize: 500, // MB
|
|
||||||
accept: function(file, done) {
|
|
||||||
if (file.name == "justinbieber.jpg") {
|
|
||||||
done("Naha, you don't.");
|
|
||||||
} else { done(); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
</script>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -1,3 +0,0 @@
|
|||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
0
interface/tests/__init__.py
Normal file
0
interface/tests/__init__.py
Normal file
26
interface/tests/test_submission.py
Normal file
26
interface/tests/test_submission.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from django.test import TestCase, Client
|
||||||
|
|
||||||
|
from interface import models
|
||||||
|
|
||||||
|
class SubmissionTestCase(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
def test_submission(self):
|
||||||
|
ensemble = models.Ensemble.objects.create(name="The Be Sharps", passphrase="Homer", bucket="virtual-orchestra")
|
||||||
|
project = ensemble.projects.create(name='Baby on Board')
|
||||||
|
|
||||||
|
response = self.client.post('/register', {'code': ensemble.code, 'passphrase': ensemble.passphrase})
|
||||||
|
self.assertRedirects(response, '/')
|
||||||
|
|
||||||
|
response = self.client.post(f"/projects/{project.pk}/submission", {'name': 'Ned', 'instrument': 'God'})
|
||||||
|
|
||||||
|
upload = response.context['upload']
|
||||||
|
self.assertEqual(upload['url'], f"https://{ensemble.bucket}.s3.amazonaws.com/")
|
||||||
|
self.assertRegex(upload['fields']['key'], r'^baby-on-board\/[0-9T\-]+_ned_god_\$\{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/{project.pk}/submission/1/cancel"), '/projects/1')
|
||||||
|
self.assertEqual(models.Submission.objects.count(), 0)
|
||||||
@ -14,20 +14,30 @@ def forbidden(request):
|
|||||||
return render(request, 'interface/forbidden.html', {})
|
return render(request, 'interface/forbidden.html', {})
|
||||||
|
|
||||||
def register(request):
|
def register(request):
|
||||||
code = ''
|
|
||||||
try:
|
request.ensemble_id = request.session.get('ensemble')
|
||||||
code = request.POST.get('code', request.GET['code'])
|
registered = request.session.setdefault('registered', {})
|
||||||
passphrase = request.POST.get('passphrase')
|
|
||||||
|
|
||||||
ensemble = models.Ensemble.objects.get(code=code)
|
if request.method == "POST":
|
||||||
if ensemble.password != passphrase:
|
form = forms.CodeForm(request.POST)
|
||||||
raise ValueError("Incorrect passphase")
|
|
||||||
|
|
||||||
request.session['ensemble'] = ensemble.pk
|
if form.is_valid():
|
||||||
return redirect('my_projects')
|
|
||||||
except:
|
data = form.cleaned_data;
|
||||||
logger.exception("Registration failed")
|
ensemble = models.Ensemble.objects.get(code=data['code'].replace('-', ''))
|
||||||
return render(request, 'interface/register.html', {'code': code})
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
current = models.Ensemble.objects.filter(pk__in=registered.values())
|
||||||
|
|
||||||
|
return render(request, 'interface/register.html', {'form': form, 'current': current})
|
||||||
|
|
||||||
|
|
||||||
@check_allowed
|
@check_allowed
|
||||||
@ -60,6 +70,10 @@ def submission(request, project_id):
|
|||||||
s.project_id = project_id
|
s.project_id = project_id
|
||||||
s.save()
|
s.save()
|
||||||
|
|
||||||
|
data = form.cleaned_data
|
||||||
|
request.session['name'] = data['name']
|
||||||
|
request.session['instrument'] = data['instrument']
|
||||||
|
|
||||||
redirect = request.build_absolute_uri(resolve_url('complete_submission', project_id=project.pk, submission_id=s.pk))
|
redirect = request.build_absolute_uri(resolve_url('complete_submission', project_id=project.pk, submission_id=s.pk))
|
||||||
|
|
||||||
upload = project.presigned_post(s.generate_key(),
|
upload = project.presigned_post(s.generate_key(),
|
||||||
@ -69,7 +83,8 @@ def submission(request, project_id):
|
|||||||
|
|
||||||
return render(request, 'interface/upload.html', context)
|
return render(request, 'interface/upload.html', context)
|
||||||
else:
|
else:
|
||||||
form = forms.SubmissionForm()
|
initial = { k: request.session.get(k) for k in ('name', 'instrument') }
|
||||||
|
form = forms.SubmissionForm(initial=initial)
|
||||||
|
|
||||||
context = {'project': project, 'form': form}
|
context = {'project': project, 'form': form}
|
||||||
return render(request, 'interface/submission.html', context)
|
return render(request, 'interface/submission.html', context)
|
||||||
@ -77,7 +92,7 @@ def submission(request, project_id):
|
|||||||
@check_allowed
|
@check_allowed
|
||||||
def cancel_submission(request, project_id, submission_id):
|
def cancel_submission(request, project_id, submission_id):
|
||||||
project = get_object_or_404(models.Project, pk=project_id, ensemble=request.ensemble_id)
|
project = get_object_or_404(models.Project, pk=project_id, ensemble=request.ensemble_id)
|
||||||
submission = project.submissions.get(pk=submission_id)
|
submission = project.all_submissions.get(pk=submission_id)
|
||||||
submission.delete()
|
submission.delete()
|
||||||
return redirect('project', project_id=project_id)
|
return redirect('project', project_id=project_id)
|
||||||
|
|
||||||
|
|||||||
@ -25,7 +25,7 @@ SECRET_KEY = '6y#33930^6@c762u(@6+&#_qx8eu^e8q+4t-(@m60vnjw37k26'
|
|||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
|
||||||
ALLOWED_HOSTS = []
|
ALLOWED_HOSTS = ['localhost', '192.168.100.123']
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user