Skip to content

Commit 5dd38b0

Browse files
committed
Merge pull request #1711 from learning-unlimited/class-change-program-module
Turn the class changes request form into a program module
2 parents 5261e6d + 39c24af commit 5dd38b0

File tree

5 files changed

+130
-101
lines changed

5 files changed

+130
-101
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
__author__ = "Individual contributors (see AUTHORS file)"
2+
__date__ = "$DATE$"
3+
__rev__ = "$REV$"
4+
__license__ = "AGPL v.3"
5+
__copyright__ = """
6+
This file is part of the ESP Web Site
7+
Copyright (c) 2007 by the individual contributors
8+
(see AUTHORS file)
9+
10+
The ESP Web Site is free software; you can redistribute it and/or
11+
modify it under the terms of the GNU Affero General Public License
12+
as published by the Free Software Foundation; either version 3
13+
of the License, or (at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public
21+
License along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23+
24+
Contact information:
25+
MIT Educational Studies Program
26+
84 Massachusetts Ave W20-467, Cambridge, MA 02139
27+
Phone: 617-253-4882
28+
29+
Learning Unlimited, Inc.
30+
527 Franklin St, Cambridge, MA 02139
31+
Phone: 617-379-0178
32+
33+
"""
34+
35+
from datetime import datetime
36+
from urllib import quote
37+
38+
from esp.middleware.threadlocalrequest import get_current_request
39+
from esp.program.models import Program, StudentAppResponse, StudentRegistration, RegistrationType
40+
from esp.program.models.class_ import ClassSubject
41+
from esp.program.modules.base import ProgramModuleObj
42+
from esp.program.modules.base import main_call, aux_call, needs_admin, needs_student, meets_grade
43+
from esp.web.util import render_to_response
44+
from esp.users.models import ESPUser
45+
from esp.utils.query_utils import nest_Q
46+
47+
from django import forms
48+
from django.http import HttpResponseRedirect
49+
50+
51+
52+
class ClassChangeRequestModule(ProgramModuleObj):
53+
doc = """Let students enter a lottery to switch classes after the program has started."""
54+
55+
@classmethod
56+
def module_properties(cls):
57+
return {
58+
"admin_title": "Class Change Request",
59+
"link_title": "Class Change Request",
60+
"module_type": "learn",
61+
"required": False,
62+
}
63+
64+
class Meta:
65+
proxy = True
66+
67+
def isCompleted(self):
68+
return StudentRegistration.valid_objects().filter(user=get_current_request().user,
69+
relationship__name="Request").exists()
70+
71+
@main_call
72+
@needs_student
73+
@meets_grade
74+
def classchangerequest(self, request, tl, one, two, module, extra, prog):
75+
timeslots = prog.getTimeSlots()
76+
sections = prog.sections().filter(status=10, meeting_times__isnull=False).distinct()
77+
78+
enrollments = {}
79+
for timeslot in timeslots:
80+
try:
81+
enrollments[timeslot] = ClassSubject.objects.get(nest_Q(StudentRegistration.is_valid_qobject(), 'sections__studentregistration'), sections__studentregistration__relationship__name="Enrolled", sections__studentregistration__user=request.user, sections__meeting_times=timeslot, parent_program=prog)
82+
except ClassSubject.DoesNotExist:
83+
enrollments[timeslot] = None
84+
85+
context = {}
86+
context['timeslots'] = timeslots
87+
context['enrollments'] = enrollments
88+
context['user'] = request.user
89+
if 'success' in request.GET:
90+
context['success'] = True
91+
else:
92+
context['success'] = False
93+
94+
if request.user.isStudent():
95+
sections_by_slot = dict([(timeslot,[(section, 1 == StudentRegistration.valid_objects().filter(user=context['user'], section=section, relationship__name="Request").count()) for section in sections if section.get_meeting_times()[0] == timeslot and section.parent_class.grade_min <= request.user.getGrade(prog) <= section.parent_class.grade_max and section.parent_class not in enrollments.values() and ESPUser.getRankInClass(request.user, section.parent_class) in (5,10)]) for timeslot in timeslots])
96+
else:
97+
sections_by_slot = dict([(timeslot,[(section, False) for section in sections if section.get_meeting_times()[0] == timeslot]) for timeslot in timeslots])
98+
99+
fields = {}
100+
for i, timeslot in enumerate(sections_by_slot.keys()):
101+
choices = [('0', "I'm happy with my current enrollment.")]
102+
initial = '0'
103+
for section in sections_by_slot[timeslot]:
104+
choices.append((section[0].emailcode(), section[0].emailcode()+": "+section[0].title()))
105+
if section[1]:
106+
initial = section[0].emailcode()
107+
fields['timeslot_'+str(i+1)] = forms.ChoiceField(label="Timeslot "+str(i+1)+" ("+timeslot.pretty_time()+")", choices=choices, initial=initial)
108+
109+
form = type('ClassChangeRequestForm', (forms.Form,), fields)
110+
context['form'] = form()
111+
if request.method == "POST":
112+
old_requests = StudentRegistration.valid_objects().filter(user=context['user'], section__parent_class__parent_program=prog, relationship__name="Request")
113+
for r in old_requests:
114+
r.expire()
115+
form = form(request.POST)
116+
if form.is_valid():
117+
for value in form.cleaned_data.values():
118+
section = None
119+
for s in sections:
120+
if s.emailcode() == value:
121+
section = s
122+
break
123+
if not section:
124+
continue
125+
r = StudentRegistration.valid_objects().get_or_create(user=context['user'], section=section, relationship=RegistrationType.objects.get_or_create(name="Request", category="student")[0])[0]
126+
r.save()
127+
128+
return HttpResponseRedirect(request.path.rstrip('/')+'/?success')
129+
else:
130+
return render_to_response(self.baseDir() + 'classchangerequest.html', request, context)

