Skip to content

Commit bfcbeb7

Browse files
committed
docs: update readme
1 parent d05cb5a commit bfcbeb7

File tree

8 files changed

+331
-35
lines changed

8 files changed

+331
-35
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,6 @@ migrations/MIGRATION_SUMMARY.md
4949
migrations/KENTUCKY_INSTITUTION_IDS.md
5050
check_env.py
5151
migrations/REGENERATION_README.md
52+
testscripts/test_new_endpoints.py
53+
testscripts/check_llm_structure.py
54+
NEW_ENDPOINTS_SUMMARY.md

README.md

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ A RESTful API for accessing educational institution data across multiple schools
88
- **Standardized Endpoints**: Consistent API structure across all institutions
99
- **Pagination**: Built-in support for large datasets
1010
- **Filtering**: Query specific data subsets using query parameters
11+
- **Data Uploads**: Upload CSV/Excel to append data with dynamic column mapping (see Data Upload section)
12+
- **New Datasets**: Access LLM recommendations and analysis-ready tables across all databases
1113

1214
## Prerequisites
1315

@@ -48,7 +50,7 @@ A RESTful API for accessing educational institution data across multiple schools
4850

4951
Start the development server:
5052
```bash
51-
uvicorn main:app --reload
53+
uvicorn api.main:app --reload
5254
```
5355

5456
The API will be available at `http://localhost:8000`
@@ -92,6 +94,20 @@ For each institution, the following endpoints are available:
9294
- `GET /{institution_code}/financial-aid` - List financial aid records
9395
- `GET /{institution_code}/financial_aid/count` - Get count of financial aid records
9496

97+
#### LLM Recommendations
98+
- `GET /{institution_code}/llm-recommendations` - List LLM recommendation records
99+
- `GET /{institution_code}/llm_recommendations/count` - Get count of LLM recommendation records
100+
101+
#### Analysis-Ready Data
102+
- `GET /{institution_code}/analysis-ready` - List analysis-ready records
103+
- `GET /{institution_code}/ar_{institution_code}/count` - Get count of analysis-ready records
104+
105+
## Data Upload Feature
106+
107+
- Quick start: see `QUICKSTART_UPLOAD.md`
108+
- Full documentation: see `UPLOAD_FEATURE_README.md`
109+
- Endpoints are available under `/upload` in Swagger UI.
110+
95111
## Query Parameters
96112

97113
### Pagination
@@ -217,7 +233,7 @@ The workflow runs automatically on:
217233
- Pushes to `main` or `develop` branches
218234
- Pull requests to the `main` branch
219235

220-
The Docker image will be tagged as `latest` and pushed to ECR.
236+
The Docker image will be tagged and pushed to Docker Hub (or your configured registry).
221237

