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 Organizations.
21 '"Madhusudan.C.S" <madhusudancs@gmail.com>',
22 '"Augie Fackler" <durin42@gmail.com>',
23 '"Sverre Rabbelier" <sverre@rabbelier.nl>',
24 '"Lennard de Rijk" <ljvderijk@gmail.com>',
30 from django
import forms
31 from django
.utils
import simplejson
32 from django
.utils
.translation
import ugettext
34 from soc
.logic
import cleaning
35 from soc
.logic
import dicts
36 from soc
.logic
import accounts
37 from soc
.logic
.helper
import timeline
as timeline_helper
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 user
as user_logic
43 from soc
.views
import helper
44 from soc
.views
import out_of_band
45 from soc
.views
.helper
import access
46 from soc
.views
.helper
import decorators
47 from soc
.views
.helper
import dynaform
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 group
53 import soc
.models
.organization
54 import soc
.logic
.models
.organization
56 class View(group
.View
):
57 """View methods for the Organization model.
60 DEF_ACCEPTED_PROJECTS_MSG_FMT
= ugettext("These projects have"
61 " been accepted into %s. You can learn more about"
62 " each project by visiting the links below.")
64 def __init__(self
, params
=None):
65 """Defines the fields and methods required for the base View class
66 to provide the user with list, public, create, edit and delete views.
69 original_params: a dict with params for this View
72 from soc
.views
.models
import program
as program_view
74 rights
= access
.Checker(params
)
75 rights
['any_access'] = ['allow']
76 rights
['show'] = ['allow']
77 rights
['create'] = ['checkIsDeveloper']
78 rights
['edit'] = [('checkHasActiveRoleForKeyFieldsAsScope',
79 org_admin_logic
.logic
,),
80 ('checkGroupIsActiveForLinkId', org_logic
.logic
)]
81 rights
['delete'] = ['checkIsDeveloper']
82 rights
['home'] = ['allow']
83 rights
['public_list'] = ['allow']
84 rights
['apply_mentor'] = ['checkIsUser']
85 rights
['list_requests'] = [('checkHasActiveRoleForKeyFieldsAsScope',
86 org_admin_logic
.logic
)]
87 rights
['list_roles'] = [('checkHasActiveRoleForKeyFieldsAsScope',
88 org_admin_logic
.logic
)]
89 rights
['applicant'] = [('checkIsApplicationAccepted',
91 rights
['list_proposals'] = [('checkHasAny', [
92 [('checkHasActiveRoleForKeyFieldsAsScope', [org_admin_logic
.logic
]),
93 ('checkHasActiveRoleForKeyFieldsAsScope', [mentor_logic
.logic
])]
97 new_params
['logic'] = soc
.logic
.models
.organization
.logic
98 new_params
['rights'] = rights
100 new_params
['scope_view'] = program_view
101 new_params
['scope_redirect'] = redirects
.getCreateRedirect
103 new_params
['name'] = "Organization"
104 new_params
['url_name'] = "org"
105 new_params
['document_prefix'] = "org"
106 new_params
['sidebar_grouping'] = 'Organizations'
108 new_params
['public_template'] = 'soc/organization/public.html'
109 new_params
['list_row'] = 'soc/organization/list/row.html'
110 new_params
['list_heading'] = 'soc/organization/list/heading.html'
111 new_params
['home_template'] = 'soc/organization/home.html'
113 new_params
['application_logic'] = org_app_logic
114 new_params
['group_applicant_url'] = True
115 new_params
['sans_link_id_public_list'] = True
117 new_params
['extra_dynaexclude'] = ['slots', 'slots_calculated',
118 'nr_applications', 'nr_mentors']
123 (r
'^%(url_name)s/(?P<access_type>apply_mentor)/%(scope)s$',
124 'soc.views.models.%(module_name)s.apply_mentor',
125 "List of all %(name_plural)s you can apply to"),
126 (r
'^%(url_name)s/(?P<access_type>list_proposals)/%(key_fields)s$',
127 'soc.views.models.%(module_name)s.list_proposals',
128 "List of all Student Proposals for this %(name)s"),
131 new_params
['extra_django_patterns'] = patterns
133 new_params
['create_dynafields'] = [
135 'base': forms
.fields
.CharField
,
136 'label': 'Organization Link ID',
140 new_params
['create_extra_dynaproperties'] = {
141 'scope_path': forms
.CharField(widget
=forms
.HiddenInput
,
143 'description': forms
.fields
.CharField(
144 widget
=helper
.widgets
.FullTinyMCE(
145 attrs
={'rows': 25, 'cols': 100})),
146 'contrib_template': forms
.fields
.CharField(
147 widget
=helper
.widgets
.FullTinyMCE(
148 attrs
={'rows': 25, 'cols': 100})),
149 'clean_description': cleaning
.clean_html_content('description'),
150 'clean_contrib_template': cleaning
.clean_html_content(
152 'clean_ideas': cleaning
.clean_url('ideas'),
153 'clean': cleaning
.validate_new_group('link_id', 'scope_path',
154 soc
.logic
.models
.organization
, org_app_logic
)
157 new_params
['edit_extra_dynaproperties'] = {
158 'clean': cleaning
.clean_refs(new_params
, ['home_link_id'])
161 params
= dicts
.merge(params
, new_params
)
163 super(View
, self
).__init
__(params
=params
)
165 # create and store the special form for applicants
167 'link_id': forms
.CharField(widget
=widgets
.ReadOnlyInput(),
169 'clean_link_id': cleaning
.clean_link_id('link_id')
172 applicant_create_form
= dynaform
.extendDynaForm(
173 dynaform
= self
._params
['create_form'],
174 dynaproperties
= updated_fields
)
176 self
._params
['applicant_create_form'] = applicant_create_form
178 @decorators.merge_params
179 @decorators.check_access
180 def applyMentor(self
, request
, access_type
,
181 page_name
=None, params
=None, **kwargs
):
182 """Shows a list of all organizations and you can choose one to
183 apply to become a mentor.
186 request: the standard Django HTTP request object
187 access_type : the name of the access type which should be checked
188 page_name: the page name displayed in templates as page and header title
189 params: a dict with params for this View
190 kwargs: the Key Fields for the specified entity
193 list_params
= params
.copy()
194 list_params
['list_action'] = (redirects
.getRequestRedirectForRole
, 'mentor')
195 list_params
['list_description'] = ugettext('Choose an Organization which '
196 'you want to become a Mentor for.')
198 filter = {'scope_path': kwargs
['scope_path'],
201 return self
.list(request
, access_type
,
202 page_name
, params
=list_params
, filter=filter)
204 @decorators.merge_params
205 @decorators.check_access
206 def listProposals(self
, request
, access_type
,
207 page_name
=None, params
=None, **kwargs
):
208 """Lists all proposals for the organization given in kwargs.
210 For params see base.View.public().
213 from soc
.logic
.models
.ranker_root
import logic
as ranker_root_logic
214 from soc
.logic
.models
.student_proposal
import logic
as sp_logic
215 from soc
.models
import student_proposal
216 from soc
.views
.helper
import list_info
as list_info_helper
217 from soc
.views
.models
import student_proposal
as student_proposal_view
220 org_entity
= self
._logic
.getFromKeyFieldsOr404(kwargs
)
221 except out_of_band
.Error
, error
:
222 return helper
.responses
.errorResponse(
223 error
, request
, template
=params
['error_public'])
226 context
['entity'] = org_entity
228 list_params
= student_proposal_view
.view
.getParams().copy()
229 list_params
['list_template'] = 'soc/student_proposal/list_for_org.html'
230 list_params
['list_key_order'] = [
231 'title', 'abstract', 'content', 'additional_info', 'mentor',
232 'possible_mentors', 'score', 'status', 'created_on',
235 ranked_params
= list_params
.copy()# ranked proposals
236 ranked_params
['list_row'] = ('soc/%(module_name)s/list/'
237 'detailed_row.html' % list_params
)
238 ranked_params
['list_heading'] = ('soc/%(module_name)s/list/'
239 'detailed_heading.html' % list_params
)
240 ranked_params
['list_description'] = '%s already under review sent to %s' % (
241 ranked_params
['name_plural'], org_entity
.name
)
242 ranked_params
['list_action'] = (redirects
.getReviewRedirect
, ranked_params
)
244 # TODO(ljvderijk) once sorting with IN operator is fixed,
245 # make this list show more
246 filter = {'org': org_entity
,
249 # order by descending score
252 prop_list
= lists
.getListContent(
253 request
, ranked_params
, filter, order
=order
, idx
=0)
255 proposals
= prop_list
['data']
257 # get a list of scores
258 scores
= [[proposal
.score
] for proposal
in proposals
]
260 # retrieve the ranker
261 fields
= {'link_id': student_proposal
.DEF_RANKER_NAME
,
264 ranker_root
= ranker_root_logic
.getForFields(fields
, unique
=True)
265 ranker
= ranker_root_logic
.getRootFromEntity(ranker_root
)
267 # retrieve the ranks for these scores
268 ranks
= [rank
+1 for rank
in ranker
.FindRanks(scores
)]
270 # link the proposals to the rank
271 ranking
= dict([i
for i
in itertools
.izip(proposals
, ranks
)])
273 assigned_proposals
= []
275 # only when the program allows allocations
276 # to be seen we should color the list
277 if org_entity
.scope
.allocations_visible
:
278 assigned_proposals
= sp_logic
.getProposalsToBeAcceptedForOrg(org_entity
)
280 # show the amount of slots assigned on the webpage
281 context
['slots_visible'] = True
283 ranking_keys
= dict([(k
.key(), v
) for k
, v
in ranking
.iteritems()])
284 proposal_keys
= [i
.key() for i
in assigned_proposals
]
286 # update the prop_list with the ranking and coloring information
287 prop_list
['info'] = (list_info_helper
.getStudentProposalInfo(ranking_keys
,
288 proposal_keys
), None)
290 # check if the current user is a mentor
291 user_entity
= user_logic
.logic
.getForCurrentAccount()
293 fields
= {'user': user_entity
,
294 'scope': org_entity
,}
295 mentor_entity
= mentor_logic
.logic
.getForFields(fields
, unique
=True)
298 mp_params
= list_params
.copy() # proposals mentored by current user
300 description
= ugettext('List of %s sent to %s you are mentoring') % (
301 mp_params
['name_plural'], org_entity
.name
)
302 mp_params
['list_description'] = description
303 mp_params
['list_action'] = (redirects
.getReviewRedirect
, mp_params
)
305 filter = {'org': org_entity
,
306 'mentor': mentor_entity
,
309 mp_list
= lists
.getListContent(
310 request
, mp_params
, filter, idx
=1, need_content
=True)
312 new_params
= list_params
.copy() # new proposals
313 new_params
['list_description'] = 'List of new %s sent to %s ' % (
314 new_params
['name_plural'], org_entity
.name
)
315 new_params
['list_action'] = (redirects
.getReviewRedirect
, new_params
)
317 filter = {'org': org_entity
,
321 new_list
= lists
.getListContent(
322 request
, new_params
, filter, idx
=2, need_content
=True)
324 ap_params
= list_params
.copy() # accepted proposals
326 description
= ugettext('List of accepted %s sent to %s ') % (
327 ap_params
['name_plural'], org_entity
.name
)
329 ap_params
['list_description'] = description
330 ap_params
['list_action'] = (redirects
.getReviewRedirect
, ap_params
)
332 filter = {'org': org_entity
,
333 'status': 'accepted'}
335 ap_list
= lists
.getListContent(
336 request
, ap_params
, filter, idx
=3, need_content
=True)
338 rp_params
= list_params
.copy() # rejected proposals
340 description
= ugettext('List of rejected %s sent to %s ') % (
341 rp_params
['name_plural'], org_entity
.name
)
343 rp_params
['list_description'] = description
344 rp_params
['list_action'] = (redirects
.getReviewRedirect
, rp_params
)
346 filter = {'org': org_entity
,
347 'status': 'rejected'}
349 rp_list
= lists
.getListContent(
350 request
, rp_params
, filter, idx
=4, need_content
=True)
352 ip_params
= list_params
.copy() # ineligible proposals
354 description
= ugettext('List of ineligible %s sent to %s ') % (
355 ip_params
['name_plural'], org_entity
.name
)
357 ip_params
['list_description'] = description
358 ip_params
['list_action'] = (redirects
.getReviewRedirect
, ip_params
)
360 filter = {'org': org_entity
,
363 ip_list
= lists
.getListContent(
364 request
, ip_params
, filter, idx
=5, need_content
=True)
366 # fill contents with all the needed lists
368 contents
.append(new_list
)
370 contents
.append(prop_list
)
372 if mentor_entity
and mp_list
!= None:
373 contents
.append(mp_list
)
376 contents
.append(ap_list
)
379 contents
.append(rp_list
)
382 contents
.append(ip_list
)
384 # call the _list method from base to display the list
385 return self
._list
(request
, list_params
, contents
, page_name
, context
)
387 @decorators.merge_params
388 @decorators.check_access
389 def listPublic(self
, request
, access_type
, page_name
=None,
390 params
=None, filter=None, **kwargs
):
391 """See base.View.list.
394 account
= accounts
.getCurrentAccount()
395 user
= user_logic
.logic
.getForAccount(account
) if account
else None
398 rights
= self
._params
['rights']
399 rights
.setCurrentUser(account
, user
)
402 except out_of_band
.Error
:
405 params
= params
.copy()
408 params
['list_action'] = (redirects
.getAdminRedirect
, params
)
410 params
['list_action'] = (redirects
.getPublicRedirect
, params
)
414 new_filter
['scope_path'] = kwargs
['scope_path']
415 new_filter
['status'] = 'active'
416 filter = dicts
.merge(filter, new_filter
)
418 content
= lists
.getListContent(request
, params
, filter)
421 return self
._list
(request
, params
, contents
, page_name
)
423 def _getMapData(self
, student_project_params
, filter=None):
424 """Constructs the JSON object required to generate
425 Google Maps on organization home page.
428 student_project_params: params for student project view
429 filter: a dict for the properties that the entities should have
432 A JSON object containing map data.
435 from soc
.logic
.models
.student_project
import logic
as student_project_logic
439 # get all the student_project entities for this organization
440 student_project_entities
= student_project_logic
.getForFields(filter=filter)
442 # construct a dictionary of mentors. For each mentor construct a
443 # list of 3-tuple containing student, project title and url. This is
444 # mainly done to track and pair all students and mentors who
445 # have allowed to publish their locations.
446 for entity
in student_project_entities
:
448 if entity
.mentor
.publish_location
and (entity
.mentor
.key().name() not in people
.keys()):
449 # if mentor has allowed to publish his location add it to the
451 people
[entity
.mentor
.key().name()] = {
453 'name': entity
.mentor
.name(),
454 'city': entity
.mentor
.res_city
,
455 'ccTLD': entity
.mentor
.ccTld(),
459 if entity
.student
.publish_location
:
460 if entity
.mentor
.publish_location
:
461 people
[entity
.mentor
.key().name()]['students'].append(entity
.student
.key().name())
462 people
[entity
.student
.key().name()] = {
464 'name': entity
.student
.name(),
465 'city': entity
.student
.res_city
,
466 'ccTLD': entity
.student
.ccTld(),
467 'summary': entity
.title
,
468 'url': redirects
.getPublicRedirect(entity
, student_project_params
),
469 'mentor': entity
.mentor
.name()
472 return simplejson
.dumps(people
)
474 def _public(self
, request
, entity
, context
):
475 """See base.View._public().
478 from soc
.views
.models
import student_project
as student_project_view
480 program_entity
= entity
.scope
482 if timeline_helper
.isAfterEvent(program_entity
.timeline
,
483 'accepted_students_announced_deadline'):
485 ap_params
= student_project_view
.view
.getParams().copy()
487 # define the list redirect action to show the notification
488 ap_params
['list_action'] = (redirects
.getPublicRedirect
, ap_params
)
489 ap_params
['list_description'] = self
.DEF_ACCEPTED_PROJECTS_MSG_FMT
% (
491 ap_params
['list_heading'] = 'soc/student_project/list/heading.html'
492 ap_params
['list_row'] = 'soc/student_project/list/row.html'
494 # only show projects that have not failed
495 filter = {'scope': entity
,
496 'status': ['accepted', 'mid_term_passed', 'passed']}
498 ap_list
= lists
.getListContent(request
, ap_params
, filter, idx
=0,
504 # this is a temporary fix for sorting Student Projects
505 # by Student name until we have a view that default
506 # sorts it self by name (right now we can't do such query)
507 ap_list
['data'].sort(key
=lambda sp
: sp
.student
.name().lower())
509 contents
.append(ap_list
)
511 # construct the list and put it into the context
512 context
['list'] = soc
.logic
.lists
.Lists(contents
)
514 # obtain data to construct the organization map as json object
515 context
['org_map_data'] = self
._getMapData
(ap_params
, filter)
517 return super(View
, self
)._public
(request
=request
, entity
=entity
,
520 def _getExtraMenuItems(self
, role_description
, params
=None):
521 """Used to create the specific Organization menu entries.
523 For args see group.View._getExtraMenuItems().
527 group_entity
= role_description
['group']
528 program_entity
= group_entity
.scope
529 roles
= role_description
['roles']
531 if roles
.get('org_admin') or roles
.get('mentor'):
532 # add a link to view all the student proposals
533 submenu
= (redirects
.getListProposalsRedirect(group_entity
, params
),
534 "View all Student Proposals", 'any_access')
535 submenus
.append(submenu
)
538 if roles
.get('org_admin'):
539 # add a link to manage student projects after they have been announced
540 if timeline_helper
.isAfterEvent(program_entity
.timeline
,
541 'accepted_students_announced_deadline'):
542 submenu
= (redirects
.getManageOverviewRedirect(group_entity
,
543 {'url_name': 'student_project'}),
544 "Manage Student Projects", 'any_access')
545 submenus
.append(submenu
)
547 # add a link to the management page
548 submenu
= (redirects
.getListRolesRedirect(group_entity
, params
),
549 "Manage Admins and Mentors", 'any_access')
550 submenus
.append(submenu
)
552 # add a link to invite an org admin
553 submenu
= (redirects
.getInviteRedirectForRole(group_entity
, 'org_admin'),
554 "Invite an Admin", 'any_access')
555 submenus
.append(submenu
)
557 # add a link to invite a member
558 submenu
= (redirects
.getInviteRedirectForRole(group_entity
, 'mentor'),
559 "Invite a Mentor", 'any_access')
560 submenus
.append(submenu
)
562 # add a link to the request page
563 submenu
= (redirects
.getListRequestsRedirect(group_entity
, params
),
564 "List Requests and Invites", 'any_access')
565 submenus
.append(submenu
)
567 # add a link to the edit page
568 submenu
= (redirects
.getEditRedirect(group_entity
, params
),
569 "Edit Organization Profile", 'any_access')
570 submenus
.append(submenu
)
572 if roles
.get('org_admin') or roles
.get('mentor'):
573 submenu
= (redirects
.getCreateDocumentRedirect(group_entity
, 'org'),
574 "Create a New Document", 'any_access')
575 submenus
.append(submenu
)
577 submenu
= (redirects
.getListDocumentsRedirect(group_entity
, 'org'),
578 "List Documents", 'any_access')
579 submenus
.append(submenu
)
582 if roles
.get('org_admin'):
583 # add a link to the resign page
584 submenu
= (redirects
.getManageRedirect(roles
['org_admin'],
585 {'url_name': 'org_admin'}),
586 "Resign as Admin", 'any_access')
587 submenus
.append(submenu
)
589 # add a link to the edit page
590 submenu
= (redirects
.getEditRedirect(roles
['org_admin'],
591 {'url_name': 'org_admin'}),
592 "Edit My Admin Profile", 'any_access')
593 submenus
.append(submenu
)
596 if roles
.get('mentor'):
597 # add a link to the resign page
598 submenu
= (redirects
.getManageRedirect(roles
['mentor'],
599 {'url_name' : 'mentor'}),
600 "Resign as Mentor", 'any_access')
601 submenus
.append(submenu
)
603 # add a link to the edit page
604 submenu
= (redirects
.getEditRedirect(roles
['mentor'],
605 {'url_name': 'mentor'}),
606 "Edit My Mentor Profile", 'any_access')
607 submenus
.append(submenu
)
614 admin
= decorators
.view(view
.admin
)
615 applicant
= decorators
.view(view
.applicant
)
616 apply_mentor
= decorators
.view(view
.applyMentor
)
617 create
= decorators
.view(view
.create
)
618 delete
= decorators
.view(view
.delete
)
619 edit
= decorators
.view(view
.edit
)
620 home
= decorators
.view(view
.home
)
621 list = decorators
.view(view
.list)
622 list_proposals
= decorators
.view(view
.listProposals
)
623 list_public
= decorators
.view(view
.listPublic
)
624 list_requests
= decorators
.view(view
.listRequests
)
625 list_roles
= decorators
.view(view
.listRoles
)
626 public
= decorators
.view(view
.public
)
627 export
= decorators
.view(view
.export
)
628 pick
= decorators
.view(view
.pick
)