-
-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
189 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,9 @@ | |
__license__ = 'GPL version 3' | ||
__email__ = '[email protected]' | ||
|
||
from wfsOutputExtension.logging import Logger | ||
from wfsOutputExtension.plausible import Plausible | ||
|
||
|
||
# noinspection PyPep8Naming | ||
def classFactory(iface): | ||
|
@@ -39,6 +42,15 @@ class WfsOutputExtensionServer: | |
|
||
def __init__(self, server_iface): | ||
self.serverIface = server_iface | ||
self.logger = Logger() | ||
|
||
# noinspection PyBroadException | ||
try: | ||
self.plausible = Plausible() | ||
self.plausible.request_stat_event() | ||
except Exception as e: | ||
self.logger.log_exception(e) | ||
self.logger.critical('Error while calling the API stats') | ||
|
||
from .wfs_filter import WFSFilter # NOQA ABS101 | ||
server_iface.registerFilter(WFSFilter(server_iface), 50) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
__copyright__ = 'Copyright 2024, 3Liz' | ||
__license__ = 'GPL version 3' | ||
__email__ = '[email protected]' | ||
|
||
import json | ||
import os | ||
import platform | ||
|
||
from qgis.core import Qgis, QgsNetworkAccessManager | ||
from qgis.PyQt.QtCore import QByteArray, QDateTime, QUrl | ||
from qgis.PyQt.QtNetwork import QNetworkReply, QNetworkRequest | ||
|
||
from wfsOutputExtension.logging import Logger | ||
from wfsOutputExtension.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://bourbon.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 | ||
|
||
plugin_version = version() | ||
if 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 | ||
|
||
is_lizcloud = "lizcloud" in os.getenv("QGIS_SERVER_APPLICATION_NAME", "").lower() | ||
if is_lizcloud: | ||
plausible_domain = os.getenv("QGIS_SERVER_PLAUSIBLE_DOMAIN_NAME", PLAUSIBLE_DOMAIN_PROD) | ||
else: | ||
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": "wfsOutputExtension-server", | ||
"props": { | ||
# Plugin version | ||
"plugin-version": 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(), | ||
}, | ||
"url": plausible_url, | ||
"domain": plausible_domain, | ||
} | ||
|
||
# noinspection PyArgumentList | ||
r: QNetworkReply = QgsNetworkAccessManager.instance().post(request, QByteArray(str.encode(json.dumps(data)))) | ||
if not is_lizcloud: | ||
return True | ||
|
||
logger = Logger() | ||
message = ( | ||
f"Request HTTP OS process '{os.getpid()}' sent to '{plausible_url}' with domain '{plausible_domain} : ") | ||
if r.error() == QNetworkReply.NoError: | ||
logger.info(message + "OK") | ||
else: | ||
logger.warning("{} {}".format(message, r.error())) | ||
|
||
return True |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
__copyright__ = 'Copyright 2024, 3Liz' | ||
__license__ = 'GPL version 3' | ||
__email__ = '[email protected]' | ||
|
||
import configparser | ||
|
||
from pathlib import Path | ||
from typing import Dict, Tuple, Union | ||
|
||
from qgis.core import Qgis, QgsMessageLog | ||
|
||
from atlasprint.logger import Logger | ||
|
||
|
||
def version() -> str: | ||
""" Returns the Lizmap current version. """ | ||
file_path = Path(__file__).parent.joinpath('metadata.txt') | ||
config = configparser.ConfigParser() | ||
try: | ||
config.read(file_path, encoding='utf8') | ||
except UnicodeDecodeError: | ||
# Issue LWC https://github.com/3liz/lizmap-web-client/issues/1908 | ||
# Maybe a locale issue ? | ||
# Do not use logger here, circular import | ||
# noinspection PyTypeChecker | ||
QgsMessageLog.logMessage( | ||
"Error, an UnicodeDecodeError occurred while reading the metadata.txt. Is the locale " | ||
"correctly set on the server ?", | ||
"WfsOutputExtension", Qgis.Critical) | ||
return 'NULL' | ||
else: | ||
return config["general"]["version"] | ||
|
||
|
||
def to_bool(val: Union[str, int, float, bool], default_value: bool = True) -> bool: | ||
""" Convert config value to boolean """ | ||
if isinstance(val, str): | ||
# For string, compare lower value to True string | ||
return val.lower() in ('yes', 'true', 't', '1') | ||
elif not val: | ||
# For value like False, 0, 0.0, None, empty list or dict returns False | ||
return False | ||
else: | ||
return default_value |