feat: track downloads via MeasurementProtocol

This commit is contained in:
Sergey Motornyuk 2023-06-14 20:23:38 +03:00
parent 5268985575
commit acb7a81bc4
5 changed files with 80 additions and 24 deletions

View File

@ -5,7 +5,9 @@ from werkzeug.utils import import_string
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
@ -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_
@ -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")

View File

@ -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
@ -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

View File

@ -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

View File

@ -9,19 +9,24 @@ from ckanext.googleanalytics import config
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)
@ -32,11 +37,28 @@ class SafeJSONEncoder(json.JSONEncoder):
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={
@ -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)

View File

@ -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,
@ -90,7 +90,11 @@ 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,
@ -98,6 +102,7 @@ def _post_analytics(
"id": request_id,
"payload": request_payload,
})
else:
data_dict = utils.UniversalAnalyticsData({
"v": 1,