Piwik: Remove unreliable/broken download tracking

This commit is contained in:
Adrian Moennich 2021-04-08 15:15:08 +02:00
parent a240e284fd
commit 65361fa851
10 changed files with 5 additions and 328 deletions

View File

@ -5,14 +5,12 @@
// them and/or modify them under the terms of the MIT License;
// see the LICENSE file for more details.
import 'jqtree';
import 'jquery';
import './main.css';
import 'indico/jquery/compat/jqplot';
$(function() {
const $t = $T.domain('piwik');
const treeDOMTarget = '#materialTree';
/**
* Clears the DOM element for the graph and then initiates a jqPlot render
@ -75,26 +73,6 @@ $(function() {
}
};
/**
* Draw a customized jqTree
*/
const draw_jqTree = function(treeData) {
$(treeDOMTarget).tree({
data: treeData,
autoOpen: 0,
saveState: true,
onCanSelectNode(node) {
// Leaf node (material) can be selected
return node.children.length === 0;
},
onCreateLi(node, $li) {
if (node.id !== undefined) {
$li.find('.title').addClass('selectableNode');
}
},
});
};
/**
* Get base values for API requests
*/
@ -146,56 +124,6 @@ $(function() {
return output;
};
/**
* Load material downloads data via AJAX and draw its graph
*/
const load_material_graph = function(uri, replot) {
replot = typeof replot !== 'undefined' ? replot : false;
const DOMTarget = 'materialDownloadChart';
const graph_params = get_api_params();
graph_params.download_url = uri;
$.ajax({
url: build_url(PiwikPlugin.urls.data_downloads, graph_params),
type: 'POST',
dataType: 'json',
success(data) {
if (handleAjaxError(data)) {
return;
}
const materialHits = [
get_jqplot_array_values(data.metrics.downloads.individual, 'total'),
get_jqplot_array_values(data.metrics.downloads.individual, 'unique'),
];
draw_jqplot_graph(materialHits, DOMTarget, replot);
$('#materialTotalDownloads').html(data.metrics.downloads.cumulative.total);
},
});
};
/**
* Load the material files data and draw its jqTree
*/
const load_material_tree = function() {
$(treeDOMTarget).html(progressIndicator(true, true).dom);
$.ajax({
url: build_url(PiwikPlugin.urls.material, get_api_params()),
type: 'POST',
dataType: 'json',
success(data) {
if (handleAjaxError(data)) {
return;
}
if (data.material.tree !== null) {
draw_jqTree(data.material.tree);
} else {
$(treeDOMTarget).html($t.gettext('No material found'));
}
},
});
};
/**
* Loads visits data and draw its graph
*/
@ -273,13 +201,6 @@ $(function() {
window.location.href = url;
});
// Event handler for clicking 'selectable' elements from the jqTree.
$(treeDOMTarget).bind('tree.click', function(event) {
$('#materialTitle').html(event.node.name);
$('#materialDownloadChart').html(progressIndicator(true, true).dom);
load_material_graph(event.node.id, true);
});
// jQuery UI Dialog if no data is received via AJAX (timeout)
$('#dialogNoGraphData').dialog({
modal: true,
@ -294,7 +215,6 @@ $(function() {
load_graphs();
load_visits_graph();
load_material_tree();
};
init();

View File

@ -116,54 +116,6 @@ div#statsGenerated {
padding-top: 10px;
}
div#materialTree {
display: block;
width: 190px;
height: 80%;
overflow: auto;
float: left;
padding: 15px 10px 10px 10px;
}
div#materialContainer {
display: block;
width: 510px;
float: left;
padding-left: 10px;
height: 90%;
}
/* jqTree Formatting */
div#materialTree ul {
margin: 0;
padding: 0 0 0 10px;
line-height: 1.5em;
}
div#marginTree ul ul {
padding-left: 5px;
}
span.selectableNode {
color: #0b63a5 !important;
font-size: 0.9em;
display: block;
width: auto;
border-bottom: 1px solid #efefef;
}
ul.tree .title {
color: #444;
}
/* jqPlot Formatting */
div#materialDownloadChart {
width: auto;
height: 250px;
}
div#visitorChart {
margin-left: 15px;
width: 95%;

View File

