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', False]),
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', False])]
80 rights
['list_self'] = [
81 ('checkIsStudent', ['scope_path', ['active', 'inactive']])]
83 ('checkIsStudent', ['scope_path', ['active']]),
84 ('checkCanStudentPropose', ['scope_path', True])]
85 rights
['review'] = [('checkRoleAndStatusForStudentProposal',
86 [['org_admin', 'mentor', 'host'],
87 ['active'], ['new', 'pending', 'invalid']])]
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_abstract': cleaning
.clean_content_length('abstract'),
130 'clean_content': cleaning
.clean_html_content('content'),
131 'clean_organization': cleaning
.clean_link_id('organization'),
132 'clean_additional_info': cleaning
.clean_url('additional_info'),
133 'clean': cleaning
.validate_student_proposal('organization',
134 'scope_path', student_logic
, org_logic
),
137 new_params
['edit_extra_dynaproperties'] = {
138 'organization': forms
.CharField(label
='Organization Link ID',
139 widget
=widgets
.ReadOnlyInput
),
140 'link_id': forms
.CharField(widget
=forms
.HiddenInput
)
143 new_params
['edit_template'] = 'soc/student_proposal/edit.html'
144 new_params
['review_template'] = 'soc/student_proposal/review.html'
146 params
= dicts
.merge(params
, new_params
)
148 super(View
, self
).__init
__(params
=params
)
150 # create the special form for students
152 {'name': 'organization',
153 'base': forms
.CharField
,
154 'label': 'Organization Link ID',
155 'widget': widgets
.ReadOnlyInput(),
160 dynaproperties
= params_helper
.getDynaFields(dynafields
)
162 student_create_form
= dynaform
.extendDynaForm(
163 dynaform
=self
._params
['create_form'],
164 dynaproperties
=dynaproperties
)
166 params
['student_create_form'] = student_create_form
168 # create the special form for public review
171 'base': forms
.CharField
,
172 'widget': widgets
.FullTinyMCE(attrs
={'rows': 10, 'cols': 40}),
178 dynaproperties
= params_helper
.getDynaFields(dynafields
)
179 dynaproperties
['clean_comment'] = cleaning
.clean_html_content('comment')
181 public_review_form
= dynaform
.newDynaForm(dynamodel
=None,
182 dynabase
=helper
.forms
.BaseForm
, dynainclude
=None,
183 dynaexclude
=None, dynaproperties
=dynaproperties
)
184 params
['public_review_form'] = public_review_form
186 # create the special form for mentors
189 'base': forms
.ChoiceField
,
193 'passthrough': ['initial', 'required', 'choices'],
195 'A score will only be assigned if the review is private!',
196 'choices': [(-4,'-4: Wow. This. Sucks.'),
197 (-3,'-3: Needs a lot of work'),
198 (-2,'-2: This is bad'),
199 (-1,'-1: I dont like this'),
201 (1,'1: Might have potential'),
203 (3,'3: Almost there'),
204 (4,'4: Made. Of. Awesome.')]
207 'base': forms
.CharField
,
208 'widget': widgets
.FullTinyMCE(attrs
={'rows': 10, 'cols': 40}),
213 'base': forms
.BooleanField
,
214 'label': 'Public review',
217 'help_text': 'By ticking this box the score will not be assigned, '
218 'and the review will be public.',
222 dynaproperties
= params_helper
.getDynaFields(dynafields
)
223 dynaproperties
['clean_comment'] = cleaning
.clean_html_content('comment')
225 mentor_review_form
= dynaform
.newDynaForm(dynamodel
=None,
226 dynabase
=helper
.forms
.BaseForm
, dynainclude
=None,
227 dynaexclude
=None, dynaproperties
=dynaproperties
)
228 params
['mentor_review_form'] = mentor_review_form
230 # TODO see if autocomplete can be used for mentor field
233 'base': forms
.IntegerField
,
234 'label': 'Set to rank',
236 'Set this proposal to the given rank (ignores the given score)',
237 'example_text': 'A rank will only be assigned if the '
238 'review is private!',
241 'passthrough': ['min_value', 'required', 'help_text'],
244 'base': forms
.CharField
,
245 'label': 'Assign Mentor (Link ID)',
247 'help_text': 'Fill in the Link ID of the Mentor '
248 'you would like to assign to this Proposal. '
249 'Leave this box empty if you don\'t want any mentor assigned.',
253 dynaproperties
= params_helper
.getDynaFields(dynafields
)
254 dynaproperties
['clean_comment'] = cleaning
.clean_html_content('comment')
256 admin_review_form
= dynaform
.extendDynaForm(dynaform
=mentor_review_form
,
257 dynaproperties
=dynaproperties
)
259 params
['admin_review_form'] = admin_review_form
261 def _editGet(self
, request
, entity
, form
):
262 """See base.View._editGet().
265 form
.fields
['link_id'].initial
= entity
.link_id
266 form
.fields
['organization'].initial
= entity
.org
.link_id
268 return super(View
, self
)._editGet
(request
, entity
, form
)
270 def _editPost(self
, request
, entity
, fields
):
271 """See base.View._editPost().
275 fields
['link_id'] = 't%i' % (int(time
.time()*100))
277 fields
['link_id'] = entity
.link_id
279 # fill in the scope via call to super
280 super(View
, self
)._editPost
(request
, entity
, fields
)
283 # creating a new application so set the program and org field
284 fields
['program'] = fields
['scope'].scope
286 filter = {'scope': fields
['program'],
287 'link_id': fields
['organization']}
288 fields
['org'] = org_logic
.logic
.getForFields(filter, unique
=True)
290 # explicitly change the last_modified_on since the content has been edited
291 fields
['last_modified_on'] = datetime
.datetime
.now()
293 @decorators.merge_params
294 @decorators.check_access
295 def public(self
, request
, access_type
,
296 page_name
=None, params
=None, **kwargs
):
297 """View in which the student can see and reply to the comments on the
300 For params see base.view.Public().
303 context
= helper
.responses
.getUniversalContext(request
)
304 helper
.responses
.useJavaScript(context
, params
['js_uses_all'])
305 context
['page_name'] = page_name
308 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
309 except out_of_band
.Error
, error
:
310 return helper
.responses
.errorResponse(
311 error
, request
, template
=params
['error_public'], context
=context
)
313 context
['entity'] = entity
314 context
['entity_type'] = params
['name']
315 context
['entity_type_url'] = params
['url_name']
317 if request
.method
== 'POST':
318 return self
.publicPost(request
, context
, params
, entity
, **kwargs
)
319 else: # request.method == 'GET'
320 return self
.publicGet(request
, context
, params
, entity
, **kwargs
)
322 def publicPost(self
, request
, context
, params
, entity
, **kwargs
):
323 """Handles the POST request for the entity's public page.
326 entity: the student proposal entity
327 rest: see base.View.public()
330 # populate the form using the POST data
331 form
= params
['public_review_form'](request
.POST
)
333 if not form
.is_valid():
334 # get some entity specific context
335 self
.updatePublicContext(context
, entity
, params
)
337 # return the invalid form response
338 return self
._constructResponse
(request
, entity
=entity
, context
=context
,
339 form
=form
, params
=params
, template
=params
['public_template'])
342 fields
= form
.cleaned_data
343 comment
= fields
['comment']
346 # create a new public review containing the comment
347 user_entity
= user_logic
.logic
.getForCurrentAccount()
349 if user_entity
.key() == entity
.scope
.user
.key():
351 reviewer
= entity
.scope
353 # check if the person commenting is an org_admin
354 # or a mentor for the given proposal
355 fields
= {'user': user_entity
,
360 reviewer
= org_admin_logic
.logic
.getForFields(fields
, unique
=True)
363 # no org_admin found, maybe it's a mentor?
364 reviewer
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
366 # create the review (reviewer might be None
367 # if a Host or Developer is posting)
368 self
._createReviewFor
(entity
, reviewer
, comment
, is_public
=True)
370 # redirect to the same page
371 return http
.HttpResponseRedirect('')
373 def publicGet(self
, request
, context
, params
, entity
, **kwargs
):
374 """Handles the GET request for the entity's public page.
377 entity: the student proposal entity
378 rest see base.View.public()
381 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
383 get_dict
= request
.GET
385 if get_dict
.get('subscription') and (
386 get_dict
['subscription'] in ['on', 'off']):
388 subscription
= get_dict
['subscription']
390 # get the current user
391 user_entity
= user_logic
.logic
.getForCurrentAccount()
393 # create the fields that should be in the ReviewFollower entity
394 fields
= {'link_id': user_entity
.link_id
,
396 'scope_path': entity
.key().name(),
399 # get the keyname for the ReviewFollower entity
400 key_name
= review_follower_logic
.getKeyNameFromFields(fields
)
402 # determine if we should set subscribed_public to True or False
403 if subscription
== 'on':
404 fields
['subscribed_public'] = True
405 elif subscription
== 'off':
406 fields
['subscribed_public'] = False
408 # update the ReviewFollower
409 review_follower_logic
.updateOrCreateFromKeyName(fields
, key_name
)
411 # get some entity specific context
412 self
.updatePublicContext(context
, entity
, params
)
414 context
['form'] = params
['public_review_form']()
415 template
= params
['public_template']
417 return responses
.respond(request
, template
, context
=context
)
419 def updatePublicContext(self
, context
, entity
, params
):
420 """Updates the context for the public page with information from the entity.
423 context: the context that should be updated
424 entity: a student proposal_entity used to set context
425 params: dict with params for the view using this context
428 from soc
.logic
.models
.review
import logic
as review_logic
429 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
431 student_entity
= entity
.scope
433 context
['student'] = student_entity
434 context
['student_name'] = student_entity
.name()
436 user_entity
= user_logic
.logic
.getForCurrentAccount()
438 # check if the current user is the student
439 if user_entity
.key() == student_entity
.user
.key():
440 # show the proposal edit link
441 context
['edit_link'] = redirects
.getEditRedirect(entity
, params
)
443 # check if the current user is subscribed to this proposal's public reviews
444 fields
= {'user': user_entity
,
446 'subscribed_public': True}
448 context
['is_subscribed'] = review_follower_logic
.getForFields(fields
,
451 context
['public_reviews'] = review_logic
.getReviewsForEntity(entity
,
452 is_public
=True, order
=['created'])
454 @decorators.merge_params
455 @decorators.check_access
456 def apply(self
, request
, access_type
,
457 page_name
=None, params
=None, **kwargs
):
458 """Special view used to prepopulate the form with the organization
459 contributors template.
461 For params see base.View.public()
463 get_dict
= request
.GET
465 if get_dict
.get('organization'):
466 # organization chosen, prepopulate with template
468 # get the organization
469 student_entity
= student_logic
.logic
.getFromKeyName(kwargs
['scope_path'])
470 program_entity
= student_entity
.scope
472 filter = {'link_id': get_dict
['organization'],
473 'scope': program_entity
}
475 org_entity
= org_logic
.logic
.getForFields(filter, unique
=True)
478 # organization found use special form
479 params
['create_form'] = params
['student_create_form']
480 kwargs
['content'] = org_entity
.contrib_template
482 # Create page is an edit page with no key fields
484 fields
= self
._logic
.getKeyFieldNames()
486 empty_kwargs
[field
] = None
488 return super(View
, self
).edit(request
, access_type
, page_name
=page_name
,
489 params
=params
, seed
=kwargs
, **empty_kwargs
)
491 @decorators.merge_params
492 @decorators.check_access
493 def edit(self
, request
, access_type
,
494 page_name
=None, params
=None, seed
=None, **kwargs
):
495 """If the POST contains (action, Withdraw) the proposal in kwargs
496 will be marked as invalid.
498 For params see base.View.edit()
501 # check if request.POST contains action
502 post_dict
= request
.POST
503 if 'action' in post_dict
and post_dict
['action'] == 'Withdraw':
504 # withdraw this proposal
505 filter = {'scope_path': kwargs
['scope_path'],
506 'link_id': kwargs
['link_id']}
508 proposal_logic
= params
['logic']
509 student_proposal_entity
= proposal_logic
.getForFields(filter, unique
=True)
510 reviewer
= student_proposal_entity
.scope
512 # update the entity mark it as invalid
513 proposal_logic
.updateEntityProperties(student_proposal_entity
,
514 {'status': 'invalid'})
516 # redirect to the program's homepage
517 redirect_url
= redirects
.getHomeRedirect(student_proposal_entity
.program
,
518 {'url_name': 'program'})
520 comment
= "Student withdrew proposal."
521 self
._createReviewFor
(student_proposal_entity
, reviewer
, comment
)
522 return http
.HttpResponseRedirect(redirect_url
)
524 return super(View
, self
).edit(request
=request
, access_type
=access_type
,
525 page_name
=page_name
, params
=params
, seed
=seed
, **kwargs
)
527 @decorators.merge_params
528 @decorators.check_access
529 def listOrgs(self
, request
, access_type
,
530 page_name
=None, params
=None, **kwargs
):
531 """Lists all organization which the given student can propose to.
533 For params see base.View.public().
536 from soc
.views
.models
import organization
as org_view
538 student_entity
= student_logic
.logic
.getFromKeyName(kwargs
['scope_path'])
540 filter = {'scope': student_entity
.scope
,
543 list_params
= org_view
.view
.getParams().copy()
544 list_params
['list_description'] = ('List of %(name_plural)s you can send '
545 'your proposal to.') % list_params
546 list_params
['list_action'] = (redirects
.getStudentProposalRedirect
,
547 {'student_key': student_entity
.key().name(),
548 'url_name': params
['url_name']})
550 return self
.list(request
, access_type
=access_type
, page_name
=page_name
,
551 params
=list_params
, filter=filter, **kwargs
)
553 @decorators.merge_params
554 @decorators.check_access
555 def listSelf(self
, request
, access_type
,
556 page_name
=None, params
=None, **kwargs
):
557 """Lists all proposals from the current logged-in user
558 for the given student.
560 For params see base.View.public().
563 student_entity
= student_logic
.logic
.getFromKeyName(kwargs
['scope_path'])
565 filter = {'scope' : student_entity
,
566 'status': ['new', 'pending', 'accepted', 'rejected']}
568 list_params
= params
.copy()
569 list_params
['list_description'] = 'List of my %(name_plural)s' % list_params
570 list_params
['list_action'] = (redirects
.getPublicRedirect
, list_params
)
572 return self
.list(request
, access_type
=access_type
, page_name
=page_name
,
573 params
=list_params
, filter=filter, **kwargs
)
575 @decorators.merge_params
576 @decorators.check_access
577 def review(self
, request
, access_type
,
578 page_name
=None, params
=None, **kwargs
):
579 """View that allows Organization Admins and Mentors to review the proposal.
581 For Args see base.View.public().
585 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
586 except out_of_band
.Error
, error
:
587 return helper
.responses
.errorResponse(
588 error
, request
, template
=params
['error_public'])
590 # get the context for this webpage
591 context
= responses
.getUniversalContext(request
)
592 responses
.useJavaScript(context
, params
['js_uses_all'])
593 context
['page_name'] = '%s "%s" from %s' % (page_name
, entity
.title
,
595 context
['entity'] = entity
596 context
['entity_type'] = params
['name']
597 context
['entity_type_url'] = params
['url_name']
599 # get the roles important for reviewing an application
600 filter = {'user': user_logic
.logic
.getForCurrentAccount(),
604 org_admin_entity
= org_admin_logic
.logic
.getForFields(filter, unique
=True)
605 mentor_entity
= mentor_logic
.logic
.getForFields(filter, unique
=True)
607 # decide which form to use
609 form
= params
['admin_review_form']
611 form
= params
['mentor_review_form']
613 if request
.method
== 'POST':
614 return self
.reviewPost(request
, context
, params
, entity
,
615 form
, org_admin_entity
, mentor_entity
, **kwargs
)
617 # request.method == 'GET'
618 return self
.reviewGet(request
, context
, params
, entity
,
619 form
, org_admin_entity
, mentor_entity
, **kwargs
)
621 def reviewPost(self
, request
, context
, params
, entity
, form
,
622 org_admin
, mentor
, **kwargs
):
623 """Handles the POST request for the proposal review view.
626 entity: the student proposal entity
627 form: the form to use in this view
628 org_admin: org admin entity for the current user/proposal (iff available)
629 mentor: mentor entity for the current user/proposal (iff available)
630 rest: see base.View.public()
632 # populate the form using the POST data
633 form
= form(request
.POST
)
635 if not form
.is_valid():
636 # return the invalid form response
637 # get all the extra information that should be in the context
638 review_context
= self
._getDefaultReviewContext
(entity
, org_admin
, mentor
)
639 context
= dicts
.merge(context
, review_context
)
641 return self
._constructResponse
(request
, entity
=entity
, context
=context
,
642 form
=form
, params
=params
, template
=params
['review_template'])
644 fields
= form
.cleaned_data
645 is_public
= fields
['public']
646 comment
= fields
['comment']
647 given_score
= int(fields
['score'])
650 # org admin found, try to adjust the assigned mentor
651 self
._adjustMentor
(entity
, fields
['mentor'])
654 # try to see if the rank is given and adjust the given_score if needed
655 rank
= fields
['rank']
657 ranker
= self
._logic
.getRankerFor(entity
)
658 # if a very high rank is filled in use the highest
659 # one that returns a score
660 rank
= min(ranker
.TotalRankedScores(), rank
)
661 # ranker uses zero-based ranking
662 score_and_rank
= ranker
.FindScore(rank
-1)
663 # get the score at the requested rank
664 score_at_rank
= score_and_rank
[0][0]
665 # calculate the score that should be given to end up at the given rank
666 given_score
= score_at_rank
- entity
.score
668 # might be None (if Host or Developer is commenting)
671 # store the properties to update the proposal with
674 if reviewer
and (not is_public
) and (given_score
is not 0):
675 # if it is not a public comment and it's made by a member of the
676 # organization we update the score of the proposal
677 new_score
= given_score
+ entity
.score
678 properties
= {'score': new_score
}
680 if comment
or (given_score
is not 0):
681 # if the proposal is new we change it status to pending
682 if entity
.status
== 'new':
683 properties
['status'] = 'pending'
685 # create the review entity
686 self
._createReviewFor
(entity
, reviewer
, comment
, given_score
, is_public
)
688 if properties
.values():
689 # there is something to update
690 self
._logic
.updateEntityProperties(entity
, properties
)
692 # redirect to the same page
693 return http
.HttpResponseRedirect('')
695 def reviewGet(self
, request
, context
, params
, entity
, form
,
696 org_admin
, mentor
, **kwargs
):
697 """Handles the GET request for the proposal review view.
700 entity: the student proposal entity
701 form: the form to use in this view
702 org_admin: org admin entity for the current user/proposal (iff available)
703 mentor: mentor entity for the current user/proposal (iff available)
704 rest: see base.View.public()
707 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
709 get_dict
= request
.GET
711 # check if the current user is a mentor and wants
712 # to change his role for this app
713 choice
= get_dict
.get('mentor')
714 if mentor
and choice
:
715 self
._adjustPossibleMentors
(entity
, mentor
, choice
)
717 ineligible
= get_dict
.get('ineligible')
724 if (org_admin
or mentor
) and ineligible
!= None:
725 ineligible
= int(ineligible
)
727 # mark the proposal invalid and return to the list
728 properties
= {'status': 'invalid'}
729 self
._logic
.updateEntityProperties(entity
, properties
)
731 redirect
= redirects
.getListProposalsRedirect(entity
.org
,
733 comment
= "Marked Student Proposal as Ineligible."
734 self
._createReviewFor
(entity
, reviewer
, comment
, is_public
=False)
735 return http
.HttpResponseRedirect(redirect
)
736 elif ineligible
== 0:
737 # mark the proposal as new and return to the list
738 properties
= {'status': 'new'}
739 self
._logic
.updateEntityProperties(entity
, properties
)
741 redirect
= redirects
.getListProposalsRedirect(entity
.org
,
743 comment
= "Marked Student Proposal as Eligible."
744 self
._createReviewFor
(entity
, reviewer
, comment
, is_public
=False)
745 return http
.HttpResponseRedirect(redirect
)
747 # check if we should change the subscription state for the current user
748 public_subscription
= None
749 private_subscription
= None
751 if get_dict
.get('public_subscription') and (
752 get_dict
['public_subscription'] in ['on', 'off']):
754 public_subscription
= get_dict
['public_subscription'] == 'on'
756 if get_dict
.get('private_subscription') and (
757 get_dict
['private_subscription'] in ['on', 'off']):
758 private_subscription
= get_dict
['private_subscription'] == 'on'
760 if public_subscription
!= None or private_subscription
!= None:
761 # get the current user
762 user_entity
= user_logic
.logic
.getForCurrentAccount()
764 # create the fields that should be in the ReviewFollower entity
765 fields
= {'link_id': user_entity
.link_id
,
767 'scope_path': entity
.key().name(),
770 # get the keyname for the ReviewFollower entity
771 key_name
= review_follower_logic
.getKeyNameFromFields(fields
)
773 # determine which subscription properties we should change
774 if public_subscription
!= None:
775 fields
['subscribed_public'] = public_subscription
777 if private_subscription
!= None:
778 fields
['subscribed_private'] = private_subscription
780 # update the ReviewFollower
781 review_follower_logic
.updateOrCreateFromKeyName(fields
, key_name
)
783 # set the initial score since the default is ignored
784 initial
= {'score': 0}
786 if org_admin
and entity
.mentor
:
787 # set the mentor field to the current mentor
788 initial
['mentor'] = entity
.mentor
.link_id
790 context
['form'] = form(initial
)
792 # create the special form for mentors
793 comment_public
= ['public', 'comment']
794 comment_private
= ['score']
795 comment_admin
= ['rank', 'mentor']
796 class FilterForm(object):
797 """Helper class used for form filtering.
799 def __init__(self
, form
, fields
):
801 self
.__fields
= fields
805 """Property that returns all fields as dictionary."""
806 fields
= self
.__form
.fields
.iteritems()
807 return dict([(k
, i
) for k
, i
in fields
if k
in self
.__fields
])
810 for field
in self
.__form
:
811 if field
.name
not in self
.__fields
:
816 def __getattr__(self
, key
, default
=_marker
):
817 if default
is self
._marker
:
818 return getattr(self
.__form
, key
)
820 return getattr(self
.__form
, key
, default
)
822 context
['form'] = form(initial
)
823 context
['comment_public'] = FilterForm(context
['form'], comment_public
)
824 context
['comment_private'] = FilterForm(context
['form'], comment_private
)
825 context
['comment_admin'] = FilterForm(context
['form'], comment_admin
)
827 # get all the extra information that should be in the context
828 review_context
= self
._getDefaultReviewContext
(entity
, org_admin
, mentor
)
829 context
= dicts
.merge(context
, review_context
)
831 template
= params
['review_template']
833 return responses
.respond(request
, template
, context
=context
)
835 def _getDefaultReviewContext(self
, entity
, org_admin
,
837 """Returns the default context for the review page.
840 entity: Student Proposal entity
841 org_admin: org admin entity for the current user/proposal (iff available)
842 mentor: mentor entity for the current user/proposal (iff available)
845 from soc
.logic
.models
.review
import logic
as review_logic
846 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
850 context
['student'] = entity
.scope
851 context
['student_name'] = entity
.scope
.name()
854 context
['mentor_name'] = entity
.mentor
.name()
856 context
['mentor_name'] = "No mentor assigned"
858 # set the possible mentors in the context
859 possible_mentors
= entity
.possible_mentors
861 if not possible_mentors
:
862 context
['possible_mentors'] = "None"
866 for mentor_key
in possible_mentors
:
867 possible_mentor
= mentor_logic
.logic
.getFromKeyName(mentor_key
.name())
868 mentor_names
.append(possible_mentor
.name())
870 context
['possible_mentors'] = ', '.join(mentor_names
)
872 # TODO(ljvderijk) listing of total given scores per mentor
873 # a dict with key as role.user ?
875 # order the reviews by ascending creation date
878 # get the public reviews
879 context
['public_reviews'] = review_logic
.getReviewsForEntity(entity
,
880 is_public
=True, order
=order
)
882 # get the private reviews
883 context
['private_reviews'] = review_logic
.getReviewsForEntity(entity
,
884 is_public
=False, order
=order
)
886 # which button should we show to the mentor?
888 context
['is_mentor'] = True
889 if mentor
.key() in possible_mentors
:
890 # show "No longer willing to mentor"
891 context
['remove_me_as_mentor'] = True
893 # show "I am willing to mentor"
894 context
['add_me_as_mentor'] = True
897 context
['is_org_admin'] = True
899 user_entity
= user_logic
.logic
.getForCurrentAccount()
901 # check if the current user is subscribed to public or private reviews
902 fields
= {'scope': entity
,
903 'user': user_entity
,}
904 follower_entity
= review_follower_logic
.getForFields(fields
, unique
=True)
907 context
['is_subscribed_public'] = follower_entity
.subscribed_public
908 context
['is_subscribed_private'] = follower_entity
.subscribed_private
912 def _adjustPossibleMentors(self
, entity
, mentor
, choice
):
913 """Adjusts the possible mentors list for a proposal.
916 entity: Student Proposal entity
917 mentor: Mentor entity
918 choice: 1 means want to mentor, 0 do not want to mentor
920 possible_mentors
= entity
.possible_mentors
923 # add the mentor to possible mentors list if not already in
924 if mentor
.key() not in possible_mentors
:
925 possible_mentors
.append(mentor
.key())
926 fields
= {'possible_mentors': possible_mentors
}
927 self
._logic
.updateEntityProperties(entity
, fields
)
929 # remove the mentor from the possible mentors list
930 if mentor
.key() in possible_mentors
:
931 possible_mentors
.remove(mentor
.key())
932 fields
= {'possible_mentors': possible_mentors
}
933 self
._logic
.updateEntityProperties(entity
, fields
)
935 def _adjustMentor(self
, entity
, mentor_id
):
936 """Changes the mentor to the given link_id.
939 entity: Student Proposal entity
940 mentor_id: Link ID of the mentor that needs to be assigned
941 Iff not given then removes the assigned mentor
944 if entity
.mentor
and entity
.mentor
.link_id
== mentor_id
:
949 # try to locate the mentor
950 fields
= {'link_id': mentor_id
,
954 mentor_entity
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
956 if not mentor_entity
:
957 # no mentor found, do not update
963 # update the proposal
964 properties
= {'mentor': mentor_entity
}
965 self
._logic
.updateEntityProperties(entity
, properties
)
967 def _createReviewFor(self
, entity
, reviewer
, comment
,
968 score
=0, is_public
=True):
969 """Creates a review for the given proposal and sends
970 out a message to all followers.
973 entity: Student Proposal entity for which the review should be created
974 reviewer: A role entity of the reviewer (if possible, else None)
975 comment: The textual contents of the review
976 score: The score of the review (only used if the review is not public)
977 is_public: Determines if the review is a public review
980 from soc
.logic
.helper
import notifications
as notifications_helper
981 from soc
.logic
.models
.review
import logic
as review_logic
982 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
984 # create the fields for the review entity
985 fields
= {'link_id': 't%i' % (int(time
.time()*100)),
987 'scope_path': entity
.key().name(),
988 'author': user_logic
.logic
.getForCurrentAccount(),
990 'is_public': is_public
,
994 # add the given score if the review is not public
996 fields
['score'] = score
998 # create a new Review
999 key_name
= review_logic
.getKeyNameFromFields(fields
)
1000 review_entity
= review_logic
.updateOrCreateFromKeyName(fields
, key_name
)
1003 fields
= {'scope': entity
}
1006 fields
['subscribed_public'] = True
1008 fields
['subscribed_private'] = True
1010 followers
= review_follower_logic
.getForFields(fields
)
1013 # redirect to public page
1014 redirect_url
= redirects
.getPublicRedirect(entity
, self
._params
)
1016 # redirect to review page
1017 redirect_url
= redirects
.getReviewRedirect(entity
, self
._params
)
1019 for follower
in followers
:
1020 # sent to every follower except the reviewer
1021 if follower
.user
.key() != review_entity
.author
.key():
1022 notifications_helper
.sendNewReviewNotification(follower
.user
,
1023 review_entity
, entity
.title
, redirect_url
)
1028 admin
= decorators
.view(view
.admin
)
1029 apply = decorators
.view(view
.apply)
1030 create
= decorators
.view(view
.create
)
1031 delete
= decorators
.view(view
.delete
)
1032 edit
= decorators
.view(view
.edit
)
1033 list = decorators
.view(view
.list)
1034 list_orgs
= decorators
.view(view
.listOrgs
)
1035 list_self
= decorators
.view(view
.listSelf
)
1036 public
= decorators
.view(view
.public
)
1037 review
= decorators
.view(view
.review
)
1038 export
= decorators
.view(view
.export
)
1039 pick
= decorators
.view(view
.pick
)