Skip to content

Commit 2b92bcc

Browse files
committed
improved course management display by adding completion status for assignments
1 parent 7f9a6ff commit 2b92bcc

21 files changed

+131
-55
lines changed

db.sqlite3

0 Bytes
Binary file not shown.
578 Bytes
Binary file not shown.
804 Bytes
Binary file not shown.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.3 on 2023-11-13 05:58
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('deimos', '0021_questionstudent_is_complete'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='assignmentstudent',
15+
name='is_complete',
16+
field=models.BooleanField(default=False),
17+
),
18+
]
Binary file not shown.

deimos/models.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class AssignmentStudent(models.Model):
7474
assignment = models.ForeignKey(Assignment, on_delete=models.CASCADE)
7575
grade = models.FloatField(validators=[MaxValueValidator(100)], default=0, null=True)
7676
due_date = models.DateTimeField(null=True, blank=True)
77+
is_complete = models.BooleanField(default=False)
7778

7879
def get_grade(self):
7980
"""
@@ -101,6 +102,10 @@ def get_grade(self):
101102
self.grade = 0
102103
self.assignment.num_points = total
103104
return self.grade
105+
106+
def get_status(self):
107+
return self.is_complete
108+
104109
def save(self, *args, **kwargs):
105110
if not self.due_date:
106111
self.due_date = self.assignment.due_date
@@ -118,6 +123,12 @@ class QuestionStudent(models.Model):
118123
instances_created = models.BooleanField(default=False)
119124
num_units_attempts = models.IntegerField(default=0, null=True, blank=True, validators=[MinValueValidator(0)])
120125
is_complete = models.BooleanField(default=False)
126+
127+
def save(self, *args, **kwargs):
128+
if self.success == True:
129+
self.is_complete = True
130+
super(QuestionStudent, self).save(*args, **kwargs)
131+
121132
def create_instances(self):
122133
"""
123134
Get variable instances from the variables associated to the question.
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
<!-- deimos: assignment_display.html -->
2+
{% load my_custom_tags %}
23
<li class="assignment-li course-li" data-assignment-id="{{ assigment.id }}">
34
<a href="{% url 'deimos:assignment_management' course_id=course.id assignment_id=assignment.id %}">
45
<div class="course-section assigment-section">
56
<div>
67
<span class="c-name assignment-name"><strong>{{assignment.name | safe}}</strong></span>
7-
<p class="timestamp" style="display: none;">Created on {{ assignment.timestamp|date:"m/d/Y" }}</p>
8+
<p class="timestamp" style="display: none;">Created on {{ assignment.timestamp|format_date }}</p>
89
{% if assignment.assigned_date %}
9-
<p class="timestamp">Assigned on {{ assignment.assigned_date|date:"m/d/Y" }}</p>
10+
<p class="timestamp">Assigned on {{ assignment.assigned_date|format_date }}</p>
1011
{% endif %}
1112
{% if assignment.due_date %}
12-
<p class="timestamp">Due on {{ assignment.due_date|date:"m/d/Y" }}</p>
13+
<p class="timestamp">Due on {{ assignment.due_date|format_date }}</p>
1314
{% endif %}
1415
</div>
16+
{% if status %}
17+
<button type="button" class="btn status edit-btn" style="position: absolute; bottom: 2%;">
18+
Completed
19+
</button>
20+
{% endif %}
1521
</div>
22+
1623
</a>
1724
</li>

