Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI revamp for tin #13

Merged
merged 4 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 bix
JasonGrace2282 marked this conversation as resolved.
Show resolved Hide resolved
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
116 changes: 116 additions & 0 deletions tin/static/css/edit.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
.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;
box-shadow: 6px 6px 12px #777,
-6px -6px 12px #ffffff;
krishnans2006 marked this conversation as resolved.
Show resolved Hide resolved
}

button.collapsible.active, .collapsible:hover {
background-color: #555;
}
JasonGrace2282 marked this conversation as resolved.
Show resolved Hide resolved
JasonGrace2282 marked this conversation as resolved.
Show resolved Hide resolved

button.collapsible.active {
border-radius: 10px 10px 0px 0px;
}

.content {
display: none;
max-width: 90%;
width: 90%;
padding-top: 18px;
overflow: hidden;
background-color: #F2F2F4;
border-radius: 0px 0px 10px 10px;
box-shadow: inset 6px 6px 12px #d9d9d9,
inset -6px -6px 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;
JasonGrace2282 marked this conversation as resolved.
Show resolved Hide resolved
JasonGrace2282 marked this conversation as resolved.
Show resolved Hide resolved
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;
}
87 changes: 59 additions & 28 deletions tin/templates/assignments/edit_create.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,28 @@

{% 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;

const coll = $('.collapsible');
let i;

for (i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function() {
this.classList.toggle("active");
const content = this.nextElementSibling;
content.classList.toggle("block");
});
}
</style>
});
</script>
{% endblock %}

{% block main %}
Expand All @@ -46,24 +53,48 @@ <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 %}
JasonGrace2282 marked this conversation as resolved.
Show resolved Hide resolved
<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>
</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 %}
Loading