diff --git a/app/handlers.py b/app/handlers.py index 1747caf..f124aeb 100755 --- a/app/handlers.py +++ b/app/handlers.py @@ -5,6 +5,8 @@ from app.modules.examples.yaml_example import YamlExampleHandler from app.modules.training.pipeline_training import PipelineTrainigHandler from app.modules.training.http_training import HttpTrainingHandler +from app.modules.training.datastore_training import DatastoreTrainingHandler +from app.modules.cityinfo.cityinfo import CityInfoBuildHandler , CityInfoViewHandler def handlers(): return [ @@ -18,5 +20,8 @@ def handlers(): (r'/examples/datastore/([^\/]+)/([^\/]+)', DatastoreExampleHandler), (r'/examples/yaml', YamlExampleHandler), (r'/training/pipeline/(\d+)', PipelineTrainigHandler), - (r'/training/http/([\d\.]+)/([\d\.]+)', HttpTrainingHandler), + (r'/training/http/([-?\d\.]+)/([-?\d\.]+)', HttpTrainingHandler), + (r'/training/datastore', DatastoreTrainingHandler), + (r'/cityinfo/build', CityInfoBuildHandler), + (r'/cityinfo/view', CityInfoViewHandler), ] diff --git a/app/modules/cityinfo/__init__.py b/app/modules/cityinfo/__init__.py new file mode 100644 index 0000000..4957e95 --- /dev/null +++ b/app/modules/cityinfo/__init__.py @@ -0,0 +1 @@ +# __init__.py needed to create "package" for this directory diff --git a/app/modules/cityinfo/city_extras.py b/app/modules/cityinfo/city_extras.py new file mode 100644 index 0000000..1627cbe --- /dev/null +++ b/app/modules/cityinfo/city_extras.py @@ -0,0 +1,75 @@ +import requests +import logging +import os +from google.appengine.ext import ndb +from requests_toolbelt.adapters import appengine +from app.modules.common.kinds import CityInfo + +# https://toolbelt.readthedocs.io/en/latest/adapters.html#appengineadapter +appengine.monkeypatch(validate_certificate=False) + +def CityWikiInfo(lat , lon): + # wikipedia base api url + API_HOST = "https://en.wikipedia.org/w/api.php?" + + # add given lat long to api url + apiUrl = "%sformat=json&action=query&prop=extracts&exintro=1&explaintext=1&exlimit=20&generator=geosearch&ggsradius=10000&ggslimit=100&ggscoord=%s%s%s" % (API_HOST, lat,"|", lon) + + logging.info(apiUrl) + + template_vars = {} + + try: + # make the api call + request = requests.get(apiUrl) + data = request.json() + + # check if there is data returned + if 'query' in data : + + for idx , page in data['query']['pages'].items(): + # get the 0 indexd page (first page data) + if page['index'] == 0: + return page['extract'] + + else: + # if there is no data returned show user + logging.error(e) + except requests.exceptions.RequestException as e: + logging.error(e) + + return None + +def CityWeatherTemp(lat , lon): + API_HOST = "http://api.openweathermap.org" + API_KEY = os.environ.get("HTTP_EXAMPLE_API_KEY") + + if API_KEY: + try: + apiUrl = "%s/data/2.5/forecast?appid=%s&mode=json&units=metric&lat=%s&lon=%s" % (API_HOST, API_KEY, lat, lon) + request = requests.get(apiUrl) + data = request.json() + return data['list'][0]['main']['temp'] + except requests.exceptions.RequestException as e: + logging.error(e) + + return None + +def StoreCitiesInfo(cities): + for city in cities: + entity_key = ndb.Key('CityInfo', city['Location']) + entity = entity_key.get() + + if entity is None: + entity = CityInfo( + Location=city['Location'], + Info=city['Info'], + Temp=city['Temp'] + ) + entity.key = ndb.Key('CityInfo', city['Location']) + entity.put() + + else: + entity.Info = city['Info'] + entity.Temp = city['Temp'] + entity.put() diff --git a/app/modules/cityinfo/city_info.html b/app/modules/cityinfo/city_info.html new file mode 100644 index 0000000..982e730 --- /dev/null +++ b/app/modules/cityinfo/city_info.html @@ -0,0 +1,16 @@ + + +

Cities Information

+
+ {% for city in cities %} +

{{ city.Location }}

