Skip to content

Commit

Permalink
update telemetry to opt-in
Browse files Browse the repository at this point in the history
  • Loading branch information
valentinsulzer committed Oct 3, 2024
1 parent 025b019 commit a41d3ab
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 37 deletions.
4 changes: 2 additions & 2 deletions docs/source/user_guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ glob:

# Telemetry

PyBaMM collects anonymous usage data to help improve the library. This telemetry is enabled by default but can be easily disabled. Here's what you need to know:
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_OPTOUT_TELEMETRY=true` or use `pybamm.telemetry.disable()` in your code.
- **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.
1 change: 0 additions & 1 deletion examples/scripts/compare_lithium_ion.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import pybamm

pybamm.set_logging_level("INFO")
pybamm.telemetry.disable()

# load models
models = [
Expand Down
5 changes: 0 additions & 5 deletions scripts/post_install.py

This file was deleted.

3 changes: 3 additions & 0 deletions src/pybamm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,6 @@
"version",
"pybamm_data",
]


pybamm.config.generate()
46 changes: 40 additions & 6 deletions src/pybamm/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import platformdirs
from pathlib import Path
import pybamm


def is_running_tests(): # pragma: no cover
Expand Down Expand Up @@ -33,6 +34,34 @@ def is_running_tests(): # pragma: no cover
return any(runner in sys.argv[0].lower() for runner in test_runners)


def ask_user_opt_in():
"""
Ask the user if they want to opt in to telemetry.
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"
)
while True:
user_input = input("Do you want to enable telemetry? (Y/n): ").strip().lower()
if user_input in ["yes", "y"]:
return True
elif user_input in ["no", "n"]:
return False
else:
print("\nInvalid input. Please enter 'yes'/'y' or 'no'/'n'.")


def generate(): # pragma: no cover
if is_running_tests():
return
Expand All @@ -41,26 +70,31 @@ def generate(): # pragma: no cover
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)
write_uuid_to_file(config_file, opt_in)

if opt_in:
pybamm.telemetry.capture("user-opted-in")


def read(): # pragma: no cover
config_file = Path(platformdirs.user_config_dir("pybamm")) / "config.yml"
return read_uuid_from_file(config_file)


def write_uuid_to_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)

# Generate a UUID
unique_id = uuid.uuid4()

# Write the UUID to the config file in YAML format
with open(config_file, "w") as f:
f.write("pybamm:\n")
f.write(f" uuid: {unique_id}\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):
Expand Down
15 changes: 5 additions & 10 deletions src/pybamm/telemetry.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ def disable():
_posthog.disabled = True


_opt_out = os.getenv("PYBAMM_OPTOUT_TELEMETRY", "false").lower()
_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():
if pybamm.config.is_running_tests() or _posthog.disabled:
return

properties = {
Expand All @@ -31,11 +31,6 @@ def capture(event): # pragma: no cover

config = pybamm.config.read()
if config:
user_id = config["uuid"]
else:
user_id = "anonymous-user-id"
properties["$process_person_profile"] = False

# setting $process_person_profile to False mean that we only track what events are
# being run and don't capture anything about the user
_posthog.capture(user_id, event, properties=properties)
if config["enable_telemetry"]:
user_id = config["uuid"]
_posthog.capture(user_id, event, properties=properties)
38 changes: 25 additions & 13 deletions tests/unit/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,38 @@


class TestConfig:
def test_write_read_uuid(self, tmp_path):
@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)
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
uuid_dict = pybamm.config.read_uuid_from_file(config_file)

config_dict = pybamm.config.read_uuid_from_file(config_file)
# Check that the UUID was read successfully
assert uuid_dict is not None
assert "uuid" in uuid_dict

# Verify that the UUID is valid

try:
uuid.UUID(uuid_dict["uuid"])
except ValueError:
pytest.fail("Invalid UUID format")
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

def test_ask_user_opt_in(self, monkeypatch):
# Mock the input function to always return "y"
monkeypatch.setattr("builtins.input", lambda _: "y")

# Call the function to ask the user if they want to opt in
opt_in = pybamm.config.ask_user_opt_in()

# Check that the function returns True
assert opt_in is True

0 comments on commit a41d3ab

Please sign in to comment.