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 def _public(self
, request
, entity
, context
):
204 """Adds the names of all additional mentors to the context.
206 For params see base.View._public()
209 additional_mentors
= entity
.additional_mentors
211 if not additional_mentors
:
212 context
['additional_mentors'] = []
216 for mentor_key
in additional_mentors
:
217 additional_mentor
= mentor_logic
.logic
.getFromKeyName(
218 mentor_key
.id_or_name())
219 mentor_names
.append(additional_mentor
.name())
221 context
['additional_mentors'] = ', '.join(mentor_names
)
223 @decorators.merge_params
224 @decorators.check_access
225 def manage(self
, request
, access_type
,
226 page_name
=None, params
=None, **kwargs
):
227 """View that allows Organization Admins to manage their Student Projects.
229 For params see base.View().public()
233 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
234 except out_of_band
.Error
, error
:
235 return responses
.errorResponse(
236 error
, request
, template
=params
['error_public'])
238 get_dict
= request
.GET
240 if 'remove' in get_dict
:
241 # get the mentor to remove
242 fields
= {'link_id': get_dict
['remove'],
243 'scope': entity
.scope
}
244 mentor
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
246 additional_mentors
= entity
.additional_mentors
247 # pylint: disable-msg=E1103
248 if additional_mentors
and mentor
.key() in additional_mentors
:
249 # remove the mentor from the additional mentors list
250 additional_mentors
.remove(mentor
.key())
251 fields
= {'additional_mentors': additional_mentors
}
252 project_logic
.updateEntityProperties(entity
, fields
)
254 # redirect to the same page without GET arguments
255 redirect
= request
.path
256 return http
.HttpResponseRedirect(redirect
)
258 template
= params
['manage_template']
260 # get the context for this webpage
261 context
= responses
.getUniversalContext(request
)
262 responses
.useJavaScript(context
, params
['js_uses_all'])
263 context
['page_name'] = "%s '%s' from %s" % (page_name
, entity
.title
,
264 entity
.student
.name())
265 context
['entity'] = entity
267 # get all mentors for this organization
268 fields
= {'scope': entity
.scope
,
270 mentors
= mentor_logic
.logic
.getForFields(fields
)
272 choices
= [(mentor
.link_id
,'%s (%s)' %(mentor
.name(), mentor
.link_id
))
273 for mentor
in mentors
]
275 # create the form that org admins will use to reassign a mentor
277 {'name': 'mentor_id',
278 'base': forms
.ChoiceField
,
279 'label': 'Primary Mentor',
281 'passthrough': ['required', 'choices', 'label'],
285 dynaproperties
= params_helper
.getDynaFields(dynafields
)
287 mentor_edit_form
= dynaform
.newDynaForm(
288 dynabase
= params
['dynabase'],
289 dynaproperties
= dynaproperties
,
292 params
['mentor_edit_form'] = mentor_edit_form
294 additional_mentors
= entity
.additional_mentors
296 # we want to show the names of the additional mentors in the context
297 # therefore they need to be resolved to entities first
298 additional_mentors_context
= []
300 for mentor_key
in additional_mentors
:
301 mentor_entity
= mentor_logic
.logic
.getFromKeyName(
302 mentor_key
.id_or_name())
303 additional_mentors_context
.append(mentor_entity
)
305 context
['additional_mentors'] = additional_mentors_context
307 # all mentors who are not already an additional mentor or
308 # the primary mentor are allowed to become an additional mentor
309 possible_additional_mentors
= [m
for m
in mentors
if
310 (m
.key() not in additional_mentors
)
311 and (m
.key() != entity
.mentor
.key())]
313 # create the information to be shown on the additional mentor form
314 additional_mentor_choices
= [
315 (mentor
.link_id
,'%s (%s)' %(mentor
.name(), mentor
.link_id
))
316 for mentor
in possible_additional_mentors
]
319 {'name': 'mentor_id',
320 'base': forms
.ChoiceField
,
321 'label': 'Co-Mentor',
323 'passthrough': ['required', 'choices', 'label'],
324 'choices': additional_mentor_choices
,
327 dynaproperties
= params_helper
.getDynaFields(dynafields
)
329 additional_mentor_form
= dynaform
.newDynaForm(
330 dynabase
= params
['dynabase'],
331 dynaproperties
= dynaproperties
,
334 params
['additional_mentor_form'] = additional_mentor_form
337 return self
.managePost(request
, template
, context
, params
, entity
,
340 return self
.manageGet(request
, template
, context
, params
, entity
,
343 def manageGet(self
, request
, template
, context
, params
, entity
, **kwargs
):
344 """Handles the GET request for the project's manage page.
347 template: the template used for this view
348 entity: the student project entity
349 rest: see base.View.public()
352 # populate form with the current mentor
353 initial
= {'mentor_id': entity
.mentor
.link_id
}
354 context
['mentor_edit_form'] = params
['mentor_edit_form'](initial
=initial
)
356 context
['additional_mentor_form'] = params
['additional_mentor_form']()
358 return responses
.respond(request
, template
, context
)
360 def managePost(self
, request
, template
, context
, params
, entity
, **kwargs
):
361 """Handles the POST request for the project's manage page.
364 template: the template used for this view
365 entity: the student project entity
366 rest: see base.View.public()
369 post_dict
= request
.POST
371 if 'set_mentor' in post_dict
:
372 form
= params
['mentor_edit_form'](post_dict
)
373 return self
._manageSetMentor
(request
, template
, context
, params
, entity
,
375 elif 'add_additional_mentor' in post_dict
:
376 form
= params
['additional_mentor_form'](post_dict
)
377 return self
._manageAddAdditionalMentor
(request
, template
, context
,
378 params
, entity
, form
)
380 # unexpected error return the normal page
381 logging
.warning('Unexpected POST data found')
382 return self
.manageGet(request
, template
, context
, params
, entity
)
384 def _manageSetMentor(self
, request
, template
, context
, params
, entity
, form
):
385 """Handles the POST request for changing a Projects's mentor.
388 template: the template used for this view
389 entity: the student project entity
390 form: instance of the form used to set the mentor
391 rest: see base.View.public()
394 if not form
.is_valid():
395 context
['mentor_edit_form'] = form
397 # add an a fresh additional mentors form
398 context
['additional_mentor_form'] = params
['additional_mentor_form']()
400 return responses
.respond(request
, template
, context
)
402 _
, fields
= forms_helper
.collectCleanedFields(form
)
404 # get the mentor from the form
405 fields
= {'link_id': fields
['mentor_id'],
406 'scope': entity
.scope
,
408 mentor
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
410 # update the project with the assigned mentor
411 fields
= {'mentor': mentor
}
413 additional_mentors
= entity
.additional_mentors
415 # pylint: disable-msg=E1103
416 if additional_mentors
and mentor
.key() in additional_mentors
:
417 # remove the mentor that is now becoming the primary mentor
418 additional_mentors
.remove(mentor
.key())
419 fields
['additional_mentors'] = additional_mentors
421 # update the project with the new mentor and possible
422 # new set of additional mentors
423 project_logic
.updateEntityProperties(entity
, fields
)
425 # redirect to the same page
426 redirect
= request
.path
427 return http
.HttpResponseRedirect(redirect
)
429 def _manageAddAdditionalMentor(self
, request
, template
,
430 context
, params
, entity
, form
):
431 """Handles the POST request for changing a Projects's additional mentors.
434 template: the template used for this view
435 entity: the student project entity
436 form: instance of the form used to add an additional mentor
437 rest: see base.View.public()
440 if not form
.is_valid():
441 context
['additional_mentor_form'] = form
443 # add a fresh edit mentor form
444 initial
= {'mentor_id': entity
.mentor
.link_id
}
445 context
['mentor_edit_form'] = params
['mentor_edit_form'](initial
=initial
)
447 return responses
.respond(request
, template
, context
)
449 _
, fields
= forms_helper
.collectCleanedFields(form
)
451 # get the mentor from the form
452 fields
= {'link_id': fields
['mentor_id'],
453 'scope': entity
.scope
,
455 mentor
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
457 # add this mentor to the additional mentors
458 # pylint: disable-msg=E1103
459 if not entity
.additional_mentors
:
460 additional_mentors
= [mentor
.key()]
462 additional_mentors
= additional_mentors
.append(mentor
.key())
464 fields
= {'additional_mentors': additional_mentors
}
465 project_logic
.updateEntityProperties(entity
, fields
)
467 # redirect to the same page
468 redirect
= request
.path
469 return http
.HttpResponseRedirect(redirect
)
471 @decorators.merge_params
472 @decorators.check_access
473 def manageOverview(self
, request
, access_type
,
474 page_name
=None, params
=None, **kwargs
):
475 """View that allows Organization Admins to see an overview of
476 their Organization's Student Projects.
478 For params see base.View().public()
481 # make sure the organization exists
482 org_entity
= org_logic
.getFromKeyNameOr404(kwargs
['scope_path'])
483 fields
= {'scope': org_entity
}
485 # get the context for this webpage
486 context
= responses
.getUniversalContext(request
)
487 responses
.useJavaScript(context
, params
['js_uses_all'])
488 context
['page_name'] = '%s %s' % (page_name
, org_entity
.name
)
490 list_params
= params
.copy()
492 #list all active projects
493 fields
['status'] = ['accepted', 'mid_term_passed']
494 active_params
= list_params
.copy()
495 active_params
['list_description'] = \
496 'List of all active %(name_plural)s' % list_params
497 active_params
['list_action'] = (redirects
.getManageRedirect
, list_params
)
499 active_list
= lists
.getListContent(
500 request
, active_params
, fields
, idx
=0)
502 # list all failed projects
503 fields
['status'] = ['mid_term_failed', 'final_failed']
504 failed_params
= list_params
.copy()
505 failed_params
['list_description'] = ('List of all failed %(name_plural)s, '
506 'these cannot be managed.') % list_params
507 failed_params
['list_action'] = (redirects
.getPublicRedirect
, list_params
)
509 failed_list
= lists
.getListContent(
510 request
, failed_params
, fields
, idx
=1, need_content
=True)
512 #list all completed projects
513 fields
['status'] = ['passed']
514 completed_params
= list_params
.copy()
515 completed_params
['list_description'] = ('List of %(name_plural)s that have '
516 'successfully completed the program, '
517 'these cannot be managed.' % list_params
)
518 completed_params
['list_action'] = (redirects
.getPublicRedirect
, list_params
)
520 completed_list
= lists
.getListContent(
521 request
, completed_params
, fields
, idx
=2, need_content
=True)
523 # always show the list with active projects
524 content
= [active_list
]
526 if failed_list
!= None:
527 # do not show empty failed list
528 content
.append(failed_list
)
530 if completed_list
!= None:
531 # do not show empty completed list
532 content
.append(completed_list
)
534 # call the _list method from base to display the list
535 return self
._list
(request
, list_params
, content
,
536 context
['page_name'], context
)
538 @decorators.merge_params
539 @decorators.check_access
540 def stEdit(self
, request
, access_type
,
541 page_name
=None, params
=None, **kwargs
):
542 """View that allows students to edit information about their project.
544 For params see base.View().public()
548 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
549 except out_of_band
.Error
, error
:
550 return responses
.errorResponse(
551 error
, request
, template
=params
['error_public'])
553 # get the context for this webpage
554 context
= responses
.getUniversalContext(request
)
555 responses
.useJavaScript(context
, params
['js_uses_all'])
556 context
['page_name'] = page_name
557 # cancel should go to the public view
558 params
['cancel_redirect'] = redirects
.getPublicRedirect(entity
, params
)
561 return self
.stEditPost(request
, context
, params
, entity
, **kwargs
)
563 return self
.stEditGet(request
, context
, params
, entity
, **kwargs
)
565 def stEditGet(self
, request
, context
, params
, entity
, **kwargs
):
566 """Handles the GET request for the student's edit page.
569 entity: the student project entity
570 rest: see base.View.public()
573 # populate form with the existing entity
574 form
= params
['student_edit_form'](instance
=entity
)
576 return self
._constructResponse
(request
, entity
, context
, form
, params
)
578 def stEditPost(self
, request
, context
, params
, entity
, **kwargs
):
579 """Handles the POST request for the student's edit page.
582 entity: the student project entity
583 rest: see base.View.public()
586 form
= params
['student_edit_form'](request
.POST
)
588 if not form
.is_valid():
589 return self
._constructResponse
(request
, entity
, context
, form
, params
)
591 _
, fields
= forms_helper
.collectCleanedFields(form
)
593 project_logic
.updateEntityProperties(entity
, fields
)
595 return self
.stEditGet(request
, context
, params
, entity
, **kwargs
)
600 admin
= decorators
.view(view
.admin
)
601 create
= decorators
.view(view
.create
)
602 delete
= decorators
.view(view
.delete
)
603 edit
= decorators
.view(view
.edit
)
604 list = decorators
.view(view
.list)
605 manage
= decorators
.view(view
.manage
)
606 manage_overview
= decorators
.view(view
.manageOverview
)
607 public
= decorators
.view(view
.public
)
608 st_edit
= decorators
.view(view
.stEdit
)
609 export
= decorators
.view(view
.export
)
610 pick
= decorators
.view(view
.pick
)