Skip to content

Commit

Permalink
Add skyward expedition submission form (#136)
Browse files Browse the repository at this point in the history
* Add submission upload capability

* Added submissions dashboard for admin
  • Loading branch information
anirudhprabhakaran3 authored Mar 4, 2024
1 parent 717bd94 commit ab1aaba
Show file tree
Hide file tree
Showing 14 changed files with 297 additions and 6 deletions.
6 changes: 3 additions & 3 deletions corpus/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ WORKDIR /corpus
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

RUN apt-get update && apt-get install -y gcc libpq-dev curl
RUN apt-get update && apt-get install -y gcc libpq-dev curl libmagic1
RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get install -y nodejs

COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip
Expand All @@ -16,7 +18,5 @@ COPY . .
RUN chmod +x ./start.sh
RUN chmod +x ./start_dev.sh

RUN curl -sL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get install -y nodejs
RUN npm i
RUN npm run tailwind-prod
57 changes: 57 additions & 0 deletions corpus/corpus/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import magic
from django.core.exceptions import ValidationError
from django.template.defaultfilters import filesizeformat
from django.utils.deconstruct import deconstructible


@deconstructible
class FileValidator(object):
error_messages = {
"max_size": (
"Ensure this file size is not greater than %(max_size)s."
"Your file size is %(size)s"
),
"min_size": (
"Ensure this file size is not less than %(min_size)s."
"Your file size is %(size)s."
),
"content_type": "Files of type %(content_type)s are not supported.",
}

def __init__(self, max_size=None, min_size=None, content_types=()):
self.max_size = max_size
self.min_size = min_size
self.content_types = content_types

def __call__(self, data):
if self.max_size is not None and data.size > self.max_size:
params = {
"max_size": filesizeformat(self.max_size),
"size": filesizeformat(data.size),
}
raise ValidationError(self.error_messages["max_size"], "max_size", params)

if self.min_size is not None and data.size < self.min_size:
params = {
"min_size": filesizeformat(self.min_size),
"size": filesizeformat(data.size),
}
raise ValidationError(self.error_messages["min_size"], "min_size", params)

if self.content_types:
content_type = magic.from_buffer(data.read(), mime=True)
data.seek(0)

if content_type not in self.content_types:
params = {"content_type": content_type}
raise ValidationError(
self.error_messages["content_type"], "content_type", params
)

def __eq__(self, other):
return (
isinstance(other, FileValidator)
and self.max_size == other.max_size
and self.min_size == other.min_size
and self.content_types == other.content_types
)
17 changes: 15 additions & 2 deletions corpus/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
asgiref==3.7.2
Django==4.2.10
cfgv==3.4.0
distlib==0.3.8
Django==4.2.7
filelock==3.13.1
gunicorn==21.2.0
identify==2.5.34
nodeenv==1.8.0
packaging==23.1
Pillow==10.2.0
Pillow==10.0.1
pip==24.0
platformdirs==4.2.0
pre-commit==3.6.1
psycopg2-binary==2.9.9
python-magic==0.4.27
PyYAML==6.0.1
setuptools==68.2.2
sqlparse==0.4.4
typing_extensions==4.8.0
virtualenv==20.25.0
wheel==0.41.2
2 changes: 2 additions & 0 deletions corpus/skyward_expedition/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from skyward_expedition.models import Announcement
from skyward_expedition.models import Invite
from skyward_expedition.models import SEUser
from skyward_expedition.models import Submission
from skyward_expedition.models import Team


Expand All @@ -18,3 +19,4 @@ class SEUserAdmin(admin.ModelAdmin):
admin.site.register(Team)
admin.site.register(Invite)
admin.site.register(Announcement)
admin.site.register(Submission)
7 changes: 7 additions & 0 deletions corpus/skyward_expedition/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from skyward_expedition.models import Announcement
from skyward_expedition.models import Invite
from skyward_expedition.models import SEUser
from skyward_expedition.models import Submission
from skyward_expedition.models import Team

from corpus.forms import CorpusModelForm
Expand Down Expand Up @@ -67,3 +68,9 @@ def clean(self):
"Both URL Link and corresponding text are required."
)
return data


class SubmissionForm(CorpusModelForm):
class Meta:
model = Submission
fields = ["file"]
59 changes: 59 additions & 0 deletions corpus/skyward_expedition/migrations/0004_submission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Generated by Django 4.2.7 on 2024-03-04 04:10
import django.db.models.deletion
from django.db import migrations
from django.db import models

import corpus.validators


class Migration(migrations.Migration):

dependencies = [
(
"skyward_expedition",
"0003_seuser_degree_seuser_ieee_member_seuser_ieee_number_and_more",
),
]

operations = [
migrations.CreateModel(
name="Submission",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"file",
models.FileField(
upload_to="skyward_expedition/submissions",
validators=[
corpus.validators.FileValidator(
content_types=(
"application/zip",
"application/x-rar-compressed",
"application/octet-stream",
),
max_size=10485760,
)
],
verbose_name="Submission File",
),
),
(
"team",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="skyward_expedition.team",
),
),
],
),
]
19 changes: 19 additions & 0 deletions corpus/skyward_expedition/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@
from django.db import models

