Initial commit: SportsTime trip planning app
- Three-scenario planning engine (A: date range, B: selected games, C: directional routes) - GeographicRouteExplorer with anchor game support for route exploration - Shared ItineraryBuilder for travel segment calculation - TravelEstimator for driving time/distance estimation - SwiftUI views for trip creation and detail display - CloudKit integration for schedule data - Python scraping scripts for sports schedules 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
61
Scripts/test_cloudkit.py
Normal file
61
Scripts/test_cloudkit.py
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Quick test to query CloudKit records."""
|
||||
|
||||
import json, hashlib, base64, requests, os, sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
try:
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
except ImportError:
|
||||
sys.exit("Error: pip install cryptography")
|
||||
|
||||
CONTAINER = "iCloud.com.sportstime.app"
|
||||
HOST = "https://api.apple-cloudkit.com"
|
||||
|
||||
def sign(key_data, date, body, path):
|
||||
key = serialization.load_pem_private_key(key_data, None, default_backend())
|
||||
body_hash = base64.b64encode(hashlib.sha256(body.encode()).digest()).decode()
|
||||
sig = key.sign(f"{date}:{body_hash}:{path}".encode(), ec.ECDSA(hashes.SHA256()))
|
||||
return base64.b64encode(sig).decode()
|
||||
|
||||
def query(key_id, key_data, record_type, env='development'):
|
||||
path = f"/database/1/{CONTAINER}/{env}/public/records/query"
|
||||
body = json.dumps({
|
||||
'query': {'recordType': record_type},
|
||||
'resultsLimit': 10
|
||||
})
|
||||
date = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Apple-CloudKit-Request-KeyID': key_id,
|
||||
'X-Apple-CloudKit-Request-ISO8601Date': date,
|
||||
'X-Apple-CloudKit-Request-SignatureV1': sign(key_data, date, body, path),
|
||||
}
|
||||
r = requests.post(f"{HOST}{path}", headers=headers, data=body, timeout=30)
|
||||
return r.status_code, r.json()
|
||||
|
||||
if __name__ == '__main__':
|
||||
key_id = os.environ.get('CLOUDKIT_KEY_ID') or (sys.argv[1] if len(sys.argv) > 1 else None)
|
||||
key_file = os.environ.get('CLOUDKIT_KEY_FILE') or (sys.argv[2] if len(sys.argv) > 2 else 'eckey.pem')
|
||||
|
||||
if not key_id:
|
||||
sys.exit("Usage: python test_cloudkit.py KEY_ID [KEY_FILE]")
|
||||
|
||||
key_data = open(key_file, 'rb').read()
|
||||
|
||||
print("Testing CloudKit connection...\n")
|
||||
|
||||
for record_type in ['Stadium', 'Team', 'Game']:
|
||||
status, result = query(key_id, key_data, record_type)
|
||||
count = len(result.get('records', []))
|
||||
print(f"{record_type}: status={status}, records={count}")
|
||||
if count > 0:
|
||||
print(f" Sample: {result['records'][0].get('recordName', 'N/A')}")
|
||||
if 'serverErrorCode' in result:
|
||||
print(f" Error: {result.get('serverErrorCode')}: {result.get('reason')}")
|
||||
|
||||
print("\nFull response for Stadium query:")
|
||||
status, result = query(key_id, key_data, 'Stadium')
|
||||
print(json.dumps(result, indent=2)[:1000])
|
||||
Reference in New Issue
Block a user