@ -11,8 +11,7 @@ from werkzeug.exceptions import NotFound
from indico.modules.events.management.controllers import RHManageEventBase
from indico_piwik.reports import (ReportCountries, ReportDevices, ReportDownloads, ReportGeneral, ReportMaterial,
ReportVisitsPerDay)
from indico_piwik.reports import ReportCountries, ReportDevices, ReportGeneral, ReportVisitsPerDay
from indico_piwik.views import WPStatistics
@ -58,15 +57,6 @@ class RHApiEventBase(RHApiBase):
self._report_params['contrib_id'] = request.args.get('contrib_id')
class RHApiDownloads(RHApiEventBase):
def _process_args(self):
RHApiEventBase._process_args(self)
self._report_params['download_url'] = request.args['download_url']
def _process(self):
return jsonify(ReportDownloads.get(**self._report_params))
class RHApiEventVisitsPerDay(RHApiEventBase):
def _process(self):
return jsonify(ReportVisitsPerDay.get(**self._report_params))
@ -80,8 +70,3 @@ class RHApiEventGraphCountries(RHApiEventBase):
class RHApiEventGraphDevices(RHApiEventBase):
def _process(self):
return jsonify(ReportDevices.get(**self._report_params))
class RHApiMaterial(RHApiEventBase):
def _process(self):
return jsonify(ReportMaterial.get(**self._report_params))

View File

