blob: d0a59fb46da3d56ec16a1238a8c6500edcf8ed3f [file] [log] [blame]
# Copyright 2014 the Melange authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Module containing ActionsRequired template."""
from google.appengine.ext import ndb
from django.utils import translation
from melange.logic import organization as org_logic
from melange.logic import profile as profile_logic
from melange.request import links
from melange.templates.helper import links as links_helper
from soc.views import template
_BACKUP_ADMIN_NEEDED_ID_PREFIX = 'backup-admin-needed-action-'
_BACKUP_ADMIN_NEEDED_TITLE = translation.ugettext(
'%s needs backup administrator')
_BACKUP_ADMIN_NEEDED_DETAILS = translation.ugettext(
'Your organization needs at least one other organization administrator. '
'The program requires two organization administrator per organization.<br>'
'Please use the link below to start a connection with another member of '
'your community it order to assign the role.<br>'
'Please note that the backup administrator will be assigned only if the '
'connected user explicitly accepts the role.<br>'
'If you fail to provide another administrator, the organization will not '
'be eligible to participate in the program and will be rejected.')
_LOGO_NEEDED_TITLE = translation.ugettext('%s needs logo')
_LOGO_NEEDED_DETAILS = translation.ugettext(
'Your organization still needs to upload a logo image. '
'Should the organization be accepted, the logo will be displayed on '
'the list of all accepted organizations.')
_APP_RESPONSE_NEEDED_ID_PREFIX = 'app-response-needed-action-'
_APP_RESPONSE_NEEDED_TITLE = translation.ugettext(
'No questionnaire submitted for %s')
_APP_RESPONSE_DETAILS = translation.ugettext(
'Your organization has not completed the required organization '
'application questionnaire. If it is not completed before the deadline, '
'your organization will not be eligible to participate in the program.')
class ActionItem(object):
"""Interface that describes a single action which is currently required."""
@property
def identifier(self):
"""Returns a string identifier of the item.
All identifiers returned by implementations of this interface should be
unique across one page.
"""
raise NotImplementedError
@property
def title(self):
"""Returns a title of this item."""
raise NotImplementedError
@property
def details(self):
"""Returns more details for this item."""
raise NotImplementedError
@property
def deadline(self):
"""Returns deadline for the item or None if there is no deadline."""
raise NotImplementedError
@property
def link(self):
"""Returns a link associated with this item."""
raise NotImplementedError
class BackupAdminNeededActionItem(ActionItem):
"""Implementation of ActionItem that states that backup administrator is
needed for the specified organization.
"""
def __init__(self, organization, deadline, url_names):
"""Initializes a new instance of this action item.
Args:
organization: org_model.Organization entity.
deadline: datetime.datetime object telling by when the action has to
be completed.
url_names:
"""
self._organization = organization
self._deadline = deadline
self._url_names = url_names
@property
def identifier(self):
"""See ActionItem.identifier for specification."""
return _BACKUP_ADMIN_NEEDED_ID_PREFIX + self._organization.org_id
@property
def title(self):
"""See ActionItem.title for specification."""
return _BACKUP_ADMIN_NEEDED_TITLE % self._organization.name
@property
def details(self):
"""See ActionItem.details for specification."""
return _BACKUP_ADMIN_NEEDED_DETAILS
@property
def deadline(self):
"""See ActionItem.deadline for specification."""
return self._deadline
@property
def link(self):
"""See ActionItem.link for specification."""
return links_helper.Link(
links.LINKER.organization(
self._organization.key, self._url_names.CONNECTION_START_AS_ORG),
'Start connection')
def createBackupAdminNeededActionItem(data, url_names):
"""Factory function for BackupAdminNeededActionItem class.
Args:
data: request_data.RequestData for the current request.
url_names: Instance of url_names.UrlNames.
Returns:
A list of created BackupAdminNeededActionItem instances.
"""
if not data.ndb_profile or not data.ndb_profile.is_admin:
# this action makes sense to organization administrators only
return []
elif data.timeline.orgsAnnounced():
# this action is applicable only during organization application period
return []
else:
items = []
organizations = ndb.get_multi(data.ndb_profile.admin_for)
for organization in organizations:
admin_keys = profile_logic.getOrgAdmins(organization, keys_only=True)
if len(admin_keys) > 1:
# there are more organization admins, so everything is fine
continue
items.append(
BackupAdminNeededActionItem(
organization, data.timeline.orgSignupEnd(), url_names))
return items
class LogoNeededActionItem(ActionItem):
"""Implementation of ActionItem that states that logo image still needs to
be uploaded for the specified organization.
"""
def __init__(self, organization, deadline, url_names):
"""Initializes a new instance of this action item.
Args:
organization: org_model.Organization entity.
deadline: datetime.datetime object telling by when the action has to
be completed.
url_names: Instance of url_names.UrlNames.
"""
self._organization = organization
self._deadline = deadline
self._url_names = url_names
@property
def identifier(self):
"""See ActionItem.identifier for specification."""
return 'logo-needed-action-' + self._organization.org_id
@property
def title(self):
"""See ActionItem.title for specification."""
return _LOGO_NEEDED_TITLE % self._organization.name
@property
def details(self):
"""See ActionItem.details for specification."""
return _LOGO_NEEDED_DETAILS
@property
def deadline(self):
"""See ActionItem.deadline for specification."""
return self._deadline
@property
def link(self):
"""See ActionItem.link for specification."""
return links_helper.Link(
links.LINKER.organization(
self._organization.key, self._url_names.ORG_LOGO_EDIT),
'Upload logo')
def createLogoNeededActionItem(data, url_names):
"""Factory function for LogoNeededActionItem class.
Args:
data: request_data.RequestData for the current request.
url_names: Instance of url_names.UrlNames.
Returns:
A newly created LogoNeededActionItem instance.
"""
if not data.ndb_profile or not data.ndb_profile.is_admin:
# this action makes sense to organization administrators only
return []
else:
items = []
organizations = ndb.get_multi(data.ndb_profile.admin_for)
for organization in organizations:
if not organization.logo_path:
items.append(
LogoNeededActionItem(
organization, data.timeline.orgSignupEnd(), url_names))
return items
class AppResponseNeededAction(ActionItem):
"""Implementation of ActionItem that states that application response has
yet to be submitted for the specified organization.
"""
def __init__(self, organization, deadline, url_names):
"""Initializes a new instance of this action item.
Args:
organization: org_model.Organization entity.
deadline: datetime.datetime object telling by when the action has to
be completed.
url_names: Instance of url_names.UrlNames.
"""
self._organization = organization
self._deadline = deadline
self._url_names = url_names
@property
def identifier(self):
"""See ActionItem.identifier for specification."""
return _APP_RESPONSE_NEEDED_ID_PREFIX + self._organization.org_id
@property
def title(self):
"""See ActionItem.title for specification."""
return _APP_RESPONSE_NEEDED_TITLE % self._organization.name
@property
def details(self):
"""See ActionItem.details for specification."""
return _APP_RESPONSE_DETAILS
@property
def deadline(self):
"""See ActionItem.deadline for specification."""
return self._deadline
@property
def link(self):
"""See ActionItem.link for specification."""
return links_helper.Link(
links.LINKER.organization(
self._organization.key, self._url_names.ORG_APPLICATION_SUBMIT),
'Questionnaire')
def createAppResponseNeededActionItems(data, url_names):
"""Factory function for AppResponseNeededAction class.
Args:
data: request_data.RequestData for the current request.
url_names: Instance of url_names.UrlNames.
Returns:
A list of created BackupAdminNeededActionItem instances.
"""
if not data.ndb_profile or not data.ndb_profile.is_admin:
# this action makes sense to organization administrators only
return []
elif not data.timeline.beforeOrgSignupEnd():
# this action is applicable only when org application survey is open
return []
else:
# check for any organizations this profile is an admin for
# that are missing the application questionnaire response
items = []
organizations = ndb.get_multi(data.ndb_profile.admin_for)
for organization in organizations:
if not org_logic.hasApplicationResponse(organization.key):
items.append(
AppResponseNeededAction(
organization, data.timeline.orgSignupEnd(), url_names))
return items
class ActionsRequired(template.Template):
"""Action Required template."""
def __init__(self, data, items, template_path):
"""Initializes a new instance of this template.
Args:
data: request_data.RequestData for the current request.
items: A list of ActionItem instances to be displayed by the template.
template_path: Path to the HTML template to use for rendering.
"""
super(ActionsRequired, self).__init__(data)
self._items = items
self._template_path = template_path
def context(self):
"""See template.Template.context for specification."""
return {'items': self._items}
def templatePath(self):
"""See template.Template.templatePath for specification."""
return 'modules/gsoc/homepage/_action_required.html'