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.
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
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.
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.
62 params: a dict with params for this View
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']
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']])]
83 ('checkIsStudent', ['scope_path', ['active']]),
84 ('checkCanStudentPropose', 'scope_path')]
85 rights
['review'] = [('checkRoleAndStatusForStudentProposal',
86 [['org_admin', 'mentor', 'host'],
87 ['active'], ['new', 'pending']])]
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
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',
116 new_params
['extra_django_patterns'] = patterns
118 new_params
['extra_dynaexclude'] = ['org', 'program', 'score',
119 'status', 'mentor', 'link_id',
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
,
127 'organization': forms
.CharField(label
='Organization Link ID',
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
150 {'name': 'organization',
151 'base': forms
.CharField
,
152 'label': 'Organization Link ID',
153 'widget': widgets
.ReadOnlyInput(),
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
169 'base': forms
.CharField
,
170 'widget': widgets
.FullTinyMCE(attrs
={'rows': 10, 'cols': 40}),
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
186 'base': forms
.ChoiceField
,
190 'passthrough': ['initial', 'required', 'choices'],
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'),
198 (1,'1: Might have potential'),
200 (3,'3: Almost there'),
201 (4,'4: Made. Of. Awesome.')]
204 'base': forms
.CharField
,
205 'widget': widgets
.FullTinyMCE(attrs
={'rows': 10, 'cols': 40}),
210 'base': forms
.BooleanField
,
211 'label': 'Public review',
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
229 'base': forms
.IntegerField
,
230 'label': 'Set to rank',
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!',
236 'passthrough': ['min_value', 'required', 'help_text'],
239 'base': forms
.CharField
,
240 'label': 'Assign Mentor (Link ID)',
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().
269 fields
['link_id'] = 't%i' %(int(time
.time()*100))
271 fields
['link_id'] = entity
.link_id
273 # fill in the scope via call to super
274 super(View
, self
)._editPost
(request
, entity
, fields
)
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
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
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.
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'])
336 fields
= form
.cleaned_data
337 comment
= fields
['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():
345 reviewer
= entity
.scope
347 # check if the person commenting is an org_admin
348 # or a mentor for the given proposal
349 fields
= {'user': user_entity
,
354 reviewer
= org_admin_logic
.logic
.getForFields(fields
, unique
=True)
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.
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
,
389 'scope_path': entity
.key().name(),
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.
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
,
439 'subscribed_public': True}
441 context
['is_subscribed'] = review_follower_logic
.getForFields(fields
,
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)
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
477 fields
= self
._logic
.getKeyFieldNames()
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
,
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().
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(),
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
598 form
= params
['admin_review_form']
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
)
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.
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'])
639 # org admin found, try to adjust the assigned mentor
640 self
._adjustMentor
(entity
, fields
['mentor'])
643 # try to see if the rank is given and adjust the given_score if needed
644 rank
= fields
['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
656 # might be None (if Host or Developer is commenting)
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.
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
,
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
,
732 'scope_path': entity
.key().name(),
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
,
767 """Returns the default context for the review page.
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
780 context
['student'] = entity
.scope
781 context
['student_name'] = entity
.scope
.name()
784 context
['mentor_name'] = entity
.mentor
.name()
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"
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
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?
818 if mentor
.key() in possible_mentors
:
819 # show "No longer willing to mentor"
820 context
['remove_me_as_mentor'] = True
822 # show "I am willing to mentor"
823 context
['add_me_as_mentor'] = True
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)
836 context
['is_subscribed_public'] = follower_entity
.subscribed_public
837 context
['is_subscribed_private'] = follower_entity
.subscribed_private
841 def _adjustPossibleMentors(self
, entity
, mentor
, choice
):
842 """Adjusts the possible mentors list for a proposal.
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
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
)
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.
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
:
878 # try to locate the mentor
879 fields
= {'link_id': mentor_id
,
883 mentor_entity
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
885 if not mentor_entity
:
886 # no mentor found, do not update
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.
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
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)),
916 'scope_path': entity
.key().name(),
917 'author': user_logic
.logic
.getForCurrentAccount(),
919 'is_public': is_public
,
923 # add the given score if the review is not 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
)
932 fields
= {'scope': entity
}
935 fields
['subscribed_public'] = True
937 fields
['subscribed_private'] = True
939 followers
= review_follower_logic
.getForFields(fields
)
942 # redirect to public page
943 redirect_url
= redirects
.getPublicRedirect(entity
, self
._params
)
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
)
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
)