Skip to content

Commit

Permalink
Revamp the assignment edit UI (#13)
Browse files Browse the repository at this point in the history
Resolves #10.

Co-authored-by: Krishnan Shankar <krishnans2006@gmail.com>
  • Loading branch information
JasonGrace2282 and krishnans2006 authored May 12, 2024
1 parent f294778 commit 72d0153
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 28 deletions.
69 changes: 69 additions & 0 deletions tin/apps/assignments/forms.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
from __future__ import annotations

from logging import getLogger
from typing import Dict, Iterable, Tuple

from django import forms
from django.conf import settings

from ..submissions.models import Submission
from .models import Assignment, Folder, MossResult

logger = getLogger(__name__)


class AssignmentForm(forms.ModelForm):
QUIZ_ACTIONS = (("-1", "No"), ("0", "Log only"), ("1", "Color Change"), ("2", "Lock"))
Expand All @@ -23,6 +30,26 @@ def __init__(self, course, *args, **kwargs):
"issues."
)

# prevent description from getting too big
self.fields["description"].widget.attrs.update({"id": "description"})

def get_sections(self) -> Iterable[Dict[str, str | Tuple[str, ...] | bool]]:
for section in self.Meta.sections:
if section["name"]:
# operate on copy so errors on refresh don't happen
section = section.copy()
section["fields"] = tuple(self[field] for field in section["fields"])
yield section

def get_main_section(self) -> Dict[str, str | Tuple[str, ...]]:
for section in self.Meta.sections:
if section["name"] == "":
section = section.copy()
section["fields"] = tuple(self[field] for field in section["fields"])
return section
logger.error(f"Could not find main section for assignment {self}")
return {"fields": ()}

class Meta:
model = Assignment
fields = [
Expand Down Expand Up @@ -57,6 +84,45 @@ class Meta:
"submission_limit_cooldown": "Rate limit cooldown period (minutes)",
"is_quiz": "Is this a quiz?",
}
sections = (
{
"name": "",
"fields": (
"name",
"description",
"markdown",
"due",
"points_possible",
"is_quiz",
"hidden",
),
},
{
"name": "Environment Setup",
"description": "",
"fields": (
"folder",
"language",
"filename",
"venv",
),
"collapsed": False,
},
{
"name": "Submissions",
"description": "",
"fields": (
"enable_grader_timeout",
"grader_timeout",
"has_network_access",
"grader_has_network_access",
"submission_limit_count",
"submission_limit_interval",
"submission_limit_cooldown",
),
"collapsed": True,
},
)
help_texts = {
"filename": "Clarify which file students need to upload (including the file "
"extension). For Java assignments, this also sets the name of the "
Expand All @@ -81,6 +147,9 @@ class Meta:
}
widgets = {"description": forms.Textarea(attrs={"cols": 30, "rows": 4})}

def __str__(self) -> str:
return f"AssignmentForm(\"{self['name'].value()}\")"


class GraderScriptUploadForm(forms.Form):
grader_file = forms.FileField(
Expand Down
122 changes: 122 additions & 0 deletions tin/static/css/edit.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
button.collapsible {
background-color: #777;
color: white;
cursor: pointer;
padding: 18px;
max-width: 90%;
width: 90%;
border-radius: 10px;
text-align: center;
outline: none;
font-size: 15px;
border: none;
}

button.collapsible:hover {
background-color: #555;
}

button.collapsible.active {
border-radius: 10px 10px 0px 0px;
box-shadow: 6px 0 12px #777,
-6px -6px 12px #ffffff;
}

button.collapsible:not(.active) {
border-radius: 10px;
box-shadow: 6px 6px 12px #777,
-6px -6px 12px #ffffff;
}

.content {
display: none;
max-width: 90%;
width: 90%;
padding-top: 18px;
overflow: hidden;
background-color: #F2F2F4;
border-radius: 0px 0px 10px 10px;
box-shadow: 6px 6px 12px #777,
-6px 0 12px #ffffff;
}

.block {
display: block;
}

.collapsible:after {
content: '+'; /* Unicode character for "plus" sign (+) */
font-size: 20px;
float: left;
margin-right: 5px;
color: white;
}

button.collapsible.active:after {
content: "-"; /* Unicode character for "minus" sign (-) */
color: white;
font-size: 20px;
}

.padding-div {
background: transparent;
height: 20px;
width: auto;
}

/* This should be aligned to the right vertically stacked */
.right-col {
width: 50%;
float: right;
}

/* This is the text that's not in a collapsible */
#required {
width: 50%;
float: left;
padding-bottom: 50px;
}

