Skip to content

Commit

Permalink
feat: track downloads via MeasurementProtocol
Browse files Browse the repository at this point in the history
  • Loading branch information
smotornyuk committed Jun 14, 2023
1 parent 5268985 commit acb7a81
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 24 deletions.
12 changes: 10 additions & 2 deletions ckanext/googleanalytics/config.py
Expand Up @@ -5,7 +5,9 @@

import ckan.plugins.toolkit as tk

CONFIG_TRACKING_ID = "googleanalytics.id"
CONFIG_HANDLER_PATH = "googleanalytics.download_handler"
CONFIG_TRACKING_MODE = "googleanalytics.tracking_mode"

DEFAULT_RESOURCE_URL_TAG = "/downloads/"
DEFAULT_RECENT_VIEW_DAYS = 14
Expand All @@ -30,10 +32,9 @@ def download_handler():
return handler



def tracking_mode():
# type: () -> Literal["ga", "gtag", "gtm"]
type_ = tk.config.get("googleanalytics.tracking_mode")
type_ = tk.config.get(CONFIG_TRACKING_MODE)
if type_:
return type_

Expand Down Expand Up @@ -75,6 +76,13 @@ def measurement_protocol_client_secret():
return tk.config.get("googleanalytics.measurement_protocol.client_secret")


def measurement_protocol_track_downloads():
# type: () -> bool
return tk.asbool(
tk.config.get("googleanalytics.measurement_protocol.track_downloads")
)


def account():
return tk.config.get("googleanalytics.account")

Expand Down
16 changes: 9 additions & 7 deletions ckanext/googleanalytics/config_declaration.yaml
Expand Up @@ -7,8 +7,9 @@ groups:
placeholder: UA-000000000-1
validators: not_empty
description: |
Google tag ID(`G-*`) for Google Analytics 4 properties or the
Tracking ID(`UA-*`) for Universal Analytics properties.
Google tag ID(`G-*`) for Google Analytics 4, the Tracking ID(`UA-*`)
for Universal Analytics, or container ID(`GTM-*`) for Google Tag
Manager.
- key: googleanalytics.download_handler
default: ckan.views.resource:download
Expand All @@ -22,13 +23,14 @@ groups:
example: gtag
description: |
Defines the type of code snippet embedded into the page. Can be set
to either `ga`(enables `googleanalytics.js`) or `gtag`(enables
`gtag.js`). The plugin will check `googleanalytics.id` and set
appropriate value depending on the prefix: `G-` enables `gtag`, while
`UA-` enables `ga` mode.
to `ga`(enables `googleanalytics.js`), `gtag`(enables `gtag.js`), or
`gtm`(enables Google Tag Manager). The plugin will check
`googleanalytics.id` and set appropriate value depending on the
prefix: `G-` enables `gtag`, `UA-` enables `ga` mode, and `GTM-`
enables Google Tag Manager.
Use this option only if you know better which snippet you need. In
future, after dropping UniversalAnalytics, this option will be
future, after dropping UniversalAnalytics, this option may be
removed.
- key: googleanalytics.account
Expand Down
22 changes: 22 additions & 0 deletions ckanext/googleanalytics/tests/test_view.py
@@ -0,0 +1,22 @@
import pytest

import ckan.plugins.toolkit as tk
from ckanext.googleanalytics import config


@pytest.mark.usefixtures("with_plugins", "with_request_context")
class TestCodeSnippets:
@pytest.mark.parametrize("mode", ["ga", "gtag", "gtm"])
@pytest.mark.parametrize("tracking_id", ["UA-123", "G-123", "GTM-123"])
def test_tracking_(self, mode, tracking_id, app, ckan_config, monkeypatch):
snippet = tk.h.googleanalytics_header()
monkeypatch.setitem(ckan_config, config.CONFIG_TRACKING_ID, tracking_id)
monkeypatch.setitem(ckan_config, config.CONFIG_TRACKING_MODE, mode)
snippet = tk.render_snippet("googleanalytics/snippets/_{}.html".format(mode), {
"googleanalytics_id": tracking_id,
"googleanalytics_domain": config.domain(),
"googleanalytics_fields": config.fields(),
"googleanalytics_linked_domains": config.linked_domains()
})
resp = app.get("/")
assert snippet in resp.body
45 changes: 32 additions & 13 deletions ckanext/googleanalytics/utils.py
Expand Up @@ -9,19 +9,24 @@
log = logging.getLogger(__name__)

EVENT_API = "CKAN API Request"
EVENT_DOWNLOAD = "CKAN Resource Download Request"


def send_event(data):
if isinstance(data, MeasurementProtocolData):
if data["event"] != EVENT_API:
log.warning("Only API event supported by Measurement Protocol at the moment")
return
if data["event"] == EVENT_API:
return _mp_api_handler({
"action": data["object"],
"payload": data["payload"],
})

return _mp_api_handler({
"action": data["object"],
"payload": data["payload"],
})
if data["event"] == EVENT_DOWNLOAD:
return _mp_download_handler({"payload": {
"resource_id": data["id"],
}})

log.warning("Only API and Download events supported by Measurement Protocol at the moment")
return

return _ga_handler(data)

Expand All @@ -32,11 +37,28 @@ def default(self, _):


def _mp_api_handler(data_dict):

log.debug(
"Sending API event to Google Analytics using the Measurement Protocol: %s",
data_dict
)
_mp_event({
"name": data_dict["action"],
"params": data_dict["payload"]
})


def _mp_download_handler(data_dict):
log.debug(
"Sending Downlaod event to Google Analytics using the Measurement Protocol: %s",
data_dict
)
_mp_event({
"name": "file_download",
"params": data_dict["payload"],
})


def _mp_event(event):
resp = requests.post(
"https://www.google-analytics.com/mp/collect",
params={
Expand All @@ -46,13 +68,10 @@ def _mp_api_handler(data_dict):
data=json.dumps({
"client_id": config.measurement_protocol_client_id(),
"non_personalized_ads": False,
"events":[{
"name": data_dict["action"],
"params": data_dict["payload"]
}]
"events":[event]
}, cls=SafeJSONEncoder)
)
# breakpoint()

if resp.status_code >= 300:
log.error("Cannot post event: %s", resp)

Expand Down
9 changes: 7 additions & 2 deletions ckanext/googleanalytics/views.py
Expand Up @@ -59,7 +59,7 @@ def download(id, resource_id, filename=None, package_type="dataset"):
handler = resource.download
_post_analytics(
g.user,
"CKAN Resource Download Request",
utils.EVENT_DOWNLOAD,
"Resource",
"Download",
resource_id,
Expand Down Expand Up @@ -90,14 +90,19 @@ def _post_analytics(
from ckanext.googleanalytics.plugin import GoogleAnalyticsPlugin

if config.tracking_id():
if config.measurement_protocol_client_id() and event_type == utils.EVENT_API:
mp_client_id = config.measurement_protocol_client_id()
if mp_client_id and (
event_type == utils.EVENT_API
or (event_type == utils.EVENT_DOWNLOAD and config.measurement_protocol_track_downloads())
):
data_dict = utils.MeasurementProtocolData({
"event": event_type,
"object": request_obj_type,
"function": request_function,
"id": request_id,
"payload": request_payload,
})

else:
data_dict = utils.UniversalAnalyticsData({
"v": 1,
Expand Down

0 comments on commit acb7a81

Please sign in to comment.