File size: 6,208 Bytes
46dd0dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ebb80d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46dd0dc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# database.py β€” SQLite database: connection, seeding, and queries

import json
import sqlite3
import os
from seed_data import CUSTOMERS, TRANSACTIONS

DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "neobank.db")


def get_connection() -> sqlite3.Connection:
    """Get a SQLite connection with row factory for dict-like access."""
    conn = sqlite3.connect(DB_PATH, check_same_thread=False)
    conn.row_factory = sqlite3.Row
    return conn


def init_database(conn: sqlite3.Connection):
    """Create tables and seed data. Safe to call on every app start."""
    cur = conn.cursor()

    # ── Create tables ──
    cur.execute("""
        CREATE TABLE IF NOT EXISTS customers (
            user_id     TEXT PRIMARY KEY,
            name        TEXT,
            tier        TEXT,
            balance     REAL,
            status      TEXT,
            email       TEXT,
            phone       TEXT,
            dob         TEXT,
            address     TEXT,
            account_opened TEXT,
            kyc_status  TEXT,
            fraud_flags TEXT
        )
    """)

    cur.execute("""
        CREATE TABLE IF NOT EXISTS transactions (
            id          INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id     TEXT,
            date        TEXT,
            type        TEXT,
            amount      REAL,
            description TEXT,
            recipient   TEXT,
            status      TEXT,
            FOREIGN KEY (user_id) REFERENCES customers(user_id)
        )
    """)

    # ── Seed customers (upsert) ──
    for c in CUSTOMERS:
        cur.execute(
            """INSERT INTO customers
               (user_id, name, tier, balance, status, email, phone, dob,
                address, account_opened, kyc_status, fraud_flags)
               VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
               ON CONFLICT(user_id) DO UPDATE SET
                 name=excluded.name, tier=excluded.tier, balance=excluded.balance,
                 status=excluded.status, email=excluded.email, phone=excluded.phone,
                 dob=excluded.dob, address=excluded.address,
                 account_opened=excluded.account_opened, kyc_status=excluded.kyc_status,
                 fraud_flags=excluded.fraud_flags
            """,
            (
                c["user_id"], c["name"], c["tier"], c["balance"],
                c["status"], c["email"], c["phone"], c["dob"],
                c["address"], c["account_opened"], c["kyc_status"],
                json.dumps(c.get("fraud_flags", [])),
            ),
        )

    # ── Seed transactions (only if table is empty) ──
    count = cur.execute("SELECT COUNT(*) FROM transactions").fetchone()[0]
    if count == 0:
        for t in TRANSACTIONS:
            cur.execute(
                """INSERT INTO transactions
                   (user_id, date, type, amount, description, recipient, status)
                   VALUES (?, ?, ?, ?, ?, ?, ?)""",
                (
                    t["user_id"], t["date"], t["type"], t["amount"],
                    t.get("description"), t.get("recipient"), t["status"],
                ),
            )

    conn.commit()


def get_customer(conn: sqlite3.Connection, user_id: str) -> dict | None:
    """Retrieve a single customer by user_id."""
    row = conn.execute(
        "SELECT * FROM customers WHERE user_id = ?", (user_id,)
    ).fetchone()
    if row is None:
        return None
    d = dict(row)
    d["fraud_flags"] = json.loads(d.get("fraud_flags") or "[]")
    return d


def get_transactions(conn: sqlite3.Connection, user_id: str) -> list[dict]:
    """Retrieve all transactions for a user_id, newest first."""
    rows = conn.execute(
        "SELECT * FROM transactions WHERE user_id = ? ORDER BY date DESC",
        (user_id,),
    ).fetchall()
    return [dict(r) for r in rows]


def authenticate_user(conn: sqlite3.Connection, user_id: str, name: str) -> dict | None:
    """Authenticate a user by matching user_id and name (case-insensitive)."""
    row = conn.execute(
        "SELECT * FROM customers WHERE user_id = ? AND LOWER(name) = LOWER(?)",
        (user_id, name),
    ).fetchone()
    if row is None:
        return None
    d = dict(row)
    d["fraud_flags"] = json.loads(d.get("fraud_flags") or "[]")
    return d


def authenticate_by_name(conn: sqlite3.Connection, name: str) -> dict | None:
    """Authenticate a user by name only (case-insensitive).

    The user_id and tier are looked up from the database and returned, so the
    login form only needs to ask for the customer's name. No schema change β€”
    the underlying records are unchanged.
    """
    row = conn.execute(
        "SELECT * FROM customers WHERE LOWER(name) = LOWER(?)",
        (name.strip(),),
    ).fetchone()
    if row is None:
        return None
    d = dict(row)
    d["fraud_flags"] = json.loads(d.get("fraud_flags") or "[]")
    return d


def format_account_details(customer: dict, transactions: list[dict]) -> str:
    """Format customer account details and transactions into a readable string."""
    if not customer:
        return "Account not found."

    lines = [
        f"Account Details for {customer['name']}",
        f"  User ID: {customer['user_id']}",
        f"  Tier: {customer['tier']}",
        f"  Balance: ${customer['balance']:,.2f}",
        f"  Status: {customer['status']}",
        f"  Email: {customer['email']}",
        f"  Phone: {customer['phone']}",
        f"  Account Opened: {customer['account_opened']}",
        f"  KYC Status: {customer['kyc_status']}",
    ]

    flags = customer.get("fraud_flags", [])
    if flags:
        lines.append(f"  Fraud Flags: {', '.join(flags)}")

    if transactions:
        lines.append("\nRecent Transactions:")
        for txn in transactions:
            amount_str = f"${txn['amount']:,.2f}" if txn["amount"] else "β€”"
            desc = txn.get("description") or ""
            recipient = txn.get("recipient")
            detail = f"{recipient} ({desc})" if recipient else desc
            lines.append(
                f"  [{txn['date']}] {txn['type']} | {amount_str} | {detail} | {txn['status']}"
            )

    return "\n".join(lines)