| # Copyright 2011 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 for the shipment tracking views.""" |
| |
| import logging |
| |
| import httplib2 |
| |
| from google.appengine.api import taskqueue |
| from google.appengine.ext import ndb |
| from google.appengine.ext import webapp |
| |
| from apiclient.discovery import build |
| from django import forms as django_forms |
| from django import http |
| from django.conf.urls import url as django_url |
| from django.utils.dateformat import format |
| from oauth2client.appengine import OAuth2Decorator |
| |
| from melange.request import access |
| from melange.request import exception |
| |
| from soc.logic import site as site_logic |
| from soc.models import site |
| from soc.views import template |
| from soc.views.helper import context as context_helper |
| from soc.views.helper import lists |
| from soc.views.helper import url_patterns as soc_url_patterns |
| |
| from soc.modules.gsoc.views import forms |
| from soc.modules.gsoc.views.helper import url_names |
| from soc.modules.gsoc.views.helper import url_patterns |
| from soc.modules.gsoc.views.helper.url_patterns import url |
| from soc.modules.gsoc.views import base |
| |
| from summerofcode.request import links |
| from summerofcode.models import shipment_tracking |
| |
| # TODO(daniel): once Site has been migrated to ndb, update this to make sure |
| # it benefits from ndbs caching mechanisms. |
| site = site_logic.singleton() |
| decorator = OAuth2Decorator( |
| client_id=site.google_client_id, |
| client_secret=site.google_client_secret, |
| scope='https://www.googleapis.com/auth/drive.file', |
| # Force an approval prompt so that the credentials object will |
| # have an refresh_token set. |
| approval_prompt='force') |
| |
| URL_FMT = ("https://docs.google.com/feeds/download/spreadsheets/Export" + |
| "?key=%s&exportFormat=csv&gid=%d") |
| |
| |
| def redirect(self, uri): |
| raise exception.Redirect(uri) |
| |
| |
| class CallbackPage(base.GSoCRequestHandler): |
| """View with the document picker. |
| """ |
| access_checker = access.PROGRAM_ADMINISTRATOR_ACCESS_CHECKER |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.getDjangoURLPatterns for specification.""" |
| callback_path = decorator.callback_path.lstrip('/') |
| return [ |
| django_url(callback_path, self, name=url_names.GSOC_PICKER_CALLBACK), |
| ] |
| |
| def get(self, data, check, mutator): |
| response = webapp.Response() |
| request = webapp.Request(data.request.environ) |
| cls = decorator.callback_handler() |
| cls.redirect = redirect |
| handler = cls() |
| handler.initialize(request, response) |
| handler.get() |
| response = http.HttpResponse(content=response.body) |
| return response |
| |
| |
| NAME_LABEL_ = "Name" |
| NAME_HELP_TEXT = "Name Of Shipment" |
| SPREADSHEET_ID_LABEL = "Spreadsheet id" |
| SPREADSHEET_HELP_TEXT = ( |
| 'Id of the Google spreadsheet that holds shipment data. ' |
| 'Click input field to select a document.') |
| |
| |
| class ShipmentInfoForm(forms.GSoCModelForm): |
| """Form for editing ShipmentInfo objects.""" |
| |
| Meta = object() |
| name = django_forms.CharField( |
| required=True, label=NAME_LABEL_, help_text=NAME_HELP_TEXT) |
| spreadsheet_id = django_forms.CharField( |
| required=True, label=SPREADSHEET_ID_LABEL, help_text=SPREADSHEET_HELP_TEXT) |
| |
| |
| class CreateShipmentInfo(base.GSoCRequestHandler): |
| """View with the document picker. |
| """ |
| access_checker = access.PROGRAM_ADMINISTRATOR_ACCESS_CHECKER |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.getDjangoURLPatterns for specification.""" |
| return [ |
| url(r'admin/shipment_info/create/%s$' % |
| soc_url_patterns.PROGRAM, |
| self, name=url_names.GSOC_CREATE_SHIPMENT_INFO), |
| url(r'admin/shipment_info/edit/%s' % |
| url_patterns.SHIPMENT_INFO, |
| self, name=url_names.GSOC_EDIT_SHIPMENT_INFO), |
| ] |
| |
| def _getShipmentInfo(self, data): |
| id_string = data.kwargs.get('id', '') |
| shipment_id = int(id_string) if id_string.isalnum() else -1 |
| if shipment_id < 1: |
| return None |
| return shipment_tracking.ShipmentInfo.get_by_id( |
| shipment_id, parent=ndb.Key.from_old_key(data.program.key())) |
| |
| def renderForm(self, data): |
| response = webapp.Response() |
| request = webapp.Request(data.request.environ) |
| shipment_info = self._getShipmentInfo(data) |
| impl = CreateShipmentInfoHandler(self.renderer, data, shipment_info) |
| impl.initialize(request, response) |
| impl.get() |
| return response.body |
| |
| def get(self, data, check, mutator): |
| rendered_form = self.renderForm(data) |
| response = http.HttpResponse(content=rendered_form) |
| return response |
| |
| def post(self, data, check, mutator): |
| form = ShipmentInfoForm(data=data.POST) |
| error = bool(form.errors) |
| if error: |
| rendered_form = self.renderForm(data) |
| return http.HttpResponse(content=rendered_form) |
| else: |
| # TODO(daniel): find a better solution for creating entities from forms |
| # that use ndb |
| name = form.cleaned_data['name'] |
| spreadsheet_id = form.cleaned_data['spreadsheet_id'] |
| shipment_info = self._getShipmentInfo(data) |
| if not shipment_info: |
| shipment_info = shipment_tracking.ShipmentInfo( |
| parent=ndb.Key.from_old_key(data.program.key())) |
| shipment_info.name = name |
| shipment_info.spreadsheet_id = spreadsheet_id |
| shipment_info.put() |
| shipment_id = shipment_info.key.id() |
| edit_url = links.SOC_LINKER.shipmentInfo( |
| data.program, shipment_id, url_names.GSOC_EDIT_SHIPMENT_INFO) |
| raise exception.Redirect(edit_url) |
| |
| |
| class CreateShipmentInfoHandler(webapp.RequestHandler): |
| """Implementation of the view with the document picker.""" |
| |
| def __init__(self, renderer, data, shipment_info): |
| self.renderer = renderer |
| self.data = data |
| self.shipment_info = shipment_info |
| |
| # TODO(nathaniel): Remove the lint suppression when |
| # https://code.google.com/p/googleappengine/issues/detail?id=10518 is |
| # resolved. |
| def redirect(self, uri, permanent=False): # pylint: disable=arguments-differ |
| raise exception.Redirect(uri) |
| |
| @decorator.oauth_required |
| def get(self): |
| template_path = 'summerofcode/shipment_tracking/picker.html' |
| result_url = links.SOC_LINKER.program( |
| self.data.program, url_names.GSOC_CREATE_SHIPMENT_INFO) |
| context = context_helper.default(self.data) |
| form_dict = self.shipment_info.to_dict() if self.shipment_info else None |
| form_data = self.data.POST or form_dict |
| form = ShipmentInfoForm(data=form_data) |
| error = bool(form.errors) |
| context.update({ |
| 'access_token': decorator.credentials.access_token, |
| 'client_id': decorator.credentials.client_id, |
| 'developer_key': site.google_developer_key, |
| 'error': error, |
| 'forms': [form], |
| 'page_name': 'Select a tracking spreadsheet', |
| 'result_url': result_url, |
| }) |
| response_content = self.renderer.render(self.data, template_path, context) |
| self.response.write(response_content) |
| |
| |
| class ShipmentInfoList(template.Template): |
| """Template for the list of ShipmentInfo objects. |
| """ |
| |
| def __init__(self, request, data): |
| self.request = request |
| self.data = data |
| |
| list_config = lists.ListConfiguration() |
| list_config.addSimpleColumn('name', 'Name') |
| list_config.addSimpleColumn('status', 'Status') |
| list_config.addPlainTextColumn( |
| 'last_sync_time', 'Last Sync Time', |
| lambda ent, *args: format( |
| ent.last_sync_time, lists.DATETIME_FORMAT) if \ |
| ent.last_sync_time else 'N/A') |
| |
| self._list_config = list_config |
| |
| def rowAction(entity, *args): |
| entity_id = entity.key.id() |
| |
| return links.SOC_LINKER.shipmentInfo( |
| data.program, entity_id, url_names.GSOC_EDIT_SHIPMENT_INFO) |
| |
| self._list_config.setRowAction(rowAction) |
| |
| def templatePath(self): |
| return 'summerofcode/shipment_tracking/_list.html' |
| |
| def context(self): |
| description = 'List of shipment informations for %s' % \ |
| self.data.program.name |
| |
| list_response = lists.ListConfigurationResponse( |
| self.data, self._list_config, 0, description) |
| return { |
| 'list_name': 'Shipment Informations', |
| 'lists': [list_response], |
| } |
| |
| def getListData(self): |
| idx = lists.getListIndex(self.request) |
| if idx != 0: |
| return None |
| |
| q = shipment_tracking.ShipmentInfo.query( |
| ancestor=ndb.Key.from_old_key(self.data.program.key())) |
| starter = lists.keyStarter |
| response_builder = lists.RawQueryContentResponseBuilder( |
| self.request, self._list_config, q, starter) |
| |
| return response_builder.build() |
| |
| |
| class ShipmentInfoListPage(base.GSoCRequestHandler): |
| """Admin view for listing all shipment infos for a specific program. |
| """ |
| access_checker = access.PROGRAM_ADMINISTRATOR_ACCESS_CHECKER |
| |
| def djangoURLPatterns(self): |
| return [ |
| url(r'admin/shipment_tracking/records/%s' % |
| soc_url_patterns.PROGRAM, |
| self, name='gsoc_shipment_info_records'), |
| ] |
| |
| def templatePath(self): |
| return 'summerofcode/shipment_tracking/records.html' |
| |
| def context(self, data, check, mutator): |
| context = { |
| 'page_name': 'Shipment informations for %s' % data.program.name, |
| 'list': ShipmentInfoList(data.request, data), |
| } |
| return context |
| |
| def jsonContext(self, data, check, mutator): |
| list_content = ShipmentInfoList(data.request, data).getListData() |
| if not list_content: |
| raise exception.Forbidden( |
| 'You do not have access to this data') |
| return list_content.content() |
| |
| |
| class SyncData(base.GSoCRequestHandler): |
| """Admin view that shows syncing tasks. |
| """ |
| access_checker = access.PROGRAM_ADMINISTRATOR_ACCESS_CHECKER |
| |
| DEF_BATCH_SIZE = 100 |
| |
| def djangoURLPatterns(self): |
| return [ |
| url(r'admin/shipment_tracking/sync/%s$' % soc_url_patterns.PROGRAM, |
| self, name=url_names.GSOC_SHIPMENT_LIST), |
| ] |
| |
| def templatePath(self): |
| return 'summerofcode/shipment_tracking/sync_data.html' |
| |
| def context(self, data, check, mutator): |
| def getUrl(shipment_info): |
| return links.SOC_LINKER.shipmentInfo( |
| data.program, shipment_info.key.id(), url_names.GSOC_SHIPMENT_SYNC) |
| query = shipment_tracking.ShipmentInfo.query( |
| ancestor=ndb.Key.from_old_key(data.program.key())) |
| list_data = [(getUrl(i), i) for i in query.fetch(self.DEF_BATCH_SIZE)] |
| return { |
| 'page_name': 'Sync Trackings Data for %s' % data.program.name, |
| 'shipment_infos': list_data, |
| } |
| |
| |
| class ShipmentTrackingPage(base.GSoCRequestHandler): |
| """View with the document picker. |
| """ |
| access_checker = access.PROGRAM_ADMINISTRATOR_ACCESS_CHECKER |
| |
| def djangoURLPatterns(self): |
| """See base.RequestHandler.getDjangoURLPatterns for specification.""" |
| return [ |
| url(r'admin/shipment_tracking/sync/%s$' % |
| url_patterns.SHIPMENT_INFO, |
| self, name=url_names.GSOC_SHIPMENT_SYNC), |
| ] |
| |
| def get(self, data, check, mutator): |
| response = webapp.Response() |
| request = webapp.Request(data.request.environ) |
| impl = ShipmentTrackingPageImpl(self.renderer, data) |
| impl.initialize(request, response) |
| impl.handle() |
| response = http.HttpResponse(content=response.body) |
| return response |
| |
| def post(self, data, check, mutator): |
| return self.get(data, check, mutator) |
| |
| |
| class ShipmentTrackingPageImpl(webapp.RequestHandler): |
| """Implementation of the view with the document picker.""" |
| |
| def __init__(self, renderer, data): |
| self.renderer = renderer |
| self.data = data |
| |
| # TODO(nathaniel): Remove the disabled lint inspection when |
| # https://code.google.com/p/googleappengine/issues/detail?id=10518 is |
| # resolved. |
| def redirect(self, uri, permanent=False): # pylint:disable=arguments-differ |
| raise exception.Redirect(uri) |
| |
| @decorator.oauth_required |
| def handle(self): |
| credentials = decorator.get_credentials() |
| authorized_http = credentials.authorize(httplib2.Http()) |
| drive_service = build('drive', 'v2', http=authorized_http) |
| shipment_info_id = int(self.data.kwargs['id']) |
| program_key = ndb.Key.from_old_key(self.data.program.key()) |
| shipment_info = shipment_tracking.ShipmentInfo.get_by_id( |
| shipment_info_id, parent=program_key) |
| usa_url = URL_FMT % (shipment_info.spreadsheet_id, 0) |
| intl_url = URL_FMT % (shipment_info.spreadsheet_id, 1) |
| |
| #get sheet content for USA students |
| resp, usa_sheet_content = drive_service._http.request(usa_url) |
| if resp.status != 200: |
| logging.error('An error occurred: %s', resp) |
| |
| resp, intl_sheet_content = drive_service._http.request(intl_url) |
| if resp.status != 200: |
| logging.error('An error occurred: %s', resp) |
| |
| task_start_url = links.SOC_LINKER.site(url_names.GSOC_SHIPMENT_TASK_START) |
| |
| #start task for USA students |
| params = { |
| 'program_key': str(self.data.program.key()), |
| 'sheet_content': usa_sheet_content, |
| 'sheet_type': 'usa', |
| 'shipment_info_id': shipment_info_id, |
| } |
| taskqueue.add(url=task_start_url, params=params) |
| |
| #start task for international students |
| params = { |
| 'program_key': str(self.data.program.key()), |
| 'sheet_content': intl_sheet_content, |
| 'sheet_type': 'intl', |
| 'shipment_info_id': shipment_info_id, |
| } |
| taskqueue.add(url=task_start_url, params=params) |
| |
| #return back to sync data page |
| result_url = links.SOC_LINKER.program( |
| self.data.program, url_names.GSOC_SHIPMENT_LIST) |
| raise exception.Redirect(result_url) |