| # 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 views for proposal duplicates.""" |
| |
| import datetime |
| |
| from google.appengine.ext import ndb |
| |
| from django import http |
| from django.utils import translation |
| |
| from mapreduce import model as mapreduce_model |
| |
| from melange.logic import profile as profile_logic |
| from melange.models import job_status as job_status_model |
| from melange.request import access |
| |
| from soc.mapreduce.helper import control as mapreduce_control |
| from soc.views.helper import url_patterns |
| from soc.views import base |
| from soc.views import template |
| |
| from soc.modules.gsoc.views import base as gsoc_base |
| from soc.modules.gsoc.views.helper import url_patterns as soc_url_patterns |
| |
| from summerofcode.logic import profile as soc_profile_logic |
| from summerofcode.mapreduce import find_duplicates |
| from summerofcode.request import error |
| from summerofcode.request import links |
| from summerofcode.request import render |
| from summerofcode.views.helper import urls as soc_urls |
| |
| |
| _CALCULATING_STATE = translation.ugettext('Calculating') |
| _COMPLETED_STATE = translation.ugettext('Completed') |
| _NOT_STARTED_STATE = translation.ugettext('Not started') |
| _UNKNOWN_STATE = translation.ugettext('Unknown') |
| |
| _FIND_DUPLICATES_JOB_ID = 'find-duplicates' |
| |
| |
| class Duplicate(template.Template): |
| """Template to display a single student with duplicates for the program.""" |
| |
| def __init__(self, data, url_names, profile): |
| """Initializes a new template. |
| |
| Args: |
| data: request_data.RequestData for the current request. |
| url_names: Instance of url_names.UrlNames. |
| profile: profile_model.Profile entity for which to display duplicates. |
| """ |
| super(Duplicate, self).__init__(data) |
| if not profile.is_student: |
| raise ValueError('Duplicates can be displayed only for student profiles.') |
| else: |
| self._url_names = url_names |
| self._profile = profile |
| |
| def context(self): |
| """See template.Template.context for specification.""" |
| org_details = {} |
| proposals = ndb.get_multi( |
| self._profile.student_data.to_be_accepted_proposals) |
| for proposal in proposals: |
| if proposal.organization not in org_details: |
| organization = proposal.organization.get() |
| org_details[proposal.organization] = { |
| 'admins': [], |
| 'home_url': links.SOC_LINKER.organization( |
| organization.key, self._url_names.ORG_HOME), |
| 'name': organization.name, |
| 'proposals': [], |
| } |
| admins = profile_logic.getOrgAdmins(organization.key) |
| for admin in admins: |
| org_details[proposal.organization]['admins'].append({ |
| 'name': admin.public_name, |
| 'email': admin.contact.email, |
| }) |
| |
| org_details[proposal.organization]['proposals'].append({ |
| 'review_url': links.SOC_LINKER.userId( |
| proposal.key.parent(), proposal.key.id(), |
| self._url_names.PROPOSAL_REVIEW_AS_ORG_MEMBER), |
| 'title': proposal.title, |
| }) |
| |
| return { |
| 'org_details': org_details, |
| 'profile': self._profile, |
| } |
| |
| def templatePath(self): |
| """See template.Template.templatePath for specification.""" |
| return 'modules/gsoc/duplicates/proposal_duplicate.html' |
| |
| |
| class DuplicatesPage(base.RequestHandler): |
| """View for the program administrators to see duplicates.""" |
| |
| access_checker = access.PROGRAM_ADMINISTRATOR_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(DuplicatesPage, 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'duplicates/%s$' % url_patterns.PROGRAM, self, |
| name=self.url_names.DUPLICATES), |
| ] |
| |
| def templatePath(self): |
| """See base.RequestHandler.templatePath for specification.""" |
| return 'modules/gsoc/duplicates/base.html' |
| |
| def context(self, data, check, mutator): |
| """See base.RequestHandler.context for specification.""" |
| duplicates = [ |
| Duplicate(data, self.url_names, profile) for profile |
| in soc_profile_logic.getProfilesWithDuplicates(data.program.key())] |
| |
| job_status = job_status_model.JobStatus.get_by_id(_FIND_DUPLICATES_JOB_ID) |
| if job_status: |
| mapreduce_state = mapreduce_model.MapreduceState.get_by_key_name( |
| job_status.identifier) |
| if mapreduce_state: |
| calculation_state = ( |
| _CALCULATING_STATE if mapreduce_state.active else _COMPLETED_STATE) |
| else: |
| calculation_state = _UNKNOWN_STATE |
| last_started_on = job_status.started_on |
| else: |
| calculation_state = _NOT_STARTED_STATE |
| last_started_on = None |
| |
| context = { |
| 'page_name': 'Duplicates for %s' % data.program.name, |
| 'calculation_state': calculation_state, |
| 'last_started_on': last_started_on, |
| 'duplicates': duplicates, |
| } |
| return context |
| |
| def post(self, data, check, mutator): |
| """See base.RequestHandler.post for specification.""" |
| _startFindDuplicateJob(data.program.key()) |
| return http.HttpResponseRedirect('') |
| |
| |
| DUPLICATES = DuplicatesPage( |
| 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/duplicates/base.html') |
| |
| |
| def _startFindDuplicateJob(program_key): |
| params = { |
| find_duplicates.ENTITY_KIND_ID_PARAM: 'melange.models.profile.Profile', |
| find_duplicates.PROGRAM_KEY_URLSAFE_PARAM: str(program_key), |
| } |
| identifier = mapreduce_control.start_map('FindDuplicates', params=params) |
| |
| @ndb.transactional |
| def createOrUpdateJobStatus(): |
| job_status = job_status_model.JobStatus( |
| id=_FIND_DUPLICATES_JOB_ID, identifier=identifier, |
| started_on=datetime.datetime.utcnow()) |
| job_status.put() |
| |
| createOrUpdateJobStatus() |