esp/esp/urls.py

-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@
128128

129129

130130
# Program stuff
131-
(r'^(onsite|manage|teach|learn|volunteer)/([-A-Za-z0-9_ ]+)/([-A-Za-z0-9_ ]+)/classchangerequest/?$', 'classchangerequest'),
132131
(r'^(onsite|manage|teach|learn|volunteer|json)/([-A-Za-z0-9_ ]+)/([-A-Za-z0-9_ ]+)/([-A-Za-z0-9_ ]+)/([-A-Za-z0-9_ ]+)/?$', 'program'),
133132
(r'^(onsite|manage|teach|learn|volunteer|json)/([-A-Za-z0-9_ ]+)/([-A-Za-z0-9_ ]+)/([-A-Za-z0-9_ ]+)/?$', 'program'),
134133

esp/esp/users/models/__init__.py

-7
Original file line numberDiff line numberDiff line change
@@ -1047,13 +1047,6 @@ def getRankInClass(student, subject, default=10):
10471047
rank = default
10481048
return rank
10491049

1050-
@staticmethod
1051-
def getRankInSection(student, section, default=10):
1052-
if isinstance(section, int):
1053-
section = ClassSection.objects.get(id=section)
1054-
return getRankInClass(student, section.parent_class, default)
1055-
1056-
10571050
@dispatch.receiver(signals.pre_save, sender=ESPUser,
10581051
dispatch_uid='update_email_save')
10591052
def update_email_save(**kwargs):

esp/esp/web/views/main.py

-93
Original file line numberDiff line numberDiff line change
@@ -110,99 +110,6 @@ def program(request, tl, one, two, module, extra = None):
110110

111111
raise Http404
112112

