diff --git a/README.md b/README.md index f225f2c..6f80288 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,16 @@ ## Python FastAPI/Postgres App -**Production-ready, open-source FastAPI application with PostgreSQL and blazing-fast full-text search.** +> Production-ready, open-source FastAPI application with PostgreSQL and blazing-fast full-text search. ---- +#### Project Overview -### 🚀 Features +This project provides a scalable API backend using FastAPI and PostgreSQL, featuring: + +- Automatic full-text search on all text fields (via tsvector) +- Endpoints for health checks, product management, prompt handling, and prospect management +- Efficient ingestion and processing of large CSV files + +#### 🚀 Features - **Python 3.11+** - **FastAPI** — Modern, high-performance REST API @@ -13,19 +19,7 @@ - **Uvicorn** — Lightning-fast ASGI server - **Pytest** — Comprehensive testing ---- - -## Project Overview - -This project provides a scalable API backend using FastAPI and PostgreSQL, featuring: - -- Automatic full-text search on all text fields (via tsvector) -- Endpoints for health checks, product management, prompt handling, and prospect management -- Efficient ingestion and processing of large CSV files - ---- - -## Getting Started +#### Getting Started ### 1. Clone & Setup Environment @@ -38,7 +32,7 @@ source venv/bin/activate pip install -r requirements.txt ``` -### 2. Run the App +#### 2. Run the App ```bash uvicorn app.main:app --reload @@ -46,16 +40,13 @@ uvicorn app.main:app --reload Visit [localhost:8000](http://localhost:8000) or [onrender](https://nx-ai.onrender.com) ---- - -## API Documentation +#### API Documentation FastAPI auto-generates interactive docs: - [Swagger UI](https://nx-ai.onrender.com/docs) - [ReDoc](https://nx-ai.onrender.com/redoc) ---- ## Full-Text Search (tsvector) @@ -69,13 +60,11 @@ SELECT * FROM prospects WHERE search_vector @@ plainto_tsquery('english', 'searc - On every insert/update, `search_vector` is computed using PostgreSQL's `to_tsvector('english', ...)`. - The GIN index (`idx_prospects_search_vector`) enables efficient search across large datasets. ---- ## Processing Large CSV Files The `/prospects/process` endpoint supports robust ingestion of large CSVs (e.g., 1300+ rows, 300KB+), following the same normalization and insertion pattern as `/prospects/seed` but optimized for scale. ---- ## Directory Structure @@ -98,13 +87,11 @@ requirements.txt # Python dependencies render.yaml # Deployment config (Render.com) ``` ---- ## Contributing Contributions welcome. Please open issues or submit pull requests. ---- ## License diff --git a/app/api/gemini/__init__.py b/app/api/gemini/__init__.py new file mode 100644 index 0000000..0d3c8e1 --- /dev/null +++ b/app/api/gemini/__init__.py @@ -0,0 +1,3 @@ +"""Gemini Routes""" + +from .gemini import router as gemini_router diff --git a/app/api/gemini/gemini.py b/app/api/gemini/gemini.py new file mode 100644 index 0000000..a0e1534 --- /dev/null +++ b/app/api/gemini/gemini.py @@ -0,0 +1,9 @@ +from fastapi import APIRouter + +router = APIRouter() + +@router.get("/gemini") +def root() -> dict: + """GET /gemini endpoint.""" + return {"message": "Welcome to the Gemini API!"} + diff --git a/app/api/root.py b/app/api/root.py index 380b924..77df888 100644 --- a/app/api/root.py +++ b/app/api/root.py @@ -13,13 +13,14 @@ def root() -> dict: base_url = os.getenv("BASE_URL", "http://localhost:8000") epoch = int(time.time() * 1000) meta = { - "title": "NX-AI", + "title": "Python", "severity": "success", "version": __version__, "base_url": base_url, "time": epoch, } endpoints = [ + {"name": "gemini", "url": f"{base_url}/gemini"}, {"name": "docs", "url": f"{base_url}/docs"}, {"name": "resend", "url": f"{base_url}/resend"}, {"name": "health", "url": f"{base_url}/health"}, diff --git a/app/api/routes.py b/app/api/routes.py index 86d8ca4..243dd37 100644 --- a/app/api/routes.py +++ b/app/api/routes.py @@ -1,10 +1,7 @@ -from app import __version__ """API routes""" - +from app import __version__ from dotenv import load_dotenv - from fastapi import APIRouter, Depends - from app.utils.db import get_db_connection from app.api.schemas import EchoRequest, EchoResponse @@ -16,10 +13,7 @@ from app.api.prompts.prompts import router as prompts_router from app.api.prospects.prospects import router as prospects_router from app.api.prospects.search import router as prospects_search_router -from app.utils.prospects.database.alter import router as prospects_alter_router -from app.utils.prospects.database.seed import router as prospects_seed_router -from app.utils.prospects.database.empty import router as prospects_empty_router -from app.utils.prospects.database.process import router as prospects_process_router +from app.api.gemini.gemini import router as gemini_router router.include_router(root_router) router.include_router(resend_router) @@ -27,7 +21,5 @@ router.include_router(prompts_router) router.include_router(prospects_search_router) router.include_router(prospects_router) -router.include_router(prospects_alter_router) -router.include_router(prospects_seed_router) -router.include_router(prospects_empty_router) -router.include_router(prospects_process_router) +router.include_router(gemini_router) + diff --git a/render.yaml b/render.yaml index 4a2a342..7d3fce9 100644 --- a/render.yaml +++ b/render.yaml @@ -8,7 +8,7 @@ projects: - type: web name: nx-ai runtime: python - repo: https://github.com/goldlabelapps/nx-ai + repo: https://github.com/goldlabelapps/python plan: free region: oregon buildCommand: pip install -r requirements.txt diff --git a/tests/test_gemini.py b/tests/test_gemini.py new file mode 100644 index 0000000..2e8eefd --- /dev/null +++ b/tests/test_gemini.py @@ -0,0 +1,14 @@ +import sys +import os +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))) +import pytest +from fastapi.testclient import TestClient +from app.main import app + +client = TestClient(app) + + +def test_gemini_endpoint(): + response = client.get("/gemini") + assert response.status_code == 200 + assert response.json() == {"message": "Welcome to the Gemini API!"} diff --git a/tests/test_prospects.py b/tests/test_prospects.py index 3c16884..c381065 100644 --- a/tests/test_prospects.py +++ b/tests/test_prospects.py @@ -11,13 +11,16 @@ def test_get_prospects_root(): assert "meta" in data assert "data" in data assert isinstance(data["data"], list) - # Check that the expected keys are present in the data list - assert any("init" in item for item in data["data"]) - assert any("search" in item for item in data["data"]) + # Check that the expected keys are present in the data list (if not empty) + if data["data"]: + first_item = data["data"][0] + # Adjust these keys to match your actual prospect schema + assert "id" in first_item + assert "first_name" in first_item or "last_name" in first_item # Meta checks meta = data["meta"] assert meta["severity"] == "success" - assert meta["title"] == "Prospects endpoint" + assert meta["title"] == "Read paginated prospects" def test_prospects_returns_list(): response = client.get("/prospects")