deimos/templates/deimos/assignment_management.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ <h4 class="suggestion original-size page-title">{{assignment.course.name}}: {{as
2626
<hr/>
2727
<!-- Display assignment's questions -->
2828
<ul class="courses course-list">
29-
{% for question, question_student in questions %}
30-
{% include 'deimos/question_display.html' with question=question assignment_id=assignment.id course_id=assignment.course.id question_student=question_student %}
29+
{% for question, status in questions %}
30+
{% include 'deimos/question_display.html' with question=question assignment_id=assignment.id course_id=assignment.course.id status=status %}
3131
{% endfor %}
3232
</ul>
3333

deimos/templates/deimos/course_management.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ <h4>{{course.name}}</h4>
3030

3131
<!-- Display course's assignments -->
3232
<ul class="courses course-list">
33-
{% for assignment in assignments %}
34-
{% include 'deimos/assignment_display.html' with assignment=assignment course=course %}
33+
{% for assignment, status in assignments %}
34+
{% include 'deimos/assignment_display.html' with assignment=assignment course=course status=status %}
3535
{% endfor %}
3636
</ul>
3737
</div>

deimos/templates/deimos/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ <h4>My courses</h4>
1919
{% for course in courses%}
2020
{% include 'deimos/course_display.html' with course=course%}
2121
{% empty %}
22-
<h5 class="suggestion original-size">You are not yet registered for any course.
22+
<h5 class="suggestion original-size" style="width:100%">You are not yet registered for any course.
2323
Go to <a href="{% url 'astros:all_courses' %}">All courses</a> to see available courses.
2424
</h5>
2525
{% endfor %}

deimos/templates/deimos/question_display.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
{{ question.text|safe }}</p><br/>
1111
<p class="timestamp" style="display: none;">Created on {{ question.timestamp|date:"m/d/Y" }}</p>
1212
</div>
13-
{% if question_student.get_status %}
13+
{% if status %}
1414
<button type="button" class="btn status edit-btn" style="position: absolute; bottom: 2%;">
1515
Completed
1616
</button>

deimos/templates/deimos/search_question.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ <h4 class="suggestion">Search Results...</h4><hr/>
3030
{% for similar_question in similar_questions %}
3131
{% include 'deimos/question_display.html' with question=similar_question.question assignment_id=similar_question.question.assignment.id course_id=similar_question.question.assignment.course.id%}
3232
{% empty %}
33-
<h4 class="suggestion original-size"> Enter your question in the search bar and hit enter.</h4>
33+
<h4 class="suggestion original-size" style="width:100%"> Enter your question in the search bar and hit enter.</h4>
3434
{% endfor %}
3535
</ul>
3636

Binary file not shown.

deimos/templatetags/my_custom_tags.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django import template
2+
from datetime import datetime
23

34
register = template.Library()
45

@@ -24,4 +25,23 @@ def divide(value, arg):
2425
return int(result)
2526
return result
2627
except (ValueError, TypeError):
27-
return 0
28+
return 0
29+
30+
@register.filter
31+
def format_date(due_date_str):
32+
# Check if due_date_str is already a datetime object
33+
if isinstance(due_date_str, datetime):
34+
due_date = due_date_str
35+
else:
36+
# Parsing the date string
37+
due_date = datetime.strptime(due_date_str, '%Y-%m-%d %H:%M:%S%z')
38+
# Getting the day with the appropriate suffix
39+
day = int(due_date.strftime('%d'))
40+
if 4 <= day <= 20 or 24 <= day <= 30:
41+
suffix = 'th'
42+
else:
43+
suffix = ['st', 'nd', 'rd'][day % 10 - 1]
44+
45+
# Formatting the date in the desired format
46+
formatted_date = due_date.strftime(f'%B {day}{suffix}, %Y at %I:%M%p')
47+
return formatted_date

deimos/views.py

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,47 @@ def course_management(request, course_id):
5050

5151
assignments = Assignment.objects.filter(course=course, assignmentstudent__student=student, \
5252
is_assigned=True)
53+
as_statuses = []
54+
if course.name != 'Question Bank':
55+
for assignment in assignments:
56+
ass, created = AssignmentStudent.objects.get_or_create(assignment= assignment, student=student)
57+
as_statuses.append(ass.get_status())
58+
5359
context = {
5460
"student":student,
55-
"assignments": assignments,
61+
"assignments": zip(assignments, as_statuses),
5662
"course": course,
5763
}
5864
return render(request, "deimos/course_management.html", context)
5965

66+
@login_required(login_url='astros:login')
67+
def assignment_management(request, assignment_id, course_id=None):
68+
# Making sure the request is done by a Student.
69+
student = get_object_or_404(Student, pk = request.user.pk)
70+
assignment = get_object_or_404(Assignment, pk = assignment_id)
71+
assignment_student = AssignmentStudent.objects.get(student=student, assignment=assignment)
72+
questions = Question.objects.filter(assignment = assignment, parent_question=None)
73+
qs_statuses = []
74+
if assignment.course.name != 'Question Bank':
75+
for question in questions:
76+
qs, created = QuestionStudent.objects.get_or_create(question=question, student=student)
77+
qs_statuses.append(qs.get_status())
78+
question.text = qs.evaluate_var_expressions_in_text(question.text, add_html_style=True)
79+
else:
80+
qs_statuses = [False for i in range(questions.count())] # here for templating purposes.
81+
82+
if sum(qs_statuses) == len(qs_statuses): # if all the questions have been completed.
83+
assignment_student.is_complete = True
84+
else:
85+
assignment_student.is_complete = False
86+
assignment_student.save()
87+
88+
context = {
89+
"questions": zip(questions, qs_statuses),
90+
"assignment": assignment,
91+
}
92+
return render(request, "deimos/assignment_management.html", context)
93+
6094
@login_required(login_url='astros:login')
6195
def gradebook(request, course_id):
6296
course = get_object_or_404(Course, pk=course_id)
@@ -87,25 +121,6 @@ def gradebook(request, course_id):
87121
"course_score": round(course_score, 2),
88122
})
89123