/* Style the description box */
textarea {
width: 90%;
height: 300px;
resize: vertical;
/* For legacy browsers without CSS 3 support */
min-width: 90%;
max-width: 90%;
}

@media (max-width: 900.02px) {

.right-col {
width: 90%;
float: left;
}

#required {
width: 90%;
float: left;
padding-bottom: 20px;
}
}

.field {
display: flex;
flex-direction: row;
width: 90%;
padding-bottom: 15px;
}

.tag {
width: 30%;
padding-left: 18px;
}

.field-input {
width: 70%;
}

#id_description {
font-family: inherit;
}
85 changes: 57 additions & 28 deletions tin/templates/assignments/edit_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,25 @@

{% block head %}
<link rel="stylesheet" type="text/css" href="{% static 'vendor/datetimepicker/jquery.datetimepicker.css' %}"/>
<link rel="stylesheet" type="text/css" href="{% static 'css/edit.css' %}">
<script type="text/javascript" src="{% static 'vendor/datetimepicker/jquery.datetimepicker.js' %}"></script>
<script type="text/javascript">
$(document).ready(function () {
$("#id_due").datetimepicker({
lazyInit: true,
timepicker: true,
format: "Y-m-d H:i:s",
});
$(document).ready(function () {
$("#id_due").datetimepicker({
lazyInit: true,
timepicker: true,
format: "Y-m-d H:i:s",
});
</script>
<style>
#id_description {
font-family: inherit;

for (category of $(".collapsible")) {
category.addEventListener("click", function() {
this.classList.toggle("active");
const content = this.nextElementSibling;
content.classList.toggle("block");
});
}
</style>
});
</script>
{% endblock %}

{% block main %}
Expand All @@ -46,24 +50,49 @@ <h3 class="errors">Please correct the errors below.</h3>

<form action="" method="post">
{% csrf_token %}
<div class="tbl">
{% for field in assignment_form %}
<div class="tbl-row">
<span class="tbl-cell bold" style="vertical-align:top;padding-right:5px;">{{ field.label_tag }}</span>
<span class="tbl-cell form-input"{% if field.help_text %} style="padding-bottom:5px"{% endif %}>{{ field }}
{% if field.help_text %}<br>{{ field.help_text }}{% endif %}</span>

<div class="fields">
<div id="required">
{% for field in assignment_form.get_main_section.fields %}
<div class="field">
<span class="tag">{{ field.label_tag }}</span>
<span class="field-input">{{ field }}{% if field.help_text %}<br>{{ field.help_text }}{% endif %}</span>
</div>
{% endfor %}
<br>
<div>
{% if action == "add" %}
Note: You'll be able to upload a grader script after you create the assignment.
{% else %}
Note: You can upload a grader script with the "Upload grader" button on the assignment page.
{% endif %}
</div>
{% endfor %}
<input type="submit" value="{% if action == "add" %}Create{% else %}Save{% endif %}">
{% if action == "edit" %}
&emsp;<a style="color:red" href="{% url 'assignments:delete' assignment.id %}">Delete Assignment</a>
{% endif %}
</input>
</div>

<div class="right-col">
{% for category in assignment_form.get_sections %}
<button type="button" class="collapsible {% if not category.collapsed %}active{% endif %}">{{ category.name }}</button>
<div class="content {% if not category.collapsed %}block{% endif %}">
{% if category.description %}
<span style="font-size:20px">{{ category.description }}</span>
<br><br>
{% endif %}
{% for field in category.fields %}
<div class="field">
<span class="tag">{{ field.label_tag }}</span>
<span class="field-input">{{ field }}{% if field.help_text %}<br>{{ field.help_text }}{% endif %}</span>
</div>
{% endfor %}
</div>
<div class="padding-div"></div>
{% endfor %}
<div class="padding-div"></div>
</div>
</div>
{% if action == "add" %}
<div>Note: You'll be able to upload a grader script after you create the assignment.</div>
{% else %}
<div>Note: You can upload a grader script with the "Upload grader" button on the assignment page.</div>
{% endif %}
<input type="submit" value="{% if action == "add" %}Create{% else %}Save{% endif %}">
{% if action == "edit" %}
&emsp;<a style="color:red" href="{% url 'assignments:delete' assignment.id %}">Delete Assignment</a>
{% endif %}
</form>

{% endblock %}

0 comments on commit 72d0153

Please sign in to comment.