From 1f1eb2185e3c800b0b2213be6343c5e1e8bbc48f Mon Sep 17 00:00:00 2001 From: yunfeng Date: Wed, 6 Nov 2024 20:11:12 -0800 Subject: [PATCH 01/13] added get method --- app/clients/router.py | 46 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/app/clients/router.py b/app/clients/router.py index f860c402..c5d131d5 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -1,5 +1,8 @@ -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException from fastapi.responses import HTMLResponse +from fastapi.responses import JSONResponse +import os +import pandas as pd from app.clients.service.logic import interpret_and_calculate from app.clients.schema import PredictionInput @@ -12,4 +15,45 @@ async def predict(data: PredictionInput): print(data.model_dump()) return interpret_and_calculate(data.model_dump()) +@router.get("/read-csv", response_class=JSONResponse) +async def read_csv(): + try: + current_dir = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') + + data = pd.read_csv(file_path) + + return data.head().to_dict(orient="records") + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error reading CSV: {e}") +async def load_csv(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + filename = os.path.join(current_dir, 'service', 'data_commontool.csv') + try: + df = pd.read_csv(filename) + return df + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error reading CSV: {str(e)}") + +@router.get("/get-age", response_model=list) +async def get_age(): + df = await load_csv() + if 'age' not in df.columns: + raise HTTPException(status_code=404, detail="Column 'age' not found in CSV") + return df['age'].tolist() + +@router.get("/get-gender", response_model=list) +async def get_gender(): + df = await load_csv() + if 'gender' not in df.columns: + raise HTTPException(status_code=404, detail="Column 'gender' not found in CSV") + return df['gender'].tolist() + +@router.get("/get-born-place", response_model=list) +async def get_born_place(): + df = await load_csv() + if 'canada_born' not in df.columns: + raise HTTPException(status_code=404, detail="Column 'canada_born' not found in CSV") + return df['canada_born'].tolist() \ No newline at end of file From 5af0fae316ef50eddf706bfbce50e5ab6555f949 Mon Sep 17 00:00:00 2001 From: yixuansun Date: Wed, 6 Nov 2024 20:40:49 -0800 Subject: [PATCH 02/13] create and read --- app/clients/router.py | 62 ++++++++++++++++++++++++- app/clients/service/data_commontool.csv | 2 +- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/app/clients/router.py b/app/clients/router.py index f860c402..6cf0c3f3 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -1,5 +1,8 @@ -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException from fastapi.responses import HTMLResponse +from fastapi.responses import JSONResponse +import os +import pandas as pd from app.clients.service.logic import interpret_and_calculate from app.clients.schema import PredictionInput @@ -12,4 +15,61 @@ async def predict(data: PredictionInput): print(data.model_dump()) return interpret_and_calculate(data.model_dump()) +# create a user +@router.post("/create-user", response_class=JSONResponse) +async def create_user(data: PredictionInput): + try: + current_dir = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') + new_data = pd.DataFrame([data.dict()]) + new_data.to_csv(file_path, mode='a', index=False, header=False) + + print("User created:", data.dict()) + return JSONResponse(content={"message": "User created successfully", "user": data.dict()}) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error creating user: {e}") +# Get user data +@router.get("/read-csv", response_class=JSONResponse) +async def read_csv(): + try: + current_dir = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') + + data = pd.read_csv(file_path) + + return data.head().to_dict(orient="records") + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error reading CSV: {e}") + + +async def load_csv(): + current_dir = os.path.dirname(os.path.abspath(__file__)) + filename = os.path.join(current_dir, 'service', 'data_commontool.csv') + try: + df = pd.read_csv(filename) + return df + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error reading CSV: {str(e)}") + +@router.get("/get-age", response_model=list) +async def get_age(): + df = await load_csv() + if 'age' not in df.columns: + raise HTTPException(status_code=404, detail="Column 'age' not found in CSV") + return df['age'].tolist() + +@router.get("/get-gender", response_model=list) +async def get_gender(): + df = await load_csv() + if 'gender' not in df.columns: + raise HTTPException(status_code=404, detail="Column 'gender' not found in CSV") + return df['gender'].tolist() + +@router.get("/get-born-place", response_model=list) +async def get_born_place(): + df = await load_csv() + if 'canada_born' not in df.columns: + raise HTTPException(status_code=404, detail="Column 'canada_born' not found in CSV") + return df['canada_born'].tolist() \ No newline at end of file diff --git a/app/clients/service/data_commontool.csv b/app/clients/service/data_commontool.csv index 09499d98..1e63e8a0 100644 --- a/app/clients/service/data_commontool.csv +++ b/app/clients/service/data_commontool.csv @@ -147,4 +147,4 @@ age,gender,work_experience,canada_workex,dep_num,canada_born,citizen_status,leve 46,2,2,6,2,0,0,9,0,6,6,8,8,3,0,0,10,5,0,1,1,0,3,0,0,1,1,0,0,1,1,70 47,1,3,7,0,1,1,10,1,7,7,9,9,4,1,1,1,6,0,0,0,0,4,1,0,0,0,0,0,0,0,60 48,2,4,8,4,0,0,11,0,8,8,8,10,5,0,0,2,8,0,1,0,0,1,1,0,1,0,0,0,1,0,90 -49,1,5,9,2,1,1,12,1,9,9,9,1,6,1,1,3,9,0,0,1,0,2,1,0,0,1,0,0,0,1,80 \ No newline at end of file +49,1,5,9,2,1,1,12,1,9,9,9,1,6,1,1,3,9,0,0,1,0,2,1,0,0,1,0,0,0,1,8030,male,5,2,0,no,permanent_resident,bachelor,yes,8,9,8,7,8,yes,no,rent,employment,no,no,yes,no,1,no From 1ba32ea10dfa38b8e128a64bd0ada40329b59452 Mon Sep 17 00:00:00 2001 From: wangdairou Date: Mon, 18 Nov 2024 21:46:33 -0800 Subject: [PATCH 03/13] Fix the bug of create user, add get client and delete client in router and test successfully --- .gitignore | 1 + app/clients/router.py | 92 +++++++- app/clients/service/data_commontool.csv | 300 ++++++++++++------------ app/main.py | 2 +- app/requirements.txt | 48 ++-- 5 files changed, 253 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 14d7fa72..281d9273 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .venv +venv/ .idea __pycache__ .DS_Store diff --git a/app/clients/router.py b/app/clients/router.py index 6cf0c3f3..7b82ef87 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -4,8 +4,8 @@ import os import pandas as pd -from app.clients.service.logic import interpret_and_calculate -from app.clients.schema import PredictionInput +from .service.logic import interpret_and_calculate +from .schema import PredictionInput router = APIRouter(prefix="/clients", tags=["clients"]) @@ -21,9 +21,18 @@ async def create_user(data: PredictionInput): try: current_dir = os.path.dirname(os.path.abspath(__file__)) file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') - new_data = pd.DataFrame([data.dict()]) - new_data.to_csv(file_path, mode='a', index=False, header=False) - + new_data = pd.DataFrame([data.dict()]) + if os.path.exists(file_path): + # Add newline before appending if file is not empty + if os.path.getsize(file_path) > 0: + with open(file_path, 'a', newline='') as f: + new_data.to_csv(f, index=False, header=False) + else: + # If file is empty, write with headers + new_data.to_csv(file_path, index=False) + else: + # If file doesn't exist, create it with headers + new_data.to_csv(file_path, index=False) print("User created:", data.dict()) return JSONResponse(content={"message": "User created successfully", "user": data.dict()}) except Exception as e: @@ -44,6 +53,79 @@ async def read_csv(): raise HTTPException(status_code=500, detail=f"Error reading CSV: {e}") +# Get specific client by unique identifier +@router.get("/client/{client_index}", response_class=JSONResponse) +async def get_client(client_index: int): + try: + current_dir = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') + data = pd.read_csv(file_path) + + if client_index >= len(data): + raise HTTPException(status_code=404, detail="Client not found") + + client_data = data.iloc[client_index].to_dict() + return JSONResponse(content=client_data) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error retrieving client: {e}") + + +# # Update specific client information +# @router.put("/client/{client_index}", response_class=JSONResponse) +# async def update_client(client_index: int, data: PredictionInput): +# try: +# current_dir = os.path.dirname(os.path.abspath(__file__)) +# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') +# df = pd.read_csv(file_path) +# +# if client_index >= len(df): +# raise HTTPException(status_code=404, detail="Client not found") +# +# # Update the client data +# df.iloc[client_index] = pd.Series(data.dict()) +# df.to_csv(file_path, index=False) +# +# # Run updated prediction +# updated_prediction = interpret_and_calculate(data.dict()) +# +# return JSONResponse( +# content={ +# "message": "Client updated successfully", +# "updated_data": data.dict(), +# "new_prediction": updated_prediction +# } +# ) +# except Exception as e: +# raise HTTPException(status_code=500, detail=f"Error updating client: {e}") + + +# Delete specific client +@router.delete("/client/{client_index}", response_class=JSONResponse) +async def delete_client(client_index: int): + try: + current_dir = os.path.dirname(os.path.abspath(__file__)) + file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') + df = pd.read_csv(file_path) + + if client_index >= len(df): + raise HTTPException(status_code=404, detail="Client not found") + + # Store client data before deletion for response + deleted_client = df.iloc[client_index].to_dict() + + # Delete the client + df = df.drop(client_index) + df.to_csv(file_path, index=False) + + return JSONResponse( + content={ + "message": "Client deleted successfully", + "deleted_client": deleted_client + } + ) + except Exception as e: + raise HTTPException(status_code=500, detail=f"Error deleting client: {e}") + async def load_csv(): current_dir = os.path.dirname(os.path.abspath(__file__)) filename = os.path.join(current_dir, 'service', 'data_commontool.csv') diff --git a/app/clients/service/data_commontool.csv b/app/clients/service/data_commontool.csv index 1e63e8a0..e7831af6 100644 --- a/app/clients/service/data_commontool.csv +++ b/app/clients/service/data_commontool.csv @@ -1,150 +1,150 @@ -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,employment_assistance,life_stabilization,retention_services,specialized_services,employment_related_financial_supports,employer_financial_supports,enhanced_referrals,success_rate -20,1,1,1,3,0,0,1,0,1,8,2,3,4,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,80 -21,2,2,2,5,1,1,2,1,2,9,3,4,5,1,0,2,2,1,1,1,0,2,0,1,1,1,0,1,1,1,30 -22,1,3,3,1,0,0,3,0,3,10,4,5,6,0,1,3,3,0,0,0,0,3,0,0,0,0,0,0,0,0,40 -23,2,4,4,2,1,1,4,1,4,1,5,6,7,1,0,4,4,1,1,1,0,4,1,1,1,1,0,1,1,1,70 -24,1,5,5,0,0,0,5,0,5,2,6,7,8,0,1,5,5,0,0,0,0,1,1,0,0,0,0,0,0,0,60 -25,2,6,6,2,1,1,6,1,6,3,7,8,9,1,0,6,6,0,1,1,0,2,0,0,1,1,0,0,1,1,40 -26,1,7,7,1,0,0,7,0,7,4,8,9,8,0,1,7,7,1,0,0,1,3,0,1,0,0,1,1,0,0,10 -27,2,8,8,0,1,1,8,1,8,5,9,8,9,1,0,8,8,0,1,1,1,4,0,0,1,1,1,0,1,1,20 -28,1,9,9,1,0,0,9,0,9,6,8,9,10,0,1,9,9,1,0,0,1,1,0,1,0,0,1,1,0,0,60 -29,2,10,10,0,1,1,10,1,10,7,9,10,1,1,0,10,10,0,1,1,1,2,0,0,1,1,1,0,1,1,50 -30,1,1,1,2,0,0,11,0,1,8,10,1,2,0,0,1,2,1,0,0,1,3,0,1,0,0,1,1,0,0,100 -31,2,2,2,0,1,1,12,1,2,9,1,2,3,1,1,2,3,0,1,1,1,4,0,0,1,1,1,0,1,1,40 -32,1,3,3,4,0,0,13,0,3,8,2,3,4,0,0,3,4,1,0,0,1,1,1,1,0,0,1,1,0,0,70 -33,2,4,4,2,1,1,1,1,4,9,3,4,5,1,1,4,5,0,1,1,0,2,1,0,1,1,0,0,1,1,90 -34,1,5,5,0,0,0,2,0,5,10,4,5,6,0,0,5,6,1,0,0,0,3,1,1,0,0,0,1,0,0,30 -35,2,6,6,3,0,0,3,0,6,1,5,6,7,0,1,6,7,0,0,0,0,1,1,0,0,0,0,0,0,0,50 -36,1,7,7,5,1,1,4,1,7,2,6,7,8,1,0,7,8,1,1,1,0,2,1,1,1,1,0,1,1,1,70 -37,2,8,8,1,0,0,5,0,8,3,7,8,9,0,1,8,9,0,0,0,0,3,1,0,0,0,0,0,0,0,40 -38,1,9,9,2,1,1,6,1,9,4,8,9,8,1,0,9,10,1,1,1,0,4,1,1,1,1,0,1,1,1,30 -39,2,10,10,0,0,0,7,0,10,5,9,8,9,0,1,10,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0 -40,1,1,1,2,1,1,8,1,1,6,8,9,10,1,0,1,2,0,1,1,1,2,0,0,1,1,1,0,1,1,80 -41,2,2,2,1,0,0,9,0,2,7,9,10,1,0,1,2,3,1,0,0,1,3,0,1,0,0,1,1,0,0,30 -42,1,3,3,0,1,1,10,1,3,8,10,1,2,1,0,3,4,0,1,1,1,4,0,0,1,1,1,0,1,1,20 -43,2,4,4,1,0,0,11,0,4,9,1,2,3,0,1,4,5,1,0,0,0,1,0,1,0,0,0,1,0,0,60 -44,1,5,5,0,1,1,12,1,5,8,2,3,4,1,0,5,6,0,1,1,0,2,0,0,1,1,0,0,1,1,60 -45,2,6,6,2,0,0,13,0,6,9,3,4,5,0,0,6,7,1,0,0,0,3,1,1,0,0,0,1,0,0,50 -46,1,7,7,0,1,1,1,1,7,10,4,5,6,1,1,7,8,0,1,1,0,4,1,0,1,1,0,0,1,1,60 -47,2,8,8,4,0,0,2,0,8,1,5,6,7,0,0,8,9,1,0,0,0,1,1,1,0,0,0,1,0,0,70 -48,1,9,9,2,1,1,3,1,9,2,6,7,8,1,1,9,10,0,1,1,0,2,1,0,1,1,0,0,1,1,100 -49,2,10,10,0,0,0,4,0,10,3,7,8,3,0,0,6,1,1,0,0,1,3,0,1,0,0,1,1,0,0,40 -50,1,1,1,3,0,0,5,0,1,4,8,9,4,0,1,7,2,0,1,1,1,1,0,0,1,1,1,0,1,1,80 -51,2,2,2,5,1,1,6,1,2,5,9,8,5,1,0,8,3,1,0,0,1,2,0,1,0,0,1,1,0,0,40 -52,1,3,3,1,0,0,7,0,3,6,8,9,6,0,1,9,4,0,1,1,0,3,0,0,1,1,0,0,1,1,100 -53,2,4,4,2,1,1,8,1,4,7,9,10,7,1,0,10,5,1,0,0,0,4,0,1,0,0,0,1,0,0,50 -54,1,5,5,0,0,0,9,0,5,8,10,1,8,0,1,1,6,0,1,1,0,1,0,0,1,1,0,0,1,1,60 -55,2,6,6,2,1,1,10,1,6,9,1,2,9,1,0,2,7,0,0,0,0,2,1,0,0,0,0,0,0,0,20 -56,1,7,7,1,0,0,11,0,7,8,2,3,8,0,1,3,8,1,1,1,0,3,1,1,1,1,0,1,1,1,70 -57,2,8,8,0,1,1,12,1,8,9,3,4,9,1,0,4,9,0,0,0,0,4,1,0,0,0,0,0,0,0,60 -58,1,9,9,1,0,0,13,0,9,10,4,5,10,0,1,5,6,1,1,1,1,1,0,1,1,1,1,1,1,1,90 -59,2,10,10,0,1,1,1,1,10,1,5,6,1,1,0,6,7,0,0,0,1,2,0,0,0,0,1,0,0,0,80 -60,1,1,1,2,0,0,2,0,1,2,6,7,2,0,0,8,8,1,0,1,1,3,0,1,0,1,1,1,0,1,60 -61,2,2,2,0,1,1,3,1,2,3,7,8,3,1,1,9,9,0,1,0,1,4,0,0,1,0,1,0,1,0,100 -62,1,3,3,4,0,0,4,0,3,4,8,3,4,0,0,10,10,1,0,1,0,1,0,1,0,1,0,1,0,1,40 -63,2,4,4,2,1,1,5,1,4,5,9,4,5,1,1,1,1,0,1,0,0,2,0,0,1,0,0,0,1,0,80 -64,1,5,5,0,0,0,6,0,5,6,8,5,6,0,0,2,2,1,0,1,0,3,1,1,0,1,0,1,0,1,30 -65,2,6,6,3,0,0,7,0,6,7,9,6,7,0,1,3,3,0,1,0,0,1,1,0,1,0,0,0,1,0,40 -66,1,7,7,5,1,1,8,1,7,8,10,7,8,1,0,4,4,1,0,1,0,2,1,1,0,1,0,1,0,1,70 -67,2,8,8,1,0,0,9,0,8,9,1,8,9,0,1,5,5,0,1,0,0,3,1,0,1,0,0,0,1,0,60 -68,1,9,9,2,1,1,10,1,9,8,2,9,8,1,0,6,6,1,0,0,1,4,0,1,0,0,1,1,0,0,40 -69,2,10,10,0,0,0,11,0,10,9,3,8,9,0,1,7,8,0,1,1,1,1,0,0,1,1,1,0,1,1,10 -70,1,1,1,2,1,1,12,1,1,10,4,9,10,1,0,8,9,0,0,0,1,2,0,0,0,0,1,0,0,0,20 -71,2,2,2,1,0,0,13,0,2,1,5,10,1,0,1,9,10,0,1,1,0,3,0,0,1,1,0,0,1,1,60 -72,1,3,3,0,1,1,1,1,3,2,6,1,4,1,0,6,1,0,0,0,0,4,0,0,0,0,0,0,0,0,50 -73,2,4,4,1,0,0,2,0,4,3,7,2,5,0,1,7,2,0,0,1,0,1,0,0,0,1,0,0,0,1,100 -74,1,5,5,0,1,1,3,1,5,4,8,3,6,1,0,8,3,0,1,0,0,2,1,0,1,0,0,0,1,0,40 -75,2,6,6,2,0,0,4,0,6,5,9,4,7,0,0,9,4,0,0,1,0,3,1,0,0,1,0,0,0,1,70 -76,1,7,7,0,1,1,5,1,7,6,8,5,8,1,1,10,5,0,0,0,1,4,1,0,0,0,1,0,0,0,90 -20,2,8,8,4,0,0,6,0,8,7,9,6,9,0,0,1,6,0,1,1,1,1,0,0,1,1,1,0,1,1,30 -21,1,9,9,2,1,1,7,1,9,8,10,7,8,1,1,2,7,0,0,0,1,2,0,0,0,0,1,0,0,0,50 -22,2,10,10,0,0,0,8,0,10,9,1,8,9,0,0,9,8,0,1,1,1,3,0,0,1,1,1,0,1,1,70 -23,1,1,1,3,0,0,9,0,1,8,2,9,10,0,1,10,9,0,0,0,0,1,0,0,0,0,0,0,0,0,40 -24,2,2,2,5,1,1,10,1,2,9,3,8,1,1,0,1,6,0,1,1,0,2,0,0,1,1,0,0,1,1,30 -25,1,3,3,1,0,0,11,0,3,10,4,9,2,0,1,2,7,0,0,0,0,3,1,0,0,0,0,0,0,0,0 -26,2,4,4,2,1,1,12,1,4,1,5,10,3,1,0,3,8,0,1,1,0,4,1,0,1,1,0,0,1,1,80 -27,1,5,5,0,0,0,13,0,5,2,6,1,4,0,1,4,9,0,0,0,0,1,1,0,0,0,0,0,0,0,30 -28,2,6,6,2,1,1,1,1,6,3,7,2,5,1,0,5,10,0,1,1,0,2,1,0,1,1,0,0,1,1,20 -29,1,7,7,1,0,0,2,0,7,4,8,3,6,0,1,6,1,0,0,0,1,3,0,0,0,0,1,0,0,0,60 -30,2,8,8,0,1,1,3,1,8,5,9,4,7,1,0,8,2,0,0,1,1,4,0,0,0,1,1,0,0,1,60 -31,1,9,9,1,0,0,4,0,9,6,8,5,8,0,1,9,9,0,1,0,1,1,0,0,1,0,1,0,1,0,50 -32,2,10,10,0,1,1,5,1,10,7,9,6,9,1,0,10,10,0,0,1,0,2,0,0,0,1,0,0,0,1,60 -33,1,1,1,2,0,0,6,0,1,8,10,7,8,0,0,1,1,0,1,0,0,3,0,0,1,0,0,0,1,0,70 -34,2,2,2,0,1,1,7,1,2,9,1,8,9,1,1,2,2,0,0,1,0,4,0,0,0,1,0,0,0,1,100 -35,1,3,3,4,0,0,8,0,3,8,2,9,10,0,0,3,3,0,1,0,0,1,1,0,1,0,0,0,1,0,40 -36,2,4,4,2,1,1,9,1,4,9,3,8,1,1,1,4,4,0,0,1,0,2,1,0,0,1,0,0,0,1,80 -37,1,5,5,0,0,0,10,0,5,10,4,9,2,0,0,5,5,0,1,0,0,3,1,0,1,0,0,0,1,0,40 -38,2,6,6,3,0,0,11,0,6,1,5,10,3,0,1,6,6,0,0,1,1,1,0,0,0,1,1,0,0,1,100 -39,1,7,7,5,1,1,12,1,7,2,6,1,4,1,0,7,8,0,1,0,1,2,0,0,1,0,1,0,1,0,50 -40,2,8,8,1,0,0,13,0,8,3,7,2,5,0,1,8,9,0,0,1,1,3,0,0,0,1,1,0,0,1,60 -41,1,9,9,2,1,1,1,1,9,4,8,3,6,1,0,9,10,0,1,0,1,4,0,0,1,0,1,0,1,0,20 -42,2,10,10,0,0,0,2,0,10,5,2,4,7,0,1,10,1,0,0,1,0,1,0,0,0,1,0,0,0,1,70 -43,1,1,1,2,1,1,3,1,1,6,3,5,8,1,0,1,2,0,0,0,0,2,0,0,0,0,0,0,0,0,60 -44,2,2,2,1,0,0,4,0,2,7,4,6,3,0,1,2,2,0,1,0,0,3,1,0,1,0,0,0,1,0,90 -45,1,3,3,0,1,1,5,1,3,8,5,7,4,1,0,3,3,0,0,1,0,4,1,0,0,1,0,0,0,1,80 -46,2,4,4,1,0,0,6,0,4,9,6,8,5,0,1,4,4,0,1,0,0,1,1,0,1,0,0,0,1,0,60 -47,1,5,5,0,1,1,7,1,5,8,7,3,6,1,0,5,5,0,0,1,0,2,1,0,0,1,0,0,0,1,100 -48,2,6,6,2,0,0,8,0,6,9,8,4,7,0,0,6,6,0,1,0,1,3,0,0,1,0,1,0,1,0,40 -49,1,7,7,0,1,1,9,1,7,10,9,5,8,1,1,8,7,0,0,1,1,4,0,0,0,1,1,0,0,1,30 -50,2,8,8,4,0,0,10,0,8,1,8,6,9,0,0,9,8,0,1,0,1,1,0,0,1,0,1,0,1,0,20 -51,1,9,9,2,1,1,11,1,9,2,9,7,8,1,1,10,9,0,0,1,0,2,0,0,0,1,0,0,0,1,60 -52,2,10,10,0,0,0,12,0,10,3,10,8,9,0,0,1,10,0,1,0,0,3,0,0,1,0,0,0,1,0,60 -53,1,1,1,3,0,0,13,0,1,4,1,9,10,0,1,2,1,0,0,1,0,1,0,0,0,1,0,0,0,1,50 -54,2,2,2,5,1,1,1,1,2,5,2,8,1,1,0,3,2,0,1,0,0,2,1,0,1,0,0,0,1,0,60 -55,1,3,3,1,0,0,2,0,3,6,3,9,2,0,1,4,3,0,0,1,0,3,1,0,0,1,0,0,0,1,70 -56,2,4,4,2,1,1,3,1,4,7,4,10,3,1,0,5,4,0,0,0,1,4,1,0,0,0,1,0,0,0,100 -57,1,5,5,0,0,0,4,0,5,8,5,1,4,0,1,6,5,0,1,1,1,1,0,0,1,1,1,0,1,1,40 -58,2,6,6,2,1,1,5,1,6,9,6,2,5,1,0,7,6,0,0,0,1,2,0,0,0,0,1,0,0,0,80 -59,1,7,7,1,0,0,6,0,7,8,7,3,6,0,1,8,7,0,1,1,1,3,0,0,1,1,1,0,1,1,40 -60,2,8,8,0,1,1,7,1,8,9,8,4,7,1,0,9,8,0,0,0,0,4,0,0,0,0,0,0,0,0,100 -61,1,9,9,1,0,0,8,0,9,10,9,5,8,0,1,10,9,0,1,1,0,1,0,0,1,1,0,0,1,1,50 -62,2,1,10,0,1,1,9,1,10,1,8,6,9,1,1,1,10,0,0,0,0,2,1,0,0,0,0,0,0,0,60 -63,1,2,1,2,0,0,10,0,1,2,9,7,8,0,0,2,1,0,1,1,0,3,1,0,1,1,0,0,1,1,20 -64,2,3,2,0,1,1,11,1,2,3,10,8,9,1,1,3,2,0,0,0,0,4,1,0,0,0,0,0,0,0,70 -65,1,4,3,4,0,0,12,0,3,4,1,9,10,0,0,4,3,0,1,1,0,1,1,0,1,1,0,0,1,1,60 -66,2,5,4,2,1,1,13,1,4,5,2,8,1,1,1,5,4,0,0,0,1,2,0,0,0,0,1,0,0,0,90 -67,1,6,5,0,0,0,1,0,5,6,3,9,4,0,0,6,5,0,1,1,1,3,0,0,1,1,1,0,1,1,80 -68,2,7,6,3,0,0,2,0,6,7,4,10,5,0,1,8,6,0,0,0,1,1,0,0,0,0,1,0,0,0,60 -69,1,8,7,5,1,1,3,1,7,8,5,1,6,1,0,9,7,0,0,1,0,2,0,0,0,1,0,0,0,1,100 -70,2,9,8,1,0,0,4,0,8,9,6,2,7,0,1,10,8,0,1,0,0,3,0,0,1,0,0,0,1,0,40 -71,1,10,9,2,1,1,5,1,9,8,7,3,8,1,0,1,9,0,0,1,0,4,0,0,0,1,0,0,0,1,30 -72,2,1,10,0,0,0,6,0,10,9,8,4,9,0,0,2,6,0,1,0,0,1,1,0,1,0,0,0,1,0,20 -73,1,2,1,2,1,1,7,1,1,10,9,5,8,1,1,3,7,0,0,1,0,2,1,0,0,1,0,0,0,1,60 -74,2,3,2,1,0,0,8,0,2,1,8,6,9,0,0,4,8,0,1,0,0,3,1,0,1,0,0,0,1,0,60 -75,1,4,3,0,1,1,9,1,3,2,9,7,10,1,1,5,9,0,0,1,1,4,0,0,0,1,1,0,0,1,50 -76,2,5,4,1,0,0,10,0,4,3,10,8,1,0,0,6,10,0,1,0,1,1,0,0,1,0,1,0,1,0,60 -21,2,6,5,0,1,1,11,1,5,4,1,9,2,1,1,7,1,0,0,0,1,2,0,0,0,0,1,0,0,0,70 -22,1,7,6,2,0,0,12,0,6,5,2,8,3,0,0,8,2,0,1,1,1,3,0,0,1,1,1,0,1,1,100 -23,2,8,7,0,1,1,13,1,7,6,3,9,4,1,1,9,3,0,0,0,0,4,0,0,0,0,0,0,0,0,40 -24,1,9,8,4,0,0,1,0,8,7,4,10,5,0,0,10,4,0,1,1,0,1,0,0,1,1,0,0,1,1,80 -25,2,10,9,2,1,1,2,1,9,8,5,1,6,1,1,1,5,0,0,0,0,2,1,0,0,0,0,0,0,0,40 -26,1,1,10,0,0,0,3,0,10,9,6,2,7,0,0,2,6,0,0,1,0,3,1,0,0,1,0,0,0,1,100 -27,2,2,1,3,0,0,4,0,1,8,7,3,8,0,1,3,8,0,1,0,0,1,1,0,1,0,0,0,1,0,50 -28,1,3,2,5,1,1,5,1,2,9,8,4,9,1,0,4,9,0,0,1,0,2,1,0,0,1,0,0,0,1,60 -29,2,4,3,1,0,0,6,0,3,10,9,5,8,0,1,5,10,0,1,0,1,3,0,0,1,0,1,0,1,0,20 -30,1,5,4,2,1,1,7,1,4,1,8,6,9,1,0,6,1,0,0,1,1,4,0,0,0,1,1,0,0,1,70 -31,2,6,5,0,0,0,8,0,5,2,9,7,10,0,0,8,2,0,1,0,1,1,0,0,1,0,1,0,1,0,60 -32,1,7,6,2,1,1,9,1,6,3,10,8,1,1,1,9,3,0,0,1,0,2,0,0,0,1,0,0,0,1,90 -33,2,8,7,1,0,0,10,0,7,4,1,3,2,0,0,10,4,0,1,0,0,3,0,0,1,0,0,0,1,0,80 -34,1,9,8,0,1,1,11,1,8,5,2,4,3,1,1,1,5,0,0,1,0,4,0,0,0,1,0,0,0,1,60 -35,2,10,9,1,0,0,12,0,9,6,3,5,4,0,0,2,6,0,1,0,0,1,1,0,1,0,0,0,1,0,100 -36,1,1,10,0,1,1,13,1,10,7,4,6,5,1,1,3,7,0,0,1,0,2,1,0,0,1,0,0,0,1,40 -37,2,2,1,2,0,0,1,0,1,8,5,7,6,0,0,4,8,0,1,0,1,3,1,0,1,0,1,0,1,0,30 -38,2,3,2,0,1,1,2,1,2,9,6,8,7,1,1,5,9,0,0,1,1,4,0,0,0,1,1,0,0,1,20 -39,1,4,3,4,0,0,3,0,3,8,7,9,8,0,0,6,6,0,0,0,1,1,0,0,0,0,1,0,0,0,60 -40,2,5,4,2,1,1,4,1,4,9,8,8,3,1,1,7,7,0,1,1,1,2,0,0,1,1,1,0,1,1,60 -41,1,1,5,0,0,0,5,0,5,10,9,9,4,0,0,8,8,0,0,0,0,3,0,0,0,0,0,0,0,0,50 -42,2,2,6,3,0,0,6,0,6,1,8,10,5,0,1,9,9,0,1,1,0,1,0,0,1,1,0,0,1,1,60 -43,1,3,7,5,1,1,7,1,7,2,9,1,6,1,0,10,10,0,0,0,0,2,1,0,0,0,0,0,0,0,70 -44,2,4,8,1,0,0,8,0,8,3,10,2,7,0,1,1,1,0,1,1,0,3,1,0,1,1,0,0,1,1,100 -39,1,5,9,2,1,1,9,1,9,4,1,3,8,1,0,2,2,0,0,0,0,4,1,0,0,0,0,0,0,0,40 -40,2,6,10,0,0,0,10,0,10,5,2,4,9,0,0,3,9,0,1,1,0,1,1,0,1,1,0,0,1,1,80 -41,1,7,1,2,1,1,11,1,1,6,3,5,8,1,1,4,10,0,0,0,1,2,0,0,0,0,1,0,0,0,40 -42,2,8,2,1,0,0,12,0,2,7,4,6,9,0,0,5,1,0,1,1,1,3,0,0,1,1,1,0,1,1,100 -43,1,9,3,0,1,1,13,1,3,8,5,7,10,1,1,6,2,0,0,0,1,4,0,0,0,0,1,0,0,0,50 -44,2,10,4,1,0,0,7,0,4,9,6,8,1,0,0,8,3,0,1,1,0,1,0,0,1,1,0,0,1,1,60 -45,1,1,5,0,1,1,8,1,5,5,7,9,2,1,1,9,4,0,0,0,0,2,0,0,0,0,0,0,0,0,20 -46,2,2,6,2,0,0,9,0,6,6,8,8,3,0,0,10,5,0,1,1,0,3,0,0,1,1,0,0,1,1,70 -47,1,3,7,0,1,1,10,1,7,7,9,9,4,1,1,1,6,0,0,0,0,4,1,0,0,0,0,0,0,0,60 -48,2,4,8,4,0,0,11,0,8,8,8,10,5,0,0,2,8,0,1,0,0,1,1,0,1,0,0,0,1,0,90 -49,1,5,9,2,1,1,12,1,9,9,9,1,6,1,1,3,9,0,0,1,0,2,1,0,0,1,0,0,0,1,8030,male,5,2,0,no,permanent_resident,bachelor,yes,8,9,8,7,8,yes,no,rent,employment,no,no,yes,no,1,no +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,employment_assistance,life_stabilization,retention_services,specialized_services,employment_related_financial_supports,employer_financial_supports,enhanced_referrals,success_rate +20,1,1,1,3,0,0,1,0,1,8,2,3,4,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,0,80 +21,2,2,2,5,1,1,2,1,2,9,3,4,5,1,0,2,2,1,1,1,0,2,0,1,1,1,0,1,1,1,30 +22,1,3,3,1,0,0,3,0,3,10,4,5,6,0,1,3,3,0,0,0,0,3,0,0,0,0,0,0,0,0,40 +23,2,4,4,2,1,1,4,1,4,1,5,6,7,1,0,4,4,1,1,1,0,4,1,1,1,1,0,1,1,1,70 +24,1,5,5,0,0,0,5,0,5,2,6,7,8,0,1,5,5,0,0,0,0,1,1,0,0,0,0,0,0,0,60 +25,2,6,6,2,1,1,6,1,6,3,7,8,9,1,0,6,6,0,1,1,0,2,0,0,1,1,0,0,1,1,40 +26,1,7,7,1,0,0,7,0,7,4,8,9,8,0,1,7,7,1,0,0,1,3,0,1,0,0,1,1,0,0,10 +27,2,8,8,0,1,1,8,1,8,5,9,8,9,1,0,8,8,0,1,1,1,4,0,0,1,1,1,0,1,1,20 +28,1,9,9,1,0,0,9,0,9,6,8,9,10,0,1,9,9,1,0,0,1,1,0,1,0,0,1,1,0,0,60 +29,2,10,10,0,1,1,10,1,10,7,9,10,1,1,0,10,10,0,1,1,1,2,0,0,1,1,1,0,1,1,50 +30,1,1,1,2,0,0,11,0,1,8,10,1,2,0,0,1,2,1,0,0,1,3,0,1,0,0,1,1,0,0,100 +31,2,2,2,0,1,1,12,1,2,9,1,2,3,1,1,2,3,0,1,1,1,4,0,0,1,1,1,0,1,1,40 +32,1,3,3,4,0,0,13,0,3,8,2,3,4,0,0,3,4,1,0,0,1,1,1,1,0,0,1,1,0,0,70 +33,2,4,4,2,1,1,1,1,4,9,3,4,5,1,1,4,5,0,1,1,0,2,1,0,1,1,0,0,1,1,90 +34,1,5,5,0,0,0,2,0,5,10,4,5,6,0,0,5,6,1,0,0,0,3,1,1,0,0,0,1,0,0,30 +35,2,6,6,3,0,0,3,0,6,1,5,6,7,0,1,6,7,0,0,0,0,1,1,0,0,0,0,0,0,0,50 +36,1,7,7,5,1,1,4,1,7,2,6,7,8,1,0,7,8,1,1,1,0,2,1,1,1,1,0,1,1,1,70 +37,2,8,8,1,0,0,5,0,8,3,7,8,9,0,1,8,9,0,0,0,0,3,1,0,0,0,0,0,0,0,40 +38,1,9,9,2,1,1,6,1,9,4,8,9,8,1,0,9,10,1,1,1,0,4,1,1,1,1,0,1,1,1,30 +39,2,10,10,0,0,0,7,0,10,5,9,8,9,0,1,10,1,0,0,0,1,1,0,0,0,0,1,0,0,0,0 +40,1,1,1,2,1,1,8,1,1,6,8,9,10,1,0,1,2,0,1,1,1,2,0,0,1,1,1,0,1,1,80 +41,2,2,2,1,0,0,9,0,2,7,9,10,1,0,1,2,3,1,0,0,1,3,0,1,0,0,1,1,0,0,30 +42,1,3,3,0,1,1,10,1,3,8,10,1,2,1,0,3,4,0,1,1,1,4,0,0,1,1,1,0,1,1,20 +43,2,4,4,1,0,0,11,0,4,9,1,2,3,0,1,4,5,1,0,0,0,1,0,1,0,0,0,1,0,0,60 +44,1,5,5,0,1,1,12,1,5,8,2,3,4,1,0,5,6,0,1,1,0,2,0,0,1,1,0,0,1,1,60 +45,2,6,6,2,0,0,13,0,6,9,3,4,5,0,0,6,7,1,0,0,0,3,1,1,0,0,0,1,0,0,50 +46,1,7,7,0,1,1,1,1,7,10,4,5,6,1,1,7,8,0,1,1,0,4,1,0,1,1,0,0,1,1,60 +47,2,8,8,4,0,0,2,0,8,1,5,6,7,0,0,8,9,1,0,0,0,1,1,1,0,0,0,1,0,0,70 +48,1,9,9,2,1,1,3,1,9,2,6,7,8,1,1,9,10,0,1,1,0,2,1,0,1,1,0,0,1,1,100 +49,2,10,10,0,0,0,4,0,10,3,7,8,3,0,0,6,1,1,0,0,1,3,0,1,0,0,1,1,0,0,40 +50,1,1,1,3,0,0,5,0,1,4,8,9,4,0,1,7,2,0,1,1,1,1,0,0,1,1,1,0,1,1,80 +51,2,2,2,5,1,1,6,1,2,5,9,8,5,1,0,8,3,1,0,0,1,2,0,1,0,0,1,1,0,0,40 +52,1,3,3,1,0,0,7,0,3,6,8,9,6,0,1,9,4,0,1,1,0,3,0,0,1,1,0,0,1,1,100 +53,2,4,4,2,1,1,8,1,4,7,9,10,7,1,0,10,5,1,0,0,0,4,0,1,0,0,0,1,0,0,50 +54,1,5,5,0,0,0,9,0,5,8,10,1,8,0,1,1,6,0,1,1,0,1,0,0,1,1,0,0,1,1,60 +55,2,6,6,2,1,1,10,1,6,9,1,2,9,1,0,2,7,0,0,0,0,2,1,0,0,0,0,0,0,0,20 +56,1,7,7,1,0,0,11,0,7,8,2,3,8,0,1,3,8,1,1,1,0,3,1,1,1,1,0,1,1,1,70 +57,2,8,8,0,1,1,12,1,8,9,3,4,9,1,0,4,9,0,0,0,0,4,1,0,0,0,0,0,0,0,60 +58,1,9,9,1,0,0,13,0,9,10,4,5,10,0,1,5,6,1,1,1,1,1,0,1,1,1,1,1,1,1,90 +59,2,10,10,0,1,1,1,1,10,1,5,6,1,1,0,6,7,0,0,0,1,2,0,0,0,0,1,0,0,0,80 +60,1,1,1,2,0,0,2,0,1,2,6,7,2,0,0,8,8,1,0,1,1,3,0,1,0,1,1,1,0,1,60 +61,2,2,2,0,1,1,3,1,2,3,7,8,3,1,1,9,9,0,1,0,1,4,0,0,1,0,1,0,1,0,100 +62,1,3,3,4,0,0,4,0,3,4,8,3,4,0,0,10,10,1,0,1,0,1,0,1,0,1,0,1,0,1,40 +63,2,4,4,2,1,1,5,1,4,5,9,4,5,1,1,1,1,0,1,0,0,2,0,0,1,0,0,0,1,0,80 +64,1,5,5,0,0,0,6,0,5,6,8,5,6,0,0,2,2,1,0,1,0,3,1,1,0,1,0,1,0,1,30 +65,2,6,6,3,0,0,7,0,6,7,9,6,7,0,1,3,3,0,1,0,0,1,1,0,1,0,0,0,1,0,40 +66,1,7,7,5,1,1,8,1,7,8,10,7,8,1,0,4,4,1,0,1,0,2,1,1,0,1,0,1,0,1,70 +67,2,8,8,1,0,0,9,0,8,9,1,8,9,0,1,5,5,0,1,0,0,3,1,0,1,0,0,0,1,0,60 +68,1,9,9,2,1,1,10,1,9,8,2,9,8,1,0,6,6,1,0,0,1,4,0,1,0,0,1,1,0,0,40 +69,2,10,10,0,0,0,11,0,10,9,3,8,9,0,1,7,8,0,1,1,1,1,0,0,1,1,1,0,1,1,10 +70,1,1,1,2,1,1,12,1,1,10,4,9,10,1,0,8,9,0,0,0,1,2,0,0,0,0,1,0,0,0,20 +71,2,2,2,1,0,0,13,0,2,1,5,10,1,0,1,9,10,0,1,1,0,3,0,0,1,1,0,0,1,1,60 +72,1,3,3,0,1,1,1,1,3,2,6,1,4,1,0,6,1,0,0,0,0,4,0,0,0,0,0,0,0,0,50 +73,2,4,4,1,0,0,2,0,4,3,7,2,5,0,1,7,2,0,0,1,0,1,0,0,0,1,0,0,0,1,100 +74,1,5,5,0,1,1,3,1,5,4,8,3,6,1,0,8,3,0,1,0,0,2,1,0,1,0,0,0,1,0,40 +75,2,6,6,2,0,0,4,0,6,5,9,4,7,0,0,9,4,0,0,1,0,3,1,0,0,1,0,0,0,1,70 +76,1,7,7,0,1,1,5,1,7,6,8,5,8,1,1,10,5,0,0,0,1,4,1,0,0,0,1,0,0,0,90 +20,2,8,8,4,0,0,6,0,8,7,9,6,9,0,0,1,6,0,1,1,1,1,0,0,1,1,1,0,1,1,30 +21,1,9,9,2,1,1,7,1,9,8,10,7,8,1,1,2,7,0,0,0,1,2,0,0,0,0,1,0,0,0,50 +22,2,10,10,0,0,0,8,0,10,9,1,8,9,0,0,9,8,0,1,1,1,3,0,0,1,1,1,0,1,1,70 +23,1,1,1,3,0,0,9,0,1,8,2,9,10,0,1,10,9,0,0,0,0,1,0,0,0,0,0,0,0,0,40 +24,2,2,2,5,1,1,10,1,2,9,3,8,1,1,0,1,6,0,1,1,0,2,0,0,1,1,0,0,1,1,30 +25,1,3,3,1,0,0,11,0,3,10,4,9,2,0,1,2,7,0,0,0,0,3,1,0,0,0,0,0,0,0,0 +26,2,4,4,2,1,1,12,1,4,1,5,10,3,1,0,3,8,0,1,1,0,4,1,0,1,1,0,0,1,1,80 +27,1,5,5,0,0,0,13,0,5,2,6,1,4,0,1,4,9,0,0,0,0,1,1,0,0,0,0,0,0,0,30 +28,2,6,6,2,1,1,1,1,6,3,7,2,5,1,0,5,10,0,1,1,0,2,1,0,1,1,0,0,1,1,20 +29,1,7,7,1,0,0,2,0,7,4,8,3,6,0,1,6,1,0,0,0,1,3,0,0,0,0,1,0,0,0,60 +30,2,8,8,0,1,1,3,1,8,5,9,4,7,1,0,8,2,0,0,1,1,4,0,0,0,1,1,0,0,1,60 +31,1,9,9,1,0,0,4,0,9,6,8,5,8,0,1,9,9,0,1,0,1,1,0,0,1,0,1,0,1,0,50 +32,2,10,10,0,1,1,5,1,10,7,9,6,9,1,0,10,10,0,0,1,0,2,0,0,0,1,0,0,0,1,60 +33,1,1,1,2,0,0,6,0,1,8,10,7,8,0,0,1,1,0,1,0,0,3,0,0,1,0,0,0,1,0,70 +34,2,2,2,0,1,1,7,1,2,9,1,8,9,1,1,2,2,0,0,1,0,4,0,0,0,1,0,0,0,1,100 +35,1,3,3,4,0,0,8,0,3,8,2,9,10,0,0,3,3,0,1,0,0,1,1,0,1,0,0,0,1,0,40 +36,2,4,4,2,1,1,9,1,4,9,3,8,1,1,1,4,4,0,0,1,0,2,1,0,0,1,0,0,0,1,80 +37,1,5,5,0,0,0,10,0,5,10,4,9,2,0,0,5,5,0,1,0,0,3,1,0,1,0,0,0,1,0,40 +38,2,6,6,3,0,0,11,0,6,1,5,10,3,0,1,6,6,0,0,1,1,1,0,0,0,1,1,0,0,1,100 +39,1,7,7,5,1,1,12,1,7,2,6,1,4,1,0,7,8,0,1,0,1,2,0,0,1,0,1,0,1,0,50 +40,2,8,8,1,0,0,13,0,8,3,7,2,5,0,1,8,9,0,0,1,1,3,0,0,0,1,1,0,0,1,60 +41,1,9,9,2,1,1,1,1,9,4,8,3,6,1,0,9,10,0,1,0,1,4,0,0,1,0,1,0,1,0,20 +42,2,10,10,0,0,0,2,0,10,5,2,4,7,0,1,10,1,0,0,1,0,1,0,0,0,1,0,0,0,1,70 +43,1,1,1,2,1,1,3,1,1,6,3,5,8,1,0,1,2,0,0,0,0,2,0,0,0,0,0,0,0,0,60 +44,2,2,2,1,0,0,4,0,2,7,4,6,3,0,1,2,2,0,1,0,0,3,1,0,1,0,0,0,1,0,90 +45,1,3,3,0,1,1,5,1,3,8,5,7,4,1,0,3,3,0,0,1,0,4,1,0,0,1,0,0,0,1,80 +46,2,4,4,1,0,0,6,0,4,9,6,8,5,0,1,4,4,0,1,0,0,1,1,0,1,0,0,0,1,0,60 +47,1,5,5,0,1,1,7,1,5,8,7,3,6,1,0,5,5,0,0,1,0,2,1,0,0,1,0,0,0,1,100 +48,2,6,6,2,0,0,8,0,6,9,8,4,7,0,0,6,6,0,1,0,1,3,0,0,1,0,1,0,1,0,40 +49,1,7,7,0,1,1,9,1,7,10,9,5,8,1,1,8,7,0,0,1,1,4,0,0,0,1,1,0,0,1,30 +50,2,8,8,4,0,0,10,0,8,1,8,6,9,0,0,9,8,0,1,0,1,1,0,0,1,0,1,0,1,0,20 +51,1,9,9,2,1,1,11,1,9,2,9,7,8,1,1,10,9,0,0,1,0,2,0,0,0,1,0,0,0,1,60 +52,2,10,10,0,0,0,12,0,10,3,10,8,9,0,0,1,10,0,1,0,0,3,0,0,1,0,0,0,1,0,60 +53,1,1,1,3,0,0,13,0,1,4,1,9,10,0,1,2,1,0,0,1,0,1,0,0,0,1,0,0,0,1,50 +54,2,2,2,5,1,1,1,1,2,5,2,8,1,1,0,3,2,0,1,0,0,2,1,0,1,0,0,0,1,0,60 +55,1,3,3,1,0,0,2,0,3,6,3,9,2,0,1,4,3,0,0,1,0,3,1,0,0,1,0,0,0,1,70 +56,2,4,4,2,1,1,3,1,4,7,4,10,3,1,0,5,4,0,0,0,1,4,1,0,0,0,1,0,0,0,100 +57,1,5,5,0,0,0,4,0,5,8,5,1,4,0,1,6,5,0,1,1,1,1,0,0,1,1,1,0,1,1,40 +58,2,6,6,2,1,1,5,1,6,9,6,2,5,1,0,7,6,0,0,0,1,2,0,0,0,0,1,0,0,0,80 +59,1,7,7,1,0,0,6,0,7,8,7,3,6,0,1,8,7,0,1,1,1,3,0,0,1,1,1,0,1,1,40 +60,2,8,8,0,1,1,7,1,8,9,8,4,7,1,0,9,8,0,0,0,0,4,0,0,0,0,0,0,0,0,100 +61,1,9,9,1,0,0,8,0,9,10,9,5,8,0,1,10,9,0,1,1,0,1,0,0,1,1,0,0,1,1,50 +62,2,1,10,0,1,1,9,1,10,1,8,6,9,1,1,1,10,0,0,0,0,2,1,0,0,0,0,0,0,0,60 +63,1,2,1,2,0,0,10,0,1,2,9,7,8,0,0,2,1,0,1,1,0,3,1,0,1,1,0,0,1,1,20 +64,2,3,2,0,1,1,11,1,2,3,10,8,9,1,1,3,2,0,0,0,0,4,1,0,0,0,0,0,0,0,70 +65,1,4,3,4,0,0,12,0,3,4,1,9,10,0,0,4,3,0,1,1,0,1,1,0,1,1,0,0,1,1,60 +66,2,5,4,2,1,1,13,1,4,5,2,8,1,1,1,5,4,0,0,0,1,2,0,0,0,0,1,0,0,0,90 +67,1,6,5,0,0,0,1,0,5,6,3,9,4,0,0,6,5,0,1,1,1,3,0,0,1,1,1,0,1,1,80 +68,2,7,6,3,0,0,2,0,6,7,4,10,5,0,1,8,6,0,0,0,1,1,0,0,0,0,1,0,0,0,60 +69,1,8,7,5,1,1,3,1,7,8,5,1,6,1,0,9,7,0,0,1,0,2,0,0,0,1,0,0,0,1,100 +70,2,9,8,1,0,0,4,0,8,9,6,2,7,0,1,10,8,0,1,0,0,3,0,0,1,0,0,0,1,0,40 +71,1,10,9,2,1,1,5,1,9,8,7,3,8,1,0,1,9,0,0,1,0,4,0,0,0,1,0,0,0,1,30 +72,2,1,10,0,0,0,6,0,10,9,8,4,9,0,0,2,6,0,1,0,0,1,1,0,1,0,0,0,1,0,20 +73,1,2,1,2,1,1,7,1,1,10,9,5,8,1,1,3,7,0,0,1,0,2,1,0,0,1,0,0,0,1,60 +74,2,3,2,1,0,0,8,0,2,1,8,6,9,0,0,4,8,0,1,0,0,3,1,0,1,0,0,0,1,0,60 +75,1,4,3,0,1,1,9,1,3,2,9,7,10,1,1,5,9,0,0,1,1,4,0,0,0,1,1,0,0,1,50 +76,2,5,4,1,0,0,10,0,4,3,10,8,1,0,0,6,10,0,1,0,1,1,0,0,1,0,1,0,1,0,60 +21,2,6,5,0,1,1,11,1,5,4,1,9,2,1,1,7,1,0,0,0,1,2,0,0,0,0,1,0,0,0,70 +22,1,7,6,2,0,0,12,0,6,5,2,8,3,0,0,8,2,0,1,1,1,3,0,0,1,1,1,0,1,1,100 +23,2,8,7,0,1,1,13,1,7,6,3,9,4,1,1,9,3,0,0,0,0,4,0,0,0,0,0,0,0,0,40 +24,1,9,8,4,0,0,1,0,8,7,4,10,5,0,0,10,4,0,1,1,0,1,0,0,1,1,0,0,1,1,80 +25,2,10,9,2,1,1,2,1,9,8,5,1,6,1,1,1,5,0,0,0,0,2,1,0,0,0,0,0,0,0,40 +26,1,1,10,0,0,0,3,0,10,9,6,2,7,0,0,2,6,0,0,1,0,3,1,0,0,1,0,0,0,1,100 +27,2,2,1,3,0,0,4,0,1,8,7,3,8,0,1,3,8,0,1,0,0,1,1,0,1,0,0,0,1,0,50 +28,1,3,2,5,1,1,5,1,2,9,8,4,9,1,0,4,9,0,0,1,0,2,1,0,0,1,0,0,0,1,60 +29,2,4,3,1,0,0,6,0,3,10,9,5,8,0,1,5,10,0,1,0,1,3,0,0,1,0,1,0,1,0,20 +30,1,5,4,2,1,1,7,1,4,1,8,6,9,1,0,6,1,0,0,1,1,4,0,0,0,1,1,0,0,1,70 +31,2,6,5,0,0,0,8,0,5,2,9,7,10,0,0,8,2,0,1,0,1,1,0,0,1,0,1,0,1,0,60 +32,1,7,6,2,1,1,9,1,6,3,10,8,1,1,1,9,3,0,0,1,0,2,0,0,0,1,0,0,0,1,90 +33,2,8,7,1,0,0,10,0,7,4,1,3,2,0,0,10,4,0,1,0,0,3,0,0,1,0,0,0,1,0,80 +34,1,9,8,0,1,1,11,1,8,5,2,4,3,1,1,1,5,0,0,1,0,4,0,0,0,1,0,0,0,1,60 +35,2,10,9,1,0,0,12,0,9,6,3,5,4,0,0,2,6,0,1,0,0,1,1,0,1,0,0,0,1,0,100 +36,1,1,10,0,1,1,13,1,10,7,4,6,5,1,1,3,7,0,0,1,0,2,1,0,0,1,0,0,0,1,40 +37,2,2,1,2,0,0,1,0,1,8,5,7,6,0,0,4,8,0,1,0,1,3,1,0,1,0,1,0,1,0,30 +38,2,3,2,0,1,1,2,1,2,9,6,8,7,1,1,5,9,0,0,1,1,4,0,0,0,1,1,0,0,1,20 +39,1,4,3,4,0,0,3,0,3,8,7,9,8,0,0,6,6,0,0,0,1,1,0,0,0,0,1,0,0,0,60 +40,2,5,4,2,1,1,4,1,4,9,8,8,3,1,1,7,7,0,1,1,1,2,0,0,1,1,1,0,1,1,60 +41,1,1,5,0,0,0,5,0,5,10,9,9,4,0,0,8,8,0,0,0,0,3,0,0,0,0,0,0,0,0,50 +42,2,2,6,3,0,0,6,0,6,1,8,10,5,0,1,9,9,0,1,1,0,1,0,0,1,1,0,0,1,1,60 +43,1,3,7,5,1,1,7,1,7,2,9,1,6,1,0,10,10,0,0,0,0,2,1,0,0,0,0,0,0,0,70 +44,2,4,8,1,0,0,8,0,8,3,10,2,7,0,1,1,1,0,1,1,0,3,1,0,1,1,0,0,1,1,100 +39,1,5,9,2,1,1,9,1,9,4,1,3,8,1,0,2,2,0,0,0,0,4,1,0,0,0,0,0,0,0,40 +40,2,6,10,0,0,0,10,0,10,5,2,4,9,0,0,3,9,0,1,1,0,1,1,0,1,1,0,0,1,1,80 +41,1,7,1,2,1,1,11,1,1,6,3,5,8,1,1,4,10,0,0,0,1,2,0,0,0,0,1,0,0,0,40 +42,2,8,2,1,0,0,12,0,2,7,4,6,9,0,0,5,1,0,1,1,1,3,0,0,1,1,1,0,1,1,100 +43,1,9,3,0,1,1,13,1,3,8,5,7,10,1,1,6,2,0,0,0,1,4,0,0,0,0,1,0,0,0,50 +44,2,10,4,1,0,0,7,0,4,9,6,8,1,0,0,8,3,0,1,1,0,1,0,0,1,1,0,0,1,1,60 +45,1,1,5,0,1,1,8,1,5,5,7,9,2,1,1,9,4,0,0,0,0,2,0,0,0,0,0,0,0,0,20 +46,2,2,6,2,0,0,9,0,6,6,8,8,3,0,0,10,5,0,1,1,0,3,0,0,1,1,0,0,1,1,70 +47,1,3,7,0,1,1,10,1,7,7,9,9,4,1,1,1,6,0,0,0,0,4,1,0,0,0,0,0,0,0,60 +48,2,4,8,4,0,0,11,0,8,8,8,10,5,0,0,2,8,0,1,0,0,1,1,0,1,0,0,0,1,0,90 +49,1,5,9,2,1,1,12,1,9,9,9,1,6,1,1,3,9,0,0,1,0,2,1,0,0,1,0,0,0,1,80 \ No newline at end of file diff --git a/app/main.py b/app/main.py index 5b6bf162..c77f03fe 100644 --- a/app/main.py +++ b/app/main.py @@ -1,7 +1,7 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from app.clients.router import router as clients_router +from clients.router import router as clients_router app = FastAPI() diff --git a/app/requirements.txt b/app/requirements.txt index 57fc8e6e..c2b2b22e 100644 --- a/app/requirements.txt +++ b/app/requirements.txt @@ -1,43 +1,23 @@ annotated-types==0.7.0 -anyio==4.4.0 -certifi==2024.7.4 +anyio==4.6.2.post1 click==8.1.7 -dnspython==2.6.1 -email_validator==2.2.0 -fastapi==0.112.2 -fastapi-cli==0.0.5 +exceptiongroup==1.2.2 +fastapi==0.115.5 h11==0.14.0 -httpcore==1.0.5 -httptools==0.6.1 -httpx==0.27.2 -idna==3.8 -Jinja2==3.1.4 +idna==3.10 joblib==1.4.2 -markdown-it-py==3.0.0 -MarkupSafe==2.1.5 -mdurl==0.1.2 -numpy==2.1.0 -pandas==2.2.2 -pydantic==2.8.2 -pydantic_core==2.20.1 -Pygments==2.18.0 +numpy==2.0.2 +pandas==2.2.3 +pydantic==2.9.2 +pydantic_core==2.23.4 python-dateutil==2.9.0.post0 -python-dotenv==1.0.1 -python-multipart==0.0.9 -pytz==2024.1 -PyYAML==6.0.2 -rich==13.8.0 -scikit-learn==1.5.1 -scipy==1.14.1 -shellingham==1.5.4 +pytz==2024.2 +scikit-learn==1.5.2 +scipy==1.13.1 six==1.16.0 sniffio==1.3.1 -starlette==0.38.2 +starlette==0.41.3 threadpoolctl==3.5.0 -typer==0.12.5 typing_extensions==4.12.2 -tzdata==2024.1 -uvicorn==0.30.6 -uvloop==0.20.0 -watchfiles==0.23.0 -websockets==13.0 +tzdata==2024.2 +uvicorn==0.32.0 From c3c7f5b7eb929e1f42eb56ae684475796e780302 Mon Sep 17 00:00:00 2001 From: zoeyfeng7 <147457131+zoeyfeng7@users.noreply.github.com> Date: Thu, 21 Nov 2024 16:58:42 -0800 Subject: [PATCH 04/13] Create pylint.yml --- .github/workflows/pylint.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/pylint.yml diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 00000000..c73e032c --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,23 @@ +name: Pylint + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Analysing the code with pylint + run: | + pylint $(git ls-files '*.py') From 431e6b447a7f6a93b4410a94c1c50669e01dab6d Mon Sep 17 00:00:00 2001 From: zoeyfeng7 <147457131+zoeyfeng7@users.noreply.github.com> Date: Sun, 24 Nov 2024 22:32:29 -0800 Subject: [PATCH 05/13] Update pylint.yml --- .github/workflows/pylint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index c73e032c..62e56d26 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -20,4 +20,4 @@ jobs: pip install pylint - name: Analysing the code with pylint run: | - pylint $(git ls-files '*.py') + pylint $(git ls-files '*.py') || true From 1765cb9ed45ea62104548113f0af3b64142bc164 Mon Sep 17 00:00:00 2001 From: wangdairou Date: Tue, 26 Nov 2024 19:53:41 -0800 Subject: [PATCH 06/13] Add Docker and GitHub Actions configuration --- .github/workflows/backend-ci.yml | 19 +++++++++++++++++++ .gitignore | 1 + Dockerfile | 21 +++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 .github/workflows/backend-ci.yml create mode 100644 Dockerfile diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml new file mode 100644 index 00000000..8ccedd24 --- /dev/null +++ b/.github/workflows/backend-ci.yml @@ -0,0 +1,19 @@ +name: Backend CI +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build Backend Docker image + run: docker build -t cat-backend:test . + - name: Run tests + run: | + docker run -d -p 5000:5000 --name backend-container cat-backend:test + sleep 10 + docker exec backend-container python -m pytest tests/ \ No newline at end of file diff --git a/.gitignore b/.gitignore index 281d9273..4213dd09 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ venv/ .idea __pycache__ .DS_Store +.dockerignore diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..b4ce4d33 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,21 @@ +# Inside CommonAssessmentTool/Dockerfile +FROM python:3.9-slim + +WORKDIR /app + +# Copy requirements first for better caching +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application +COPY app/ app/ +COPY tests/ tests/ + +# Set environment variables if needed +ENV PYTHONPATH=/app + +# Expose the port your backend runs on +EXPOSE 5000 + +# Command to run the application +CMD ["python", "-m", "app.main"] \ No newline at end of file From 05f2c32c6c63cb2ea1362a65372c9dcaf47f7d6a Mon Sep 17 00:00:00 2001 From: wangdairou Date: Tue, 26 Nov 2024 21:08:54 -0800 Subject: [PATCH 07/13] Simplify workflow --- .github/workflows/backend-ci.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index 8ccedd24..53d587e2 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -1,4 +1,5 @@ -name: Backend CI +name: Backend Docker CI + on: push: branches: [ main ] @@ -8,12 +9,24 @@ on: jobs: build-and-test: runs-on: ubuntu-latest + steps: - uses: actions/checkout@v3 + - name: Build Backend Docker image - run: docker build -t cat-backend:test . - - name: Run tests + run: | + docker build -t cat-backend:test . + + - name: Run Backend container run: | docker run -d -p 5000:5000 --name backend-container cat-backend:test + # Wait for container to start and check its status sleep 10 - docker exec backend-container python -m pytest tests/ \ No newline at end of file + docker ps + docker logs backend-container + + - name: Clean up + if: always() + run: | + docker stop backend-container || true + docker rm backend-container || true \ No newline at end of file From aa07cd97f94c624a7dc61f9c92a8c7000ffbf2e9 Mon Sep 17 00:00:00 2001 From: yixuansun Date: Wed, 27 Nov 2024 00:51:53 -0800 Subject: [PATCH 08/13] sprint 4 crud and database --- app/clients/crud.py | 136 ++++++++++++++ app/clients/db_setup.py | 46 +++++ app/clients/router.py | 392 +++++++++++++++++++++++++++++----------- app/main.py | 104 ++++++++++- 4 files changed, 571 insertions(+), 107 deletions(-) create mode 100644 app/clients/crud.py create mode 100644 app/clients/db_setup.py diff --git a/app/clients/crud.py b/app/clients/crud.py new file mode 100644 index 00000000..3fbabae7 --- /dev/null +++ b/app/clients/crud.py @@ -0,0 +1,136 @@ +import sqlite3 +from .schema import PredictionInput + +DB_FILE = "case_management.db" + + +def create_user(data: PredictionInput): + """ + insert user data to database + """ + connection = sqlite3.connect(DB_FILE) + cursor = connection.cursor() + + cursor.execute(''' + INSERT INTO users ( + 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 + )) + + connection.commit() + connection.close() + + print("User created successfully.") + + +def get_all_user_data(): + """ + Fetch all users from the database. + """ + conn = sqlite3.connect(DB_FILE) + cur = conn.cursor() + + cur.execute('SELECT * FROM users') + user_list = cur.fetchall() + + conn.close() + return user_list + + +def get_user_by_id(uid: int): + """ + Retrieve a user's information using their ID. + """ + conn = sqlite3.connect(DB_FILE) + cur = conn.cursor() + + cur.execute('SELECT * FROM users WHERE id = ?', (uid,)) + user_info = cur.fetchone() + + conn.close() + + if user_info: + return user_info + else: + print(f"No user found with ID {uid}") + return None + + +def update_user(uid: int, user_data: PredictionInput): + """ + Update user information based on their ID. Checks if the user ID exists before updating. + """ + conn = sqlite3.connect(DB_FILE) + cur = conn.cursor() + + # Check if the user ID exists + cur.execute('SELECT id FROM users WHERE id = ?', (uid,)) + user_exists = cur.fetchone() + + if not user_exists: + print(f"User with ID {uid} does not exist. Update aborted.") + conn.close() + return False # Return False to indicate the update failed + + # Perform the update if the user exists + cur.execute(''' + UPDATE users + 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 = ? + ''', ( + user_data.age, user_data.gender, user_data.work_experience, user_data.canada_workex, user_data.dep_num, + user_data.canada_born, user_data.citizen_status, user_data.level_of_schooling, user_data.fluent_english, + user_data.reading_english_scale, user_data.speaking_english_scale, user_data.writing_english_scale, + user_data.numeracy_scale, user_data.computer_scale, user_data.transportation_bool, user_data.caregiver_bool, + user_data.housing, user_data.income_source, user_data.felony_bool, user_data.attending_school, + user_data.currently_employed, user_data.substance_use, user_data.time_unemployed, + user_data.need_mental_health_support_bool, uid + )) + + conn.commit() + conn.close() + + print(f"User with ID {uid} has been updated successfully.") + return True # Return True to indicate the update was successful + + +def delete_user(uid: int): + """ + Delete a user by their ID. Check if the user ID exists; if not, return 404. + """ + conn = sqlite3.connect(DB_FILE) + cur = conn.cursor() + + # Check if the user ID exists + cur.execute('SELECT id FROM users WHERE id = ?', (uid,)) + user_exists = cur.fetchone() + + if not user_exists: + conn.close() + print(f"User with ID {uid} not found. Returning 404.") + return 404 # Return 404 status if user does not exist + + # Proceed with deletion if the user exists + cur.execute('DELETE FROM users WHERE id = ?', (uid,)) + conn.commit() + conn.close() + + print(f"User with ID {uid} has been deleted successfully.") + return 200 # Return 200 status for successful deletion diff --git a/app/clients/db_setup.py b/app/clients/db_setup.py new file mode 100644 index 00000000..5b98d46b --- /dev/null +++ b/app/clients/db_setup.py @@ -0,0 +1,46 @@ +import sqlite3 + +DB_FILE = "case_management.db" + + +def create_tables(): + connection = sqlite3.connect(DB_FILE) + cursor = connection.cursor() + + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + age INTEGER NOT NULL, + gender TEXT NOT NULL, + work_experience INTEGER NOT NULL, + canada_workex INTEGER NOT NULL, + dep_num INTEGER NOT NULL, + canada_born TEXT NOT NULL, + citizen_status TEXT NOT NULL, + level_of_schooling TEXT NOT NULL, + fluent_english TEXT NOT NULL, + reading_english_scale INTEGER NOT NULL, + speaking_english_scale INTEGER NOT NULL, + writing_english_scale INTEGER NOT NULL, + numeracy_scale INTEGER NOT NULL, + computer_scale INTEGER NOT NULL, + transportation_bool TEXT NOT NULL, + caregiver_bool TEXT NOT NULL, + housing TEXT NOT NULL, + income_source TEXT NOT NULL, + felony_bool TEXT NOT NULL, + attending_school TEXT NOT NULL, + currently_employed TEXT NOT NULL, + substance_use TEXT NOT NULL, + time_unemployed INTEGER NOT NULL, + need_mental_health_support_bool TEXT NOT NULL + ) + ''') + + connection.commit() + connection.close() + + +if __name__ == "__main__": + create_tables() + print("Database tables created successfully.") diff --git a/app/clients/router.py b/app/clients/router.py index 7b82ef87..c4ad2c5b 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -6,9 +6,11 @@ from .service.logic import interpret_and_calculate from .schema import PredictionInput +from .crud import create_user, get_all_user_data, get_user_by_id, update_user, delete_user router = APIRouter(prefix="/clients", tags=["clients"]) + @router.post("/predictions") async def predict(data: PredictionInput): print("HERE") @@ -16,142 +18,320 @@ async def predict(data: PredictionInput): return interpret_and_calculate(data.model_dump()) # create a user + + @router.post("/create-user", response_class=JSONResponse) async def create_user(data: PredictionInput): + """ + API endpoint to create a user in the database. + """ try: - current_dir = os.path.dirname(os.path.abspath(__file__)) - file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') - new_data = pd.DataFrame([data.dict()]) - if os.path.exists(file_path): - # Add newline before appending if file is not empty - if os.path.getsize(file_path) > 0: - with open(file_path, 'a', newline='') as f: - new_data.to_csv(f, index=False, header=False) - else: - # If file is empty, write with headers - new_data.to_csv(file_path, index=False) - else: - # If file doesn't exist, create it with headers - new_data.to_csv(file_path, index=False) - print("User created:", data.dict()) - return JSONResponse(content={"message": "User created successfully", "user": data.dict()}) + # Call the create_user function from crud.py + create_user(data) + return JSONResponse( + content={"message": "User created successfully", + "user": data.dict()}, + status_code=201 + ) except Exception as e: - raise HTTPException(status_code=500, detail=f"Error creating user: {e}") + # Handle any exceptions raised during the creation process + raise HTTPException( + status_code=500, + detail=f"Error creating user: {str(e)}" + ) # Get user data -@router.get("/read-csv", response_class=JSONResponse) -async def read_csv(): + + +@router.get("/users", response_class=JSONResponse) +async def get_all_users(): + """ + API endpoint to retrieve all user data from the database. + """ try: - current_dir = os.path.dirname(os.path.abspath(__file__)) - file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') - - data = pd.read_csv(file_path) - - return data.head().to_dict(orient="records") - - except Exception as e: - raise HTTPException(status_code=500, detail=f"Error reading CSV: {e}") + # Fetch all users from the database + users = get_all_user_data() + + # Check if the database returned any users + if not users: + return JSONResponse( + content={"message": "No users found in the database."}, + status_code=200 + ) + # Define the keys for the user dictionary + keys = [ + "id", "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" + ] + + # Convert the list of tuples into a list of dictionaries + users_list = [dict(zip(keys, user)) for user in users] + + return JSONResponse(content=users_list, status_code=200) + + except Exception as e: + # Handle unexpected errors + raise HTTPException( + status_code=500, + detail=f"Error retrieving users: {e}" + ) # Get specific client by unique identifier -@router.get("/client/{client_index}", response_class=JSONResponse) -async def get_client(client_index: int): + + +@router.get("/users/{user_id}", response_class=JSONResponse) +async def get_client(user_id: int): + """ + API endpoint to retrieve a specific client by their unique ID from the database. + """ try: - current_dir = os.path.dirname(os.path.abspath(__file__)) - file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') - data = pd.read_csv(file_path) + # Fetch client data by ID from the database + client_data = get_user_by_id(user_id) + + # If the client does not exist, raise a 404 error + if not client_data: + raise HTTPException(status_code=404, detail="Client not found") + + # Define the keys for the client dictionary + keys = [ + "id", "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" + ] + + # Convert the tuple returned by get_user_by_id into a dictionary + client_dict = dict(zip(keys, client_data)) + + return JSONResponse(content=client_dict, status_code=200) + + except HTTPException: + raise + except Exception as e: + # Handle unexpected errors + raise HTTPException( + status_code=500, + detail=f"Error retrieving user: {e}" + ) - if client_index >= len(data): +# update user + + +@router.put("/users/{user_id}", response_class=JSONResponse) +async def update_user(user_id: int, updated_data: PredictionInput): + """ + API endpoint to update a specific client's information by their unique ID. + """ + try: + # Check if the client exists before updating + existing_client = get_user_by_id(user_id) + if not existing_client: raise HTTPException(status_code=404, detail="Client not found") - client_data = data.iloc[client_index].to_dict() - return JSONResponse(content=client_data) + # Update the client information + update_status = update_user(user_id, updated_data) + + # Check if the update was successful + if not update_status: + raise HTTPException( + status_code=500, detail="Error updating user") + + # Return a success message + return JSONResponse( + content={ + "message": "User updated successfully", + "updated_user_id": user_id + }, + status_code=200 + ) + + except HTTPException: + raise # Re-raise HTTP exceptions except Exception as e: - raise HTTPException(status_code=500, detail=f"Error retrieving client: {e}") + # Handle unexpected errors + raise HTTPException( + status_code=500, + detail=f"Error updating client: {e}" + ) + + +# Delete specific client -# # Update specific client information -# @router.put("/client/{client_index}", response_class=JSONResponse) -# async def update_client(client_index: int, data: PredictionInput): +@router.delete("/users/{user_id}", response_class=JSONResponse) +async def delete_client(user_id: int): + """ + API endpoint to delete a specific client by their unique ID. + """ + try: + # Retrieve client data before deletion + client_data = get_user_by_id(user_id) + + # If the client does not exist, raise a 404 error + if not client_data: + raise HTTPException(status_code=404, detail="Client not found") + + # Perform the deletion + delete_status = delete_user(user_id) + + # Check the deletion status + if delete_status == 404: + raise HTTPException( + status_code=404, detail="Client not found during deletion") + + # Return the deleted client data (tuple) + return JSONResponse( + content={ + "message": "Client deleted successfully", + "deleted_client": client_data + }, + status_code=200 + ) + + except HTTPException: + raise # Re-raise HTTP exceptions + except Exception as e: + # Handle unexpected errors + raise HTTPException( + status_code=500, + detail=f"Error deleting client: {e}" + ) + + +# from fastapi import APIRouter, HTTPException +# from fastapi.responses import HTMLResponse +# from fastapi.responses import JSONResponse +# import os +# import pandas as pd + +# from .service.logic import interpret_and_calculate +# from .schema import PredictionInput + +# router = APIRouter(prefix="/clients", tags=["clients"]) + +# @router.post("/predictions") +# async def predict(data: PredictionInput): +# print("HERE") +# print(data.model_dump()) +# return interpret_and_calculate(data.model_dump()) + +# # create a user +# @router.post("/create-user", response_class=JSONResponse) +# async def create_user(data: PredictionInput): +# try: +# current_dir = os.path.dirname(os.path.abspath(__file__)) +# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') +# new_data = pd.DataFrame([data.dict()]) +# if os.path.exists(file_path): +# # Add newline before appending if file is not empty +# if os.path.getsize(file_path) > 0: +# with open(file_path, 'a', newline='') as f: +# new_data.to_csv(f, index=False, header=False) +# else: +# # If file is empty, write with headers +# new_data.to_csv(file_path, index=False) +# else: +# # If file doesn't exist, create it with headers +# new_data.to_csv(file_path, index=False) +# print("User created:", data.dict()) +# return JSONResponse(content={"message": "User created successfully", "user": data.dict()}) +# except Exception as e: +# raise HTTPException(status_code=500, detail=f"Error creating user: {e}") + +# # Get user data +# @router.get("/read-csv", response_class=JSONResponse) +# async def read_csv(): +# try: +# current_dir = os.path.dirname(os.path.abspath(__file__)) +# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') + +# data = pd.read_csv(file_path) + +# return data.head().to_dict(orient="records") + +# except Exception as e: +# raise HTTPException(status_code=500, detail=f"Error reading CSV: {e}") + + +# # Get specific client by unique identifier +# @router.get("/client/{client_index}", response_class=JSONResponse) +# async def get_client(client_index: int): +# try: +# current_dir = os.path.dirname(os.path.abspath(__file__)) +# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') +# data = pd.read_csv(file_path) + +# if client_index >= len(data): +# raise HTTPException(status_code=404, detail="Client not found") + +# client_data = data.iloc[client_index].to_dict() +# return JSONResponse(content=client_data) +# except Exception as e: +# raise HTTPException(status_code=500, detail=f"Error retrieving client: {e}") + + +# # Delete specific client +# @router.delete("/client/{client_index}", response_class=JSONResponse) +# async def delete_client(client_index: int): # try: # current_dir = os.path.dirname(os.path.abspath(__file__)) # file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') # df = pd.read_csv(file_path) -# + # if client_index >= len(df): # raise HTTPException(status_code=404, detail="Client not found") -# -# # Update the client data -# df.iloc[client_index] = pd.Series(data.dict()) + +# # Store client data before deletion for response +# deleted_client = df.iloc[client_index].to_dict() + +# # Delete the client +# df = df.drop(client_index) # df.to_csv(file_path, index=False) -# -# # Run updated prediction -# updated_prediction = interpret_and_calculate(data.dict()) -# + # return JSONResponse( # content={ -# "message": "Client updated successfully", -# "updated_data": data.dict(), -# "new_prediction": updated_prediction +# "message": "Client deleted successfully", +# "deleted_client": deleted_client # } # ) # except Exception as e: -# raise HTTPException(status_code=500, detail=f"Error updating client: {e}") - - -# Delete specific client -@router.delete("/client/{client_index}", response_class=JSONResponse) -async def delete_client(client_index: int): - try: - current_dir = os.path.dirname(os.path.abspath(__file__)) - file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') - df = pd.read_csv(file_path) - - if client_index >= len(df): - raise HTTPException(status_code=404, detail="Client not found") +# raise HTTPException(status_code=500, detail=f"Error deleting client: {e}") - # Store client data before deletion for response - deleted_client = df.iloc[client_index].to_dict() +# async def load_csv(): +# current_dir = os.path.dirname(os.path.abspath(__file__)) +# filename = os.path.join(current_dir, 'service', 'data_commontool.csv') +# try: +# df = pd.read_csv(filename) +# return df +# except Exception as e: +# raise HTTPException(status_code=500, detail=f"Error reading CSV: {str(e)}") - # Delete the client - df = df.drop(client_index) - df.to_csv(file_path, index=False) +# @router.get("/get-age", response_model=list) +# async def get_age(): +# df = await load_csv() +# if 'age' not in df.columns: +# raise HTTPException(status_code=404, detail="Column 'age' not found in CSV") +# return df['age'].tolist() - return JSONResponse( - content={ - "message": "Client deleted successfully", - "deleted_client": deleted_client - } - ) - except Exception as e: - raise HTTPException(status_code=500, detail=f"Error deleting client: {e}") +# @router.get("/get-gender", response_model=list) +# async def get_gender(): +# df = await load_csv() +# if 'gender' not in df.columns: +# raise HTTPException(status_code=404, detail="Column 'gender' not found in CSV") +# return df['gender'].tolist() -async def load_csv(): - current_dir = os.path.dirname(os.path.abspath(__file__)) - filename = os.path.join(current_dir, 'service', 'data_commontool.csv') - try: - df = pd.read_csv(filename) - return df - except Exception as e: - raise HTTPException(status_code=500, detail=f"Error reading CSV: {str(e)}") - -@router.get("/get-age", response_model=list) -async def get_age(): - df = await load_csv() - if 'age' not in df.columns: - raise HTTPException(status_code=404, detail="Column 'age' not found in CSV") - return df['age'].tolist() - -@router.get("/get-gender", response_model=list) -async def get_gender(): - df = await load_csv() - if 'gender' not in df.columns: - raise HTTPException(status_code=404, detail="Column 'gender' not found in CSV") - return df['gender'].tolist() - -@router.get("/get-born-place", response_model=list) -async def get_born_place(): - df = await load_csv() - if 'canada_born' not in df.columns: - raise HTTPException(status_code=404, detail="Column 'canada_born' not found in CSV") - return df['canada_born'].tolist() \ No newline at end of file +# @router.get("/get-born-place", response_model=list) +# async def get_born_place(): +# df = await load_csv() +# if 'canada_born' not in df.columns: +# raise HTTPException(status_code=404, detail="Column 'canada_born' not found in CSV") +# return df['canada_born'].tolist() diff --git a/app/main.py b/app/main.py index c77f03fe..c0a66b39 100644 --- a/app/main.py +++ b/app/main.py @@ -1,8 +1,16 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from clients.router import router as clients_router +# from clients.router import router as clients_router +# from clients.schema import PredictionInput +# from clients.crud import create_user, get_all_user_data, get_user_by_id, update_user, delete_user +# from clients.db_setup import create_tables + +from app.clients.router import router as clients_router +from app.clients.schema import PredictionInput +from app.clients.crud import create_user, get_all_user_data, get_user_by_id, update_user, delete_user +from app.clients.db_setup import create_tables app = FastAPI() @@ -17,4 +25,98 @@ allow_headers=["*"], # Allows all headers ) +# # Sprint4- make sure database has been created +# create_tables() + +# # insert data- create user +# new_user = PredictionInput( +# age=25, +# gender="Female", +# work_experience=3, +# canada_workex=1, +# dep_num=1, +# canada_born="No", +# citizen_status="Citizen", +# level_of_schooling="Master's", +# fluent_english="Yes", +# reading_english_scale=5, +# speaking_english_scale=5, +# writing_english_scale=4, +# numeracy_scale=3, +# computer_scale=4, +# transportation_bool="Yes", +# caregiver_bool="No", +# housing="Apartment", +# income_source="Employment", +# felony_bool="No", +# attending_school="No", +# currently_employed="Yes", +# substance_use="No", +# time_unemployed=2, +# need_mental_health_support_bool="Yes" +# ) + +# create_user(new_user) + +# # Retrieve all users +# # print("\nFetching all users...") +# # users = get_all_user_data() +# # for user in users: +# # print(user) + +# # Retrieve a specific user by ID +# print("\nFetching user with ID 2...") +# user = get_user_by_id(2) +# if user: +# print(user) +# else: +# print("User not found.") + +# # Update a user +# print("\nUpdating user with ID 2...") +# updated_user = PredictionInput( +# age=50, +# gender="Male", +# work_experience=20, +# canada_workex=5, +# dep_num=2, +# canada_born="No", +# citizen_status="Citizen", +# level_of_schooling="Master's", +# fluent_english="Yes", +# reading_english_scale=5, +# speaking_english_scale=5, +# writing_english_scale=5, +# numeracy_scale=4, +# computer_scale=5, +# transportation_bool="Yes", +# caregiver_bool="No", +# housing="Own House", +# income_source="Self-employed", +# felony_bool="No", +# attending_school="No", +# currently_employed="Yes", +# substance_use="No", +# time_unemployed=0, +# need_mental_health_support_bool="No" +# ) +# if update_user(2, updated_user): +# print("User updated successfully.") +# else: +# print("Failed to update user.") + +# # # Delete a user +# # print("\nDeleting user with ID 1...") +# # status = delete_user(2) +# # if status == 200: +# # print("User deleted successfully.") +# # elif status == 404: +# # print("User not found. Could not delete.") +# # # Attempt to fetch the deleted user +# # print("\nFetching user with ID 1 after deletion...") +# # user = get_user_by_id(2) +# # if user: +# # print(user) +# # else: +# # print("User not found.") From 522028d25bcc524644d44edf39a5e7d526d96db0 Mon Sep 17 00:00:00 2001 From: yixuansun Date: Mon, 2 Dec 2024 16:33:36 -0800 Subject: [PATCH 09/13] connect database --- app/clients/crud.py | 20 +++-- app/clients/router.py | 168 ++---------------------------------------- app/main.py | 103 +------------------------- 3 files changed, 16 insertions(+), 275 deletions(-) diff --git a/app/clients/crud.py b/app/clients/crud.py index 3fbabae7..2e77bacf 100644 --- a/app/clients/crud.py +++ b/app/clients/crud.py @@ -4,7 +4,7 @@ DB_FILE = "case_management.db" -def create_user(data: PredictionInput): +def create_user_in_db(data: PredictionInput): """ insert user data to database """ @@ -37,7 +37,7 @@ def create_user(data: PredictionInput): def get_all_user_data(): """ - Fetch all users from the database. + Get all users from the database. """ conn = sqlite3.connect(DB_FILE) cur = conn.cursor() @@ -51,7 +51,7 @@ def get_all_user_data(): def get_user_by_id(uid: int): """ - Retrieve a user's information using their ID. + Get a user's information using their ID. """ conn = sqlite3.connect(DB_FILE) cur = conn.cursor() @@ -70,7 +70,7 @@ def get_user_by_id(uid: int): def update_user(uid: int, user_data: PredictionInput): """ - Update user information based on their ID. Checks if the user ID exists before updating. + Update user information based on their ID. """ conn = sqlite3.connect(DB_FILE) cur = conn.cursor() @@ -82,9 +82,9 @@ def update_user(uid: int, user_data: PredictionInput): if not user_exists: print(f"User with ID {uid} does not exist. Update aborted.") conn.close() - return False # Return False to indicate the update failed + return False - # Perform the update if the user exists + # update if the user exists cur.execute(''' UPDATE users SET age = ?, gender = ?, work_experience = ?, canada_workex = ?, dep_num = ?, @@ -108,7 +108,7 @@ def update_user(uid: int, user_data: PredictionInput): conn.close() print(f"User with ID {uid} has been updated successfully.") - return True # Return True to indicate the update was successful + return True def delete_user(uid: int): @@ -118,19 +118,17 @@ def delete_user(uid: int): conn = sqlite3.connect(DB_FILE) cur = conn.cursor() - # Check if the user ID exists cur.execute('SELECT id FROM users WHERE id = ?', (uid,)) user_exists = cur.fetchone() if not user_exists: conn.close() print(f"User with ID {uid} not found. Returning 404.") - return 404 # Return 404 status if user does not exist + return 404 - # Proceed with deletion if the user exists cur.execute('DELETE FROM users WHERE id = ?', (uid,)) conn.commit() conn.close() print(f"User with ID {uid} has been deleted successfully.") - return 200 # Return 200 status for successful deletion + return 200 diff --git a/app/clients/router.py b/app/clients/router.py index c4ad2c5b..23a5e900 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -6,7 +6,7 @@ from .service.logic import interpret_and_calculate from .schema import PredictionInput -from .crud import create_user, get_all_user_data, get_user_by_id, update_user, delete_user +from .crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user, delete_user router = APIRouter(prefix="/clients", tags=["clients"]) @@ -26,15 +26,13 @@ async def create_user(data: PredictionInput): API endpoint to create a user in the database. """ try: - # Call the create_user function from crud.py - create_user(data) + create_user_in_db(data) return JSONResponse( content={"message": "User created successfully", "user": data.dict()}, status_code=201 ) except Exception as e: - # Handle any exceptions raised during the creation process raise HTTPException( status_code=500, detail=f"Error creating user: {str(e)}" @@ -49,17 +47,12 @@ async def get_all_users(): API endpoint to retrieve all user data from the database. """ try: - # Fetch all users from the database users = get_all_user_data() - - # Check if the database returned any users if not users: return JSONResponse( content={"message": "No users found in the database."}, status_code=200 ) - - # Define the keys for the user dictionary keys = [ "id", "age", "gender", "work_experience", "canada_workex", "dep_num", "canada_born", "citizen_status", "level_of_schooling", "fluent_english", @@ -70,19 +63,17 @@ async def get_all_users(): "need_mental_health_support_bool" ] - # Convert the list of tuples into a list of dictionaries users_list = [dict(zip(keys, user)) for user in users] return JSONResponse(content=users_list, status_code=200) except Exception as e: - # Handle unexpected errors raise HTTPException( status_code=500, detail=f"Error retrieving users: {e}" ) -# Get specific client by unique identifier +# Get client by unique id @router.get("/users/{user_id}", response_class=JSONResponse) @@ -91,14 +82,11 @@ async def get_client(user_id: int): API endpoint to retrieve a specific client by their unique ID from the database. """ try: - # Fetch client data by ID from the database client_data = get_user_by_id(user_id) - # If the client does not exist, raise a 404 error if not client_data: raise HTTPException(status_code=404, detail="Client not found") - # Define the keys for the client dictionary keys = [ "id", "age", "gender", "work_experience", "canada_workex", "dep_num", "canada_born", "citizen_status", "level_of_schooling", "fluent_english", @@ -109,7 +97,6 @@ async def get_client(user_id: int): "need_mental_health_support_bool" ] - # Convert the tuple returned by get_user_by_id into a dictionary client_dict = dict(zip(keys, client_data)) return JSONResponse(content=client_dict, status_code=200) @@ -117,7 +104,6 @@ async def get_client(user_id: int): except HTTPException: raise except Exception as e: - # Handle unexpected errors raise HTTPException( status_code=500, detail=f"Error retrieving user: {e}" @@ -132,20 +118,16 @@ async def update_user(user_id: int, updated_data: PredictionInput): API endpoint to update a specific client's information by their unique ID. """ try: - # Check if the client exists before updating existing_client = get_user_by_id(user_id) if not existing_client: raise HTTPException(status_code=404, detail="Client not found") - # Update the client information update_status = update_user(user_id, updated_data) - # Check if the update was successful if not update_status: raise HTTPException( status_code=500, detail="Error updating user") - # Return a success message return JSONResponse( content={ "message": "User updated successfully", @@ -155,16 +137,15 @@ async def update_user(user_id: int, updated_data: PredictionInput): ) except HTTPException: - raise # Re-raise HTTP exceptions + raise except Exception as e: - # Handle unexpected errors raise HTTPException( status_code=500, detail=f"Error updating client: {e}" ) -# Delete specific client +# Delete client @router.delete("/users/{user_id}", response_class=JSONResponse) @@ -173,22 +154,16 @@ async def delete_client(user_id: int): API endpoint to delete a specific client by their unique ID. """ try: - # Retrieve client data before deletion client_data = get_user_by_id(user_id) - # If the client does not exist, raise a 404 error if not client_data: raise HTTPException(status_code=404, detail="Client not found") - # Perform the deletion delete_status = delete_user(user_id) - - # Check the deletion status if delete_status == 404: raise HTTPException( status_code=404, detail="Client not found during deletion") - # Return the deleted client data (tuple) return JSONResponse( content={ "message": "Client deleted successfully", @@ -198,140 +173,9 @@ async def delete_client(user_id: int): ) except HTTPException: - raise # Re-raise HTTP exceptions + raise except Exception as e: - # Handle unexpected errors raise HTTPException( status_code=500, detail=f"Error deleting client: {e}" ) - - -# from fastapi import APIRouter, HTTPException -# from fastapi.responses import HTMLResponse -# from fastapi.responses import JSONResponse -# import os -# import pandas as pd - -# from .service.logic import interpret_and_calculate -# from .schema import PredictionInput - -# router = APIRouter(prefix="/clients", tags=["clients"]) - -# @router.post("/predictions") -# async def predict(data: PredictionInput): -# print("HERE") -# print(data.model_dump()) -# return interpret_and_calculate(data.model_dump()) - -# # create a user -# @router.post("/create-user", response_class=JSONResponse) -# async def create_user(data: PredictionInput): -# try: -# current_dir = os.path.dirname(os.path.abspath(__file__)) -# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') -# new_data = pd.DataFrame([data.dict()]) -# if os.path.exists(file_path): -# # Add newline before appending if file is not empty -# if os.path.getsize(file_path) > 0: -# with open(file_path, 'a', newline='') as f: -# new_data.to_csv(f, index=False, header=False) -# else: -# # If file is empty, write with headers -# new_data.to_csv(file_path, index=False) -# else: -# # If file doesn't exist, create it with headers -# new_data.to_csv(file_path, index=False) -# print("User created:", data.dict()) -# return JSONResponse(content={"message": "User created successfully", "user": data.dict()}) -# except Exception as e: -# raise HTTPException(status_code=500, detail=f"Error creating user: {e}") - -# # Get user data -# @router.get("/read-csv", response_class=JSONResponse) -# async def read_csv(): -# try: -# current_dir = os.path.dirname(os.path.abspath(__file__)) -# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') - -# data = pd.read_csv(file_path) - -# return data.head().to_dict(orient="records") - -# except Exception as e: -# raise HTTPException(status_code=500, detail=f"Error reading CSV: {e}") - - -# # Get specific client by unique identifier -# @router.get("/client/{client_index}", response_class=JSONResponse) -# async def get_client(client_index: int): -# try: -# current_dir = os.path.dirname(os.path.abspath(__file__)) -# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') -# data = pd.read_csv(file_path) - -# if client_index >= len(data): -# raise HTTPException(status_code=404, detail="Client not found") - -# client_data = data.iloc[client_index].to_dict() -# return JSONResponse(content=client_data) -# except Exception as e: -# raise HTTPException(status_code=500, detail=f"Error retrieving client: {e}") - - -# # Delete specific client -# @router.delete("/client/{client_index}", response_class=JSONResponse) -# async def delete_client(client_index: int): -# try: -# current_dir = os.path.dirname(os.path.abspath(__file__)) -# file_path = os.path.join(current_dir, 'service', 'data_commontool.csv') -# df = pd.read_csv(file_path) - -# if client_index >= len(df): -# raise HTTPException(status_code=404, detail="Client not found") - -# # Store client data before deletion for response -# deleted_client = df.iloc[client_index].to_dict() - -# # Delete the client -# df = df.drop(client_index) -# df.to_csv(file_path, index=False) - -# return JSONResponse( -# content={ -# "message": "Client deleted successfully", -# "deleted_client": deleted_client -# } -# ) -# except Exception as e: -# raise HTTPException(status_code=500, detail=f"Error deleting client: {e}") - -# async def load_csv(): -# current_dir = os.path.dirname(os.path.abspath(__file__)) -# filename = os.path.join(current_dir, 'service', 'data_commontool.csv') -# try: -# df = pd.read_csv(filename) -# return df -# except Exception as e: -# raise HTTPException(status_code=500, detail=f"Error reading CSV: {str(e)}") - -# @router.get("/get-age", response_model=list) -# async def get_age(): -# df = await load_csv() -# if 'age' not in df.columns: -# raise HTTPException(status_code=404, detail="Column 'age' not found in CSV") -# return df['age'].tolist() - -# @router.get("/get-gender", response_model=list) -# async def get_gender(): -# df = await load_csv() -# if 'gender' not in df.columns: -# raise HTTPException(status_code=404, detail="Column 'gender' not found in CSV") -# return df['gender'].tolist() - -# @router.get("/get-born-place", response_model=list) -# async def get_born_place(): -# df = await load_csv() -# if 'canada_born' not in df.columns: -# raise HTTPException(status_code=404, detail="Column 'canada_born' not found in CSV") -# return df['canada_born'].tolist() diff --git a/app/main.py b/app/main.py index c0a66b39..8b1c6015 100644 --- a/app/main.py +++ b/app/main.py @@ -1,15 +1,10 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -# from clients.router import router as clients_router - -# from clients.schema import PredictionInput -# from clients.crud import create_user, get_all_user_data, get_user_by_id, update_user, delete_user -# from clients.db_setup import create_tables from app.clients.router import router as clients_router from app.clients.schema import PredictionInput -from app.clients.crud import create_user, get_all_user_data, get_user_by_id, update_user, delete_user +from app.clients.crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user, delete_user from app.clients.db_setup import create_tables app = FastAPI() @@ -24,99 +19,3 @@ allow_methods=["*"], # Allows all methods, including OPTIONS allow_headers=["*"], # Allows all headers ) - -# # Sprint4- make sure database has been created -# create_tables() - -# # insert data- create user -# new_user = PredictionInput( -# age=25, -# gender="Female", -# work_experience=3, -# canada_workex=1, -# dep_num=1, -# canada_born="No", -# citizen_status="Citizen", -# level_of_schooling="Master's", -# fluent_english="Yes", -# reading_english_scale=5, -# speaking_english_scale=5, -# writing_english_scale=4, -# numeracy_scale=3, -# computer_scale=4, -# transportation_bool="Yes", -# caregiver_bool="No", -# housing="Apartment", -# income_source="Employment", -# felony_bool="No", -# attending_school="No", -# currently_employed="Yes", -# substance_use="No", -# time_unemployed=2, -# need_mental_health_support_bool="Yes" -# ) - -# create_user(new_user) - -# # Retrieve all users -# # print("\nFetching all users...") -# # users = get_all_user_data() -# # for user in users: -# # print(user) - -# # Retrieve a specific user by ID -# print("\nFetching user with ID 2...") -# user = get_user_by_id(2) -# if user: -# print(user) -# else: -# print("User not found.") - -# # Update a user -# print("\nUpdating user with ID 2...") -# updated_user = PredictionInput( -# age=50, -# gender="Male", -# work_experience=20, -# canada_workex=5, -# dep_num=2, -# canada_born="No", -# citizen_status="Citizen", -# level_of_schooling="Master's", -# fluent_english="Yes", -# reading_english_scale=5, -# speaking_english_scale=5, -# writing_english_scale=5, -# numeracy_scale=4, -# computer_scale=5, -# transportation_bool="Yes", -# caregiver_bool="No", -# housing="Own House", -# income_source="Self-employed", -# felony_bool="No", -# attending_school="No", -# currently_employed="Yes", -# substance_use="No", -# time_unemployed=0, -# need_mental_health_support_bool="No" -# ) -# if update_user(2, updated_user): -# print("User updated successfully.") -# else: -# print("Failed to update user.") - -# # # Delete a user -# # print("\nDeleting user with ID 1...") -# # status = delete_user(2) -# # if status == 200: -# # print("User deleted successfully.") -# # elif status == 404: -# # print("User not found. Could not delete.") - -# # # Attempt to fetch the deleted user -# # print("\nFetching user with ID 1 after deletion...") -# # user = get_user_by_id(2) -# # if user: -# # print(user) -# # else: -# # print("User not found.") From ee5df25f18d5b48fccfab3affdf0e75cf4fe3a2b Mon Sep 17 00:00:00 2001 From: yixuansun Date: Mon, 2 Dec 2024 18:02:11 -0800 Subject: [PATCH 10/13] update main and db setup --- app/clients/db_setup.py | 1 + app/main.py | 3 +++ 2 files changed, 4 insertions(+) diff --git a/app/clients/db_setup.py b/app/clients/db_setup.py index 5b98d46b..4acb90ef 100644 --- a/app/clients/db_setup.py +++ b/app/clients/db_setup.py @@ -1,4 +1,5 @@ import sqlite3 +import os DB_FILE = "case_management.db" diff --git a/app/main.py b/app/main.py index 8b1c6015..088b7ead 100644 --- a/app/main.py +++ b/app/main.py @@ -7,8 +7,11 @@ from app.clients.crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user, delete_user from app.clients.db_setup import create_tables + app = FastAPI() +create_tables() + # Set API endpoints on router app.include_router(clients_router) From f11c8fe1e0a24585302ecfcd534a8c5e39e67ada Mon Sep 17 00:00:00 2001 From: wangdairou Date: Mon, 2 Dec 2024 22:41:07 -0800 Subject: [PATCH 11/13] Add API Documentation --- .gitignore | 12 +- API_DOCUMENTATION.md | 434 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+), 5 deletions(-) create mode 100644 API_DOCUMENTATION.md diff --git a/.gitignore b/.gitignore index 4213dd09..eaba3edc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ -.venv -venv/ -.idea -__pycache__ +/bin +/include +/lib +*.db +venv +pyvenv.cfg .DS_Store -.dockerignore +__pycache__ diff --git a/API_DOCUMENTATION.md b/API_DOCUMENTATION.md new file mode 100644 index 00000000..2360ffcc --- /dev/null +++ b/API_DOCUMENTATION.md @@ -0,0 +1,434 @@ +Case Management CRUD API Documentation + +Base URL +http://127.0.0.1:8000/clients + + +Endpoints + +1. Create User + +POST /create-user +Creates a new client record. + +**Request Body** + +jsonCopy{ + //user data to create +} + +**Response** + +{ + "message": "User created successfully", + "user": { + //user data + } +} + +Status: 201 Created + +**Error Responses** + +404 Not Found: Client doesn't exist + +jsonCopy{ + "detail": "Client not found" +} + +500 Internal Server Error: Server-side error + +jsonCopy{ + "detail": "Error creating user: {error message}" +} + +**Example Use** + +Request Body: + +jsonCopy{ + "age": 0, + "gender": "string", + "work_experience": 0, + "canada_workex": 0, + "dep_num": 0, + "canada_born": "string", + "citizen_status": "string", + "level_of_schooling": "string", + "fluent_english": "string", + "reading_english_scale": 0, + "speaking_english_scale": 0, + "writing_english_scale": 0, + "numeracy_scale": 0, + "computer_scale": 0, + "transportation_bool": "string", + "caregiver_bool": "string", + "housing": "string", + "income_source": "string", + "felony_bool": "string", + "attending_school": "string", + "currently_employed": "string", + "substance_use": "string", + "time_unemployed": 0, + "need_mental_health_support_bool": "string" +} + +Response: + +{ + "message": "User created successfully", + "user": { + "age": 0, + "gender": "string", + "work_experience": 0, + "canada_workex": 0, + "dep_num": 0, + "canada_born": "string", + "citizen_status": "string", + "level_of_schooling": "string", + "fluent_english": "string", + "reading_english_scale": 0, + "speaking_english_scale": 0, + "writing_english_scale": 0, + "numeracy_scale": 0, + "computer_scale": 0, + "transportation_bool": "string", + "caregiver_bool": "string", + "housing": "string", + "income_source": "string", + "felony_bool": "string", + "attending_school": "string", + "currently_employed": "string", + "substance_use": "string", + "time_unemployed": 0, + "need_mental_health_support_bool": "string" + } +} + +Status: 201 Created + +2. Get All Users + +GET /users +Retrieves information for all registered clients. + +**Response** + +jsonCopy[ + { + //user data * + }, + { + //user data * + } +] + +Status: 200 OK + +**Error Responses** + +500 Internal Server Error: Server-side error + +jsonCopy{ + "detail": "Error retrieving user" +} + + +**Example Use** + +Response: + +jsonCopy[ + { + "id": 1, + "age": 0, + "gender": "string", + "work_experience": 0, + "canada_workex": 0, + "dep_num": 0, + "canada_born": "string", + "citizen_status": "string", + "level_of_schooling": "string", + "fluent_english": "string", + "reading_english_scale": 0, + "speaking_english_scale": 0, + "writing_english_scale": 0, + "numeracy_scale": 0, + "computer_scale": 0, + "transportation_bool": "string", + "caregiver_bool": "string", + "housing": "string", + "income_source": "string", + "felony_bool": "string", + "attending_school": "string", + "currently_employed": "string", + "substance_use": "string", + "time_unemployed": 0, + "need_mental_health_support_bool": "string" + }, + { + "id": 2, + "age": 0, + "gender": "string", + "work_experience": 0, + "canada_workex": 0, + "dep_num": 0, + "canada_born": "string", + "citizen_status": "string", + "level_of_schooling": "string", + "fluent_english": "string", + "reading_english_scale": 0, + "speaking_english_scale": 0, + "writing_english_scale": 0, + "numeracy_scale": 0, + "computer_scale": 0, + "transportation_bool": "string", + "caregiver_bool": "string", + "housing": "string", + "income_source": "string", + "felony_bool": "string", + "attending_school": "string", + "currently_employed": "string", + "substance_use": "string", + "time_unemployed": 0, + "need_mental_health_support_bool": "string" + } +] + +Status: 200 OK + + +3. Get Single User + +GET /users/{user_id} +Retrieves information for a specific client. + +**Parameters** + +user_id (path parameter, integer): Unique identifier of the client + + +**Response** + +Status: 200 OK + +jsonCopy{ + //user data of user_id +} + +**Error Responses** + +404 Not Found: Client doesn't exist + +jsonCopy{ + "detail": "Client not found" +} + + +500 Internal Server Error: Server-side error + +jsonCopy{ + "detail": "Error retrieving user" +} + +**Example Use** + +Request Body: + +user_id: 1 + +Response: + +Status: 200 OK + +jsonCopy{ + "id": 1, + "age": 0, + "gender": "string", + "work_experience": 0, + "canada_workex": 0, + "dep_num": 0, + "canada_born": "string", + "citizen_status": "string", + "level_of_schooling": "string", + "fluent_english": "string", + "reading_english_scale": 0, + "speaking_english_scale": 0, + "writing_english_scale": 0, + "numeracy_scale": 0, + "computer_scale": 0, + "transportation_bool": "string", + "caregiver_bool": "string", + "housing": "string", + "income_source": "string", + "felony_bool": "string", + "attending_school": "string", + "currently_employed": "string", + "substance_use": "string", + "time_unemployed": 0, + "need_mental_health_support_bool": "string" +} + + + +4. Update User +PUT /users/{user_id} +Updates an existing client's information. + +**Parameters** + +user_id (path parameter, integer): Unique identifier of the client + +**Request Body** + +user_id: user_id +{ + //user data updates +} + +**Response** + +{ + "message": "User updated successfully", + "updated_user_id": user_id +} + +Status: 200 OK + +**Error Responses** + +404 Not Found: Client doesn't exist + +jsonCopy{ + "detail": "Client not found" +} + +500 Internal Server Error: user not found during updating + +jsonCopy{ + "detail": "Error updating user" +} + +500 Internal Server Error: Server-side error + +jsonCopy{ + "detail": "Error updating user" +} + +**Example Use** +Request Body: + +user_id: 1 +{ + "age": 10, + "gender": "string", + "work_experience": 0, + "canada_workex": 0, + "dep_num": 0, + "canada_born": "string", + "citizen_status": "string", + "level_of_schooling": "string", + "fluent_english": "string", + "reading_english_scale": 0, + "speaking_english_scale": 0, + "writing_english_scale": 0, + "numeracy_scale": 0, + "computer_scale": 0, + "transportation_bool": "string", + "caregiver_bool": "string", + "housing": "string", + "income_source": "string", + "felony_bool": "string", + "attending_school": "string", + "currently_employed": "string", + "substance_use": "string", + "time_unemployed": 0, + "need_mental_health_support_bool": "string" +} + +Response: + +{ + "message": "User updated successfully", + "updated_user_id": 1 +} + +Status: 200 OK + + + +5. Delete User + +DELETE /users/{user_id} +Deletes a client's record. + +**Parameters** + +user_id (path parameter, integer): Unique identifier of the client + +**Response** +{ + "message": "Client deleted successfully", + "deleted_client": [ + //user data + ] +} +Status: 200 OK + + +**Error Responses** + +404 Not Found: Client doesn't exist + +jsonCopy{ + "detail": "Client not found" +} + +404 Not Found: user not found during deletion + +jsonCopy{ + "detail": "Client not found during deletion" +} + +500 Internal Server Error: Server-side error + +jsonCopy{ + "detail": "Error deleting client: {error_message}" +} + +**Example Use** +Request Body: +user_id: user_id + +Response: +{ + "message": "Client deleted successfully", + "deleted_client": [ + 1, + 0, + "string", + 0, + 0, + 0, + "string", + "string", + "string", + "string", + 0, + 0, + 0, + 0, + 0, + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string", + 0, + "string" + ] +} +Status: 200 OK \ No newline at end of file From e75d165de4697068776be027a0084a0cdf7b7df9 Mon Sep 17 00:00:00 2001 From: yunfeng Date: Mon, 2 Dec 2024 23:36:19 -0800 Subject: [PATCH 12/13] modified update_user functions and added tests --- app/clients/crud.py | 4 +- app/clients/router.py | 4 +- app/main.py | 2 +- tests/__init__.py | 0 tests/test.py | 147 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 tests/__init__.py diff --git a/app/clients/crud.py b/app/clients/crud.py index 2e77bacf..3a7b749f 100644 --- a/app/clients/crud.py +++ b/app/clients/crud.py @@ -68,7 +68,7 @@ def get_user_by_id(uid: int): return None -def update_user(uid: int, user_data: PredictionInput): +def update_user_in_db(uid: int, user_data: PredictionInput): """ Update user information based on their ID. """ @@ -76,6 +76,7 @@ def update_user(uid: int, user_data: PredictionInput): cur = conn.cursor() # Check if the user ID exists + print(f"Updating user with ID {uid} using data: {user_data.dict()}") cur.execute('SELECT id FROM users WHERE id = ?', (uid,)) user_exists = cur.fetchone() @@ -105,6 +106,7 @@ def update_user(uid: int, user_data: PredictionInput): )) conn.commit() + print("Update query executed and changes committed.") conn.close() print(f"User with ID {uid} has been updated successfully.") diff --git a/app/clients/router.py b/app/clients/router.py index 23a5e900..2dd485d1 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -6,7 +6,7 @@ from .service.logic import interpret_and_calculate from .schema import PredictionInput -from .crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user, delete_user +from .crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user_in_db, delete_user router = APIRouter(prefix="/clients", tags=["clients"]) @@ -122,7 +122,7 @@ async def update_user(user_id: int, updated_data: PredictionInput): if not existing_client: raise HTTPException(status_code=404, detail="Client not found") - update_status = update_user(user_id, updated_data) + update_status = update_user_in_db(user_id, updated_data) if not update_status: raise HTTPException( diff --git a/app/main.py b/app/main.py index 088b7ead..d51f05b0 100644 --- a/app/main.py +++ b/app/main.py @@ -4,7 +4,7 @@ from app.clients.router import router as clients_router from app.clients.schema import PredictionInput -from app.clients.crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user, delete_user +from app.clients.crud import create_user_in_db, get_all_user_data, get_user_by_id, update_user_in_db, delete_user from app.clients.db_setup import create_tables diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test.py b/tests/test.py index a911f0a2..d6e39714 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,23 +1,136 @@ -from logic import interpret_and_calculate -from itertools import combinations_with_replacement +import pytest +from fastapi.testclient import TestClient +from app.main import app -# def test_interpret_and_calculate(): -# print("running tests") -# data = {"23","1","1","1","1","0","1","2","2","3","2", -# "2","3","2","1","1","1","1","1","1","0","1","1","1" -# } -# result = interpret_and_calculate(data) -# print(data) +client = TestClient(app) -from itertools import product +# Test for creating a user +def test_create_user(): + payload = { + "age": 30, + "gender": "male", + "work_experience": 5, + "canada_workex": 2, + "dep_num": 0, + "canada_born": "yes", + "citizen_status": "citizen", + "level_of_schooling": "bachelor", + "fluent_english": "yes", + "reading_english_scale": 5, + "speaking_english_scale": 5, + "writing_english_scale": 5, + "numeracy_scale": 4, + "computer_scale": 5, + "transportation_bool": "yes", + "caregiver_bool": "no", + "housing": "rented", + "income_source": "employment", + "felony_bool": "no", + "attending_school": "no", + "currently_employed": "yes", + "substance_use": "no", + "time_unemployed": 0, + "need_mental_health_support_bool": "no" + } + response = client.post("/clients/create-user", json=payload) + assert response.status_code == 201 + response_data = response.json() + assert response_data["message"] == "User created successfully" -# Cartesian product of [0, 1] repeated 2 times -result = list(product([0, 1], repeat=2)) + # Validate that the response includes the correct user data + user = response_data["user"] + for key in payload.keys(): + assert user[key] == payload[key] -# Output: [(0, 0), (0, 1), (1, 0), (1, 1)] -print(result) +# Test for getting all users +def test_get_all_users(): + response = client.get("/clients/users") + assert response.status_code == 200 + response_data = response.json() + assert isinstance(response_data, list) -result = list(combinations_with_replacement([0, 1], 2)) + # Ensure at least one user exists + assert len(response_data) > 0 -# Output: [(0, 0), (0, 1), (1, 1)] -print(result) \ No newline at end of file + # Validate the structure of the first user in the list + user = response_data[0] + expected_keys = [ + "id", "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" + ] + for key in expected_keys: + assert key in user + +# Test for getting a user by ID +def test_get_user_by_id(): + # Adjust ID based on your database setup + user_id = 1 + response = client.get(f"/clients/users/{user_id}") + if response.status_code == 200: + user = response.json() + assert user["id"] == user_id + else: + assert response.status_code == 404 + assert response.json()["detail"] == "Client not found" + +# Test for updating a user +def test_update_user(): + # Adjust ID based on your database setup + user_id = 1 + payload = { + "age": 35, + "gender": "female", + "work_experience": 10, + "canada_workex": 5, + "dep_num": 2, + "canada_born": "no", + "citizen_status": "permanent_resident", + "level_of_schooling": "master", + "fluent_english": "yes", + "reading_english_scale": 4, + "speaking_english_scale": 4, + "writing_english_scale": 4, + "numeracy_scale": 4, + "computer_scale": 5, + "transportation_bool": "no", + "caregiver_bool": "yes", + "housing": "owned", + "income_source": "self_employment", + "felony_bool": "no", + "attending_school": "no", + "currently_employed": "yes", + "substance_use": "no", + "time_unemployed": 0, + "need_mental_health_support_bool": "yes" + } + response = client.put(f"/clients/users/{user_id}", json=payload) + if response.status_code == 200: + assert response.json()["message"] == "User updated successfully" + + # Validate updated data + updated_user = client.get(f"/clients/users/{user_id}").json() + for key in payload.keys(): + assert updated_user[key] == payload[key] + else: + assert response.status_code == 404 + assert response.json()["detail"] == "Client not found" + +# Test for deleting a user +def test_delete_user(): + # Adjust ID based on your database setup + user_id = 1 + response = client.delete(f"/clients/users/{user_id}") + if response.status_code == 200: + assert response.json()["message"] == "Client deleted successfully" + + # Ensure the user no longer exists + get_response = client.get(f"/clients/users/{user_id}") + assert get_response.status_code == 404 + else: + assert response.status_code == 404 + assert response.json()["detail"] == "Client not found" From c8d45d400b1ea6b1a8b7a796b2cdf7d200b51c82 Mon Sep 17 00:00:00 2001 From: zoeyfeng7 <147457131+zoeyfeng7@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:05:51 -0800 Subject: [PATCH 13/13] Update README.md --- README.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a48dc37..3a65cb8c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,15 @@ -This will contain the model used for the project that based on the input information will give the social workers the clients baseline level of success and what their success will be after certain interventions. +The CommonAssessmentTool project is a RESTful API-based system designed to manage and process user data. The project includes endpoints for creating, updating, retrieving, and deleting user records, all backed by a SQLite database. -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. +Core CRUD Functionalities: +Description: Implemented endpoints for creating users (POST /create-user), retrieving all users (GET /users), fetching users by ID (GET /users/{id}), updating user details (PUT /users/{id}), and deleting users (DELETE /users/{id}). -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. +Database Integration and Schema Management: +Description: Designed a structured SQLite database schema to store user data with relevant fields. Added logic to initialize and verify the database schema during the application startup. + +Unit and Integration Testing: +Description: Developed a comprehensive suite of tests using pytest and FastAPI’s test client. The tests validate the behavior of all endpoints under various scenarios, including success cases and edge cases (e.g., non-existent user IDs). + +Improved Error Handling and Validation: +Description: Standardized error responses and added detailed exception handling for common issues such as invalid user inputs, database constraints, and non-existent records. + +These changes enhance the overall stability, scalability, and usability of the CommonAssessmentTool project, providing a strong foundation for managing user data effectively while maintaining a high-quality development process.