Commit 81b65361 by Joseph Walton-Rivers

Merge branch 'hanabi' into 'master'

Hanabi See merge request !2
parents ebec4199 59959afe
Pipeline #1247 passed with stages
in 1 minute 52 seconds
......@@ -31,6 +31,7 @@ router.register(r'competition', rest_views.CompetitionViewSet)
router.register(r'track', rest_views.TrackViewSet)
router.register(r'submission', rest_views.SubmissionViewSet)
router.register(r'upload', rest_views.UploadViewSet)
router.register(r'text', rest_views.SubmissionTextViewSet)
urlpatterns = [
url(r'^$', extra.Homepage.as_view(), name="home"),
......
#! /bin/bash
# build and push the docker image
docker build -t fossgalaxy/comet .
docker push fossgalaxy/comet
docker build -t docker.io/fossgalaxy/comet .
docker push docker.io/fossgalaxy/comet
from django.contrib import admin
from .models import Competition, CompetitionLink, Track, Submission, SubmissionUpload
from .models import Competition, CompetitionLink, Track, Submission, SubmissionUpload, SubmissionText
# Register your models here.
class CompetitionLinkInline(admin.TabularInline):
......@@ -21,10 +21,15 @@ class SubmissionUploadInline(admin.TabularInline):
model = SubmissionUpload
extra = 1
class SubmissionTextInline(admin.TabularInline):
model = SubmissionText
extra = 1
class SubmissionAdmin(admin.ModelAdmin):
fieldsets = (
(None, {
'fields': ('name', 'track', ('owner', "sample"), 'description')
'fields': ('name', 'track', ('owner', "sample"), 'description', "submission_type")
}),
("Ranking", {
'fields': ('ranking', 'ranking_rd', 'velocity')
......@@ -33,6 +38,7 @@ class SubmissionAdmin(admin.ModelAdmin):
list_filter = ("track",)
list_display = ("__str__", "track")
inlines = [
SubmissionUploadInline
SubmissionUploadInline,
SubmissionTextInline
]
admin.site.register(Submission, SubmissionAdmin)
from django import forms
from django.core.exceptions import ValidationError
from .models import Track, Submission, SubmissionUpload
from .models import Track, Submission, SubmissionUpload, SubmissionText
class RegisterForm(forms.ModelForm):
......@@ -28,3 +28,10 @@ class UploadForm(forms.ModelForm):
fields = ["upload", "submission"]
widgets = {'submission': forms.HiddenInput()}
model = SubmissionUpload
class SubmissionTextForm(forms.ModelForm):
class Meta:
fields = ["body", "submission"]
widgets = {'submission': forms.HiddenInput()}
model = SubmissionText
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-08-18 10:20
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('fg_competitions', '0006_submission_sample'),
]
operations = [
migrations.CreateModel(
name='SubmissionText',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('BP', 'Build pending'), ('BF', 'Build failed'), ('BS', 'Build succeeded'), ('DA', 'Disqualified by admin')], default='BP', max_length=5)),
('created', models.DateTimeField(auto_now_add=True)),
('feedback', models.TextField(blank=True, null=True)),
('body', models.TextField(null=True)),
],
options={
'ordering': ['-created'],
'abstract': False,
},
),
migrations.AlterModelOptions(
name='submission',
options={'ordering': ['-ranking', 'ranking_rd']},
),
migrations.AddField(
model_name='submission',
name='submission_type',
field=models.CharField(choices=[('T', 'Text Submission'), ('U', 'Upload')], default='U', max_length=1),
),
migrations.AddField(
model_name='submissiontext',
name='submission',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='text_submissions', to='fg_competitions.Submission'),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-09-12 17:29
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('fg_competitions', '0007_auto_20170818_1120'),
]
operations = [
migrations.CreateModel(
name='AllowedSubmissionType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('submission_type', models.CharField(choices=[('T', 'Text Submission'), ('U', 'Upload')], max_length=1)),
('track', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fg_competitions.Track')),
],
),
migrations.AlterUniqueTogether(
name='allowedsubmissiontype',
unique_together=set([('track', 'submission_type')]),
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2018-02-08 16:39
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('fg_competitions', '0007_auto_20180208_1628'),
('fg_competitions', '0008_auto_20170912_1829'),
]
operations = [
]
from __future__ import unicode_literals
from django.utils.encoding import python_2_unicode_compatible
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.conf import settings
......@@ -11,6 +12,11 @@ STATUS_LIST = [
("DA", "Disqualified by admin")
]
SUBMISSION_TYPES = [
("T", "Text Submission"),
("U", "Upload")
]
@python_2_unicode_compatible
class Competition(models.Model):
"""A high-level descripiton of a topic"""
......@@ -54,6 +60,17 @@ class Track(models.Model):
def __str__(self):
return self.name
class AllowedSubmissionType(models.Model):
"""Types of submissions allowed"""
track = models.ForeignKey(Track)
submission_type = models.CharField(max_length=1, choices=SUBMISSION_TYPES)
def __str__(self):
return self.get_submission_type_display()
class Meta:
unique_together = ( ("track", "submission_type"), )
@python_2_unicode_compatible
class Submission(models.Model):
"""An individual entry into the competition"""
......@@ -62,6 +79,7 @@ class Submission(models.Model):
description = models.TextField(blank=True)
created = models.DateTimeField(auto_now_add=True)
sample = models.BooleanField(default=False)
submission_type = models.CharField(max_length=1, default="U", choices=SUBMISSION_TYPES)
# foreign keys
track = models.ForeignKey(Track)
......@@ -74,7 +92,7 @@ class Submission(models.Model):
@property
def pretty_score(self):
curr_upload = self.current_upload
curr_upload = self.current
if not curr_upload:
return "No submission"
elif curr_upload.status != "BS":
......@@ -86,6 +104,21 @@ class Submission(models.Model):
def current_upload(self):
return self.uploads.first()
@property
def current_text(self):
return self.text_submissions.first()
@property
def current(self):
if self.submission_type == "U":
return self.current_upload
elif self.submission_type == "T":
ct = self.current_text
print(ct)
return ct
else:
raise ValueError("unknown submission type")
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('submission_detail', kwargs={'pk':self.pk})
......@@ -114,14 +147,33 @@ class ExtensionValidator(RegexValidator):
def __call__(self, value):
super(ExtensionValidator, self).__call__(value.name)
class BaseSubmission(models.Model):
status = models.CharField(max_length=5, default="BP", choices=STATUS_LIST)
created = models.DateTimeField(auto_now_add=True)
feedback = models.TextField(blank=True, null=True)
class Meta:
abstract = True
ordering = ["-created"]
@python_2_unicode_compatible
class SubmissionText(BaseSubmission):
"""A textual version of a submission"""
submission = models.ForeignKey(Submission, related_name='text_submissions')
body = models.TextField(null=True)
def __str__(self):
return "{0}".format(self.body)
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('submission_detail', kwargs={'pk':self.submission.pk})
@python_2_unicode_compatible
class SubmissionUpload(models.Model):
class SubmissionUpload(BaseSubmission):
"""A version of a submission"""
submission = models.ForeignKey(Submission, related_name='uploads')
status = models.CharField(max_length=5, default="BP", choices=STATUS_LIST)
created = models.DateTimeField(auto_now_add=True)
upload = models.FileField(upload_to=submission_path, validators=[ExtensionValidator(['zip'])])
feedback = models.TextField(blank=True, null=True)
def __str__(self):
return "{0}".format(self.upload)
......@@ -130,5 +182,3 @@ class SubmissionUpload(models.Model):
from django.core.urlresolvers import reverse
return reverse('submission_detail', kwargs={'pk':self.submission.pk})
class Meta:
ordering = ["-created"]
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from .serializers import UserSerializer, GroupSerializer, CompetitionSerializer, TrackSerializer, SubmissionSerializer, UploadSerializer
from .models import Competition, Track, Submission, SubmissionUpload
from .serializers import UserSerializer, GroupSerializer, CompetitionSerializer, TrackSerializer, SubmissionSerializer, UploadSerializer, SubmissionTextSerializer
from .models import Competition, Track, Submission, SubmissionUpload, SubmissionText
class UserViewSet(viewsets.ModelViewSet):
"""
......@@ -26,7 +26,6 @@ class CompetitionViewSet(viewsets.ModelViewSet):
queryset = Competition.objects.all()
serializer_class = CompetitionSerializer
class TrackViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
......@@ -41,6 +40,13 @@ class SubmissionViewSet(viewsets.ModelViewSet):
queryset = Submission.objects.all()
serializer_class = SubmissionSerializer
class SubmissionTextViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = SubmissionText.objects.all()
serializer_class = SubmissionTextSerializer
class UploadViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
......
from django.contrib.auth.models import User, Group
from rest_framework import serializers
from .models import Competition, Track, Submission, SubmissionUpload
from .models import Competition, Track, Submission, SubmissionUpload, SubmissionText
class CompetitionSerializer(serializers.ModelSerializer):
class Meta:
......@@ -13,17 +13,28 @@ class UploadReadOnlySerializer(serializers.ModelSerializer):
model = SubmissionUpload
fields = ('pk', 'upload', 'status', 'created', 'feedback')
class SubmissionTextReadOnlySerializer(serializers.ModelSerializer):
class Meta:
model = SubmissionText
fields = ('pk', 'body', 'status', 'created', 'feedback')
class UploadSerializer(serializers.ModelSerializer):
class Meta:
model = SubmissionUpload
fields = ('pk', 'status', 'created', 'feedback')
class SubmissionTextSerializer(serializers.ModelSerializer):
class Meta:
model = SubmissionText
fields = ('pk', 'submission', 'status', 'body', 'created', 'feedback')
class SubmissionSerializer(serializers.ModelSerializer):
current_upload = UploadReadOnlySerializer(read_only=True)
current_text = SubmissionTextReadOnlySerializer(read_only=True)
owner = serializers.SlugRelatedField(slug_field='username', read_only=True)
class Meta:
model = Submission
fields = ('pk', 'name', 'owner', 'ranking', 'ranking_rd', 'velocity', 'current_upload')
fields = ('pk', 'name', 'owner', 'ranking', 'ranking_rd', 'velocity', 'current_upload', 'current_text')
read_only_fields = ["pk", "owner"]
class TrackSerializer(serializers.ModelSerializer):
......
{% extends "base.html" %}
{% load account bootstrap3 %}
{% block head_title %}{{competition}}{% endblock %}
{% block content %}
<h1>Upload new revision of '{{submission}}'</h1>
{% if submission.track.instructions %}
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title"><a data-toggle="collapse" href="#instructions">Submission Instructions</a></h4>
</div>
<div id="instructions" class="panel-collapse collapse">
<div class="panel-body">
<p>SOMETHING.</p>
</div>
</div>
</div>
{% endif %}
<form method="post" action="#" enctype="multipart/form-data" >
{% csrf_token %}
{% bootstrap_form form %}
<input type="hidden" name="submission" value="{{submission.pk}}" />
<input type="submit" class="btn btn-primary" value="Upload Revision" />
<a href="{{ submission.get_absolute_url }}" class="btn btn-text">cancel</a>
</form>
{% endblock %}
......@@ -52,10 +52,11 @@
<tbody>
{% for submission in track.submission_set.all %}
<tr {% if not submission.current_upload %} class="warning" {% endif %}>
<tr {% if not submission.current %} class="warning" {% endif %}>
<th><a href="{{submission.get_absolute_url}}">{{submission}}</a>{% if submission.sample %} <span class="label label-default">sample</span>{% endif %}</th>
<td>{% user_display submission.owner %}</td>
<td>{% if not submission.current_upload %}No submission{% else %}{{submission.ranking}}{% endif %}</td>
<td>{{ submission.pretty_score|default:"wtf" }}</td>
</tr>
{% empty %}
<tr>
......
......@@ -8,6 +8,7 @@ urlpatterns = [
url(r'^t/(?P<pk>\w+)$', views.TrackDetail.as_view(), name="track_detail"),
url(r'^t/(?P<track>\w+)/enter$', views.SubmissionCreate.as_view(), name="submission_create"),
url(r'^s/(?P<pk>\w+)$', views.SubmissionDetail.as_view(), name="submission_detail"),
url(r'^s/(?P<submission>\w+)/text$', views.TextSubmission.as_view(), name="submission_text"),
url(r'^s/(?P<submission>\w+)/upload$', views.UploadSubmission.as_view(), name="submission_upload"),
url(r'^s/(?P<pk>\w+)/update$', views.SubmissionUpdate.as_view(), name="submission_update"),
url(r'^d/(?P<pk>\w+)/$', views.download_submission, name="submission_download"),
......
......@@ -4,8 +4,8 @@ from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse
from django.http.response import HttpResponse, FileResponse
from .models import Competition, Track, Submission, SubmissionUpload, submission_path
from .forms import RegisterForm, UploadForm
from .models import Competition, Track, Submission, SubmissionUpload, SubmissionText, submission_path
from .forms import RegisterForm, UploadForm, SubmissionTextForm
from .filters import TrackFilter
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
......@@ -155,6 +155,38 @@ class SubmissionUpdate(UpdateView):
return context
@method_decorator(login_required, name='dispatch')
class TextSubmission(CreateView):
model = SubmissionText
form_class = SubmissionTextForm
def get_context_data(self, **kwargs):
context = super(TextSubmission, self).get_context_data(**kwargs)
submission = self.kwargs.get('submission')
context['submission'] = get_object_or_404(Submission, id=submission)
# if the user is not the owner of that submission, tell them off
if not context['submission'].owner == self.request.user:
raise PermissionDenied()
# check that the track allows updates
track = context['submission'].track
if not track.allow_update:
raise PermissionDenied()
return context
def get_initial(self):
context = super(TextSubmission, self).get_initial()
context['submission'] = self.kwargs.get('submission', None)
return context
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.status = "BS"
form.instance.submission.submission_type = "T"
return super(TextSubmission, self).form_valid(form)
@method_decorator(login_required, name='dispatch')
class UploadSubmission(CreateView):
model = SubmissionUpload
form_class = UploadForm
......@@ -182,4 +214,5 @@ class UploadSubmission(CreateView):
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.submission.submission_type = "U"
return super(UploadSubmission, self).form_valid(form)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment