diff --git a/.github/workflows/integration_tests_wsl.yml b/.github/workflows/integration_tests_wsl.yml index bcc36feb9..0021618bf 100644 --- a/.github/workflows/integration_tests_wsl.yml +++ b/.github/workflows/integration_tests_wsl.yml @@ -61,23 +61,28 @@ jobs: working-directory: ./reflex-examples/counter shell: wsl-bash {0} run: | + export TELEMETRY_ENABLED=false poetry run reflex export --backend-only - name: Check run --backend-only before init for counter example shell: wsl-bash {0} run: | + export TELEMETRY_ENABLED=false dos2unix scripts/integration.sh poetry run bash scripts/integration.sh ./reflex-examples/counter dev 8001 --backend-only --backend-port 8001 - name: Init Website for counter example working-directory: ./reflex-examples/counter shell: wsl-bash {0} run: | + export TELEMETRY_ENABLED=false poetry run reflex init --loglevel debug - name: Check export for counter example working-directory: ./reflex-examples/counter shell: wsl-bash {0} run: | + export TELEMETRY_ENABLED=false poetry run reflex export --frontend-only --loglevel debug - name: Run Website and Check for errors shell: wsl-bash {0} run: | + export TELEMETRY_ENABLED=false poetry run bash scripts/integration.sh ./reflex-examples/counter dev diff --git a/reflex/utils/prerequisites.py b/reflex/utils/prerequisites.py index faa202174..2656ffe39 100644 --- a/reflex/utils/prerequisites.py +++ b/reflex/utils/prerequisites.py @@ -434,19 +434,21 @@ def initialize_app_directory(app_name: str, template: constants.Templates.Kind): ) -def get_project_hash() -> int | None: +def get_project_hash(raise_on_fail: bool = False) -> int | None: """Get the project hash from the reflex.json file if the file exists. + Args: + raise_on_fail: Whether to raise an error if the file does not exist. + Returns: project_hash: The app hash. """ - if not os.path.exists(constants.Reflex.JSON): + if not os.path.exists(constants.Reflex.JSON) and not raise_on_fail: return None # Open and read the file with open(constants.Reflex.JSON, "r") as file: data = json.load(file) - project_hash = data["project_hash"] - return project_hash + return data["project_hash"] def initialize_web_directory(): diff --git a/reflex/utils/telemetry.py b/reflex/utils/telemetry.py index 347123e4e..84080d1ee 100644 --- a/reflex/utils/telemetry.py +++ b/reflex/utils/telemetry.py @@ -2,15 +2,16 @@ from __future__ import annotations -import json import multiprocessing import platform from datetime import datetime +import httpx import psutil from reflex import constants -from reflex.utils.prerequisites import ensure_reflex_installation_id +from reflex.utils import console +from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash POSTHOG_API_URL: str = "https://app.posthog.com/capture/" @@ -57,7 +58,52 @@ def get_memory() -> int: Returns: The total memory in MB. """ - return psutil.virtual_memory().total >> 20 + try: + return psutil.virtual_memory().total >> 20 + except ValueError: # needed to pass ubuntu test + return 0 + + +def _prepare_event(event: str) -> dict: + """Prepare the event to be sent to the PostHog server. + + Args: + event: The event name. + + Returns: + The event data. + """ + installation_id = ensure_reflex_installation_id() + project_hash = get_project_hash(raise_on_fail=True) + + if installation_id is None or project_hash is None: + console.debug( + f"Could not get installation_id or project_hash: {installation_id}, {project_hash}" + ) + return {} + + return { + "api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb", + "event": event, + "properties": { + "distinct_id": installation_id, + "distinct_app_id": project_hash, + "user_os": get_os(), + "reflex_version": get_reflex_version(), + "python_version": get_python_version(), + "cpu_count": get_cpu_count(), + "memory": get_memory(), + }, + "timestamp": datetime.utcnow().isoformat(), + } + + +def _send_event(event_data: dict) -> bool: + try: + httpx.post(POSTHOG_API_URL, json=event_data) + return True + except Exception: + return False def send(event: str, telemetry_enabled: bool | None = None) -> bool: @@ -70,8 +116,6 @@ def send(event: str, telemetry_enabled: bool | None = None) -> bool: Returns: Whether the telemetry was sent successfully. """ - import httpx - from reflex.config import get_config # Get the telemetry_enabled from the config if it is not specified. @@ -82,29 +126,8 @@ def send(event: str, telemetry_enabled: bool | None = None) -> bool: if not telemetry_enabled: return False - installation_id = ensure_reflex_installation_id() - if installation_id is None: + event_data = _prepare_event(event) + if not event_data: return False - try: - with open(constants.Dirs.REFLEX_JSON) as f: - reflex_json = json.load(f) - project_hash = reflex_json["project_hash"] - post_hog = { - "api_key": "phc_JoMo0fOyi0GQAooY3UyO9k0hebGkMyFJrrCw1Gt5SGb", - "event": event, - "properties": { - "distinct_id": installation_id, - "distinct_app_id": project_hash, - "user_os": get_os(), - "reflex_version": get_reflex_version(), - "python_version": get_python_version(), - "cpu_count": get_cpu_count(), - "memory": get_memory(), - }, - "timestamp": datetime.utcnow().isoformat(), - } - httpx.post(POSTHOG_API_URL, json=post_hog) - return True - except Exception: - return False + return _send_event(event_data) diff --git a/tests/test_telemetry.py b/tests/test_telemetry.py index 4ba9ec8c7..2cbbe0ee8 100644 --- a/tests/test_telemetry.py +++ b/tests/test_telemetry.py @@ -1,3 +1,5 @@ +import httpx +import pytest from packaging.version import parse as parse_python_version from reflex.utils import telemetry @@ -28,3 +30,23 @@ def test_telemetry(): def test_disable(): """Test that disabling telemetry works.""" assert not telemetry.send("test", telemetry_enabled=False) + + +@pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"]) +def test_send(mocker, event): + mocker.patch("httpx.post") + mocker.patch( + "builtins.open", + mocker.mock_open( + read_data='{"project_hash": "78285505863498957834586115958872998605"}' + ), + ) + + telemetry.send(event, telemetry_enabled=True) + httpx.post.assert_called_once() + if telemetry.get_os() == "Windows": + open.assert_called_with(".web\\reflex.json", "r") + elif telemetry.get_os() == "Linux": + open.assert_called_with("/proc/meminfo", "rb", buffering=32768) + else: + open.assert_called_with(".web/reflex.json", "r")