Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions coderdojochi/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,8 @@ class CourseAdmin(ImportExportMixin, ImportExportActionModelAdmin):
"description",
]

filter_horizontal = ["prerequisite"]

prepopulated_fields = {
"slug": ("title",),
}
Expand Down
18 changes: 18 additions & 0 deletions coderdojochi/migrations/0038_course_prerequisite.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.1.5 on 2021-01-30 19:37

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('coderdojochi', '0037_auto_20210118_1808'),
]

operations = [
migrations.AddField(
model_name='course',
name='prerequisite',
field=models.ManyToManyField(blank=True, limit_choices_to={'is_active': True}, to='coderdojochi.Course'),
),
]
2 changes: 2 additions & 0 deletions coderdojochi/models/course.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class Course(CommonInfo):
default=True,
)

prerequisite = models.ManyToManyField("self", symmetrical=False, blank=True, limit_choices_to={"is_active": True})

def __str__(self):
if self.code:
return f"{self.code} | {self.title}"
Expand Down
17 changes: 16 additions & 1 deletion coderdojochi/models/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.urls.base import reverse
from django.utils import formats
from django.utils import formats, timezone
from django.utils.functional import cached_property

from .common import CommonInfo
Expand Down Expand Up @@ -307,6 +307,21 @@ def get_mentor_capacity(self):
else:
return int(self.capacity / 2)

def get_course_prerequisites_needed(self, student):
from .order import Order

student_previous_orders = Order.objects.filter(
student=student,
session__start_date__lt=timezone.now(),
session__course__id__in=self.course.prerequisite.values_list("id"),
).exclude(check_in=None)

student_prerequisites_needed = self.course.prerequisite.exclude(
id__in=student_previous_orders.values("session__course__id")
).distinct("id")

return student_prerequisites_needed


class PartnerPasswordAccess(CommonInfo):
from .user import CDCUser
Expand Down
12 changes: 12 additions & 0 deletions coderdojochi/templates/guardian/session_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ <h3>How To Join Online Class</h3>
<strong>Password:</strong> {{ object.online_video_meeting_password }}</p>

{% endif %}
{% if object.course.prerequisite.all.count > 0 %}
<div class="grid-x">
<div class="cell medium-6">
<h6 class="text-uppercase margin-top-1">Prerequisites</h6>
<ul class="list-plus">
{% for prerequisite in object.course.prerequisite.all %}
<li>{{prerequisite.code}}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>

<div class="cell medium-4 bg-primary padding-2">
Expand Down
13 changes: 13 additions & 0 deletions coderdojochi/templates/public/session_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ <h3>Technical Requirements</h3>

<p><strong>Microphone and Speakers:</strong> We highly recommend headphones with a built-in microphone, however any microphone and speakers will work in a quiet room.</p>
{% endif %}

{% if object.course.prerequisite.all.count > 0 %}
<div class="grid-x">
<div class="cell medium-6">
<h6 class="text-uppercase margin-top-1">Prerequisites</h6>
<ul class="list-plus">
{% for prerequisite in object.course.prerequisite.all %}
<li>{{prerequisite.code}}</li>
{% endfor %}
</ul>
</div>
</div>
{% endif %}
</div>

<div class="cell medium-4 bg-primary padding-2">
Expand Down
38 changes: 38 additions & 0 deletions coderdojochi/templatetags/coderdojochi_extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def student_register_link(context, student, session):
button_additional_attributes = ""
button_msg = "Enroll"
button_href = f"href={url}"
prerequisite_needed_buttons = ""

if orders.count():
button_modifier = "tertiary"
Expand Down Expand Up @@ -73,8 +74,45 @@ def student_register_link(context, student, session):
message = f"Sorry, this class is limited to {session.gender_limitation}s this time around."
button_href = f'data-trigger="hover" data-placement="top" data-toggle="popover" title="" data-content="{message}" data-original-title="{title}" '

else:
# Get the prerequisites the student still needs to take in order to attend the current session.
course_prerequisites_needed = list(session.get_course_prerequisites_needed(student).values("id", "code"))

if len(course_prerequisites_needed) > 0:
# When there are outstanding prerequisites, add buttons to navigate to future sessions (if available; disabled otherwise).
for course_prerequisite in course_prerequisites_needed:
needed_course_id = course_prerequisite["id"]
upcoming_prerequisite_sessions = context["upcoming_prerequisite_sessions"]