90-
@login_required(login_url='astros:login')
91-
def assignment_management(request, assignment_id, course_id=None):
92-
# Making sure the request is done by a Student.
93-
student = get_object_or_404(Student, pk = request.user.pk)
94-
assignment = get_object_or_404(Assignment, pk = assignment_id)
95-
questions = Question.objects.filter(assignment = assignment, parent_question=None)
96-
question_students = []
97-
if assignment.course != 'Question Bank':
98-
for question in questions:
99-
qs, created = QuestionStudent.objects.get_or_create(question=question, student=student)
100-
question_students.append(qs)
101-
question.text = qs.evaluate_var_expressions_in_text(question.text, add_html_style=True)
102-
else:
103-
question_students = [i for i in range(questions.count())] # here for templating purposes.
104-
context = {
105-
"questions": zip(questions, question_students),
106-
"assignment": assignment,
107-
}
108-
return render(request, "deimos/assignment_management.html", context)
109124

110125
# List of all answer types
111126
all_mcq_answer_types = {
@@ -480,13 +495,13 @@ def validate_answer(request, question_id, landed_question_id=None,assignment_id=
480495
question_student.save()
481496

482497
# Some info that will be used to update the front end.
483-
if (not correct and (data["questionType"].startswith('structural'))):
498+
if ((not correct) and (data["questionType"].startswith('structural'))):
484499
too_many_attempts = num_attempts + 1 == question.struct_settings.max_num_attempts
485500
if not units_correct:
486501
units_too_many_attempts = question_student.num_units_attempts >= question.struct_settings.units_num_attempts
487502

488503
# Updating completion status
489-
if not question_student.is_complete and (units_too_many_attempts or units_correct) and (too_many_attempts or prev_success):
504+
if (not question_student.is_complete) and (units_too_many_attempts or units_correct) and (too_many_attempts or prev_success):
490505
question_student.is_complete = True
491506
question_student.save()
492507
# Return a JsonResponse
47 Bytes
Binary file not shown.

phobos/templates/phobos/assignment_display.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
<!-- phobos: assignment_display.html -->
2+
{% load my_custom_tags %}
23
<li class="assignment-li course-li" data-assignment-id="{{ assigment.id }}">
34
<a href="{% url 'phobos:assignment_management' course_id=course.id assignment_id=assignment.id %}">
45
<div class="course-section assigment-section">
56
<div>
67
<span class="c-name assignment-name"><strong>{{assignment.name | safe}}</strong></span>
7-
<p class="timestamp" style="display: none;">Created on {{ assignment.timestamp|date:"m/d/Y" }}</p>
8+
<p class="timestamp" style="display: none;">Created on {{ assignment.timestamp|format_date }}</p>
89
{% if assignment.assigned_date %}
9-
<p class="timestamp">Assigned on {{ assignment.assigned_date|date:"m/d/Y" }}</p>
10+
<p class="timestamp">Assigned on {{ assignment.assigned_date|format_date }}</p>
1011
{% endif %}
1112

1213
{% if assignment.due_date %}
13-
<p class="timestamp">Due on {{ assignment.due_date|date:"m/d/Y" }}</p>
14+
<p class="timestamp">Due on {{ assignment.due_date|format_date }}</p>
1415
{% endif %}
1516
</div>
1617
</div>

phobos/templates/phobos/assignment_management.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
{% extends 'phobos/layout.html' %}
33
{% block title %} Assignment {% endblock %}
44
{% load static %}
5+
{% load my_custom_tags %}
56
{% block body%}
67
<div class="sidebar" style="display:none">
78
<header class="sidebar-header">X</header>

phobos/templates/phobos/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ <h4>My courses</h4>
2929
{% for course in courses%}
3030
{% include 'phobos/course_display.html' with course=course%}
3131
{% empty %}
32-
<h4 class="suggestion original-size"> You did not create a course yet. Go to
32+
<h4 class="suggestion original-size" style="width:100%"> You did not create a course yet. Go to
3333
<a href="{% url 'phobos:create_course' %}">create</a> to create a new course.
3434
</h4>
3535
{% endfor %}

phobos/templates/phobos/search_question.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ <h4>Search Results...</h4><hr/>
2828
{% for similar_question in similar_questions %}
2929
{% include 'phobos/question_display.html' with question=similar_question.question assignment_id=similar_question.question.assignment.id course_id=similar_question.question.assignment.course.id%}
3030
{% empty %}
31-
<h4 class="suggestion original-size"> Enter your question in the search bar and hit enter.</h4>
31+
<h4 class="suggestion original-size" style="width:100%;"> Enter your question in the search bar and hit enter.</h4>
3232
{% endfor %}
3333
</ul>
3434

phobos/views.py

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -324,14 +324,14 @@ def create_mcq_text_answer(new_question, answer_content):
324324

325325
# Function to determine which MCQ float answer to create
326326
def create_appropriate_mcq_float_answer(new_question, answer_content, vars_dict):
327-
if not vars_dict:
328-
return create_mcq_float_answer(new_question, answer_content)
329-
elif answer_content.startswith('@{') and answer_content.endswith('}@'):
330-
return create_mcq_variable_float_answer(new_question, answer_content)
327+
if answer_content.startswith('@{') and answer_content.endswith('}@'):
328+
if vars_dict:
329+
return create_mcq_variable_float_answer(new_question, answer_content)
330+
else:
331+
raise ValueError('Expected variable expression but got no variable.')
331332
else:
332333
return create_mcq_float_answer(new_question, answer_content)
333334

334-
335335
def create_expression_answer(new_question, question_answer, answer_unit, answer_preface):
336336
return ExpressionAnswer(question=new_question, content=question_answer,
337337
answer_unit=answer_unit, preface=answer_preface)
@@ -351,10 +351,11 @@ def create_text_answer(new_question):
351351
return TextAnswer(question=new_question, content='')
352352

353353
def create_appropriate_float_answer(new_question, question_answer, answer_unit, answer_preface,vars_dict):
354-
if not vars_dict:
355-
return create_float_answer(new_question, question_answer, answer_unit, answer_preface)
356-
elif question_answer.startswith('@{') and question_answer.endswith('}@'):
357-
return create_variable_float_answer(new_question, question_answer, answer_unit, answer_preface)
354+
if question_answer.startswith('@{') and question_answer.endswith('}@'):
355+
if vars_dict:
356+
return create_variable_float_answer(new_question, question_answer, answer_unit, answer_preface)
357+
else:
358+
raise ValueError('Expected variable expression but got no variable.')
358359
else:
359360
return create_float_answer(new_question, question_answer, answer_unit, answer_preface)
360361

@@ -495,10 +496,11 @@ def create_question(request, assignment_id=None, question_nums_types=None):
495496
# Special handling for float answers to determine the correct QuestionChoices
496497
if answer_type_encoding == "1":
497498
answer = creation_func(new_question, answer_content, vars_dict)
498-
if not vars_dict:
499-
new_question.answer_type = QuestionChoices.MCQ_FLOAT
500-
elif answer_content.startswith('@{') and answer_content.endswith('}@'):
501-
new_question.answer_type = QuestionChoices.MCQ_VARIABLE_FLOAT
499+
if answer_content.startswith('@{') and answer_content.endswith('}@'):
500+
if vars_dict:
501+
new_question.answer_type = QuestionChoices.MCQ_VARIABLE_FLOAT
502+
else:
503+
raise ValueError('Expected variable expression but got no variable.')
502504
else:
503505
new_question.answer_type = QuestionChoices.MCQ_FLOAT
504506
else:
@@ -552,10 +554,11 @@ def create_question(request, assignment_id=None, question_nums_types=None):
552554
if type_int == 1:
553555
answer = creation_func(new_question, question_answer,\
554556
answer_unit, answer_preface, vars_dict)
555-
if not vars_dict:
556-
new_question.answer_type = QuestionChoices.STRUCTURAL_FLOAT
557-
elif question_answer.startswith('@{') and question_answer.endswith('}@'):
558-
new_question.answer_type = QuestionChoices.STRUCTURAL_VARIABLE_FLOAT
557+
if question_answer.startswith('@{') and question_answer.endswith('}@'):
558+
if vars_dict:
559+
new_question.answer_type = QuestionChoices.STRUCTURAL_VARIABLE_FLOAT
560+
else:
561+
raise ValueError('Expected variable expression but got no variable.')
559562
else:
560563
new_question.answer_type = QuestionChoices.STRUCTURAL_FLOAT
561564
else:

0 commit comments

Comments
 (0)