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