| # 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. |
| |
| """Logic for projects.""" |
| |
| import logging |
| |
| from google.appengine.api import datastore_errors |
| from google.appengine.ext import ndb |
| |
| from melange.utils import rich_bool |
| |
| from summerofcode.models import project as project_model |
| |
| |
| # list of properties of Proposal model which cannot be updated |
| # by updateProposal function |
| _PROPERTIES_NOT_FOR_UPDATE = ['program', 'organization'] |
| |
| ILLEGAL_PROJECT_STATE = 'illegal_project_state' |
| |
| |
| def queryAcceptedProjects(program_key): |
| """Returns a query to fetch all projects accepted for the specified program. |
| |
| Args: |
| program_key: ndb.Key of the program. |
| |
| Returns: |
| ndb.Query object to fetch all projects accepted for the specified program. |
| """ |
| return project_model.Project.query( |
| project_model.Project.program == ndb.Key.from_old_key(program_key), |
| project_model.Project.status == project_model.Status.ACCEPTED) |
| |
| |
| def queryAllProjects(program_key): |
| """Returns a query to fetch all projects for the specified program. |
| |
| Args: |
| program_key: ndb.Key of the program. |
| |
| Returns: |
| ndb.Query object to fetch all projects for the specified program. |
| """ |
| return project_model.Project.query( |
| project_model.Project.program == ndb.Key.from_old_key(program_key)) |
| |
| |
| def queryAllProjectsForStudent(student_key): |
| """Returns a query to fetch all projects for the specified student. |
| |
| Args: |
| student_key: ndb.Key of the student. |
| |
| Returns: |
| ndb.Query object to fetch all projects for the specified student. |
| """ |
| return project_model.Project.query(ancestor=student_key) |
| |
| |
| def queryAcceptedProjectsForOrg(org_key): |
| """Returns a query to fetch all projects accepted for |
| the specified organization. |
| |
| Args: |
| org_ket: ndb.Key of the organization. |
| |
| Returns: |
| ndb.Query object to fetch all projects accepted for the specified |
| organization. |
| """ |
| return project_model.Project.query( |
| project_model.Project.organization == org_key, |
| project_model.Project.status == project_model.Status.ACCEPTED) |
| |
| |
| def queryAllProjectsForOrgMember(profile_key): |
| """Returns a query to fetch all projects mentored by the specified |
| organization memebr. |
| |
| Args: |
| profile_key: ndb.Key of the profile. |
| |
| Returns: |
| ndb.Query object to fetch all projects mentored by specified profile. |
| """ |
| return project_model.Project.query( |
| project_model.Project.mentors == profile_key) |
| |
| |
| @ndb.transactional |
| def updateProject(project_key, project_properties): |
| """Updates the specified project based on the specified properties. |
| |
| Args: |
| project_key: Project key. |
| project_properties: A dict containing properties to be updated. |
| |
| Returns: |
| rich_bool.RichBool whose value is set to True if the project has been |
| successfully updated. In that case, extra part points to the newly updated |
| project entity. Otherwise, rich_bool.RichBool whose value is set to |
| False and extra part is a string that represents the reason why the action |
| could not be completed. |
| """ |
| for prop in project_properties: |
| if prop in _PROPERTIES_NOT_FOR_UPDATE: |
| raise ValueError('Property %s cannot be updated by this function' % prop) |
| |
| project = project_key.get() |
| try: |
| project.populate(**project_properties) |
| project.put() |
| |
| return rich_bool.RichBool(True, project) |
| except ValueError as e: |
| logging.warning(e) |
| return rich_bool.RichBool(False, extra=str(e)) |
| except datastore_errors.BadValueError as e: |
| logging.warning(e) |
| return rich_bool.RichBool(False, extra=str(e)) |
| except TypeError as e: |
| logging.warning(e) |
| return rich_bool.RichBool(False, extra=str(e)) |
| |
| |
| @ndb.transactional |
| def withdrawProject(project_key): |
| """Withdraws the specified project. |
| |
| Args: |
| project_key: ndb.Key of the project. |
| |
| Returns: |
| rich_bool.RichBool whose value is set to True, if the specified project |
| is successfully withdrawn at this time. Otherwise, rich_bool.RichBool whose |
| value is set to False and extra part is a string that represents the reason |
| why the project cannot be withdrawn. |
| """ |
| project = project_key.get() |
| if project.status == project_model.Status.ACCEPTED: |
| project.status = project_model.Status.WITHDRAWN |
| |
| profile = project_key.parent().get() |
| profile.student_data.number_of_projects -= 1 |
| |
| ndb.put_multi([project, profile]) |
| return rich_bool.TRUE |
| elif project.status == project_model.Status.WITHDRAWN: |
| return rich_bool.TRUE |
| elif project.status == project_model.Status.FAILED: |
| return rich_bool.RichBool(False, ILLEGAL_PROJECT_STATE) |
| |
| |
| @ndb.transactional |
| def acceptProject(project_key): |
| """Marks the specified project as accepted. |
| |
| Please note that this function does not have anything to do with the |
| corresponding proposal. The function may be used, for example, to accept |
| a project which was withdrawn before. |
| |
| Args: |
| project_key: ndb.Key of the project. |
| |
| Returns: |
| rich_bool.RichBool whose value is set to True, if the specified project |
| is successfully accepted at this time. Otherwise, rich_bool.RichBool whose |
| value is set to False and extra part is a string that represents the reason |
| why the project cannot be accepted. |
| """ |
| project = project_key.get() |
| if project.status == project_model.Status.ACCEPTED: |
| return rich_bool.TRUE |
| elif project.status == project_model.Status.WITHDRAWN: |
| project.status = project_model.Status.ACCEPTED |
| |
| profile = project_key.parent().get() |
| profile.student_data.number_of_projects += 1 |
| |
| ndb.put_multi([project, profile]) |
| return rich_bool.TRUE |
| elif project.status == project_model.Status.FAILED: |
| return rich_bool.RichBool(False, ILLEGAL_PROJECT_STATE) |
| |
| |
| @ndb.transactional |
| def assignMentors(project_key, mentors): |
| """Assigns the specified mentors to the specified project. |
| |
| Args: |
| project_key: ndb.Key of the project. |
| mentors: List of profile_model.Profile entities of the mentors to |
| assign for the project. |
| |
| Returns: |
| rich_bool.RichBool whose value is set to True, if the specified project |
| is assigned the specified mentors. Otherwise, rich_bool.RichBool |
| whose value is set to False and extra part is a list of profile entities |
| of users who are not eligible to be assigned as mentors. |
| """ |
| project = project_key.get() |
| |
| if not mentors: |
| return rich_bool.RichBool(False, []) |
| else: |
| not_valid = [ |
| mentor for mentor in mentors |
| if project.organization not in mentor.mentor_for] |
| if not_valid: |
| return rich_bool.RichBool(False, not_valid) |
| else: |
| project.mentors = [mentor.key for mentor in mentors] |
| project.put() |
| return rich_bool.TRUE |