Hook up the new template tag for StudentProposal reviews.
[Melange.git] / app / soc / views / models / student_proposal.py
blobfff2999e883174cf4b6631df53bea979c46b4fe0
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
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 import organization as org_logic
35 from soc.logic.models import org_admin as org_admin_logic
36 from soc.logic.models import student as student_logic
37 from soc.logic.models import user as user_logic
38 from soc.views import helper
39 from soc.views import out_of_band
40 from soc.views.helper import access
41 from soc.views.helper import decorators
42 from soc.views.helper import dynaform
43 from soc.views.helper import params as params_helper
44 from soc.views.helper import redirects
45 from soc.views.helper import responses
46 from soc.views.helper import widgets
47 from soc.views.models import base
48 from soc.views.models import student as student_view
50 import soc.logic.models.student_proposal
53 class View(base.View):
54 """View methods for the Student Proposal model.
55 """
57 def __init__(self, params=None):
58 """Defines the fields and methods required for the base View class
59 to provide the user with list, public, create, edit and delete views.
61 Params:
62 params: a dict with params for this View
63 """
65 rights = access.Checker(params)
66 rights['create'] = ['checkIsDeveloper']
67 rights['edit'] = [('checkCanStudentPropose', 'scope_path'),
68 ('checkRoleAndStatusForStudentProposal',
69 [['proposer'], ['active'], ['new', 'pending']])]
70 rights['delete'] = ['checkIsDeveloper']
71 rights['show'] = [
72 ('checkRoleAndStatusForStudentProposal',
73 [['proposer', 'org_admin', 'mentor', 'host'],
74 ['active', 'inactive'],
75 ['new', 'pending', 'accepted', 'rejected']])]
76 rights['list'] = ['checkIsDeveloper']
77 rights['list_orgs'] = [
78 ('checkIsStudent', ['scope_path', ['active']]),
79 ('checkCanStudentPropose', 'scope_path')]
80 rights['list_self'] = [
81 ('checkIsStudent', ['scope_path', ['active', 'inactive']])]
82 rights['apply'] = [
83 ('checkIsStudent', ['scope_path', ['active']]),
84 ('checkCanStudentPropose', 'scope_path')]
85 rights['review'] = [('checkRoleAndStatusForStudentProposal',
86 [['org_admin', 'mentor', 'host'],
87 ['active'], ['new', 'pending']])]
89 new_params = {}
90 new_params['logic'] = soc.logic.models.student_proposal.logic
91 new_params['rights'] = rights
92 new_params['name'] = "Student Proposal"
93 new_params['url_name'] = "student_proposal"
94 new_params['sidebar_grouping'] = 'Students'
96 new_params['scope_view'] = student_view
97 new_params['scope_redirect'] = redirects.getCreateRedirect
99 new_params['no_create_with_key_fields'] = True
101 patterns = [
102 (r'^%(url_name)s/(?P<access_type>apply)/%(scope)s$',
103 'soc.views.models.%(module_name)s.apply',
104 'Create a new %(name)s'),
105 (r'^%(url_name)s/(?P<access_type>list_self)/%(scope)s$',
106 'soc.views.models.%(module_name)s.list_self',
107 'List my %(name_plural)s'),
108 (r'^%(url_name)s/(?P<access_type>list_orgs)/%(scope)s$',
109 'soc.views.models.%(module_name)s.list_orgs',
110 'List my %(name_plural)s'),
111 (r'^%(url_name)s/(?P<access_type>review)/%(key_fields)s$',
112 'soc.views.models.%(module_name)s.review',
113 'Review %(name)s'),
116 new_params['extra_django_patterns'] = patterns
118 new_params['extra_dynaexclude'] = ['org', 'program', 'score',
119 'status', 'mentor', 'link_id',
120 'possible_mentors']
122 new_params['create_extra_dynaproperties'] = {
123 'content': forms.fields.CharField(required=True,
124 widget=widgets.FullTinyMCE(attrs={'rows': 25, 'cols': 100})),
125 'scope_path': forms.CharField(widget=forms.HiddenInput,
126 required=True),
127 'organization': forms.CharField(label='Organization Link ID',
128 required=True),
129 'clean_organization': cleaning.clean_link_id('organization'),
130 'clean_additional_info': cleaning.clean_url('additional_info'),
131 'clean': cleaning.validate_student_proposal('organization',
132 'scope_path', student_logic, org_logic),
135 new_params['edit_extra_dynaproperties'] = {
136 'organization': forms.CharField(label='Organization Link ID',
137 widget=widgets.ReadOnlyInput),
138 'link_id': forms.CharField(widget=forms.HiddenInput)
141 new_params['edit_template'] = 'soc/student_proposal/edit.html'
142 new_params['review_template'] = 'soc/student_proposal/review.html'
144 params = dicts.merge(params, new_params)
146 super(View, self).__init__(params=params)
148 # create the special form for students
149 dynafields = [
150 {'name': 'organization',
151 'base': forms.CharField,
152 'label': 'Organization Link ID',
153 'widget': widgets.ReadOnlyInput(),
154 'required': False,
158 dynaproperties = params_helper.getDynaFields(dynafields)
160 student_create_form = dynaform.extendDynaForm(
161 dynaform=self._params['create_form'],
162 dynaproperties=dynaproperties)
164 params['student_create_form'] = student_create_form
166 # create the special form for public review
167 dynafields = [
168 {'name': 'comment',
169 'base': forms.CharField,
170 'widget': widgets.FullTinyMCE(attrs={'rows': 10, 'cols': 40}),
171 'label': 'Comment',
172 'required': False,
176 dynaproperties = params_helper.getDynaFields(dynafields)
178 public_review_form = dynaform.newDynaForm(dynamodel=None,
179 dynabase=helper.forms.BaseForm, dynainclude=None,
180 dynaexclude=None, dynaproperties=dynaproperties)
181 params['public_review_form'] = public_review_form
183 # create the special form for mentors
184 dynafields = [
185 {'name': 'score',
186 'base': forms.ChoiceField,
187 'label': 'Score',
188 'initial': 0,
189 'required': False,
190 'passthrough': ['initial', 'required', 'choices'],
191 'example_text':
192 'A score will only be assigned if the review is private!',
193 'choices': [(-4,'-4: Wow. This. Sucks.'),
194 (-3,'-3: Needs a lot of work'),
195 (-2,'-2: This is bad'),
196 (-1,'-1: I dont like this'),
197 (0,'0: No score'),
198 (1,'1: Might have potential'),
199 (2,'2: Good'),
200 (3,'3: Almost there'),
201 (4,'4: Made. Of. Awesome.')]
203 {'name': 'comment',
204 'base': forms.CharField,
205 'widget': widgets.FullTinyMCE(attrs={'rows': 10, 'cols': 40}),
206 'label': 'Comment',
207 'required': False,
209 {'name': 'public',
210 'base': forms.BooleanField,
211 'label': 'Public review',
212 'initial': False,
213 'required': False,
214 'help_text': 'By ticking this box the score will not be assigned, '
215 'and the review will be public.',
219 dynaproperties = params_helper.getDynaFields(dynafields)
221 mentor_review_form = dynaform.newDynaForm(dynamodel=None,
222 dynabase=helper.forms.BaseForm, dynainclude=None,
223 dynaexclude=None, dynaproperties=dynaproperties)
224 params['mentor_review_form'] = mentor_review_form
226 # TODO see if autocomplete can be used for mentor field
227 dynafields = [
228 {'name': 'rank',
229 'base': forms.IntegerField,
230 'label': 'Set to rank',
231 'help_text':
232 'Set this proposal to the given rank (ignores the given score)',
233 'example_text': 'A rank will only be assigned if the review is private!',
234 'min_value': 1,
235 'required': False,
236 'passthrough': ['min_value', 'required', 'help_text'],
238 {'name': 'mentor',
239 'base': forms.CharField,
240 'label': 'Assign Mentor (Link ID)',
241 'required': False,
242 'help_text': 'Fill in the Link ID of the Mentor '
243 'you would like to assign to this Proposal. '
244 'Leave this box empty if you don\'t want any mentor assigned.',
248 dynaproperties = params_helper.getDynaFields(dynafields)
250 admin_review_form = dynaform.extendDynaForm(dynaform=mentor_review_form,
251 dynaproperties=dynaproperties)
253 params['admin_review_form'] = admin_review_form
255 def _editGet(self, request, entity, form):
256 """See base.View._editGet().
259 form.fields['link_id'].initial = entity.link_id
260 form.fields['organization'].initial = entity.org.link_id
262 return super(View, self)._editGet(request, entity, form)
264 def _editPost(self, request, entity, fields):
265 """See base.View._editPost().
268 if not entity:
269 fields['link_id'] = 't%i' %(int(time.time()*100))
270 else:
271 fields['link_id'] = entity.link_id
273 # fill in the scope via call to super
274 super(View, self)._editPost(request, entity, fields)
276 if not entity:
277 # creating a new application so set the program and org field
278 fields['program'] = fields['scope'].scope
280 filter = {'scope': fields['program'],
281 'link_id': fields['organization']}
282 fields['org'] = org_logic.logic.getForFields(filter, unique=True)
284 # explicitly change the last_modified_on since the content has been edited
285 fields['last_modified_on'] = datetime.datetime.now()
287 @decorators.merge_params
288 @decorators.check_access
289 def public(self, request, access_type,
290 page_name=None, params=None, **kwargs):
291 """View in which the student can see and reply to the comments on the
292 Student Proposal.
294 For params see base.view.Public().
297 context = helper.responses.getUniversalContext(request)
298 helper.responses.useJavaScript(context, params['js_uses_all'])
299 context['page_name'] = page_name
301 try:
302 entity = self._logic.getFromKeyFieldsOr404(kwargs)
303 except out_of_band.Error, error:
304 return helper.responses.errorResponse(
305 error, request, template=params['error_public'], context=context)
307 context['entity'] = entity
308 context['entity_type'] = params['name']
309 context['entity_type_url'] = params['url_name']
311 if request.method == 'POST':
312 return self.publicPost(request, context, params, entity, **kwargs)
313 else: # request.method == 'GET'
314 return self.publicGet(request, context, params, entity, **kwargs)
316 def publicPost(self, request, context, params, entity, **kwargs):
317 """Handles the POST request for the entity's public page.
319 Args:
320 entity: the student proposal entity
321 rest: see base.View.public()
324 # populate the form using the POST data
325 form = params['public_review_form'](request.POST)
327 if not form.is_valid():
328 # get some entity specific context
329 self.updatePublicContext(context, entity, params)
331 # return the invalid form response
332 return self._constructResponse(request, entity=entity, context=context,
333 form=form, params=params, template=params['public_template'])
335 # get the commentary
336 fields = form.cleaned_data
337 comment = fields['comment']
339 if comment:
340 # create a new public review containing the comment
341 user_entity = user_logic.logic.getForCurrentAccount()
343 if user_entity.key() == entity.scope.user.key():
344 # student is posting
345 reviewer = entity.scope
346 else:
347 # check if the person commenting is an org_admin
348 # or a mentor for the given proposal
349 fields = {'user': user_entity,
350 'scope': entity.org,
351 'status': 'active',
354 reviewer = org_admin_logic.logic.getForFields(fields, unique=True)
356 if not reviewer:
357 # no org_admin found, maybe it's a mentor?
358 reviewer = mentor_logic.logic.getForFields(filter, unique=True)
360 # create the review (reviewer might be None if a Host or Developer is posting)
361 self._createReviewFor(entity, reviewer, comment, is_public=True)
363 # redirect to the same page
364 return http.HttpResponseRedirect('')
366 def publicGet(self, request, context, params, entity, **kwargs):
367 """Handles the GET request for the entity's public page.
369 Args:
370 entity: the student proposal entity
371 rest see base.View.public()
374 from soc.logic.models.review_follower import logic as review_follower_logic
376 get_dict = request.GET
378 if get_dict.get('subscription') and (
379 get_dict['subscription'] in ['on', 'off']):
381 subscription = get_dict['subscription']
383 # get the current user
384 user_entity = user_logic.logic.getForCurrentAccount()
386 # create the fields that should be in the ReviewFollower entity
387 fields = {'link_id': user_entity.link_id,
388 'scope': entity,
389 'scope_path': entity.key().name(),
390 'user': user_entity
392 # get the keyname for the ReviewFollower entity
393 key_name = review_follower_logic.getKeyNameFromFields(fields)
395 # determine if we should set subscribed_public to True or False
396 if subscription == 'on':
397 fields['subscribed_public'] = True
398 elif subscription == 'off':
399 fields['subscribed_public'] = False
401 # update the ReviewFollower
402 review_follower_logic.updateOrCreateFromKeyName(fields, key_name)
404 # get some entity specific context
405 self.updatePublicContext(context, entity, params)
407 context['form'] = params['public_review_form']()
408 template = params['public_template']
410 return responses.respond(request, template, context=context)
412 def updatePublicContext(self, context, entity, params):
413 """Updates the context for the public page with information from the entity.
415 Args:
416 context: the context that should be updated
417 entity: a student proposal_entity used to set context
418 params: dict with params for the view using this context
421 from soc.logic.models.review import logic as review_logic
422 from soc.logic.models.review_follower import logic as review_follower_logic
424 student_entity = entity.scope
426 context['student'] = student_entity
427 context['student_name'] = student_entity.name()
429 user_entity = user_logic.logic.getForCurrentAccount()
431 # check if the current user is the student
432 if user_entity.key() == student_entity.user.key():
433 # show the proposal edit link
434 context['edit_link'] = redirects.getEditRedirect(entity, params)
436 # check if the current user is subscribed to this proposal's public reviews
437 fields = {'user': user_entity,
438 'scope': entity,
439 'subscribed_public': True}
441 context['is_subscribed'] = review_follower_logic.getForFields(fields,
442 unique=True)
444 context['public_reviews'] = review_logic.getReviewsForEntity(entity,
445 is_public=True, order=['created'])
447 @decorators.merge_params
448 @decorators.check_access
449 def apply(self, request, access_type,
450 page_name=None, params=None, **kwargs):
451 """Special view used to prepopulate the form with the organization
452 contributors template.
454 For params see base.View.public()
456 get_dict = request.GET
458 if get_dict.get('organization'):
459 # organization chosen, prepopulate with template
461 # get the organization
462 student_entity = student_logic.logic.getFromKeyName(kwargs['scope_path'])
463 program_entity = student_entity.scope
465 filter = {'link_id': get_dict['organization'],
466 'scope': program_entity}
468 org_entity = org_logic.logic.getForFields(filter, unique=True)
470 if org_entity:
471 # organization found use special form
472 params['create_form'] = params['student_create_form']
473 kwargs['content'] = org_entity.contrib_template
475 # Create page is an edit page with no key fields
476 empty_kwargs = {}
477 fields = self._logic.getKeyFieldNames()
478 for field in fields:
479 empty_kwargs[field] = None
481 return super(View, self).edit(request, access_type, page_name=page_name,
482 params=params, seed=kwargs, **empty_kwargs)
484 @decorators.merge_params
485 @decorators.check_access
486 def edit(self, request, access_type,
487 page_name=None, params=None, seed=None, **kwargs):
488 """If the POST contains (action, Withdraw) the proposal in kwargs
489 will be marked as invalid.
491 For params see base.View.edit()
494 # check if request.POST contains action
495 post_dict = request.POST
496 if 'action' in post_dict and post_dict['action'] == 'Withdraw':
497 # withdraw this proposal
498 filter = {'scope_path': kwargs['scope_path'],
499 'link_id': kwargs['link_id']}
501 proposal_logic = params['logic']
502 student_proposal_entity = proposal_logic.getForFields(filter, unique=True)
504 # update the entity mark it as invalid
505 proposal_logic.updateEntityProperties(student_proposal_entity,
506 {'status': 'invalid'})
508 # redirect to the program's homepage
509 redirect_url = redirects.getHomeRedirect(student_proposal_entity.program,
510 {'url_name': 'program'})
512 return http.HttpResponseRedirect(redirect_url)
514 return super(View, self).edit(request=request, access_type=access_type,
515 page_name=page_name, params=params, seed=seed, **kwargs)
517 @decorators.merge_params
518 @decorators.check_access
519 def listOrgs(self, request, access_type,
520 page_name=None, params=None, **kwargs):
521 """Lists all organization which the given student can propose to.
523 For params see base.View.public().
526 from soc.views.models import organization as org_view
528 student_entity = student_logic.logic.getFromKeyName(kwargs['scope_path'])
530 filter = {'scope' : student_entity.scope,
531 'status': 'active'}
533 list_params = org_view.view.getParams().copy()
534 list_params['list_description'] = ('List of %(name_plural)s you can send '
535 'your proposal to.') % list_params
536 list_params['list_action'] = (redirects.getStudentProposalRedirect,
537 {'student_key': student_entity.key().name(),
538 'url_name': params['url_name']})
540 return self.list(request, access_type=access_type, page_name=page_name,
541 params=list_params, filter=filter, **kwargs)
543 @decorators.merge_params
544 @decorators.check_access
545 def listSelf(self, request, access_type,
546 page_name=None, params=None, **kwargs):
547 """Lists all proposals from the current logged-in user
548 for the given student.
550 For params see base.View.public().
553 student_entity = student_logic.logic.getFromKeyName(kwargs['scope_path'])
555 filter = {'scope' : student_entity,
556 'status': ['new', 'pending', 'accepted', 'rejected']}
558 list_params = params.copy()
559 list_params['list_description'] = 'List of my %(name_plural)s' % list_params
560 list_params['list_action'] = (redirects.getPublicRedirect, list_params)
562 return self.list(request, access_type=access_type, page_name=page_name,
563 params=list_params, filter=filter, **kwargs)
565 @decorators.merge_params
566 @decorators.check_access
567 def review(self, request, access_type,
568 page_name=None, params=None, **kwargs):
569 """View that allows Organization Admins and Mentors to review the proposal.
571 For Args see base.View.public().
574 try:
575 entity = self._logic.getFromKeyFieldsOr404(kwargs)
576 except out_of_band.Error, error:
577 return helper.responses.errorResponse(
578 error, request, template=params['error_public'])
580 # get the context for this webpage
581 context = responses.getUniversalContext(request)
582 responses.useJavaScript(context, params['js_uses_all'])
583 context['page_name'] = page_name
584 context['entity'] = entity
585 context['entity_type'] = params['name']
586 context['entity_type_url'] = params['url_name']
588 # get the roles important for reviewing an application
589 filter = {'user': user_logic.logic.getForCurrentAccount(),
590 'scope': entity.org,
591 'status': 'active'}
593 org_admin_entity = org_admin_logic.logic.getForFields(filter, unique=True)
594 mentor_entity = mentor_logic.logic.getForFields(filter, unique=True)
596 # decide which form to use
597 if org_admin_entity:
598 form = params['admin_review_form']
599 else:
600 form = params['mentor_review_form']
602 if request.method == 'POST':
603 return self.reviewPost(request, context, params, entity,
604 form, org_admin_entity, mentor_entity, **kwargs)
605 else:
606 # request.method == 'GET'
607 return self.reviewGet(request, context, params, entity,
608 form, org_admin_entity, mentor_entity, **kwargs)
610 def reviewPost(self, request, context, params, entity, form,
611 org_admin, mentor, **kwargs):
612 """Handles the POST request for the proposal review view.
614 Args:
615 entity: the student proposal entity
616 form: the form to use in this view
617 org_admin: org admin entity for the current user/proposal (iff available)
618 mentor: mentor entity for the current user/proposal (iff available)
619 rest: see base.View.public()
621 # populate the form using the POST data
622 form = form(request.POST)
624 if not form.is_valid():
625 # return the invalid form response
626 # get all the extra information that should be in the context
627 review_context = self._getDefaultReviewContext(entity, org_admin, mentor)
628 context = dicts.merge(context, review_context)
630 return self._constructResponse(request, entity=entity, context=context,
631 form=form, params=params, template=params['review_template'])
633 fields = form.cleaned_data
634 is_public = fields['public']
635 comment = fields['comment']
636 given_score = int(fields['score'])
638 if org_admin:
639 # org admin found, try to adjust the assigned mentor
640 self._adjustMentor(entity, fields['mentor'])
641 reviewer = org_admin
643 # try to see if the rank is given and adjust the given_score if needed
644 rank = fields['rank']
645 if rank:
646 ranker = self._logic.getRankerFor(entity)
647 # if a very high rank is filled in use the highest one that returns a score
648 rank = min(ranker.TotalRankedScores(), rank)
649 # ranker uses zero-based ranking
650 score_and_rank = ranker.FindScore(rank-1)
651 # get the score at the requested rank
652 score_at_rank = score_and_rank[0][0]
653 # calculate the score that should be given to end up at the given rank
654 given_score = score_at_rank - entity.score
655 else:
656 # might be None (if Host or Developer is commenting)
657 reviewer = mentor
659 if reviewer and (not is_public) and (given_score is not 0):
660 # if it is not a public comment and it's made by a member of the
661 # organization we update the score of the proposal
662 new_score = given_score + entity.score
664 properties = {'score': new_score}
666 # if the proposal is new we change it status to pending
667 if entity.status == 'new':
668 properties['status'] = 'pending'
670 # update the proposal with the new score
671 self._logic.updateEntityProperties(entity, properties)
673 # create the review entity
674 if comment or (given_score is not 0):
675 self._createReviewFor(entity, reviewer, comment, given_score, is_public)
677 # redirect to the same page
678 return http.HttpResponseRedirect('')
680 def reviewGet(self, request, context, params, entity, form,
681 org_admin, mentor, **kwargs):
682 """Handles the GET request for the proposal review view.
684 Args:
685 entity: the student proposal entity
686 form: the form to use in this view
687 org_admin: org admin entity for the current user/proposal (iff available)
688 mentor: mentor entity for the current user/proposal (iff available)
689 rest: see base.View.public()
692 from soc.logic.models.review_follower import logic as review_follower_logic
694 get_dict = request.GET
696 # check if the current user is a mentor and wants
697 # to change his role for this app
698 choice = get_dict.get('mentor')
699 if mentor and choice:
700 self._adjustPossibleMentors(entity, mentor, choice)
702 is_ineligible = get_dict.get('ineligible')
703 if org_admin and is_ineligible:
704 # mark the proposal invalid and return to the list
705 properties = {'status': 'invalid'}
706 self._logic.updateEntityProperties(entity, properties)
708 redirect = redirects.getListProposalsRedirect(entity.org,
709 {'url_name': 'org'})
710 return http.HttpResponseRedirect(redirect)
712 # check if we should change the subscription state for the current user
713 public_subscription = None
714 private_subscription = None
716 if get_dict.get('public_subscription') and (
717 get_dict['public_subscription'] in ['on', 'off']):
719 public_subscription = get_dict['public_subscription'] == 'on'
721 if get_dict.get('private_subscription') and (
722 get_dict['private_subscription'] in ['on', 'off']):
723 private_subscription = get_dict['private_subscription'] == 'on'
725 if public_subscription != None or private_subscription != None:
726 # get the current user
727 user_entity = user_logic.logic.getForCurrentAccount()
729 # create the fields that should be in the ReviewFollower entity
730 fields = {'link_id': user_entity.link_id,
731 'scope': entity,
732 'scope_path': entity.key().name(),
733 'user': user_entity
735 # get the keyname for the ReviewFollower entity
736 key_name = review_follower_logic.getKeyNameFromFields(fields)
738 # determine which subscription properties we should change
739 if public_subscription != None:
740 fields['subscribed_public'] = public_subscription
742 if private_subscription != None:
743 fields['subscribed_private'] = private_subscription
745 # update the ReviewFollower
746 review_follower_logic.updateOrCreateFromKeyName(fields, key_name)
748 # set the initial score since the default is ignored
749 initial = {'score': 0}
751 if org_admin and entity.mentor:
752 # set the mentor field to the current mentor
753 initial['mentor'] = entity.mentor.link_id
755 context['form'] = form(initial)
757 # get all the extra information that should be in the context
758 review_context = self._getDefaultReviewContext(entity, org_admin, mentor)
759 context = dicts.merge(context, review_context)
761 template = params['review_template']
763 return responses.respond(request, template, context=context)
765 def _getDefaultReviewContext(self, entity, org_admin,
766 mentor):
767 """Returns the default context for the review page.
769 Args:
770 entity: Student Proposal entity
771 org_admin: org admin entity for the current user/proposal (iff available)
772 mentor: mentor entity for the current user/proposal (iff available)
775 from soc.logic.models.review import logic as review_logic
776 from soc.logic.models.review_follower import logic as review_follower_logic
778 context = {}
780 context['student'] = entity.scope
781 context['student_name'] = entity.scope.name()
783 if entity.mentor:
784 context['mentor_name'] = entity.mentor.name()
785 else:
786 context['mentor_name'] = "No mentor assigned"
788 # set the possible mentors in the context
789 possible_mentors = entity.possible_mentors
791 if not possible_mentors:
792 context['possible_mentors'] = "None"
793 else:
794 mentor_names = []
796 for mentor_key in possible_mentors:
797 possible_mentor = mentor_logic.logic.getFromKeyName(mentor_key.name())
798 mentor_names.append(possible_mentor.name())
800 context['possible_mentors'] = ', '.join(mentor_names)
802 # TODO(ljvderijk) listing of total given scores per mentor
803 # a dict with key as role.user ?
805 # order the reviews by ascending creation date
806 order = ['created']
808 # get the public reviews
809 context['public_reviews'] = review_logic.getReviewsForEntity(entity,
810 is_public=True, order=order)
812 # get the private reviews
813 context['private_reviews'] = review_logic.getReviewsForEntity(entity,
814 is_public=False, order=order)
816 # which button should we show to the mentor?
817 if mentor:
818 if mentor.key() in possible_mentors:
819 # show "No longer willing to mentor"
820 context['remove_me_as_mentor'] = True
821 else:
822 # show "I am willing to mentor"
823 context['add_me_as_mentor'] = True
825 if org_admin:
826 context['is_org_admin'] = True
828 user_entity = user_logic.logic.getForCurrentAccount()
830 # check if the current user is subscribed to public or private reviews
831 fields = {'scope': entity,
832 'user': user_entity,}
833 follower_entity = review_follower_logic.getForFields(fields, unique=True)
835 if follower_entity:
836 context['is_subscribed_public'] = follower_entity.subscribed_public
837 context['is_subscribed_private'] = follower_entity.subscribed_private
839 return context
841 def _adjustPossibleMentors(self, entity, mentor, choice):
842 """Adjusts the possible mentors list for a proposal.
844 Args:
845 entity: Student Proposal entity
846 mentor: Mentor entity
847 choice: 1 means want to mentor, 0 do not want to mentor
849 possible_mentors = entity.possible_mentors
851 if choice == '1':
852 # add the mentor to possible mentors list if not already in
853 if mentor.key() not in possible_mentors:
854 possible_mentors.append(mentor.key())
855 fields = {'possible_mentors': possible_mentors}
856 self._logic.updateEntityProperties(entity, fields)
857 elif choice == '0':
858 # remove the mentor from the possible mentors list
859 if mentor.key() in possible_mentors:
860 possible_mentors.remove(mentor.key())
861 fields = {'possible_mentors': possible_mentors}
862 self._logic.updateEntityProperties(entity, fields)
864 def _adjustMentor(self, entity, mentor_id):
865 """Changes the mentor to the given link_id.
867 Args:
868 entity: Student Proposal entity
869 mentor_id: Link ID of the mentor that needs to be assigned
870 Iff not given then removes the assigned mentor
873 if entity.mentor and entity.mentor.link_id == mentor_id:
874 # no need to change
875 return
877 if mentor_id:
878 # try to locate the mentor
879 fields = {'link_id': mentor_id,
880 'scope': entity.org,
881 'status': 'active'}
883 mentor_entity = mentor_logic.logic.getForFields(fields, unique=True)
885 if not mentor_entity:
886 # no mentor found, do not update
887 return
888 else:
889 # reset to None
890 mentor_entity = None
892 # update the proposal
893 properties = {'mentor': mentor_entity}
894 self._logic.updateEntityProperties(entity, properties)
896 def _createReviewFor(self, entity, reviewer, comment, score=0, is_public=True):
897 """Creates a review for the given proposal and sends out a message to all followers.
899 Args:
900 entity: Student Proposal entity for which the review should be created
901 reviewer: A role entity of the reviewer (if possible, else None)
902 comment: The textual contents of the review
903 score: The score of the review (only used if the review is not public)
904 is_public: Determines if the review is a public review
907 import time
909 from soc.logic.helper import notifications as notifications_helper
910 from soc.logic.models.review import logic as review_logic
911 from soc.logic.models.review_follower import logic as review_follower_logic
913 # create the fields for the review entity
914 fields = {'link_id': 't%i' %(int(time.time()*100)),
915 'scope': entity,
916 'scope_path': entity.key().name(),
917 'author': user_logic.logic.getForCurrentAccount(),
918 'content': comment,
919 'is_public': is_public,
920 'reviewer': reviewer
923 # add the given score if the review is not public
924 if not is_public:
925 fields['score'] = score
927 # create a new Review
928 key_name = review_logic.getKeyNameFromFields(fields)
929 review_entity = review_logic.updateOrCreateFromKeyName(fields, key_name)
931 # get all followers
932 fields = {'scope': entity}
934 if is_public:
935 fields['subscribed_public'] = True
936 else:
937 fields['subscribed_private'] = True
939 followers = review_follower_logic.getForFields(fields)
941 if is_public:
942 # redirect to public page
943 redirect_url = redirects.getPublicRedirect(entity, self._params)
944 else:
945 # redirect to review page
946 redirect_url = redirects.getReviewRedirect(entity, self._params)
948 for follower in followers:
949 # sent to every follower except the reviewer
950 if follower.user.key() != review_entity.author.key():
951 notifications_helper.sendNewReviewNotification(follower.user,
952 review_entity, entity.title, redirect_url)
955 view = View()
957 admin = decorators.view(view.admin)
958 apply = decorators.view(view.apply)
959 create = decorators.view(view.create)
960 delete = decorators.view(view.delete)
961 edit = decorators.view(view.edit)
962 list = decorators.view(view.list)
963 list_orgs = decorators.view(view.listOrgs)
964 list_self = decorators.view(view.listSelf)
965 public = decorators.view(view.public)
966 review = decorators.view(view.review)
967 export = decorators.view(view.export)
968 pick = decorators.view(view.pick)