113-
def classchangerequest(request, tl, one, two):
114-
from esp.program.models import Program, StudentAppResponse, StudentRegistration, RegistrationType
115-
from esp.program.models.class_ import ClassSubject
116-
from urllib import quote
117-
try:
118-
prog = Program.by_prog_inst(one, two)
119-
except Program.DoesNotExist:
120-
raise Http404("Program not found.")
121-
122-
if tl != "learn":
123-
raise Http404
124-
125-
if not request.user or not request.user.is_authenticated():
126-
return HttpResponseRedirect('%s?%s=%s' % (LOGIN_URL, REDIRECT_FIELD_NAME, quote(request.get_full_path()) ))
127-
128-
if not request.user.isStudent() and not request.user.isAdmin(prog):
129-
allowed_student_types = Tag.getTag("allowed_student_types", prog, default='')
130-
matching_user_types = any(x in request.user.groups.all().values_list("name",flat=True) for x in allowed_student_types.split(","))
131-
if not matching_user_types:
132-
return render_to_response('errors/program/notastudent.html', request, {})
133-
134-
errorpage = 'errors/program/wronggrade.html'
135-
136-
cur_grade = request.user.getGrade(prog)
137-
if (not Permission.user_has_perm(request.user, 'GradeOverride', program=prog) and (cur_grade != 0 and (cur_grade < prog.grade_min or cur_grade > prog.grade_max))):
138-
return render_to_response(errorpage, request, {'yog': request.user.getYOG(prog)})
139-
140-
setattr(request, "program", prog)
141-
setattr(request, "tl", tl)
142-
setattr(request, "module", "classchangerequest")
143-
144-
from django import forms
145-
from datetime import datetime
146-
from esp.utils.scheduling import getRankInClass
147-
148-
timeslots = prog.getTimeSlots()
149-
sections = prog.sections().filter(status=10, meeting_times__isnull=False).distinct()
150-
151-
enrollments = {}
152-
for timeslot in timeslots:
153-
try:
154-
enrollments[timeslot] = ClassSubject.objects.get(nest_Q(StudentRegistration.is_valid_qobject(), 'sections__studentregistration'), sections__studentregistration__relationship__name="Enrolled", sections__studentregistration__user=request.user, sections__meeting_times=timeslot, parent_program=prog)
155-
except ClassSubject.DoesNotExist:
156-
enrollments[timeslot] = None
157-
158-
context = {}
159-
context['timeslots'] = timeslots
160-
context['enrollments'] = enrollments
161-
context['user'] = request.user
162-
if 'success' in request.GET:
163-
context['success'] = True
164-
else:
165-
context['success'] = False
166-
167-
if request.user.isStudent():
168-
sections_by_slot = dict([(timeslot,[(section, 1 == StudentRegistration.valid_objects().filter(user=context['user'], section=section, relationship__name="Request").count()) for section in sections if section.get_meeting_times()[0] == timeslot and section.parent_class.grade_min <= request.user.getGrade(prog) <= section.parent_class.grade_max and section.parent_class not in enrollments.values() and getRankInClass(request.user, section) in (5,10)]) for timeslot in timeslots])
169-
else:
170-
sections_by_slot = dict([(timeslot,[(section, False) for section in sections if section.get_meeting_times()[0] == timeslot]) for timeslot in timeslots])
171-
172-
fields = {}
173-
for i, timeslot in enumerate(sections_by_slot.keys()):
174-
choices = [('0', "I'm happy with my current enrollment.")]
175-
initial = '0'
176-
for section in sections_by_slot[timeslot]:
177-
choices.append((section[0].emailcode(), section[0].emailcode()+": "+section[0].title()))
178-
if section[1]:
179-
initial = section[0].emailcode()
180-
fields['timeslot_'+str(i+1)] = forms.ChoiceField(label="Timeslot "+str(i+1)+" ("+timeslot.pretty_time()+")", choices=choices, initial=initial)
181-
182-
form = type('ClassChangeRequestForm', (forms.Form,), fields)
183-
context['form'] = form()
184-
if request.method == "POST":
185-
old_requests = StudentRegistration.valid_objects().filter(user=context['user'], section__parent_class__parent_program=prog, relationship__name="Request")
186-
for r in old_requests:
187-
r.expire()
188-
form = form(request.POST)
189-
if form.is_valid():
190-
for value in form.cleaned_data.values():
191-
section = None
192-
for s in sections:
193-
if s.emailcode() == value:
194-
section = s
195-
break
196-
if not section:
197-
continue
198-
r = StudentRegistration.objects.get_or_create(user=context['user'], section=section, relationship=RegistrationType.objects.get_or_create(name="Request", category="student")[0])[0]
199-
r.save()
200-
201-
return HttpResponseRedirect(request.path.rstrip('/')+'/?success')
202-
else:
203-
return render_to_response('program/classchangerequest.html', request, context)
204-
205-
206113
def archives(request, selection, category = None, options = None):
207114
""" Return a page with class archives """
208115

0 commit comments

Comments
 (0)