+

Temp : {{ city.Temp }}

+

Last Updated : {{ city.LastUpdated }}

+

Info : {{ city.Info }}

+ {% endfor %} + {% if not cities %} + No cities found + {% endif %} +
+ + diff --git a/app/modules/cityinfo/cityinfo.py b/app/modules/cityinfo/cityinfo.py new file mode 100644 index 0000000..266e7cf --- /dev/null +++ b/app/modules/cityinfo/cityinfo.py @@ -0,0 +1,24 @@ +import webapp2 +import logging +import os +from google.appengine.ext.webapp import template +from app.modules.common.city_info import CityInfoRootPipeline +from app.modules.common.kinds import CityInfo + +class CityInfoBuildHandler(webapp2.RequestHandler): + def get(self): + logging.info("CityInfoBuildHandler") + CitiesUpdate = CityInfoRootPipeline() + CitiesUpdate.start() + +class CityInfoViewHandler(webapp2.RequestHandler): + def get(self): + logging.info("CityInfoViewHandler") + + # get cities + cities = CityInfo.query().fetch(20) + + template_path = os.path.join(os.path.dirname(__file__), 'city_info.html') + self.response.write(template.render(template_path, { + 'cities': cities, + })) diff --git a/app/modules/cityinfo/cityinfo.yaml b/app/modules/cityinfo/cityinfo.yaml new file mode 100644 index 0000000..5ce7847 --- /dev/null +++ b/app/modules/cityinfo/cityinfo.yaml @@ -0,0 +1,18 @@ +cities: + - + name: London + lat: 51.507222 + lon: -0.1275 + - + name: Cairo + lat: 30.044444 + lon: 31.235833 + - + name: Beijing + lat: 39.916667 + lon: 116.383333 + + - + name: Paris + lat: 48.8588376 + lon: 2.2768489 diff --git a/app/modules/common/city_info.py b/app/modules/common/city_info.py new file mode 100644 index 0000000..1809aee --- /dev/null +++ b/app/modules/common/city_info.py @@ -0,0 +1,56 @@ +import webapp2 +import logging +import pipeline +import app.modules.cityinfo.city_extras as CityExtras +import app.modules.common.util as Utils + +class CityInfoRootPipeline(pipeline.Pipeline): + + def run(self): + logging.info("CityInfoRootPipeline") + + # Read cityinfo.yaml file + data = Utils.ReadYamlFile('../cityinfo/cityinfo.yaml') + cities = [] + + for city in data['cities'] : + cityInfo = yield CityInfoFetchPipeline(city) + cities.append(cityInfo) + + yield CityInfoPersistPipeline(*cities) + +class CityInfoFetchPipeline(pipeline.Pipeline): + + def run(self, city): + logging.info("CityInfoFetchPipeline") + + cityinfo = yield CityInfoInfoPipeline(city['lat'] , city['lon'] ) + citytemp = yield CityInfoWeatherPipeline(city['lat'] , city['lon']) + + yield CityInfoReturn(city['name'], cityinfo,citytemp) + +class CityInfoInfoPipeline(pipeline.Pipeline): + + def run(self, lat , lon): + logging.info("CityInfoInfoPipeline") + return CityExtras.CityWikiInfo(lat , lon) + +class CityInfoWeatherPipeline(pipeline.Pipeline): + + def run(self , lat , lon): + logging.info("CityInfoWeatherPipeline") + return CityExtras.CityWeatherTemp(lat , lon) + +class CityInfoPersistPipeline(pipeline.Pipeline): + + def run(self, *args): + logging.info("CityInfoPersistPipeline") + + CityExtras.StoreCitiesInfo(args) + +class CityInfoReturn(pipeline.Pipeline): + + def run(self, cityname,cityInfo , cityTemp): + logging.info("CityInfoReturn") + + return {'Location' : cityname ,'Info' : cityInfo , 'Temp' : cityTemp } diff --git a/app/modules/common/kinds.py b/app/modules/common/kinds.py new file mode 100644 index 0000000..6b6e3eb --- /dev/null +++ b/app/modules/common/kinds.py @@ -0,0 +1,12 @@ +import webapp2 +import logging +from google.appengine.ext import ndb + +class Example(ndb.Model): + value = ndb.StringProperty() + +class CityInfo(ndb.Model): + Location = ndb.StringProperty() + Info = ndb.TextProperty() + Temp = ndb.FloatProperty() + LastUpdated = ndb.DateTimeProperty(auto_now=True) \ No newline at end of file diff --git a/app/modules/common/squares.py b/app/modules/common/squares.py index 96de852..fe63051 100755 --- a/app/modules/common/squares.py +++ b/app/modules/common/squares.py @@ -33,4 +33,5 @@ def run(self, number): class LogResult(pipeline.Pipeline): def run(self, number): - logging.info('All done! Value is %s', number) \ No newline at end of file + logging.info('All done! Value is %s', number) + diff --git a/app/modules/common/util.py b/app/modules/common/util.py new file mode 100644 index 0000000..c2d6fa9 --- /dev/null +++ b/app/modules/common/util.py @@ -0,0 +1,10 @@ +import os +import yaml + +def ReadYamlFile(fileLocation): + data = {} + yaml_path = os.path.join(os.path.dirname(__file__), fileLocation) + with open(yaml_path, 'r') as stream: + data = yaml.load(stream) + + return data \ No newline at end of file diff --git a/app/modules/home.html b/app/modules/home.html index a341e1f..47b8f02 100755 --- a/app/modules/home.html +++ b/app/modules/home.html @@ -9,7 +9,9 @@

