3 # Copyright 2009 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Views for Student Project.
21 '"Lennard de Rijk" <ljvderijk@gmail.com>',
28 from django
import forms
29 from django
import http
31 from soc
.logic
import cleaning
32 from soc
.logic
import dicts
33 from soc
.logic
.models
import mentor
as mentor_logic
34 from soc
.logic
.models
.organization
import logic
as org_logic
35 from soc
.logic
.models
.org_admin
import logic
as org_admin_logic
36 from soc
.logic
.models
import student
as student_logic
37 from soc
.logic
.models
.student_project
import logic
as project_logic
38 from soc
.views
import out_of_band
39 from soc
.views
.helper
import access
40 from soc
.views
.helper
import decorators
41 from soc
.views
.helper
import dynaform
42 from soc
.views
.helper
import forms
as forms_helper
43 from soc
.views
.helper
import lists
44 from soc
.views
.helper
import params
as params_helper
45 from soc
.views
.helper
import redirects
46 from soc
.views
.helper
import responses
47 from soc
.views
.helper
import widgets
48 from soc
.views
.models
import base
49 from soc
.views
.models
import organization
as org_view
51 import soc
.logic
.models
.student_project
54 class View(base
.View
):
55 """View methods for the Student Project model.
58 def __init__(self
, params
=None):
59 """Defines the fields and methods required for the base View class
60 to provide the user with list, public, create, edit and delete views.
63 params: a dict with params for this View
66 rights
= access
.Checker(params
)
67 rights
['any_access'] = ['allow']
68 rights
['create'] = ['checkIsDeveloper']
69 rights
['edit'] = ['checkIsDeveloper']
70 rights
['delete'] = ['checkIsDeveloper']
71 rights
['show'] = ['allow']
72 rights
['list'] = ['checkIsDeveloper']
73 rights
['manage'] = [('checkHasActiveRoleForScope',
75 ('checkStudentProjectHasStatus', [['accepted', 'mid_term_passed']])]
76 rights
['manage_overview'] = [('checkHasActiveRoleForScope',
78 # TODO lack of better name here!
79 rights
['st_edit'] = ['checkIsMyStudentProject',
80 ('checkStudentProjectHasStatus',
81 [['accepted', 'mid_term_passed', 'passed']])
85 new_params
['logic'] = soc
.logic
.models
.student_project
.logic
86 new_params
['rights'] = rights
87 new_params
['name'] = "Student Project"
88 new_params
['url_name'] = "student_project"
89 new_params
['sidebar_grouping'] = 'Students'
91 new_params
['scope_view'] = org_view
92 new_params
['scope_redirect'] = redirects
.getCreateRedirect
94 new_params
['no_create_with_key_fields'] = True
96 new_params
['extra_dynaexclude'] = ['program', 'status', 'link_id',
97 'mentor', 'additional_mentors',
100 new_params
['create_extra_dynaproperties'] = {
101 'scope_path': forms
.CharField(widget
=forms
.HiddenInput
,
103 'public_info': forms
.fields
.CharField(required
=True,
104 widget
=widgets
.FullTinyMCE(attrs
={'rows': 25, 'cols': 100})),
105 'student_id': forms
.CharField(label
='Student Link ID',
107 'mentor_id': forms
.CharField(label
='Mentor Link ID',
109 'clean_abstract': cleaning
.clean_content_length('abstract'),
110 'clean_public_info': cleaning
.clean_html_content('public_info'),
111 'clean_student': cleaning
.clean_link_id('student'),
112 'clean_mentor': cleaning
.clean_link_id('mentor'),
113 'clean_additional_info': cleaning
.clean_url('additional_info'),
114 'clean_feed_url': cleaning
.clean_feed_url
,
115 'clean': cleaning
.validate_student_project('scope_path',
116 'mentor_id', 'student_id')
119 new_params
['edit_extra_dynaproperties'] = {
120 'link_id': forms
.CharField(widget
=forms
.HiddenInput
),
124 (r
'^%(url_name)s/(?P<access_type>manage_overview)/%(scope)s$',
125 'soc.views.models.%(module_name)s.manage_overview',
126 'Overview of %(name_plural)s to Manage for'),
127 (r
'^%(url_name)s/(?P<access_type>manage)/%(key_fields)s$',
128 'soc.views.models.%(module_name)s.manage',
130 (r
'^%(url_name)s/(?P<access_type>st_edit)/%(key_fields)s$',
131 'soc.views.models.%(module_name)s.st_edit',
135 new_params
['extra_django_patterns'] = patterns
137 new_params
['edit_template'] = 'soc/student_project/edit.html'
138 new_params
['manage_template'] = 'soc/student_project/manage.html'
140 params
= dicts
.merge(params
, new_params
)
142 super(View
, self
).__init
__(params
=params
)
144 # create the form that students will use to edit their projects
146 'public_info': forms
.fields
.CharField(required
=True,
147 widget
=widgets
.FullTinyMCE(attrs
={'rows': 25, 'cols': 100})),
148 'clean_abstract': cleaning
.clean_content_length('abstract'),
149 'clean_public_info': cleaning
.clean_html_content('public_info'),
150 'clean_additional_info': cleaning
.clean_url('additional_info'),
151 'clean_feed_url': cleaning
.clean_feed_url
,
154 student_edit_form
= dynaform
.newDynaForm(
155 dynabase
= self
._params
['dynabase'],
156 dynamodel
= self
._params
['logic'].getModel(),
157 dynaexclude
= self
._params
['create_dynaexclude'],
158 dynaproperties
= dynaproperties
,
161 self
._params
['student_edit_form'] = student_edit_form
164 def _editGet(self
, request
, entity
, form
):
165 """See base.View._editGet().
168 form
.fields
['link_id'].initial
= entity
.link_id
169 form
.fields
['student_id'].initial
= entity
.student
.link_id
170 form
.fields
['mentor_id'].initial
= entity
.mentor
.link_id
172 return super(View
, self
)._editGet
(request
, entity
, form
)
174 def _editPost(self
, request
, entity
, fields
):
175 """See base.View._editPost().
179 fields
['link_id'] = 't%i' % (int(time
.time()*100))
181 fields
['link_id'] = entity
.link_id
183 # fill in the scope via call to super
184 super(View
, self
)._editPost
(request
, entity
, fields
)
186 # editing a project so set the program, student and mentor field
188 organization
= entity
.scope
190 organization
= fields
['scope']
192 fields
['program'] = organization
.scope
194 filter = {'scope': fields
['program'],
195 'link_id': fields
['student_id']}
196 fields
['student'] = student_logic
.logic
.getForFields(filter, unique
=True)
198 filter = {'scope': organization
,
199 'link_id': fields
['mentor_id'],
201 fields
['mentor'] = mentor_logic
.logic
.getForFields(filter, unique
=True)
203 @decorators.merge_params
204 @decorators.check_access
205 def manage(self
, request
, access_type
,
206 page_name
=None, params
=None, **kwargs
):
207 """View that allows Organization Admins to manage their Student Projects.
209 For params see base.View().public()
213 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
214 except out_of_band
.Error
, error
:
215 return responses
.errorResponse(
216 error
, request
, template
=params
['error_public'])
218 get_dict
= request
.GET
220 if 'remove' in get_dict
:
221 # get the mentor to remove
222 fields
= {'link_id': get_dict
['remove'],
223 'scope': entity
.scope
}
224 mentor
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
226 additional_mentors
= entity
.additional_mentors
227 if additional_mentors
and mentor
.key() in additional_mentors
:
228 # remove the mentor from the additional mentors list
229 additional_mentors
.remove(mentor
.key())
230 fields
= {'additional_mentors': additional_mentors
}
231 project_logic
.updateEntityProperties(entity
, fields
)
233 # redirect to the same page without GET arguments
234 redirect
= request
.path
235 return http
.HttpResponseRedirect(redirect
)
237 template
= params
['manage_template']
239 # get the context for this webpage
240 context
= responses
.getUniversalContext(request
)
241 responses
.useJavaScript(context
, params
['js_uses_all'])
242 context
['page_name'] = "%s '%s' from %s" % (page_name
, entity
.title
,
243 entity
.student
.name())
244 context
['entity'] = entity
246 # get all mentors for this organization
247 fields
= {'scope': entity
.scope
,
249 mentors
= mentor_logic
.logic
.getForFields(fields
)
251 choices
= [(mentor
.link_id
,'%s (%s)' %(mentor
.name(), mentor
.link_id
))
252 for mentor
in mentors
]
254 # create the form that org admins will use to reassign a mentor
256 {'name': 'mentor_id',
257 'base': forms
.ChoiceField
,
258 'label': 'Primary Mentor',
260 'passthrough': ['required', 'choices', 'label'],
264 dynaproperties
= params_helper
.getDynaFields(dynafields
)
266 mentor_edit_form
= dynaform
.newDynaForm(
267 dynabase
= params
['dynabase'],
268 dynaproperties
= dynaproperties
,
271 params
['mentor_edit_form'] = mentor_edit_form
273 additional_mentors
= entity
.additional_mentors
275 # we want to show the names of the additional mentors in the context
276 # therefore they need to be resolved to entities first
277 additional_mentors_context
= []
279 for mentor_key
in additional_mentors
:
280 mentor_entity
= mentor_logic
.logic
.getFromKeyName(
281 mentor_key
.id_or_name())
282 additional_mentors_context
.append(mentor_entity
)
284 context
['additional_mentors'] = additional_mentors_context
286 # all mentors who are not already an additional mentor or
287 # the primary mentor are allowed to become an additional mentor
288 possible_additional_mentors
= [m
for m
in mentors
if
289 (m
.key() not in additional_mentors
) and (m
.key() != entity
.mentor
.key())]
291 # create the information to be shown on the additional mentor form
292 additional_mentor_choices
= [
293 (mentor
.link_id
,'%s (%s)' %(mentor
.name(), mentor
.link_id
))
294 for mentor
in possible_additional_mentors
]
297 {'name': 'mentor_id',
298 'base': forms
.ChoiceField
,
299 'label': 'Co-Mentor',
301 'passthrough': ['required', 'choices', 'label'],
302 'choices': additional_mentor_choices
,
305 dynaproperties
= params_helper
.getDynaFields(dynafields
)
307 additional_mentor_form
= dynaform
.newDynaForm(
308 dynabase
= params
['dynabase'],
309 dynaproperties
= dynaproperties
,
312 params
['additional_mentor_form'] = additional_mentor_form
315 return self
.managePost(request
, template
, context
, params
, entity
,
318 return self
.manageGet(request
, template
, context
, params
, entity
,
321 def manageGet(self
, request
, template
, context
, params
, entity
, **kwargs
):
322 """Handles the GET request for the project's manage page.
325 template: the template used for this view
326 entity: the student project entity
327 rest: see base.View.public()
330 # populate form with the current mentor
331 initial
= {'mentor_id': entity
.mentor
.link_id
}
332 context
['mentor_edit_form'] = params
['mentor_edit_form'](initial
=initial
)
334 context
['additional_mentor_form'] = params
['additional_mentor_form']()
336 return responses
.respond(request
, template
, context
)
338 def managePost(self
, request
, template
, context
, params
, entity
, **kwargs
):
339 """Handles the POST request for the project's manage page.
342 template: the template used for this view
343 entity: the student project entity
344 rest: see base.View.public()
347 post_dict
= request
.POST
349 if 'set_mentor' in post_dict
:
350 form
= params
['mentor_edit_form'](post_dict
)
351 return self
._manageSetMentor
(request
, template
, context
, params
, entity
,
353 elif 'add_additional_mentor' in post_dict
:
354 form
= params
['additional_mentor_form'](post_dict
)
355 return self
._manageAddAdditionalMentor
(request
, template
, context
,
356 params
, entity
, form
)
358 # unexpected error return the normal page
359 logging
.warning('Unexpected POST data found')
360 return self
.manageGet(request
, template
, context
, params
, entity
)
362 def _manageSetMentor(self
, request
, template
, context
, params
, entity
, form
):
363 """Handles the POST request for changing a Projects's mentor.
366 template: the template used for this view
367 entity: the student project entity
368 form: instance of the form used to set the mentor
369 rest: see base.View.public()
372 if not form
.is_valid():
373 context
['mentor_edit_form'] = form
375 # add an a fresh additional mentors form
376 context
['additional_mentor_form'] = params
['additional_mentor_form']()
378 return responses
.respond(request
, template
, context
)
380 _
, fields
= forms_helper
.collectCleanedFields(form
)
382 # get the mentor from the form
383 fields
= {'link_id': fields
['mentor_id'],
384 'scope': entity
.scope
,
386 mentor
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
388 # update the project with the assigned mentor
389 fields
= {'mentor': mentor
}
391 additional_mentors
= entity
.additional_mentors
393 if additional_mentors
and mentor
.key() in additional_mentors
:
394 # remove the mentor that is now becoming the primary mentor
395 additional_mentors
.remove(mentor
.key())
396 fields
['additional_mentors'] = additional_mentors
398 # update the project with the new mentor and possible
399 # new set of additional mentors
400 project_logic
.updateEntityProperties(entity
, fields
)
402 # redirect to the same page
403 redirect
= request
.path
404 return http
.HttpResponseRedirect(redirect
)
406 def _manageAddAdditionalMentor(self
, request
, template
, context
, params
, entity
, form
):
407 """Handles the POST request for changing a Projects's additional mentors.
410 template: the template used for this view
411 entity: the student project entity
412 form: instance of the form used to add an additional mentor
413 rest: see base.View.public()
416 if not form
.is_valid():
417 context
['additional_mentor_form'] = form
419 # add a fresh edit mentor form
420 initial
= {'mentor_id': entity
.mentor
.link_id
}
421 context
['mentor_edit_form'] = params
['mentor_edit_form'](initial
=initial
)
423 return responses
.respond(request
, template
, context
)
425 _
, fields
= forms_helper
.collectCleanedFields(form
)
427 # get the mentor from the form
428 fields
= {'link_id': fields
['mentor_id'],
429 'scope': entity
.scope
,
431 mentor
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
433 # add this mentor to the additional mentors
434 if not entity
.additional_mentors
:
435 additional_mentors
= [mentor
.key()]
437 additional_mentors
= additional_mentors
.append(mentor
.key())
439 fields
= {'additional_mentors': additional_mentors
}
440 project_logic
.updateEntityProperties(entity
, fields
)
442 # redirect to the same page
443 redirect
= request
.path
444 return http
.HttpResponseRedirect(redirect
)
446 @decorators.merge_params
447 @decorators.check_access
448 def manageOverview(self
, request
, access_type
,
449 page_name
=None, params
=None, **kwargs
):
450 """View that allows Organization Admins to see an overview of
451 their Organization's Student Projects.
453 For params see base.View().public()
456 # make sure the organization exists
457 org_entity
= org_logic
.getFromKeyNameOr404(kwargs
['scope_path'])
458 fields
= {'scope': org_entity
}
460 # get the context for this webpage
461 context
= responses
.getUniversalContext(request
)
462 responses
.useJavaScript(context
, params
['js_uses_all'])
463 context
['page_name'] = '%s %s' % (page_name
, org_entity
.name
)
465 list_params
= params
.copy()
467 #list all active projects
468 fields
['status'] = ['accepted', 'mid_term_passed']
469 active_params
= list_params
.copy()
470 active_params
['list_description'] = \
471 'List of all active %(name_plural)s' % list_params
472 active_params
['list_action'] = (redirects
.getManageRedirect
, list_params
)
474 active_list
= lists
.getListContent(
475 request
, active_params
, fields
, idx
=0)
477 # list all failed projects
478 fields
['status'] = ['mid_term_failed', 'final_failed']
479 failed_params
= list_params
.copy()
480 failed_params
['list_description'] = ('List of all failed %(name_plural)s, '
481 'these cannot be managed.') % list_params
482 failed_params
['list_action'] = (redirects
.getPublicRedirect
, list_params
)
484 failed_list
= lists
.getListContent(
485 request
, failed_params
, fields
, idx
=1, need_content
=True)
487 #list all completed projects
488 fields
['status'] = ['passed']
489 completed_params
= list_params
.copy()
490 completed_params
['list_description'] = ('List of %(name_plural)s that have '
491 'successfully completed the program, '
492 'these cannot be managed.' % list_params
)
493 completed_params
['list_action'] = (redirects
.getPublicRedirect
, list_params
)
495 completed_list
= lists
.getListContent(
496 request
, completed_params
, fields
, idx
=2, need_content
=True)
498 # always show the list with active projects
499 content
= [active_list
]
501 if failed_list
!= None:
502 # do not show empty failed list
503 content
.append(failed_list
)
505 if completed_list
!= None:
506 # do not show empty completed list
507 content
.append(completed_list
)
509 # call the _list method from base to display the list
510 return self
._list
(request
, list_params
, content
,
511 context
['page_name'], context
)
513 @decorators.merge_params
514 @decorators.check_access
515 def stEdit(self
, request
, access_type
,
516 page_name
=None, params
=None, **kwargs
):
517 """View that allows students to edit information about their project.
519 For params see base.View().public()
523 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
524 except out_of_band
.Error
, error
:
525 return responses
.errorResponse(
526 error
, request
, template
=params
['error_public'])
528 # get the context for this webpage
529 context
= responses
.getUniversalContext(request
)
530 responses
.useJavaScript(context
, params
['js_uses_all'])
531 context
['page_name'] = page_name
532 # cancel should go to the public view
533 params
['edit_cancel_redirect'] = redirects
.getPublicRedirect(entity
, params
)
536 return self
.stEditPost(request
, context
, params
, entity
, **kwargs
)
538 return self
.stEditGet(request
, context
, params
, entity
, **kwargs
)
540 def stEditGet(self
, request
, context
, params
, entity
, **kwargs
):
541 """Handles the GET request for the student's edit page.
544 entity: the student project entity
545 rest: see base.View.public()
548 # populate form with the existing entity
549 form
= params
['student_edit_form'](instance
=entity
)
551 return self
._constructResponse
(request
, entity
, context
, form
, params
)
553 def stEditPost(self
, request
, context
, params
, entity
, **kwargs
):
554 """Handles the POST request for the student's edit page.
557 entity: the student project entity
558 rest: see base.View.public()
561 form
= params
['student_edit_form'](request
.POST
)
563 if not form
.is_valid():
564 return self
._constructResponse
(request
, entity
, context
, form
, params
)
566 _
, fields
= forms_helper
.collectCleanedFields(form
)
568 project_logic
.updateEntityProperties(entity
, fields
)
570 return self
.stEditGet(request
, context
, params
, entity
, **kwargs
)
575 admin
= decorators
.view(view
.admin
)
576 create
= decorators
.view(view
.create
)
577 delete
= decorators
.view(view
.delete
)
578 edit
= decorators
.view(view
.edit
)
579 list = decorators
.view(view
.list)
580 manage
= decorators
.view(view
.manage
)
581 manage_overview
= decorators
.view(view
.manageOverview
)
582 public
= decorators
.view(view
.public
)
583 st_edit
= decorators
.view(view
.stEdit
)
584 export
= decorators
.view(view
.export
)
585 pick
= decorators
.view(view
.pick
)