@ -17,7 +17,6 @@ from indico_piwik import _
class SettingsForm(IndicoForm):
enabled = BooleanField(_("Track global visits"), widget=SwitchWidget())
enabled_for_events = BooleanField(_("Track events"), widget=SwitchWidget())
enabled_for_downloads = BooleanField(_("Track downloads"), widget=SwitchWidget())
cache_enabled = BooleanField(_("Cache results"), widget=SwitchWidget())
server_url = StringField(_("Piwik server URL"))
server_api_url = StringField(_("Piwik API server URL"),

View File

@ -12,15 +12,13 @@ from flask_pluginengine import render_plugin_template
from indico.core import signals
from indico.core.plugins import IndicoPlugin, IndicoPluginBlueprint, plugin_url_rule_to_js, url_for_plugin
from indico.modules.attachments.models.attachments import AttachmentType
from indico.modules.events.contributions import Contribution
from indico.web.menu import SideMenuItem
from indico_piwik import _
from indico_piwik.controllers import (RHApiDownloads, RHApiEventGraphCountries, RHApiEventGraphDevices,
RHApiEventVisitsPerDay, RHApiMaterial, RHStatistics)
from indico_piwik.controllers import (RHApiEventGraphCountries, RHApiEventGraphDevices, RHApiEventVisitsPerDay,
RHStatistics)
from indico_piwik.forms import SettingsForm
from indico_piwik.queries.tracking import track_download_request
class PiwikPlugin(IndicoPlugin):
@ -36,7 +34,6 @@ class PiwikPlugin(IndicoPlugin):
default_settings = {
'enabled': False,
'enabled_for_events': True,
'enabled_for_downloads': True,
'cache_enabled': True,
'server_url': '//127.0.0.1/piwik/',
'server_api_url': '//127.0.0.1/piwik/',
@ -50,7 +47,6 @@ class PiwikPlugin(IndicoPlugin):
def init(self):
super().init()
self.connect(signals.menu.items, self.add_sidemenu_item, sender='event-management-sidemenu')
self.connect(signals.attachments.attachment_accessed, self.track_download)
self.template_hook('html-head', self.inject_tracking)
def inject_tracking(self, **kwargs):
@ -73,23 +69,10 @@ class PiwikPlugin(IndicoPlugin):
return blueprint
def get_vars_js(self):
return {'urls': {'material': plugin_url_rule_to_js('piwik.material'),
'data_downloads': plugin_url_rule_to_js('piwik.data_downloads'),
'data_visits': plugin_url_rule_to_js('piwik.data_visits'),
return {'urls': {'data_visits': plugin_url_rule_to_js('piwik.data_visits'),
'graph_countries': plugin_url_rule_to_js('piwik.graph_countries'),
'graph_devices': plugin_url_rule_to_js('piwik.graph_devices')}}
def track_download(self, attachment, from_preview, **kwargs):
if from_preview or not self.settings.get('enabled_for_downloads'):
return
if attachment.type == AttachmentType.link:
resource_url = attachment.link_url
resource_title = f'Link - {attachment.title}'
else:
resource_url = request.base_url
resource_title = f'Download - {attachment.title}'
track_download_request.delay(resource_url, resource_title)
def _get_event_tracking_params(self):
site_id_events = PiwikPlugin.settings.get('site_id_events')
if not self.settings.get('enabled_for_events') or not site_id_events:
@ -115,8 +98,6 @@ class PiwikPlugin(IndicoPlugin):
blueprint = IndicoPluginBlueprint('piwik', __name__, url_prefix='/event/<int:event_id>/manage/statistics')
blueprint.add_url_rule('/', 'view', RHStatistics)
blueprint.add_url_rule('/material', 'material', RHApiMaterial, methods=('GET', 'POST'))
blueprint.add_url_rule('/data/downloads', 'data_downloads', RHApiDownloads, methods=('GET', 'POST'))
blueprint.add_url_rule('/data/visits', 'data_visits', RHApiEventVisitsPerDay, methods=('GET', 'POST'))
blueprint.add_url_rule('/graph/countries', 'graph_countries', RHApiEventGraphCountries, methods=('GET', 'POST'))
blueprint.add_url_rule('/graph/devices', 'graph_devices', RHApiEventGraphDevices, methods=('GET', 'POST'))

View File

@ -6,7 +6,6 @@
# see the LICENSE file for more details.
from operator import itemgetter
from urllib.parse import quote
from indico_piwik.queries.base import PiwikQueryReportEventBase
from indico_piwik.queries.utils import get_json_from_remote_server, reduce_json, stringify_seconds
@ -30,43 +29,6 @@ class PiwikQueryReportEventMetricVisitsBase(PiwikQueryReportEventMetricBase):
return result if result else {}
class PiwikQueryReportEventMetricDownloads(PiwikQueryReportEventMetricBase):
def call(self, download_url, **query_params):
return super().call(method='Actions.getDownload', downloadUrl=quote(download_url), **query_params)
def get_result(self, download_url):
result = get_json_from_remote_server(self.call, download_url=download_url, segmentation_enabled=False)
return {'cumulative': self._get_cumulative_results(result),
'individual': self._get_per_day_results(result)}
def _get_per_day_results(self, results):
hits_calendar = {}
for date, hits in results.items():
day_hits = {'total': 0, 'unique': 0}
if hits:
for metrics in hits:
day_hits['total'] += metrics['nb_hits']
day_hits['unique'] += metrics['nb_uniq_visitors']
hits_calendar[date] = day_hits
return hits_calendar
def _get_cumulative_results(self, results):
"""
Returns a dictionary of {'total': x, 'unique': y} for the
date range.
"""
hits = {'total': 0, 'unique': 0}
day_hits = list(hits[0] for hits in results.values() if hits)
for metrics in day_hits:
hits['total'] += metrics['nb_hits']
hits['unique'] += metrics['nb_uniq_visitors']
return hits
class PiwikQueryReportEventMetricReferrers(PiwikQueryReportEventMetricBase):
def call(self, **query_params):
return super().call(method='Referrers.getReferrerType', period='range', **query_params)

View File

@ -1,38 +0,0 @@
# This file is part of the Indico plugins.
# Copyright (C) 2002 - 2021 CERN
#
# The Indico plugins are free software; you can redistribute
# them and/or modify them under the terms of the MIT License;
# see the LICENSE file for more details.
from datetime import datetime
from urllib.parse import quote
from indico.core.celery import celery
from indico_piwik.piwik import PiwikRequest
@celery.task
def track_download_request(download_url, download_title):
"""Track a download in Piwik"""
from indico_piwik.plugin import PiwikPlugin
if not download_url:
raise ValueError("download_url can't be empty")
if not download_title:
raise ValueError("download_title can't be empty")
request = PiwikRequest(server_url=PiwikPlugin.settings.get('server_api_url'),
site_id=PiwikPlugin.settings.get('site_id_events'),
api_token=PiwikPlugin.settings.get('server_token'),
query_script=PiwikPlugin.track_script)
action_url = quote(download_url)
dt = datetime.now()
request.call(idsite=request.site_id,
rec=1,
action_name=quote(download_title),
url=action_url,
download=action_url,
h=dt.hour, m=dt.minute, s=dt.second)

View File

@ -11,15 +11,13 @@ from datetime import timedelta
from sqlalchemy.orm import joinedload
from indico.core.cache import make_scoped_cache
from indico.modules.attachments.util import get_nested_attached_items
from indico.modules.events import Event
from indico.modules.events.contributions import Contribution
from indico.util.date_time import format_time, now_utc, utc_to_server
from indico.util.serializer import Serializer
from indico_piwik.queries.graphs import PiwikQueryReportEventGraphCountries, PiwikQueryReportEventGraphDevices
from indico_piwik.queries.metrics import (PiwikQueryReportEventMetricDownloads,
PiwikQueryReportEventMetricPeakDateAndVisitors,
from indico_piwik.queries.metrics import (PiwikQueryReportEventMetricPeakDateAndVisitors,
PiwikQueryReportEventMetricReferrers, PiwikQueryReportEventMetricUniqueVisits,
PiwikQueryReportEventMetricVisitDuration, PiwikQueryReportEventMetricVisits)
@ -92,13 +90,6 @@ class ReportDevices(ReportBase):
self.graphs = {'devices': PiwikQueryReportEventGraphDevices(**self.params).get_result()}
class ReportDownloads(ReportBase):
__public__ = ['metrics']
def _build_report(self, download_url):
self.metrics = {'downloads': PiwikQueryReportEventMetricDownloads(**self.params).get_result(download_url)}
class ReportGeneral(ReportBase):
__public__ = ['event_id', 'contrib_id', 'start_date', 'end_date', 'metrics', 'contributions', 'timestamp']
@ -132,34 +123,6 @@ class ReportGeneral(ReportBase):
self.contributions[key] = '{} ({})'.format(contribution.title, format_time(contribution.start_dt))
class ReportMaterial(ReportBase):
__public__ = ['material']
def _build_report(self):
event = Event.get_or_404(self.params['event_id'], is_deleted=False)
new_material = get_nested_attached_items(event)
self.material = {'tree': [self._format_data(new_material)]}
def _format_data(self, raw_node):
if 'object' not in raw_node:
return None
node = {'label': raw_node['object'].title,
'children': []}
if 'files' in raw_node:
node['children'] += self._format_data_files(raw_node['files'])
if 'folders' in raw_node:
for folder in raw_node['folders']:
node['children'] += [{'label': folder.title, 'children': self._format_data_files(folder.attachments)}]
if 'children' in raw_node:
node['children'] += [self._format_data(child) for child in raw_node['children']]
if not node['children']:
return None
return node
def _format_data_files(self, files):
return [{'id': f.absolute_download_url, 'label': f.title} for f in files]
class ReportVisitsPerDay(ReportBase):
__public__ = ['metrics']

View File

@ -179,34 +179,6 @@
</div>
<div class="statsRow">
<!-- File Downloads -->
<div class="statsWidget full edge">
<div class="statsWidgetTitle">
{% trans %}Material Downloads{% endtrans %}
</div>
<div class="statsWidgetContent">
<div id="materialTree"></div>
<div id="materialContainer">
<div id="materialHeader" class="statsTableDivider">
{% trans %}Downloads for{% endtrans %}:
<span id="materialTitle">
&mdash;
</span>
({% trans %}Total{% endtrans %}: <span id="materialTotalDownloads">0</span>)
</div>
<div id="materialInfo">
<div id="materialDownloadChart">
{% trans %}No material selected{% endtrans %}
</div>
</div>
</div>
</div>
</div>
</div>
<div id="statsGenerated">
{% trans dt=report.timestamp | format_datetime('long') -%}
This report was generated at: {{ dt }}

View File

@ -1,19 +0,0 @@
{
"name": "indico-plugin-piwik",
"version": "1.0.0",
"description": "",
"main": "indico_piwik/client/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/indico/indico-plugins.git"
},
"author": "Indico Team <indico.team@cern.ch>",
"license": "MIT",
"bugs": {
"url": "https://github.com/indico/indico-plugins/issues"
},
"homepage": "https://github.com/indico/indico-plugins#readme",
"dependencies": {
"jqtree": "^1.4.4"
}
}