156 lines
5.9 KiB
Python
156 lines
5.9 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Seed script: Populate audit.financial_ledger with 20 dummy records.
|
|
Uses psycopg2 (sync) to bypass asyncpg prepared statement enum validation issues.
|
|
"""
|
|
import uuid
|
|
import random
|
|
import os
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
import psycopg2
|
|
|
|
TRANSACTION_TYPES = [
|
|
"SUBSCRIPTION", "COMMISSION", "REFUND", "MANUAL_ADJUSTMENT",
|
|
"PURCHASE", "WITHDRAWAL", "BONUS", "FEE", "REWARD",
|
|
]
|
|
|
|
DB_ENTRY_TYPES = ["CREDIT", "DEBIT"]
|
|
DB_WALLET_TYPES = ["EARNED", "PURCHASED", "SERVICE_COINS", "VOUCHER"]
|
|
DB_STATUSES = ["PENDING", "SUCCESS", "FAILED", "REFUNDED"]
|
|
|
|
TEST_USER_IDS = [1, 2, 28, 29, 55, 75, 79, 85, 86, 88, 100, 102, 103, 104, 105, 106]
|
|
|
|
NOW = datetime.now(timezone.utc)
|
|
|
|
|
|
def get_dsn_params():
|
|
"""Extract connection parameters from DATABASE_URL."""
|
|
db_url = os.environ.get("DATABASE_URL", "postgresql+asyncpg://service_finder_app:AppSafePass_2026@shared-postgres:5432/service_finder")
|
|
db_url = db_url.replace("postgresql+asyncpg://", "postgresql://")
|
|
# Parse URL
|
|
parts = db_url.replace("postgresql://", "").split("@")
|
|
user_pass = parts[0].split(":")
|
|
host_db = parts[1].split("/")
|
|
host_port = host_db[0].split(":")
|
|
return {
|
|
"user": user_pass[0],
|
|
"password": user_pass[1],
|
|
"host": host_port[0],
|
|
"port": int(host_port[1]) if len(host_port) > 1 else 5432,
|
|
"dbname": host_db[1].split("?")[0],
|
|
}
|
|
|
|
|
|
def seed():
|
|
params = get_dsn_params()
|
|
conn = psycopg2.connect(**params)
|
|
conn.autocommit = False
|
|
|
|
try:
|
|
with conn.cursor() as cur:
|
|
# Count existing
|
|
cur.execute("SELECT COUNT(*) FROM audit.financial_ledger")
|
|
print(f"Existing records: {cur.fetchone()[0]}")
|
|
|
|
# Get person_ids
|
|
person_map = {}
|
|
for uid in TEST_USER_IDS:
|
|
cur.execute("SELECT person_id FROM identity.users WHERE id = %s", (uid,))
|
|
pid = cur.fetchone()
|
|
if pid and pid[0]:
|
|
person_map[uid] = pid[0]
|
|
|
|
if not person_map:
|
|
print("No users with person_id found!")
|
|
return
|
|
|
|
print(f"Users with person records: {list(person_map.keys())}")
|
|
|
|
inserted = 0
|
|
for i in range(20):
|
|
uid = random.choice(list(person_map.keys()))
|
|
pid = person_map[uid]
|
|
entry_t = random.choice(DB_ENTRY_TYPES)
|
|
wallet_t = random.choice(DB_WALLET_TYPES)
|
|
stat = random.choice(DB_STATUSES)
|
|
txn_t = random.choice(TRANSACTION_TYPES)
|
|
cur_r = random.choice(["HUF", "EUR", "USD", "GBP"])
|
|
amt = round(random.uniform(5.0, 5000.0), 2)
|
|
days_ago = random.randint(0, 30)
|
|
created_ts = NOW - timedelta(
|
|
days=random.randint(0, days_ago),
|
|
hours=random.randint(0, 23),
|
|
minutes=random.randint(0, 59),
|
|
)
|
|
ref = "TXN-" + uuid.uuid4().hex[:8].upper()
|
|
desc = txn_t.lower().replace("_", " ").title() + f" for user {uid}"
|
|
net_amt = round(amt / 1.27, 2) if random.random() > 0.3 else None
|
|
tax_amt = round(amt - net_amt, 2) if net_amt else None
|
|
balance = round(random.uniform(100, 50000), 2)
|
|
inv_stat = "PAID" if stat == "completed" else stat.upper()
|
|
txn_id = str(uuid.uuid4())
|
|
|
|
# psycopg2 handles text → enum cast correctly with simple execute
|
|
cur.execute(
|
|
"""
|
|
INSERT INTO audit.financial_ledger
|
|
(user_id, person_id, amount, currency, transaction_type, details,
|
|
created_at, entry_type, balance_after, wallet_type, status,
|
|
transaction_id, net_amount, tax_amount, gross_amount, invoice_status)
|
|
VALUES (
|
|
%s, %s, %s, %s, %s,
|
|
%s::jsonb,
|
|
%s::timestamptz,
|
|
%s::audit.ledger_entry_type,
|
|
%s,
|
|
%s::audit.wallet_type,
|
|
%s::audit.ledger_status,
|
|
%s::uuid,
|
|
%s, %s, %s, %s
|
|
)
|
|
""",
|
|
(
|
|
uid, pid, amt, cur_r, txn_t,
|
|
f'{{"description": "{desc}", "reference": "{ref}"}}',
|
|
created_ts.isoformat(),
|
|
entry_t, balance, wallet_t, stat, txn_id,
|
|
net_amt, tax_amt, amt, inv_stat,
|
|
),
|
|
)
|
|
inserted += 1
|
|
|
|
conn.commit()
|
|
print(f"✅ Inserted {inserted} records successfully!")
|
|
|
|
cur.execute("SELECT COUNT(*) FROM audit.financial_ledger")
|
|
print(f"Total records now: {cur.fetchone()[0]}")
|
|
|
|
cur.execute("""
|
|
SELECT
|
|
fl.entry_type::text,
|
|
fl.wallet_type::text,
|
|
fl.status::text,
|
|
COUNT(*)::int,
|
|
ROUND(AVG(fl.amount)::numeric, 2),
|
|
u.email
|
|
FROM audit.financial_ledger fl
|
|
LEFT JOIN identity.users u ON u.id = fl.user_id
|
|
GROUP BY fl.entry_type, fl.wallet_type, fl.status, u.email
|
|
ORDER BY fl.entry_type, fl.wallet_type
|
|
""")
|
|
rows = cur.fetchall()
|
|
total_sum = sum(r[3] for r in rows) if rows else 0
|
|
print(f"\n📊 Summary (total {total_sum} records):")
|
|
print(f" {'Entry':8} {'Wallet':15} {'Status':10} {'Cnt':5} {'Avg Amt':10} {'User'}")
|
|
print(f" {'-'*8} {'-'*15} {'-'*10} {'-'*5} {'-'*10} {'-'*30}")
|
|
for r in rows:
|
|
print(f" {r[0]:8} {r[1]:15} {r[2]:10} {r[3]:5} {r[4]:10} {r[5] or 'N/A':30}")
|
|
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
seed()
|