/
telemetry.py
162 lines (120 loc) ยท 3.92 KB
/
telemetry.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""Anonymous telemetry for Reflex."""
from __future__ import annotations
import multiprocessing
import platform
try:
from datetime import UTC, datetime
except ImportError:
from datetime import datetime
UTC = None
import httpx
import psutil
from reflex import constants
from reflex.utils import console
from reflex.utils.exec import should_skip_compile
from reflex.utils.prerequisites import ensure_reflex_installation_id, get_project_hash
POSTHOG_API_URL: str = "https://app.posthog.com/capture/"
def get_os() -> str:
"""Get the operating system.
Returns:
The operating system.
"""
return platform.system()
def get_python_version() -> str:
"""Get the Python version.
Returns:
The Python version.
"""
return platform.python_version()
def get_reflex_version() -> str:
"""Get the Reflex version.
Returns:
The Reflex version.
"""
return constants.Reflex.VERSION
def get_cpu_count() -> int:
"""Get the number of CPUs.
Returns:
The number of CPUs.
"""
return multiprocessing.cpu_count()
def get_memory() -> int:
"""Get the total memory in MB.
Returns:
The total memory in MB.
"""
try:
return psutil.virtual_memory().total >> 20
except ValueError: # needed to pass ubuntu test
return 0
def _raise_on_missing_project_hash() -> bool:
"""Check if an error should be raised when project hash is missing.
When running reflex with --backend-only, or doing database migration
operations, there is no requirement for a .web directory, so the reflex.json
file may not exist, and this should not be considered an error.
Returns:
False when compilation should be skipped (i.e. no .web directory is required).
Otherwise return True.
"""
if should_skip_compile():
return False
return True
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=_raise_on_missing_project_hash())
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 {}
if UTC is None:
# for python 3.8, 3.9 & 3.10
stamp = datetime.utcnow().isoformat()
else:
# for python 3.11 & 3.12
stamp = datetime.now(UTC).isoformat()
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": stamp,
}
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:
"""Send anonymous telemetry for Reflex.
Args:
event: The event name.
telemetry_enabled: Whether to send the telemetry (If None, get from config).
Returns:
Whether the telemetry was sent successfully.
"""
from reflex.config import get_config
# Get the telemetry_enabled from the config if it is not specified.
if telemetry_enabled is None:
telemetry_enabled = get_config().telemetry_enabled
# Return if telemetry is disabled.
if not telemetry_enabled:
return False
event_data = _prepare_event(event)
if not event_data:
return False
return _send_event(event_data)