From e4ca28658a32102069b5bb1b821ca0bb70b1871d Mon Sep 17 00:00:00 2001 From: Wei Zang Date: Thu, 2 Apr 2026 07:26:54 +0100 Subject: [PATCH] Add flag/hide columns and PATCH endpoint Bump package version and add DB migration to introduce boolean `flag` and `hide` columns on the `prospects` table. Include a SQL migration file and a small runner (app/api/prospects/database/run_migration.py) that executes the SQL using get_db_connection. Update prospects API: add ProspectUpdate Pydantic schema, import Body and HTTPException, and implement a PATCH /prospects/{id} endpoint to update `flag` and/or `hide` with validation, DB update, error handling and commit/rollback. Also ensure single-record read endpoint returns meta+data. --- app/__init__.py | 2 +- .../database/alter_add_flag_hide.sql | 3 ++ app/api/prospects/database/run_migration.py | 24 +++++++++ app/api/prospects/prospects.py | 52 ++++++++++++++++++- 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 app/api/prospects/database/alter_add_flag_hide.sql create mode 100644 app/api/prospects/database/run_migration.py diff --git a/app/__init__.py b/app/__init__.py index d15324b..4ef4cfd 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,4 +1,4 @@ """NX AI - FastAPI, Python, Postgres, tsvector""" # Current Version -__version__ = "2.0.1" +__version__ = "2.0.2" diff --git a/app/api/prospects/database/alter_add_flag_hide.sql b/app/api/prospects/database/alter_add_flag_hide.sql new file mode 100644 index 0000000..47f6ab3 --- /dev/null +++ b/app/api/prospects/database/alter_add_flag_hide.sql @@ -0,0 +1,3 @@ +-- Migration: Add flag and hide columns to prospects table +ALTER TABLE prospects ADD COLUMN IF NOT EXISTS flag BOOLEAN DEFAULT FALSE; +ALTER TABLE prospects ADD COLUMN IF NOT EXISTS hide BOOLEAN DEFAULT FALSE; diff --git a/app/api/prospects/database/run_migration.py b/app/api/prospects/database/run_migration.py new file mode 100644 index 0000000..f06716b --- /dev/null +++ b/app/api/prospects/database/run_migration.py @@ -0,0 +1,24 @@ +# Run SQL migration to add 'flag' and 'hide' columns to prospects table +from app.utils.db import get_db_connection + +SQL_PATH = "app/api/prospects/database/alter_add_flag_hide.sql" + +def run_migration(): + with open(SQL_PATH, "r") as f: + sql = f.read() + conn_gen = get_db_connection() + conn = next(conn_gen) + cur = conn.cursor() + try: + cur.execute(sql) + conn.commit() + print("Migration successful: 'flag' and 'hide' columns added.") + except Exception as e: + print(f"Migration failed: {e}") + conn.rollback() + finally: + cur.close() + conn.close() + +if __name__ == "__main__": + run_migration() diff --git a/app/api/prospects/prospects.py b/app/api/prospects/prospects.py index c0a9dbf..0b194d2 100644 --- a/app/api/prospects/prospects.py +++ b/app/api/prospects/prospects.py @@ -2,7 +2,7 @@ from app import __version__ import os from app.utils.make_meta import make_meta -from fastapi import APIRouter, Query, Path +from fastapi import APIRouter, Query, Path, Body, HTTPException from app.utils.db import get_db_connection router = APIRouter() @@ -61,6 +61,13 @@ def prospects_read( from typing import Optional +# Schema for update +from pydantic import BaseModel + +class ProspectUpdate(BaseModel): + flag: Optional[bool] = None + hide: Optional[bool] = None + # endpoint: /prospects/search @router.get("/prospects/search") def prospects_search(query: Optional[str] = Query(None, description="Search query string"), @@ -194,6 +201,7 @@ def slugify(text): return {"meta": meta, "data": data} +# endpoint: /prospects/{id} # endpoint: /prospects/{id} @router.get("/prospects/{id}") def prospects_read_one(id: int = Path(..., description="ID of the prospect to retrieve")) -> dict: @@ -221,4 +229,46 @@ def prospects_read_one(id: int = Path(..., description="ID of the prospect to re finally: cur.close() conn.close() + return {"meta": meta, "data": data} + + +# PATCH endpoint to update flag/hide +@router.patch("/prospects/{id}") +def update_prospect(id: int = Path(..., description="ID of the prospect to update"), update: ProspectUpdate = Body(...)) -> dict: + """Update flag and/or hide fields for a prospect by id.""" + meta = make_meta("success", f"Updated prospect with id {id}") + conn_gen = get_db_connection() + conn = next(conn_gen) + cur = conn.cursor() + fields = [] + values = [] + if update.flag is not None: + fields.append("flag = %s") + values.append(update.flag) + if update.hide is not None: + fields.append("hide = %s") + values.append(update.hide) + if not fields: + raise HTTPException(status_code=400, detail="No fields to update.") + values.append(id) + try: + cur.execute(f"UPDATE prospects SET {', '.join(fields)} WHERE id = %s RETURNING *;", tuple(values)) + if cur.description is not None: + row = cur.fetchone() + if row is not None: + columns = [desc[0] for desc in cur.description] + data = dict(zip(columns, row)) + else: + data = None + meta = make_meta("error", f"No prospect found with id {id}") + else: + data = None + meta = make_meta("error", f"No prospect found with id {id}") + conn.commit() + except Exception as e: + data = None + meta = make_meta("error", f"Failed to update prospect: {str(e)}") + finally: + cur.close() + conn.close() return {"meta": meta, "data": data} \ No newline at end of file