diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml new file mode 100644 index 00000000..53d587e2 --- /dev/null +++ b/.github/workflows/backend-ci.yml @@ -0,0 +1,32 @@ +name: Backend Docker 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 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 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 diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 00000000..62e56d26 --- /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') || true diff --git a/.gitignore b/.gitignore index 14d7fa72..eaba3edc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,8 @@ -.venv -.idea -__pycache__ +/bin +/include +/lib +*.db +venv +pyvenv.cfg .DS_Store +__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 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 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. diff --git a/app/clients/crud.py b/app/clients/crud.py new file mode 100644 index 00000000..3a7b749f --- /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_in_db(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(): + """ + Get 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): + """ + Get 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_in_db(uid: int, user_data: PredictionInput): + """ + Update user information based on their ID. + """ + conn = sqlite3.connect(DB_FILE) + 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() + + if not user_exists: + print(f"User with ID {uid} does not exist. Update aborted.") + conn.close() + return False + + # 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() + print("Update query executed and changes committed.") + conn.close() + + print(f"User with ID {uid} has been updated successfully.") + return True + + +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() + + 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 + + cur.execute('DELETE FROM users WHERE id = ?', (uid,)) + conn.commit() + conn.close() + + print(f"User with ID {uid} has been deleted successfully.") + return 200 diff --git a/app/clients/db_setup.py b/app/clients/db_setup.py new file mode 100644 index 00000000..4acb90ef --- /dev/null +++ b/app/clients/db_setup.py @@ -0,0 +1,47 @@ +import sqlite3 +import os + +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 f860c402..2dd485d1 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -1,15 +1,181 @@ -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 +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_in_db, delete_user 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): + """ + API endpoint to create a user in the database. + """ + try: + create_user_in_db(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: {str(e)}" + ) + +# Get user data + + +@router.get("/users", response_class=JSONResponse) +async def get_all_users(): + """ + API endpoint to retrieve all user data from the database. + """ + try: + users = get_all_user_data() + if not users: + return JSONResponse( + content={"message": "No users found in the database."}, + status_code=200 + ) + 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" + ] + + users_list = [dict(zip(keys, user)) for user in users] + + return JSONResponse(content=users_list, status_code=200) + + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"Error retrieving users: {e}" + ) + +# Get client by unique id + + +@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: + client_data = get_user_by_id(user_id) + + if not client_data: + raise HTTPException(status_code=404, detail="Client not found") + + 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" + ] + + client_dict = dict(zip(keys, client_data)) + + return JSONResponse(content=client_dict, status_code=200) + + except HTTPException: + raise + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"Error retrieving user: {e}" + ) + +# 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: + existing_client = get_user_by_id(user_id) + if not existing_client: + raise HTTPException(status_code=404, detail="Client not found") + + update_status = update_user_in_db(user_id, updated_data) + + if not update_status: + raise HTTPException( + status_code=500, detail="Error updating user") + + return JSONResponse( + content={ + "message": "User updated successfully", + "updated_user_id": user_id + }, + status_code=200 + ) + + except HTTPException: + raise + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"Error updating client: {e}" + ) + + +# Delete client + + +@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: + client_data = get_user_by_id(user_id) + + if not client_data: + raise HTTPException(status_code=404, detail="Client not found") + + delete_status = delete_user(user_id) + if delete_status == 404: + raise HTTPException( + status_code=404, detail="Client not found during deletion") + + return JSONResponse( + content={ + "message": "Client deleted successfully", + "deleted_client": client_data + }, + status_code=200 + ) + except HTTPException: + raise + except Exception as e: + raise HTTPException( + status_code=500, + detail=f"Error deleting client: {e}" + ) diff --git a/app/clients/service/data_commontool.csv b/app/clients/service/data_commontool.csv index 09499d98..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 +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..d51f05b0 100644 --- a/app/main.py +++ b/app/main.py @@ -1,11 +1,17 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware + 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_in_db, delete_user +from app.clients.db_setup import create_tables app = FastAPI() +create_tables() + # Set API endpoints on router app.include_router(clients_router) @@ -16,5 +22,3 @@ allow_methods=["*"], # Allows all methods, including OPTIONS allow_headers=["*"], # Allows all headers ) - - 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 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"