Fix user_self calling editGet with a wrong parameter
[Melange.git] / app / soc / views / models / program.py
blob8dcb0ceae8b1f75197c19cf14f329388ebaa5df3
1 #!/usr/bin/python2.5
3 # Copyright 2008 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 Programs.
18 """
20 __authors__ = [
21 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
22 '"Lennard de Rijk" <ljvderijk@gmail.com>',
26 import os
28 from django import forms
29 from django import http
30 from django.utils.translation import ugettext
32 from soc.logic import allocations
33 from soc.logic import cleaning
34 from soc.logic import dicts
35 from soc.logic.helper import timeline as timeline_helper
36 from soc.logic.models import host as host_logic
37 from soc.logic.models import mentor as mentor_logic
38 from soc.logic.models import organization as org_logic
39 from soc.logic.models import org_admin as org_admin_logic
40 from soc.logic.models import org_app as org_app_logic
41 from soc.logic.models import student_proposal as student_proposal_logic
42 from soc.logic.models import program as program_logic
43 from soc.logic.models import student as student_logic
44 from soc.views import helper
45 from soc.views import out_of_band
46 from soc.views.helper import access
47 from soc.views.helper import decorators
48 from soc.views.helper import lists
49 from soc.views.helper import redirects
50 from soc.views.helper import widgets
51 from soc.views.models import presence
52 from soc.views.models import document as document_view
53 from soc.views.models import sponsor as sponsor_view
54 from soc.views.sitemap import sidebar
56 import soc.cache.logic
57 import soc.logic.models.program
58 import soc.models.work
61 class View(presence.View):
62 """View methods for the Program model.
63 """
65 DEF_ACCEPTED_ORGS_MSG_FMT = ugettext("These organizations have"
66 " been accepted into %(name)s, but they have not yet completed"
67 " their organization profile. You can still learn more about"
68 " each organization by visiting the links below.")
70 DEF_CREATED_ORGS_MSG_FMT = ugettext("These organizations have been"
71 " accepted into %(name)s and have completed their organization"
72 " profiles. You can learn more about each organization by"
73 " visiting the links below.")
75 DEF_SLOTS_ALLOCATION_MSG = ugettext("Use this view to assign slots.")
77 def __init__(self, params=None):
78 """Defines the fields and methods required for the base View class
79 to provide the user with list, public, create, edit and delete views.
81 Params:
82 params: a dict with params for this View
83 """
85 rights = access.Checker(params)
86 rights['any_access'] = ['allow']
87 rights['show'] = ['allow']
88 rights['create'] = [('checkSeeded', ['checkHasActiveRoleForScope',
89 host_logic.logic])]
90 rights['edit'] = ['checkIsHostForProgram']
91 rights['delete'] = ['checkIsDeveloper']
92 rights['assign_slots'] = ['checkIsHostForProgram']
93 rights['slots'] = ['checkIsHostForProgram']
94 rights['show_duplicates'] = ['checkIsHostForProgram']
95 rights['assigned_proposals'] = ['checkIsHostForProgram']
96 rights['accepted_orgs'] = [('checkisAfterEvent',
97 ['accepted_organization_announced_deadline', '__all__'])]
99 new_params = {}
100 new_params['logic'] = soc.logic.models.program.logic
101 new_params['rights'] = rights
103 new_params['scope_view'] = sponsor_view
104 new_params['scope_redirect'] = redirects.getCreateRedirect
106 new_params['name'] = "Program"
107 new_params['sidebar_grouping'] = 'Programs'
108 new_params['document_prefix'] = "program"
110 new_params['extra_dynaexclude'] = ['timeline', 'org_admin_agreement',
111 'mentor_agreement', 'student_agreement', 'slots_allocation']
113 patterns = []
114 patterns += [
115 (r'^%(url_name)s/(?P<access_type>assign_slots)/%(key_fields)s$',
116 'soc.views.models.%(module_name)s.assign_slots',
117 'Assign slots'),
118 (r'^%(url_name)s/(?P<access_type>slots)/%(key_fields)s$',
119 'soc.views.models.%(module_name)s.slots',
120 'Assign slots (JSON)'),
121 (r'^%(url_name)s/(?P<access_type>show_duplicates)/%(key_fields)s$',
122 'soc.views.models.%(module_name)s.show_duplicates',
123 'Show duplicate slot assignments'),
124 (r'^%(url_name)s/(?P<access_type>assigned_proposals)/%(key_fields)s$',
125 'soc.views.models.%(module_name)s.assigned_proposals',
126 "Assigned proposals for multiple organizations"),
127 (r'^%(url_name)s/(?P<access_type>accepted_orgs)/%(key_fields)s$',
128 'soc.views.models.%(module_name)s.accepted_orgs',
129 "List all accepted organizations"),
132 new_params['extra_django_patterns'] = patterns
134 new_params['create_dynafields'] = [
135 {'name': 'link_id',
136 'base': forms.fields.CharField,
137 'label': 'Program Link ID',
141 # TODO add clean field to check for uniqueness in link_id and scope_path
142 new_params['create_extra_dynaproperties'] = {
143 'description': forms.fields.CharField(widget=helper.widgets.TinyMCE(
144 attrs={'rows':10, 'cols':40})),
145 'accepted_orgs_msg': forms.fields.CharField(
146 widget=helper.widgets.TinyMCE(attrs={'rows':10, 'cols':40})),
147 'scope_path': forms.CharField(widget=forms.HiddenInput, required=True),
148 'workflow': forms.ChoiceField(choices=[('gsoc','Project-based'),
149 ('ghop','Task-based')], required=True),
152 reference_fields = [
153 ('org_admin_agreement_link_id', soc.models.work.Work.link_id.help_text,
154 ugettext('Organization Admin Agreement Document link ID')),
155 ('mentor_agreement_link_id', soc.models.work.Work.link_id.help_text,
156 ugettext('Mentor Agreement Document link ID')),
157 ('student_agreement_link_id', soc.models.work.Work.link_id.help_text,
158 ugettext('Student Agreement Document link ID')),
159 ('home_link_id', soc.models.work.Work.link_id.help_text,
160 ugettext('Home page Document link ID')),
163 result = {}
165 for key, help_text, label in reference_fields:
166 result[key] = widgets.ReferenceField(
167 reference_url='document', filter=['__scoped__'],
168 filter_fields={'prefix': new_params['document_prefix']},
169 required=False, label=label, help_text=help_text)
171 result['workflow'] = forms.CharField(widget=widgets.ReadOnlyInput(),
172 required=True)
173 result['clean'] = cleaning.clean_refs(new_params,
174 [i for i,_,_ in reference_fields])
176 new_params['edit_extra_dynaproperties'] = result
178 document_references = [
179 ('org_admin_agreement_link_id', 'org_admin_agreement',
180 lambda x: x.org_admin_agreement),
181 ('mentor_agreement_link_id', 'mentor_agreement',
182 lambda x: x.mentor_agreement),
183 ('student_agreement_link_id', 'student_agreement',
184 lambda x: x.student_agreement),
187 new_params['references'] = document_references
189 params = dicts.merge(params, new_params, sub_merge=True)
191 super(View, self).__init__(params=params)
193 def _getAcceptedOrgsList(self, description, params, filter, use_cache):
194 """Returns a list with all accepted orgs.
196 Args:
197 description: the description of the list
198 params: the params to use
199 filter: the filter to use
200 use_cache: whether or not to use the cache
203 logic = params['logic']
205 order = ['name']
207 if not use_cache:
208 fun = self._getData
209 else:
210 # only cache if all profiles are created
211 fun = soc.cache.logic.cache(self._getData)
212 entities = fun(logic.getModel(), filter, order, logic)
214 result = dicts.rename(params, params['list_params'])
215 result['action'] = (redirects.getHomeRedirect, params)
216 result['description'] = description
217 result['pagination'] = 'soc/list/no_pagination.html'
218 result['data'] = entities
220 return result
222 @decorators.merge_params
223 @decorators.check_access
224 def acceptedOrgs(self, request, access_type,
225 page_name=None, params=None, filter=None, **kwargs):
226 """See base.View.list.
229 contents = []
230 logic = params['logic']
232 program_entity = logic.getFromKeyFieldsOr404(kwargs)
234 fmt = {'name': program_entity.name}
235 description = self.DEF_ACCEPTED_ORGS_MSG_FMT % fmt
237 filter = {
238 'status': 'accepted',
239 'scope': program_entity,
242 from soc.views.models import org_app as org_app_view
243 aa_params = org_app_view.view.getParams().copy() # accepted applications
245 # define the list redirect action to show the notification
246 del aa_params['list_key_order']
247 aa_params['list_action'] = (redirects.getHomeRedirect, aa_params)
248 aa_params['list_description'] = description
250 aa_list = lists.getListContent(request, aa_params, filter, idx=0,
251 need_content=True)
253 if aa_list:
254 contents.append(aa_list)
256 use_cache = not aa_list # only cache if there are no aa's left
257 description = self.DEF_CREATED_ORGS_MSG_FMT % fmt
259 filter['status'] = ['new', 'active']
261 from soc.views.models.organization import view as org_view
262 ao_params = org_view.getParams().copy() # active orgs
263 ao_list = self._getAcceptedOrgsList(description, ao_params,
264 filter, use_cache)
266 contents.append(ao_list)
268 params = params.copy()
269 params['list_msg'] = program_entity.accepted_orgs_msg
271 return self._list(request, params, contents, page_name)
273 @decorators.merge_params
274 @decorators.check_access
275 def slots(self, request, acces_type, page_name=None, params=None, **kwargs):
276 """Returns a JSON object with all orgs allocation.
278 Args:
279 request: the standard Django HTTP request object
280 access_type : the name of the access type which should be checked
281 page_name: the page name displayed in templates as page and header title
282 params: a dict with params for this View, not used
285 from django.utils import simplejson
287 program = program_logic.logic.getFromKeyFieldsOr404(kwargs)
288 program_slots = program.slots
290 filter = {
291 'scope': program,
292 'status': 'active',
295 query = org_logic.logic.getQueryForFields(filter=filter)
296 organizations = org_logic.logic.getAll(query)
298 locked_slots = adjusted_slots = {}
300 if request.method == 'POST' and 'result' in request.POST:
301 result = request.POST['result']
302 submit = request.GET.get('submit')
303 load = request.GET.get('load')
304 stored = program.slots_allocation
306 if load and stored:
307 result = stored
309 from_json = simplejson.loads(result)
311 locked_slots = dicts.groupDictBy(from_json, 'locked', 'slots')
313 if submit:
314 program.slots_allocation = result
315 program.put()
317 orgs = {}
318 applications = {}
319 max = {}
321 for org in organizations:
322 orgs[org.link_id] = org
323 applications[org.link_id] = org.nr_applications
324 max[org.link_id] = min(org.nr_mentors, org.slots_desired)
326 max_slots_per_org = program.max_slots
327 min_slots_per_org = program.min_slots
328 algorithm = 2
330 allocator = allocations.Allocator(orgs.keys(), applications, max,
331 program_slots, max_slots_per_org,
332 min_slots_per_org, algorithm)
334 result = allocator.allocate(locked_slots)
336 data = []
338 # TODO: remove adjustment here and in the JS
339 for link_id, count in result.iteritems():
340 org = orgs[link_id]
341 data.append({
342 'link_id': link_id,
343 'slots': count,
344 'locked': locked_slots.get(link_id, 0),
345 'adjustment': adjusted_slots.get(link_id, 0),
348 return self.json(request, data)
350 @decorators.merge_params
351 @decorators.check_access
352 def assignSlots(self, request, access_type, page_name=None,
353 params=None, **kwargs):
354 """View that allows to assign slots to orgs.
357 from soc.views.models import organization as organization_view
359 org_params = organization_view.view.getParams().copy()
360 org_params['list_template'] = 'soc/program/allocation/allocation.html'
361 org_params['list_heading'] = 'soc/program/allocation/heading.html'
362 org_params['list_row'] = 'soc/program/allocation/row.html'
363 org_params['list_pagination'] = 'soc/list/no_pagination.html'
365 program = program_logic.logic.getFromKeyFieldsOr404(kwargs)
367 description = self.DEF_SLOTS_ALLOCATION_MSG
369 filter = {
370 'scope': program,
371 'status': 'active',
374 content = self._getAcceptedOrgsList(description, org_params, filter, False)
376 contents = [content]
378 return_url = "http://%(host)s%(index)s" % {
379 'host' : os.environ['HTTP_HOST'],
380 'index': redirects.getSlotsRedirect(program, params)
383 context = {
384 'total_slots': program.slots,
385 'uses_json': True,
386 'uses_slot_allocator': True,
387 'return_url': return_url,
390 return self._list(request, org_params, contents, page_name, context)
392 @decorators.merge_params
393 @decorators.check_access
394 def showDuplicates(self, request, access_type, page_name=None,
395 params=None, **kwargs):
396 """View in which a host can see which students have been assigned
397 multiple slots.
399 For params see base.view.Public().
402 from django.utils import simplejson
404 from soc.logic.models.proposal_duplicates import logic as duplicates_logic
406 program_entity = program_logic.logic.getFromKeyFieldsOr404(kwargs)
408 if request.POST and request.POST.get('result'):
409 # store result in the datastore
410 fields = {'link_id': program_entity.link_id,
411 'scope': program_entity,
412 'scope_path': program_entity.key().id_or_name(),
413 'json_representation' : request.POST['result']
415 key_name = duplicates_logic.getKeyNameFromFields(fields)
416 duplicates_logic.updateOrCreateFromKeyName(fields, key_name)
418 response = simplejson.dumps({'status': 'done'})
419 return http.HttpResponse(response)
421 context = helper.responses.getUniversalContext(request)
422 helper.responses.useJavaScript(context, params['js_uses_all'])
423 context['uses_duplicates'] = True
424 context['uses_json'] = True
425 context['page_name'] = page_name
427 # get all orgs for this program who are active and have slots assigned
428 fields = {'scope': program_entity,
429 'slots >': 0,
430 'status': 'active'}
432 query = org_logic.logic.getQueryForFields(fields)
434 to_json = {
435 'nr_of_orgs': query.count(),
436 'program_key': program_entity.key().id_or_name()}
437 json = simplejson.dumps(to_json)
438 context['info'] = json
439 context['offset_length'] = 10
441 fields = {'link_id': program_entity.link_id,
442 'scope': program_entity}
443 duplicates = duplicates_logic.getForFields(fields, unique=True)
445 if duplicates:
446 # we have stored information
447 context['duplicate_cache_content'] = duplicates.json_representation
448 context['date_of_calculation'] = duplicates.calculated_on
449 else:
450 # no information stored
451 context['duplicate_cache_content'] = simplejson.dumps({})
453 template = 'soc/program/show_duplicates.html'
455 return helper.responses.respond(request, template=template, context=context)
457 @decorators.merge_params
458 @decorators.check_access
459 def assignedProposals(self, request, access_type, page_name=None,
460 params=None, filter=None, **kwargs):
461 """Returns a JSON dict containing all the proposals that would have
462 a slot assigned for a specific set of orgs.
464 The request.GET limit and offset determines how many and which
465 organizations should be returned.
467 For params see base.View.public().
469 Returns: JSON object with a collection of orgs and proposals. Containing
470 identification information and contact information.
473 get_dict = request.GET
475 if not (get_dict.get('limit') and get_dict.get('offset')):
476 return self.json(request, {})
478 try:
479 limit = max(0, int(get_dict['limit']))
480 offset = max(0, int(get_dict['offset']))
481 except ValueError:
482 return self.json(request, {})
484 program_entity = program_logic.logic.getFromKeyFieldsOr404(kwargs)
486 fields = {'scope': program_entity,
487 'slots >': 0,
488 'status': 'active'}
490 org_entities = org_logic.logic.getForFields(fields,
491 limit=limit, offset=offset)
493 orgs_data = {}
494 proposals_data = []
496 # for each org get the proposals who will be assigned a slot
497 for org in org_entities:
499 org_data = {'name': org.name}
501 fields = {'scope': org,
502 'status': 'active',
503 'user': org.founder}
505 org_admin = org_admin_logic.logic.getForFields(fields, unique=True)
507 if org_admin:
508 org_data['admin_name'] = org_admin.name()
509 org_data['admin_email'] = org_admin.email
511 proposals = student_proposal_logic.logic.getProposalsToBeAcceptedForOrg(
512 org, step_size=program_entity.max_slots)
514 if not proposals:
515 # nothing to accept, next organization
516 continue
518 # store information about the org
519 orgs_data[org.key().id_or_name()] = org_data
521 # store each proposal in the dictionary
522 for proposal in proposals:
523 student_entity = proposal.scope
525 proposals_data.append(
526 {'key_name': proposal.key().id_or_name(),
527 'proposal_title': proposal.title,
528 'student_key': student_entity.key().id_or_name(),
529 'student_name': student_entity.name(),
530 'student_contact': student_entity.email,
531 'org_key': org.key().id_or_name()
534 # return all the data in JSON format
535 data = {'orgs': orgs_data,
536 'proposals': proposals_data}
538 return self.json(request, data)
540 def _editPost(self, request, entity, fields):
541 """See base._editPost().
544 super(View, self)._editPost(request, entity, fields)
546 if not entity:
547 # there is no existing entity so create a new timeline
548 fields['timeline'] = self._createTimelineForType(fields)
549 else:
550 # use the timeline from the entity
551 fields['timeline'] = entity.timeline
553 def _createTimelineForType(self, fields):
554 """Creates and stores a timeline model for the given type of program.
557 workflow = fields['workflow']
559 timeline_logic = program_logic.logic.TIMELINE_LOGIC[workflow]
561 properties = timeline_logic.getKeyFieldsFromFields(fields)
562 key_name = timeline_logic.getKeyNameFromFields(properties)
564 properties['scope'] = fields['scope']
566 timeline = timeline_logic.updateOrCreateFromKeyName(properties, key_name)
567 return timeline
569 @decorators.merge_params
570 def getExtraMenus(self, id, user, params=None):
571 """Returns the extra menu's for this view.
573 A menu item is generated for each program that is currently
574 running. The public page for each program is added as menu item,
575 as well as all public documents for that program.
577 Args:
578 params: a dict with params for this View.
581 logic = params['logic']
582 rights = params['rights']
584 # only get all invisible and visible programs
585 fields = {'status': ['invisible', 'visible']}
586 entities = logic.getForFields(fields)
588 menus = []
590 rights.setCurrentUser(id, user)
592 for entity in entities:
593 items = []
595 if entity.status == 'visible':
596 # show the documents for this program, even for not logged in users
597 items += document_view.view.getMenusForScope(entity, params)
598 items += self._getTimeDependentEntries(entity, params, id, user)
600 try:
601 # check if the current user is a host for this program
602 rights.doCachedCheck('checkIsHostForProgram',
603 {'scope_path': entity.scope_path,
604 'link_id': entity.link_id}, [])
606 if entity.status == 'invisible':
607 # still add the document links so hosts can see how it looks like
608 items += document_view.view.getMenusForScope(entity, params)
609 items += self._getTimeDependentEntries(entity, params, id, user)
611 items += [(redirects.getReviewOverviewRedirect(
612 entity, {'url_name': 'org_app'}),
613 "Review Organization Applications", 'any_access')]
614 # add link to edit Program Profile
615 items += [(redirects.getEditRedirect(entity, params),
616 'Edit Program Profile', 'any_access')]
617 # add link to Assign Slots
618 items += [(redirects.getAssignSlotsRedirect(entity, params),
619 'Assign Slots', 'any_access')]
620 # add link to Show Duplicate project assignments
621 items += [(redirects.getShowDuplicatesRedirect(entity, params),
622 'Show Duplicate Project Assignments', 'any_access')]
623 # add link to edit Program Timeline
624 items += [(redirects.getEditRedirect(entity, {'url_name': 'timeline'}),
625 "Edit Program Timeline", 'any_access')]
626 # add link to create a new Program Document
627 items += [(redirects.getCreateDocumentRedirect(entity, 'program'),
628 "Create a New Document", 'any_access')]
629 # add link to list all Program Document
630 items += [(redirects.getListDocumentsRedirect(entity, 'program'),
631 "List Documents", 'any_access')]
633 except out_of_band.Error:
634 pass
636 items = sidebar.getSidebarMenu(id, user, items, params=params)
637 if not items:
638 continue
640 menu = {}
641 menu['heading'] = entity.short_name
642 menu['items'] = items
643 menu['group'] = 'Programs'
644 menus.append(menu)
646 return menus
648 def _getTimeDependentEntries(self, program_entity, params, id, user):
649 """Returns a list with time dependent menu items.
651 items = []
653 #TODO(ljvderijk) Add more timeline dependent entries
654 timeline_entity = program_entity.timeline
656 if timeline_helper.isActivePeriod(timeline_entity, 'org_signup'):
657 # add the organization signup link
658 items += [
659 (redirects.getApplyRedirect(program_entity, {'url_name': 'org_app'}),
660 "Apply to become an Organization", 'any_access')]
662 if user and timeline_helper.isAfterEvent(timeline_entity,
663 'org_signup_start'):
664 filter = {
665 'applicant': user,
666 'scope': program_entity,
669 if org_app_logic.logic.getForFields(filter, unique=True):
670 # add the 'List my Organization Applications' link
671 items += [
672 (redirects.getListSelfRedirect(program_entity,
673 {'url_name' : 'org_app'}),
674 "List My Organization Applications", 'any_access')]
676 # get the student entity for this user and program
677 filter = {'user': user,
678 'scope': program_entity,
679 'status': 'active'}
680 student_entity = student_logic.logic.getForFields(filter, unique=True)
682 if student_entity:
683 items += self._getStudentEntries(program_entity, student_entity,
684 params, id, user)
686 # get mentor and org_admin entity for this user and program
687 filter = {'user': user,
688 'program': program_entity,
689 'status': 'active'}
690 mentor_entity = mentor_logic.logic.getForFields(filter, unique=True)
691 org_admin_entity = org_admin_logic.logic.getForFields(filter, unique=True)
693 if mentor_entity or org_admin_entity:
694 items += self._getOrganizationEntries(program_entity, org_admin_entity,
695 mentor_entity, params, id, user)
697 if user and not (student_entity or mentor_entity or org_admin_entity):
698 if timeline_helper.isActivePeriod(timeline_entity, 'student_signup'):
699 # this user does not have a role yet for this program
700 items += [('/student/apply/%s' % (program_entity.key().id_or_name()),
701 "Register as a Student", 'any_access')]
703 deadline = 'accepted_organization_announced_deadline'
705 if timeline_helper.isAfterEvent(timeline_entity, deadline):
706 url = redirects.getAcceptedOrgsRedirect(program_entity, params)
707 # add a link to list all the organizations
708 items += [(url, "List participating Organizations", 'any_access')]
710 if not student_entity:
711 # add apply to become a mentor link
712 items += [('/org/apply_mentor/%s' % (program_entity.key().id_or_name()),
713 "Apply to become a Mentor", 'any_access')]
715 return items
717 def _getStudentEntries(self, program_entity, student_entity,
718 params, id, user):
719 """Returns a list with menu items for students in a specific program.
722 items = []
724 timeline_entity = program_entity.timeline
726 if timeline_helper.isActivePeriod(timeline_entity, 'student_signup'):
727 items += [('/student_proposal/list_orgs/%s' % (
728 student_entity.key().id_or_name()),
729 "Submit your Student Proposal", 'any_access')]
731 if timeline_helper.isAfterEvent(timeline_entity, 'student_signup_start'):
732 items += [(redirects.getListSelfRedirect(student_entity,
733 {'url_name':'student_proposal'}),
734 "List my Student Proposals", 'any_access')]
736 items += [(redirects.getEditRedirect(student_entity,
737 {'url_name': 'student'}),
738 "Edit my Student Profile", 'any_access')]
740 if timeline_helper.isAfterEvent(timeline_entity,
741 'accepted_students_announced_deadline'):
742 # add a link to show all projects
743 items += [(redirects.getListProjectsRedirect(program_entity,
744 {'url_name':'student'}),
745 "List my Student Projects", 'any_access')]
747 return items
749 def _getOrganizationEntries(self, program_entity, org_admin_entity,
750 mentor_entity, params, id, user):
751 """Returns a list with menu items for org admins and mentors in a
752 specific program.
755 # TODO(ljvderijk) think about adding specific org items like submit review
757 items = []
759 return items
762 view = View()
764 accepted_orgs = decorators.view(view.acceptedOrgs)
765 admin = decorators.view(view.admin)
766 assign_slots = decorators.view(view.assignSlots)
767 assigned_proposals = decorators.view(view.assignedProposals)
768 create = decorators.view(view.create)
769 delete = decorators.view(view.delete)
770 edit = decorators.view(view.edit)
771 list = decorators.view(view.list)
772 public = decorators.view(view.public)
773 export = decorators.view(view.export)
774 show_duplicates = decorators.view(view.showDuplicates)
775 slots = decorators.view(view.slots)
776 home = decorators.view(view.home)
777 pick = decorators.view(view.pick)