Commit
·
18afae5
1
Parent(s):
38dfa43
Integrate OpenAI recommendations
Browse files- HUGGINGFACE_DEPLOYMENT.md +8 -6
- README.md +11 -5
- Smart_Budget_Recommendation_API.postman_collection.json +384 -0
- app/models.py +4 -0
- app/smart_recommendation.py +62 -4
- requirements.txt +2 -0
- test_hf_api.py +215 -0
HUGGINGFACE_DEPLOYMENT.md
CHANGED
|
@@ -20,15 +20,17 @@ This guide will help you deploy the Smart Budget Recommendation API to Hugging F
|
|
| 20 |
- **Visibility**: Choose Public or Private
|
| 21 |
4. Click "Create Space"
|
| 22 |
|
| 23 |
-
### Step 2: Add MongoDB
|
| 24 |
|
| 25 |
1. In your Space, go to **Settings** (gear icon)
|
| 26 |
2. Navigate to **Variables and secrets** section
|
| 27 |
3. Click **New secret**
|
| 28 |
-
4. Add:
|
| 29 |
- **Name**: `MONGODB_URI`
|
| 30 |
-
|
| 31 |
-
|
|
|
|
|
|
|
| 32 |
|
| 33 |
### Step 3: Upload Files
|
| 34 |
|
|
@@ -110,9 +112,9 @@ print(response.json())
|
|
| 110 |
|
| 111 |
## Important Notes
|
| 112 |
|
| 113 |
-
1. **Port Configuration**: Hugging Face
|
| 114 |
|
| 115 |
-
2. **Environment Variables**:
|
| 116 |
|
| 117 |
3. **Cold Starts**: The first request after inactivity may take longer (cold start). Subsequent requests will be faster
|
| 118 |
|
|
|
|
| 20 |
- **Visibility**: Choose Public or Private
|
| 21 |
4. Click "Create Space"
|
| 22 |
|
| 23 |
+
### Step 2: Add secrets (MongoDB and optional OpenAI)
|
| 24 |
|
| 25 |
1. In your Space, go to **Settings** (gear icon)
|
| 26 |
2. Navigate to **Variables and secrets** section
|
| 27 |
3. Click **New secret**
|
| 28 |
+
4. Add the following secrets:
|
| 29 |
- **Name**: `MONGODB_URI`
|
| 30 |
+
- **Value**: `mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin`
|
| 31 |
+
- **Name**: `OPENAI_API_KEY` *(optional, enables GPT-based recommendations)*
|
| 32 |
+
- **Value**: `sk-...`
|
| 33 |
+
5. Click **Add secret** after each entry
|
| 34 |
|
| 35 |
### Step 3: Upload Files
|
| 36 |
|
|
|
|
| 112 |
|
| 113 |
## Important Notes
|
| 114 |
|
| 115 |
+
1. **Port Configuration**: Hugging Face Spaces expect apps to serve on port **7860**, so the Dockerfile exposes and binds to that port.
|
| 116 |
|
| 117 |
+
2. **Environment Variables**: Secrets such as `MONGODB_URI` (and optional `OPENAI_API_KEY`) are injected automatically into the container environment.
|
| 118 |
|
| 119 |
3. **Cold Starts**: The first request after inactivity may take longer (cold start). Subsequent requests will be faster
|
| 120 |
|
README.md
CHANGED
|
@@ -52,9 +52,11 @@ A FastAPI-based service that provides intelligent budget recommendations based o
|
|
| 52 |
pip install -r requirements.txt
|
| 53 |
```
|
| 54 |
|
| 55 |
-
2. Set environment
|
| 56 |
```bash
|
| 57 |
export MONGODB_URI="mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin"
|
|
|
|
|
|
|
| 58 |
```
|
| 59 |
|
| 60 |
3. Run the application:
|
|
@@ -73,7 +75,7 @@ docker build -t smart-budget-recommendation .
|
|
| 73 |
|
| 74 |
### Run Docker Container
|
| 75 |
```bash
|
| 76 |
-
docker run -p
|
| 77 |
```
|
| 78 |
|
| 79 |
## Hugging Face Deployment
|
|
@@ -85,10 +87,11 @@ docker run -p 8000:8000 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommend
|
|
| 85 |
|
| 86 |
### Steps
|
| 87 |
|
| 88 |
-
1. **Store
|
| 89 |
- Go to your Hugging Face Space settings
|
| 90 |
- Navigate to "Variables and secrets"
|
| 91 |
-
- Add
|
|
|
|
| 92 |
|
| 93 |
2. **Create Hugging Face Space**:
|
| 94 |
- Create a new Space on Hugging Face
|
|
@@ -107,7 +110,7 @@ docker run -p 8000:8000 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommend
|
|
| 107 |
The Dockerfile is configured to:
|
| 108 |
- Use Python 3.11
|
| 109 |
- Install all dependencies
|
| 110 |
-
- Expose port
|
| 111 |
- Run the FastAPI application with Uvicorn
|
| 112 |
|
| 113 |
Hugging Face will automatically:
|
|
@@ -139,10 +142,13 @@ The Smart Budget Recommendation engine:
|
|
| 139 |
4. **Confidence Scoring**:
|
| 140 |
- Based on data quality (months analyzed, transaction count, consistency)
|
| 141 |
- Returns score from 0-1
|
|
|
|
|
|
|
| 142 |
|
| 143 |
## Environment Variables
|
| 144 |
|
| 145 |
- `MONGODB_URI`: MongoDB connection string (required)
|
|
|
|
| 146 |
|
| 147 |
## License
|
| 148 |
|
|
|
|
| 52 |
pip install -r requirements.txt
|
| 53 |
```
|
| 54 |
|
| 55 |
+
2. Set environment variables:
|
| 56 |
```bash
|
| 57 |
export MONGODB_URI="mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin"
|
| 58 |
+
# optional
|
| 59 |
+
export OPENAI_API_KEY="your_openai_key"
|
| 60 |
```
|
| 61 |
|
| 62 |
3. Run the application:
|
|
|
|
| 75 |
|
| 76 |
### Run Docker Container
|
| 77 |
```bash
|
| 78 |
+
docker run -p 7860:7860 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommendation
|
| 79 |
```
|
| 80 |
|
| 81 |
## Hugging Face Deployment
|
|
|
|
| 87 |
|
| 88 |
### Steps
|
| 89 |
|
| 90 |
+
1. **Store secrets in Hugging Face**:
|
| 91 |
- Go to your Hugging Face Space settings
|
| 92 |
- Navigate to "Variables and secrets"
|
| 93 |
+
- Add `MONGODB_URI` with your MongoDB connection string
|
| 94 |
+
- *(Optional)* Add `OPENAI_API_KEY` if you want GPT-powered recommendations
|
| 95 |
|
| 96 |
2. **Create Hugging Face Space**:
|
| 97 |
- Create a new Space on Hugging Face
|
|
|
|
| 110 |
The Dockerfile is configured to:
|
| 111 |
- Use Python 3.11
|
| 112 |
- Install all dependencies
|
| 113 |
+
- Expose port 7860 (Hugging Face default)
|
| 114 |
- Run the FastAPI application with Uvicorn
|
| 115 |
|
| 116 |
Hugging Face will automatically:
|
|
|
|
| 142 |
4. **Confidence Scoring**:
|
| 143 |
- Based on data quality (months analyzed, transaction count, consistency)
|
| 144 |
- Returns score from 0-1
|
| 145 |
+
5. **(Optional) GPT Refinement**:
|
| 146 |
+
- If `OPENAI_API_KEY` is set, GPT reviews the history and suggests increase/decrease/keep along with a natural-language reason.
|
| 147 |
|
| 148 |
## Environment Variables
|
| 149 |
|
| 150 |
- `MONGODB_URI`: MongoDB connection string (required)
|
| 151 |
+
- `OPENAI_API_KEY`: Optional. If set, GPT-based recommendations augment the rule-based engine.
|
| 152 |
|
| 153 |
## License
|
| 154 |
|
Smart_Budget_Recommendation_API.postman_collection.json
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"info": {
|
| 3 |
+
"_postman_id": "smart-budget-recommendation-api",
|
| 4 |
+
"name": "Smart Budget Recommendation API",
|
| 5 |
+
"description": "API collection for Smart Budget Recommendation service deployed on Hugging Face",
|
| 6 |
+
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
| 7 |
+
},
|
| 8 |
+
"item": [
|
| 9 |
+
{
|
| 10 |
+
"name": "Health Check",
|
| 11 |
+
"request": {
|
| 12 |
+
"method": "GET",
|
| 13 |
+
"header": [],
|
| 14 |
+
"url": {
|
| 15 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/health",
|
| 16 |
+
"protocol": "https",
|
| 17 |
+
"host": [
|
| 18 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 19 |
+
"hf",
|
| 20 |
+
"space"
|
| 21 |
+
],
|
| 22 |
+
"path": [
|
| 23 |
+
"health"
|
| 24 |
+
]
|
| 25 |
+
},
|
| 26 |
+
"description": "Check if the API and database are running"
|
| 27 |
+
}
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"name": "Root Endpoint",
|
| 31 |
+
"request": {
|
| 32 |
+
"method": "GET",
|
| 33 |
+
"header": [],
|
| 34 |
+
"url": {
|
| 35 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/",
|
| 36 |
+
"protocol": "https",
|
| 37 |
+
"host": [
|
| 38 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 39 |
+
"hf",
|
| 40 |
+
"space"
|
| 41 |
+
],
|
| 42 |
+
"path": [
|
| 43 |
+
""
|
| 44 |
+
]
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
},
|
| 48 |
+
{
|
| 49 |
+
"name": "Create Expense",
|
| 50 |
+
"request": {
|
| 51 |
+
"method": "POST",
|
| 52 |
+
"header": [
|
| 53 |
+
{
|
| 54 |
+
"key": "Content-Type",
|
| 55 |
+
"value": "application/json"
|
| 56 |
+
}
|
| 57 |
+
],
|
| 58 |
+
"body": {
|
| 59 |
+
"mode": "raw",
|
| 60 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 3800,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries\",\n \"date\": \"2025-01-15T00:00:00\",\n \"type\": \"expense\"\n}"
|
| 61 |
+
},
|
| 62 |
+
"url": {
|
| 63 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
|
| 64 |
+
"protocol": "https",
|
| 65 |
+
"host": [
|
| 66 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 67 |
+
"hf",
|
| 68 |
+
"space"
|
| 69 |
+
],
|
| 70 |
+
"path": [
|
| 71 |
+
"expenses"
|
| 72 |
+
]
|
| 73 |
+
},
|
| 74 |
+
"description": "Create a new expense record"
|
| 75 |
+
}
|
| 76 |
+
},
|
| 77 |
+
{
|
| 78 |
+
"name": "Get Expenses",
|
| 79 |
+
"request": {
|
| 80 |
+
"method": "GET",
|
| 81 |
+
"header": [],
|
| 82 |
+
"url": {
|
| 83 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses?user_id=68a834c3f4694b11efedacd2&limit=20",
|
| 84 |
+
"protocol": "https",
|
| 85 |
+
"host": [
|
| 86 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 87 |
+
"hf",
|
| 88 |
+
"space"
|
| 89 |
+
],
|
| 90 |
+
"path": [
|
| 91 |
+
"expenses"
|
| 92 |
+
],
|
| 93 |
+
"query": [
|
| 94 |
+
{
|
| 95 |
+
"key": "user_id",
|
| 96 |
+
"value": "68a834c3f4694b11efedacd2",
|
| 97 |
+
"description": "User identifier"
|
| 98 |
+
},
|
| 99 |
+
{
|
| 100 |
+
"key": "limit",
|
| 101 |
+
"value": "20",
|
| 102 |
+
"description": "Maximum number of expenses to return"
|
| 103 |
+
}
|
| 104 |
+
]
|
| 105 |
+
},
|
| 106 |
+
"description": "Get expenses for a specific user"
|
| 107 |
+
}
|
| 108 |
+
},
|
| 109 |
+
{
|
| 110 |
+
"name": "Create Budget",
|
| 111 |
+
"request": {
|
| 112 |
+
"method": "POST",
|
| 113 |
+
"header": [
|
| 114 |
+
{
|
| 115 |
+
"key": "Content-Type",
|
| 116 |
+
"value": "application/json"
|
| 117 |
+
}
|
| 118 |
+
],
|
| 119 |
+
"body": {
|
| 120 |
+
"mode": "raw",
|
| 121 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"category\": \"Groceries\",\n \"amount\": 4000,\n \"period\": \"monthly\",\n \"start_date\": \"2025-02-01T00:00:00\",\n \"end_date\": \"2025-02-29T00:00:00\"\n}"
|
| 122 |
+
},
|
| 123 |
+
"url": {
|
| 124 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/budgets",
|
| 125 |
+
"protocol": "https",
|
| 126 |
+
"host": [
|
| 127 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 128 |
+
"hf",
|
| 129 |
+
"space"
|
| 130 |
+
],
|
| 131 |
+
"path": [
|
| 132 |
+
"budgets"
|
| 133 |
+
]
|
| 134 |
+
},
|
| 135 |
+
"description": "Create a new budget"
|
| 136 |
+
}
|
| 137 |
+
},
|
| 138 |
+
{
|
| 139 |
+
"name": "Get Budgets",
|
| 140 |
+
"request": {
|
| 141 |
+
"method": "GET",
|
| 142 |
+
"header": [],
|
| 143 |
+
"url": {
|
| 144 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/budgets?user_id=68a834c3f4694b11efedacd2",
|
| 145 |
+
"protocol": "https",
|
| 146 |
+
"host": [
|
| 147 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 148 |
+
"hf",
|
| 149 |
+
"space"
|
| 150 |
+
],
|
| 151 |
+
"path": [
|
| 152 |
+
"budgets"
|
| 153 |
+
],
|
| 154 |
+
"query": [
|
| 155 |
+
{
|
| 156 |
+
"key": "user_id",
|
| 157 |
+
"value": "68a834c3f4694b11efedacd2"
|
| 158 |
+
}
|
| 159 |
+
]
|
| 160 |
+
},
|
| 161 |
+
"description": "Get budgets for a specific user"
|
| 162 |
+
}
|
| 163 |
+
},
|
| 164 |
+
{
|
| 165 |
+
"name": "Get Smart Budget Recommendations",
|
| 166 |
+
"request": {
|
| 167 |
+
"method": "GET",
|
| 168 |
+
"header": [],
|
| 169 |
+
"url": {
|
| 170 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/recommendations/68a834c3f4694b11efedacd2?month=2&year=2025",
|
| 171 |
+
"protocol": "https",
|
| 172 |
+
"host": [
|
| 173 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 174 |
+
"hf",
|
| 175 |
+
"space"
|
| 176 |
+
],
|
| 177 |
+
"path": [
|
| 178 |
+
"recommendations",
|
| 179 |
+
"68a834c3f4694b11efedacd2"
|
| 180 |
+
],
|
| 181 |
+
"query": [
|
| 182 |
+
{
|
| 183 |
+
"key": "month",
|
| 184 |
+
"value": "2",
|
| 185 |
+
"description": "Target month (1-12), optional - defaults to next month"
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
"key": "year",
|
| 189 |
+
"value": "2025",
|
| 190 |
+
"description": "Target year, optional - defaults to next year"
|
| 191 |
+
}
|
| 192 |
+
]
|
| 193 |
+
},
|
| 194 |
+
"description": "Get smart budget recommendations based on past spending behavior. Requires at least 2-3 months of expense data."
|
| 195 |
+
}
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
"name": "Get Category Expenses",
|
| 199 |
+
"request": {
|
| 200 |
+
"method": "GET",
|
| 201 |
+
"header": [],
|
| 202 |
+
"url": {
|
| 203 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/category-expenses/68a834c3f4694b11efedacd2?months=3",
|
| 204 |
+
"protocol": "https",
|
| 205 |
+
"host": [
|
| 206 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 207 |
+
"hf",
|
| 208 |
+
"space"
|
| 209 |
+
],
|
| 210 |
+
"path": [
|
| 211 |
+
"category-expenses",
|
| 212 |
+
"68a834c3f4694b11efedacd2"
|
| 213 |
+
],
|
| 214 |
+
"query": [
|
| 215 |
+
{
|
| 216 |
+
"key": "months",
|
| 217 |
+
"value": "3",
|
| 218 |
+
"description": "Number of months to analyze (default: 3)"
|
| 219 |
+
}
|
| 220 |
+
]
|
| 221 |
+
},
|
| 222 |
+
"description": "Get average expenses by category for the past N months"
|
| 223 |
+
}
|
| 224 |
+
},
|
| 225 |
+
{
|
| 226 |
+
"name": "Sample Expenses - Create Multiple",
|
| 227 |
+
"item": [
|
| 228 |
+
{
|
| 229 |
+
"name": "Groceries - Month 1 (Sept 2024)",
|
| 230 |
+
"request": {
|
| 231 |
+
"method": "POST",
|
| 232 |
+
"header": [
|
| 233 |
+
{
|
| 234 |
+
"key": "Content-Type",
|
| 235 |
+
"value": "application/json"
|
| 236 |
+
}
|
| 237 |
+
],
|
| 238 |
+
"body": {
|
| 239 |
+
"mode": "raw",
|
| 240 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 3500,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries - September 2024\",\n \"date\": \"2024-09-15T00:00:00\",\n \"type\": \"expense\"\n}"
|
| 241 |
+
},
|
| 242 |
+
"url": {
|
| 243 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
|
| 244 |
+
"protocol": "https",
|
| 245 |
+
"host": [
|
| 246 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 247 |
+
"hf",
|
| 248 |
+
"space"
|
| 249 |
+
],
|
| 250 |
+
"path": [
|
| 251 |
+
"expenses"
|
| 252 |
+
]
|
| 253 |
+
}
|
| 254 |
+
}
|
| 255 |
+
},
|
| 256 |
+
{
|
| 257 |
+
"name": "Groceries - Month 2 (Oct 2024)",
|
| 258 |
+
"request": {
|
| 259 |
+
"method": "POST",
|
| 260 |
+
"header": [
|
| 261 |
+
{
|
| 262 |
+
"key": "Content-Type",
|
| 263 |
+
"value": "application/json"
|
| 264 |
+
}
|
| 265 |
+
],
|
| 266 |
+
"body": {
|
| 267 |
+
"mode": "raw",
|
| 268 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 3800,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries - October 2024\",\n \"date\": \"2024-10-15T00:00:00\",\n \"type\": \"expense\"\n}"
|
| 269 |
+
},
|
| 270 |
+
"url": {
|
| 271 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
|
| 272 |
+
"protocol": "https",
|
| 273 |
+
"host": [
|
| 274 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 275 |
+
"hf",
|
| 276 |
+
"space"
|
| 277 |
+
],
|
| 278 |
+
"path": [
|
| 279 |
+
"expenses"
|
| 280 |
+
]
|
| 281 |
+
}
|
| 282 |
+
}
|
| 283 |
+
},
|
| 284 |
+
{
|
| 285 |
+
"name": "Groceries - Month 3 (Nov 2024)",
|
| 286 |
+
"request": {
|
| 287 |
+
"method": "POST",
|
| 288 |
+
"header": [
|
| 289 |
+
{
|
| 290 |
+
"key": "Content-Type",
|
| 291 |
+
"value": "application/json"
|
| 292 |
+
}
|
| 293 |
+
],
|
| 294 |
+
"body": {
|
| 295 |
+
"mode": "raw",
|
| 296 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 4000,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries - November 2024\",\n \"date\": \"2024-11-15T00:00:00\",\n \"type\": \"expense\"\n}"
|
| 297 |
+
},
|
| 298 |
+
"url": {
|
| 299 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
|
| 300 |
+
"protocol": "https",
|
| 301 |
+
"host": [
|
| 302 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 303 |
+
"hf",
|
| 304 |
+
"space"
|
| 305 |
+
],
|
| 306 |
+
"path": [
|
| 307 |
+
"expenses"
|
| 308 |
+
]
|
| 309 |
+
}
|
| 310 |
+
}
|
| 311 |
+
},
|
| 312 |
+
{
|
| 313 |
+
"name": "Transport - Month 1 (Sept 2024)",
|
| 314 |
+
"request": {
|
| 315 |
+
"method": "POST",
|
| 316 |
+
"header": [
|
| 317 |
+
{
|
| 318 |
+
"key": "Content-Type",
|
| 319 |
+
"value": "application/json"
|
| 320 |
+
}
|
| 321 |
+
],
|
| 322 |
+
"body": {
|
| 323 |
+
"mode": "raw",
|
| 324 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 2000,\n \"category\": \"Transport\",\n \"description\": \"Monthly transport - September 2024\",\n \"date\": \"2024-09-20T00:00:00\",\n \"type\": \"expense\"\n}"
|
| 325 |
+
},
|
| 326 |
+
"url": {
|
| 327 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
|
| 328 |
+
"protocol": "https",
|
| 329 |
+
"host": [
|
| 330 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 331 |
+
"hf",
|
| 332 |
+
"space"
|
| 333 |
+
],
|
| 334 |
+
"path": [
|
| 335 |
+
"expenses"
|
| 336 |
+
]
|
| 337 |
+
}
|
| 338 |
+
}
|
| 339 |
+
},
|
| 340 |
+
{
|
| 341 |
+
"name": "Transport - Month 2 (Oct 2024)",
|
| 342 |
+
"request": {
|
| 343 |
+
"method": "POST",
|
| 344 |
+
"header": [
|
| 345 |
+
{
|
| 346 |
+
"key": "Content-Type",
|
| 347 |
+
"value": "application/json"
|
| 348 |
+
}
|
| 349 |
+
],
|
| 350 |
+
"body": {
|
| 351 |
+
"mode": "raw",
|
| 352 |
+
"raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 2200,\n \"category\": \"Transport\",\n \"description\": \"Monthly transport - October 2024\",\n \"date\": \"2024-10-20T00:00:00\",\n \"type\": \"expense\"\n}"
|
| 353 |
+
},
|
| 354 |
+
"url": {
|
| 355 |
+
"raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
|
| 356 |
+
"protocol": "https",
|
| 357 |
+
"host": [
|
| 358 |
+
"logicgoinfotechspaces-smart-budget-recommendation",
|
| 359 |
+
"hf",
|
| 360 |
+
"space"
|
| 361 |
+
],
|
| 362 |
+
"path": [
|
| 363 |
+
"expenses"
|
| 364 |
+
]
|
| 365 |
+
}
|
| 366 |
+
}
|
| 367 |
+
}
|
| 368 |
+
]
|
| 369 |
+
}
|
| 370 |
+
],
|
| 371 |
+
"variable": [
|
| 372 |
+
{
|
| 373 |
+
"key": "base_url",
|
| 374 |
+
"value": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space",
|
| 375 |
+
"type": "string"
|
| 376 |
+
},
|
| 377 |
+
{
|
| 378 |
+
"key": "user_id",
|
| 379 |
+
"value": "68a834c3f4694b11efedacd2",
|
| 380 |
+
"type": "string"
|
| 381 |
+
}
|
| 382 |
+
]
|
| 383 |
+
}
|
| 384 |
+
|
app/models.py
CHANGED
|
@@ -29,6 +29,10 @@ class BudgetRecommendation(BaseModel):
|
|
| 29 |
recommended_budget: float
|
| 30 |
reason: str
|
| 31 |
confidence: float = Field(..., ge=0, le=1, description="Confidence score (0-1)")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
|
| 33 |
class CategoryExpense(BaseModel):
|
| 34 |
category: str
|
|
|
|
| 29 |
recommended_budget: float
|
| 30 |
reason: str
|
| 31 |
confidence: float = Field(..., ge=0, le=1, description="Confidence score (0-1)")
|
| 32 |
+
action: Optional[str] = Field(
|
| 33 |
+
None,
|
| 34 |
+
description="AI suggestion: increase, decrease, or keep"
|
| 35 |
+
)
|
| 36 |
|
| 37 |
class CategoryExpense(BaseModel):
|
| 38 |
category: str
|
app/smart_recommendation.py
CHANGED
|
@@ -1,8 +1,18 @@
|
|
| 1 |
-
|
| 2 |
-
|
|
|
|
| 3 |
from collections import defaultdict
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
from app.models import BudgetRecommendation, CategoryExpense
|
| 5 |
-
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
class SmartBudgetRecommender:
|
| 8 |
"""
|
|
@@ -14,6 +24,7 @@ class SmartBudgetRecommender:
|
|
| 14 |
|
| 15 |
def __init__(self, db):
|
| 16 |
self.db = db
|
|
|
|
| 17 |
|
| 18 |
def get_recommendations(self, user_id: str, month: int, year: int) -> List[BudgetRecommendation]:
|
| 19 |
"""
|
|
@@ -51,12 +62,21 @@ class SmartBudgetRecommender:
|
|
| 51 |
|
| 52 |
reason = self._generate_reason(category, avg_expense, recommended_budget)
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
recommendations.append(BudgetRecommendation(
|
| 55 |
category=category,
|
| 56 |
average_expense=round(avg_expense, 2),
|
| 57 |
recommended_budget=round(recommended_budget, 2),
|
| 58 |
reason=reason,
|
| 59 |
-
confidence=confidence
|
|
|
|
| 60 |
))
|
| 61 |
|
| 62 |
# Sort by average expense (highest first)
|
|
@@ -216,3 +236,41 @@ class SmartBudgetRecommender:
|
|
| 216 |
result.sort(key=lambda x: x.average_monthly_expense, reverse=True)
|
| 217 |
return result
|
| 218 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import math
|
| 3 |
+
import os
|
| 4 |
from collections import defaultdict
|
| 5 |
+
from datetime import datetime, timedelta
|
| 6 |
+
from typing import Dict, List
|
| 7 |
+
|
| 8 |
+
from dotenv import load_dotenv
|
| 9 |
+
from openai import OpenAI
|
| 10 |
+
|
| 11 |
from app.models import BudgetRecommendation, CategoryExpense
|
| 12 |
+
|
| 13 |
+
load_dotenv()
|
| 14 |
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
|
| 15 |
+
openai_client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None
|
| 16 |
|
| 17 |
class SmartBudgetRecommender:
|
| 18 |
"""
|
|
|
|
| 24 |
|
| 25 |
def __init__(self, db):
|
| 26 |
self.db = db
|
| 27 |
+
self.openai_client = openai_client
|
| 28 |
|
| 29 |
def get_recommendations(self, user_id: str, month: int, year: int) -> List[BudgetRecommendation]:
|
| 30 |
"""
|
|
|
|
| 62 |
|
| 63 |
reason = self._generate_reason(category, avg_expense, recommended_budget)
|
| 64 |
|
| 65 |
+
ai_result = self._get_ai_recommendation(category, data, avg_expense, recommended_budget)
|
| 66 |
+
if ai_result:
|
| 67 |
+
recommended_budget = ai_result.get("recommended_budget", recommended_budget)
|
| 68 |
+
reason = ai_result.get("reason", reason)
|
| 69 |
+
action = ai_result.get("action")
|
| 70 |
+
else:
|
| 71 |
+
action = None
|
| 72 |
+
|
| 73 |
recommendations.append(BudgetRecommendation(
|
| 74 |
category=category,
|
| 75 |
average_expense=round(avg_expense, 2),
|
| 76 |
recommended_budget=round(recommended_budget, 2),
|
| 77 |
reason=reason,
|
| 78 |
+
confidence=confidence,
|
| 79 |
+
action=action
|
| 80 |
))
|
| 81 |
|
| 82 |
# Sort by average expense (highest first)
|
|
|
|
| 236 |
result.sort(key=lambda x: x.average_monthly_expense, reverse=True)
|
| 237 |
return result
|
| 238 |
|
| 239 |
+
def _get_ai_recommendation(self, category: str, data: Dict, avg_expense: float, fallback_budget: float):
|
| 240 |
+
"""Use OpenAI to refine the budget recommendation."""
|
| 241 |
+
if not self.openai_client:
|
| 242 |
+
return None
|
| 243 |
+
|
| 244 |
+
history = ", ".join(f"{value:.0f}" for value in data["monthly_values"])
|
| 245 |
+
summary = (
|
| 246 |
+
f"Category: {category}\n"
|
| 247 |
+
f"Monthly totals: [{history}]\n"
|
| 248 |
+
f"Average spend: {avg_expense:.2f}\n"
|
| 249 |
+
f"Std deviation: {data['std_dev']:.2f}\n"
|
| 250 |
+
f"Months observed: {data['months_analyzed']}\n"
|
| 251 |
+
f"Current suggested budget: {fallback_budget:.2f}\n"
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
prompt = (
|
| 255 |
+
"You are an Indian personal finance coach. "
|
| 256 |
+
"Given the user's spending history, decide whether to increase, decrease, "
|
| 257 |
+
"or keep the upcoming month's budget and provide a short explanation. "
|
| 258 |
+
"Respond strictly as JSON with the following keys:\n"
|
| 259 |
+
'{ "recommended_budget": number, "action": "increase|decrease|keep", "reason": "string" }.\n'
|
| 260 |
+
"Use rupees for all amounts.\n\n"
|
| 261 |
+
f"{summary}"
|
| 262 |
+
)
|
| 263 |
+
|
| 264 |
+
try:
|
| 265 |
+
response = self.openai_client.responses.create(
|
| 266 |
+
model="gpt-4.1-mini",
|
| 267 |
+
input=prompt,
|
| 268 |
+
temperature=0.1,
|
| 269 |
+
response_format={"type": "json_object"},
|
| 270 |
+
)
|
| 271 |
+
content = response.output[0].content[0].text
|
| 272 |
+
return json.loads(content)
|
| 273 |
+
except Exception as exc:
|
| 274 |
+
print(f"OpenAI recommendation error for {category}: {exc}")
|
| 275 |
+
return None
|
| 276 |
+
|
requirements.txt
CHANGED
|
@@ -3,4 +3,6 @@ uvicorn[standard]==0.24.0
|
|
| 3 |
pymongo==4.6.0
|
| 4 |
pydantic==2.5.0
|
| 5 |
python-multipart==0.0.6
|
|
|
|
|
|
|
| 6 |
|
|
|
|
| 3 |
pymongo==4.6.0
|
| 4 |
pydantic==2.5.0
|
| 5 |
python-multipart==0.0.6
|
| 6 |
+
openai==1.51.0
|
| 7 |
+
python-dotenv==1.0.1
|
| 8 |
|
test_hf_api.py
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Test script for Hugging Face deployed API
|
| 3 |
+
Creates sample expenses and tests recommendations
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import requests
|
| 7 |
+
import json
|
| 8 |
+
from datetime import datetime, timedelta
|
| 9 |
+
|
| 10 |
+
BASE_URL = "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space"
|
| 11 |
+
USER_ID = "68a834c3f4694b11efedacd2"
|
| 12 |
+
|
| 13 |
+
def test_health():
|
| 14 |
+
"""Test health endpoint"""
|
| 15 |
+
print("=" * 60)
|
| 16 |
+
print("1. Testing Health Endpoint")
|
| 17 |
+
print("=" * 60)
|
| 18 |
+
try:
|
| 19 |
+
response = requests.get(f"{BASE_URL}/health")
|
| 20 |
+
print(f"Status: {response.status_code}")
|
| 21 |
+
print(f"Response: {json.dumps(response.json(), indent=2)}")
|
| 22 |
+
return response.status_code == 200
|
| 23 |
+
except Exception as e:
|
| 24 |
+
print(f"Error: {e}")
|
| 25 |
+
return False
|
| 26 |
+
|
| 27 |
+
def create_sample_expenses():
|
| 28 |
+
"""Create multiple sample expenses across different months"""
|
| 29 |
+
print("\n" + "=" * 60)
|
| 30 |
+
print("2. Creating Sample Expenses")
|
| 31 |
+
print("=" * 60)
|
| 32 |
+
|
| 33 |
+
# Create expenses for the past 4 months
|
| 34 |
+
base_date = datetime(2024, 9, 15) # Start from September 2024
|
| 35 |
+
expenses = []
|
| 36 |
+
|
| 37 |
+
# Groceries - 4 months
|
| 38 |
+
for i in range(4):
|
| 39 |
+
date = base_date + timedelta(days=30 * i)
|
| 40 |
+
expenses.append({
|
| 41 |
+
"user_id": USER_ID,
|
| 42 |
+
"amount": 3500 + (i * 100), # Varying amounts: 3500, 3600, 3700, 3800
|
| 43 |
+
"category": "Groceries",
|
| 44 |
+
"description": f"Monthly groceries - {date.strftime('%B %Y')}",
|
| 45 |
+
"date": date.isoformat(),
|
| 46 |
+
"type": "expense"
|
| 47 |
+
})
|
| 48 |
+
|
| 49 |
+
# Transport - 3 months
|
| 50 |
+
for i in range(3):
|
| 51 |
+
date = base_date + timedelta(days=30 * i)
|
| 52 |
+
expenses.append({
|
| 53 |
+
"user_id": USER_ID,
|
| 54 |
+
"amount": 2000 + (i * 50), # 2000, 2050, 2100
|
| 55 |
+
"category": "Transport",
|
| 56 |
+
"description": f"Monthly transport - {date.strftime('%B %Y')}",
|
| 57 |
+
"date": date.isoformat(),
|
| 58 |
+
"type": "expense"
|
| 59 |
+
})
|
| 60 |
+
|
| 61 |
+
# Utilities - 2 months
|
| 62 |
+
for i in range(2):
|
| 63 |
+
date = base_date + timedelta(days=30 * i)
|
| 64 |
+
expenses.append({
|
| 65 |
+
"user_id": USER_ID,
|
| 66 |
+
"amount": 1500 + (i * 100), # 1500, 1600
|
| 67 |
+
"category": "Utilities",
|
| 68 |
+
"description": f"Monthly utilities - {date.strftime('%B %Y')}",
|
| 69 |
+
"date": date.isoformat(),
|
| 70 |
+
"type": "expense"
|
| 71 |
+
})
|
| 72 |
+
|
| 73 |
+
created_count = 0
|
| 74 |
+
for expense in expenses:
|
| 75 |
+
try:
|
| 76 |
+
response = requests.post(
|
| 77 |
+
f"{BASE_URL}/expenses",
|
| 78 |
+
json=expense,
|
| 79 |
+
headers={"Content-Type": "application/json"}
|
| 80 |
+
)
|
| 81 |
+
if response.status_code == 200:
|
| 82 |
+
print(f"[OK] Created: {expense['category']} - Rs.{expense['amount']} ({expense['date'][:10]})")
|
| 83 |
+
created_count += 1
|
| 84 |
+
else:
|
| 85 |
+
print(f"[FAIL] Failed: {expense['category']} - {response.text}")
|
| 86 |
+
except Exception as e:
|
| 87 |
+
print(f"[ERROR] Error creating expense: {e}")
|
| 88 |
+
|
| 89 |
+
print(f"\nTotal expenses created: {created_count}/{len(expenses)}")
|
| 90 |
+
return created_count > 0
|
| 91 |
+
|
| 92 |
+
def get_expenses():
|
| 93 |
+
"""Get all expenses for the user"""
|
| 94 |
+
print("\n" + "=" * 60)
|
| 95 |
+
print("3. Getting All Expenses")
|
| 96 |
+
print("=" * 60)
|
| 97 |
+
try:
|
| 98 |
+
response = requests.get(
|
| 99 |
+
f"{BASE_URL}/expenses",
|
| 100 |
+
params={"user_id": USER_ID, "limit": 100}
|
| 101 |
+
)
|
| 102 |
+
if response.status_code == 200:
|
| 103 |
+
expenses = response.json()
|
| 104 |
+
print(f"Found {len(expenses)} expenses:\n")
|
| 105 |
+
for exp in expenses:
|
| 106 |
+
print(f" - {exp['category']}: Rs.{exp['amount']} ({exp['date'][:10]})")
|
| 107 |
+
return True
|
| 108 |
+
else:
|
| 109 |
+
print(f"Error: {response.status_code} - {response.text}")
|
| 110 |
+
return False
|
| 111 |
+
except Exception as e:
|
| 112 |
+
print(f"Error: {e}")
|
| 113 |
+
return False
|
| 114 |
+
|
| 115 |
+
def test_recommendations():
|
| 116 |
+
"""Test getting budget recommendations"""
|
| 117 |
+
print("\n" + "=" * 60)
|
| 118 |
+
print("4. Testing Smart Budget Recommendations")
|
| 119 |
+
print("=" * 60)
|
| 120 |
+
try:
|
| 121 |
+
# Get recommendations for next month
|
| 122 |
+
next_month = datetime.now().month + 1
|
| 123 |
+
next_year = datetime.now().year
|
| 124 |
+
if next_month > 12:
|
| 125 |
+
next_month = 1
|
| 126 |
+
next_year += 1
|
| 127 |
+
|
| 128 |
+
response = requests.get(
|
| 129 |
+
f"{BASE_URL}/recommendations/{USER_ID}",
|
| 130 |
+
params={"month": next_month, "year": next_year}
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
if response.status_code == 200:
|
| 134 |
+
recommendations = response.json()
|
| 135 |
+
print(f"\nFound {len(recommendations)} recommendations:\n")
|
| 136 |
+
|
| 137 |
+
if len(recommendations) == 0:
|
| 138 |
+
print("[WARNING] No recommendations yet. Need more expense data (at least 2-3 months).")
|
| 139 |
+
else:
|
| 140 |
+
for rec in recommendations:
|
| 141 |
+
print(f"Category: {rec['category']}")
|
| 142 |
+
print(f" Average Expense: Rs.{rec['average_expense']:,.0f}")
|
| 143 |
+
print(f" Recommended Budget: Rs.{rec['recommended_budget']:,.0f}")
|
| 144 |
+
print(f" Confidence: {rec['confidence']*100:.0f}%")
|
| 145 |
+
print(f" Reason: {rec['reason']}")
|
| 146 |
+
print()
|
| 147 |
+
return True
|
| 148 |
+
else:
|
| 149 |
+
print(f"Error: {response.status_code} - {response.text}")
|
| 150 |
+
return False
|
| 151 |
+
except Exception as e:
|
| 152 |
+
print(f"Error: {e}")
|
| 153 |
+
return False
|
| 154 |
+
|
| 155 |
+
def test_category_expenses():
|
| 156 |
+
"""Test getting category expense averages"""
|
| 157 |
+
print("\n" + "=" * 60)
|
| 158 |
+
print("5. Testing Category Expense Averages")
|
| 159 |
+
print("=" * 60)
|
| 160 |
+
try:
|
| 161 |
+
response = requests.get(
|
| 162 |
+
f"{BASE_URL}/category-expenses/{USER_ID}",
|
| 163 |
+
params={"months": 6}
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
if response.status_code == 200:
|
| 167 |
+
categories = response.json()
|
| 168 |
+
print(f"\nFound {len(categories)} categories:\n")
|
| 169 |
+
|
| 170 |
+
for cat in categories:
|
| 171 |
+
print(f"{cat['category']}:")
|
| 172 |
+
print(f" Average Monthly: Rs.{cat['average_monthly_expense']:,.0f}")
|
| 173 |
+
print(f" Total Transactions: {cat['total_expenses']}")
|
| 174 |
+
print(f" Months Analyzed: {cat['months_analyzed']}")
|
| 175 |
+
print()
|
| 176 |
+
return True
|
| 177 |
+
else:
|
| 178 |
+
print(f"Error: {response.status_code} - {response.text}")
|
| 179 |
+
return False
|
| 180 |
+
except Exception as e:
|
| 181 |
+
print(f"Error: {e}")
|
| 182 |
+
return False
|
| 183 |
+
|
| 184 |
+
if __name__ == "__main__":
|
| 185 |
+
print("\n" + "=" * 60)
|
| 186 |
+
print("Smart Budget Recommendation API - Hugging Face Test")
|
| 187 |
+
print("=" * 60)
|
| 188 |
+
print(f"\nBase URL: {BASE_URL}")
|
| 189 |
+
print(f"User ID: {USER_ID}\n")
|
| 190 |
+
|
| 191 |
+
results = []
|
| 192 |
+
|
| 193 |
+
# Run tests
|
| 194 |
+
results.append(("Health Check", test_health()))
|
| 195 |
+
results.append(("Create Expenses", create_sample_expenses()))
|
| 196 |
+
results.append(("Get Expenses", get_expenses()))
|
| 197 |
+
results.append(("Get Recommendations", test_recommendations()))
|
| 198 |
+
results.append(("Category Averages", test_category_expenses()))
|
| 199 |
+
|
| 200 |
+
# Summary
|
| 201 |
+
print("\n" + "=" * 60)
|
| 202 |
+
print("Test Summary")
|
| 203 |
+
print("=" * 60)
|
| 204 |
+
for test_name, passed in results:
|
| 205 |
+
status = "[PASS]" if passed else "[FAIL]"
|
| 206 |
+
print(f"{status} - {test_name}")
|
| 207 |
+
|
| 208 |
+
passed_count = sum(1 for _, passed in results if passed)
|
| 209 |
+
print(f"\nTotal: {passed_count}/{len(results)} tests passed")
|
| 210 |
+
|
| 211 |
+
print("\n" + "=" * 60)
|
| 212 |
+
print("API Documentation:")
|
| 213 |
+
print(f" Swagger UI: {BASE_URL}/docs")
|
| 214 |
+
print("=" * 60)
|
| 215 |
+
|