Fixes an issue where the organization home page would throw a 505 when no projects...
[Melange.git] / app / soc / views / models / program.py
blob36c2a957519b9c5eb5578ad69e2a67e604a696d8
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 '"Daniel Hans" <daniel.m.hans@gmail.com>',
22 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
23 '"Lennard de Rijk" <ljvderijk@gmail.com>',
27 import os
29 from django import forms
30 from django import http
31 from django.utils.translation import ugettext
33 from soc.logic import allocations
34 from soc.logic import cleaning
35 from soc.logic import dicts
36 from soc.logic.helper import timeline as timeline_helper
37 from soc.logic.models import host as host_logic
38 from soc.logic.models import mentor as mentor_logic
39 from soc.logic.models import organization as org_logic
40 from soc.logic.models import org_admin as org_admin_logic
41 from soc.logic.models import org_app as org_app_logic
42 from soc.logic.models import student_proposal as student_proposal_logic
43 from soc.logic.models import program as program_logic
44 from soc.logic.models import student as student_logic
45 from soc.views import helper
46 from soc.views import out_of_band
47 from soc.views.helper import access
48 from soc.views.helper import decorators
49 from soc.views.helper import lists
50 from soc.views.helper import redirects
51 from soc.views.helper import widgets
52 from soc.views.models import presence
53 from soc.views.models import document as document_view
54 from soc.views.models import sponsor as sponsor_view
55 from soc.views.sitemap import sidebar
57 import soc.cache.logic
58 import soc.logic.models.program
59 import soc.models.work
62 class View(presence.View):
63 """View methods for the Program model.
64 """
66 DEF_ACCEPTED_ORGS_MSG_FMT = ugettext("These organizations have"
67 " been accepted into %(name)s, but they have not yet completed"
68 " their organization profile. You can still learn more about"
69 " each organization by visiting the links below.")
71 DEF_CREATED_ORGS_MSG_FMT = ugettext("These organizations have been"
72 " accepted into %(name)s and have completed their organization"
73 " profiles. You can learn more about each organization by"
74 " visiting the links below.")
76 DEF_SLOTS_ALLOCATION_MSG = ugettext("Use this view to assign slots.")
78 DEF_ACCEPTED_PROJECTS_MSG_FMT = ugettext("These projects have been"
79 " accepted into %(name)s. You can learn more about each project"
80 " by visiting the links below.")
82 def __init__(self, params=None):
83 """Defines the fields and methods required for the base View class
84 to provide the user with list, public, create, edit and delete views.
86 Params:
87 params: a dict with params for this View
88 """
90 rights = access.Checker(params)
91 rights['any_access'] = ['allow']
92 rights['show'] = ['allow']
93 rights['create'] = [('checkSeeded', ['checkHasActiveRoleForScope',
94 host_logic.logic])]
95 rights['edit'] = ['checkIsHostForProgram']
96 rights['delete'] = ['checkIsDeveloper']
97 rights['assign_slots'] = ['checkIsHostForProgram']
98 rights['slots'] = ['checkIsHostForProgram']
99 rights['show_duplicates'] = ['checkIsHostForProgram']
100 rights['assigned_proposals'] = ['checkIsHostForProgram']
101 rights['accepted_orgs'] = [('checkIsAfterEvent',
102 ['accepted_organization_announced_deadline', '__all__'])]
103 rights['list_projects'] = [('checkIsAfterEvent',
104 ['accepted_students_announced_deadline', '__all__'])]
106 new_params = {}
107 new_params['logic'] = soc.logic.models.program.logic
108 new_params['rights'] = rights
110 new_params['scope_view'] = sponsor_view
111 new_params['scope_redirect'] = redirects.getCreateRedirect
113 new_params['name'] = "Program"
114 new_params['sidebar_grouping'] = 'Programs'
115 new_params['document_prefix'] = "program"
117 new_params['extra_dynaexclude'] = ['timeline', 'org_admin_agreement',
118 'mentor_agreement', 'student_agreement', 'slots_allocation']
120 patterns = []
121 patterns += [
122 (r'^%(url_name)s/(?P<access_type>assign_slots)/%(key_fields)s$',
123 'soc.views.models.%(module_name)s.assign_slots',
124 'Assign slots'),
125 (r'^%(url_name)s/(?P<access_type>slots)/%(key_fields)s$',
126 'soc.views.models.%(module_name)s.slots',
127 'Assign slots (JSON)'),
128 (r'^%(url_name)s/(?P<access_type>show_duplicates)/%(key_fields)s$',
129 'soc.views.models.%(module_name)s.show_duplicates',
130 'Show duplicate slot assignments'),
131 (r'^%(url_name)s/(?P<access_type>assigned_proposals)/%(key_fields)s$',
132 'soc.views.models.%(module_name)s.assigned_proposals',
133 "Assigned proposals for multiple organizations"),
134 (r'^%(url_name)s/(?P<access_type>accepted_orgs)/%(key_fields)s$',
135 'soc.views.models.%(module_name)s.accepted_orgs',
136 "List all accepted organizations"),
137 (r'^%(url_name)s/(?P<access_type>list_projects)/%(key_fields)s$',
138 'soc.views.models.%(module_name)s.list_projects',
139 "List all student projects"),
142 new_params['extra_django_patterns'] = patterns
144 new_params['create_dynafields'] = [
145 {'name': 'link_id',
146 'base': forms.fields.CharField,
147 'label': 'Program Link ID',
151 # TODO add clean field to check for uniqueness in link_id and scope_path
152 new_params['create_extra_dynaproperties'] = {
153 'description': forms.fields.CharField(widget=helper.widgets.TinyMCE(
154 attrs={'rows':10, 'cols':40})),
155 'accepted_orgs_msg': forms.fields.CharField(
156 widget=helper.widgets.TinyMCE(attrs={'rows':10, 'cols':40})),
157 'scope_path': forms.CharField(widget=forms.HiddenInput, required=True),
158 'workflow': forms.ChoiceField(choices=[('gsoc','Project-based'),
159 ('ghop','Task-based')], required=True),
162 reference_fields = [
163 ('org_admin_agreement_link_id', soc.models.work.Work.link_id.help_text,
164 ugettext('Organization Admin Agreement Document link ID')),
165 ('mentor_agreement_link_id', soc.models.work.Work.link_id.help_text,
166 ugettext('Mentor Agreement Document link ID')),
167 ('student_agreement_link_id', soc.models.work.Work.link_id.help_text,
168 ugettext('Student Agreement Document link ID')),
169 ('home_link_id', soc.models.work.Work.link_id.help_text,
170 ugettext('Home page Document link ID')),
173 result = {}
175 for key, help_text, label in reference_fields:
176 result[key] = widgets.ReferenceField(
177 reference_url='document', filter=['__scoped__'],
178 filter_fields={'prefix': new_params['document_prefix']},
179 required=False, label=label, help_text=help_text)
181 result['workflow'] = forms.CharField(widget=widgets.ReadOnlyInput(),
182 required=True)
183 result['clean'] = cleaning.clean_refs(new_params,
184 [i for i,_,_ in reference_fields])
186 new_params['edit_extra_dynaproperties'] = result
188 document_references = [
189 ('org_admin_agreement_link_id', 'org_admin_agreement',
190 lambda x: x.org_admin_agreement),
191 ('mentor_agreement_link_id', 'mentor_agreement',
192 lambda x: x.mentor_agreement),
193 ('student_agreement_link_id', 'student_agreement',
194 lambda x: x.student_agreement),
197 new_params['references'] = document_references
199 params = dicts.merge(params, new_params, sub_merge=True)
201 super(View, self).__init__(params=params)
203 def _getAcceptedOrgsList(self, description, params, filter, use_cache):
204 """Returns a list with all accepted orgs.
206 Args:
207 description: the description of the list
208 params: the params to use
209 filter: the filter to use
210 use_cache: whether or not to use the cache
213 logic = params['logic']
215 order = ['name']
217 if not use_cache:
218 fun = self._getData
219 else:
220 # only cache if all profiles are created
221 fun = soc.cache.logic.cache(self._getData)
222 entities = fun(logic.getModel(), filter, order, logic)
224 result = dicts.rename(params, params['list_params'])
225 result['action'] = (redirects.getHomeRedirect, params)
226 result['description'] = description
227 result['pagination'] = 'soc/list/no_pagination.html'
228 result['data'] = entities
230 return result
232 @decorators.merge_params
233 @decorators.check_access
234 def acceptedOrgs(self, request, access_type,
235 page_name=None, params=None, filter=None, **kwargs):
236 """See base.View.list.
239 contents = []
240 logic = params['logic']
242 program_entity = logic.getFromKeyFieldsOr404(kwargs)
244 fmt = {'name': program_entity.name}
245 description = self.DEF_ACCEPTED_ORGS_MSG_FMT % fmt
247 filter = {
248 'status': 'accepted',
249 'scope': program_entity,
252 from soc.views.models import org_app as org_app_view
253 aa_params = org_app_view.view.getParams().copy() # accepted applications
255 # define the list redirect action to show the notification
256 del aa_params['list_key_order']
257 aa_params['list_action'] = (redirects.getHomeRedirect, aa_params)
258 aa_params['list_description'] = description
260 aa_list = lists.getListContent(request, aa_params, filter, idx=0,
261 need_content=True)
263 if aa_list:
264 contents.append(aa_list)
266 use_cache = not aa_list # only cache if there are no aa's left
267 description = self.DEF_CREATED_ORGS_MSG_FMT % fmt
269 filter['status'] = ['new', 'active']
271 from soc.views.models.organization import view as org_view
272 ao_params = org_view.getParams().copy() # active orgs
273 ao_list = self._getAcceptedOrgsList(description, ao_params,
274 filter, use_cache)
276 contents.append(ao_list)
278 params = params.copy()
279 params['list_msg'] = program_entity.accepted_orgs_msg
281 return self._list(request, params, contents, page_name)
283 @decorators.merge_params
284 @decorators.check_access
285 def acceptedProjects(self, request, access_type,
286 page_name=None, params=None, filter=None, **kwargs):
287 """See base.View.list.
289 contents = []
290 logic = params['logic']
292 program_entity = logic.getFromKeyFieldsOr404(kwargs)
294 filter = {
295 'status': 'accepted',
296 'program': program_entity}
298 fmt = {'name': program_entity.name}
299 description = self.DEF_ACCEPTED_PROJECTS_MSG_FMT % fmt
301 from soc.views.models import student_project as sp_view
303 ap_params = sp_view.view.getParams().copy() # accepted projects
304 ap_params['list_action'] = (redirects.getPublicRedirect, ap_params)
305 ap_params['list_description'] = description
306 ap_params['list_heading'] = 'soc/student_project/list/heading_all.html'
307 ap_params['list_row'] = 'soc/student_project/list/row_all.html'
309 return self.list(request, access_type, page_name=page_name,
310 params=ap_params, filter=filter)
312 @decorators.merge_params
313 @decorators.check_access
314 def slots(self, request, acces_type, page_name=None, params=None, **kwargs):
315 """Returns a JSON object with all orgs allocation.
317 Args:
318 request: the standard Django HTTP request object
319 access_type : the name of the access type which should be checked
320 page_name: the page name displayed in templates as page and header title
321 params: a dict with params for this View, not used
324 from django.utils import simplejson
326 program = program_logic.logic.getFromKeyFieldsOr404(kwargs)
327 program_slots = program.slots
329 filter = {
330 'scope': program,
331 'status': 'active',
334 query = org_logic.logic.getQueryForFields(filter=filter)
335 organizations = org_logic.logic.getAll(query)
337 locked_slots = adjusted_slots = {}
339 if request.method == 'POST' and 'result' in request.POST:
340 result = request.POST['result']
341 submit = request.GET.get('submit')
342 load = request.GET.get('load')
343 stored = program.slots_allocation
345 if load and stored:
346 result = stored
348 from_json = simplejson.loads(result)
350 locked_slots = dicts.groupDictBy(from_json, 'locked', 'slots')
352 if submit:
353 program.slots_allocation = result
354 program.put()
356 orgs = {}
357 applications = {}
358 max = {}
360 for org in organizations:
361 orgs[org.link_id] = org
362 applications[org.link_id] = org.nr_applications
363 max[org.link_id] = min(org.nr_mentors, org.slots_desired)
365 max_slots_per_org = program.max_slots
366 min_slots_per_org = program.min_slots
367 algorithm = 2
369 allocator = allocations.Allocator(orgs.keys(), applications, max,
370 program_slots, max_slots_per_org,
371 min_slots_per_org, algorithm)
373 result = allocator.allocate(locked_slots)
375 data = []
377 # TODO: remove adjustment here and in the JS
378 for link_id, count in result.iteritems():
379 org = orgs[link_id]
380 data.append({
381 'link_id': link_id,
382 'slots': count,
383 'locked': locked_slots.get(link_id, 0),
384 'adjustment': adjusted_slots.get(link_id, 0),
387 return self.json(request, data)
389 @decorators.merge_params
390 @decorators.check_access
391 def assignSlots(self, request, access_type, page_name=None,
392 params=None, **kwargs):
393 """View that allows to assign slots to orgs.
396 from soc.views.models import organization as organization_view
398 org_params = organization_view.view.getParams().copy()
399 org_params['list_template'] = 'soc/program/allocation/allocation.html'
400 org_params['list_heading'] = 'soc/program/allocation/heading.html'
401 org_params['list_row'] = 'soc/program/allocation/row.html'
402 org_params['list_pagination'] = 'soc/list/no_pagination.html'
404 program = program_logic.logic.getFromKeyFieldsOr404(kwargs)
406 description = self.DEF_SLOTS_ALLOCATION_MSG
408 filter = {
409 'scope': program,
410 'status': 'active',
413 content = self._getAcceptedOrgsList(description, org_params, filter, False)
415 contents = [content]
417 return_url = "http://%(host)s%(index)s" % {
418 'host' : os.environ['HTTP_HOST'],
419 'index': redirects.getSlotsRedirect(program, params)
422 context = {
423 'total_slots': program.slots,
424 'uses_json': True,
425 'uses_slot_allocator': True,
426 'return_url': return_url,
429 return self._list(request, org_params, contents, page_name, context)
431 @decorators.merge_params
432 @decorators.check_access
433 def showDuplicates(self, request, access_type, page_name=None,
434 params=None, **kwargs):
435 """View in which a host can see which students have been assigned
436 multiple slots.
438 For params see base.view.Public().
441 from django.utils import simplejson
443 from soc.logic.models.proposal_duplicates import logic as duplicates_logic
445 program_entity = program_logic.logic.getFromKeyFieldsOr404(kwargs)
447 if request.POST and request.POST.get('result'):
448 # store result in the datastore
449 fields = {'link_id': program_entity.link_id,
450 'scope': program_entity,
451 'scope_path': program_entity.key().id_or_name(),
452 'json_representation' : request.POST['result']
454 key_name = duplicates_logic.getKeyNameFromFields(fields)
455 duplicates_logic.updateOrCreateFromKeyName(fields, key_name)
457 response = simplejson.dumps({'status': 'done'})
458 return http.HttpResponse(response)
460 context = helper.responses.getUniversalContext(request)
461 helper.responses.useJavaScript(context, params['js_uses_all'])
462 context['uses_duplicates'] = True
463 context['uses_json'] = True
464 context['page_name'] = page_name
466 # get all orgs for this program who are active and have slots assigned
467 fields = {'scope': program_entity,
468 'slots >': 0,
469 'status': 'active'}
471 query = org_logic.logic.getQueryForFields(fields)
473 to_json = {
474 'nr_of_orgs': query.count(),
475 'program_key': program_entity.key().id_or_name()}
476 json = simplejson.dumps(to_json)
477 context['info'] = json
478 context['offset_length'] = 10
480 fields = {'link_id': program_entity.link_id,
481 'scope': program_entity}
482 duplicates = duplicates_logic.getForFields(fields, unique=True)
484 if duplicates:
485 # we have stored information
486 # pylint: disable-msg=E1103
487 context['duplicate_cache_content'] = duplicates.json_representation
488 context['date_of_calculation'] = duplicates.calculated_on
489 else:
490 # no information stored
491 context['duplicate_cache_content'] = simplejson.dumps({})
493 template = 'soc/program/show_duplicates.html'
495 return helper.responses.respond(request, template=template, context=context)
497 @decorators.merge_params
498 @decorators.check_access
499 def assignedProposals(self, request, access_type, page_name=None,
500 params=None, filter=None, **kwargs):
501 """Returns a JSON dict containing all the proposals that would have
502 a slot assigned for a specific set of orgs.
504 The request.GET limit and offset determines how many and which
505 organizations should be returned.
507 For params see base.View.public().
509 Returns: JSON object with a collection of orgs and proposals. Containing
510 identification information and contact information.
513 get_dict = request.GET
515 if not (get_dict.get('limit') and get_dict.get('offset')):
516 return self.json(request, {})
518 try:
519 limit = max(0, int(get_dict['limit']))
520 offset = max(0, int(get_dict['offset']))
521 except ValueError:
522 return self.json(request, {})
524 program_entity = program_logic.logic.getFromKeyFieldsOr404(kwargs)
526 fields = {'scope': program_entity,
527 'slots >': 0,
528 'status': 'active'}
530 org_entities = org_logic.logic.getForFields(fields,
531 limit=limit, offset=offset)
533 orgs_data = {}
534 proposals_data = []
536 # for each org get the proposals who will be assigned a slot
537 for org in org_entities:
539 org_data = {'name': org.name}
541 fields = {'scope': org,
542 'status': 'active',
543 'user': org.founder}
545 org_admin = org_admin_logic.logic.getForFields(fields, unique=True)
547 if org_admin:
548 # pylint: disable-msg=E1103
549 org_data['admin_name'] = org_admin.name()
550 org_data['admin_email'] = org_admin.email
552 proposals = student_proposal_logic.logic.getProposalsToBeAcceptedForOrg(
553 org, step_size=program_entity.max_slots)
555 if not proposals:
556 # nothing to accept, next organization
557 continue
559 # store information about the org
560 orgs_data[org.key().id_or_name()] = org_data
562 # store each proposal in the dictionary
563 for proposal in proposals:
564 student_entity = proposal.scope
566 proposals_data.append(
567 {'key_name': proposal.key().id_or_name(),
568 'proposal_title': proposal.title,
569 'student_key': student_entity.key().id_or_name(),
570 'student_name': student_entity.name(),
571 'student_contact': student_entity.email,
572 'org_key': org.key().id_or_name()
575 # return all the data in JSON format
576 data = {'orgs': orgs_data,
577 'proposals': proposals_data}
579 return self.json(request, data)
581 def _editPost(self, request, entity, fields):
582 """See base._editPost().
585 super(View, self)._editPost(request, entity, fields)
587 if not entity:
588 # there is no existing entity so create a new timeline
589 fields['timeline'] = self._createTimelineForType(fields)
590 else:
591 # use the timeline from the entity
592 fields['timeline'] = entity.timeline
594 def _createTimelineForType(self, fields):
595 """Creates and stores a timeline model for the given type of program.
598 workflow = fields['workflow']
600 timeline_logic = program_logic.logic.TIMELINE_LOGIC[workflow]
602 properties = timeline_logic.getKeyFieldsFromFields(fields)
603 key_name = timeline_logic.getKeyNameFromFields(properties)
605 properties['scope'] = fields['scope']
607 timeline = timeline_logic.updateOrCreateFromKeyName(properties, key_name)
608 return timeline
610 @decorators.merge_params
611 def getExtraMenus(self, id, user, params=None):
612 """Returns the extra menu's for this view.
614 A menu item is generated for each program that is currently
615 running. The public page for each program is added as menu item,
616 as well as all public documents for that program.
618 Args:
619 params: a dict with params for this View.
622 logic = params['logic']
623 rights = params['rights']
625 # only get all invisible and visible programs
626 fields = {'status': ['invisible', 'visible']}
627 entities = logic.getForFields(fields)
629 menus = []
631 rights.setCurrentUser(id, user)
633 for entity in entities:
634 items = []
636 if entity.status == 'visible':
637 # show the documents for this program, even for not logged in users
638 items += document_view.view.getMenusForScope(entity, params)
639 items += self._getTimeDependentEntries(entity, params, id, user)
641 try:
642 # check if the current user is a host for this program
643 rights.doCachedCheck('checkIsHostForProgram',
644 {'scope_path': entity.scope_path,
645 'link_id': entity.link_id}, [])
647 if entity.status == 'invisible':
648 # still add the document links so hosts can see how it looks like
649 items += document_view.view.getMenusForScope(entity, params)
650 items += self._getTimeDependentEntries(entity, params, id, user)
652 items += [(redirects.getReviewOverviewRedirect(
653 entity, {'url_name': 'org_app'}),
654 "Review Organization Applications", 'any_access')]
655 # add link to edit Program Profile
656 items += [(redirects.getEditRedirect(entity, params),
657 'Edit Program Profile', 'any_access')]
658 # add link to Assign Slots
659 items += [(redirects.getAssignSlotsRedirect(entity, params),
660 'Assign Slots', 'any_access')]
661 # add link to Show Duplicate project assignments
662 items += [(redirects.getShowDuplicatesRedirect(entity, params),
663 'Show Duplicate Project Assignments', 'any_access')]
664 # add link to edit Program Timeline
665 items += [(redirects.getEditRedirect(entity, {'url_name': 'timeline'}),
666 "Edit Program Timeline", 'any_access')]
667 # add link to create a new Program Document
668 items += [(redirects.getCreateDocumentRedirect(entity, 'program'),
669 "Create a New Document", 'any_access')]
670 # add link to list all Program Document
671 items += [(redirects.getListDocumentsRedirect(entity, 'program'),
672 "List Documents", 'any_access')]
674 except out_of_band.Error:
675 pass
677 items = sidebar.getSidebarMenu(id, user, items, params=params)
678 if not items:
679 continue
681 menu = {}
682 menu['heading'] = entity.short_name
683 menu['items'] = items
684 menu['group'] = 'Programs'
685 menus.append(menu)
687 return menus
689 def _getTimeDependentEntries(self, program_entity, params, id, user):
690 """Returns a list with time dependent menu items.
692 items = []
694 #TODO(ljvderijk) Add more timeline dependent entries
695 timeline_entity = program_entity.timeline
697 if timeline_helper.isActivePeriod(timeline_entity, 'org_signup'):
698 # add the organization signup link
699 items += [
700 (redirects.getApplyRedirect(program_entity, {'url_name': 'org_app'}),
701 "Apply to become an Organization", 'any_access')]
703 if user and timeline_helper.isAfterEvent(timeline_entity,
704 'org_signup_start'):
705 filter = {
706 'applicant': user,
707 'scope': program_entity,
710 if org_app_logic.logic.getForFields(filter, unique=True):
711 # add the 'List my Organization Applications' link
712 items += [
713 (redirects.getListSelfRedirect(program_entity,
714 {'url_name' : 'org_app'}),
715 "List My Organization Applications", 'any_access')]
717 # get the student entity for this user and program
718 filter = {'user': user,
719 'scope': program_entity,
720 'status': 'active'}
721 student_entity = student_logic.logic.getForFields(filter, unique=True)
723 if student_entity:
724 items += self._getStudentEntries(program_entity, student_entity,
725 params, id, user)
727 # get mentor and org_admin entity for this user and program
728 filter = {'user': user,
729 'program': program_entity,
730 'status': 'active'}
731 mentor_entity = mentor_logic.logic.getForFields(filter, unique=True)
732 org_admin_entity = org_admin_logic.logic.getForFields(filter, unique=True)
734 if mentor_entity or org_admin_entity:
735 items += self._getOrganizationEntries(program_entity, org_admin_entity,
736 mentor_entity, params, id, user)
738 if user and not (student_entity or mentor_entity or org_admin_entity):
739 if timeline_helper.isActivePeriod(timeline_entity, 'student_signup'):
740 # this user does not have a role yet for this program
741 items += [('/student/apply/%s' % (program_entity.key().id_or_name()),
742 "Register as a Student", 'any_access')]
744 deadline = 'accepted_organization_announced_deadline'
746 if timeline_helper.isAfterEvent(timeline_entity, deadline):
747 url = redirects.getAcceptedOrgsRedirect(program_entity, params)
748 # add a link to list all the organizations
749 items += [(url, "List participating Organizations", 'any_access')]
751 if not student_entity:
752 # add apply to become a mentor link
753 items += [('/org/apply_mentor/%s' % (program_entity.key().id_or_name()),
754 "Apply to become a Mentor", 'any_access')]
756 deadline = 'accepted_students_announced_deadline'
758 if timeline_helper.isAfterEvent(timeline_entity, deadline):
759 items += [(redirects.getListProjectsRedirect(program_entity,
760 {'url_name':'program'}),
761 "List all student projects", 'any_access')]
763 return items
765 def _getStudentEntries(self, program_entity, student_entity,
766 params, id, user):
767 """Returns a list with menu items for students in a specific program.
770 items = []
772 timeline_entity = program_entity.timeline
774 if timeline_helper.isActivePeriod(timeline_entity, 'student_signup'):
775 items += [('/student_proposal/list_orgs/%s' % (
776 student_entity.key().id_or_name()),
777 "Submit your Student Proposal", 'any_access')]
779 if timeline_helper.isAfterEvent(timeline_entity, 'student_signup_start'):
780 items += [(redirects.getListSelfRedirect(student_entity,
781 {'url_name':'student_proposal'}),
782 "List my Student Proposals", 'any_access')]
784 items += [(redirects.getEditRedirect(student_entity,
785 {'url_name': 'student'}),
786 "Edit my Student Profile", 'any_access')]
788 if timeline_helper.isAfterEvent(timeline_entity,
789 'accepted_students_announced_deadline'):
790 # add a link to show all projects
791 items += [(redirects.getListProjectsRedirect(program_entity,
792 {'url_name':'student'}),
793 "List my Student Projects", 'any_access')]
795 return items
797 def _getOrganizationEntries(self, program_entity, org_admin_entity,
798 mentor_entity, params, id, user):
799 """Returns a list with menu items for org admins and mentors in a
800 specific program.
803 # TODO(ljvderijk) think about adding specific org items like submit review
805 items = []
807 return items
810 view = View()
812 accepted_orgs = decorators.view(view.acceptedOrgs)
813 list_projects = decorators.view(view.acceptedProjects)
814 admin = decorators.view(view.admin)
815 assign_slots = decorators.view(view.assignSlots)
816 assigned_proposals = decorators.view(view.assignedProposals)
817 create = decorators.view(view.create)
818 delete = decorators.view(view.delete)
819 edit = decorators.view(view.edit)
820 list = decorators.view(view.list)
821 public = decorators.view(view.public)
822 export = decorators.view(view.export)
823 show_duplicates = decorators.view(view.showDuplicates)
824 slots = decorators.view(view.slots)
825 home = decorators.view(view.home)
826 pick = decorators.view(view.pick)