Compare commits
10 Commits
7859e01b12
...
272f2c6add
Author | SHA1 | Date |
---|---|---|
Francesco Mangiacrapa | 272f2c6add | |
Sergey Motornyuk | b30f5c10c1 | |
Sergey Motornyuk | 650dbe218d | |
Sergey Motornyuk | e80e3a310b | |
Sergey Motornyuk | 154d8a64b3 | |
Sergey Motornyuk | af8380070a | |
Sergey Motornyuk | 05c153dc77 | |
Sergey | a11d2b0d5c | |
Mark Calvert | 2c0b03f8d7 | |
Mark Calvert | 8a5c9c2cbf |
|
@ -1,4 +1,4 @@
|
||||||
include README.rst
|
include README.rst
|
||||||
include LICENSE.txt
|
include LICENSE.txt
|
||||||
include requirements.txt
|
include requirements.txt
|
||||||
recursive-include ckanext/googleanalytics *.html *.js
|
recursive-include ckanext/googleanalytics *.html *.js *.json *.css *.yml *.mo
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
CKAN Google Analytics Extension
|
# CKAN Google Analytics Extension
|
||||||
===============================
|
|
||||||
|
|
||||||
**Status:** Production
|
**Status:** Production
|
||||||
|
|
||||||
|
@ -8,8 +7,8 @@ CKAN Google Analytics Extension
|
||||||
A CKAN extension that both sends tracking data to Google Analytics and
|
A CKAN extension that both sends tracking data to Google Analytics and
|
||||||
retrieves statistics from Google Analytics and inserts them into CKAN pages.
|
retrieves statistics from Google Analytics and inserts them into CKAN pages.
|
||||||
|
|
||||||
Features
|
## Features
|
||||||
--------
|
|
||||||
|
|
||||||
* Puts the Google Analytics asynchronous tracking code into your page headers
|
* Puts the Google Analytics asynchronous tracking code into your page headers
|
||||||
for basic Google Analytics page tracking.
|
for basic Google Analytics page tracking.
|
||||||
|
@ -24,26 +23,22 @@ Features
|
||||||
* Add Google Analytics Event Tracking function that can be used in any exstension
|
* Add Google Analytics Event Tracking function that can be used in any exstension
|
||||||
to create your custom events tracking.
|
to create your custom events tracking.
|
||||||
|
|
||||||
``ckanext.googleanalytics.plugin._post_analytics``
|
ckanext.googleanalytics.plugin._post_analytics
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
Installation
|
|
||||||
------------
|
|
||||||
|
|
||||||
1. Install the extension as usual, e.g. (from an activated virtualenv):
|
1. Install the extension as usual, e.g. (from an activated virtualenv):
|
||||||
|
|
||||||
::
|
$ pip install -e git+https://github.com/ckan/ckanext-googleanalytics.git#egg=ckanext-googleanalytics
|
||||||
|
$ pip install -r ckanext-googleanalytics/requirements.txt
|
||||||
$ pip install -e git+https://github.com/ckan/ckanext-googleanalytics.git#egg=ckanext-googleanalytics
|
|
||||||
$ pip install -r ckanext-googleanalytics/requirements.txt
|
|
||||||
|
|
||||||
2. Edit your development.ini (or similar) to provide these necessary parameters:
|
2. Edit your development.ini (or similar) to provide these necessary parameters:
|
||||||
|
|
||||||
::
|
googleanalytics.id = UA-1010101-1
|
||||||
|
googleanalytics.account = Account name (i.e. data.gov.uk, see top level item at https://www.google.com/analytics)
|
||||||
googleanalytics.id = UA-1010101-1
|
googleanalytics.username = googleaccount@gmail.com
|
||||||
googleanalytics.account = Account name (i.e. data.gov.uk, see top level item at https://www.google.com/analytics)
|
googleanalytics.password = googlepassword
|
||||||
googleanalytics.username = googleaccount@gmail.com
|
|
||||||
googleanalytics.password = googlepassword
|
|
||||||
|
|
||||||
Note that your password will probably be readable by other people;
|
Note that your password will probably be readable by other people;
|
||||||
so you may want to set up a new gmail account specifically for
|
so you may want to set up a new gmail account specifically for
|
||||||
|
@ -52,22 +47,20 @@ Installation
|
||||||
3. Edit again your configuration ini file to activate the plugin
|
3. Edit again your configuration ini file to activate the plugin
|
||||||
with:
|
with:
|
||||||
|
|
||||||
::
|
ckan.plugins = googleanalytics
|
||||||
|
|
||||||
ckan.plugins = googleanalytics
|
|
||||||
|
|
||||||
(If there are other plugins activated, add this to the list. Each
|
(If there are other plugins activated, add this to the list. Each
|
||||||
plugin should be separated with a space).
|
plugin should be separated with a space).
|
||||||
|
|
||||||
4. Finally, there are some optional configuration settings (shown here
|
4. Finally, there are some optional configuration settings (shown here
|
||||||
with their default settings)::
|
with their default settings)
|
||||||
|
|
||||||
googleanalytics_resource_prefix = /downloads/
|
googleanalytics_resource_prefix = /downloads/
|
||||||
googleanalytics.domain = auto
|
googleanalytics.domain = auto
|
||||||
googleanalytics.track_events = false
|
googleanalytics.track_events = false
|
||||||
googleanalytics.fields = {}
|
googleanalytics.fields = {}
|
||||||
googleanalytics.enable_user_id = false
|
googleanalytics.enable_user_id = false
|
||||||
googleanalytics.download_handler = ckan.views.resource:download
|
googleanalytics.download_handler = ckan.views.resource:download
|
||||||
|
|
||||||
``resource_prefix`` is an arbitrary identifier so that we can query
|
``resource_prefix`` is an arbitrary identifier so that we can query
|
||||||
for downloads in Google Analytics. It can theoretically be any
|
for downloads in Google Analytics. It can theoretically be any
|
||||||
|
@ -99,12 +92,12 @@ Installation
|
||||||
and some of your plugins redefines `resource.download`
|
and some of your plugins redefines `resource.download`
|
||||||
route(ckanext-cloudstorage, for example), you can specify which
|
route(ckanext-cloudstorage, for example), you can specify which
|
||||||
function must be called instead of `ckan.views.resource:download`
|
function must be called instead of `ckan.views.resource:download`
|
||||||
via `ckanext.googleanalytics.download_handler` config variable. For ckanext-cloudstorage you can use::
|
via `ckanext.googleanalytics.download_handler` config variable. For ckanext-cloudstorage you can use:
|
||||||
|
|
||||||
ckanext.googleanalytics.download_handler = ckanext.cloudstorage.views:download
|
ckanext.googleanalytics.download_handler = ckanext.cloudstorage.views:download
|
||||||
|
|
||||||
|
# Domain Linking
|
||||||
|
|
||||||
Domain Linking
|
|
||||||
--------------
|
|
||||||
|
|
||||||
This plugin supports cross-domain tracking using Googles' site linking feature.
|
This plugin supports cross-domain tracking using Googles' site linking feature.
|
||||||
|
|
||||||
|
@ -112,18 +105,18 @@ To use this, set the ``googleanalytics.linked_domains`` configuration option to
|
||||||
|
|
||||||
See `Googles' documentation<https://support.google.com/analytics/answer/1034342?hl=en>`_ for more information
|
See `Googles' documentation<https://support.google.com/analytics/answer/1034342?hl=en>`_ for more information
|
||||||
|
|
||||||
Setting Up Statistics Retrieval from Google Analytics
|
# Setting Up Statistics Retrieval from Google Analytics
|
||||||
-----------------------------------------------------
|
|
||||||
|
|
||||||
1. Run the following command from ``src/ckanext-googleanalytics`` to
|
1. Run the following command from ``src/ckanext-googleanalytics`` to
|
||||||
set up the required database tables (of course, altering the
|
set up the required database tables (of course, altering the
|
||||||
``--config`` option to point to your site config file)::
|
``--config`` option to point to your site config file):
|
||||||
|
|
||||||
paster initdb --config=../ckan/development.ini
|
paster initdb --config=../ckan/development.ini
|
||||||
|
|
||||||
2. Optionally, add::
|
2. Optionally, add::
|
||||||
|
|
||||||
googleanalytics.show_downloads = true
|
googleanalytics.show_downloads = true
|
||||||
|
|
||||||
to your CKAN ini file. If ``show_downloads`` is set, a download count for
|
to your CKAN ini file. If ``show_downloads`` is set, a download count for
|
||||||
resources will be displayed on individual package pages.
|
resources will be displayed on individual package pages.
|
||||||
|
@ -137,7 +130,7 @@ Setting Up Statistics Retrieval from Google Analytics
|
||||||
6. Import Google stats by running the following command from
|
6. Import Google stats by running the following command from
|
||||||
``src/ckanext-googleanalytics``::
|
``src/ckanext-googleanalytics``::
|
||||||
|
|
||||||
paster loadanalytics credentials.json --config=../ckan/development.ini
|
paster loadanalytics credentials.json --config=../ckan/development.ini
|
||||||
|
|
||||||
(Of course, pointing config at your specific site config and credentials.json at the
|
(Of course, pointing config at your specific site config and credentials.json at the
|
||||||
key file obtained from the authorization step)
|
key file obtained from the authorization step)
|
||||||
|
@ -157,8 +150,8 @@ Setting Up Statistics Retrieval from Google Analytics
|
||||||
remember to run it by hand, or your statistics won't get updated.
|
remember to run it by hand, or your statistics won't get updated.
|
||||||
|
|
||||||
|
|
||||||
Authorization
|
## Authorization
|
||||||
--------------
|
|
||||||
|
|
||||||
Before ckanext-googleanalytics can retrieve statistics from Google Analytics, you need to set up the OAUTH details which you can do by following the `instructions <https://developers.google.com/analytics/devguides/reporting/core/v3/quickstart/service-py>`_ the outcome of which will be a file with authentication key. These steps are below for convenience:
|
Before ckanext-googleanalytics can retrieve statistics from Google Analytics, you need to set up the OAUTH details which you can do by following the `instructions <https://developers.google.com/analytics/devguides/reporting/core/v3/quickstart/service-py>`_ the outcome of which will be a file with authentication key. These steps are below for convenience:
|
||||||
|
|
||||||
|
@ -175,17 +168,16 @@ Before ckanext-googleanalytics can retrieve statistics from Google Analytics, yo
|
||||||
6. Find "User management" button in corresponding column. Add service account using Service account ID(email) generated in 3rd step and grant "Read" role to it.
|
6. Find "User management" button in corresponding column. Add service account using Service account ID(email) generated in 3rd step and grant "Read" role to it.
|
||||||
|
|
||||||
|
|
||||||
Testing
|
## Testing
|
||||||
-------
|
|
||||||
|
|
||||||
There are some very high-level functional tests that you can run using::
|
There are some very high-level functional tests that you can run using::
|
||||||
|
|
||||||
(pyenv)~/pyenv/src/ckan$ nosetests --ckan ../ckanext-googleanalytics/tests/
|
(pyenv)~/pyenv/src/ckan$ nosetests --ckan ../ckanext-googleanalytics/tests/
|
||||||
|
|
||||||
(note -- that's run from the CKAN software root, not the extension root)
|
(note -- that's run from the CKAN software root, not the extension root)
|
||||||
|
|
||||||
Future
|
## Future
|
||||||
------
|
|
||||||
|
|
||||||
This is a bare-bones, first release of the software. There are
|
This is a bare-bones, first release of the software. There are
|
||||||
several directions it could take in the future.
|
several directions it could take in the future.
|
|
@ -1,5 +1,5 @@
|
||||||
// Add Google Analytics Event Tracking to resource download links.
|
// Add Google Analytics Event Tracking to resource download links.
|
||||||
this.ckan.module("google-analytics", function(jQuery, _) {
|
ckan.module("google-analytics", function(jQuery, _) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return {
|
return {
|
||||||
options: {
|
options: {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
event_tracking:
|
event_tracking:
|
||||||
filters: rjsmin
|
filters: rjsmin
|
||||||
output: ckanext-googleanalytics/event_tracking.js
|
output: ckanext-googleanalytics/event_tracking.js
|
||||||
|
extra:
|
||||||
|
preload:
|
||||||
|
- base/main
|
||||||
contents:
|
contents:
|
||||||
- googleanalytics_event_tracking.js
|
- googleanalytics_event_tracking.js
|
||||||
|
|
|
@ -15,25 +15,24 @@ import ckan.plugins.toolkit as tk
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
PACKAGE_URL = "/dataset/" # XXX get from routes...
|
PACKAGE_URL = "/dataset/" # XXX get from routes...
|
||||||
DEFAULT_RESOURCE_URL_TAG = "/downloads/"
|
DEFAULT_RESOURCE_URL_TAG = "/downloads/"
|
||||||
|
DEFAULT_RECENT_VIEW_DAYS = 14
|
||||||
|
|
||||||
RESOURCE_URL_REGEX = re.compile("/dataset/[a-z0-9-_]+/resource/([a-z0-9-_]+)")
|
RESOURCE_URL_REGEX = re.compile("/dataset/[a-z0-9-_]+/resource/([a-z0-9-_]+)")
|
||||||
DATASET_EDIT_REGEX = re.compile("/dataset/edit/([a-z0-9-_]+)")
|
DATASET_EDIT_REGEX = re.compile("/dataset/edit/([a-z0-9-_]+)")
|
||||||
|
|
||||||
|
|
||||||
def get_commands():
|
def get_commands():
|
||||||
return [
|
return [googleanalytics]
|
||||||
googleanalytics
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
@click.group(short_help=u"GoogleAnalytics commands")
|
@click.group(short_help=u"GoogleAnalytics commands")
|
||||||
def googleanalytics():
|
def googleanalytics():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@googleanalytics.command()
|
@googleanalytics.command()
|
||||||
def init():
|
def init():
|
||||||
"""Initialise the local stats database tables
|
"""Initialise the local stats database tables"""
|
||||||
"""
|
|
||||||
model.Session.remove()
|
model.Session.remove()
|
||||||
model.Session.configure(bind=model.meta.engine)
|
model.Session.configure(bind=model.meta.engine)
|
||||||
dbutil.init_tables()
|
dbutil.init_tables()
|
||||||
|
@ -73,6 +72,14 @@ def _resource_url_tag():
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _recent_view_days():
|
||||||
|
return tk.asint(
|
||||||
|
tk.config.get(
|
||||||
|
"googleanalytics.recent_view_days", DEFAULT_RECENT_VIEW_DAYS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# xxx #
|
# xxx #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
@ -129,10 +136,10 @@ def internal_save(packages_data, summary_date):
|
||||||
SELECT sum(count)
|
SELECT sum(count)
|
||||||
FROM tracking_summary t2
|
FROM tracking_summary t2
|
||||||
WHERE t1.url = t2.url
|
WHERE t1.url = t2.url
|
||||||
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - 14
|
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - %s
|
||||||
) + t1.count
|
) + t1.count
|
||||||
WHERE t1.running_total = 0 AND tracking_type = 'resource';"""
|
WHERE t1.running_total = 0 AND tracking_type = 'resource';"""
|
||||||
engine.execute(sql)
|
engine.execute(sql, _recent_view_days())
|
||||||
|
|
||||||
# update summary totals for pages
|
# update summary totals for pages
|
||||||
sql = """UPDATE tracking_summary t1
|
sql = """UPDATE tracking_summary t1
|
||||||
|
@ -146,12 +153,12 @@ def internal_save(packages_data, summary_date):
|
||||||
SELECT sum(count)
|
SELECT sum(count)
|
||||||
FROM tracking_summary t2
|
FROM tracking_summary t2
|
||||||
WHERE t1.package_id = t2.package_id
|
WHERE t1.package_id = t2.package_id
|
||||||
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - 14
|
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - %s
|
||||||
) + t1.count
|
) + t1.count
|
||||||
WHERE t1.running_total = 0 AND tracking_type = 'page'
|
WHERE t1.running_total = 0 AND tracking_type = 'page'
|
||||||
AND t1.package_id IS NOT NULL
|
AND t1.package_id IS NOT NULL
|
||||||
AND t1.package_id != '~~not~found~~';"""
|
AND t1.package_id != '~~not~found~~';"""
|
||||||
engine.execute(sql)
|
engine.execute(sql, _recent_view_days())
|
||||||
|
|
||||||
|
|
||||||
def bulk_import(service, profile_id, start_date=None):
|
def bulk_import(service, profile_id, start_date=None):
|
||||||
|
@ -246,8 +253,7 @@ def get_ga_data_new(service, profile_id, start_date=None, end_date=None):
|
||||||
|
|
||||||
|
|
||||||
def save_ga_data(packages_data):
|
def save_ga_data(packages_data):
|
||||||
"""Save tuples of packages_data to the database
|
"""Save tuples of packages_data to the database"""
|
||||||
"""
|
|
||||||
for identifier, visits in list(packages_data.items()):
|
for identifier, visits in list(packages_data.items()):
|
||||||
recently = visits.get("recent", 0)
|
recently = visits.get("recent", 0)
|
||||||
ever = visits.get("ever", 0)
|
ever = visits.get("ever", 0)
|
||||||
|
@ -280,10 +286,13 @@ def save_ga_data(packages_data):
|
||||||
|
|
||||||
|
|
||||||
def ga_query(
|
def ga_query(
|
||||||
service, profile_id, query_filter=None, from_date=None, metrics=None,
|
service,
|
||||||
|
profile_id,
|
||||||
|
query_filter=None,
|
||||||
|
from_date=None,
|
||||||
|
metrics=None,
|
||||||
):
|
):
|
||||||
"""Execute a query against Google Analytics
|
"""Execute a query against Google Analytics"""
|
||||||
"""
|
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
to_date = now.strftime("%Y-%m-%d")
|
to_date = now.strftime("%Y-%m-%d")
|
||||||
if isinstance(from_date, datetime.date):
|
if isinstance(from_date, datetime.date):
|
||||||
|
@ -322,7 +331,7 @@ def get_ga_data(service, profile_id, query_filter):
|
||||||
{'identifier': {'recent':3, 'ever':6}}
|
{'identifier': {'recent':3, 'ever':6}}
|
||||||
"""
|
"""
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
recent_date = now - datetime.timedelta(14)
|
recent_date = now - datetime.timedelta(_recent_view_days())
|
||||||
recent_date = recent_date.strftime("%Y-%m-%d")
|
recent_date = recent_date.strftime("%Y-%m-%d")
|
||||||
floor_date = datetime.date(2005, 1, 1)
|
floor_date = datetime.date(2005, 1, 1)
|
||||||
packages = {}
|
packages = {}
|
||||||
|
|
|
@ -9,20 +9,21 @@ import time
|
||||||
from pylons import config as pylonsconfig
|
from pylons import config as pylonsconfig
|
||||||
from ckan.lib.cli import CkanCommand
|
from ckan.lib.cli import CkanCommand
|
||||||
import ckan.model as model
|
import ckan.model as model
|
||||||
|
from ckan.plugins.toolkit import asint
|
||||||
|
|
||||||
from . import dbutil
|
from . import dbutil
|
||||||
|
|
||||||
log = logging.getLogger("ckanext.googleanalytics")
|
log = logging.getLogger("ckanext.googleanalytics")
|
||||||
PACKAGE_URL = "/dataset/" # XXX get from routes...
|
PACKAGE_URL = "/dataset/" # XXX get from routes...
|
||||||
DEFAULT_RESOURCE_URL_TAG = "/downloads/"
|
DEFAULT_RESOURCE_URL_TAG = "/downloads/"
|
||||||
|
DEFAULT_RECENT_VIEW_DAYS = 14
|
||||||
|
|
||||||
RESOURCE_URL_REGEX = re.compile("/dataset/[a-z0-9-_]+/resource/([a-z0-9-_]+)")
|
RESOURCE_URL_REGEX = re.compile("/dataset/[a-z0-9-_]+/resource/([a-z0-9-_]+)")
|
||||||
DATASET_EDIT_REGEX = re.compile("/dataset/edit/([a-z0-9-_]+)")
|
DATASET_EDIT_REGEX = re.compile("/dataset/edit/([a-z0-9-_]+)")
|
||||||
|
|
||||||
|
|
||||||
class InitDB(CkanCommand):
|
class InitDB(CkanCommand):
|
||||||
"""Initialise the local stats database tables
|
"""Initialise the local stats database tables"""
|
||||||
"""
|
|
||||||
|
|
||||||
summary = __doc__.split("\n")[0]
|
summary = __doc__.split("\n")[0]
|
||||||
usage = __doc__
|
usage = __doc__
|
||||||
|
@ -63,6 +64,11 @@ class LoadAnalytics(CkanCommand):
|
||||||
self.resource_url_tag = self.CONFIG.get(
|
self.resource_url_tag = self.CONFIG.get(
|
||||||
"googleanalytics_resource_prefix", DEFAULT_RESOURCE_URL_TAG
|
"googleanalytics_resource_prefix", DEFAULT_RESOURCE_URL_TAG
|
||||||
)
|
)
|
||||||
|
self.recent_view_days = asint(
|
||||||
|
self.CONFIG.get(
|
||||||
|
"googleanalytics.recent_view_days", DEFAULT_RECENT_VIEW_DAYS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# funny dance we need to do to make sure we've got a
|
# funny dance we need to do to make sure we've got a
|
||||||
# configured session
|
# configured session
|
||||||
|
@ -121,10 +127,10 @@ class LoadAnalytics(CkanCommand):
|
||||||
SELECT sum(count)
|
SELECT sum(count)
|
||||||
FROM tracking_summary t2
|
FROM tracking_summary t2
|
||||||
WHERE t1.url = t2.url
|
WHERE t1.url = t2.url
|
||||||
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - 14
|
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - %s
|
||||||
) + t1.count
|
) + t1.count
|
||||||
WHERE t1.running_total = 0 AND tracking_type = 'resource';"""
|
WHERE t1.running_total = 0 AND tracking_type = 'resource';"""
|
||||||
engine.execute(sql)
|
engine.execute(sql, self.recent_view_days)
|
||||||
|
|
||||||
# update summary totals for pages
|
# update summary totals for pages
|
||||||
sql = """UPDATE tracking_summary t1
|
sql = """UPDATE tracking_summary t1
|
||||||
|
@ -138,12 +144,12 @@ class LoadAnalytics(CkanCommand):
|
||||||
SELECT sum(count)
|
SELECT sum(count)
|
||||||
FROM tracking_summary t2
|
FROM tracking_summary t2
|
||||||
WHERE t1.package_id = t2.package_id
|
WHERE t1.package_id = t2.package_id
|
||||||
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - 14
|
AND t2.tracking_date <= t1.tracking_date AND t2.tracking_date >= t1.tracking_date - %s
|
||||||
) + t1.count
|
) + t1.count
|
||||||
WHERE t1.running_total = 0 AND tracking_type = 'page'
|
WHERE t1.running_total = 0 AND tracking_type = 'page'
|
||||||
AND t1.package_id IS NOT NULL
|
AND t1.package_id IS NOT NULL
|
||||||
AND t1.package_id != '~~not~found~~';"""
|
AND t1.package_id != '~~not~found~~';"""
|
||||||
engine.execute(sql)
|
engine.execute(sql, self.recent_view_days)
|
||||||
|
|
||||||
def bulk_import(self):
|
def bulk_import(self):
|
||||||
if len(self.args) == 3:
|
if len(self.args) == 3:
|
||||||
|
@ -262,8 +268,7 @@ class LoadAnalytics(CkanCommand):
|
||||||
log.info("Saved %s records from google" % len(packages_data))
|
log.info("Saved %s records from google" % len(packages_data))
|
||||||
|
|
||||||
def save_ga_data(self, packages_data):
|
def save_ga_data(self, packages_data):
|
||||||
"""Save tuples of packages_data to the database
|
"""Save tuples of packages_data to the database"""
|
||||||
"""
|
|
||||||
for identifier, visits in list(packages_data.items()):
|
for identifier, visits in list(packages_data.items()):
|
||||||
recently = visits.get("recent", 0)
|
recently = visits.get("recent", 0)
|
||||||
ever = visits.get("ever", 0)
|
ever = visits.get("ever", 0)
|
||||||
|
@ -304,8 +309,7 @@ class LoadAnalytics(CkanCommand):
|
||||||
metrics=None,
|
metrics=None,
|
||||||
sort=None,
|
sort=None,
|
||||||
):
|
):
|
||||||
"""Execute a query against Google Analytics
|
"""Execute a query against Google Analytics"""
|
||||||
"""
|
|
||||||
if not to_date:
|
if not to_date:
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
to_date = now.strftime("%Y-%m-%d")
|
to_date = now.strftime("%Y-%m-%d")
|
||||||
|
@ -345,7 +349,7 @@ class LoadAnalytics(CkanCommand):
|
||||||
{'identifier': {'recent':3, 'ever':6}}
|
{'identifier': {'recent':3, 'ever':6}}
|
||||||
"""
|
"""
|
||||||
now = datetime.datetime.now()
|
now = datetime.datetime.now()
|
||||||
recent_date = now - datetime.timedelta(14)
|
recent_date = now - datetime.timedelta(self.recent_view_days)
|
||||||
recent_date = recent_date.strftime("%Y-%m-%d")
|
recent_date = recent_date.strftime("%Y-%m-%d")
|
||||||
floor_date = datetime.date(2005, 1, 1)
|
floor_date = datetime.date(2005, 1, 1)
|
||||||
packages = {}
|
packages = {}
|
||||||
|
|
|
@ -15,6 +15,7 @@ from ckan.controllers.api import ApiController
|
||||||
|
|
||||||
from ckan.exceptions import CkanVersionException
|
from ckan.exceptions import CkanVersionException
|
||||||
import ckan.plugins.toolkit as tk
|
import ckan.plugins.toolkit as tk
|
||||||
|
|
||||||
try:
|
try:
|
||||||
tk.requires_ckan_version("2.9")
|
tk.requires_ckan_version("2.9")
|
||||||
except CkanVersionException:
|
except CkanVersionException:
|
||||||
|
|
|
@ -47,7 +47,9 @@ class AnalyticsPostThread(threading.Thread):
|
||||||
log.debug("Sending API event to Google Analytics: " + data)
|
log.debug("Sending API event to Google Analytics: " + data)
|
||||||
# send analytics
|
# send analytics
|
||||||
res = requests.post(
|
res = requests.post(
|
||||||
"http://www.google-analytics.com/collect", data, timeout=10,
|
"http://www.google-analytics.com/collect",
|
||||||
|
data,
|
||||||
|
timeout=10,
|
||||||
)
|
)
|
||||||
# signals to queue job is done
|
# signals to queue job is done
|
||||||
self.queue.task_done()
|
self.queue.task_done()
|
||||||
|
@ -148,6 +150,10 @@ class GoogleAnalyticsPlugin(GAMixinPlugin, p.SingletonPlugin):
|
||||||
"googleanalytics_fields": str(self.googleanalytics_fields),
|
"googleanalytics_fields": str(self.googleanalytics_fields),
|
||||||
"googleanalytics_linked_domains": self.googleanalytics_linked_domains,
|
"googleanalytics_linked_domains": self.googleanalytics_linked_domains,
|
||||||
}
|
}
|
||||||
|
#return p.toolkit.render_snippet(
|
||||||
|
# "googleanalytics/snippets/googleanalytics_header.html", data
|
||||||
|
#)
|
||||||
|
# Updated to GA4 #25196
|
||||||
return p.toolkit.render_snippet(
|
return p.toolkit.render_snippet(
|
||||||
"googleanalytics/snippets/googleanalytics_header.html", data
|
"googleanalytics/snippets/gtag_header.html", data
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import ckan.plugins as plugins
|
||||||
from ckanext.googleanalytics.views import ga
|
from ckanext.googleanalytics.views import ga
|
||||||
from ckanext.googleanalytics.cli import get_commands
|
from ckanext.googleanalytics.cli import get_commands
|
||||||
|
|
||||||
|
|
||||||
class GAMixinPlugin(plugins.SingletonPlugin):
|
class GAMixinPlugin(plugins.SingletonPlugin):
|
||||||
plugins.implements(plugins.IBlueprint)
|
plugins.implements(plugins.IBlueprint)
|
||||||
plugins.implements(plugins.IClick)
|
plugins.implements(plugins.IClick)
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!-- Google tag (gtag.js) -->
|
||||||
|
<script async src="https://www.googletagmanager.com/gtag/js?id={{googleanalytics_id}}"></script>
|
||||||
|
<script>
|
||||||
|
window.dataLayer = window.dataLayer || [];
|
||||||
|
function gtag(){dataLayer.push(arguments);}
|
||||||
|
gtag('js', new Date());
|
||||||
|
|
||||||
|
gtag('config', '{{googleanalytics_id}}');
|
||||||
|
</script>
|
|
@ -14,7 +14,7 @@ import ckan.views.resource as resource
|
||||||
|
|
||||||
from ckan.common import g
|
from ckan.common import g
|
||||||
|
|
||||||
CONFIG_HANDLER_PATH = 'googleanalytics.download_handler'
|
CONFIG_HANDLER_PATH = "googleanalytics.download_handler"
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
ga = Blueprint("google_analytics", "google_analytics")
|
ga = Blueprint("google_analytics", "google_analytics")
|
||||||
|
@ -40,7 +40,9 @@ def action(logic_function, ver=api.API_MAX_VERSION):
|
||||||
|
|
||||||
|
|
||||||
ga.add_url_rule(
|
ga.add_url_rule(
|
||||||
"/api/action/<logic_function>", methods=["GET", "POST"], view_func=action,
|
"/api/action/<logic_function>",
|
||||||
|
methods=["GET", "POST"],
|
||||||
|
view_func=action,
|
||||||
)
|
)
|
||||||
ga.add_url_rule(
|
ga.add_url_rule(
|
||||||
u"/<int(min=3, max={0}):ver>/action/<logic_function>".format(
|
u"/<int(min=3, max={0}):ver>/action/<logic_function>".format(
|
||||||
|
@ -57,11 +59,9 @@ def download(id, resource_id, filename=None, package_type="dataset"):
|
||||||
handler = import_string(handler_path, silent=True)
|
handler = import_string(handler_path, silent=True)
|
||||||
else:
|
else:
|
||||||
handler = None
|
handler = None
|
||||||
log.warning((
|
log.warning(("Missing {} config option.").format(CONFIG_HANDLER_PATH))
|
||||||
'Missing {} config option.'
|
|
||||||
).format(CONFIG_HANDLER_PATH))
|
|
||||||
if not handler:
|
if not handler:
|
||||||
log.debug('Use default CKAN callback for resource.download')
|
log.debug("Use default CKAN callback for resource.download")
|
||||||
handler = resource.download
|
handler = resource.download
|
||||||
_post_analytics(
|
_post_analytics(
|
||||||
g.user,
|
g.user,
|
||||||
|
@ -71,8 +71,11 @@ def download(id, resource_id, filename=None, package_type="dataset"):
|
||||||
resource_id,
|
resource_id,
|
||||||
)
|
)
|
||||||
return handler(
|
return handler(
|
||||||
package_type=package_type, id=id,
|
package_type=package_type,
|
||||||
resource_id=resource_id, filename=filename)
|
id=id,
|
||||||
|
resource_id=resource_id,
|
||||||
|
filename=filename,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
ga.add_url_rule(
|
ga.add_url_rule(
|
||||||
|
|
|
@ -1,3 +1,63 @@
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 79
|
line-length = 79
|
||||||
include = '\.py$'
|
|
||||||
|
[tool.towncrier]
|
||||||
|
issue_format = ""
|
||||||
|
directory = "changes"
|
||||||
|
package = "ckanext.googleanalytics"
|
||||||
|
package_dir = "ckanext"
|
||||||
|
filename = "CHANGELOG.rst"
|
||||||
|
name = "ckanext-googleanalytics"
|
||||||
|
|
||||||
|
[tool.pyright]
|
||||||
|
pythonVersion = "3.8"
|
||||||
|
include = ["ckanext/googleanalytics"]
|
||||||
|
exclude = [
|
||||||
|
"**/test*",
|
||||||
|
"**/migration",
|
||||||
|
]
|
||||||
|
ignore = [
|
||||||
|
"ckan"
|
||||||
|
]
|
||||||
|
strict = []
|
||||||
|
|
||||||
|
strictParameterNoneValue = true # type must be Optional if default value is None
|
||||||
|
|
||||||
|
reportFunctionMemberAccess = true # non-standard member accesses for functions
|
||||||
|
reportMissingImports = true
|
||||||
|
reportMissingModuleSource = true
|
||||||
|
reportMissingTypeStubs = false
|
||||||
|
reportImportCycles = false
|
||||||
|
reportUnusedImport = false
|
||||||
|
reportUnusedClass = true
|
||||||
|
reportUnusedFunction = true
|
||||||
|
reportUnusedVariable = false
|
||||||
|
reportDuplicateImport = true
|
||||||
|
reportOptionalSubscript = true
|
||||||
|
reportOptionalMemberAccess = true
|
||||||
|
reportOptionalCall = true
|
||||||
|
reportOptionalIterable = true
|
||||||
|
reportOptionalContextManager = true
|
||||||
|
reportOptionalOperand = true
|
||||||
|
reportTypedDictNotRequiredAccess = false # We are using Context in a way that conflicts with this check
|
||||||
|
reportConstantRedefinition = false
|
||||||
|
reportIncompatibleMethodOverride = false
|
||||||
|
reportIncompatibleVariableOverride = true
|
||||||
|
reportOverlappingOverload = true
|
||||||
|
reportUntypedFunctionDecorator = false
|
||||||
|
reportUnknownParameterType = false # it creates a lot of noise
|
||||||
|
reportUnknownArgumentType = false
|
||||||
|
reportUnknownLambdaType = false
|
||||||
|
reportMissingTypeArgument = true
|
||||||
|
reportInvalidTypeVarUse = true
|
||||||
|
reportCallInDefaultInitializer = true
|
||||||
|
reportUnknownVariableType = false
|
||||||
|
reportUntypedBaseClass = false # ignore it because we are relying on untyped CKAN
|
||||||
|
reportUnnecessaryIsInstance = true
|
||||||
|
reportUnnecessaryCast = true
|
||||||
|
reportUnnecessaryComparison = true
|
||||||
|
reportAssertAlwaysTrue = true
|
||||||
|
reportSelfClsParameterName = true
|
||||||
|
reportUnusedCallResult = false # allow function calls for side-effect only (like logic.check_acces)
|
||||||
|
|
||||||
|
useLibraryCodeForTypes = true
|
||||||
|
|
10
setup.py
10
setup.py
|
@ -2,7 +2,7 @@ import os
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages
|
||||||
HERE = os.path.dirname(__file__)
|
HERE = os.path.dirname(__file__)
|
||||||
|
|
||||||
version = "2.0.3"
|
version = "2.0.7"
|
||||||
|
|
||||||
extras_require = {}
|
extras_require = {}
|
||||||
_extras_groups = [
|
_extras_groups = [
|
||||||
|
@ -12,12 +12,16 @@ for group, filepath in _extras_groups:
|
||||||
with open(os.path.join(HERE, filepath), 'r') as f:
|
with open(os.path.join(HERE, filepath), 'r') as f:
|
||||||
extras_require[group] = f.readlines()
|
extras_require[group] = f.readlines()
|
||||||
|
|
||||||
|
# Get the long description from the relevant file
|
||||||
|
with open(os.path.join(HERE, 'README.md'), encoding='utf-8') as f:
|
||||||
|
long_description = f.read()
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="ckanext-googleanalytics",
|
name="ckanext-googleanalytics",
|
||||||
version=version,
|
version=version,
|
||||||
description="Add GA tracking and reporting to CKAN instance",
|
description="Add GA tracking and reporting to CKAN instance",
|
||||||
long_description="""\
|
long_description=long_description,
|
||||||
""",
|
long_description_content_type="text/markdown",
|
||||||
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||||
keywords="",
|
keywords="",
|
||||||
author="Seb Bacon",
|
author="Seb Bacon",
|
||||||
|
|
Loading…
Reference in New Issue