mirror of
https://github.com/bckelley/tconnectsync.git
synced 2026-08-24 03:34:12 -05:00
226 lines
6.4 KiB
Python
Executable File
226 lines
6.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Sync event definitions from Tandem's official webapp and regenerate event classes.
|
|
|
|
Extracts the complete events.json from the JSON.parse() statement embedded in
|
|
Tandem's reports module JavaScript, updates local events.json, and regenerates
|
|
events.py with all event class definitions.
|
|
|
|
Usage:
|
|
python3 scripts/sync_tandem_events.py [--output FILE] <URL>
|
|
|
|
Example:
|
|
python3 scripts/sync_tandem_events.py \\
|
|
https://modules.us.tandemdiabetes.com/webapp/modules/reports-module/v1.8.0/2451.97042bc1.chunk.js
|
|
"""
|
|
|
|
import sys
|
|
import json
|
|
import subprocess
|
|
import requests
|
|
from pathlib import Path
|
|
|
|
|
|
DEFAULT_EVENTS_FILE = "tconnectsync/eventparser/events.json"
|
|
DEFAULT_GENERATOR = "build_events.py"
|
|
|
|
|
|
def fetch_module(url):
|
|
"""Fetch the minified JavaScript module from Tandem."""
|
|
print(f"Fetching Tandem module...", file=sys.stderr)
|
|
response = requests.get(url, timeout=30)
|
|
response.raise_for_status()
|
|
content = response.text
|
|
print(f"Fetched {len(content):,} bytes", file=sys.stderr)
|
|
return content
|
|
|
|
|
|
def extract_events_json_from_parse(js_content):
|
|
"""
|
|
Extract the complete events.json from the JSON.parse() statement.
|
|
|
|
Finds: JSON.parse('{"events":{...}}')
|
|
And returns the parsed events dictionary.
|
|
"""
|
|
start = js_content.find("JSON.parse('")
|
|
if start < 0:
|
|
return None
|
|
|
|
start += len("JSON.parse('")
|
|
|
|
# Find the matching closing brace
|
|
brace_count = 0
|
|
end = start
|
|
escape_next = False
|
|
|
|
for i in range(start, len(js_content)):
|
|
char = js_content[i]
|
|
|
|
if escape_next:
|
|
escape_next = False
|
|
continue
|
|
|
|
if char == '\\':
|
|
escape_next = True
|
|
continue
|
|
|
|
if char == '{':
|
|
brace_count += 1
|
|
elif char == '}':
|
|
brace_count -= 1
|
|
if brace_count == 0:
|
|
end = i + 1
|
|
break
|
|
|
|
if brace_count != 0:
|
|
return None
|
|
|
|
json_str = js_content[start:end]
|
|
|
|
# Unescape the string
|
|
json_str = json_str.replace('\\"', '"')
|
|
|
|
try:
|
|
return json.loads(json_str)
|
|
except json.JSONDecodeError:
|
|
return None
|
|
|
|
|
|
def load_existing_events(filepath):
|
|
"""Load the existing events.json file."""
|
|
if not Path(filepath).exists():
|
|
return {"events": {}}
|
|
|
|
try:
|
|
with open(filepath, 'r') as f:
|
|
data = json.load(f)
|
|
print(f"Loaded {len(data.get('events', {}))} existing events", file=sys.stderr)
|
|
return data
|
|
except Exception as e:
|
|
print(f"Warning: Could not load existing events.json: {e}", file=sys.stderr)
|
|
return {"events": {}}
|
|
|
|
|
|
def merge_events(existing_data, extracted_data):
|
|
"""
|
|
Merge extracted events with existing events.
|
|
|
|
Keeps all existing events and updates/adds with extracted ones.
|
|
"""
|
|
existing = existing_data.get('events', {})
|
|
extracted = extracted_data.get('events', {})
|
|
|
|
before_count = len(existing)
|
|
|
|
# Add/update extracted events
|
|
existing.update(extracted)
|
|
|
|
after_count = len(existing)
|
|
added = after_count - before_count
|
|
|
|
if added > 0:
|
|
print(f"Added {added} new events from Tandem module", file=sys.stderr)
|
|
else:
|
|
print(f"Updated {len(extracted)} events from Tandem module", file=sys.stderr)
|
|
|
|
return existing_data
|
|
|
|
|
|
def write_events_file(filepath, data):
|
|
"""Write events.json with proper formatting."""
|
|
if 'events' in data:
|
|
data['events'] = {
|
|
k: data['events'][k]
|
|
for k in sorted(data['events'].keys(), key=lambda x: int(x))
|
|
}
|
|
|
|
filepath = Path(filepath)
|
|
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(filepath, 'w') as f:
|
|
json.dump(data, f, indent=2)
|
|
|
|
print(f"Wrote {len(data.get('events', {}))} events to {filepath}", file=sys.stderr)
|
|
|
|
|
|
def regenerate_events_py(events_json_path, generator_name):
|
|
"""Regenerate events.py from updated events.json."""
|
|
events_dir = Path(events_json_path).parent
|
|
generator_path = events_dir / generator_name
|
|
|
|
if not generator_path.exists():
|
|
print(f"⚠ Generator not found at {generator_path}", file=sys.stderr)
|
|
return False
|
|
|
|
print(f"Regenerating events.py...", file=sys.stderr)
|
|
|
|
try:
|
|
result = subprocess.run(
|
|
[sys.executable, generator_name],
|
|
cwd=str(events_dir),
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=30
|
|
)
|
|
|
|
if result.returncode == 0:
|
|
# Write generated code to events.py
|
|
events_py = events_dir / 'events.py'
|
|
events_py.write_text(result.stdout)
|
|
print(f"✓ events.py regenerated ({len(result.stdout):,} bytes)", file=sys.stderr)
|
|
return True
|
|
else:
|
|
print(f"✗ Generator failed: {result.stderr}", file=sys.stderr)
|
|
return False
|
|
|
|
except subprocess.TimeoutExpired:
|
|
print("✗ Generator timed out", file=sys.stderr)
|
|
return False
|
|
except Exception as e:
|
|
print(f"✗ Error running generator: {e}", file=sys.stderr)
|
|
return False
|
|
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Sync event definitions from Tandem and regenerate events.py'
|
|
)
|
|
parser.add_argument('url', help='URL to Tandem JavaScript module')
|
|
parser.add_argument('--output', default=DEFAULT_EVENTS_FILE, help='Output events.json path')
|
|
parser.add_argument('--no-generate', action='store_true', help='Skip events.py regeneration')
|
|
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
js_content = fetch_module(args.url)
|
|
|
|
extracted_data = extract_events_json_from_parse(js_content)
|
|
if not extracted_data:
|
|
print("✗ Could not extract events.json from Tandem module", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
extracted_events = extracted_data.get('events', {})
|
|
print(f"✓ Extracted {len(extracted_events)} events from Tandem module", file=sys.stderr)
|
|
|
|
existing_data = load_existing_events(args.output)
|
|
merged_data = merge_events(existing_data, extracted_data)
|
|
write_events_file(args.output, merged_data)
|
|
|
|
if not args.no_generate:
|
|
regenerate_events_py(args.output, DEFAULT_GENERATOR)
|
|
|
|
print(f"\n✓ Done", file=sys.stderr)
|
|
|
|
except requests.exceptions.RequestException as e:
|
|
print(f"✗ Network error: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"✗ Error: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|