Add report action

This commit is contained in:
Sergey Motornyuk 2022-05-07 01:59:15 +03:00
parent 86ec2858a5
commit 67b599ce1a
10 changed files with 99 additions and 73 deletions

View File

@ -49,7 +49,6 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: Install requirements - name: Install requirements
run: | run: |
pip install -r requirements.txt
pip install -r dev-requirements.txt pip install -r dev-requirements.txt
pip install -e . pip install -e .
# Replace default path to CKAN core config file with the one on the container # Replace default path to CKAN core config file with the one on the container

View File

@ -11,9 +11,13 @@ groups:
- key: googleanalytics.account - key: googleanalytics.account
- key: googleanalytics.profile_id
- key: googleanalytics.domain - key: googleanalytics.domain
default: auto default: auto
- key: googleanalytics.credentials.path
- key: googleanalytics.fields - key: googleanalytics.fields
default: "{}" default: "{}"

View File

@ -1,44 +1,35 @@
import httplib2
from apiclient.discovery import build from apiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials from oauth2client.service_account import ServiceAccountCredentials
from ckanext.googleanalytics import utils from . import utils
def _prepare_credentials(credentials_filename):
"""
Either returns the user's oauth credentials or uses the credentials
file to generate a token (by forcing the user to login in the browser)
"""
scope = ["https://www.googleapis.com/auth/analytics.readonly"]
credentials = ServiceAccountCredentials.from_json_keyfile_name(
credentials_filename, scopes=scope
)
return credentials
def init_service(credentials_file): def init_service(credentials_file):
""" """Get a service that communicates to a Google API."""
Given a file containing the user's oauth token (and another with scope = ["https://www.googleapis.com/auth/analytics.readonly"]
credentials in case we need to generate the token) will return a credentials = ServiceAccountCredentials.from_json_keyfile_name(
service object representing the analytics API. credentials_file, scopes=scope
""" )
http = httplib2.Http()
credentials = _prepare_credentials(credentials_file) return build("analytics", "v3", credentials=credentials)
http = credentials.authorize(http) # authorize the http object
return build("analytics", "v3", http=http)
def get_profile_id(service): def get_profile_id(service):
""" """Get static profile ID or fetch one from the service.
Get the profile ID for this user and the service specified by the Get the profile ID for this user and the service specified by the
'googleanalytics.id' configuration option. This function iterates 'googleanalytics.id' configuration option. This function iterates
over all of the accounts available to the user who invoked the over all of the accounts available to the user who invoked the
service to find one where the account name matches (in case the service to find one where the account name matches (in case the
user has several). user has several).
If not user configured, the first account is used
""" """
profile_id = utils.config_profile_id()
if profile_id:
return profile_id
accounts = service.management().accounts().list().execute() accounts = service.management().accounts().list().execute()
if not accounts.get("items"): if not accounts.get("items"):
@ -46,14 +37,9 @@ def get_profile_id(service):
accountName = utils.config_account() accountName = utils.config_account()
webPropertyId = utils.config_id() webPropertyId = utils.config_id()
for acc in accounts.get("items"): for acc in accounts.get("items"):
if acc.get("name") == accountName: if not accountName or acc.get("name") == accountName:
accountId = acc.get("id") accountId = acc.get("id")
# TODO: check, whether next line is doing something useful.
service.management().webproperties().list(
accountId=accountId
).execute()
profiles = ( profiles = (
service.management() service.management()
.profiles() .profiles()

View File

@ -1,28 +0,0 @@
header_code = """
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', '%s']);
_gaq.push(['_setDomainName', '%s']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ?
'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script>
"""
footer_code = """
<script type="text/javascript" src="%s"></script>
"""
download_style = """
<style type="text/css">
span.downloads-count {
font-size: 0.9em;
}
</style>
"""

View File

@ -4,19 +4,22 @@ import ckan.plugins.toolkit as tk
from ckan.logic import validate from ckan.logic import validate
from . import schema from . import schema
from .. import utils
from ..model import PackageStats, ResourceStats from ..model import PackageStats, ResourceStats
from ..ga_auth import init_service, get_profile_id
def get_actions(): def get_actions():
return dict( return dict(
googleanalytics_package_stats_show=googleanalytics_package_stats_show, googleanalytics_package_stats_show=package_stats_show,
googleanalytics_resource_stats_show=googleanalytics_resource_stats_show googleanalytics_resource_stats_show=resource_stats_show,
googleanalytics_event_report=event_report,
) )
@validate(schema.googleanalytics_package_stats_show) @validate(schema.package_stats_show)
@tk.side_effect_free @tk.side_effect_free
def googleanalytics_package_stats_show(context, data_dict): def package_stats_show(context, data_dict):
tk.check_access("googleanalytics_package_stats_show", context, data_dict) tk.check_access("googleanalytics_package_stats_show", context, data_dict)
rec = ( rec = (
context["session"] context["session"]
@ -31,9 +34,9 @@ def googleanalytics_package_stats_show(context, data_dict):
return rec.for_json(context) return rec.for_json(context)
@validate(schema.googleanalytics_resource_stats_show) @validate(schema.resource_stats_show)
@tk.side_effect_free @tk.side_effect_free
def googleanalytics_resource_stats_show(context, data_dict): def resource_stats_show(context, data_dict):
tk.check_access("googleanalytics_resource_stats_show", context, data_dict) tk.check_access("googleanalytics_resource_stats_show", context, data_dict)
rec = ( rec = (
context["session"] context["session"]
@ -46,3 +49,35 @@ def googleanalytics_resource_stats_show(context, data_dict):
raise tk.ObjectNotFound() raise tk.ObjectNotFound()
return rec.for_json(context) return rec.for_json(context)
@validate(schema.event_report)
@tk.side_effect_free
def event_report(context, data_dict):
tk.check_access("sysadmin", context, data_dict)
se = init_service(utils.config_credentials())
filters = "ga:eventAction=={action};ga:eventCategory=={category}".format(
action=data_dict["action"], category=data_dict["category"]
)
if "label" in data_dict:
filters += ";ga:eventLabel=={label}".format(label=data_dict["label"])
report = (
se.data()
.ga()
.get(
ids="ga:{id}".format(id=get_profile_id(se)),
dimensions=",".join(data_dict["dimensions"]),
metrics=",".join(data_dict["metrics"]),
start_date=data_dict["start_date"].date().isoformat(),
end_date=data_dict["end_date"].date().isoformat(),
filters=filters,
)
.execute()
)
return {
"headers": [h["name"] for h in report["columnHeaders"]],
"rows": report["rows"],
}

View File

@ -5,14 +5,14 @@ from ckan.authz import is_authorized
def get_auth(): def get_auth():
return dict( return dict(
googleanalytics_package_stats_show=googleanalytics_package_stats_show, googleanalytics_package_stats_show=package_stats_show,
googleanalytics_resource_stats_show=googleanalytics_resource_stats_show googleanalytics_resource_stats_show=resource_stats_show,
) )
def googleanalytics_package_stats_show(context, data_dict): def package_stats_show(context, data_dict):
return {"success": is_authorized("package_show", context, data_dict)} return {"success": is_authorized("package_show", context, data_dict)}
def googleanalytics_resource_stats_show(context, data_dict): def resource_stats_show(context, data_dict):
return {"success": is_authorized("resource_show", context, data_dict)} return {"success": is_authorized("resource_show", context, data_dict)}

View File

@ -2,10 +2,31 @@ from ckan.logic.schema import validator_args
@validator_args @validator_args
def googleanalytics_package_stats_show(not_empty): def package_stats_show(not_empty):
return {"id": [not_empty]} return {"id": [not_empty]}
@validator_args @validator_args
def googleanalytics_resource_stats_show(not_empty): def resource_stats_show(not_empty):
return {"id": [not_empty]} return {"id": [not_empty]}
@validator_args
def event_report(
not_empty, isodate, ignore_missing, json_list_or_string, default
):
return {
"start_date": [not_empty, isodate],
"end_date": [not_empty, isodate],
"category": [not_empty],
"action": [not_empty],
"label": [ignore_missing, not_empty],
"dimensions": [
default("ga:eventCategory,ga:eventAction,ga:eventLabel"),
json_list_or_string,
],
"metrics": [
default("ga:totalEvents,ga:uniqueEvents,ga:eventValue"),
json_list_or_string,
],
}

View File

@ -14,6 +14,14 @@ def config_account():
return tk.config.get("googleanalytics.account") return tk.config.get("googleanalytics.account")
def config_profile_id():
return tk.config.get("googleanalytics.profile_id")
def config_credentials():
return tk.config.get("googleanalytics.credentials.path")
def config_domain(): def config_domain():
return tk.config.get("googleanalytics.domain", "auto") return tk.config.get("googleanalytics.domain", "auto")

View File

@ -1,6 +1,6 @@
[metadata] [metadata]
name = ckanext-googleanalytics name = ckanext-googleanalytics
version = 2.0.8 version = 2.1.0
description = Add GA tracking and reporting to CKAN instance description = Add GA tracking and reporting to CKAN instance
long_description = file: README.md long_description = file: README.md
long_description_content_type = text/markdown long_description_content_type = text/markdown
@ -23,6 +23,7 @@ keywords =
# python_requires = >= 3.7 # python_requires = >= 3.7
install_requires = install_requires =
ckantoolkit ckantoolkit
google-api-python-client
packages = find: packages = find:
namespace_packages = ckanext namespace_packages = ckanext