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.
21 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
22 '"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 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.
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.
82 params: a dict with params for this View
85 rights
= access
.Checker(params
)
86 rights
['any_access'] = ['allow']
87 rights
['show'] = ['allow']
88 rights
['create'] = [('checkSeeded', ['checkHasActiveRoleForScope',
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__'])]
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']
115 (r
'^%(url_name)s/(?P<access_type>assign_slots)/%(key_fields)s$',
116 'soc.views.models.%(module_name)s.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'] = [
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),
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')),
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(),
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.
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']
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
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.
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
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,
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
,
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.
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
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
309 from_json
= simplejson
.loads(result
)
311 locked_slots
= dicts
.groupDictBy(from_json
, 'locked', 'slots')
314 program
.slots_allocation
= result
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
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
)
338 # TODO: remove adjustment here and in the JS
339 for link_id
, count
in result
.iteritems():
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
374 content
= self
._getAcceptedOrgsList
(description
, org_params
, filter, False)
378 return_url
= "http://%(host)s%(index)s" % {
379 'host' : os
.environ
['HTTP_HOST'],
380 'index': redirects
.getSlotsRedirect(program
, params
)
384 'total_slots': program
.slots
,
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
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
,
432 query
= org_logic
.logic
.getQueryForFields(fields
)
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)
446 # we have stored information
447 context
['duplicate_cache_content'] = duplicates
.json_representation
448 context
['date_of_calculation'] = duplicates
.calculated_on
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
, {})
479 limit
= max(0, int(get_dict
['limit']))
480 offset
= max(0, int(get_dict
['offset']))
482 return self
.json(request
, {})
484 program_entity
= program_logic
.logic
.getFromKeyFieldsOr404(kwargs
)
486 fields
= {'scope': program_entity
,
490 org_entities
= org_logic
.logic
.getForFields(fields
,
491 limit
=limit
, offset
=offset
)
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
,
505 org_admin
= org_admin_logic
.logic
.getForFields(fields
, unique
=True)
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
)
515 # nothing to accept, next organization
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
)
547 # there is no existing entity so create a new timeline
548 fields
['timeline'] = self
._createTimelineForType
(fields
)
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
)
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.
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
)
590 rights
.setCurrentUser(id, user
)
592 for entity
in entities
:
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
)
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
:
636 items
= sidebar
.getSidebarMenu(id, user
, items
, params
=params
)
641 menu
['heading'] = entity
.short_name
642 menu
['items'] = items
643 menu
['group'] = 'Programs'
648 def _getTimeDependentEntries(self
, program_entity
, params
, id, user
):
649 """Returns a list with time dependent menu 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
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
,
666 'scope': program_entity
,
669 if org_app_logic
.logic
.getForFields(filter, unique
=True):
670 # add the 'List my Organization Applications' link
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
,
680 student_entity
= student_logic
.logic
.getForFields(filter, unique
=True)
683 items
+= self
._getStudentEntries
(program_entity
, student_entity
,
686 # get mentor and org_admin entity for this user and program
687 filter = {'user': user
,
688 'program': program_entity
,
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')]
717 def _getStudentEntries(self
, program_entity
, student_entity
,
719 """Returns a list with menu items for students in a specific program.
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')]
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
755 # TODO(ljvderijk) think about adding specific org items like submit review
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
)