GSoumyajit2005 commited on
Commit
e8efc4f
·
1 Parent(s): 65fe9aa

chore: Add scaffolded database layer (not yet implemented)

Browse files

- database.py: PostgreSQL connection setup (TODO)
- models.py: SQLModel schema for Invoice/LineItem (TODO)
- repository.py: CRUD operations placeholder (TODO)

Scaffolded for future human-in-the-loop persistence feature.

Files changed (3) hide show
  1. src/database.py +33 -0
  2. src/models.py +50 -0
  3. src/repository.py +41 -0
src/database.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/database.py
2
+
3
+ from sqlmodel import SQLModel, create_engine, Session
4
+ from typing import Generator
5
+ import os
6
+
7
+ # --- INSTRUCTIONS ---
8
+ # 1. Get credentials from environment variables (Os.getenv)
9
+ # 2. Construct the DATABASE_URL string: postgresql://user:pass@host:port/db
10
+ # 3. Create the SQLModel engine
11
+ # 4. Implement the init_db and get_session functions
12
+
13
+ # TODO: Define constants for DB params (POSTGRES_USER, etc.)
14
+
15
+ # TODO: Define DATABASE_URL
16
+
17
+ # TODO: Create 'engine' using create_engine(DATABASE_URL)
18
+
19
+ def init_db():
20
+ """
21
+ Idempotent DB initialization.
22
+ Should create all tables defined in SQLModel metadata.
23
+ """
24
+ # TODO: Call SQLModel.metadata.create_all(engine)
25
+ pass
26
+
27
+ def get_session() -> Generator[Session, None, None]:
28
+ """
29
+ Dependency for yielding a database session.
30
+ Useful for FastAPI dependencies or context usage.
31
+ """
32
+ # TODO: Open a session with the engine, yield it, and ensure it closes
33
+ pass
src/models.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/models.py
2
+
3
+ from typing import List, Optional
4
+ from datetime import date as DateType
5
+ from decimal import Decimal
6
+ from sqlmodel import SQLModel, Field, Relationship
7
+
8
+ # --- INSTRUCTIONS ---
9
+ # SQLModel classes should mirror the Pydantic models in src/schema.py
10
+ # but with database-specific configurations (primary keys, foreign keys).
11
+
12
+ class Invoice(SQLModel, table=True):
13
+ __tablename__ = "invoices"
14
+
15
+ # TODO: Define Primary Key 'id' (int, optional, default=None)
16
+
17
+ # TODO: Add Data Fields
18
+ # - receipt_number (str, indexed)
19
+ # - date (DateType)
20
+ # - total_amount (Decimal, max_digits=10, decimal_places=2)
21
+ # - vendor (str)
22
+ # - address (str)
23
+ # - semantic_hash (str, unique, indexed) -> Critical for deduplication
24
+
25
+ # TODO: Add Metadata Fields
26
+ # - validation_status (str)
27
+ # - validation_errors (str) -> Store as JSON string since we don't need to query inside it yet
28
+ # - created_at (DateType) -> Default to today
29
+
30
+ # TODO: Define relationship to LineItem (One-to-Many)
31
+ # items: List["LineItem"] = Relationship(...)
32
+ pass
33
+
34
+
35
+ class LineItem(SQLModel, table=True):
36
+ __tablename__ = "line_items"
37
+
38
+ # TODO: Define Primary Key
39
+
40
+ # TODO: Define Foreign Key 'invoice_id' linking to 'invoices.id'
41
+
42
+ # TODO: Add Data Fields
43
+ # - description (str)
44
+ # - quantity (int)
45
+ # - unit_price (Decimal)
46
+ # - total (Decimal)
47
+
48
+ # TODO: Define relationship back to Invoice
49
+ # invoice: Optional[Invoice] = Relationship(...)
50
+ pass
src/repository.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # src/repository.py
2
+
3
+ from sqlmodel import Session, select
4
+ from typing import Dict, Any, Optional
5
+ import json
6
+
7
+ from src.models import Invoice, LineItem
8
+ from src.database import get_session, engine
9
+
10
+ class InvoiceRepository:
11
+ def __init__(self, session: Session = None):
12
+ """
13
+ Initialize with an optional session.
14
+ Allows dependency injection for testing or API usage.
15
+ """
16
+ self.session = session
17
+
18
+ def save_invoice(self, invoice_data: Dict[str, Any]) -> Invoice:
19
+ """
20
+ Saves an invoice and its line items to the database.
21
+
22
+ Steps to implement:
23
+ 1. Manage Session: If self.session is None, create a new one using 'engine'.
24
+ 2. Clean Data: Separate 'items' list from the main invoice properties.
25
+ 3. Create Invoice: Instantiate the Invoice SQLModel.
26
+ 4. Deserialize Complex Types: e.g. 'validation_errors' list -> JSON string.
27
+ 5. Process Items: Iterate 'items', create LineItem models, check keys match, and append to invoice.items.
28
+ 6. Commit: Add to session, commit, and refresh.
29
+ 7. Error Handling: Wrap in try/except to rollback on failure.
30
+ """
31
+ # TODO: Implementation
32
+ raise NotImplementedError("Implement the save logic.")
33
+
34
+ def get_by_hash(self, semantic_hash: str) -> Optional[Invoice]:
35
+ """
36
+ Check if invoice already exists using the semantic hash.
37
+ """
38
+ # TODO: Create session if needed
39
+ # TODO: Execute SELECT statement filtering by hash
40
+ # TODO: Return first result or None
41
+ raise NotImplementedError("Implement the query logic.")