from corpus.utils import send_email
from corpus.validators import FileValidator

file_validator = FileValidator(
max_size=1024 * 1024 * 10,
content_types=(
"application/zip",
"application/x-rar-compressed",
"application/octet-stream",
),
)


# Create your models here.
Expand Down Expand Up @@ -70,3 +80,12 @@ def send_email(self, mail_option):
{"announcement": self},
bcc=email_ids,
)


class Submission(models.Model):
team = models.ForeignKey(Team, on_delete=models.CASCADE, blank=True, null=True)
file = models.FileField(
verbose_name="Submission File",
upload_to="skyward_expedition/submissions",
validators=[file_validator],
)
6 changes: 6 additions & 0 deletions corpus/skyward_expedition/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
views.delete_invite,
name="skyward_expedition_delete_invite",
),
path("submission/", views.submission, name="skyward_expedition_create_submission"),
path("admin/", views.admin, name="skyward_expedition_admin"),
path(
"admin/members/",
Expand Down Expand Up @@ -53,4 +54,9 @@
views.delete_announcement,
name="skyward_expedition_delete_announcement",
),
path(
"admin/submissions/",
views.submissions_dashboard,
name="skyward_expedition_submissions_dashboard",
),
]
41 changes: 41 additions & 0 deletions corpus/skyward_expedition/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
from skyward_expedition.forms import AnnouncementForm
from skyward_expedition.forms import InviteForm
from skyward_expedition.forms import SEForm
from skyward_expedition.forms import SubmissionForm
from skyward_expedition.forms import TeamCreationForm
from skyward_expedition.models import Announcement
from skyward_expedition.models import Invite
from skyward_expedition.models import SEUser
from skyward_expedition.models import Submission
from skyward_expedition.models import Team

from corpus.decorators import ensure_group_membership
Expand Down Expand Up @@ -254,6 +256,37 @@ def delete_invite(request, pk):
return redirect("skyward_expedition_dashboard")


@login_required
@module_enabled(module_name="skyward_expedition")
def submission(request):
team = SEUser.objects.get(user=request.user).team

try:
prev_submission = Submission.objects.get(team=team)
except Submission.DoesNotExist:
prev_submission = None

if prev_submission:
form = SubmissionForm(instance=prev_submission)
else:
form = SubmissionForm()

if request.method == "POST":
print(request.POST)
print(request.FILES)
form = SubmissionForm(request.POST, request.FILES)
if form.is_valid():
submission = form.save(commit=False)
submission.team = team
submission.save()
messages.success(request, "Submission made successfully!")
return redirect("skyward_expedition_dashboard")

args = {"form": form}

return render(request, "skyward_expedition/create_submission.html", args)


@login_required
@ensure_group_membership(group_names=["skyward_expedition_admin"])
def admin(request):
Expand Down Expand Up @@ -360,3 +393,11 @@ def delete_announcement(request, announcement_id):

messages.success(request, "Announcement deleted!")
return redirect("skyward_expedition_announcements_dashboard")


@login_required
@ensure_group_membership(group_names=["skyward_expedition_admin"])
def submissions_dashboard(request):
submissions = Submission.objects.all()
args = {"submissions": submissions}
return render(request, "skyward_expedition/admin/submissions_dashboard.html", args)
3 changes: 3 additions & 0 deletions corpus/templates/skyward_expedition/admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ <h1 class="text-4xl font-bold text-center">Admin Dashboard</h1>
<a href="{% url 'skyward_expedition_announcements_dashboard' %}" class="btn btn-accent">
Announcements Dashboard
</a>
<a href="{% url 'skyward_expedition_submissions_dashboard' %}" class="btn btn-accent">
Submissions Dashboard
</a>
</div>
</div>
{% endblock %}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{% extends 'skyward_expedition/base.html' %}

{% block title %}
Submissions Dashboard
{{ block.super }}
{% endblock %}

{% block content %}
<div class="flex flex-col mx-20">
<div class="flex flex-row justify-center items-center my-20">
<h1 class="text-4xl font-bold text-center">Submissions Dashboard</h1>
</div>
<div class="flex flex-row mb-20">
<a href="{% url 'skyward_expedition_admin' %}" class="btn btn-accent m-5">Go to Admin Dashboard</a>
</div>
<div class="flex flex-col my-10 overflow-x-auto">
<table class="table">
<thead>
<tr>
<td>Team</td>
<td>Submission</td>
</tr>
</thead>
<tbody>
{% for submission in submissions %}
<tr>
<td>{{ submission.team.team_name }}</td>
<td>
<a href="{{ submission.file.url }}"
class="underline underline-offset-4 text-primary">{{ submission.file }}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
3 changes: 2 additions & 1 deletion corpus/templates/skyward_expedition/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

{% block script %}
<script>
document.addEventListener("DOMContentLoaded", function () {
document.addEventListener("DOMContentLoaded", async function () {
await new Promise(r => setTimeout(r, 100));
const themeButton = document.getElementById("dark-mode-button");
themeButton.remove();
document.documentElement.setAttribute("data-theme", "winter");
Expand Down
Loading

0 comments on commit ab1aaba

Please sign in to comment.