blob: 21c9041b72c008815687091279794e32bba3e0b3 [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 the project related views for Summer Of Code."""
from django import forms as django_forms
from django import http
from django.utils import translation
from melange.logic import profile as profile_logic
from melange.request import access
from melange.request import exception
from soc.logic import cleaning
from soc.modules.gsoc.models import code_sample as code_sample_model
from soc.modules.gsoc.views import assign_mentor
from soc.modules.gsoc.views import forms as gsoc_forms
from soc.modules.gsoc.views import base as gsoc_base
from soc.modules.gsoc.views import project_details as old_project_view
from soc.modules.gsoc.views.helper import url_patterns as soc_url_patterns
from soc.views import base
from soc.views import base_templates
from soc.views.helper import url_patterns
from summerofcode.logic import project as project_logic
from summerofcode.models import project as project_model
from summerofcode.request import access as soc_access
from summerofcode.request import error
from summerofcode.request import links
from summerofcode.request import render
from summerofcode.templates import project_list
from summerofcode.templates import sidebar_actions
from summerofcode.views.helper import urls as soc_urls
_MANAGE_ACTIONS_TITLE = translation.ugettext('Project Actions')
_PROJECT_DETAILS_PAGE_NAME = translation.ugettext('Project details')
_PROJECT_EDIT_PAGE_NAME = translation.ugettext('Edit Project')
_PROJECT_LIST_PUBLIC_PAGE_NAME = translation.ugettext('Accepted projects - %s')
_PROJECT_FORM_TITLE_LABEL = translation.ugettext('Title')
_PROJECT_FORM_TITLE_HELP_TEXT = translation.ugettext('Title of the project')
_PROJECT_FORM_TITLE_MAX_LENGTH = 75
_PROJECT_FORM_ABSTRACT_LABEL = translation.ugettext('Abstract')
_PROJECT_FORM_ABSTRACT_MAX_LENGTH = 500
_PROJECT_FORM_ABSTRACT_HELP_TEXT = translation.ugettext(
'Short description, summary, or snippet for the project. '
'This part is displayed publicly. Please enter up to %s characters.'
% _PROJECT_FORM_ABSTRACT_MAX_LENGTH)
_PROJECT_FORM_FULL_CONTENT_LABEL = translation.ugettext('Content')
_PROJECT_FORM_FULL_CONTENT_HELP_TEXT = translation.ugettext(
'Additional information about this project to be shown publicly.')
_PROJECT_FORM_ADDITIONAL_INFO_LABEL = translation.ugettext(
'Additional Info URL')
_PROJECT_FORM_ADDITIONAL_INFO_HELP_TEXT = translation.ugettext(
'URL to a resource containing more information about your project.')
class ProjectForm(gsoc_forms.GSoCModelForm):
"""Django form to show specific fields for a project."""
title = django_forms.CharField(
required=True,
label=_PROJECT_FORM_TITLE_LABEL,
help_text=_PROJECT_FORM_TITLE_HELP_TEXT,
max_length=_PROJECT_FORM_TITLE_MAX_LENGTH)
abstract = django_forms.CharField(
required=True,
widget=django_forms.Textarea(),
label=_PROJECT_FORM_ABSTRACT_LABEL,
help_text=_PROJECT_FORM_ABSTRACT_HELP_TEXT,
max_length=_PROJECT_FORM_ABSTRACT_MAX_LENGTH)
full_content = django_forms.CharField(
required=False,
widget=django_forms.Textarea(),
label=_PROJECT_FORM_FULL_CONTENT_LABEL,
help_text=_PROJECT_FORM_FULL_CONTENT_HELP_TEXT)
additional_info = django_forms.URLField(
required=False,
label=_PROJECT_FORM_ADDITIONAL_INFO_LABEL,
help_text=_PROJECT_FORM_ADDITIONAL_INFO_HELP_TEXT)
clean_full_content = cleaning.clean_html_content('full_content')
def _adaptProjectPropertiesForForm(project_properties):
"""Adapts properties of a project entity, which are persisted in datastore,
to representation which may be passed to populate ProjectForm.
Args:
project_properties: A dict containing project properties as persisted
in datastore.
Returns:
A dict mapping properties of project model to values which can be
populated to a project form.
"""
return {
'title': project_properties['title'],
'abstract': project_properties['abstract'],
'full_content': project_properties['content'],
'additional_info': project_properties.get('additional_info'),
}
def _adaptProjectPropertiesForDatastore(form_data):
"""Adapts properties corresponding to project's properties, which
have been submitted in a form, to the format that is compliant with
project_model.Project model.
Args:
form_data: A dict containing data submitted in a form.
Returns:
A dict mapping properties of project model to values based on
data submitted in a form.
"""
return {
project_model.Project.title._name: form_data.get('title'),
project_model.Project.abstract._name: form_data.get('abstract'),
project_model.Project.content._name: form_data.get('full_content'),
project_model.Project.additional_info._name:
form_data.get('additional_info') or None,
}
class ProjectEditPage(base.RequestHandler):
"""View to edit various project details by the student."""
access_checker = access.ConjuctionAccessChecker([
access.IS_URL_USER_ACCESS_CHECKER,
soc_access.STUDENTS_ANNOUNCED_ACCESS_CHECKER])
def __init__(self, initializer, linker, renderer, error_handler,
url_pattern_constructor, url_names, template_path):
"""Initializes a new instance of the request handler for the specified
parameters.
Args:
initializer: Implementation of initialize.Initializer interface.
linker: Instance of links.Linker class.
renderer: Implementation of render.Renderer interface.
error_handler: Implementation of error.ErrorHandler interface.
url_pattern_constructor:
Implementation of url_patterns.UrlPatternConstructor.
url_names: Instance of url_names.UrlNames.
template_path: The path of the template to be used.
"""
super(ProjectEditPage, self).__init__(
initializer, linker, renderer, error_handler)
self.url_pattern_constructor = url_pattern_constructor
self.url_names = url_names
self.template_path = template_path
def djangoURLPatterns(self):
"""See base.RequestHandler.djangoURLPatterns for specification."""
return [
self.url_pattern_constructor.construct(
r'project/edit/%s$' % url_patterns.USER_ID,
self, name=self.url_names.PROJECT_EDIT),
]
def templatePath(self):
"""See base.RequestHandler.templatePath for specification."""
return self.template_path
def context(self, data, check, mutator):
"""See base.RequestHandler.context for specification."""
form_data = _adaptProjectPropertiesForForm(data.url_ndb_project.to_dict())
form = ProjectForm(data=data.POST or form_data)
return {
'forms': [form],
'page_name': _PROJECT_EDIT_PAGE_NAME,
}
def post(self, data, check, mutator):
"""See base.RequestHandler.post for specification."""
form = ProjectForm(data=data.POST)
if not form.is_valid():
# TODO(nathaniel): problematic self-use.
return self.get(data, check, mutator)
else:
project_properties = (
_adaptProjectPropertiesForDatastore(form.cleaned_data))
result = project_logic.updateProject(
data.url_ndb_project.key, project_properties)
if not result:
raise exception.BadRequest(message=result.extra)
else:
url = links.SOC_LINKER.userId(
data.ndb_profile.key, result.extra.key.id(),
self.url_names.PROJECT_EDIT) + '?validated=True'
return http.HttpResponseRedirect(url)
PROJECT_EDIT_PAGE = ProjectEditPage(
gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
soc_urls.UrlNames, 'modules/gsoc/form_base.html',)
class ProjectDetailsPage(base.RequestHandler):
"""View to display project details."""
access_checker = soc_access.STUDENTS_ANNOUNCED_ACCESS_CHECKER
def __init__(self, initializer, linker, renderer, error_handler,
url_pattern_constructor, url_names, template_path):
"""Initializes a new instance of the request handler for the specified
parameters.
Args:
initializer: Implementation of initialize.Initializer interface.
linker: Instance of links.Linker class.
renderer: Implementation of render.Renderer interface.
error_handler: Implementation of error.ErrorHandler interface.
url_pattern_constructor:
Implementation of url_patterns.UrlPatternConstructor.
url_names: Instance of url_names.UrlNames.
template_path: The path of the template to be used.
"""
super(ProjectDetailsPage, self).__init__(
initializer, linker, renderer, error_handler)
self.url_pattern_constructor = url_pattern_constructor
self.url_names = url_names
self.template_path = template_path
def djangoURLPatterns(self):
"""See base.RequestHandler.djangoURLPatterns for specification."""
return [
self.url_pattern_constructor.construct(
r'project/details/%s$' % url_patterns.USER_ID,
self, name=self.url_names.PROJECT_DETAILS),
]
def templatePath(self):
"""See base.RequestHandler.templatePath for specification."""
return self.template_path
def context(self, data, check, mutator):
"""See base.RequestHandler.context for specification."""
mentors = ', '.join(
mentor_key.get().public_name
for mentor_key in data.url_ndb_project.mentors)
if ((data.ndb_profile
and data.url_ndb_project.organization in data.ndb_profile.admin_for)
or data.is_host):
all_mentors = profile_logic.queryAllMentorsForOrg(
data.url_ndb_project.organization).fetch(1000, keys_only=True)
# TODO(daniel): replace it with a correct URL
assign_mentor_url = links.SOC_LINKER.userId(
data.url_ndb_profile.key, data.url_ndb_project.key.id(),
self.url_names.PROJECT_ASSIGN_MENTORS)
assign_mentor_field = assign_mentor.AssignMentorFields(
data, data.url_ndb_project.mentors, assign_mentor_url,
all_mentors=all_mentors, allow_multiple=True)
# TODO(daniel): add actual toggle buttons.
toggle_buttons = []
manage_actions = sidebar_actions.SidebarActions(
data, _MANAGE_ACTIONS_TITLE, toggle_buttons,
assign_mentor_field=assign_mentor_field)
else:
manage_actions = None
edit_url = (
links.SOC_LINKER.userId(
data.url_ndb_profile.key, data.url_ndb_project.key.id(),
self.url_names.PROJECT_EDIT)
if (data.ndb_profile
and data.ndb_profile.key == data.url_ndb_project.key.parent())
else None)
# TODO(daniel): replace it with new updated code samples at some point
query = code_sample_model.GSoCCodeSample.all()
query.ancestor(data.url_ndb_project.key.to_old_key())
code_samples_list = (
old_project_view.ListCodeSamples(data, False)
if query.count() > 0 else None)
code_samples_url = (
links.SOC_LINKER.userId(
data.url_ndb_profile.key, data.url_ndb_project.key.id(),
self.url_names.PROJECT_CODE_SAMPLES)
# TODO(daniel): move this condition to a logic function
if (len(data.url_ndb_project.passed_evaluations) >= 2
and data.ndb_profile
and data.ndb_profile.key == data.url_ndb_project.key.parent())
else None)
return {
'abstract': data.url_ndb_project.abstract,
'code_samples_list': code_samples_list,
'code_samples_url': code_samples_url,
'content': data.url_ndb_project.content,
'edit_url': edit_url,
'manage_actions': manage_actions,
'mentors': mentors,
'org_name': data.url_ndb_project.organization.get().name,
'page_name': _PROJECT_DETAILS_PAGE_NAME,
'student_name': data.url_ndb_project.key.parent().get().public_name,
'title': data.url_ndb_project.title,
}
PROJECT_DETAILS_PAGE = ProjectDetailsPage(
gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
soc_urls.UrlNames, 'summerofcode/project/project_details.html')
class ListProjectPublic(base.RequestHandler):
"""View to list all projects for the specified program."""
access_checker = soc_access.STUDENTS_ANNOUNCED_ACCESS_CHECKER
def __init__(self, initializer, linker, renderer, error_handler,
url_pattern_constructor, url_names, template_path):
"""Initializes a new instance of the request handler for the specified
parameters.
Args:
initializer: Implementation of initialize.Initializer interface.
linker: Instance of links.Linker class.
renderer: Implementation of render.Renderer interface.
error_handler: Implementation of error.ErrorHandler interface.
url_pattern_constructor:
Implementation of url_patterns.UrlPatternConstructor.
url_names: Instance of url_names.UrlNames.
template_path: The path of the template to be used.
"""
super(ListProjectPublic, self).__init__(
initializer, linker, renderer, error_handler)
self.url_pattern_constructor = url_pattern_constructor
self.url_names = url_names
self.template_path = template_path
def djangoURLPatterns(self):
"""See base.RequestHandler.djangoURLPatterns for specification."""
return [
self.url_pattern_constructor.construct(
r'projects/list/%s$' % url_patterns.PROGRAM,
self, name=self.url_names.PROJECT_LIST_PUBLIC)
]
def templatePath(self):
"""See base.RequestHandler.templatePath for specification."""
return self.template_path
def jsonContext(self, data, check, mutator):
"""Handler for JSON requests."""
list_content = project_list.getPublicProjectList(data).getListData()
if list_content:
return list_content.content()
else:
raise exception.BadRequest(message='This data cannot be accessed.')
def context(self, data, check, mutator):
"""Handler for GSoC Accepted Projects List page HTTP get request."""
return {
'page_name': _PROJECT_LIST_PUBLIC_PAGE_NAME % data.program.name,
'list': project_list.getPublicProjectList(data),
'program_select': base_templates.DefaultProgramSelect(
data, self.url_names.PROJECT_LIST_PUBLIC),
}
LIST_PROJECT_PUBLIC = ListProjectPublic(
gsoc_base._GSOC_INITIALIZER, links.SOC_LINKER, render.SOC_RENDERER,
error.SOC_ERROR_HANDLER, soc_url_patterns.SOC_URL_PATTERN_CONSTRUCTOR,
soc_urls.UrlNames, 'summerofcode/list.html')