-
-
Notifications
You must be signed in to change notification settings - Fork 553
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4441 from pybamm-team/telemetry
Add telemetry
- Loading branch information
Showing
13 changed files
with
389 additions
and
6 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 |
---|---|---|
|
@@ -46,7 +46,6 @@ input/* | |
|
||
# simulation outputs | ||
out/ | ||
config.py | ||
matplotlibrc | ||
*.pickle | ||
*.sav | ||
|
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
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
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
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
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
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
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
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
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,163 @@ | ||
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 |
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
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,36 @@ | ||
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", | ||
) | ||
|
||
|
||
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) |
Oops, something went wrong.