#!/usr/bin/env python3 """ Truth Serum Script - Database Auditor (STRICT READ-ONLY MODE) Executes raw SQL JOIN queries to show EXACTLY what is in the database for the user tester_pro@profibot.hu. This script MUST NOT modify any data - it's read-only. """ import asyncio import asyncpg import os from datetime import datetime async def get_truth_serum(): """ Fetch complete map for tester_pro@profibot.hu: - User/Person: email and person_id - Organizations: Every Organization linked to this person_id (Private and Corporate) - Branches (Telephely): Every Branch linked to those Organizations - Assets (Járművek): Every Asset linked to those Branches """ # Use the correct connection string for asyncpg DATABASE_URL = "postgresql://service_finder_app:AppSafePass_2026@db:5432/service_finder" print("=== TRUTH SERUM DATABASE AUDIT ===") print(f"Timestamp: {datetime.now().isoformat()}") print("User: tester_pro@profibot.hu") print("Mode: STRICT READ-ONLY (no modifications)") print("=" * 60) conn = await asyncpg.connect(DATABASE_URL) try: # 1. First, get the person_id for the user print("\n1. Finding user tester_pro@profibot.hu...") person = await conn.fetchrow(""" SELECT id, email, first_name, last_name FROM identity.persons WHERE email = 'tester_pro@profibot.hu' """) if not person: print(" ❌ ERROR: User tester_pro@profibot.hu not found in identity.persons!") return person_id = person['id'] print(f" ✓ Found user: ID={person_id}, Email={person['email']}") print(f" Name: {person['first_name']} {person['last_name']}") # 2. Get all organizations for this person print("\n2. Fetching organizations linked to this person...") organizations = await conn.fetch(""" SELECT id as org_id, full_name as org_name, org_type, owner_id, created_at FROM fleet.organizations WHERE owner_id = $1 ORDER BY id """, person_id) print(f" ✓ Found {len(organizations)} organizations") if not organizations: print(" ⚠️ No organizations found for this user") return org_ids = [org['org_id'] for org in organizations] # 3. Get all branches for these organizations print("\n3. Fetching branches for these organizations...") branches = await conn.fetch(""" SELECT b.id as branch_id, b.name as branch_name, b.organization_id, b.created_at FROM fleet.branches b WHERE b.organization_id = ANY($1::int[]) ORDER BY b.organization_id, b.id """, org_ids) print(f" ✓ Found {len(branches)} branches") branch_ids = [branch['branch_id'] for branch in branches] # 4. Get all assets for these branches print("\n4. Fetching assets (vehicles) for these branches...") assets = await conn.fetch(""" SELECT a.id as asset_id, a.license_plate, a.branch_id, a.make, a.model, a.status, a.created_at FROM data.assets a WHERE a.branch_id = ANY($1::int[]) ORDER BY a.branch_id, a.id """, branch_ids) print(f" ✓ Found {len(assets)} assets") # 5. Now create the comprehensive JOIN for the final table print("\n5. Generating comprehensive JOIN map...") comprehensive_data = await conn.fetch(""" SELECT p.email as "User Email", o.full_name as "Organization Name", b.name as "Branch Name", a.license_plate as "License Plate", o.org_type as "Org Type", o.id as org_id, b.id as branch_id, a.id as asset_id FROM identity.persons p LEFT JOIN fleet.organizations o ON o.owner_id = p.id LEFT JOIN fleet.branches b ON b.organization_id = o.id LEFT JOIN data.assets a ON a.branch_id = b.id WHERE p.email = 'tester_pro@profibot.hu' ORDER BY o.id, b.id, a.id """) # 6. Print summary statistics print("\n" + "=" * 60) print("SUMMARY STATISTICS:") print(f" • User: {person['email']} (ID: {person_id})") print(f" • Organizations: {len(organizations)}") print(f" • Branches: {len(branches)}") print(f" • Assets (Vehicles): {len(assets)}") print(f" • Comprehensive JOIN rows: {len(comprehensive_data)}") print("=" * 60) # 7. Print the clean Markdown table print("\n" + "#" * 80) print("# TRUTH SERUM DATABASE MAP") print("# User: tester_pro@profibot.hu") print("# Generated at: " + datetime.now().isoformat()) print("#" * 80 + "\n") if comprehensive_data: print("| User Email | Organization Name | Branch Name | License Plate |") print("|------------|-------------------|-------------|---------------|") for row in comprehensive_data: # Handle None values org_name = row['Organization Name'] or "(No Organization)" branch_name = row['Branch Name'] or "(No Branch)" license_plate = row['License Plate'] or "(No License Plate)" print(f"| {row['User Email']} | {org_name} | {branch_name} | {license_plate} |") print(f"\nTotal rows: {len(comprehensive_data)}") else: print("⚠️ No data found in comprehensive JOIN.") # 8. Print detailed breakdown for debugging print("\n" + "=" * 60) print("DETAILED BREAKDOWN:") for org in organizations: print(f"\nOrganization: {org['org_name']} (ID: {org['org_id']}, Type: {org['org_type']})") org_branches = [b for b in branches if b['organization_id'] == org['org_id']] if not org_branches: print(" No branches") continue for branch in org_branches: print(f" └─ Branch: {branch['branch_name']} (ID: {branch['branch_id']})") branch_assets = [a for a in assets if a['branch_id'] == branch['branch_id']] if not branch_assets: print(" No assets") else: for asset in branch_assets: print(f" └─ Asset: {asset['license_plate']} (ID: {asset['asset_id']}, Make: {asset['make']}, Model: {asset['model']})") print("\n" + "=" * 60) print("AUDIT COMPLETE - NO DATA MODIFIED") print("=" * 60) except Exception as e: print(f"\n❌ ERROR during audit: {e}") import traceback traceback.print_exc() finally: await conn.close() if __name__ == "__main__": asyncio.run(get_truth_serum())