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'],
90 ['new', 'pending', 'accepted', 'rejected', 'invalid']])]
93 new_params
['logic'] = soc
.logic
.models
.student_proposal
.logic
94 new_params
['rights'] = rights
95 new_params
['name'] = "Student Proposal"
96 new_params
['url_name'] = "student_proposal"
97 new_params
['sidebar_grouping'] = 'Students'
99 new_params
['scope_view'] = student_view
100 new_params
['scope_redirect'] = redirects
.getCreateRedirect
102 new_params
['no_create_with_key_fields'] = True
103 new_params
['list_key_order'] = ['title', 'abstract', 'content',
104 'additional_info', 'created_on', 'last_modified_on']
107 (r
'^%(url_name)s/(?P<access_type>apply)/%(scope)s$',
108 'soc.views.models.%(module_name)s.apply',
109 'Create a new %(name)s'),
110 (r
'^%(url_name)s/(?P<access_type>list_self)/%(scope)s$',
111 'soc.views.models.%(module_name)s.list_self',
112 'List my %(name_plural)s'),
113 (r
'^%(url_name)s/(?P<access_type>list_orgs)/%(scope)s$',
114 'soc.views.models.%(module_name)s.list_orgs',
115 'List my %(name_plural)s'),
116 (r
'^%(url_name)s/(?P<access_type>review)/%(key_fields)s$',
117 'soc.views.models.%(module_name)s.review',
121 new_params
['extra_django_patterns'] = patterns
123 new_params
['extra_dynaexclude'] = ['org', 'program', 'score',
124 'status', 'mentor', 'link_id',
127 new_params
['create_extra_dynaproperties'] = {
128 'content': forms
.fields
.CharField(required
=True,
129 widget
=widgets
.FullTinyMCE(attrs
={'rows': 25, 'cols': 100})),
130 'scope_path': forms
.CharField(widget
=forms
.HiddenInput
,
132 'organization': forms
.CharField(label
='Organization Link ID',
134 'clean_abstract': cleaning
.clean_content_length('abstract'),
135 'clean_content': cleaning
.clean_html_content('content'),
136 'clean_organization': cleaning
.clean_link_id('organization'),
137 'clean_additional_info': cleaning
.clean_url('additional_info'),
138 'clean': cleaning
.validate_student_proposal('organization',
139 'scope_path', student_logic
, org_logic
),
142 new_params
['edit_extra_dynaproperties'] = {
143 'organization': forms
.CharField(label
='Organization Link ID',
144 widget
=widgets
.ReadOnlyInput
),
145 'link_id': forms
.CharField(widget
=forms
.HiddenInput
)
148 new_params
['edit_template'] = 'soc/student_proposal/edit.html'
149 new_params
['review_template'] = 'soc/student_proposal/review.html'
150 new_params
['review_after_deadline_template'] = \
151 'soc/student_proposal/review_after_deadline.html'
153 params
= dicts
.merge(params
, new_params
)
155 super(View
, self
).__init
__(params
=params
)
157 # create the special form for students
159 {'name': 'organization',
160 'base': forms
.CharField
,
161 'label': 'Organization Link ID',
162 'widget': widgets
.ReadOnlyInput(),
167 dynaproperties
= params_helper
.getDynaFields(dynafields
)
169 student_create_form
= dynaform
.extendDynaForm(
170 dynaform
=self
._params
['create_form'],
171 dynaproperties
=dynaproperties
)
173 self
._params
['student_create_form'] = student_create_form
175 # create the special form for public review
178 'base': forms
.CharField
,
179 'widget': widgets
.FullTinyMCE(attrs
={'rows': 10, 'cols': 40}),
182 'example_text': 'Caution, you will not be able to edit your comment!',
186 dynaproperties
= params_helper
.getDynaFields(dynafields
)
187 dynaproperties
['clean_comment'] = cleaning
.clean_html_content('comment')
189 public_review_form
= dynaform
.newDynaForm(dynamodel
=None,
190 dynabase
=helper
.forms
.BaseForm
, dynainclude
=None,
191 dynaexclude
=None, dynaproperties
=dynaproperties
)
192 self
._params
['public_review_form'] = public_review_form
194 # create the special form for mentors
197 'base': forms
.ChoiceField
,
201 'passthrough': ['initial', 'required', 'choices'],
203 'A score will only be assigned if the review is private!',
204 'choices': [(-4,'-4: Wow. This. Sucks.'),
205 (-3,'-3: Needs a lot of work'),
206 (-2,'-2: This is bad'),
207 (-1,'-1: I dont like this'),
209 (1,'1: Might have potential'),
211 (3,'3: Almost there'),
212 (4,'4: Made. Of. Awesome.')]
215 'base': forms
.CharField
,
216 'widget': widgets
.FullTinyMCE(attrs
={'rows': 10, 'cols': 40}),
219 'example_text': 'Caution, you will not be able to edit your review!',
222 'base': forms
.BooleanField
,
223 'label': 'Review visible to Student',
226 'help_text': 'By ticking this box the score will not be assigned, '
227 'and the review will be visible to the student.',
231 dynaproperties
= params_helper
.getDynaFields(dynafields
)
232 dynaproperties
['clean_comment'] = cleaning
.clean_html_content('comment')
234 mentor_review_form
= dynaform
.newDynaForm(dynamodel
=None,
235 dynabase
=helper
.forms
.BaseForm
, dynainclude
=None,
236 dynaexclude
=None, dynaproperties
=dynaproperties
)
237 self
._params
['mentor_review_form'] = mentor_review_form
241 'base': forms
.IntegerField
,
242 'label': 'Set to rank',
244 'Set this proposal to the given rank (ignores the given score)',
245 'example_text': 'A rank will only be assigned if the '
246 'review is private!',
249 'passthrough': ['min_value', 'required', 'help_text'],
252 'base': widgets
.ReferenceField
,
253 'passthrough': ['reference_url', 'required', 'label', 'filter'],
254 'reference_url': 'mentor',
255 'filter': ['__org__'],
256 'label': 'Assign Mentor (Link ID)',
258 'help_text': 'Fill in the Link ID of the Mentor '
259 'you would like to assign to this Proposal. '
260 'Leave this box empty if you don\'t want any mentor assigned.',
264 dynaproperties
= params_helper
.getDynaFields(dynafields
)
265 dynaproperties
['clean_comment'] = cleaning
.clean_html_content('comment')
267 admin_review_form
= dynaform
.extendDynaForm(dynaform
=mentor_review_form
,
268 dynaproperties
=dynaproperties
)
270 self
._params
['admin_review_form'] = admin_review_form
272 def _editGet(self
, request
, entity
, form
):
273 """See base.View._editGet().
276 form
.fields
['link_id'].initial
= entity
.link_id
277 form
.fields
['organization'].initial
= entity
.org
.link_id
279 return super(View
, self
)._editGet
(request
, entity
, form
)
281 def _editPost(self
, request
, entity
, fields
):
282 """See base.View._editPost().
286 fields
['link_id'] = 't%i' % (int(time
.time()*100))
288 fields
['link_id'] = entity
.link_id
290 # fill in the scope via call to super
291 super(View
, self
)._editPost
(request
, entity
, fields
)
294 # creating a new application so set the program and org field
295 fields
['program'] = fields
['scope'].scope
297 filter = {'scope': fields
['program'],
298 'link_id': fields
['organization']}
299 fields
['org'] = org_logic
.logic
.getForFields(filter, unique
=True)
301 # explicitly change the last_modified_on since the content has been edited
302 fields
['last_modified_on'] = datetime
.datetime
.now()
304 @decorators.merge_params
305 @decorators.check_access
306 def public(self
, request
, access_type
,
307 page_name
=None, params
=None, **kwargs
):
308 """View in which the student can see and reply to the comments on the
311 For params see base.view.Public().
314 context
= helper
.responses
.getUniversalContext(request
)
315 helper
.responses
.useJavaScript(context
, params
['js_uses_all'])
316 context
['page_name'] = page_name
319 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
320 except out_of_band
.Error
, error
:
321 return helper
.responses
.errorResponse(
322 error
, request
, template
=params
['error_public'], context
=context
)
324 context
['entity'] = entity
325 context
['entity_type'] = params
['name']
326 context
['entity_type_url'] = params
['url_name']
328 if request
.method
== 'POST':
329 return self
.publicPost(request
, context
, params
, entity
, **kwargs
)
330 else: # request.method == 'GET'
331 return self
.publicGet(request
, context
, params
, entity
, **kwargs
)
333 def publicPost(self
, request
, context
, params
, entity
, **kwargs
):
334 """Handles the POST request for the entity's public page.
337 entity: the student proposal entity
338 rest: see base.View.public()
341 # populate the form using the POST data
342 form
= params
['public_review_form'](request
.POST
)
344 if not form
.is_valid():
345 # get some entity specific context
346 self
.updatePublicContext(context
, entity
, params
)
348 # return the invalid form response
349 return self
._constructResponse
(request
, entity
=entity
, context
=context
,
350 form
=form
, params
=params
, template
=params
['public_template'])
353 fields
= form
.cleaned_data
354 comment
= fields
['comment']
357 # create a new public review containing the comment
358 user_entity
= user_logic
.logic
.getForCurrentAccount()
359 # pylint: disable-msg=E1103
360 if user_entity
.key() == entity
.scope
.user
.key():
362 reviewer
= entity
.scope
364 # check if the person commenting is an org_admin
365 # or a mentor for the given proposal
366 fields
= {'user': user_entity
,
371 reviewer
= org_admin_logic
.logic
.getForFields(fields
, unique
=True)
374 # no org_admin found, maybe it's a mentor?
375 reviewer
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
377 # create the review (reviewer might be None
378 # if a Host or Developer is posting)
379 self
._createReviewFor
(entity
, reviewer
, comment
, is_public
=True)
381 # redirect to the same page
382 return http
.HttpResponseRedirect('')
384 def publicGet(self
, request
, context
, params
, entity
, **kwargs
):
385 """Handles the GET request for the entity's public page.
388 entity: the student proposal entity
389 rest see base.View.public()
392 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
394 get_dict
= request
.GET
396 if get_dict
.get('subscription') and (
397 get_dict
['subscription'] in ['on', 'off']):
399 subscription
= get_dict
['subscription']
401 # get the current user
402 user_entity
= user_logic
.logic
.getForCurrentAccount()
404 # create the fields that should be in the ReviewFollower entity
405 # pylint: disable-msg=E1103
406 fields
= {'link_id': user_entity
.link_id
,
408 'scope_path': entity
.key().id_or_name(),
411 # get the keyname for the ReviewFollower entity
412 key_name
= review_follower_logic
.getKeyNameFromFields(fields
)
414 # determine if we should set subscribed_public to True or False
415 if subscription
== 'on':
416 fields
['subscribed_public'] = True
417 elif subscription
== 'off':
418 fields
['subscribed_public'] = False
420 # update the ReviewFollower
421 review_follower_logic
.updateOrCreateFromKeyName(fields
, key_name
)
423 # get some entity specific context
424 self
.updatePublicContext(context
, entity
, params
)
426 context
['form'] = params
['public_review_form']()
427 template
= params
['public_template']
429 return responses
.respond(request
, template
, context
=context
)
431 def updatePublicContext(self
, context
, entity
, params
):
432 """Updates the context for the public page with information from the entity.
435 context: the context that should be updated
436 entity: a student proposal_entity used to set context
437 params: dict with params for the view using this context
440 from soc
.logic
.models
.review
import logic
as review_logic
441 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
443 student_entity
= entity
.scope
445 context
['student'] = student_entity
446 context
['student_name'] = student_entity
.name()
448 user_entity
= user_logic
.logic
.getForCurrentAccount()
450 # check if the current user is the student
451 # pylint: disable-msg=E1103
452 if user_entity
.key() == student_entity
.user
.key():
453 # show the proposal edit link
454 context
['edit_link'] = redirects
.getEditRedirect(entity
, params
)
456 # check if the current user is subscribed to this proposal's public reviews
457 fields
= {'user': user_entity
,
459 'subscribed_public': True}
461 context
['is_subscribed'] = review_follower_logic
.getForFields(fields
,
464 context
['public_reviews'] = review_logic
.getReviewsForEntity(entity
,
465 is_public
=True, order
=['created'])
467 @decorators.merge_params
468 @decorators.check_access
469 def apply(self
, request
, access_type
,
470 page_name
=None, params
=None, **kwargs
):
471 """Special view used to prepopulate the form with the organization
472 contributors template.
474 For params see base.View.public()
476 get_dict
= request
.GET
478 if get_dict
.get('organization'):
479 # organization chosen, prepopulate with template
481 # get the organization
482 student_entity
= student_logic
.logic
.getFromKeyName(kwargs
['scope_path'])
483 program_entity
= student_entity
.scope
485 filter = {'link_id': get_dict
['organization'],
486 'scope': program_entity
}
488 org_entity
= org_logic
.logic
.getForFields(filter, unique
=True)
491 # organization found use special form and also seed this form
492 params
['create_form'] = params
['student_create_form']
493 # pylint: disable-msg=E1103
494 kwargs
['organization'] = org_entity
.link_id
495 kwargs
['content'] = org_entity
.contrib_template
497 return super(View
, self
).create(request
, access_type
, page_name
=page_name
,
498 params
=params
, **kwargs
)
500 @decorators.merge_params
501 @decorators.check_access
502 def edit(self
, request
, access_type
,
503 page_name
=None, params
=None, seed
=None, **kwargs
):
504 """If the POST contains (action, Withdraw) the proposal in kwargs
505 will be marked as invalid.
507 For params see base.View.edit()
510 # check if request.POST contains action
511 post_dict
= request
.POST
512 if 'action' in post_dict
and post_dict
['action'] == 'Withdraw':
513 # withdraw this proposal
514 filter = {'scope_path': kwargs
['scope_path'],
515 'link_id': kwargs
['link_id']}
517 proposal_logic
= params
['logic']
518 student_proposal_entity
= proposal_logic
.getForFields(filter, unique
=True)
519 reviewer
= student_proposal_entity
.scope
521 # update the entity mark it as invalid
522 proposal_logic
.updateEntityProperties(student_proposal_entity
,
523 {'status': 'invalid'})
525 # redirect to the program's homepage
526 redirect_url
= redirects
.getHomeRedirect(student_proposal_entity
.program
,
527 {'url_name': 'program'})
529 comment
= "Student withdrew proposal."
530 self
._createReviewFor
(student_proposal_entity
, reviewer
, comment
)
531 return http
.HttpResponseRedirect(redirect_url
)
533 return super(View
, self
).edit(request
=request
, access_type
=access_type
,
534 page_name
=page_name
, params
=params
, seed
=seed
, **kwargs
)
536 @decorators.merge_params
537 @decorators.check_access
538 def listOrgs(self
, request
, access_type
,
539 page_name
=None, params
=None, **kwargs
):
540 """Lists all organization which the given student can propose to.
542 For params see base.View.public().
545 from soc
.views
.models
import organization
as org_view
547 student_entity
= student_logic
.logic
.getFromKeyName(kwargs
['scope_path'])
549 filter = {'scope': student_entity
.scope
,
552 list_params
= org_view
.view
.getParams().copy()
553 list_params
['list_description'] = ('List of %(name_plural)s you can send '
554 'your proposal to.') % list_params
555 list_params
['list_action'] = (redirects
.getStudentProposalRedirect
,
556 {'student_key': student_entity
.key().id_or_name(),
557 'url_name': params
['url_name']})
559 return self
.list(request
, access_type
=access_type
, page_name
=page_name
,
560 params
=list_params
, filter=filter, **kwargs
)
562 @decorators.merge_params
563 @decorators.check_access
564 def listSelf(self
, request
, access_type
,
565 page_name
=None, params
=None, **kwargs
):
566 """Lists all proposals from the current logged-in user
567 for the given student.
569 For params see base.View.public().
573 student_entity
= student_logic
.logic
.getFromKeyName(kwargs
['scope_path'])
575 filter = {'scope' : student_entity
,
576 'status': ['new', 'pending', 'accepted', 'rejected']}
578 list_params
= params
.copy()
579 list_params
['list_description'] = \
580 'List of my %(name_plural)s.' % list_params
581 list_params
['list_action'] = (redirects
.getPublicRedirect
, list_params
)
583 valid_list
= lists
.getListContent(
584 request
, list_params
, filter, idx
=0)
586 ip_params
= list_params
.copy() # ineligible proposals
588 description
= ugettext('List of my ineligible/withdrawn %s.') % (
589 ip_params
['name_plural'])
591 ip_params
['list_description'] = description
592 ip_params
['list_action'] = (redirects
.getPublicRedirect
, ip_params
)
594 filter = {'scope' : student_entity
,
597 ip_list
= lists
.getListContent(
598 request
, ip_params
, filter, idx
=1, need_content
=True)
601 # fill contents with all the needed lists
602 contents
.append(valid_list
)
605 contents
.append(ip_list
)
607 # call the _list method from base to display the list
608 return self
._list
(request
, list_params
, contents
, page_name
, context
)
610 @decorators.merge_params
611 @decorators.check_access
612 def review(self
, request
, access_type
,
613 page_name
=None, params
=None, **kwargs
):
614 """View that allows Organization Admins and Mentors to review the proposal.
616 For Args see base.View.public().
619 from soc
.logic
.helper
import timeline
as timeline_helper
623 entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
624 except out_of_band
.Error
, error
:
625 return helper
.responses
.errorResponse(
626 error
, request
, template
=params
['error_public'])
628 # get the context for this webpage
629 context
= responses
.getUniversalContext(request
)
630 responses
.useJavaScript(context
, params
['js_uses_all'])
631 context
['page_name'] = '%s "%s" from %s' % (page_name
, entity
.title
,
633 context
['entity'] = entity
634 context
['entity_type'] = params
['name']
635 context
['entity_type_url'] = params
['url_name']
637 program_entity
= entity
.program
639 if timeline_helper
.isAfterEvent(program_entity
.timeline
,
640 'accepted_students_announced_deadline'):
641 return self
.reviewAfterDeadline(request
, context
, params
, entity
,
644 # get the roles important for reviewing an application
645 filter = {'user': user_logic
.logic
.getForCurrentAccount(),
649 org_admin_entity
= org_admin_logic
.logic
.getForFields(filter, unique
=True)
650 mentor_entity
= mentor_logic
.logic
.getForFields(filter, unique
=True)
652 # decide which form to use
654 form
= params
['admin_review_form']
656 form
= params
['mentor_review_form']
658 if request
.method
== 'POST':
659 return self
.reviewPost(request
, context
, params
, entity
,
660 form
, org_admin_entity
, mentor_entity
, **kwargs
)
662 # request.method == 'GET'
663 return self
.reviewGet(request
, context
, params
, entity
,
664 form
, org_admin_entity
, mentor_entity
, **kwargs
)
666 def reviewPost(self
, request
, context
, params
, entity
, form
,
667 org_admin
, mentor
, **kwargs
):
668 """Handles the POST request for the proposal review view.
671 entity: the student proposal entity
672 form: the form to use in this view
673 org_admin: org admin entity for the current user/proposal (iff available)
674 mentor: mentor entity for the current user/proposal (iff available)
675 rest: see base.View.public()
677 # populate the form using the POST data
678 form
= form(request
.POST
)
680 if not form
.is_valid():
681 # return the invalid form response
682 # get all the extra information that should be in the context
683 review_context
= self
._getDefaultReviewContext
(entity
, org_admin
, mentor
)
684 context
= dicts
.merge(context
, review_context
)
686 return self
._constructResponse
(request
, entity
=entity
, context
=context
,
687 form
=form
, params
=params
, template
=params
['review_template'])
689 fields
= form
.cleaned_data
690 is_public
= fields
['public']
691 comment
= fields
['comment']
692 given_score
= int(fields
['score'])
695 # org admin found, try to adjust the assigned mentor
696 self
._adjustMentor
(entity
, fields
['mentor'])
699 # try to see if the rank is given and adjust the given_score if needed
700 rank
= fields
['rank']
702 ranker
= self
._logic
.getRankerFor(entity
)
703 # if a very high rank is filled in use the highest
704 # one that returns a score
705 rank
= min(ranker
.TotalRankedScores(), rank
)
706 # ranker uses zero-based ranking
707 score_and_rank
= ranker
.FindScore(rank
-1)
708 # get the score at the requested rank
709 score_at_rank
= score_and_rank
[0][0]
710 # calculate the score that should be given to end up at the given rank
711 # give +1 to make sure that in the case of a tie they end up top
712 given_score
= score_at_rank
- entity
.score
+ 1
714 # might be None (if Host or Developer is commenting)
717 # store the properties to update the proposal with
720 if reviewer
and (not is_public
) and (given_score
is not 0):
721 # if it is not a public comment and it's made by a member of the
722 # organization we update the score of the proposal
723 new_score
= given_score
+ entity
.score
724 properties
= {'score': new_score
}
726 if comment
or (given_score
is not 0):
727 # if the proposal is new we change it status to pending
728 if entity
.status
== 'new':
729 properties
['status'] = 'pending'
731 # create the review entity
732 self
._createReviewFor
(entity
, reviewer
, comment
, given_score
, is_public
)
734 if properties
.values():
735 # there is something to update
736 self
._logic
.updateEntityProperties(entity
, properties
)
738 # redirect to the same page
739 return http
.HttpResponseRedirect('')
741 def reviewGet(self
, request
, context
, params
, entity
, form
,
742 org_admin
, mentor
, **kwargs
):
743 """Handles the GET request for the proposal review view.
746 entity: the student proposal entity
747 form: the form to use in this view
748 org_admin: org admin entity for the current user/proposal (iff available)
749 mentor: mentor entity for the current user/proposal (iff available)
750 rest: see base.View.public()
753 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
755 get_dict
= request
.GET
757 # check if the current user is a mentor and wants
758 # to change his role for this app
759 choice
= get_dict
.get('mentor')
760 if mentor
and choice
:
761 self
._adjustPossibleMentors
(entity
, mentor
, choice
)
763 ineligible
= get_dict
.get('ineligible')
770 if (org_admin
or mentor
) and (ineligible
!= None) and (
771 entity
.status
not in ['accepted', 'rejected']):
772 ineligible
= int(ineligible
)
774 # mark the proposal invalid and return to the list
775 properties
= {'status': 'invalid'}
776 self
._logic
.updateEntityProperties(entity
, properties
)
778 redirect
= redirects
.getListProposalsRedirect(entity
.org
,
780 comment
= "Marked Student Proposal as Ineligible."
781 self
._createReviewFor
(entity
, reviewer
, comment
, is_public
=False)
782 return http
.HttpResponseRedirect(redirect
)
783 elif ineligible
== 0:
784 # mark the proposal as new and return to the list
785 properties
= {'status': 'new'}
786 self
._logic
.updateEntityProperties(entity
, properties
)
788 redirect
= redirects
.getListProposalsRedirect(entity
.org
,
790 comment
= "Marked Student Proposal as Eligible."
791 self
._createReviewFor
(entity
, reviewer
, comment
, is_public
=False)
792 return http
.HttpResponseRedirect(redirect
)
794 # check if we should change the subscription state for the current user
795 public_subscription
= None
796 private_subscription
= None
798 if get_dict
.get('public_subscription') and (
799 get_dict
['public_subscription'] in ['on', 'off']):
801 public_subscription
= get_dict
['public_subscription'] == 'on'
803 if get_dict
.get('private_subscription') and (
804 get_dict
['private_subscription'] in ['on', 'off']):
805 private_subscription
= get_dict
['private_subscription'] == 'on'
807 if public_subscription
!= None or private_subscription
!= None:
808 # get the current user
809 user_entity
= user_logic
.logic
.getForCurrentAccount()
811 # create the fields that should be in the ReviewFollower entity
812 # pylint: disable-msg=E1103
813 fields
= {'link_id': user_entity
.link_id
,
815 'scope_path': entity
.key().id_or_name(),
818 # get the keyname for the ReviewFollower entity
819 key_name
= review_follower_logic
.getKeyNameFromFields(fields
)
821 # determine which subscription properties we should change
822 if public_subscription
!= None:
823 fields
['subscribed_public'] = public_subscription
825 if private_subscription
!= None:
826 fields
['subscribed_private'] = private_subscription
828 # update the ReviewFollower
829 review_follower_logic
.updateOrCreateFromKeyName(fields
, key_name
)
831 # set the initial score since the default is ignored
832 initial
= {'score': 0}
834 if org_admin
and entity
.mentor
:
835 # set the mentor field to the current mentor
836 initial
['mentor'] = entity
.mentor
.link_id
838 context
['form'] = form(initial
)
840 # create the special form for mentors
841 comment_public
= ['public', 'comment']
842 comment_private
= ['score']
843 comment_admin
= ['rank', 'mentor']
844 class FilterForm(object):
845 """Helper class used for form filtering.
847 def __init__(self
, form
, fields
):
849 self
.__fields
= fields
853 """Property that returns all fields as dictionary."""
854 fields
= self
.__form
.fields
.iteritems()
855 return dict([(k
, i
) for k
, i
in fields
if k
in self
.__fields
])
858 for field
in self
.__form
:
859 if field
.name
not in self
.__fields
:
864 def __getattr__(self
, key
, default
=_marker
):
865 if default
is self
._marker
:
866 return getattr(self
.__form
, key
)
868 return getattr(self
.__form
, key
, default
)
870 context
['form'] = form(initial
)
871 context
['comment_public'] = FilterForm(context
['form'], comment_public
)
872 context
['comment_private'] = FilterForm(context
['form'], comment_private
)
873 context
['comment_admin'] = FilterForm(context
['form'], comment_admin
)
875 # get all the extra information that should be in the context
876 review_context
= self
._getDefaultReviewContext
(entity
, org_admin
, mentor
)
877 context
= dicts
.merge(context
, review_context
)
879 template
= params
['review_template']
881 return responses
.respond(request
, template
, context
=context
)
883 def reviewAfterDeadline(self
, request
, context
, params
, entity
, **kwargs
):
884 """View that shows the review view after the accepted students
887 For Args see base.View.public().
890 review_context
= self
._getDefaultReviewContext
(entity
, None, None)
891 context
= dicts
.merge(context
, review_context
)
893 template
= params
['review_after_deadline_template']
895 return responses
.respond(request
, template
, context
=context
)
897 def _getDefaultReviewContext(self
, entity
, org_admin
,
899 """Returns the default context for the review page.
902 entity: Student Proposal entity
903 org_admin: org admin entity for the current user/proposal (iff available)
904 mentor: mentor entity for the current user/proposal (iff available)
907 from soc
.logic
.models
.review
import logic
as review_logic
908 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
912 context
['student'] = entity
.scope
913 context
['student_name'] = entity
.scope
.name()
916 context
['mentor_name'] = entity
.mentor
.name()
918 context
['mentor_name'] = "No mentor assigned"
920 # set the possible mentors in the context
921 possible_mentors
= entity
.possible_mentors
923 if not possible_mentors
:
924 context
['possible_mentors'] = "None"
928 for mentor_key
in possible_mentors
:
929 possible_mentor
= mentor_logic
.logic
.getFromKeyName(
930 mentor_key
.id_or_name())
931 mentor_names
.append(possible_mentor
.name())
933 context
['possible_mentors'] = ', '.join(mentor_names
)
935 # order the reviews by ascending creation date
938 # get the public reviews
939 public_reviews
= review_logic
.getReviewsForEntity(entity
,
940 is_public
=True, order
=order
)
942 # get the private reviews
943 private_reviews
= review_logic
.getReviewsForEntity(entity
,
944 is_public
=False, order
=order
)
946 # store the reviews in the context
947 context
['public_reviews'] = public_reviews
948 context
['private_reviews'] = private_reviews
950 # create a summary of all the private reviews
953 for private_review
in private_reviews
:
954 # make sure there is a reviewer
955 reviewer
= private_review
.reviewer
959 reviewer_key
= reviewer
.key()
960 reviewer_summary
= review_summary
.get(reviewer_key
)
963 # we already have something on file for this reviewer
964 old_total_score
= reviewer_summary
['total_score']
965 reviewer_summary
['total_score'] = old_total_score
+ private_review
.score
967 old_total_comments
= reviewer_summary
['total_comments']
968 reviewer_summary
['total_comments'] = old_total_comments
+ 1
970 review_summary
[reviewer_key
] = {
971 'name': reviewer
.name(),
973 'total_score': private_review
.score
}
975 context
['review_summary'] = review_summary
977 # which button should we show to the mentor?
979 context
['is_mentor'] = True
980 if mentor
.key() in possible_mentors
:
981 # show "No longer willing to mentor"
982 context
['remove_me_as_mentor'] = True
984 # show "I am willing to mentor"
985 context
['add_me_as_mentor'] = True
988 context
['is_org_admin'] = True
990 user_entity
= user_logic
.logic
.getForCurrentAccount()
992 # check if the current user is subscribed to public or private reviews
993 fields
= {'scope': entity
,
994 'user': user_entity
,}
995 follower_entity
= review_follower_logic
.getForFields(fields
, unique
=True)
998 # pylint: disable-msg=E1103
999 context
['is_subscribed_public'] = follower_entity
.subscribed_public
1000 context
['is_subscribed_private'] = follower_entity
.subscribed_private
1004 def _adjustPossibleMentors(self
, entity
, mentor
, choice
):
1005 """Adjusts the possible mentors list for a proposal.
1008 entity: Student Proposal entity
1009 mentor: Mentor entity
1010 choice: 1 means want to mentor, 0 do not want to mentor
1012 possible_mentors
= entity
.possible_mentors
1015 # add the mentor to possible mentors list if not already in
1016 if mentor
.key() not in possible_mentors
:
1017 possible_mentors
.append(mentor
.key())
1018 fields
= {'possible_mentors': possible_mentors
}
1019 self
._logic
.updateEntityProperties(entity
, fields
)
1021 # remove the mentor from the possible mentors list
1022 if mentor
.key() in possible_mentors
:
1023 possible_mentors
.remove(mentor
.key())
1024 fields
= {'possible_mentors': possible_mentors
}
1025 self
._logic
.updateEntityProperties(entity
, fields
)
1027 def _adjustMentor(self
, entity
, mentor_id
):
1028 """Changes the mentor to the given link_id.
1031 entity: Student Proposal entity
1032 mentor_id: Link ID of the mentor that needs to be assigned
1033 Iff not given then removes the assigned mentor
1036 if entity
.mentor
and entity
.mentor
.link_id
== mentor_id
:
1041 # try to locate the mentor
1042 fields
= {'link_id': mentor_id
,
1043 'scope': entity
.org
,
1046 mentor_entity
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
1048 if not mentor_entity
:
1049 # no mentor found, do not update
1053 mentor_entity
= None
1055 # update the proposal
1056 properties
= {'mentor': mentor_entity
}
1057 self
._logic
.updateEntityProperties(entity
, properties
)
1059 def _createReviewFor(self
, entity
, reviewer
, comment
,
1060 score
=0, is_public
=True):
1061 """Creates a review for the given proposal and sends
1062 out a message to all followers.
1065 entity: Student Proposal entity for which the review should be created
1066 reviewer: A role entity of the reviewer (if possible, else None)
1067 comment: The textual contents of the review
1068 score: The score of the review (only used if the review is not public)
1069 is_public: Determines if the review is a public review
1072 from soc
.logic
.helper
import notifications
as notifications_helper
1073 from soc
.logic
.models
.review
import logic
as review_logic
1074 from soc
.logic
.models
.review_follower
import logic
as review_follower_logic
1076 # create the fields for the review entity
1077 fields
= {'link_id': 't%i' % (int(time
.time()*100)),
1079 'scope_path': entity
.key().id_or_name(),
1080 'author': user_logic
.logic
.getForCurrentAccount(),
1082 'is_public': is_public
,
1083 'reviewer': reviewer
1086 # add the given score if the review is not public
1088 fields
['score'] = score
1090 # create a new Review
1091 key_name
= review_logic
.getKeyNameFromFields(fields
)
1092 review_entity
= review_logic
.updateOrCreateFromKeyName(fields
, key_name
)
1095 fields
= {'scope': entity
}
1098 fields
['subscribed_public'] = True
1100 fields
['subscribed_private'] = True
1102 followers
= review_follower_logic
.getForFields(fields
)
1105 # redirect to public page
1106 redirect_url
= redirects
.getPublicRedirect(entity
, self
._params
)
1108 # redirect to review page
1109 redirect_url
= redirects
.getReviewRedirect(entity
, self
._params
)
1111 for follower
in followers
:
1112 # sent to every follower except the reviewer
1113 if follower
.user
.key() != review_entity
.author
.key():
1114 notifications_helper
.sendNewReviewNotification(follower
.user
,
1115 review_entity
, entity
.title
, redirect_url
)
1120 admin
= decorators
.view(view
.admin
)
1121 apply = decorators
.view(view
.apply)
1122 create
= decorators
.view(view
.create
)
1123 delete
= decorators
.view(view
.delete
)
1124 edit
= decorators
.view(view
.edit
)
1125 list = decorators
.view(view
.list)
1126 list_orgs
= decorators
.view(view
.listOrgs
)
1127 list_self
= decorators
.view(view
.listSelf
)
1128 public
= decorators
.view(view
.public
)
1129 review
= decorators
.view(view
.review
)
1130 export
= decorators
.view(view
.export
)
1131 pick
= decorators
.view(view
.pick
)