222238
**Install required packages:**
223239
```bash
@@ -243,7 +259,7 @@ Each database in this project contains the following three tables:
243259
244260
### 1. Create Databases and Tables
245261
```bash
246-
python db_setup.py
262+
python db_operations/db_setup.py
247263
```
248264

249265
This creates 5 databases with 3 tables each:
@@ -284,28 +300,34 @@ If Ollama is not available or fails, scripts automatically use rule-based synthe
284300
devcolor-backend/
285301
├── api/
286302
│ ├── __init__.py
287-
│ ├── main.py # API routing setup
288-
│ ├── schemas.py # Pydantic models
303+
│ ├── main.py # FastAPI app and router registration
304+
│ ├── schemas.py # Pydantic models
289305
│ └── routers/
290306
│ ├── __init__.py
291-
│ ├── al.py # Alabama institution endpoints
292-
│ ├── csusb.py # CSUSB institution endpoints
293-
│ ├── kctcs.py # KCTCS institution endpoints
294-
│ ├── ky.py # Kentucky institution endpoints
295-
│ └── oh.py # Ohio institution endpoints
307+
│ ├── al.py # AL endpoints (incl. new endpoints)
308+
│ ├── csusb.py # CSUSB endpoints (incl. new endpoints)
309+
│ ├── kctcs.py # KCTCS endpoints (incl. new endpoints)
310+
│ ├── ky.py # KY endpoints (incl. new endpoints)
311+
│ ├── oh.py # OH endpoints (incl. new endpoints)
312+
│ └── upload.py # Data upload endpoints
296313
├── db_operations/
297314
│ ├── __init__.py
298-
│ ├── connection.py # Database connection utilities
299-
│ ├── db_setup.py # Database setup and table creation
300-
│ ├── generate_db_summary.py # Database summary generation
301-
│ └── test_db_connection.py # Database connection testing
302-
├── .env # Environment variables (not version controlled)
303-
├── .gitignore
304-
├── check_databases.py # Database availability checker
305-
├── check_tables.py # Table structure inspector
306-
├── count_records.py # Record counting utility
307-
├── main.py # FastAPI application entry point
308-
├── requirements.txt # Python dependencies
309-
├── README.md # This file
310-
├── test_env.py # Environment variable testing
311-
└── test_ky_query.py # KY database query testing
315+
│ ├── connection.py # DB connection utilities
316+
│ ├── db_setup.py # Database setup and table creation
317+
│ ├── add_dynamic_columns.py # Migration for dynamic upload columns
318+
│ ├── upload_handler.py # Upload processing logic
319+
│ └── generate_db_summary.py # Database summary generation
320+
├── docker/
321+
│ ├── Dockerfile
322+
│ └── docker-compose.yml
323+
├── testscripts/
324+
│ ├── check_databases.py
325+
│ ├── check_schema.py
326+
│ ├── check_tables.py
327+
│ ├── count_records.py
328+
│ └── test_new_endpoints.py
329+
├── requirements.txt # Python dependencies
330+
├── README.md # This file
331+
├── QUICKSTART_UPLOAD.md # Upload quick start
332+
├── UPLOAD_FEATURE_README.md # Upload feature docs
333+
└── NEW_ENDPOINTS_SUMMARY.md # Summary of new endpoints

api/routers/al.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from fastapi import APIRouter, HTTPException, Query
22
from typing import List
3-
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, TableCount, DatabaseInfo
3+
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, LlmRecommendationRecord, AnalysisReadyRecord, TableCount, DatabaseInfo
44
from db_operations.connection import get_db_connection, format_records
55

66
router = APIRouter()
@@ -69,10 +69,44 @@ async def get_financial_aid(
6969
except Exception as e:
7070
raise HTTPException(status_code=500, detail=f"Error fetching financial aid: {str(e)}")
7171

72+
@router.get("/llm-recommendations", response_model=List[LlmRecommendationRecord])
73+
async def get_llm_recommendations(
74+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
75+
offset: int = Query(0, ge=0, description="Number of records to skip")
76+
):
77+
"""Get LLM recommendation records from AL database."""
78+
try:
79+
with get_db_connection(DATABASE_NAME) as connection:
80+
cursor = connection.cursor(dictionary=True)
81+
query = "SELECT * FROM llm_recommendations ORDER BY id LIMIT %s OFFSET %s"
82+
cursor.execute(query, (limit, offset))
83+
records = cursor.fetchall()
84+
cursor.close()
85+
return format_records(records)
86+
except Exception as e:
87+
raise HTTPException(status_code=500, detail=f"Error fetching LLM recommendations: {str(e)}")
88+
89+
@router.get("/analysis-ready", response_model=List[AnalysisReadyRecord])
90+
async def get_analysis_ready(
91+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
92+
offset: int = Query(0, ge=0, description="Number of records to skip")
93+
):
94+
"""Get analysis ready records from AL database."""
95+
try:
96+
with get_db_connection(DATABASE_NAME) as connection:
97+
cursor = connection.cursor(dictionary=True)
98+
query = "SELECT * FROM ar_al ORDER BY id LIMIT %s OFFSET %s"
99+
cursor.execute(query, (limit, offset))
100+
records = cursor.fetchall()
101+
cursor.close()
102+
return format_records(records)
103+
except Exception as e:
104+
raise HTTPException(status_code=500, detail=f"Error fetching analysis ready records: {str(e)}")
105+
72106
@router.get("/{table_name}/count", response_model=TableCount)
73107
async def get_table_count(table_name: str):
74108
"""Get the total count of records in a specific table."""
75-
valid_tables = ["cohort", "course", "financial_aid"]
109+
valid_tables = ["cohort", "course", "financial_aid", "llm_recommendations", "ar_al"]
76110
if table_name not in valid_tables:
77111
raise HTTPException(
78112
status_code=404,

api/routers/csusb.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from fastapi import APIRouter, HTTPException, Query
22
from typing import List
3-
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, TableCount, DatabaseInfo
3+
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, LlmRecommendationRecord, AnalysisReadyRecord, TableCount, DatabaseInfo
44
from db_operations.connection import get_db_connection, format_records
55

66
router = APIRouter()
@@ -69,10 +69,44 @@ async def get_financial_aid(
6969
except Exception as e:
7070
raise HTTPException(status_code=500, detail=f"Error fetching financial aid: {str(e)}")
7171

72+
@router.get("/llm-recommendations", response_model=List[LlmRecommendationRecord])
73+
async def get_llm_recommendations(
74+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
75+
offset: int = Query(0, ge=0, description="Number of records to skip")
76+
):
77+
"""Get LLM recommendation records from CSUSB database."""
78+
try:
79+
with get_db_connection(DATABASE_NAME) as connection:
80+
cursor = connection.cursor(dictionary=True)
81+
query = "SELECT * FROM llm_recommendations ORDER BY id LIMIT %s OFFSET %s"
82+
cursor.execute(query, (limit, offset))
83+
records = cursor.fetchall()
84+
cursor.close()
85+
return format_records(records)
86+
except Exception as e:
87+
raise HTTPException(status_code=500, detail=f"Error fetching LLM recommendations: {str(e)}")
88+
89+
@router.get("/analysis-ready", response_model=List[AnalysisReadyRecord])
90+
async def get_analysis_ready(
91+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
92+
offset: int = Query(0, ge=0, description="Number of records to skip")
93+
):
94+
"""Get analysis ready records from CSUSB database."""
95+
try:
96+
with get_db_connection(DATABASE_NAME) as connection:
97+
cursor = connection.cursor(dictionary=True)
98+
query = "SELECT * FROM ar_csusb ORDER BY id LIMIT %s OFFSET %s"
99+
cursor.execute(query, (limit, offset))
100+
records = cursor.fetchall()
101+
cursor.close()
102+
return format_records(records)
103+
except Exception as e:
104+
raise HTTPException(status_code=500, detail=f"Error fetching analysis ready records: {str(e)}")
105+
72106
@router.get("/{table_name}/count", response_model=TableCount)
73107
async def get_table_count(table_name: str):
74108
"""Get the total count of records in a specific table."""
75-
valid_tables = ["cohort", "course", "financial_aid"]
109+
valid_tables = ["cohort", "course", "financial_aid", "llm_recommendations", "ar_csusb"]
76110
if table_name not in valid_tables:
77111
raise HTTPException(
78112
status_code=404,

api/routers/kctcs.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from fastapi import APIRouter, HTTPException, Query
22
from typing import List
3-
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, TableCount, DatabaseInfo
3+
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, LlmRecommendationRecord, AnalysisReadyRecord, TableCount, DatabaseInfo
44
from db_operations.connection import get_db_connection, format_records
55

66
router = APIRouter()
@@ -69,10 +69,44 @@ async def get_financial_aid(
6969
except Exception as e:
7070
raise HTTPException(status_code=500, detail=f"Error fetching financial aid: {str(e)}")
7171

72+
@router.get("/llm-recommendations", response_model=List[LlmRecommendationRecord])
73+
async def get_llm_recommendations(
74+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
75+
offset: int = Query(0, ge=0, description="Number of records to skip")
76+
):
77+
"""Get LLM recommendation records from KCTCS database."""
78+
try:
79+
with get_db_connection(DATABASE_NAME) as connection:
80+
cursor = connection.cursor(dictionary=True)
81+
query = "SELECT * FROM llm_recommendations ORDER BY id LIMIT %s OFFSET %s"
82+
cursor.execute(query, (limit, offset))
83+
records = cursor.fetchall()
84+
cursor.close()
85+
return format_records(records)
86+
except Exception as e:
87+
raise HTTPException(status_code=500, detail=f"Error fetching LLM recommendations: {str(e)}")
88+
89+
@router.get("/analysis-ready", response_model=List[AnalysisReadyRecord])
90+
async def get_analysis_ready(
91+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
92+
offset: int = Query(0, ge=0, description="Number of records to skip")
93+
):
94+
"""Get analysis ready records from KCTCS database."""
95+
try:
96+
with get_db_connection(DATABASE_NAME) as connection:
97+
cursor = connection.cursor(dictionary=True)
98+
query = "SELECT * FROM ar_kctcs ORDER BY id LIMIT %s OFFSET %s"
99+
cursor.execute(query, (limit, offset))
100+
records = cursor.fetchall()
101+
cursor.close()
102+
return format_records(records)
103+
except Exception as e:
104+
raise HTTPException(status_code=500, detail=f"Error fetching analysis ready records: {str(e)}")
105+
72106
@router.get("/{table_name}/count", response_model=TableCount)
73107
async def get_table_count(table_name: str):
74108
"""Get the total count of records in a specific table."""
75-
valid_tables = ["cohort", "course", "financial_aid"]
109+
valid_tables = ["cohort", "course", "financial_aid", "llm_recommendations", "ar_kctcs"]
76110
if table_name not in valid_tables:
77111
raise HTTPException(
78112
status_code=404,

api/routers/ky.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from fastapi import APIRouter, HTTPException, Query
22
from typing import List
3-
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, TableCount, DatabaseInfo
3+
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, LlmRecommendationRecord, AnalysisReadyRecord, TableCount, DatabaseInfo
44
from db_operations.connection import get_db_connection, format_records
55

66
router = APIRouter()
@@ -69,10 +69,44 @@ async def get_financial_aid(
6969
except Exception as e:
7070
raise HTTPException(status_code=500, detail=f"Error fetching financial aid: {str(e)}")
7171

72+
@router.get("/llm-recommendations", response_model=List[LlmRecommendationRecord])
73+
async def get_llm_recommendations(
74+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
75+
offset: int = Query(0, ge=0, description="Number of records to skip")
76+
):
77+
"""Get LLM recommendation records from KY database."""
78+
try:
79+
with get_db_connection(DATABASE_NAME) as connection:
80+
cursor = connection.cursor(dictionary=True)
81+
query = "SELECT * FROM llm_recommendations ORDER BY id LIMIT %s OFFSET %s"
82+
cursor.execute(query, (limit, offset))
83+
records = cursor.fetchall()
84+
cursor.close()
85+
return format_records(records)
86+
except Exception as e:
87+
raise HTTPException(status_code=500, detail=f"Error fetching LLM recommendations: {str(e)}")
88+
89+
@router.get("/analysis-ready", response_model=List[AnalysisReadyRecord])
90+
async def get_analysis_ready(
91+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
92+
offset: int = Query(0, ge=0, description="Number of records to skip")
93+
):
94+
"""Get analysis ready records from KY database."""
95+
try:
96+
with get_db_connection(DATABASE_NAME) as connection:
97+
cursor = connection.cursor(dictionary=True)
98+
query = "SELECT * FROM ar_ky ORDER BY id LIMIT %s OFFSET %s"
99+
cursor.execute(query, (limit, offset))
100+
records = cursor.fetchall()
101+
cursor.close()
102+
return format_records(records)
103+
except Exception as e:
104+
raise HTTPException(status_code=500, detail=f"Error fetching analysis ready records: {str(e)}")
105+
72106
@router.get("/{table_name}/count", response_model=TableCount)
73107
async def get_table_count(table_name: str):
74108
"""Get the total count of records in a specific table."""
75-
valid_tables = ["cohort", "course", "financial_aid"]
109+
valid_tables = ["cohort", "course", "financial_aid", "llm_recommendations", "ar_ky"]
76110
if table_name not in valid_tables:
77111
raise HTTPException(
78112
status_code=404,

api/routers/oh.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from fastapi import APIRouter, HTTPException, Query
22
from typing import List
3-
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, TableCount, DatabaseInfo
3+
from ..schemas import CohortRecord, CourseRecord, FinancialAidRecord, LlmRecommendationRecord, AnalysisReadyRecord, TableCount, DatabaseInfo
44
from db_operations.connection import get_db_connection, format_records
55

66
router = APIRouter()
@@ -69,10 +69,44 @@ async def get_financial_aid(
6969
except Exception as e:
7070
raise HTTPException(status_code=500, detail=f"Error fetching financial aid: {str(e)}")
7171

72+
@router.get("/llm-recommendations", response_model=List[LlmRecommendationRecord])
73+
async def get_llm_recommendations(
74+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
75+
offset: int = Query(0, ge=0, description="Number of records to skip")
76+
):
77+
"""Get LLM recommendation records from OH database."""
78+
try:
79+
with get_db_connection(DATABASE_NAME) as connection:
80+
cursor = connection.cursor(dictionary=True)
81+
query = "SELECT * FROM llm_recommendations ORDER BY id LIMIT %s OFFSET %s"
82+
cursor.execute(query, (limit, offset))
83+
records = cursor.fetchall()
84+
cursor.close()
85+
return format_records(records)
86+
except Exception as e:
87+
raise HTTPException(status_code=500, detail=f"Error fetching LLM recommendations: {str(e)}")
88+
89+
@router.get("/analysis-ready", response_model=List[AnalysisReadyRecord])
90+
async def get_analysis_ready(
91+
limit: int = Query(100, ge=1, le=1000, description="Number of records to return"),
92+
offset: int = Query(0, ge=0, description="Number of records to skip")
93+
):
94+
"""Get analysis ready records from OH database."""
95+
try:
96+
with get_db_connection(DATABASE_NAME) as connection:
97+
cursor = connection.cursor(dictionary=True)
98+
query = "SELECT * FROM ar_oh ORDER BY id LIMIT %s OFFSET %s"
99+
cursor.execute(query, (limit, offset))
100+
records = cursor.fetchall()
101+
cursor.close()
102+
return format_records(records)
103+
except Exception as e:
104+
raise HTTPException(status_code=500, detail=f"Error fetching analysis ready records: {str(e)}")
105+
72106
@router.get("/{table_name}/count", response_model=TableCount)
73107
async def get_table_count(table_name: str):
74108
"""Get the total count of records in a specific table."""
75-
valid_tables = ["cohort", "course", "financial_aid"]
109+
valid_tables = ["cohort", "course", "financial_aid", "llm_recommendations", "ar_oh"]
76110
if table_name not in valid_tables:
77111
raise HTTPException(
78112
status_code=404,

0 commit comments

Comments
 (0)