mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-25 05:58:50 -06:00
- Bump version to 4.5.1 - Refactor version retrieval: make get_version_from_setup() public and add multiple path fallbacks for better reliability in production and development environments - Optimize task listing performance: * Replace joinedload with selectinload to avoid cartesian product issues * Implement optimized pagination that avoids expensive count queries when possible * Move AJAX request check earlier to skip unnecessary filter data loading * Add query limits to filter dropdowns (projects: 500, users: 200) * Optimize permission checks by checking is_admin first (no DB query) - Update API info endpoint and context processor to use centralized version retrieval - Maintain backward compatibility with _get_version_from_setup alias
166 lines
5.5 KiB
Python
166 lines
5.5 KiB
Python
"""
|
|
Analytics configuration for TimeTracker.
|
|
|
|
These values are embedded at build time and cannot be overridden by users.
|
|
This allows collecting anonymized usage metrics from all installations
|
|
to improve the product while respecting user privacy.
|
|
|
|
Key Privacy Protections:
|
|
- Telemetry is OPT-IN (disabled by default)
|
|
- No personally identifiable information is ever collected
|
|
- Users can disable telemetry at any time via admin dashboard
|
|
- All tracked events are documented and transparent
|
|
|
|
DO NOT commit actual keys to this file - they are injected at build time only.
|
|
"""
|
|
|
|
# PostHog Configuration
|
|
# Replaced by GitHub Actions: POSTHOG_API_KEY_PLACEHOLDER
|
|
POSTHOG_API_KEY_DEFAULT = "%%POSTHOG_API_KEY_PLACEHOLDER%%"
|
|
POSTHOG_HOST_DEFAULT = "https://us.i.posthog.com"
|
|
|
|
# Sentry Configuration
|
|
# Replaced by GitHub Actions: SENTRY_DSN_PLACEHOLDER
|
|
SENTRY_DSN_DEFAULT = "%%SENTRY_DSN_PLACEHOLDER%%"
|
|
SENTRY_TRACES_RATE_DEFAULT = "0.1"
|
|
|
|
# Telemetry Configuration
|
|
# All builds have analytics configured, but telemetry is OPT-IN
|
|
TELE_ENABLED_DEFAULT = "false" # Disabled by default for privacy
|
|
|
|
|
|
def get_version_from_setup():
|
|
"""
|
|
Get the application version from setup.py.
|
|
|
|
setup.py is the SINGLE SOURCE OF TRUTH for version information.
|
|
This function reads setup.py at runtime to get the current version.
|
|
All other code should reference this function, not define versions themselves.
|
|
|
|
This function tries multiple paths to find setup.py to work correctly
|
|
in both production and development modes.
|
|
|
|
Returns:
|
|
str: Application version (e.g., "4.5.0") or "unknown" if setup.py can't be read
|
|
"""
|
|
import os
|
|
import re
|
|
|
|
# Try multiple possible paths to setup.py
|
|
possible_paths = []
|
|
|
|
# Path 1: Relative to this file (app/config/analytics_defaults.py -> setup.py)
|
|
try:
|
|
base_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
|
possible_paths.append(os.path.join(base_path, "setup.py"))
|
|
except Exception:
|
|
pass
|
|
|
|
# Path 2: Current working directory
|
|
try:
|
|
possible_paths.append(os.path.join(os.getcwd(), "setup.py"))
|
|
except Exception:
|
|
pass
|
|
|
|
# Path 3: From environment variable (if set)
|
|
try:
|
|
project_root = os.getenv("PROJECT_ROOT") or os.getenv("APP_ROOT")
|
|
if project_root:
|
|
possible_paths.append(os.path.join(project_root, "setup.py"))
|
|
except Exception:
|
|
pass
|
|
|
|
# Path 4: Try to find setup.py by walking up from current file
|
|
try:
|
|
current = os.path.dirname(__file__)
|
|
for _ in range(5): # Max 5 levels up
|
|
current = os.path.dirname(current)
|
|
setup_path = os.path.join(current, "setup.py")
|
|
if os.path.exists(setup_path):
|
|
possible_paths.append(setup_path)
|
|
break
|
|
except Exception:
|
|
pass
|
|
|
|
# Try each path until we find setup.py
|
|
for setup_path in possible_paths:
|
|
try:
|
|
if os.path.exists(setup_path):
|
|
# Read setup.py
|
|
with open(setup_path, "r", encoding="utf-8") as f:
|
|
content = f.read()
|
|
|
|
# Extract version using regex
|
|
# Matches: version='X.Y.Z' or version="X.Y.Z"
|
|
version_match = re.search(r'version\s*=\s*[\'"]([^\'"]+)[\'"]', content)
|
|
|
|
if version_match:
|
|
return version_match.group(1)
|
|
except Exception:
|
|
continue
|
|
|
|
# Fallback version if setup.py can't be read
|
|
# This is the ONLY place besides setup.py where version is defined
|
|
return "unknown"
|
|
|
|
|
|
# Keep the old function name for backward compatibility
|
|
_get_version_from_setup = get_version_from_setup
|
|
|
|
|
|
def get_analytics_config():
|
|
"""
|
|
Get analytics configuration.
|
|
|
|
Analytics keys are embedded at build time and cannot be overridden
|
|
to ensure consistent telemetry collection across all installations.
|
|
|
|
However, users maintain full control:
|
|
- Telemetry is OPT-IN (disabled by default)
|
|
- Can be disabled anytime in admin dashboard
|
|
- No PII is ever collected
|
|
|
|
Returns:
|
|
dict: Analytics configuration
|
|
"""
|
|
|
|
# Helper to check if a value is a placeholder (not replaced by GitHub Actions)
|
|
def is_placeholder(value):
|
|
return value.startswith("%%") and value.endswith("%%")
|
|
|
|
# PostHog configuration - use embedded keys (no override)
|
|
posthog_api_key = POSTHOG_API_KEY_DEFAULT if not is_placeholder(POSTHOG_API_KEY_DEFAULT) else ""
|
|
|
|
# Sentry configuration - use embedded keys (no override)
|
|
sentry_dsn = SENTRY_DSN_DEFAULT if not is_placeholder(SENTRY_DSN_DEFAULT) else ""
|
|
|
|
# App version - read from setup.py at runtime
|
|
app_version = get_version_from_setup()
|
|
|
|
# Note: Environment variables are NOT checked for keys to prevent override
|
|
# Users control telemetry via the opt-in/opt-out toggle in admin dashboard
|
|
|
|
return {
|
|
"posthog_api_key": posthog_api_key,
|
|
"posthog_host": POSTHOG_HOST_DEFAULT, # Fixed host, no override
|
|
"sentry_dsn": sentry_dsn,
|
|
"sentry_traces_rate": float(SENTRY_TRACES_RATE_DEFAULT), # Fixed rate, no override
|
|
"app_version": app_version,
|
|
"telemetry_enabled_default": False, # Always opt-in
|
|
}
|
|
|
|
|
|
def has_analytics_configured():
|
|
"""
|
|
Check if analytics keys are configured (embedded at build time).
|
|
|
|
Returns:
|
|
bool: True if analytics keys are embedded
|
|
"""
|
|
|
|
def is_placeholder(value):
|
|
return value.startswith("%%") and value.endswith("%%")
|
|
|
|
# Check if keys have been replaced during build
|
|
return not is_placeholder(POSTHOG_API_KEY_DEFAULT)
|