if needed_course_id in upcoming_prerequisite_sessions:
session_id = upcoming_prerequisite_sessions[needed_course_id]["id"]
url = reverse("session-detail", kwargs={"pk": session_id})
button_tag = "a"
button_href = f"href='{url}'"
title = "There is an upcoming session for this prerequisite."
button_additional_attributes = f"data-tooltip aria-haspopup='true' title='{title}'"
else:
button_tag = "span"
button_href = ""
title = "No upcoming session for this prerequisite."
button_additional_attributes = f"data-tooltip aria-haspopup='true' title='{title}' disabled"

button_modifier = "has-tip"
button_msg = course_prerequisite["code"]
prerequisite_needed_buttons += f"<{button_tag} {button_href} class='button {button_modifier}' {button_additional_attributes}>{button_msg}</{button_tag}>"

button_tag = "span"
button_modifier = "has-tip"
button_additional_attributes = "disabled"
button_msg = "Enroll"
title = f"Sorry, this class has prerequisites that have not been met by {student.first_name} yet."
button_href = f'data-tooltip aria-haspopup="true" title="{title}"'

form = f"<{button_tag} {button_href} class='button small {button_modifier}' {button_additional_attributes}>{button_msg}</{button_tag}>"

if prerequisite_needed_buttons:
form += f"<br /><div class='tiny button-group align-right'>{prerequisite_needed_buttons}</div>"

return Template(form).render(context)


Expand Down
15 changes: 15 additions & 0 deletions coderdojochi/views/guardian/sessions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.views.generic import DetailView

from ...models import Guardian, Mentor, MentorOrder, Session
Expand All @@ -18,4 +19,18 @@ def get_context_data(self, **kwargs):
id__in=MentorOrder.objects.filter(session=self.object, is_active=True).values("mentor__id")
)

upcoming_prerequisite_sessions = (
Session.objects.filter(
is_active=True,
is_public=True,
start_date__gte=timezone.now(),
course__id__in=self.object.course.prerequisite.values_list("id"),
)
.distinct("course__code")
.order_by("course__code", "start_date")
.values("id", "course__id", "course__code")
)

context["upcoming_prerequisite_sessions"] = {p["course__id"]: p for p in list(upcoming_prerequisite_sessions)}

return context
64 changes: 49 additions & 15 deletions fixtures/05-coderdojochi.course.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 18,
"is_active": true
"is_active": true,
"prerequisite": []
}
},
{
Expand All @@ -30,7 +31,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 18,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -47,7 +49,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 18,
"is_active": true
"is_active": true,
"prerequisite": []
}
},
{
Expand All @@ -56,15 +59,16 @@
"fields": {
"created_at": "2017-03-30T16:53:37.340Z",
"updated_at": "2017-04-10T23:07:51.518Z",
"code": "JS 101",
"code": "JS 1",
"course_type": "WE",
"title": "Drawing with Javascript",
"slug": "drawing-with-javascript",
"description": "<p>Ever wanted to start building your own game but didn't know where to start? Ever thought it would be cool to have a computer draw a picture or create an animation for you?</p>\r\n\r\n<p>By the end of the session, students will have the understanding of Canvas using the Javascript programming language. Students will learn how to break down tasks and objects to understand how they are built.</p>",
"duration": "03:00:00",
"minimum_age": 9,
"maximum_age": 18,
"is_active": true
"is_active": true,
"prerequisite": []
}
},
{
Expand All @@ -81,7 +85,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -98,7 +103,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -115,7 +121,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -132,7 +139,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -149,7 +157,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -166,7 +175,8 @@
"duration": "03:00:00",
"minimum_age": 9,
"maximum_age": 17,
"is_active": true
"is_active": true,
"prerequisite": []
}
},
{
Expand All @@ -183,7 +193,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -200,7 +211,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -217,7 +229,8 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
Expand All @@ -234,7 +247,28 @@
"duration": "03:00:00",
"minimum_age": 7,
"maximum_age": 17,
"is_active": true
"is_active": false,
"prerequisite": []
}
},
{
"model": "coderdojochi.course",
"pk": 15,
"fields": {
"created_at": "2021-02-18T04:07:02.925Z",
"updated_at": "2021-02-18T04:07:02.925Z",
"code": "JS 2",
"course_type": "WE",
"title": "Advanced Drawing With Javascript",
"slug": "advanced-drawing-javascript",
"description": "<p>Ever wanted to start building your own game but didn't know where to start? Ever thought it would be cool to have a computer draw a picture or create an animation for you?</p>\r\n\r\n<p>By the end of the session, students will have the understanding of Canvas using the Javascript programming language. Students will learn how to break down tasks and objects to understand how they are built.</p>",
"duration": "03:00:00",
"minimum_age": 10,
"maximum_age": 18,
"is_active": true,
"prerequisite": [
4
]
}
}
]
Loading