From 01fb2e2e4a87da9a05557621e84a4cdc4824c6d8 Mon Sep 17 00:00:00 2001 From: Etienne Trimaille Date: Mon, 29 Apr 2024 18:40:08 +0200 Subject: [PATCH] Call Plausible API event --- lizmap_server/plausible.py | 116 +++++++++++++++++++++++++++++++++++++ lizmap_server/plugin.py | 5 +- 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 lizmap_server/plausible.py diff --git a/lizmap_server/plausible.py b/lizmap_server/plausible.py new file mode 100644 index 00000000..7ee297c4 --- /dev/null +++ b/lizmap_server/plausible.py @@ -0,0 +1,116 @@ +__copyright__ = 'Copyright 2024, 3Liz' +__license__ = 'GPL version 3' +__email__ = 'info@3liz.org' + +import json +import os +import platform + +from qgis.core import Qgis, QgsNetworkAccessManager, QgsSettings +from qgis.PyQt.QtCore import QByteArray, QDateTime, QLocale, QUrl +from qgis.PyQt.QtNetwork import QNetworkRequest + +from lizmap_server.tools import to_bool, version + +MIN_SECONDS = 3600 +ENV_SKIP_STATS = "3LIZ_SKIP_STATS" + +PLAUSIBLE_DOMAIN_PROD = "plugin.server.lizmap.com" +PLAUSIBLE_URL_PROD = "https://analytics.3liz.com/api/event" + +PLAUSIBLE_DOMAIN_TEST = PLAUSIBLE_DOMAIN_PROD +PLAUSIBLE_URL_TEST = "https://plausible.snap.3liz.net/api/event" + + +# For testing purpose, to test. +# Similar to QGIS dashboard https://feed.qgis.org/metabase/public/dashboard/df81071d-4c75-45b8-a698-97b8649d7228 +# We only collect data listed in the list below +# and the country according to IP address. +# The IP is not stored by Plausible Community Edition https://github.com/plausible/analytics +# Plausible is GDPR friendly https://plausible.io/data-policy +# The User-Agent is set by QGIS Desktop itself + +class Plausible: + + def __init__(self): + """ Constructor. """ + self.previous_date = None + + def request_stat_event(self) -> bool: + """ Request to send an event to the API. """ + if to_bool(os.getenv(ENV_SKIP_STATS), default_value=False): + # Disabled by environment variable + return False + + if to_bool(os.getenv("CI"), default_value=False): + # If running on CI, do not send stats + return False + + current = QDateTime().currentDateTimeUtc() + if self.previous_date and self.previous_date.secsTo(current) < MIN_SECONDS: + # Not more than one request per hour + # It's done at plugin startup anyway + return False + + if self._send_stat_event(): + self.previous_date = current + return True + + return False + + @staticmethod + def _send_stat_event() -> bool: + """ Send stats event to the API. """ + # Only turn ON for debug purpose, temporary ! + debug = False + extra_debug = False + + lizmap_plugin_version = version() + if lizmap_plugin_version in ('master', 'dev'): + # Dev versions of the plugin, it's a kind of debug + debug = True + + plausible_url = PLAUSIBLE_URL_TEST if debug else PLAUSIBLE_URL_PROD + plausible_domain = PLAUSIBLE_DOMAIN_TEST if debug else PLAUSIBLE_DOMAIN_PROD + + request = QNetworkRequest() + # noinspection PyArgumentList + request.setUrl(QUrl(plausible_url)) + if extra_debug: + request.setRawHeader(b"X-Debug-Request", b"true") + request.setRawHeader(b"X-Forwarded-For", b"127.0.0.1") + request.setHeader(QNetworkRequest.ContentTypeHeader, "application/json") + + # Qgis.QGIS_VERSION → 3.34.6-Prizren + # noinspection PyUnresolvedReferences + qgis_version_full = Qgis.QGIS_VERSION.split('-')[0] + # qgis_version_full → 3.34.6 + qgis_version_branch = '.'.join(qgis_version_full.split('.')[0:2]) + # qgis_version_branch → 3.34 + + python_version_full = platform.python_version() + # python_version_full → 3.10.12 + python_version_branch = '.'.join(python_version_full.split('.')[0:2]) + # python_version_branch → 3.10 + + data = { + "name": "lizmap-server", + "props": { + # Plugin version + "plugin-version": lizmap_plugin_version, + # QGIS + "qgis-version-full": qgis_version_full, + "qgis-version-branch": qgis_version_branch, + # Python + "python-version-full": python_version_full, + "python-version-branch": python_version_branch, + # OS + "os-name": platform.system(), + "os-version": platform.release(), + }, + "url": plausible_url, + "domain": plausible_domain, + } + # noinspection PyArgumentList + QgsNetworkAccessManager.instance().post(request, QByteArray(str.encode(json.dumps(data)))) + return True diff --git a/lizmap_server/plugin.py b/lizmap_server/plugin.py index 94188ae6..660404d5 100755 --- a/lizmap_server/plugin.py +++ b/lizmap_server/plugin.py @@ -1,4 +1,4 @@ -__copyright__ = 'Copyright 2022, 3Liz' +__copyright__ = 'Copyright 2024, 3Liz' __license__ = 'GPL version 3' __email__ = 'info@3liz.org' @@ -15,6 +15,7 @@ from lizmap_server.lizmap_filter import LizmapFilter from lizmap_server.lizmap_service import LizmapService from lizmap_server.logger import Logger +from lizmap_server.plausible import Plausible from lizmap_server.server_info_handler import ServerInfoHandler from lizmap_server.tools import check_environment_variable, version @@ -28,6 +29,8 @@ def __init__(self, server_iface: QgsServerInterface) -> None: self.logger = Logger() self.version = version() self.logger.info('Init server version "{}"'.format(self.version)) + self.plausible = Plausible() + self.plausible.request_stat_event() service_registry = server_iface.serviceRegistry()