Skip to content
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,86 @@ This will contain the model used for the project that based on the input informa
The model works off of dummy data of several combinations of clients alongside the interventions chosen for them as well as their success rate at finding a job afterward. The model will be updated by the case workers by inputing new data for clients with their updated outcome information, and it can be updated on a daily, weekly, or monthly basis.

This also has an API file to interact with the front end, and logic in order to process the interventions coming from the front end. This includes functions to clean data, create a matrix of all possible combinations in order to get the ones with the highest increase of success, and output the results in a way the front end can interact with.

## Assessment Tool Documentation

This API provides several endpoints to handle client-related operations, such as predicting outcomes based on input data, adding client information to an SQLite database, retrieving client information, updating client data, and deleting a client from the database.

### Prerequisites
To run this project, ensure that the following tools are installed on your system:

* Python 3.8 or later
* FastAPI
* SQLite

### API Endpoints
1. POST /clients/predictions
This endpoint accepts input data and returns a prediction result by calling the interpret_and_calculate logic.

Request:
* Method: POST
* URL: /clients/predictions

2. POST /clients/add
This endpoint adds the input data to the SQLite database and returns the newly created client_id along with the prediction result.

Request:
* Method: POST
* URL: /clients/add
* Body: Same as /clients/predictions

3. GET /clients/{client_id}
This endpoint retrieves a client's information by client_id from the database.

Request:
* Method: GET
* URL: /clients/{client_id}

4. PUT /clients/update/{client_id}
This endpoint updates the client information for the provided client_id using the input data.

Request:

* Method: PUT
* URL: /clients/update/{client_id}
* Body: Same as /clients/predictions

5. DELETE /clients/delete/{client_id}
This endpoint deletes the client record from the database using the provided client_id.

Request:
* Method: DELETE
* URL: /clients/delete/{client_id}

### Database Configuration
This project uses an SQLite database to store client information. The database schema can be defined as follows:

CREATE TABLE clients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
age INTEGER,
gender TEXT,
work_experience INTEGER,
canada_workex INTEGER,
dep_num INTEGER,
canada_born BOOLEAN,
citizen_status TEXT,
level_of_schooling TEXT,
fluent_english BOOLEAN,
reading_english_scale INTEGER,
speaking_english_scale INTEGER,
writing_english_scale INTEGER,
numeracy_scale INTEGER,
computer_scale INTEGER,
transportation_bool BOOLEAN,
caregiver_bool BOOLEAN,
housing TEXT,
income_source TEXT,
felony_bool BOOLEAN,
attending_school BOOLEAN,
currently_employed BOOLEAN,
substance_use TEXT,
time_unemployed INTEGER,
need_mental_health_support_bool BOOLEAN
);

You can create the database by running this SQL command on your SQLite database engine.
41 changes: 41 additions & 0 deletions app/clients/database.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import sqlite3

def get_db_connection():
conn = sqlite3.connect('clients.db')
conn.row_factory = sqlite3.Row
return conn


def create_clients_table():
conn = get_db_connection()
conn.execute('''
CREATE TABLE IF NOT EXISTS clients (
id INTEGER PRIMARY KEY AUTOINCREMENT,
age INTEGER,
gender INTEGER,
work_experience INTEGER,
canada_workex INTEGER,
dep_num INTEGER,
canada_born INTEGER,
citizen_status INTEGER,
level_of_schooling INTEGER,
fluent_english INTEGER,
reading_english_scale INTEGER,
speaking_english_scale INTEGER,
writing_english_scale INTEGER,
numeracy_scale INTEGER,
computer_scale INTEGER,
transportation_bool INTEGER,
caregiver_bool INTEGER,
housing INTEGER,
income_source INTEGER,
felony_bool INTEGER,
attending_school INTEGER,
currently_employed INTEGER,
substance_use INTEGER,
time_unemployed INTEGER,
need_mental_health_support_bool INTEGER
)
''')
conn.commit()
conn.close()
88 changes: 85 additions & 3 deletions app/clients/router.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from fastapi import APIRouter
from fastapi.responses import HTMLResponse