Home

  • Datastore example (load)
  • Yaml example
  • Pipeline training
  • -
  • Http training
  • +
  • Http training
  • +
  • Datastore training
  • +
  • City info view
  • Logged in as : {{user_name}}

    diff --git a/app/modules/training/datastore_training.html b/app/modules/training/datastore_training.html new file mode 100644 index 0000000..597cf8f --- /dev/null +++ b/app/modules/training/datastore_training.html @@ -0,0 +1,11 @@ + + +

    Datastore Training

    +
    + {% for entity in entities %} +

    {{ entity.key }}

    +

    {{ entity.value }}

    + {% endfor %} +
    + + diff --git a/app/modules/training/datastore_training.py b/app/modules/training/datastore_training.py new file mode 100644 index 0000000..1d2001e --- /dev/null +++ b/app/modules/training/datastore_training.py @@ -0,0 +1,24 @@ +import webapp2 +import logging +import os +from google.appengine.ext import ndb +from google.appengine.ext.webapp import template +from app.modules.common.kinds import Example + +class DatastoreTrainingHandler(webapp2.RequestHandler): + def get(self): + logging.info("DatastoreTrainingHandler") + + # get entities + entities_for_example_kind = Example.query().fetch(20) + + # make a list of key value pairs + entities = [] + for entity in entities_for_example_kind: + entities.append({'key':entity.key.string_id() , 'value':entity.value}) + + template_path = os.path.join(os.path.dirname(__file__), 'datastore_training.html') + self.response.write(template.render(template_path, { + 'entities': entities, + })) + diff --git a/app/modules/training/http_training.py b/app/modules/training/http_training.py index f16e826..d2eeec8 100644 --- a/app/modules/training/http_training.py +++ b/app/modules/training/http_training.py @@ -14,7 +14,7 @@ def get(self, lat = None, lon = None): # wikipedia base api url API_HOST = "https://en.wikipedia.org/w/api.php?" - + # add given lat long to api url apiUrl = "%sformat=json&action=query&prop=extracts&exintro=1&explaintext=1&exlimit=20&generator=geosearch&ggsradius=10000&ggslimit=100&ggscoord=%s%s%s" % (API_HOST, lat,"|", lon) diff --git a/cron.yaml b/cron.yaml new file mode 100644 index 0000000..1ca68f5 --- /dev/null +++ b/cron.yaml @@ -0,0 +1,4 @@ +cron: +- description: "City info build hourly" + url: /cityinfo/build + schedule: every 1 hours \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index fe1347a..371f95a 100755 --- a/deploy.sh +++ b/deploy.sh @@ -16,5 +16,5 @@ if [ -z "$APPENGINE_PROJECT_ID" ] then echo "You must obtain an AppEngine PROJECT_ID [https://console.cloud.google.com/appengine] and place it in .env before running this!" else - gcloud app deploy app-deploy.yaml --project $APPENGINE_PROJECT_ID --version 1 --promote + gcloud app deploy app-deploy.yaml cron.yaml --project $APPENGINE_PROJECT_ID --version 1 --promote fi