196 lines
7.3 KiB
Python
196 lines
7.3 KiB
Python
#!/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()) |