from fastapi import APIRouter, HTTPException
from app.clients.service.logic import interpret_and_calculate
from app.clients.schema import PredictionInput
from app.clients.database import get_db_connection

router = APIRouter(prefix="/clients", tags=["clients"])

Expand All @@ -12,4 +11,87 @@ async def predict(data: PredictionInput):
print(data.model_dump())
return interpret_and_calculate(data.model_dump())

@router.post("/add")
async def add(data: PredictionInput):
# Input data to SQLite
conn = get_db_connection()
conn.execute('''INSERT INTO clients
(age, gender, work_experience, canada_workex, dep_num, canada_born,
citizen_status, level_of_schooling, fluent_english, reading_english_scale,
speaking_english_scale, writing_english_scale, numeracy_scale, computer_scale,
transportation_bool, caregiver_bool, housing, income_source, felony_bool,
attending_school, currently_employed, substance_use, time_unemployed,
need_mental_health_support_bool)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''',
(data.age, data.gender, data.work_experience, data.canada_workex, data.dep_num, data.canada_born,
data.citizen_status, data.level_of_schooling, data.fluent_english, data.reading_english_scale,
data.speaking_english_scale, data.writing_english_scale, data.numeracy_scale, data.computer_scale,
data.transportation_bool, data.caregiver_bool, data.housing, data.income_source, data.felony_bool,
data.attending_school, data.currently_employed, data.substance_use, data.time_unemployed,
data.need_mental_health_support_bool))
conn.commit()
client_id = conn.execute("SELECT last_insert_rowid()").fetchone()[0]
conn.close()
return { "client_id": client_id,
"prediction_result": interpret_and_calculate(data.model_dump())}

# get client by id
@router.get("/{client_id}")
async def get_client(client_id: int):
conn = get_db_connection()
client = conn.execute('SELECT * FROM clients WHERE id = ?', (client_id,)).fetchone()
conn.close()
if client is None:
raise HTTPException(status_code=404, detail="Client not found")
return dict(client)

# update clients info by id
@router.put("/update/{client_id}")
async def update_client(client_id: int, data: PredictionInput):
conn = get_db_connection()

client = conn.execute('SELECT * FROM clients WHERE id = ?', (client_id,)).fetchone()
if client is None:
conn.close()
raise HTTPException(status_code=404, detail="Client not found")

conn.execute('''
UPDATE clients
SET age = ?, gender = ?, work_experience = ?, canada_workex = ?, dep_num = ?,
canada_born = ?, citizen_status = ?, level_of_schooling = ?, fluent_english = ?,
reading_english_scale = ?, speaking_english_scale = ?, writing_english_scale = ?,
numeracy_scale = ?, computer_scale = ?, transportation_bool = ?, caregiver_bool = ?,
housing = ?, income_source = ?, felony_bool = ?, attending_school = ?,
currently_employed = ?, substance_use = ?, time_unemployed = ?, need_mental_health_support_bool = ?
WHERE id = ?
''', (
data.age, data.gender, data.work_experience, data.canada_workex, data.dep_num,
data.canada_born, data.citizen_status, data.level_of_schooling, data.fluent_english,
data.reading_english_scale, data.speaking_english_scale, data.writing_english_scale,
data.numeracy_scale, data.computer_scale, data.transportation_bool, data.caregiver_bool,
data.housing, data.income_source, data.felony_bool, data.attending_school,
data.currently_employed, data.substance_use, data.time_unemployed,
data.need_mental_health_support_bool, client_id
))

conn.commit()
conn.close()

return {"message": "Client information updated successfully", "client_id": client_id}

# delete clients info by id
@router.delete("/delete/{client_id}")
async def delete_client(client_id: int):
conn = get_db_connection()

client = conn.execute('SELECT * FROM clients WHERE id = ?', (client_id,)).fetchone()
if client is None:
conn.close()
raise HTTPException(status_code=404, detail="Client not found")

conn.execute('DELETE FROM clients WHERE id = ?', (client_id,))
conn.commit()
conn.close()

