Skip to content

Commit 03d9009

Browse files
committed
sqlAlchemy change
1 parent 204b0cf commit 03d9009

8 files changed

Lines changed: 743 additions & 565 deletions

File tree

CLAUDE.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,22 @@ docker compose up --build
4545
3. Upsert into `request_ai_report` keyed by `search_request_id`.
4646
4. **Best-effort:** any Mistral or DB failure inside this step is logged but does not fail the search request — articles are still loaded and the request still ends `success`. Set `AI_SUMMARY_ENABLED=false` to skip entirely.
4747

48-
**Schema creation lives in `src/db.py`** as `create_*_table()` functions. `main.py:init_all_tables` orchestrates them and `_ensure_runtime_schema` validates required tables exist before each run. When adding a new table:
49-
1. Add `create_X_table()` in `src/db.py`
50-
2. Re-export it from `src/__init__.py`
51-
3. Call it from `init_all_tables` in `main.py`
52-
4. Add the table name to `required_tables` in `_ensure_runtime_schema`
48+
**Database layer is SQLAlchemy 2.x ORM** on top of psycopg2-binary (driver only — no direct `import psycopg2` in the codebase). Engine cache + session factory live in `src/db.py`. Declarative models live in `src/models.py` (one class per table). Use `from src.db import get_session` for read/write work; use `engine_for(db_name)` for raw `text()` DDL (triggers, ALTER migrations, CREATE DATABASE).
49+
50+
**Schema creation lives in `src/db.py`** as `create_*_table()` functions that call `Base.metadata.tables["X"].create(engine, checkfirst=True)`. Triggers and idempotent migrations are run as raw `text()` afterwards. `main.py:init_all_tables` orchestrates them and `_ensure_runtime_schema` validates required tables exist before each run. When adding a new table:
51+
1. Define the model in `src/models.py`
52+
2. Add `create_X_table()` in `src/db.py` (call `_create_table("table_name")`; add trigger/migration DDL via raw `text()` if needed)
53+
3. Re-export it from `src/__init__.py`
54+
4. Call it from `init_all_tables` in `main.py`
55+
5. Add the table name to `required_tables` in `_ensure_runtime_schema`
5356

5457
## Non-obvious behaviors / gotchas
5558

5659
- **`pyproject.toml` must be BOM-free.** A UTF-8 BOM at the start breaks pytest's TOML parser silently with a misleading error. New TOML files: write without BOM.
57-
- **`db.py` has special handling for non-UTF8 libpq errors** on localized Windows installs (`_decode_non_utf8_error` tries cp1251/cp866). Do not replace it with a naive `str(exc)` — it will surface mojibake instead of a real error message.
60+
- **`db.py` has special handling for non-UTF8 libpq errors** on localized Windows installs (`_decode_non_utf8_error` tries cp1251/cp866, wrapped around `engine.connect()` / `sessionmaker()`). Do not replace it with a naive `str(exc)` — it will surface mojibake instead of a real error message. SQLAlchemy passes the underlying psycopg2 `UnicodeDecodeError` through unwrapped, so the `try/except UnicodeDecodeError` in `_connect` / `get_session` is what catches it.
5861
- **`NEWS_DB` vs `DB_NEWS` mismatch:** `config/config.py` reads `NEWS_DB`, but `docker-compose.yml` references `${DB_NEWS:-...}`. Known inconsistency — don't "fix" it without verifying the deploy story.
5962
- **`docker-compose.yml` overrides `DB_HOST: localhost` for the app container**, which doesn't reach the `db` service. Pre-existing — don't change without confirming.
60-
- **`request_ai_report.promt_version`** is misspelled (missing 'p'). Schema column is `promt_version`; the Python field is `prompt_version`. The mapping happens in `load_ai_report`. Don't rename either side without coordinating.
63+
- **`request_ai_report.promt_version`** is misspelled (missing 'p'). Schema column is `promt_version`; the Python field is `prompt_version`. The mapping happens in `src/models.py` (`mapped_column("promt_version", ...)`). In `pg_insert(RequestAiReport).values(...)` and `stmt.excluded.*` you must use the **DB column name** (`promt_version`), not the Python attr (`prompt_version`). Don't rename either side without coordinating.
6164
- **Debug pipeline rewrites disk on every page** with timestamped filenames — the user inspects these manually. Don't add cleanup logic.
6265
- **Tests in `tests/test_pipeline.py` monkeypatch `pipeline.make_extract_web` etc.** Stubs must accept the `news_api_key` kwarg even if unused, because the pipeline always passes it.
6366

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
psycopg2-binary>=2.9,<3.0
33
requests==2.32.5
44
cryptography>=42.0,<46.0
5-
pydantic>=2.0,<3.0
5+
pydantic>=2.0,<3.0
6+
sqlalchemy>=2.0.41,<2.1

src/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
ensure_databases_exists,
1515
ensure_tables_exist,
1616
fetch_articles_for_search_request,
17-
get_connection,
18-
get_cursor,
17+
get_session,
1918
init_database,
2019
search_request_belongs_to_user,
2120
search_request_exists,
@@ -43,8 +42,7 @@
4342
"ensure_databases_exists",
4443
"ensure_tables_exist",
4544
"fetch_articles_for_search_request",
46-
"get_connection",
47-
"get_cursor",
45+
"get_session",
4846
"init_database",
4947
"load_ai_report",
5048
"load_failed_ai_report",

0 commit comments

Comments
 (0)