Changed label and help_text for public review button on studentproposal review page.
[Melange.git] / app / soc / views / models / student_proposal.py
blobb579ebc09001860912b31312345a889d98be4c8d
1 #!/usr/bin/python2.5
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 Proposal.
18 """
20 __authors__ = [
21 '"Lennard de Rijk" <ljvderijk@gmail.com>',
25 import datetime
26 import time
28 from django import forms
29 from django import http
30 from django.utils.translation import ugettext
32 from soc.logic import cleaning
33 from soc.logic import dicts
34 from soc.logic.models import mentor as mentor_logic
35 from soc.logic.models import organization as org_logic
36 from soc.logic.models import org_admin as org_admin_logic
37 from soc.logic.models import student as student_logic
38 from soc.logic.models import user as user_logic
39 from soc.views import helper
40 from soc.views import out_of_band
41 from soc.views.helper import access
42 from soc.views.helper import decorators
43 from soc.views.helper import dynaform
44 from soc.views.helper import lists
45 from soc.views.helper import params as params_helper
46 from soc.views.helper import redirects
47 from soc.views.helper import responses
48 from soc.views.helper import widgets
49 from soc.views.models import base
50 from soc.views.models import student as student_view
52 import soc.logic.models.student_proposal
55 class View(base.View):
56 """View methods for the Student Proposal model.
57 """
59 def __init__(self, params=None):
60 """Defines the fields and methods required for the base View class
61 to provide the user with list, public, create, edit and delete views.
63 Params:
64 params: a dict with params for this View
65 """
67 rights = access.Checker(params)
68 rights['create'] = ['checkIsDeveloper']
69 rights['edit'] = [('checkCanStudentPropose', ['scope_path', False]),
70 ('checkRoleAndStatusForStudentProposal',
71 [['proposer'], ['active'], ['new', 'pending', 'invalid']])]
72 rights['delete'] = ['checkIsDeveloper']
73 rights['show'] = [
74 ('checkRoleAndStatusForStudentProposal',
75 [['proposer', 'org_admin', 'mentor', 'host'],
76 ['active', 'inactive'],
77 ['new', 'pending', 'accepted', 'rejected', 'invalid']])]
78 rights['list'] = ['checkIsDeveloper']
79 rights['list_orgs'] = [
80 ('checkIsStudent', ['scope_path', ['active']]),
81 ('checkCanStudentPropose', ['scope_path', False])]
82 rights['list_self'] = [
83 ('checkIsStudent', ['scope_path', ['active', 'inactive']])]
84 rights['apply'] = [
85 ('checkIsStudent', ['scope_path', ['active']]),
86 ('checkCanStudentPropose', ['scope_path', True])]
87 rights['review'] = [('checkRoleAndStatusForStudentProposal',
88 [['org_admin', 'mentor', 'host'],
89 ['active'], ['new', 'pending', 'invalid']])]
91 new_params = {}
92 new_params['logic'] = soc.logic.models.student_proposal.logic
93 new_params['rights'] = rights
94 new_params['name'] = "Student Proposal"
95 new_params['url_name'] = "student_proposal"
96 new_params['sidebar_grouping'] = 'Students'
98 new_params['scope_view'] = student_view
99 new_params['scope_redirect'] = redirects.getCreateRedirect
101 new_params['no_create_with_key_fields'] = True
103 patterns = [
104 (r'^%(url_name)s/(?P<access_type>apply)/%(scope)s$',
105 'soc.views.models.%(module_name)s.apply',
106 'Create a new %(name)s'),
107 (r'^%(url_name)s/(?P<access_type>list_self)/%(scope)s$',
108 'soc.views.models.%(module_name)s.list_self',
109 'List my %(name_plural)s'),
110 (r'^%(url_name)s/(?P<access_type>list_orgs)/%(scope)s$',
111 'soc.views.models.%(module_name)s.list_orgs',
112 'List my %(name_plural)s'),
113 (r'^%(url_name)s/(?P<access_type>review)/%(key_fields)s$',
114 'soc.views.models.%(module_name)s.review',
115 'Review %(name)s'),
118 new_params['extra_django_patterns'] = patterns
120 new_params['extra_dynaexclude'] = ['org', 'program', 'score',
121 'status', 'mentor', 'link_id',
122 'possible_mentors']
124 new_params['create_extra_dynaproperties'] = {
125 'content': forms.fields.CharField(required=True,
126 widget=widgets.FullTinyMCE(attrs={'rows': 25, 'cols': 100})),
127 'scope_path': forms.CharField(widget=forms.HiddenInput,
128 required=True),
129 'organization': forms.CharField(label='Organization Link ID',
130 required=True),
131 'clean_abstract': cleaning.clean_content_length('abstract'),
132 'clean_content': cleaning.clean_html_content('content'),
133 'clean_organization': cleaning.clean_link_id('organization'),
134 'clean_additional_info': cleaning.clean_url('additional_info'),
135 'clean': cleaning.validate_student_proposal('organization',
136 'scope_path', student_logic, org_logic),
139 new_params['edit_extra_dynaproperties'] = {
140 'organization': forms.CharField(label='Organization Link ID',
141 widget=widgets.ReadOnlyInput),
142 'link_id': forms.CharField(widget=forms.HiddenInput)
145 new_params['edit_template'] = 'soc/student_proposal/edit.html'
146 new_params['review_template'] = 'soc/student_proposal/review.html'
148 params = dicts.merge(params, new_params)
150 super(View, self).__init__(params=params)
152 # create the special form for students
153 dynafields = [
154 {'name': 'organization',
155 'base': forms.CharField,
156 'label': 'Organization Link ID',
157 'widget': widgets.ReadOnlyInput(),
158 'required': False,
162 dynaproperties = params_helper.getDynaFields(dynafields)
164 student_create_form = dynaform.extendDynaForm(
165 dynaform=self._params['create_form'],
166 dynaproperties=dynaproperties)
168 params['student_create_form'] = student_create_form
170 # create the special form for public review
171 dynafields = [
172 {'name': 'comment',
173 'base': forms.CharField,
174 'widget': widgets.FullTinyMCE(attrs={'rows': 10, 'cols': 40}),
175 'label': 'Comment',
176 'required': False,
180 dynaproperties = params_helper.getDynaFields(dynafields)
181 dynaproperties['clean_comment'] = cleaning.clean_html_content('comment')
183 public_review_form = dynaform.newDynaForm(dynamodel=None,
184 dynabase=helper.forms.BaseForm, dynainclude=None,
185 dynaexclude=None, dynaproperties=dynaproperties)
186 params['public_review_form'] = public_review_form
188 # create the special form for mentors
189 dynafields = [
190 {'name': 'score',
191 'base': forms.ChoiceField,
192 'label': 'Score',
193 'initial': 0,
194 'required': False,
195 'passthrough': ['initial', 'required', 'choices'],
196 'example_text':
197 'A score will only be assigned if the review is private!',
198 'choices': [(-4,'-4: Wow. This. Sucks.'),
199 (-3,'-3: Needs a lot of work'),
200 (-2,'-2: This is bad'),
201 (-1,'-1: I dont like this'),
202 (0,'0: No score'),
203 (1,'1: Might have potential'),
204 (2,'2: Good'),
205 (3,'3: Almost there'),
206 (4,'4: Made. Of. Awesome.')]
208 {'name': 'comment',
209 'base': forms.CharField,
210 'widget': widgets.FullTinyMCE(attrs={'rows': 10, 'cols': 40}),
211 'label': 'Comment',
212 'required': False,
214 {'name': 'public',
215 'base': forms.BooleanField,
216 'label': 'Review visible to Student',
217 'initial': False,
218 'required': False,
219 'help_text': 'By ticking this box the score will not be assigned, '
220 'and the review will be visible to the student.',
224 dynaproperties = params_helper.getDynaFields(dynafields)
225 dynaproperties['clean_comment'] = cleaning.clean_html_content('comment')
227 mentor_review_form = dynaform.newDynaForm(dynamodel=None,
228 dynabase=helper.forms.BaseForm, dynainclude=None,
229 dynaexclude=None, dynaproperties=dynaproperties)
230 params['mentor_review_form'] = mentor_review_form
232 # TODO see if autocomplete can be used for mentor field
233 dynafields = [
234 {'name': 'rank',
235 'base': forms.IntegerField,
236 'label': 'Set to rank',
237 'help_text':
238 'Set this proposal to the given rank (ignores the given score)',
239 'example_text': 'A rank will only be assigned if the '
240 'review is private!',
241 'min_value': 1,
242 'required': False,
243 'passthrough': ['min_value', 'required', 'help_text'],
245 {'name': 'mentor',
246 'base': forms.CharField,
247 'label': 'Assign Mentor (Link ID)',
248 'required': False,
249 'help_text': 'Fill in the Link ID of the Mentor '
250 'you would like to assign to this Proposal. '
251 'Leave this box empty if you don\'t want any mentor assigned.',
255 dynaproperties = params_helper.getDynaFields(dynafields)
256 dynaproperties['clean_comment'] = cleaning.clean_html_content('comment')
258 admin_review_form = dynaform.extendDynaForm(dynaform=mentor_review_form,
259 dynaproperties=dynaproperties)
261 params['admin_review_form'] = admin_review_form
263 def _editGet(self, request, entity, form):
264 """See base.View._editGet().
267 form.fields['link_id'].initial = entity.link_id
268 form.fields['organization'].initial = entity.org.link_id
270 return super(View, self)._editGet(request, entity, form)
272 def _editPost(self, request, entity, fields):
273 """See base.View._editPost().
276 if not entity:
277 fields['link_id'] = 't%i' % (int(time.time()*100))
278 else:
279 fields['link_id'] = entity.link_id
281 # fill in the scope via call to super
282 super(View, self)._editPost(request, entity, fields)
284 if not entity:
285 # creating a new application so set the program and org field
286 fields['program'] = fields['scope'].scope
288 filter = {'scope': fields['program'],
289 'link_id': fields['organization']}
290 fields['org'] = org_logic.logic.getForFields(filter, unique=True)
292 # explicitly change the last_modified_on since the content has been edited
293 fields['last_modified_on'] = datetime.datetime.now()
295 @decorators.merge_params
296 @decorators.check_access
297 def public(self, request, access_type,
298 page_name=None, params=None, **kwargs):
299 """View in which the student can see and reply to the comments on the
300 Student Proposal.
302 For params see base.view.Public().
305 context = helper.responses.getUniversalContext(request)
306 helper.responses.useJavaScript(context, params['js_uses_all'])
307 context['page_name'] = page_name
309 try:
310 entity = self._logic.getFromKeyFieldsOr404(kwargs)
311 except out_of_band.Error, error:
312 return helper.responses.errorResponse(
313 error, request, template=params['error_public'], context=context)
315 context['entity'] = entity
316 context['entity_type'] = params['name']
317 context['entity_type_url'] = params['url_name']
319 if request.method == 'POST':
320 return self.publicPost(request, context, params, entity, **kwargs)
321 else: # request.method == 'GET'
322 return self.publicGet(request, context, params, entity, **kwargs)
324 def publicPost(self, request, context, params, entity, **kwargs):
325 """Handles the POST request for the entity's public page.
327 Args:
328 entity: the student proposal entity
329 rest: see base.View.public()
332 # populate the form using the POST data
333 form = params['public_review_form'](request.POST)
335 if not form.is_valid():
336 # get some entity specific context
337 self.updatePublicContext(context, entity, params)
339 # return the invalid form response
340 return self._constructResponse(request, entity=entity, context=context,
341 form=form, params=params, template=params['public_template'])
343 # get the commentary
344 fields = form.cleaned_data
345 comment = fields['comment']
347 if comment:
348 # create a new public review containing the comment
349 user_entity = user_logic.logic.getForCurrentAccount()
351 if user_entity.key() == entity.scope.user.key():
352 # student is posting
353 reviewer = entity.scope
354 else:
355 # check if the person commenting is an org_admin
356 # or a mentor for the given proposal
357 fields = {'user': user_entity,
358 'scope': entity.org,
359 'status': 'active',
362 reviewer = org_admin_logic.logic.getForFields(fields, unique=True)
364 if not reviewer:
365 # no org_admin found, maybe it's a mentor?
366 reviewer = mentor_logic.logic.getForFields(fields, unique=True)
368 # create the review (reviewer might be None
369 # if a Host or Developer is posting)
370 self._createReviewFor(entity, reviewer, comment, is_public=True)
372 # redirect to the same page
373 return http.HttpResponseRedirect('')
375 def publicGet(self, request, context, params, entity, **kwargs):
376 """Handles the GET request for the entity's public page.
378 Args:
379 entity: the student proposal entity
380 rest see base.View.public()
383 from soc.logic.models.review_follower import logic as review_follower_logic
385 get_dict = request.GET
387 if get_dict.get('subscription') and (
388 get_dict['subscription'] in ['on', 'off']):
390 subscription = get_dict['subscription']
392 # get the current user
393 user_entity = user_logic.logic.getForCurrentAccount()
395 # create the fields that should be in the ReviewFollower entity
396 fields = {'link_id': user_entity.link_id,
397 'scope': entity,
398 'scope_path': entity.key().name(),
399 'user': user_entity
401 # get the keyname for the ReviewFollower entity
402 key_name = review_follower_logic.getKeyNameFromFields(fields)
404 # determine if we should set subscribed_public to True or False
405 if subscription == 'on':
406 fields['subscribed_public'] = True
407 elif subscription == 'off':
408 fields['subscribed_public'] = False
410 # update the ReviewFollower
411 review_follower_logic.updateOrCreateFromKeyName(fields, key_name)
413 # get some entity specific context
414 self.updatePublicContext(context, entity, params)
416 context['form'] = params['public_review_form']()
417 template = params['public_template']
419 return responses.respond(request, template, context=context)
421 def updatePublicContext(self, context, entity, params):
422 """Updates the context for the public page with information from the entity.
424 Args:
425 context: the context that should be updated
426 entity: a student proposal_entity used to set context
427 params: dict with params for the view using this context
430 from soc.logic.models.review import logic as review_logic
431 from soc.logic.models.review_follower import logic as review_follower_logic
433 student_entity = entity.scope
435 context['student'] = student_entity
436 context['student_name'] = student_entity.name()
438 user_entity = user_logic.logic.getForCurrentAccount()
440 # check if the current user is the student
441 if user_entity.key() == student_entity.user.key():
442 # show the proposal edit link
443 context['edit_link'] = redirects.getEditRedirect(entity, params)
445 # check if the current user is subscribed to this proposal's public reviews
446 fields = {'user': user_entity,
447 'scope': entity,
448 'subscribed_public': True}
450 context['is_subscribed'] = review_follower_logic.getForFields(fields,
451 unique=True)
453 context['public_reviews'] = review_logic.getReviewsForEntity(entity,
454 is_public=True, order=['created'])
456 @decorators.merge_params
457 @decorators.check_access
458 def apply(self, request, access_type,
459 page_name=None, params=None, **kwargs):
460 """Special view used to prepopulate the form with the organization
461 contributors template.
463 For params see base.View.public()
465 get_dict = request.GET
467 if get_dict.get('organization'):
468 # organization chosen, prepopulate with template
470 # get the organization
471 student_entity = student_logic.logic.getFromKeyName(kwargs['scope_path'])
472 program_entity = student_entity.scope
474 filter = {'link_id': get_dict['organization'],
475 'scope': program_entity}
477 org_entity = org_logic.logic.getForFields(filter, unique=True)
479 if org_entity:
480 # organization found use special form
481 params['create_form'] = params['student_create_form']
482 kwargs['content'] = org_entity.contrib_template
484 # Create page is an edit page with no key fields
485 empty_kwargs = {}
486 fields = self._logic.getKeyFieldNames()
487 for field in fields:
488 empty_kwargs[field] = None
490 return super(View, self).edit(request, access_type, page_name=page_name,
491 params=params, seed=kwargs, **empty_kwargs)
493 @decorators.merge_params
494 @decorators.check_access
495 def edit(self, request, access_type,
496 page_name=None, params=None, seed=None, **kwargs):
497 """If the POST contains (action, Withdraw) the proposal in kwargs
498 will be marked as invalid.
500 For params see base.View.edit()
503 # check if request.POST contains action
504 post_dict = request.POST
505 if 'action' in post_dict and post_dict['action'] == 'Withdraw':
506 # withdraw this proposal
507 filter = {'scope_path': kwargs['scope_path'],
508 'link_id': kwargs['link_id']}
510 proposal_logic = params['logic']
511 student_proposal_entity = proposal_logic.getForFields(filter, unique=True)
512 reviewer = student_proposal_entity.scope
514 # update the entity mark it as invalid
515 proposal_logic.updateEntityProperties(student_proposal_entity,
516 {'status': 'invalid'})
518 # redirect to the program's homepage
519 redirect_url = redirects.getHomeRedirect(student_proposal_entity.program,
520 {'url_name': 'program'})
522 comment = "Student withdrew proposal."
523 self._createReviewFor(student_proposal_entity, reviewer, comment)
524 return http.HttpResponseRedirect(redirect_url)
526 return super(View, self).edit(request=request, access_type=access_type,
527 page_name=page_name, params=params, seed=seed, **kwargs)
529 @decorators.merge_params
530 @decorators.check_access
531 def listOrgs(self, request, access_type,
532 page_name=None, params=None, **kwargs):
533 """Lists all organization which the given student can propose to.
535 For params see base.View.public().
538 from soc.views.models import organization as org_view
540 student_entity = student_logic.logic.getFromKeyName(kwargs['scope_path'])
542 filter = {'scope': student_entity.scope,
543 'status': 'active'}
545 list_params = org_view.view.getParams().copy()
546 list_params['list_description'] = ('List of %(name_plural)s you can send '
547 'your proposal to.') % list_params
548 list_params['list_action'] = (redirects.getStudentProposalRedirect,
549 {'student_key': student_entity.key().name(),
550 'url_name': params['url_name']})
552 return self.list(request, access_type=access_type, page_name=page_name,
553 params=list_params, filter=filter, **kwargs)
555 @decorators.merge_params
556 @decorators.check_access
557 def listSelf(self, request, access_type,
558 page_name=None, params=None, **kwargs):
559 """Lists all proposals from the current logged-in user
560 for the given student.
562 For params see base.View.public().
565 context = {}
566 student_entity = student_logic.logic.getFromKeyName(kwargs['scope_path'])
568 filter = {'scope' : student_entity,
569 'status': ['new', 'pending', 'accepted', 'rejected']}
571 list_params = params.copy()
572 list_params['list_description'] = 'List of my %(name_plural)s.' % list_params
573 list_params['list_action'] = (redirects.getPublicRedirect, list_params)
575 valid_list = lists.getListContent(
576 request, list_params, filter, idx=0)
578 ip_params = list_params.copy() # ineligible proposals
580 description = ugettext('List of my ineligible/withdrawn %s.') % (
581 ip_params['name_plural'])
583 ip_params['list_description'] = description
584 ip_params['list_action'] = (redirects.getPublicRedirect, ip_params)
586 filter = {'scope' : student_entity,
587 'status': 'invalid'}
589 ip_list = lists.getListContent(
590 request, ip_params, filter, idx=1, need_content=True)
592 contents = []
593 # fill contents with all the needed lists
594 contents.append(valid_list)
596 if ip_list != None:
597 contents.append(ip_list)
599 # call the _list method from base to display the list
600 return self._list(request, list_params, contents, page_name, context)
602 @decorators.merge_params
603 @decorators.check_access
604 def review(self, request, access_type,
605 page_name=None, params=None, **kwargs):
606 """View that allows Organization Admins and Mentors to review the proposal.
608 For Args see base.View.public().
611 try:
612 entity = self._logic.getFromKeyFieldsOr404(kwargs)
613 except out_of_band.Error, error:
614 return helper.responses.errorResponse(
615 error, request, template=params['error_public'])
617 # get the context for this webpage
618 context = responses.getUniversalContext(request)
619 responses.useJavaScript(context, params['js_uses_all'])
620 context['page_name'] = '%s "%s" from %s' % (page_name, entity.title,
621 entity.scope.name())
622 context['entity'] = entity
623 context['entity_type'] = params['name']
624 context['entity_type_url'] = params['url_name']
626 # get the roles important for reviewing an application
627 filter = {'user': user_logic.logic.getForCurrentAccount(),
628 'scope': entity.org,
629 'status': 'active'}
631 org_admin_entity = org_admin_logic.logic.getForFields(filter, unique=True)
632 mentor_entity = mentor_logic.logic.getForFields(filter, unique=True)
634 # decide which form to use
635 if org_admin_entity:
636 form = params['admin_review_form']
637 else:
638 form = params['mentor_review_form']
640 if request.method == 'POST':
641 return self.reviewPost(request, context, params, entity,
642 form, org_admin_entity, mentor_entity, **kwargs)
643 else:
644 # request.method == 'GET'
645 return self.reviewGet(request, context, params, entity,
646 form, org_admin_entity, mentor_entity, **kwargs)
648 def reviewPost(self, request, context, params, entity, form,
649 org_admin, mentor, **kwargs):
650 """Handles the POST request for the proposal review view.
652 Args:
653 entity: the student proposal entity
654 form: the form to use in this view
655 org_admin: org admin entity for the current user/proposal (iff available)
656 mentor: mentor entity for the current user/proposal (iff available)
657 rest: see base.View.public()
659 # populate the form using the POST data
660 form = form(request.POST)
662 if not form.is_valid():
663 # return the invalid form response
664 # get all the extra information that should be in the context
665 review_context = self._getDefaultReviewContext(entity, org_admin, mentor)
666 context = dicts.merge(context, review_context)
668 return self._constructResponse(request, entity=entity, context=context,
669 form=form, params=params, template=params['review_template'])
671 fields = form.cleaned_data
672 is_public = fields['public']
673 comment = fields['comment']
674 given_score = int(fields['score'])
676 if org_admin:
677 # org admin found, try to adjust the assigned mentor
678 self._adjustMentor(entity, fields['mentor'])
679 reviewer = org_admin
681 # try to see if the rank is given and adjust the given_score if needed
682 rank = fields['rank']
683 if rank:
684 ranker = self._logic.getRankerFor(entity)
685 # if a very high rank is filled in use the highest
686 # one that returns a score
687 rank = min(ranker.TotalRankedScores(), rank)
688 # ranker uses zero-based ranking
689 score_and_rank = ranker.FindScore(rank-1)
690 # get the score at the requested rank
691 score_at_rank = score_and_rank[0][0]
692 # calculate the score that should be given to end up at the given rank
693 given_score = score_at_rank - entity.score
694 else:
695 # might be None (if Host or Developer is commenting)
696 reviewer = mentor
698 # store the properties to update the proposal with
699 properties = {}
701 if reviewer and (not is_public) and (given_score is not 0):
702 # if it is not a public comment and it's made by a member of the
703 # organization we update the score of the proposal
704 new_score = given_score + entity.score
705 properties = {'score': new_score}
707 if comment or (given_score is not 0):
708 # if the proposal is new we change it status to pending
709 if entity.status == 'new':
710 properties['status'] = 'pending'
712 # create the review entity
713 self._createReviewFor(entity, reviewer, comment, given_score, is_public)
715 if properties.values():
716 # there is something to update
717 self._logic.updateEntityProperties(entity, properties)
719 # redirect to the same page
720 return http.HttpResponseRedirect('')
722 def reviewGet(self, request, context, params, entity, form,
723 org_admin, mentor, **kwargs):
724 """Handles the GET request for the proposal review view.
726 Args:
727 entity: the student proposal entity
728 form: the form to use in this view
729 org_admin: org admin entity for the current user/proposal (iff available)
730 mentor: mentor entity for the current user/proposal (iff available)
731 rest: see base.View.public()
734 from soc.logic.models.review_follower import logic as review_follower_logic
736 get_dict = request.GET
738 # check if the current user is a mentor and wants
739 # to change his role for this app
740 choice = get_dict.get('mentor')
741 if mentor and choice:
742 self._adjustPossibleMentors(entity, mentor, choice)
744 ineligible = get_dict.get('ineligible')
746 if org_admin:
747 reviewer = org_admin
748 elif mentor:
749 reviewer = mentor
751 if (org_admin or mentor) and ineligible != None:
752 ineligible = int(ineligible)
753 if ineligible == 1:
754 # mark the proposal invalid and return to the list
755 properties = {'status': 'invalid'}
756 self._logic.updateEntityProperties(entity, properties)
758 redirect = redirects.getListProposalsRedirect(entity.org,
759 {'url_name': 'org'})
760 comment = "Marked Student Proposal as Ineligible."
761 self._createReviewFor(entity, reviewer, comment, is_public=False)
762 return http.HttpResponseRedirect(redirect)
763 elif ineligible == 0:
764 # mark the proposal as new and return to the list
765 properties = {'status': 'new'}
766 self._logic.updateEntityProperties(entity, properties)
768 redirect = redirects.getListProposalsRedirect(entity.org,
769 {'url_name': 'org'})
770 comment = "Marked Student Proposal as Eligible."
771 self._createReviewFor(entity, reviewer, comment, is_public=False)
772 return http.HttpResponseRedirect(redirect)
774 # check if we should change the subscription state for the current user
775 public_subscription = None
776 private_subscription = None
778 if get_dict.get('public_subscription') and (
779 get_dict['public_subscription'] in ['on', 'off']):
781 public_subscription = get_dict['public_subscription'] == 'on'
783 if get_dict.get('private_subscription') and (
784 get_dict['private_subscription'] in ['on', 'off']):
785 private_subscription = get_dict['private_subscription'] == 'on'
787 if public_subscription != None or private_subscription != None:
788 # get the current user
789 user_entity = user_logic.logic.getForCurrentAccount()
791 # create the fields that should be in the ReviewFollower entity
792 fields = {'link_id': user_entity.link_id,
793 'scope': entity,
794 'scope_path': entity.key().name(),
795 'user': user_entity
797 # get the keyname for the ReviewFollower entity
798 key_name = review_follower_logic.getKeyNameFromFields(fields)
800 # determine which subscription properties we should change
801 if public_subscription != None:
802 fields['subscribed_public'] = public_subscription
804 if private_subscription != None:
805 fields['subscribed_private'] = private_subscription
807 # update the ReviewFollower
808 review_follower_logic.updateOrCreateFromKeyName(fields, key_name)
810 # set the initial score since the default is ignored
811 initial = {'score': 0}
813 if org_admin and entity.mentor:
814 # set the mentor field to the current mentor
815 initial['mentor'] = entity.mentor.link_id
817 context['form'] = form(initial)
819 # create the special form for mentors
820 comment_public = ['public', 'comment']
821 comment_private = ['score']
822 comment_admin = ['rank', 'mentor']
823 class FilterForm(object):
824 """Helper class used for form filtering.
826 def __init__(self, form, fields):
827 self.__form = form
828 self.__fields = fields
830 @property
831 def fields(self):
832 """Property that returns all fields as dictionary."""
833 fields = self.__form.fields.iteritems()
834 return dict([(k, i) for k, i in fields if k in self.__fields])
836 def __iter__(self):
837 for field in self.__form:
838 if field.name not in self.__fields:
839 continue
840 yield field
842 _marker = []
843 def __getattr__(self, key, default=_marker):
844 if default is self._marker:
845 return getattr(self.__form, key)
846 else:
847 return getattr(self.__form, key, default)
849 context['form'] = form(initial)
850 context['comment_public'] = FilterForm(context['form'], comment_public)
851 context['comment_private'] = FilterForm(context['form'], comment_private)
852 context['comment_admin'] = FilterForm(context['form'], comment_admin)
854 # get all the extra information that should be in the context
855 review_context = self._getDefaultReviewContext(entity, org_admin, mentor)
856 context = dicts.merge(context, review_context)
858 template = params['review_template']
860 return responses.respond(request, template, context=context)
862 def _getDefaultReviewContext(self, entity, org_admin,
863 mentor):
864 """Returns the default context for the review page.
866 Args:
867 entity: Student Proposal entity
868 org_admin: org admin entity for the current user/proposal (iff available)
869 mentor: mentor entity for the current user/proposal (iff available)
872 from soc.logic.models.review import logic as review_logic
873 from soc.logic.models.review_follower import logic as review_follower_logic
875 context = {}
877 context['student'] = entity.scope
878 context['student_name'] = entity.scope.name()
880 if entity.mentor:
881 context['mentor_name'] = entity.mentor.name()
882 else:
883 context['mentor_name'] = "No mentor assigned"
885 # set the possible mentors in the context
886 possible_mentors = entity.possible_mentors
888 if not possible_mentors:
889 context['possible_mentors'] = "None"
890 else:
891 mentor_names = []
893 for mentor_key in possible_mentors:
894 possible_mentor = mentor_logic.logic.getFromKeyName(mentor_key.name())
895 mentor_names.append(possible_mentor.name())
897 context['possible_mentors'] = ', '.join(mentor_names)
899 # TODO(ljvderijk) listing of total given scores per mentor
900 # a dict with key as role.user ?
902 # order the reviews by ascending creation date
903 order = ['created']
905 # get the public reviews
906 context['public_reviews'] = review_logic.getReviewsForEntity(entity,
907 is_public=True, order=order)
909 # get the private reviews
910 context['private_reviews'] = review_logic.getReviewsForEntity(entity,
911 is_public=False, order=order)
913 # which button should we show to the mentor?
914 if mentor:
915 context['is_mentor'] = True
916 if mentor.key() in possible_mentors:
917 # show "No longer willing to mentor"
918 context['remove_me_as_mentor'] = True
919 else:
920 # show "I am willing to mentor"
921 context['add_me_as_mentor'] = True
923 if org_admin:
924 context['is_org_admin'] = True
926 user_entity = user_logic.logic.getForCurrentAccount()
928 # check if the current user is subscribed to public or private reviews
929 fields = {'scope': entity,
930 'user': user_entity,}
931 follower_entity = review_follower_logic.getForFields(fields, unique=True)
933 if follower_entity:
934 context['is_subscribed_public'] = follower_entity.subscribed_public
935 context['is_subscribed_private'] = follower_entity.subscribed_private
937 return context
939 def _adjustPossibleMentors(self, entity, mentor, choice):
940 """Adjusts the possible mentors list for a proposal.
942 Args:
943 entity: Student Proposal entity
944 mentor: Mentor entity
945 choice: 1 means want to mentor, 0 do not want to mentor
947 possible_mentors = entity.possible_mentors
949 if choice == '1':
950 # add the mentor to possible mentors list if not already in
951 if mentor.key() not in possible_mentors:
952 possible_mentors.append(mentor.key())
953 fields = {'possible_mentors': possible_mentors}
954 self._logic.updateEntityProperties(entity, fields)
955 elif choice == '0':
956 # remove the mentor from the possible mentors list
957 if mentor.key() in possible_mentors:
958 possible_mentors.remove(mentor.key())
959 fields = {'possible_mentors': possible_mentors}
960 self._logic.updateEntityProperties(entity, fields)
962 def _adjustMentor(self, entity, mentor_id):
963 """Changes the mentor to the given link_id.
965 Args:
966 entity: Student Proposal entity
967 mentor_id: Link ID of the mentor that needs to be assigned
968 Iff not given then removes the assigned mentor
971 if entity.mentor and entity.mentor.link_id == mentor_id:
972 # no need to change
973 return
975 if mentor_id:
976 # try to locate the mentor
977 fields = {'link_id': mentor_id,
978 'scope': entity.org,
979 'status': 'active'}
981 mentor_entity = mentor_logic.logic.getForFields(fields, unique=True)
983 if not mentor_entity:
984 # no mentor found, do not update
985 return
986 else:
987 # reset to None
988 mentor_entity = None
990 # update the proposal
991 properties = {'mentor': mentor_entity}
992 self._logic.updateEntityProperties(entity, properties)
994 def _createReviewFor(self, entity, reviewer, comment,
995 score=0, is_public=True):
996 """Creates a review for the given proposal and sends
997 out a message to all followers.
999 Args:
1000 entity: Student Proposal entity for which the review should be created
1001 reviewer: A role entity of the reviewer (if possible, else None)
1002 comment: The textual contents of the review
1003 score: The score of the review (only used if the review is not public)
1004 is_public: Determines if the review is a public review
1007 from soc.logic.helper import notifications as notifications_helper
1008 from soc.logic.models.review import logic as review_logic
1009 from soc.logic.models.review_follower import logic as review_follower_logic
1011 # create the fields for the review entity
1012 fields = {'link_id': 't%i' % (int(time.time()*100)),
1013 'scope': entity,
1014 'scope_path': entity.key().name(),
1015 'author': user_logic.logic.getForCurrentAccount(),
1016 'content': comment,
1017 'is_public': is_public,
1018 'reviewer': reviewer
1021 # add the given score if the review is not public
1022 if not is_public:
1023 fields['score'] = score
1025 # create a new Review
1026 key_name = review_logic.getKeyNameFromFields(fields)
1027 review_entity = review_logic.updateOrCreateFromKeyName(fields, key_name)
1029 # get all followers
1030 fields = {'scope': entity}
1032 if is_public:
1033 fields['subscribed_public'] = True
1034 else:
1035 fields['subscribed_private'] = True
1037 followers = review_follower_logic.getForFields(fields)
1039 if is_public:
1040 # redirect to public page
1041 redirect_url = redirects.getPublicRedirect(entity, self._params)
1042 else:
1043 # redirect to review page
1044 redirect_url = redirects.getReviewRedirect(entity, self._params)
1046 for follower in followers:
1047 # sent to every follower except the reviewer
1048 if follower.user.key() != review_entity.author.key():
1049 notifications_helper.sendNewReviewNotification(follower.user,
1050 review_entity, entity.title, redirect_url)
1053 view = View()
1055 admin = decorators.view(view.admin)
1056 apply = decorators.view(view.apply)
1057 create = decorators.view(view.create)
1058 delete = decorators.view(view.delete)
1059 edit = decorators.view(view.edit)
1060 list = decorators.view(view.list)
1061 list_orgs = decorators.view(view.listOrgs)
1062 list_self = decorators.view(view.listSelf)
1063 public = decorators.view(view.public)
1064 review = decorators.view(view.review)
1065 export = decorators.view(view.export)
1066 pick = decorators.view(view.pick)