return {"message": f"Client with id {client_id} deleted successfully."}
8 changes: 4 additions & 4 deletions app/clients/service/logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
def clean_input_data(data):
#translate input into wahtever we trained the model on, numerical data in a specific order
columns = ["age","gender","work_experience","canada_workex","dep_num", "canada_born",
"citizen_status", "level_of_schooling", "fluent_english", "reading_english_scale",
"speaking_english_scale", "writing_english_scale", "numeracy_scale", "computer_scale",
"transportation_bool", "caregiver_bool", "housing", "income_source", "felony_bool", "attending_school",
"currently_employed", "substance_use", "time_unemployed", "need_mental_health_support_bool"]
"citizen_status", "level_of_schooling", "fluent_english", "reading_english_scale",
"speaking_english_scale", "writing_english_scale", "numeracy_scale", "computer_scale",
"transportation_bool", "caregiver_bool", "housing", "income_source", "felony_bool", "attending_school",
"currently_employed", "substance_use", "time_unemployed", "need_mental_health_support_bool"]
demographics = {
'age': data['age'],
'gender': data['gender'],
Expand Down
8 changes: 6 additions & 2 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from app.clients.router import router as clients_router

from app.clients.database import create_clients_table # input from database.py

app = FastAPI()

Expand All @@ -17,4 +16,9 @@
allow_headers=["*"], # Allows all headers
)

@app.on_event("startup")
async def startup_event():
create_clients_table()

if __name__ == "__main__":
create_clients_table()
Binary file added clients.db
Binary file not shown.
86 changes: 84 additions & 2 deletions tests/test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from logic import interpret_and_calculate
from app.clients.service.logic import interpret_and_calculate
from itertools import combinations_with_replacement

# def test_interpret_and_calculate():
Expand All @@ -20,4 +20,86 @@
result = list(combinations_with_replacement([0, 1], 2))

# Output: [(0, 0), (0, 1), (1, 1)]
print(result)
print(result)

from fastapi.testclient import TestClient
from app.main import app # Replace with the correct path to your FastAPI app

client = TestClient(app)

# Sample client data
sample_data = {
"age": "37",
"gender": "2",
"work_experience": "1",
"canada_workex": "1",
"dep_num": "0",
"canada_born": "1",
"citizen_status": "2",
"level_of_schooling": "2",
"fluent_english": "3",
"reading_english_scale": "2",
"speaking_english_scale": "2",
"writing_english_scale": "3",
"numeracy_scale": "2",
"computer_scale": "3",
"transportation_bool": "2",
"caregiver_bool": "1",
"housing": "1",
"income_source": "5",
"felony_bool": "1",
"attending_school": "0",
"currently_employed": "1",
"substance_use": "1",
"time_unemployed": "1",
"need_mental_health_support_bool": "1"
}

# 1. Test Add Client
def test_add_client():
response = client.post("/clients/add", json=sample_data)
if response.status_code == 200:
data = response.json()
print(f"Client added successfully: {data}")
return data["client_id"]
else:
print(f"Failed to add client: {response.status_code}")
return None

# 2. Test Get Client by ID
def test_get_client(client_id):
response = client.get(f"/clients/{client_id}")
if response.status_code == 200:
print(f"Client fetched successfully: {response.json()}")
else:
print(f"Failed to fetch client: {response.status_code}")

# 3. Test Update Client
def test_update_client(client_id):
updated_data = sample_data.copy()
updated_data["age"] = 30 # Update some field
response = client.put(f"/clients/update/{client_id}", json=updated_data)
if response.status_code == 200:
print(f"Client updated successfully: {response.json()}")
else:
print(f"Failed to update client: {response.status_code}")

# 4. Test Delete Client by ID
def test_delete_client(client_id):
response = client.delete(f"/clients/delete/{client_id}")
if response.status_code == 200:
print(f"Client deleted successfully: {response.json()}")
else:
print(f"Failed to delete client: {response.status_code}")

# Running the tests
if __name__ == "__main__":
# Test adding a client
client_id = test_add_client()
if client_id:
# Test fetching the added client
test_get_client(client_id)
# Test updating the client
test_update_client(client_id)
# Test deleting the client
test_delete_client(client_id)