| |
| """ |
| Generate sample logs for FastVLM Screen Observer |
| This script creates realistic NDJSON logs with various analysis results |
| """ |
|
|
| import json |
| import requests |
| import time |
| from datetime import datetime |
| from PIL import Image, ImageDraw, ImageFont |
| import io |
| import base64 |
| import os |
|
|
| API_BASE = "http://localhost:8000" |
| LOGS_DIR = "logs" |
| SAMPLE_LOGS_FILE = "logs/sample_logs.ndjson" |
|
|
| def ensure_directories(): |
| """Ensure logs directory exists""" |
| os.makedirs(LOGS_DIR, exist_ok=True) |
| os.makedirs(f"{LOGS_DIR}/frames", exist_ok=True) |
|
|
| def create_test_image(scenario="default"): |
| """Create different test images for various scenarios""" |
| |
| if scenario == "login": |
| |
| img = Image.new('RGB', (1920, 1080), color='#f0f0f0') |
| draw = ImageDraw.Draw(img) |
| |
| |
| draw.rectangle([660, 340, 1260, 740], fill='white', outline='#ddd') |
| draw.text((880, 380), "Login to System", fill='#333') |
| |
| |
| draw.rectangle([760, 460, 1160, 510], fill='white', outline='#999') |
| draw.text((770, 475), "Username", fill='#666') |
| |
| |
| draw.rectangle([760, 530, 1160, 580], fill='white', outline='#999') |
| draw.text((770, 545), "••••••••", fill='#666') |
| |
| |
| draw.rectangle([760, 620, 1160, 680], fill='#2196F3', outline='#1976D2') |
| draw.text((920, 640), "Sign In", fill='white') |
| |
| description = "Login form with username and password fields" |
| |
| elif scenario == "dashboard": |
| |
| img = Image.new('RGB', (1920, 1080), color='white') |
| draw = ImageDraw.Draw(img) |
| |
| |
| draw.rectangle([0, 0, 1920, 80], fill='#333') |
| draw.text((50, 30), "Analytics Dashboard", fill='white') |
| |
| |
| colors = ['#4CAF50', '#2196F3', '#FF9800', '#F44336'] |
| titles = ['Users', 'Revenue', 'Orders', 'Alerts'] |
| values = ['1,234', '$45,678', '89', '3'] |
| |
| for i, (color, title, value) in enumerate(zip(colors, titles, values)): |
| x = 100 + i * 450 |
| draw.rectangle([x, 150, x+400, 300], fill=color) |
| draw.text((x+20, 170), title, fill='white') |
| draw.text((x+20, 220), value, fill='white') |
| |
| |
| draw.rectangle([100, 350, 900, 750], fill='#fafafa', outline='#ddd') |
| draw.text((450, 540), "Chart Area", fill='#999') |
| |
| |
| draw.rectangle([1000, 350, 1820, 750], fill='#fafafa', outline='#ddd') |
| draw.text((1350, 380), "Recent Activity", fill='#333') |
| |
| description = "Analytics dashboard with charts and statistics" |
| |
| elif scenario == "code_editor": |
| |
| img = Image.new('RGB', (1920, 1080), color='#1e1e1e') |
| draw = ImageDraw.Draw(img) |
| |
| |
| draw.rectangle([0, 0, 1920, 40], fill='#2d2d2d') |
| draw.text((20, 12), "main.py", fill='white') |
| draw.text((120, 12), "utils.py", fill='#888') |
| |
| |
| for i in range(1, 30): |
| draw.text((20, 50 + i*25), str(i), fill='#666') |
| |
| |
| code_lines = [ |
| "def process_data(input_file):", |
| " '''Process input data file'''", |
| " with open(input_file, 'r') as f:", |
| " data = json.load(f)", |
| " ", |
| " results = []", |
| " for item in data:", |
| " processed = transform(item)", |
| " results.append(processed)", |
| " ", |
| " return results", |
| "", |
| "def transform(item):", |
| " '''Transform single data item'''", |
| " return {", |
| " 'id': item.get('id'),", |
| " 'value': item.get('value') * 2,", |
| " 'timestamp': datetime.now()", |
| " }" |
| ] |
| |
| for i, line in enumerate(code_lines): |
| draw.text((70, 75 + i*25), line, fill='#d4d4d4') |
| |
| |
| draw.rectangle([1700, 40, 1920, 1080], fill='#252525') |
| draw.text((1720, 60), "Explorer", fill='white') |
| |
| description = "Code editor showing Python script" |
| |
| elif scenario == "sensitive": |
| |
| img = Image.new('RGB', (1920, 1080), color='white') |
| draw = ImageDraw.Draw(img) |
| |
| |
| draw.rectangle([0, 0, 1920, 60], fill='#FFF3CD') |
| draw.text((50, 20), "⚠️ Sensitive Information - Handle with Care", fill='#856404') |
| |
| |
| draw.rectangle([100, 150, 700, 350], fill='#f8f9fa', outline='#dc3545') |
| draw.text((120, 170), "Payment Information", fill='#dc3545') |
| draw.text((120, 220), "Card Number: **** **** **** 1234", fill='#333') |
| draw.text((120, 260), "CVV: ***", fill='#333') |
| draw.text((120, 300), "Expiry: 12/25", fill='#333') |
| |
| |
| draw.rectangle([800, 150, 1400, 350], fill='#f8f9fa', outline='#dc3545') |
| draw.text((820, 170), "Personal Details", fill='#dc3545') |
| draw.text((820, 220), "SSN: ***-**-6789", fill='#333') |
| draw.text((820, 260), "DOB: 01/15/1990", fill='#333') |
| |
| |
| draw.rectangle([100, 450, 1400, 600], fill='#fff5f5', outline='#dc3545') |
| draw.text((120, 470), "API Configuration", fill='#dc3545') |
| draw.text((120, 520), "API_KEY=sk-...REDACTED", fill='#666') |
| draw.text((120, 560), "SECRET=sec_...REDACTED", fill='#666') |
| |
| description = "Screen containing sensitive financial and personal information" |
| |
| else: |
| |
| img = Image.new('RGB', (1280, 720), color='white') |
| draw = ImageDraw.Draw(img) |
| |
| |
| draw.rectangle([0, 0, 1280, 60], fill='#4a90e2') |
| draw.text((20, 20), "Application Window", fill='white') |
| |
| |
| draw.rectangle([100, 100, 250, 150], fill='#5cb85c') |
| draw.text((150, 115), "Save", fill='white') |
| |
| draw.rectangle([300, 100, 450, 150], fill='#f0ad4e') |
| draw.text((340, 115), "Cancel", fill='white') |
| |
| |
| draw.rectangle([100, 200, 1180, 500], fill='#f5f5f5', outline='#ddd') |
| draw.text((120, 220), "Sample text content here", fill='#333') |
| |
| description = "Generic application window with buttons" |
| |
| return img, description |
|
|
| def generate_sample_logs(): |
| """Generate various sample log entries""" |
| |
| print("Generating sample logs...") |
| ensure_directories() |
| |
| scenarios = [ |
| ("default", "Generic application"), |
| ("login", "Login screen"), |
| ("dashboard", "Analytics dashboard"), |
| ("code_editor", "Code editor"), |
| ("sensitive", "Sensitive data screen") |
| ] |
| |
| logs = [] |
| |
| |
| try: |
| response = requests.get(f"{API_BASE}/model/status") |
| model_status = response.json() |
| print(f"Model Status: {model_status['model_type']} on {model_status['device']}") |
| except Exception as e: |
| print(f"Warning: API not responding: {e}") |
| print("Generating mock logs instead...") |
| model_status = {"model_type": "mock", "device": "cpu"} |
| |
| |
| for scenario_type, scenario_name in scenarios: |
| print(f"\nProcessing scenario: {scenario_name}") |
| |
| |
| img, description = create_test_image(scenario_type) |
| |
| |
| buffered = io.BytesIO() |
| img.save(buffered, format="PNG") |
| img_base64 = base64.b64encode(buffered.getvalue()).decode() |
| |
| |
| frame_id = f"frame_{int(time.time() * 1000)}" |
| timestamp = datetime.now().isoformat() |
| |
| |
| logs.append({ |
| "timestamp": timestamp, |
| "type": "frame_capture", |
| "frame_id": frame_id, |
| "scenario": scenario_name, |
| "has_thumbnail": True |
| }) |
| |
| |
| try: |
| response = requests.post( |
| f"{API_BASE}/analyze", |
| json={ |
| "image_data": f"data:image/png;base64,{img_base64}", |
| "include_thumbnail": True |
| }, |
| timeout=10 |
| ) |
| |
| if response.status_code == 200: |
| result = response.json() |
| analysis_log = { |
| "timestamp": datetime.now().isoformat(), |
| "type": "analysis", |
| "frame_id": frame_id, |
| "scenario": scenario_name, |
| "summary": result.get("summary", description), |
| "ui_elements": result.get("ui_elements", []), |
| "text_snippets": result.get("text_snippets", []), |
| "risk_flags": result.get("risk_flags", []) |
| } |
| else: |
| raise Exception(f"API returned {response.status_code}") |
| |
| except Exception as e: |
| print(f" API analysis failed: {e}, using mock data") |
| |
| analysis_log = generate_mock_analysis(scenario_type, frame_id, description) |
| |
| logs.append(analysis_log) |
| |
| |
| if scenario_type in ["login", "dashboard"]: |
| logs.append({ |
| "timestamp": datetime.now().isoformat(), |
| "type": "automation", |
| "frame_id": frame_id, |
| "action": "click" if scenario_type == "login" else "scroll", |
| "target": "button#submit" if scenario_type == "login" else "div.chart-container", |
| "success": True |
| }) |
| |
| |
| time.sleep(0.5) |
| |
| |
| with open(SAMPLE_LOGS_FILE, 'w') as f: |
| for log in logs: |
| f.write(json.dumps(log) + '\n') |
| |
| print(f"\n✅ Sample logs generated: {SAMPLE_LOGS_FILE}") |
| print(f" Total entries: {len(logs)}") |
| |
| |
| pretty_file = SAMPLE_LOGS_FILE.replace('.ndjson', '_pretty.json') |
| with open(pretty_file, 'w') as f: |
| json.dump(logs, f, indent=2) |
| print(f" Pretty version: {pretty_file}") |
| |
| return logs |
|
|
| def generate_mock_analysis(scenario_type, frame_id, description): |
| """Generate mock analysis data for when API is not available""" |
| |
| mock_data = { |
| "default": { |
| "ui_elements": [ |
| {"type": "button", "text": "Save", "position": {"x": 150, "y": 115}}, |
| {"type": "button", "text": "Cancel", "position": {"x": 340, "y": 115}}, |
| {"type": "textarea", "text": "Text input area", "position": {"x": 640, "y": 350}} |
| ], |
| "text_snippets": ["Application Window", "Save", "Cancel", "Sample text content here"], |
| "risk_flags": [] |
| }, |
| "login": { |
| "ui_elements": [ |
| {"type": "input", "text": "Username field", "position": {"x": 960, "y": 485}}, |
| {"type": "input", "text": "Password field", "position": {"x": 960, "y": 555}}, |
| {"type": "button", "text": "Sign In", "position": {"x": 960, "y": 650}} |
| ], |
| "text_snippets": ["Login to System", "Username", "Sign In"], |
| "risk_flags": ["AUTH_FORM", "PASSWORD_FIELD"] |
| }, |
| "dashboard": { |
| "ui_elements": [ |
| {"type": "card", "text": "Users: 1,234", "position": {"x": 300, "y": 225}}, |
| {"type": "card", "text": "Revenue: $45,678", "position": {"x": 750, "y": 225}}, |
| {"type": "chart", "text": "Chart Area", "position": {"x": 500, "y": 550}}, |
| {"type": "table", "text": "Recent Activity", "position": {"x": 1410, "y": 550}} |
| ], |
| "text_snippets": ["Analytics Dashboard", "Users", "Revenue", "Orders", "Alerts"], |
| "risk_flags": [] |
| }, |
| "code_editor": { |
| "ui_elements": [ |
| {"type": "tab", "text": "main.py", "position": {"x": 60, "y": 20}}, |
| {"type": "editor", "text": "Code editor", "position": {"x": 960, "y": 540}}, |
| {"type": "sidebar", "text": "Explorer", "position": {"x": 1810, "y": 560}} |
| ], |
| "text_snippets": ["def process_data", "json.load", "transform", "return results"], |
| "risk_flags": ["SOURCE_CODE"] |
| }, |
| "sensitive": { |
| "ui_elements": [ |
| {"type": "warning", "text": "Sensitive Information", "position": {"x": 960, "y": 30}}, |
| {"type": "form", "text": "Payment Information", "position": {"x": 400, "y": 250}}, |
| {"type": "form", "text": "Personal Details", "position": {"x": 1100, "y": 250}} |
| ], |
| "text_snippets": ["Card Number: ****", "SSN: ***", "API_KEY=", "SECRET="], |
| "risk_flags": ["SENSITIVE_DATA", "CREDIT_CARD", "PII", "API_KEYS", "HIGH_RISK"] |
| } |
| } |
| |
| data = mock_data.get(scenario_type, mock_data["default"]) |
| |
| return { |
| "timestamp": datetime.now().isoformat(), |
| "type": "analysis", |
| "frame_id": frame_id, |
| "scenario": scenario_type, |
| "summary": f"[MOCK] {description}", |
| "ui_elements": data["ui_elements"], |
| "text_snippets": data["text_snippets"], |
| "risk_flags": data["risk_flags"], |
| "mock_mode": True |
| } |
|
|
| if __name__ == "__main__": |
| try: |
| generate_sample_logs() |
| except KeyboardInterrupt: |
| print("\n\nGeneration interrupted by user") |
| except Exception as e: |
| print(f"\n❌ Error: {e}") |
| import traceback |
| traceback.print_exc() |