From ed296f55009f9cf932fb06e443561d10d3ce5663 Mon Sep 17 00:00:00 2001 From: Jon Wayne Parrott Date: Mon, 6 Jun 2016 11:16:15 -0700 Subject: [PATCH] Adding GAE standard analytics sample. Change-Id: I6aa41df0e3b4bff39dc6c9a4de212c1acaa985d3 --- appengine/flexible/analytics/main.py | 4 +- appengine/standard/analytics/app.yaml | 12 +++ .../standard/analytics/appengine_config.py | 18 +++++ appengine/standard/analytics/main.py | 77 +++++++++++++++++++ appengine/standard/analytics/main_test.py | 47 +++++++++++ appengine/standard/analytics/requirements.txt | 3 + 6 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 appengine/standard/analytics/app.yaml create mode 100644 appengine/standard/analytics/appengine_config.py create mode 100644 appengine/standard/analytics/main.py create mode 100644 appengine/standard/analytics/main_test.py create mode 100644 appengine/standard/analytics/requirements.txt diff --git a/appengine/flexible/analytics/main.py b/appengine/flexible/analytics/main.py index 7af0be94105..1c9fb9a7168 100644 --- a/appengine/flexible/analytics/main.py +++ b/appengine/flexible/analytics/main.py @@ -27,7 +27,7 @@ GA_TRACKING_ID = os.environ['GA_TRACKING_ID'] -def track_event(category, action, label=None, value=None): +def track_event(category, action, label=None, value=0): data = { 'v': '1', # API Version. 'tid': GA_TRACKING_ID, # Tracking ID / Property ID. @@ -38,7 +38,7 @@ def track_event(category, action, label=None, value=None): 'ec': category, # Event category. 'ea': action, # Event action. 'el': label, # Event label. - 'ev': value, # Event valueself. + 'ev': value, # Event value, must be an integer } response = requests.post( diff --git a/appengine/standard/analytics/app.yaml b/appengine/standard/analytics/app.yaml new file mode 100644 index 00000000000..212e1aac4a8 --- /dev/null +++ b/appengine/standard/analytics/app.yaml @@ -0,0 +1,12 @@ +runtime: python27 +api_version: 1 +threadsafe: yes + +handlers: +- url: .* + script: main.app + +#[START env] +env_variables: + GA_TRACKING_ID: your-tracking-id +#[END env] diff --git a/appengine/standard/analytics/appengine_config.py b/appengine/standard/analytics/appengine_config.py new file mode 100644 index 00000000000..c903d9a0ac5 --- /dev/null +++ b/appengine/standard/analytics/appengine_config.py @@ -0,0 +1,18 @@ +# Copyright 2016 Google Inc. +# +# 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. + +from google.appengine.ext import vendor + +# Add any libraries installed in the "lib" folder. +vendor.add('lib') diff --git a/appengine/standard/analytics/main.py b/appengine/standard/analytics/main.py new file mode 100644 index 00000000000..dbed63a2977 --- /dev/null +++ b/appengine/standard/analytics/main.py @@ -0,0 +1,77 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# 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. + +import logging +import os + +from flask import Flask +import requests +import requests_toolbelt.adapters.appengine + +# Use the App Engine Requests adapter. This makes sure that Requests uses +# URLFetch. +requests_toolbelt.adapters.appengine.monkeypatch() + +app = Flask(__name__) + +# Environment variables are defined in app.yaml. +GA_TRACKING_ID = os.environ['GA_TRACKING_ID'] + + +# [START track_event] +def track_event(category, action, label=None, value=0): + data = { + 'v': '1', # API Version. + 'tid': GA_TRACKING_ID, # Tracking ID / Property ID. + # Anonymous Client Identifier. Ideally, this should be a UUID that + # is associated with particular user, device, or browser instance. + 'cid': '555', + 't': 'event', # Event hit type. + 'ec': category, # Event category. + 'ea': action, # Event action. + 'el': label, # Event label. + 'ev': value, # Event value, must be an integer + } + + response = requests.post( + 'http://www.google-analytics.com/collect', data=data) + + # If the request fails, this will raise a RequestException. Depending + # on your application's needs, this may be a non-error and can be caught + # by the caller. + response.raise_for_status() + + +@app.route('/') +def track_example(): + track_event( + category='Example', + action='test action') + return 'Event tracked.' +# [STOP track_event] + + +@app.errorhandler(500) +def server_error(e): + logging.exception('An error occurred during a request.') + return """ + An internal error occurred:
{}
+ See logs for full stacktrace. + """.format(e), 500 + + +if __name__ == '__main__': + # This is used when running locally. Gunicorn is used to run the + # application on Google App Engine. See entrypoint in app.yaml. + app.run(host='127.0.0.1', port=8080, debug=True) diff --git a/appengine/standard/analytics/main_test.py b/appengine/standard/analytics/main_test.py new file mode 100644 index 00000000000..db9d2a93691 --- /dev/null +++ b/appengine/standard/analytics/main_test.py @@ -0,0 +1,47 @@ +# Copyright 2016 Google Inc. All Rights Reserved. +# +# 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. + +import re + +import pytest +import responses + + +@pytest.fixture +def app(monkeypatch, testbed): + monkeypatch.setenv('GA_TRACKING_ID', '1234') + + import main + + main.app.testing = True + return main.app.test_client() + + +@responses.activate +def test_tracking(app): + responses.add( + responses.POST, + re.compile(r'.*'), + body='{}', + content_type='application/json') + + r = app.get('/') + + assert r.status_code == 200 + assert 'Event tracked' in r.data.decode('utf-8') + + assert len(responses.calls) == 1 + request_body = responses.calls[0].request.body + assert 'tid=1234' in request_body + assert 'ea=test+action' in request_body diff --git a/appengine/standard/analytics/requirements.txt b/appengine/standard/analytics/requirements.txt new file mode 100644 index 00000000000..74ecb786dda --- /dev/null +++ b/appengine/standard/analytics/requirements.txt @@ -0,0 +1,3 @@ +Flask==0.10.1 +requests==2.10.0 +requests-toolbelt==0.6.2