From d717da62a0350e8200ba92fd3bb3f37fcf4e98a4 Mon Sep 17 00:00:00 2001 From: kratman Date: Wed, 13 Nov 2024 22:09:28 -0500 Subject: [PATCH] Revert "Merge pull request #4441 from pybamm-team/telemetry" This reverts commit 0eae1a49c950bb8ac478c5e268378c458f88f37b, reversing changes made to 9ac0b5b410f9c9c89da042c1c00076b039e6cedf. --- .gitignore | 1 + .lycheeignore | 3 - CHANGELOG.md | 4 +- conftest.py | 5 - docs/source/user_guide/index.md | 9 - docs/source/user_guide/installation/index.rst | 1 - pyproject.toml | 1 - src/pybamm/__init__.py | 12 +- src/pybamm/config.py | 163 ------------------ src/pybamm/simulation.py | 3 - src/pybamm/telemetry.py | 38 ---- tests/unit/test_config.py | 157 ----------------- 12 files changed, 6 insertions(+), 391 deletions(-) delete mode 100644 src/pybamm/config.py delete mode 100644 src/pybamm/telemetry.py delete mode 100644 tests/unit/test_config.py diff --git a/.gitignore b/.gitignore index 7ffcbfb9bd..b678710922 100644 --- a/.gitignore +++ b/.gitignore @@ -46,6 +46,7 @@ input/* # simulation outputs out/ +config.py matplotlibrc *.pickle *.sav diff --git a/.lycheeignore b/.lycheeignore index 929fe36475..55a4a4c623 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -15,6 +15,3 @@ file:///home/runner/work/PyBaMM/PyBaMM/docs/source/user_guide/fundamentals/pybam # Errors in docs/source/user_guide/index.md file:///home/runner/work/PyBaMM/PyBaMM/docs/source/user_guide/api_docs - -# Telemetry -https://us.i.posthog.com diff --git a/CHANGELOG.md b/CHANGELOG.md index eee7de05d7..65d84301af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,11 @@ - Adds an option "voltage as a state" that can be "false" (default) or "true". If "true" adds an explicit algebraic equation for the voltage. ([#4507](https://github.com/pybamm-team/PyBaMM/pull/4507)) - Improved `QuickPlot` accuracy for simulations with Hermite interpolation. ([#4483](https://github.com/pybamm-team/PyBaMM/pull/4483)) - Added Hermite interpolation to the (`IDAKLUSolver`) that improves the accuracy and performance of post-processing variables. ([#4464](https://github.com/pybamm-team/PyBaMM/pull/4464)) -- Added basic telemetry to record which functions are being run. See [Telemetry section in the User Guide](https://docs.pybamm.org/en/latest/source/user_guide/index.html#telemetry) for more information. ([#4441](https://github.com/pybamm-team/PyBaMM/pull/4441)) - Added `BasicDFN` model for sodium-ion batteries ([#4451](https://github.com/pybamm-team/PyBaMM/pull/4451)) - Added sensitivity calculation support for `pybamm.Simulation` and `pybamm.Experiment` ([#4415](https://github.com/pybamm-team/PyBaMM/pull/4415)) - Added OpenMP parallelization to IDAKLU solver for lists of input parameters ([#4449](https://github.com/pybamm-team/PyBaMM/pull/4449)) -- Added phase-dependent particle options to LAM ([#4369](https://github.com/pybamm-team/PyBaMM/pull/4369)) +- Added phase-dependent particle options to LAM + ([#4369](https://github.com/pybamm-team/PyBaMM/pull/4369)) - Added a lithium ion equivalent circuit model with split open circuit voltages for each electrode (`SplitOCVR`). ([#4330](https://github.com/pybamm-team/PyBaMM/pull/4330)) - Added the `pybamm.DiscreteTimeSum` expression node to sum an expression over a sequence of data times, and accompanying `pybamm.DiscreteTimeData` class to store the data times and values ([#4501](https://github.com/pybamm-team/PyBaMM/pull/4501)) diff --git a/conftest.py b/conftest.py index 77513d56db..7ac6cf3c74 100644 --- a/conftest.py +++ b/conftest.py @@ -51,8 +51,3 @@ def set_random_seed(): @pytest.fixture(autouse=True) def set_debug_value(): pybamm.settings.debug_mode = True - - -@pytest.fixture(autouse=True) -def disable_telemetry(): - pybamm.telemetry.disable() diff --git a/docs/source/user_guide/index.md b/docs/source/user_guide/index.md index b497ed1a01..79e61936b2 100644 --- a/docs/source/user_guide/index.md +++ b/docs/source/user_guide/index.md @@ -73,12 +73,3 @@ glob: ../examples/notebooks/creating_models/5-half-cell-model.ipynb ../examples/notebooks/creating_models/6-a-simple-SEI-model.ipynb ``` - -# Telemetry - -PyBaMM optionally collects anonymous usage data to help improve the library. This telemetry is opt-in and can be easily disabled. Here's what you need to know: - -- **What is collected**: Basic usage information like PyBaMM version, Python version, and which functions are run. -- **Why**: To understand how PyBaMM is used and prioritize development efforts. -- **Opt-out**: To disable telemetry, set the environment variable `PYBAMM_DISABLE_TELEMETRY=true` (or any value other than `false`) or use `pybamm.telemetry.disable()` in your code. -- **Privacy**: No personal information (name, email, etc) or sensitive information (parameter values, simulation results, etc) is ever collected. diff --git a/docs/source/user_guide/installation/index.rst b/docs/source/user_guide/installation/index.rst index 9225f1ee98..18e62c5dfa 100644 --- a/docs/source/user_guide/installation/index.rst +++ b/docs/source/user_guide/installation/index.rst @@ -71,7 +71,6 @@ Package Minimum supp `typing-extensions `__ 4.10.0 `pandas `__ 1.5.0 `pooch `__ 1.8.1 -`posthog `__ 3.6.5 =================================================================== ========================== .. _install.optional_dependencies: diff --git a/pyproject.toml b/pyproject.toml index 68af974c1e..89e92a3001 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,7 +39,6 @@ dependencies = [ "typing-extensions>=4.10.0", "pandas>=1.5.0", "pooch>=1.8.1", - "posthog", ] [project.urls] diff --git a/src/pybamm/__init__.py b/src/pybamm/__init__.py index 794316c01b..3de52e5724 100644 --- a/src/pybamm/__init__.py +++ b/src/pybamm/__init__.py @@ -21,7 +21,6 @@ from .logger import logger, set_logging_level, get_new_logger from .settings import settings from .citations import Citations, citations, print_citations -from . import config # Classes for the Expression Tree from .expression_tree.symbol import * @@ -195,8 +194,8 @@ # Batch Study from .batch_study import BatchStudy -# Callbacks, telemetry, config -from . import callbacks, telemetry, config +# Callbacks +from . import callbacks # Pybamm Data manager using pooch from .pybamm_data import DataLoader @@ -205,14 +204,12 @@ import os import pathlib import sysconfig - -os.environ["CASADIPATH"] = str(pathlib.Path(sysconfig.get_path("purelib")) / "casadi") +os.environ["CASADIPATH"] = str(pathlib.Path(sysconfig.get_path('purelib')) / 'casadi') __all__ = [ "batch_study", "callbacks", "citations", - "config", "discretisations", "doc_utils", "experiment", @@ -228,11 +225,8 @@ "simulation", "solvers", "spatial_methods", - "telemetry", "type_definitions", "util", "version", "pybamm_data", ] - -#config.generate() diff --git a/src/pybamm/config.py b/src/pybamm/config.py deleted file mode 100644 index ba7171e5d2..0000000000 --- a/src/pybamm/config.py +++ /dev/null @@ -1,163 +0,0 @@ -import uuid -import os -import platformdirs -from pathlib import Path -import pybamm -import sys -import threading -import time - - -def is_running_tests(): # pragma: no cover - """ - Detect if the code is being run as part of a test suite or building docs with Sphinx. - - Returns: - bool: True if running tests or building docs, False otherwise. - """ - # Check if pytest or unittest is running - if any( - test_module in sys.modules for test_module in ["pytest", "unittest", "nose"] - ): - return True - - # Check for GitHub Actions environment variable - if "GITHUB_ACTIONS" in os.environ: - return True - - # Check for other common CI environment variables - ci_env_vars = ["CI", "TRAVIS", "CIRCLECI", "JENKINS_URL", "GITLAB_CI"] - if any(var in os.environ for var in ci_env_vars): - return True - - # Check for common test runner names in command-line arguments - test_runners = ["pytest", "unittest", "nose", "trial", "nox", "tox"] - if any(runner in arg.lower() for arg in sys.argv for runner in test_runners): - return True - - # Check if building docs with Sphinx - if any(mod == "sphinx" or mod.startswith("sphinx.") for mod in sys.modules): - print( - f"Found Sphinx module: {[mod for mod in sys.modules if mod.startswith('sphinx')]}" - ) - return True - - return False - - -def ask_user_opt_in(timeout=10): - """ - Ask the user if they want to opt in to telemetry. - - Parameters - ---------- - timeout : float, optional - The timeout for the user to respond to the prompt. Default is 10 seconds. - - Returns - ------- - bool - True if the user opts in, False otherwise. - """ - print( - "PyBaMM can collect usage data and send it to the PyBaMM team to " - "help us improve the software.\n" - "We do not collect any sensitive information such as models, parameters, " - "or simulation results - only information on which parts of the code are " - "being used and how frequently.\n" - "This is entirely optional and does not impact the functionality of PyBaMM.\n" - "For more information, see https://docs.pybamm.org/en/latest/source/user_guide/index.html#telemetry" - ) - - def get_input(): # pragma: no cover - try: - user_input = ( - input("Do you want to enable telemetry? (Y/n): ").strip().lower() - ) - answer.append(user_input) - except Exception: - # Handle any input errors - pass - - time_start = time.time() - - while True: - if time.time() - time_start > timeout: - print("\nTimeout reached. Defaulting to not enabling telemetry.") - return False - - answer = [] - # Create and start input thread - input_thread = threading.Thread(target=get_input) - input_thread.daemon = True - input_thread.start() - - # Wait for either timeout or input - input_thread.join(timeout) - - if answer: - if answer[0] in ["yes", "y", ""]: - print("\nTelemetry enabled.\n") - return True - elif answer[0] in ["no", "n"]: - print("\nTelemetry disabled.\n") - return False - else: - print("\nInvalid input. Please enter 'yes/y' for yes or 'no/n' for no.") - else: - print("\nTimeout reached. Defaulting to not enabling telemetry.") - return False - - -def generate(): - if is_running_tests(): - return - - # Check if the config file already exists - if read() is not None: - return - - # Ask the user if they want to opt in to telemetry - opt_in = ask_user_opt_in() - config_file = Path(platformdirs.user_config_dir("pybamm")) / "config.yml" - write_uuid_to_file(config_file, opt_in) - - if opt_in: - pybamm.telemetry.capture("user-opted-in") - - -def read(): - config_file = Path(platformdirs.user_config_dir("pybamm")) / "config.yml" - return read_uuid_from_file(config_file) - - -def write_uuid_to_file(config_file, opt_in): - # Create the directory if it doesn't exist - config_file.parent.mkdir(parents=True, exist_ok=True) - - # Write the UUID to the config file in YAML format - with open(config_file, "w") as f: - f.write("pybamm:\n") - f.write(f" enable_telemetry: {opt_in}\n") - if opt_in: - unique_id = uuid.uuid4() - f.write(f" uuid: {unique_id}\n") - - -def read_uuid_from_file(config_file): - # Check if the config file exists - if not config_file.exists(): - return None - - # Read the UUID from the config file - with open(config_file) as f: - content = f.read().strip() - - # Extract the UUID using YAML parsing - try: - import yaml - - config = yaml.safe_load(content) - return config["pybamm"] - except (yaml.YAMLError, ValueError): - return None diff --git a/src/pybamm/simulation.py b/src/pybamm/simulation.py index 75799d6334..ab22972741 100644 --- a/src/pybamm/simulation.py +++ b/src/pybamm/simulation.py @@ -10,7 +10,6 @@ import warnings from functools import lru_cache from datetime import timedelta -import pybamm.telemetry from pybamm.util import import_optional_dependency from pybamm.expression_tree.operations.serialise import Serialise @@ -451,8 +450,6 @@ def solve( Additional key-word arguments passed to `solver.solve`. See :meth:`pybamm.BaseSolver.solve`. """ - pybamm.telemetry.capture("simulation-solved") - # Setup if solver is None: solver = self._solver diff --git a/src/pybamm/telemetry.py b/src/pybamm/telemetry.py deleted file mode 100644 index b3319b9237..0000000000 --- a/src/pybamm/telemetry.py +++ /dev/null @@ -1,38 +0,0 @@ -from posthog import Posthog -import os -import pybamm -import sys - -_posthog = Posthog( - # this is the public, write only API key, so it's ok to include it here - project_api_key="phc_bLZKBW03XjgiRhbWnPsnKPr0iw0z03fA6ZZYjxgW7ej", - host="https://us.i.posthog.com", -) - -_posthog.log.setLevel("CRITICAL") - - -def disable(): - _posthog.disabled = True - - -_opt_out = os.getenv("PYBAMM_DISABLE_TELEMETRY", "false").lower() -if _opt_out != "false": # pragma: no cover - disable() - - -def capture(event): # pragma: no cover - # don't capture events in automated testing - if pybamm.config.is_running_tests() or _posthog.disabled: - return - - properties = { - "python_version": sys.version, - "pybamm_version": pybamm.__version__, - } - - config = pybamm.config.read() - if config: - if config["enable_telemetry"]: - user_id = config["uuid"] - _posthog.capture(user_id, event, properties=properties) diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py deleted file mode 100644 index 62906b348d..0000000000 --- a/tests/unit/test_config.py +++ /dev/null @@ -1,157 +0,0 @@ -import pytest -import select -import sys - -import pybamm -import uuid -from pathlib import Path -import platformdirs - - -class TestConfig: - @pytest.mark.parametrize("write_opt_in", [True, False]) - def test_write_read_uuid(self, tmp_path, write_opt_in): - # Create a temporary file path - config_file = tmp_path / "config.yml" - - # Call the function to write UUID to file - pybamm.config.write_uuid_to_file(config_file, write_opt_in) - - # Check that the file was created - assert config_file.exists() - - # Read the UUID using the read_uuid_from_file function - config_dict = pybamm.config.read_uuid_from_file(config_file) - # Check that the UUID was read successfully - if write_opt_in: - assert config_dict["enable_telemetry"] is True - assert "uuid" in config_dict - - # Verify that the UUID is valid - try: - uuid.UUID(config_dict["uuid"]) - except ValueError: - pytest.fail("Invalid UUID format") - else: - assert config_dict["enable_telemetry"] is False - - @pytest.mark.parametrize("user_opted_in, user_input", [(True, "y"), (False, "n")]) - def test_ask_user_opt_in(self, monkeypatch, capsys, user_opted_in, user_input): - # Mock select.select to simulate user input - def mock_select(*args, **kwargs): - return [sys.stdin], [], [] - - monkeypatch.setattr(select, "select", mock_select) - - # Mock sys.stdin.readline to return the desired input - monkeypatch.setattr(sys.stdin, "readline", lambda: user_input + "\n") - - # Call the function to ask the user if they want to opt in - opt_in = pybamm.config.ask_user_opt_in() - - # Check the result - assert opt_in is user_opted_in - - # Check that the prompt was printed - captured = capsys.readouterr() - assert "Do you want to enable telemetry? (Y/n):" in captured.out - - def test_ask_user_opt_in_invalid_input(self, monkeypatch, capsys): - # Mock select.select to simulate user input and then timeout - def mock_select(*args, **kwargs): - nonlocal call_count - if call_count == 0: - call_count += 1 - return [sys.stdin], [], [] - else: - return [], [], [] - - monkeypatch.setattr(select, "select", mock_select) - - # Mock sys.stdin.readline to return invalid input - monkeypatch.setattr(sys.stdin, "readline", lambda: "invalid\n") - - # Initialize call count - call_count = 0 - - # Call the function to ask the user if they want to opt in - opt_in = pybamm.config.ask_user_opt_in(timeout=1) - - # Check the result (should be False for timeout after invalid input) - assert opt_in is False - - # Check that the prompt, invalid input message, and timeout message were printed - captured = capsys.readouterr() - assert "Do you want to enable telemetry? (Y/n):" in captured.out - assert ( - "Invalid input. Please enter 'yes/y' for yes or 'no/n' for no." - in captured.out - ) - assert "Timeout reached. Defaulting to not enabling telemetry." in captured.out - - def test_ask_user_opt_in_timeout(self, monkeypatch, capsys): - # Mock select.select to simulate a timeout - def mock_select(*args, **kwargs): - return [], [], [] - - monkeypatch.setattr(select, "select", mock_select) - - # Call the function to ask the user if they want to opt in - opt_in = pybamm.config.ask_user_opt_in(timeout=1) - - # Check the result (should be False for timeout) - assert opt_in is False - - # Check that the prompt and timeout message were printed - captured = capsys.readouterr() - assert "Do you want to enable telemetry? (Y/n):" in captured.out - assert "Timeout reached. Defaulting to not enabling telemetry." in captured.out - - def test_generate_and_read(self, monkeypatch, tmp_path): - # Mock is_running_tests to return False - monkeypatch.setattr(pybamm.config, "is_running_tests", lambda: False) - - # Mock ask_user_opt_in to return True - monkeypatch.setattr(pybamm.config, "ask_user_opt_in", lambda: True) - - # Mock telemetry capture - capture_called = False - - def mock_capture(event): - nonlocal capture_called - assert event == "user-opted-in" - capture_called = True - - monkeypatch.setattr(pybamm.telemetry, "capture", mock_capture) - - # Mock config directory - monkeypatch.setattr(platformdirs, "user_config_dir", lambda x: str(tmp_path)) - - # Test generate() creates new config - pybamm.config.generate() - - # Verify config was created - config = pybamm.config.read() - assert config is not None - assert config["enable_telemetry"] is True - assert "uuid" in config - assert capture_called is True - - # Test generate() does nothing if config exists - capture_called = False - pybamm.config.generate() - assert capture_called is False - - def test_read_uuid_from_file_no_file(self): - config_dict = pybamm.config.read_uuid_from_file(Path("nonexistent_file.yml")) - assert config_dict is None - - def test_read_uuid_from_file_invalid_yaml(self, tmp_path): - # Create a temporary directory and file with invalid YAML content - invalid_yaml = tmp_path / "invalid_yaml.yml" - with open(invalid_yaml, "w") as f: - f.write("invalid: yaml: content:") - - config_dict = pybamm.config.read_uuid_from_file(invalid_yaml) - - assert config_dict is None