From 0e94714bcd2bd99aee2e355ebb0d9e8409b1b514 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Sun, 17 Nov 2024 23:23:21 -0800 Subject: [PATCH 01/38] feat: delete api --- app/clients/router.py | 5 ++++- app/clients/service/delete.py | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 app/clients/service/delete.py diff --git a/app/clients/router.py b/app/clients/router.py index f860c402..b1c49250 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -12,4 +12,7 @@ async def predict(data: PredictionInput): print(data.model_dump()) return interpret_and_calculate(data.model_dump()) - +@router.delete("/{client_id}") +async def delete_client_endpoint(client_id: str): + print(f"Delete client: {client_id}") + return await delete_client(client_id) diff --git a/app/clients/service/delete.py b/app/clients/service/delete.py new file mode 100644 index 00000000..0900fd20 --- /dev/null +++ b/app/clients/service/delete.py @@ -0,0 +1,21 @@ +from fastapi import HTTPException + +def check_valid_input(): + # TODO: check + pass + +# Deletion service logic +async def delete_client(client_id: str): + try: + # TODO: delete from database + + return { + "success": True, + "message": f"Client {client_id} successfully deleted", + "client_id": client_id + } + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"Client with ID {client_id} not found or could not be deleted" + ) From bdf2e92f047995fc30971c222505fe628ccdf944 Mon Sep 17 00:00:00 2001 From: Kevin990001 <79775196+Kevin990001@users.noreply.github.com> Date: Mon, 18 Nov 2024 16:17:42 -0800 Subject: [PATCH 02/38] Add to branch --- app/clients/service/retrieve.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 app/clients/service/retrieve.py diff --git a/app/clients/service/retrieve.py b/app/clients/service/retrieve.py new file mode 100644 index 00000000..533d7dc5 --- /dev/null +++ b/app/clients/service/retrieve.py @@ -0,0 +1,19 @@ +from fastapi import HTTPException + + +async def retrieve_client(client_id: str): + try: + # Check if client exists + if client_id not in database: + raise Exception("Client not found") + + # Return client information + return { + "success": True, + "data": database[client_id] + } + except Exception as e: + raise HTTPException( + status_code=404, + detail=f"Client with ID {client_id} not found" + ) From a27ef7e6a863578e7c8277ba82c157656df0f499 Mon Sep 17 00:00:00 2001 From: G C Date: Mon, 18 Nov 2024 19:35:29 -0800 Subject: [PATCH 03/38] add db creation sql add db creation sql --- db.sql | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 db.sql diff --git a/db.sql b/db.sql new file mode 100644 index 00000000..989ff578 --- /dev/null +++ b/db.sql @@ -0,0 +1,35 @@ +CREATE TABLE CommonAssessmentTool_Table ( + age INTEGER, + gender TEXT, + work_experience INTEGER, + canada_workex INTEGER, + dep_num INTEGER, + canada_born TEXT, + citizen_status TEXT, + level_of_schooling TEXT, + fluent_english TEXT, + reading_english_scale INTEGER, + speaking_english_scale INTEGER, + writing_english_scale INTEGER, + numeracy_scale INTEGER, + computer_scale INTEGER, + transportation_bool TEXT, + caregiver_bool TEXT, + housing TEXT, + income_source TEXT, + felony_bool TEXT, + attending_school TEXT, + currently_employed TEXT, + substance_use TEXT, + time_unemployed INTEGER, + need_mental_health_support_bool TEXT, + employment_assistance INTEGER, + life_stabilization INTEGER, + retention_services INTEGER, + specialized_services INTEGER, + employment_related_financial_supports INTEGER, + employer_financial_supports INTEGER, + enhanced_referrals INTEGER, + success_rate INTEGER, + client_id INTEGER PRIMARY KEY AUTOINCREMENT +); \ No newline at end of file From e51eccfac855f3719d6bf849a1ec105d14988d68 Mon Sep 17 00:00:00 2001 From: ruchenlu Date: Tue, 19 Nov 2024 00:23:05 -0800 Subject: [PATCH 04/38] update api --- app/clients/router.py | 14 ++++++++++- app/clients/service/update.py | 45 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 app/clients/service/update.py diff --git a/app/clients/router.py b/app/clients/router.py index b1c49250..2af6f8fd 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -1,9 +1,12 @@ from fastapi import APIRouter from fastapi.responses import HTMLResponse - from app.clients.service.logic import interpret_and_calculate from app.clients.schema import PredictionInput +from fastapi import HTTPException +from sqlalchemy.orm import Session +from .database import get_db +from app.clients.service.update import update_client_data, ClientUpdateRequest router = APIRouter(prefix="/clients", tags=["clients"]) @router.post("/predictions") @@ -16,3 +19,12 @@ async def predict(data: PredictionInput): async def delete_client_endpoint(client_id: str): print(f"Delete client: {client_id}") return await delete_client(client_id) + + +@router.put("/client/{client_id}") +async def update_client(client_id: int, update_data: ClientUpdateRequest, db: Session = Depends(get_db)): + try: + updated_client = update_client_data(client_id, update_data.dict(exclude_unset=True), db) + return updated_client + except Exception as e: + raise HTTPException(status_code=404, detail=str(e)) \ No newline at end of file diff --git a/app/clients/service/update.py b/app/clients/service/update.py new file mode 100644 index 00000000..ae0e9eb5 --- /dev/null +++ b/app/clients/service/update.py @@ -0,0 +1,45 @@ +from fastapi import HTTPException +from sqlalchemy.orm import Session +from pydantic import BaseModel, Field +from typing import Optional + +# Define the update request model based on actual Client model fields +class ClientUpdateRequest(BaseModel): + age: Optional[int] = Field(None, description="Client's age") + gender: Optional[bool] = Field(None, description="Client's gender") + work_experience: Optional[int] = Field(None, description="Years of work experience") + canada_workex: Optional[int] = Field(None, description="Years of work experience in Canada") + dep_num: Optional[int] = Field(None, description="Number of dependents") + canada_born: Optional[bool] = Field(None, description="Born in Canada") + citizen_status: Optional[int] = Field(None, description="Citizen status") + level_of_schooling: Optional[int] = Field(None, description="Highest level of schooling achieved") + fluent_english: Optional[int] = Field(None, description="English fluency level") + reading_english_scale: Optional[int] = Field(None, description="English reading scale") + speaking_english_scale: Optional[int] = Field(None, description="English speaking comfort level") + writing_english_scale: Optional[int] = Field(None, description="English writing scale") + numeracy_scale: Optional[int] = Field(None, description="Numeracy scale") + computer_scale: Optional[int] = Field(None, description="Computer use scale") + transportation_bool: Optional[bool] = Field(None, description="Needs transportation support") + caregiver_bool: Optional[bool] = Field(None, description="Is a primary caregiver") + housing: Optional[int] = Field(None, description="Housing situation") + income_source: Optional[int] = Field(None, description="Source of income") + felony_bool: Optional[bool] = Field(None, description="Has a felony") + attending_school: Optional[bool] = Field(None, description="Currently a student") + currently_employed: Optional[bool] = Field(None, description="Currently employed") + substance_use: Optional[bool] = Field(None, description="Substance use disorder") + time_unemployed: Optional[int] = Field(None, description="Time unemployed") + need_mental_health_support_bool: Optional[bool] = Field(None, description="Needs mental health support") + +# Update client data in the database +def update_client_data(client_id: int, update_data: ClientUpdateRequest, db: Session): + from .models import Client + client = db.query(Client).filter(Client.id == client_id).first() + if not client: + raise HTTPException(status_code=404, detail="Client not found") + + update_dict = update_data.dict(exclude_unset=True) + for key, value in update_dict.items(): + setattr(client, key, value) + + db.commit() + return client \ No newline at end of file From 02eb4bba533a63365ca630cca4875a7c470749cf Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 19 Nov 2024 20:38:32 -0800 Subject: [PATCH 05/38] feat: ci --- .github/workflows/ci.yml | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..373d8788 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,51 @@ +name: CI Pipeline + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + ci: + runs-on: ubuntu-latest + + steps: + # Checkout the code + - name: Checkout Code + uses: actions/checkout@v4 + + # Set up Python + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + # Install dependencies + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pylint pytest + + # Run linter (Pylint) + - name: Run Pylint + run: | + echo "Running Pylint..." + pylint **/*.py + + # Run tests + # - name: Run Tests + # run: | + # echo "Running Tests..." + # pytest tests/ + + # Logs for debugging + - name: Debugging Logs + run: | + echo "Installed Python Version:" + python --version + echo "Installed Packages:" + pip list From 3ff61b7e6229b5fef84263a1d8e6c607102c06a4 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 19 Nov 2024 20:42:05 -0800 Subject: [PATCH 06/38] fix: bugs --- app/main.py | 3 +-- tests/test.py | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/main.py b/app/main.py index 5b6bf162..9ca9ea0a 100644 --- a/app/main.py +++ b/app/main.py @@ -1,3 +1,4 @@ +"""Main function""" from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware @@ -16,5 +17,3 @@ allow_methods=["*"], # Allows all methods, including OPTIONS allow_headers=["*"], # Allows all headers ) - - diff --git a/tests/test.py b/tests/test.py index a911f0a2..2aa8bd4b 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,4 +1,5 @@ -from logic import interpret_and_calculate +"""Test functions""" +# from logic import interpret_and_calculate from itertools import combinations_with_replacement # def test_interpret_and_calculate(): @@ -20,4 +21,4 @@ result = list(combinations_with_replacement([0, 1], 2)) # Output: [(0, 0), (0, 1), (1, 1)] -print(result) \ No newline at end of file +print(result) From 30fff5ed71c187f7fa7f13ff82a23b7195990b70 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 16:11:26 -0800 Subject: [PATCH 07/38] feat: delete api logic --- app/clients/router.py | 1 + app/clients/service/delete.py | 40 +++++++++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/app/clients/router.py b/app/clients/router.py index b1c49250..89189245 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -2,6 +2,7 @@ from fastapi.responses import HTMLResponse from app.clients.service.logic import interpret_and_calculate +from app.clients.service.delete import delete_client from app.clients.schema import PredictionInput router = APIRouter(prefix="/clients", tags=["clients"]) diff --git a/app/clients/service/delete.py b/app/clients/service/delete.py index 0900fd20..3289ddbf 100644 --- a/app/clients/service/delete.py +++ b/app/clients/service/delete.py @@ -1,21 +1,43 @@ +import sqlite3 from fastapi import HTTPException -def check_valid_input(): - # TODO: check - pass +DATABASE = '../mydatabase.db' + +def check_valid_input(client_id): + if not client_id.isdigit(): + raise HTTPException(status_code=400, detail="Invalid client_id format.") # Deletion service logic async def delete_client(client_id: str): + # Validate input + check_valid_input(client_id) try: - # TODO: delete from database - + # Connect to the database + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() + + # Check if the client exists + cursor.execute("SELECT * FROM CommonAssessmentTool_Table WHERE client_id = ?", (client_id,)) + row = cursor.fetchone() + if not row: + raise HTTPException( + status_code=404, + detail=f"Client with ID {client_id} not found" + ) + + # Delete the client + cursor.execute("DELETE FROM CommonAssessmentTool_Table WHERE client_id = ?", (client_id,)) + conn.commit() + return { "success": True, "message": f"Client {client_id} successfully deleted", "client_id": client_id } - except Exception as e: + except sqlite3.Error as e: raise HTTPException( - status_code=404, - detail=f"Client with ID {client_id} not found or could not be deleted" - ) + status_code=500, + detail=f"Database error occurred: {str(e)}" + ) from e + finally: + conn.close() From fa322c7548707f8a12d8468158444a9dc572ceaf Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 19:51:12 -0800 Subject: [PATCH 08/38] feat: cd --- .github/workflows/cd.yml | 39 +++++++++++++++++++++++++++++++++++++++ Dockerfile | 11 +++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/workflows/cd.yml create mode 100644 Dockerfile diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml new file mode 100644 index 00000000..2497e5d9 --- /dev/null +++ b/.github/workflows/cd.yml @@ -0,0 +1,39 @@ +name: CD Pipeline + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + cd: + runs-on: ubuntu-latest + + steps: + # Checkout the code + - name: Checkout Code + uses: actions/checkout@v3 + + # Build Docker Image + - name: Build Docker Image + run: docker build -t my-api . + + # Run the Docker Container + - name: Run Docker Container + run: docker run -d --name my-container -p 8080:80 my-api + + # Test the Running Container + - name: Test API Endpoints + run: | + sleep 5 # Allow some time for the container to start + curl --fail http://localhost:8080 || exit 1 + echo "API is accessible and working!" + + # Stop and Remove the Container + - name: Cleanup + run: | + docker stop my-container + docker rm my-container diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..6d568e39 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:3.11 +# +WORKDIR /code +# +COPY ./requirements.txt /code/requirements.txt +# +RUN pip install --no-cache-dir -r /code/requirements.txt +# +COPY . /code +# +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] From 28eed68a4ebfc9de0fc2bc41d9fcdb9ab45aaff7 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 20:31:07 -0800 Subject: [PATCH 09/38] fix: bugs --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 6d568e39..d3506974 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11 +FROM python:3.10 # WORKDIR /code # From 66d3cee700478dd371a07c43e2035513b2382a63 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 20:35:43 -0800 Subject: [PATCH 10/38] fix: bugs --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2497e5d9..5f25a86b 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -29,7 +29,7 @@ jobs: - name: Test API Endpoints run: | sleep 5 # Allow some time for the container to start - curl --fail http://localhost:8080 || exit 1 + curl --fail http://0.0.0.0:8080/docs || exit 1 echo "API is accessible and working!" # Stop and Remove the Container From 936ee7809f35f53f54a2298b5f470117b9106e9d Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 20:36:16 -0800 Subject: [PATCH 11/38] fix: bugs --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 5f25a86b..2ac4e412 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -29,7 +29,7 @@ jobs: - name: Test API Endpoints run: | sleep 5 # Allow some time for the container to start - curl --fail http://0.0.0.0:8080/docs || exit 1 + curl --fail http://0.0.0.0:8000/docs || exit 1 echo "API is accessible and working!" # Stop and Remove the Container From 402eee7e17092e1455f48a04333cfb1c4acc1635 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 20:37:18 -0800 Subject: [PATCH 12/38] fix: bugs --- .github/workflows/cd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 2ac4e412..f3be2bbb 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -23,7 +23,7 @@ jobs: # Run the Docker Container - name: Run Docker Container - run: docker run -d --name my-container -p 8080:80 my-api + run: docker run -d --name my-container -p 8000:80 my-api # Test the Running Container - name: Test API Endpoints From ee48e9df6735e74eb2cb47aca1819f4da1feb45d Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 20:42:57 -0800 Subject: [PATCH 13/38] fix: bugs --- .github/workflows/cd.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f3be2bbb..6ad2a849 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -23,13 +23,13 @@ jobs: # Run the Docker Container - name: Run Docker Container - run: docker run -d --name my-container -p 8000:80 my-api + run: docker run -d --name my-container -p 8080:80 my-api # Test the Running Container - name: Test API Endpoints run: | - sleep 5 # Allow some time for the container to start - curl --fail http://0.0.0.0:8000/docs || exit 1 + sleep 10 # Allow some time for the container to start + curl --fail http://0.0.0.0:8080/docs || exit 1 echo "API is accessible and working!" # Stop and Remove the Container From feb450c77a18078c9b315ad157accbbb96636348 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 26 Nov 2024 20:45:20 -0800 Subject: [PATCH 14/38] fix: bugs --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index d3506974..58351e62 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,4 @@ RUN pip install --no-cache-dir -r /code/requirements.txt # COPY . /code # -CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"] +CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"] From 5dfaeb3936a43502e421ad81c6b75938e2f3aabe Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Sun, 1 Dec 2024 11:44:43 -0800 Subject: [PATCH 15/38] fix: tests and docs --- README.md | 61 +++++++++++++++++++++++++++++++++++ app/clients/mapper.py | 33 +++++++++++++++++++ app/clients/service/delete.py | 31 ++++++------------ requirements.txt | 1 + tests/test_delete_client.py | 46 ++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 21 deletions(-) create mode 100644 app/clients/mapper.py create mode 100644 tests/test_delete_client.py diff --git a/README.md b/README.md index 0a48dc37..dfb49871 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,64 @@ This will contain the model used for the project that based on the input informa The model works off of dummy data of several combinations of clients alongside the interventions chosen for them as well as their success rate at finding a job afterward. The model will be updated by the case workers by inputing new data for clients with their updated outcome information, and it can be updated on a daily, weekly, or monthly basis. This also has an API file to interact with the front end, and logic in order to process the interventions coming from the front end. This includes functions to clean data, create a matrix of all possible combinations in order to get the ones with the highest increase of success, and output the results in a way the front end can interact with. + +# Documentation + +## DELETE `/clients/{client_id}` + +### Description +Deletes a client with the specified `client_id` from the database. + +### Path Parameters + +| Parameter | Type | Description | +|-------------|----------|----------------------------------------| +| `client_id` | `string` | The unique ID of the client to delete. | + +### Request Example + +**URL:** + +``` +DELETE /clients/1 +``` +## Responses + +**200 OK** + +The client was successfully deleted. + +**Response Example:** +```json +{ + "success": true, + "message": "Client 1 successfully deleted", + "client_id": "1" +} +``` + +**404 Not Found** + +No client with the specified `client_id` exists. + +**Response Example:** +```json +{ + "detail": "Client with ID 1 not found." +} +``` + +**500 Internal Server Error** + +An unexpected error occurred on the server. + +**Response Example:** +```json +{ + "detail": "An error message describing the issue." +} +``` + +### Notes + +This endpoint requires the `client_id` to be a valid **integer string**. diff --git a/app/clients/mapper.py b/app/clients/mapper.py new file mode 100644 index 00000000..4ad8c214 --- /dev/null +++ b/app/clients/mapper.py @@ -0,0 +1,33 @@ +import os +import sqlite3 +from typing import Optional + +DATABASE = os.path.join(os.path.dirname(__file__), '../../mydatabase.db') + +def get_client(client_id: str) -> Optional[tuple]: + """ + Retrieve a client record from the database by client_id. + """ + try: + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() + cursor.execute("SELECT * FROM CommonAssessmentTool_Table WHERE client_id = ?", (client_id,)) + return cursor.fetchone() + except sqlite3.Error as e: + raise Exception(f"Database error occurred: {str(e)}") from e + finally: + conn.close() + +def delete_client_from_db(client_id: str): + """ + Delete a client record from the database by client_id. + """ + try: + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() + cursor.execute("DELETE FROM CommonAssessmentTool_Table WHERE client_id = ?", (client_id,)) + conn.commit() + except sqlite3.Error as e: + raise Exception(f"Database error occurred: {str(e)}") from e + finally: + conn.close() diff --git a/app/clients/service/delete.py b/app/clients/service/delete.py index 3289ddbf..2d5553b3 100644 --- a/app/clients/service/delete.py +++ b/app/clients/service/delete.py @@ -1,43 +1,32 @@ -import sqlite3 from fastapi import HTTPException - -DATABASE = '../mydatabase.db' +from app.clients.mapper import get_client, delete_client_from_db def check_valid_input(client_id): + """Check if the input is valid""" if not client_id.isdigit(): raise HTTPException(status_code=400, detail="Invalid client_id format.") -# Deletion service logic async def delete_client(client_id: str): + """Deletion service logic""" # Validate input check_valid_input(client_id) - try: - # Connect to the database - conn = sqlite3.connect(DATABASE) - cursor = conn.cursor() - # Check if the client exists - cursor.execute("SELECT * FROM CommonAssessmentTool_Table WHERE client_id = ?", (client_id,)) - row = cursor.fetchone() - if not row: + # Retrieve the client + try: + client = get_client(client_id) + if not client: raise HTTPException( status_code=404, detail=f"Client with ID {client_id} not found" ) # Delete the client - cursor.execute("DELETE FROM CommonAssessmentTool_Table WHERE client_id = ?", (client_id,)) - conn.commit() + delete_client_from_db(client_id) return { "success": True, "message": f"Client {client_id} successfully deleted", "client_id": client_id } - except sqlite3.Error as e: - raise HTTPException( - status_code=500, - detail=f"Database error occurred: {str(e)}" - ) from e - finally: - conn.close() + except Exception as e: + raise e diff --git a/requirements.txt b/requirements.txt index 1ccf75b7..2c306757 100644 --- a/requirements.txt +++ b/requirements.txt @@ -97,6 +97,7 @@ Pygments==2.16.1 pylint==3.0.1 pyrsistent==0.19.3 pytest==7.2.0 +pytest-asyncio==0.21.0 python-dateutil==2.8.2 python-dotenv==1.0.0 python-jose==3.3.0 diff --git a/tests/test_delete_client.py b/tests/test_delete_client.py new file mode 100644 index 00000000..3ef791ee --- /dev/null +++ b/tests/test_delete_client.py @@ -0,0 +1,46 @@ +import pytest +from fastapi import HTTPException +from app.clients.service.delete import delete_client + +@pytest.mark.asyncio +async def test_delete_client_success(): + """ + Test successful deletion of a client. + """ + + # NOTE: this id should be changed each time running the test + # please ensure the id is exist in current db + client_id = "61" + result = await delete_client(client_id) + + # Assertions + assert result["success"] is True + assert result["message"] == f"Client {client_id} successfully deleted" + assert result["client_id"] == client_id + +@pytest.mark.asyncio +async def test_delete_client_not_found(): + """ + Test deletion when the client is not found in the database. + """ + + client_id = "250" + with pytest.raises(HTTPException) as excinfo: + await delete_client(client_id) + + # Assertions + assert excinfo.value.status_code == 404 + assert excinfo.value.detail == f"Client with ID {client_id} not found" + +@pytest.mark.asyncio +async def test_delete_client_invalid_id(): + """ + Test deletion with an invalid client_id. + """ + client_id = "invalid_id" + with pytest.raises(HTTPException) as excinfo: + await delete_client(client_id) + + # Assertions + assert excinfo.value.status_code == 400 + assert excinfo.value.detail == "Invalid client_id format." From d8deb1d72848d33ae9ad768b316c91f3dc7a616c Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Sun, 1 Dec 2024 11:48:34 -0800 Subject: [PATCH 16/38] fix: missing docstring --- tests/test_delete_client.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_delete_client.py b/tests/test_delete_client.py index 3ef791ee..112fc9af 100644 --- a/tests/test_delete_client.py +++ b/tests/test_delete_client.py @@ -1,3 +1,4 @@ +"""Test functions for the delete api""" import pytest from fastapi import HTTPException from app.clients.service.delete import delete_client From 8c8fe0fc959528c1745fca3e1a48cd806def6867 Mon Sep 17 00:00:00 2001 From: Kevin990001 <79775196+Kevin990001@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:37:14 -0800 Subject: [PATCH 17/38] add test --- tests/test_retrieve_client.py | 48 +++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/test_retrieve_client.py diff --git a/tests/test_retrieve_client.py b/tests/test_retrieve_client.py new file mode 100644 index 00000000..e616c545 --- /dev/null +++ b/tests/test_retrieve_client.py @@ -0,0 +1,48 @@ +"""Test functions for the retrieve API""" +import pytest +from fastapi import HTTPException +from app.clients.service.retrieve import retrieve_client + +# Mock database for testing +database = { + "123": {"name": "John Doe", "email": "john@example.com"}, + "456": {"name": "Jane Smith", "email": "jane@example.com"} +} + +@pytest.mark.asyncio +async def test_retrieve_client_success(): + """ + Test successful retrieval of a client. + """ + client_id = "123" # Ensure this ID exists in the mock database + result = await retrieve_client(client_id) + + # Assertions + assert result["success"] is True + assert result["data"] == {"name": "John Doe", "email": "john@example.com"} + +@pytest.mark.asyncio +async def test_retrieve_client_not_found(): + """ + Test retrieval when the client is not found in the database. + """ + client_id = "999" # Ensure this ID does not exist in the mock database + with pytest.raises(HTTPException) as excinfo: + await retrieve_client(client_id) + + # Assertions + assert excinfo.value.status_code == 404 + assert excinfo.value.detail == f"Client with ID {client_id} not found" + +@pytest.mark.asyncio +async def test_retrieve_client_invalid_id(): + """ + Test retrieval with an invalid client_id format. + """ + client_id = "" # Simulate an invalid ID + with pytest.raises(HTTPException) as excinfo: + await retrieve_client(client_id) + + # Assertions + assert excinfo.value.status_code == 400 + assert excinfo.value.detail == "Invalid client_id format." From e478182979540e8296ccee6938ab2140a59b1a0e Mon Sep 17 00:00:00 2001 From: Kevin990001 <79775196+Kevin990001@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:38:32 -0800 Subject: [PATCH 18/38] add readme --- README.md | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/README.md b/README.md index dfb49871..d684fc29 100644 --- a/README.md +++ b/README.md @@ -64,3 +64,72 @@ An unexpected error occurred on the server. ### Notes This endpoint requires the `client_id` to be a valid **integer string**. + + +GET /clients/{client_id} +Description +Retrieves information for a client with the specified client_id from the database. + +Path Parameters +Parameter Type Description +client_id string The unique ID of the client to retrieve. +Request Example +URL: + +http +Copy code +GET /clients/123 +Responses +200 OK +The client information was successfully retrieved. + +Response Example: + +json +Copy code +{ + "success": true, + "data": { + "name": "John Doe", + "email": "john@example.com" + } +} +404 Not Found +No client with the specified client_id exists. + +Response Example: + +json +Copy code +{ + "detail": "Client with ID 123 not found." +} +400 Bad Request +The client_id is invalid or improperly formatted. + +Response Example: + +json +Copy code +{ + "detail": "Invalid client_id format." +} +500 Internal Server Error +An unexpected error occurred on the server. + +Response Example: + +json +Copy code +{ + "detail": "An error message describing the issue." +} +Notes +The client_id must be a valid string that matches the expected format in the database. +Ensure the client_id exists before making the request to avoid a 404 error. + + + + + + From baad1de020018de4c91f69ae672526af887848e7 Mon Sep 17 00:00:00 2001 From: Kevin990001 <79775196+Kevin990001@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:48:13 -0800 Subject: [PATCH 19/38] update readme --- README.md | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index d684fc29..2939ebe4 100644 --- a/README.md +++ b/README.md @@ -66,27 +66,33 @@ An unexpected error occurred on the server. This endpoint requires the `client_id` to be a valid **integer string**. -GET /clients/{client_id} -Description -Retrieves information for a client with the specified client_id from the database. +### **GET /clients/{client_id}** -Path Parameters -Parameter Type Description -client_id string The unique ID of the client to retrieve. -Request Example -URL: +#### **Description** +Retrieves information for a client with the specified `client_id` from the database. -http -Copy code +--- + +### **Path Parameters** + +| Parameter | Type | Description | +|------------|--------|---------------------------------------| +| `client_id` | string | The unique ID of the client to retrieve. | + +--- + +### **Request Example** + +**URL**: + +```http GET /clients/123 + Responses 200 OK The client information was successfully retrieved. Response Example: - -json -Copy code { "success": true, "data": { @@ -94,40 +100,36 @@ Copy code "email": "john@example.com" } } + 404 Not Found No client with the specified client_id exists. Response Example: - -json -Copy code { "detail": "Client with ID 123 not found." } + 400 Bad Request The client_id is invalid or improperly formatted. Response Example: - -json -Copy code { "detail": "Invalid client_id format." } + + 500 Internal Server Error An unexpected error occurred on the server. Response Example: - -json -Copy code { "detail": "An error message describing the issue." } + Notes The client_id must be a valid string that matches the expected format in the database. Ensure the client_id exists before making the request to avoid a 404 error. - +Copy code From 469d2f6694c24a50f5e8454e8a658dbcc5e8ee2a Mon Sep 17 00:00:00 2001 From: ruchenlu Date: Mon, 2 Dec 2024 18:50:11 -0800 Subject: [PATCH 20/38] update and test --- app/clients/mapper.py | 16 +++++++ app/clients/router.py | 21 +++++---- app/clients/service/update.py | 66 +++++++++++----------------- app/clients/service/update_model.py | 31 +++++++++++++ mydatabase.db | Bin 0 -> 40960 bytes tests/test_update_client.py | 52 ++++++++++++++++++++++ 6 files changed, 135 insertions(+), 51 deletions(-) create mode 100644 app/clients/service/update_model.py create mode 100644 mydatabase.db create mode 100644 tests/test_update_client.py diff --git a/app/clients/mapper.py b/app/clients/mapper.py index 4ad8c214..3d409236 100644 --- a/app/clients/mapper.py +++ b/app/clients/mapper.py @@ -31,3 +31,19 @@ def delete_client_from_db(client_id: str): raise Exception(f"Database error occurred: {str(e)}") from e finally: conn.close() + +def update_client_in_db(client_id: str, update_data: dict): + """Update a client record in the database by client_id.""" + try: + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() + # Assuming update_data is a dict with column names as keys + updates = ", ".join([f"{key} = ?" for key in update_data.keys()]) + values = list(update_data.values()) + values.append(client_id) + cursor.execute(f"UPDATE CommonAssessmentTool_Table SET {updates} WHERE client_id = ?", values) + conn.commit() + except sqlite3.Error as e: + raise Exception(f"Database error occurred: {str(e)}") from e + finally: + conn.close() \ No newline at end of file diff --git a/app/clients/router.py b/app/clients/router.py index 9af61456..09c2d2a7 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -4,10 +4,11 @@ from app.clients.service.delete import delete_client from app.clients.schema import PredictionInput -from fastapi import HTTPException -from sqlalchemy.orm import Session -from .database import get_db -from app.clients.service.update import update_client_data, ClientUpdateRequest +from app.clients.service.update import update_client +from app.clients.service.update_model import ClientUpdateModel +from fastapi import Body + + router = APIRouter(prefix="/clients", tags=["clients"]) @router.post("/predictions") @@ -22,10 +23,8 @@ async def delete_client_endpoint(client_id: str): return await delete_client(client_id) -@router.put("/client/{client_id}") -async def update_client(client_id: int, update_data: ClientUpdateRequest, db: Session = Depends(get_db)): - try: - updated_client = update_client_data(client_id, update_data.dict(exclude_unset=True), db) - return updated_client - except Exception as e: - raise HTTPException(status_code=404, detail=str(e)) \ No newline at end of file +@router.put("/{client_id}", response_model=ClientUpdateModel) +async def update_client_endpoint(client_id: str, update_data: ClientUpdateModel = Body(...)): + print(f"Update client: {client_id}") + return await update_client(client_id, update_data) + diff --git a/app/clients/service/update.py b/app/clients/service/update.py index ae0e9eb5..d601f22c 100644 --- a/app/clients/service/update.py +++ b/app/clients/service/update.py @@ -1,45 +1,31 @@ from fastapi import HTTPException -from sqlalchemy.orm import Session -from pydantic import BaseModel, Field -from typing import Optional +from app.clients.mapper import get_client, update_client_in_db -# Define the update request model based on actual Client model fields -class ClientUpdateRequest(BaseModel): - age: Optional[int] = Field(None, description="Client's age") - gender: Optional[bool] = Field(None, description="Client's gender") - work_experience: Optional[int] = Field(None, description="Years of work experience") - canada_workex: Optional[int] = Field(None, description="Years of work experience in Canada") - dep_num: Optional[int] = Field(None, description="Number of dependents") - canada_born: Optional[bool] = Field(None, description="Born in Canada") - citizen_status: Optional[int] = Field(None, description="Citizen status") - level_of_schooling: Optional[int] = Field(None, description="Highest level of schooling achieved") - fluent_english: Optional[int] = Field(None, description="English fluency level") - reading_english_scale: Optional[int] = Field(None, description="English reading scale") - speaking_english_scale: Optional[int] = Field(None, description="English speaking comfort level") - writing_english_scale: Optional[int] = Field(None, description="English writing scale") - numeracy_scale: Optional[int] = Field(None, description="Numeracy scale") - computer_scale: Optional[int] = Field(None, description="Computer use scale") - transportation_bool: Optional[bool] = Field(None, description="Needs transportation support") - caregiver_bool: Optional[bool] = Field(None, description="Is a primary caregiver") - housing: Optional[int] = Field(None, description="Housing situation") - income_source: Optional[int] = Field(None, description="Source of income") - felony_bool: Optional[bool] = Field(None, description="Has a felony") - attending_school: Optional[bool] = Field(None, description="Currently a student") - currently_employed: Optional[bool] = Field(None, description="Currently employed") - substance_use: Optional[bool] = Field(None, description="Substance use disorder") - time_unemployed: Optional[int] = Field(None, description="Time unemployed") - need_mental_health_support_bool: Optional[bool] = Field(None, description="Needs mental health support") +def check_valid_input(client_id): + """Check if the input is valid""" + if not client_id.isdigit(): + raise HTTPException(status_code=400, detail="Invalid client_id format.") -# Update client data in the database -def update_client_data(client_id: int, update_data: ClientUpdateRequest, db: Session): - from .models import Client - client = db.query(Client).filter(Client.id == client_id).first() - if not client: - raise HTTPException(status_code=404, detail="Client not found") +async def update_client(client_id: str, update_data: dict): + """Update service logic""" + # Validate input + check_valid_input(client_id) - update_dict = update_data.dict(exclude_unset=True) - for key, value in update_dict.items(): - setattr(client, key, value) + # Check if client exists + client = get_client(client_id) + if not client: + raise HTTPException( + status_code=404, + detail=f"Client with ID {client_id} not found" + ) - db.commit() - return client \ No newline at end of file + # Update the client + try: + update_client_in_db(client_id, update_data) + return { + "success": True, + "message": f"Client {client_id} successfully updated", + "client_id": client_id + } + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) diff --git a/app/clients/service/update_model.py b/app/clients/service/update_model.py new file mode 100644 index 00000000..719d595e --- /dev/null +++ b/app/clients/service/update_model.py @@ -0,0 +1,31 @@ +from pydantic import BaseModel, Field + +class ClientUpdateModel(BaseModel): + + age: int = Field(None, example=30) + gender: str = Field(None, example="Male") + work_experience: int = Field(None, example=5) + canada_workex: int = Field(None, example=2) + dep_num: int = Field(None, example=1) + canada_born: str = Field(None, example="No") + citizen_status: str = Field(None, example="Citizen") + level_of_schooling: str = Field(None, example="Bachelor’s degree") + fluent_english: str = Field(None, example="Yes") + reading_english_scale: int = Field(None, example=10) + speaking_english_scale: int = Field(None, example=10) + writing_english_scale: int = Field(None, example=10) + numeracy_scale: int = Field(None, example=10) + computer_scale: int = Field(None, example=10) + transportation_bool: str = Field(None, example="No") + caregiver_bool: str = Field(None, example="No") + housing: str = Field(None, example="Renting") + income_source: str = Field(None, example="Employment") + felony_bool: str = Field(None, example="No") + attending_school: str = Field(None, example="No") + currently_employed: str = Field(None, example="Yes") + substance_use: str = Field(None, example="No") + time_unemployed: int = Field(None, example=0) + need_mental_health_support_bool: str = Field(None, example="No") + + class Config: + orm_mode = True diff --git a/mydatabase.db b/mydatabase.db new file mode 100644 index 0000000000000000000000000000000000000000..93e1091ec3a2f8a8a64ead6f6ed3997ca64736ef GIT binary patch literal 40960 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 Date: Mon, 2 Dec 2024 22:42:10 -0800 Subject: [PATCH 21/38] fix: bugs --- app/clients/mapper.py | 9 ++++++--- app/clients/router.py | 4 +--- app/clients/service/update.py | 2 +- tests/test_update_client.py | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/app/clients/mapper.py b/app/clients/mapper.py index 3d409236..fe6d6239 100644 --- a/app/clients/mapper.py +++ b/app/clients/mapper.py @@ -1,6 +1,7 @@ import os import sqlite3 from typing import Optional +from app.clients.service.update_model import ClientUpdateModel DATABASE = os.path.join(os.path.dirname(__file__), '../../mydatabase.db') @@ -35,15 +36,17 @@ def delete_client_from_db(client_id: str): def update_client_in_db(client_id: str, update_data: dict): """Update a client record in the database by client_id.""" try: + validated_data = ClientUpdateModel(**update_data) + update_dict = validated_data.model_dump(exclude_unset=True) conn = sqlite3.connect(DATABASE) cursor = conn.cursor() # Assuming update_data is a dict with column names as keys - updates = ", ".join([f"{key} = ?" for key in update_data.keys()]) - values = list(update_data.values()) + updates = ", ".join([f"{key} = ?" for key in update_dict.keys()]) + values = list(update_dict.values()) values.append(client_id) cursor.execute(f"UPDATE CommonAssessmentTool_Table SET {updates} WHERE client_id = ?", values) conn.commit() except sqlite3.Error as e: raise Exception(f"Database error occurred: {str(e)}") from e finally: - conn.close() \ No newline at end of file + conn.close() diff --git a/app/clients/router.py b/app/clients/router.py index 09c2d2a7..630fc8d5 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -22,9 +22,7 @@ async def delete_client_endpoint(client_id: str): print(f"Delete client: {client_id}") return await delete_client(client_id) - -@router.put("/{client_id}", response_model=ClientUpdateModel) +@router.put("/{client_id}") async def update_client_endpoint(client_id: str, update_data: ClientUpdateModel = Body(...)): print(f"Update client: {client_id}") return await update_client(client_id, update_data) - diff --git a/app/clients/service/update.py b/app/clients/service/update.py index d601f22c..c86d60b3 100644 --- a/app/clients/service/update.py +++ b/app/clients/service/update.py @@ -28,4 +28,4 @@ async def update_client(client_id: str, update_data: dict): "client_id": client_id } except Exception as e: - raise HTTPException(status_code=500, detail=str(e)) + raise e diff --git a/tests/test_update_client.py b/tests/test_update_client.py index 5fc9d975..2e19ceb8 100644 --- a/tests/test_update_client.py +++ b/tests/test_update_client.py @@ -6,7 +6,7 @@ @pytest.mark.asyncio async def test_update_client_success(): """Test successful update of a client.""" - client_id = "61" + client_id = "45" update_data = { "age": 32, "work_experience": 7, @@ -35,7 +35,7 @@ async def test_update_client_not_found(): # Assertions assert excinfo.value.status_code == 404 - assert excinfo.value.detail == "Client with ID {client_id} not found" + assert excinfo.value.detail == f"Client with ID {client_id} not found" @pytest.mark.asyncio async def test_update_client_invalid_id(): @@ -49,4 +49,4 @@ async def test_update_client_invalid_id(): # Assertions assert excinfo.value.status_code == 400 - assert excinfo.value.detail == "Invalid client_id format." \ No newline at end of file + assert excinfo.value.detail == "Invalid client_id format." From f01845b25fa221767f17023ea22d366dedd8c568 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Mon, 2 Dec 2024 22:47:33 -0800 Subject: [PATCH 22/38] fix: bugs --- app/clients/mapper.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/clients/mapper.py b/app/clients/mapper.py index fe6d6239..c2a12751 100644 --- a/app/clients/mapper.py +++ b/app/clients/mapper.py @@ -36,10 +36,15 @@ def delete_client_from_db(client_id: str): def update_client_in_db(client_id: str, update_data: dict): """Update a client record in the database by client_id.""" try: - validated_data = ClientUpdateModel(**update_data) - update_dict = validated_data.model_dump(exclude_unset=True) conn = sqlite3.connect(DATABASE) cursor = conn.cursor() + # If update_data is already a ClientUpdateModel, convert it to a dict + if isinstance(update_data, ClientUpdateModel): + update_dict = update_data.model_dump(exclude_unset=True) + else: + # If it's a dict, validate it first + validated_data = ClientUpdateModel(**update_data) + update_dict = validated_data.model_dump(exclude_unset=True) # Assuming update_data is a dict with column names as keys updates = ", ".join([f"{key} = ?" for key in update_dict.keys()]) values = list(update_dict.values()) From f31fe25c3928a214b0a6ba15e212062c31645f7f Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Mon, 2 Dec 2024 22:49:42 -0800 Subject: [PATCH 23/38] fix: db --- mydatabase.db | Bin 40960 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 mydatabase.db diff --git a/mydatabase.db b/mydatabase.db deleted file mode 100644 index 93e1091ec3a2f8a8a64ead6f6ed3997ca64736ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40960 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 Date: Mon, 2 Dec 2024 22:56:06 -0800 Subject: [PATCH 24/38] Update README.md --- README.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/README.md b/README.md index 2939ebe4..dfb49c5a 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,113 @@ Ensure the client_id exists before making the request to avoid a 404 error. Copy code +### **PUT /clients/{client_id}** + +#### **Description** +Updates specific fields of an existing client record. + +--- + +### **Path Parameters** + +| Parameter | Type | Description | +|-------------|----------|----------------------------------------| +| `client_id` | `string` | The unique ID of the client to update. | + +--- + +### **Request Body** + +The fields to be updated must be passed as a JSON object. Unspecified fields will remain unchanged. + +| Field | Type | Example | Description | +|--------------------------------|---------|------------------------|-----------------------------------------| +| `age` | integer | `32` | Age of the client. | +| `gender` | string | `"Male"` | Gender of the client. | +| `work_experience` | integer | `7` | Years of total work experience. | +| `canada_workex` | integer | `3` | Years of work experience in Canada. | +| `dep_num` | integer | `1` | Number of dependents. | +| `canada_born` | string | `"No"` | Whether the client was born in Canada. | +| `citizen_status` | string | `"Citizen"` | Citizenship status of the client. | +| `level_of_schooling` | string | `"Master's degree"` | Highest level of education completed. | +| `fluent_english` | string | `"Yes"` | Whether the client is fluent in English. | +| `reading_english_scale` | integer | `10` | English reading skill level (1-10). | +| `speaking_english_scale` | integer | `10` | English speaking skill level (1-10). | +| `writing_english_scale` | integer | `10` | English writing skill level (1-10). | +| `numeracy_scale` | integer | `10` | Numeracy skill level (1-10). | +| `computer_scale` | integer | `10` | Computer skill level (1-10). | +| `transportation_bool` | string | `"No"` | Whether transportation support is needed. | +| `caregiver_bool` | string | `"No"` | Whether the client is a primary caregiver. | +| `housing` | string | `"Homeowner"` | Housing status of the client. | +| `income_source` | string | `"Employment"` | Source of income. | +| `felony_bool` | string | `"No"` | Whether the client has a felony record. | +| `attending_school` | string | `"No"` | Whether the client is currently a student. | +| `currently_employed` | string | `"Yes"` | Employment status of the client. | +| `substance_use` | string | `"No"` | Whether the client has a substance use disorder. | +| `time_unemployed` | integer | `0` | Time unemployed in years. | +| `need_mental_health_support_bool` | string | `"No"` | Whether the client needs mental health support. | + +--- + +### **Request Example** + +**URL**: +PUT /clients/61 + + +**Request Body**: +```json +{ + "age": 32, + "work_experience": 7, + "canada_workex": 3, + "level_of_schooling": "Master's degree", + "fluent_english": "Yes", + "currently_employed": "Yes", + "housing": "Homeowner" +} +Responses +200 OK + +The client information was successfully updated. + +Response Example: + +{ + "success": true, + "message": "Client 61 successfully updated", + "client_id": "61" +} + +404 Not Found + +No client with the specified client_id exists. + +Response Example: + +{ + "detail": "Client with ID 61 not found." +} +400 Bad Request + +The client_id is invalid or improperly formatted. + +Response Example: + +{ + "detail": "Invalid client_id format." +} +{ + "detail": "Invalid client_id format." +} +500 Internal Server Error + +An unexpected error occurred on the server. + +Response Example: +{ + "detail": "An error message describing the issue." +} From 79699852918b0a586f79ff7deb1b585ada1b3619 Mon Sep 17 00:00:00 2001 From: ChenluRu <144400953+ChenluRu@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:22:56 -0800 Subject: [PATCH 25/38] Update README.md --- README.md | 103 ++++++++++-------------------------------------------- 1 file changed, 19 insertions(+), 84 deletions(-) diff --git a/README.md b/README.md index dfb49c5a..e73dc9e4 100644 --- a/README.md +++ b/README.md @@ -66,72 +66,6 @@ An unexpected error occurred on the server. This endpoint requires the `client_id` to be a valid **integer string**. -### **GET /clients/{client_id}** - -#### **Description** -Retrieves information for a client with the specified `client_id` from the database. - ---- - -### **Path Parameters** - -| Parameter | Type | Description | -|------------|--------|---------------------------------------| -| `client_id` | string | The unique ID of the client to retrieve. | - ---- - -### **Request Example** - -**URL**: - -```http -GET /clients/123 - -Responses -200 OK -The client information was successfully retrieved. - -Response Example: -{ - "success": true, - "data": { - "name": "John Doe", - "email": "john@example.com" - } -} - -404 Not Found -No client with the specified client_id exists. - -Response Example: -{ - "detail": "Client with ID 123 not found." -} - -400 Bad Request -The client_id is invalid or improperly formatted. - -Response Example: -{ - "detail": "Invalid client_id format." -} - - -500 Internal Server Error -An unexpected error occurred on the server. - -Response Example: -{ - "detail": "An error message describing the issue." -} - -Notes -The client_id must be a valid string that matches the expected format in the database. -Ensure the client_id exists before making the request to avoid a 404 error. -Copy code - - ### **PUT /clients/{client_id}** #### **Description** @@ -197,48 +131,49 @@ PUT /clients/61 "currently_employed": "Yes", "housing": "Homeowner" } -Responses -200 OK +``` -The client information was successfully updated. +**Responses** +**200 OK** -Response Example: +The client information was successfully updated. +**Response Example:** +```json { "success": true, "message": "Client 61 successfully updated", "client_id": "61" } - -404 Not Found +``` +**404 Not Found** No client with the specified client_id exists. -Response Example: - +**Response Example:** +```json { "detail": "Client with ID 61 not found." } -400 Bad Request +``` +**400 Bad Request** The client_id is invalid or improperly formatted. -Response Example: - -{ - "detail": "Invalid client_id format." -} +**Response Example:** +```json { "detail": "Invalid client_id format." } -500 Internal Server Error +``` +**500 Internal Server Error** An unexpected error occurred on the server. -Response Example: +```json +**Response Example:** { "detail": "An error message describing the issue." } - - +``` From 03d4245c8edd0ec364660e59ffe2669853a23126 Mon Sep 17 00:00:00 2001 From: ChenluRu <144400953+ChenluRu@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:27:06 -0800 Subject: [PATCH 26/38] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e73dc9e4..9c282acc 100644 --- a/README.md +++ b/README.md @@ -170,8 +170,8 @@ The client_id is invalid or improperly formatted. An unexpected error occurred on the server. -```json **Response Example:** +```json { "detail": "An error message describing the issue." } From 05fbbaea8a7205f7c846c973f8b2f845b5d6fff0 Mon Sep 17 00:00:00 2001 From: ChenluRu <144400953+ChenluRu@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:39:13 -0800 Subject: [PATCH 27/38] Update README.md --- README.md | 184 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 124 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 9c282acc..390b64e1 100644 --- a/README.md +++ b/README.md @@ -6,66 +6,6 @@ This also has an API file to interact with the front end, and logic in order to # Documentation -## DELETE `/clients/{client_id}` - -### Description -Deletes a client with the specified `client_id` from the database. - -### Path Parameters - -| Parameter | Type | Description | -|-------------|----------|----------------------------------------| -| `client_id` | `string` | The unique ID of the client to delete. | - -### Request Example - -**URL:** - -``` -DELETE /clients/1 -``` -## Responses - -**200 OK** - -The client was successfully deleted. - -**Response Example:** -```json -{ - "success": true, - "message": "Client 1 successfully deleted", - "client_id": "1" -} -``` - -**404 Not Found** - -No client with the specified `client_id` exists. - -**Response Example:** -```json -{ - "detail": "Client with ID 1 not found." -} -``` - -**500 Internal Server Error** - -An unexpected error occurred on the server. - -**Response Example:** -```json -{ - "detail": "An error message describing the issue." -} -``` - -### Notes - -This endpoint requires the `client_id` to be a valid **integer string**. - - ### **PUT /clients/{client_id}** #### **Description** @@ -177,3 +117,127 @@ An unexpected error occurred on the server. } ``` +## DELETE `/clients/{client_id}` + +### Description +Deletes a client with the specified `client_id` from the database. + +### Path Parameters + +| Parameter | Type | Description | +|-------------|----------|----------------------------------------| +| `client_id` | `string` | The unique ID of the client to delete. | + +### Request Example + +**URL:** + +``` +DELETE /clients/1 +``` +## Responses + +**200 OK** + +The client was successfully deleted. + +**Response Example:** +```json +{ + "success": true, + "message": "Client 1 successfully deleted", + "client_id": "1" +} +``` + +**404 Not Found** + +No client with the specified `client_id` exists. + +**Response Example:** +```json +{ + "detail": "Client with ID 1 not found." +} +``` + +**500 Internal Server Error** + +An unexpected error occurred on the server. + +**Response Example:** +```json +{ + "detail": "An error message describing the issue." +} +``` + +### Notes + +This endpoint requires the `client_id` to be a valid **integer string**. + + +### **GET /clients/{client_id}** + +#### **Description** +Retrieves information for a client with the specified `client_id` from the database. + +--- + +### **Path Parameters** + +| Parameter | Type | Description | +|------------|--------|---------------------------------------| +| `client_id` | string | The unique ID of the client to retrieve. | + +--- + +### **Request Example** + +**URL**: + +```http +GET /clients/123 + +Responses +200 OK +The client information was successfully retrieved. + +Response Example: +{ + "success": true, + "data": { + "name": "John Doe", + "email": "john@example.com" + } +} + +404 Not Found +No client with the specified client_id exists. + +Response Example: +{ + "detail": "Client with ID 123 not found." +} + +400 Bad Request +The client_id is invalid or improperly formatted. + +Response Example: +{ + "detail": "Invalid client_id format." +} + + +500 Internal Server Error +An unexpected error occurred on the server. + +Response Example: +{ + "detail": "An error message describing the issue." +} + +Notes +The client_id must be a valid string that matches the expected format in the database. +Ensure the client_id exists before making the request to avoid a 404 error. +Copy code From 80be56c6490da25832bd4a84e067630c10846e2c Mon Sep 17 00:00:00 2001 From: G C Date: Tue, 3 Dec 2024 04:48:41 -0800 Subject: [PATCH 28/38] add get and create feature add get and create feature --- app/clients/mapper.py | 28 ++++++++++++++++++ app/clients/router.py | 19 +++++++++++- app/clients/service/create.py | 18 ++++++++++++ app/clients/service/retrieve.py | 44 +++++++++++++++++++++------- app/clients/service/update_model.py | 8 +++++ mydatabase.db | Bin 0 -> 40960 bytes 6 files changed, 105 insertions(+), 12 deletions(-) create mode 100644 app/clients/service/create.py create mode 100644 mydatabase.db diff --git a/app/clients/mapper.py b/app/clients/mapper.py index c2a12751..81aa06be 100644 --- a/app/clients/mapper.py +++ b/app/clients/mapper.py @@ -55,3 +55,31 @@ def update_client_in_db(client_id: str, update_data: dict): raise Exception(f"Database error occurred: {str(e)}") from e finally: conn.close() + +def create_client_in_db(client_data: ClientUpdateModel) -> int: + try: + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() + if isinstance(client_data, ClientUpdateModel): + client_dict = client_data.model_dump(exclude_unset=True) + else: + validated_data = ClientUpdateModel(**client_data) + client_dict = validated_data.model_dump(exclude_unset=True) + + columns = ", ".join(client_dict.keys()) + placeholders = ", ".join(["?"] * len(client_dict)) + values = list(client_dict.values()) + + cursor.execute( + f"INSERT INTO CommonAssessmentTool_Table ({columns}) VALUES ({placeholders})", + values + ) + conn.commit() + + # Retrieve the last inserted row's ID (autoincremented client_id) + return cursor.lastrowid + except sqlite3.Error as e: + raise Exception(f"Database error occurred: {str(e)}") from e + finally: + conn.close() + diff --git a/app/clients/router.py b/app/clients/router.py index 630fc8d5..cbb24c77 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -1,8 +1,12 @@ -from fastapi import APIRouter + +from fastapi import APIRouter, HTTPException from fastapi.responses import HTMLResponse + +from app.clients.service.create import create_client from app.clients.service.logic import interpret_and_calculate from app.clients.service.delete import delete_client from app.clients.schema import PredictionInput +from app.clients.service.retrieve import retrieve_client from app.clients.service.update import update_client from app.clients.service.update_model import ClientUpdateModel @@ -26,3 +30,16 @@ async def delete_client_endpoint(client_id: str): async def update_client_endpoint(client_id: str, update_data: ClientUpdateModel = Body(...)): print(f"Update client: {client_id}") return await update_client(client_id, update_data) + +@router.get("/{client_id}") +async def query_client(client_id: str): + print(f"Query client: {client_id}") + client_data = await retrieve_client(client_id) + if not client_data: + raise HTTPException(status_code=404, detail="Client not found") + return client_data + +@router.post("/") +async def create_client_endpoint(client_data: ClientUpdateModel = Body(...)): + print("Create new client") + return await create_client(client_data) diff --git a/app/clients/service/create.py b/app/clients/service/create.py new file mode 100644 index 00000000..78059d12 --- /dev/null +++ b/app/clients/service/create.py @@ -0,0 +1,18 @@ +from fastapi import HTTPException + +from app.clients.mapper import create_client_in_db +from app.clients.service.update_model import ClientUpdateModel + + +async def create_client(client_data: ClientUpdateModel): + validated_data = client_data + + try: + client_id = create_client_in_db(validated_data) + return { + "success": True, + "message": f"Client successfully created with ID {client_id}", + "client_id": client_id + } + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) diff --git a/app/clients/service/retrieve.py b/app/clients/service/retrieve.py index 533d7dc5..18ed36ba 100644 --- a/app/clients/service/retrieve.py +++ b/app/clients/service/retrieve.py @@ -1,19 +1,41 @@ from fastapi import HTTPException +from app.clients.mapper import get_client + +def check_valid_input(client_id): + if not client_id.isdigit(): + raise HTTPException(status_code=400, detail="Invalid client_id format.") async def retrieve_client(client_id: str): try: - # Check if client exists - if client_id not in database: - raise Exception("Client not found") + # Query the client + client = get_client(client_id) + if not client: + raise HTTPException( + status_code=404, + detail=f"Client with ID {client_id} not found" + ) + + client_data = map_client_data(client) - # Return client information - return { - "success": True, - "data": database[client_id] - } + # Return the client data as JSON + return client_data except Exception as e: raise HTTPException( - status_code=404, - detail=f"Client with ID {client_id} not found" - ) + status_code=500, + detail="An unexpected error occurred while retrieving the client." + ) from e + + +def map_client_data(client: tuple) -> dict: + columns = [ + "age", "gender", "work_experience", "canada_workex", "dep_num", "canada_born", + "citizen_status", "level_of_schooling", "fluent_english", "reading_english_scale", + "speaking_english_scale", "writing_english_scale", "numeracy_scale", "computer_scale", + "transportation_bool", "caregiver_bool", "housing", "income_source", "felony_bool", + "attending_school", "currently_employed", "substance_use", "time_unemployed", + "need_mental_health_support_bool", "employment_assistance", "life_stabilization", + "retention_services", "specialized_services", "employment_related_financial_supports", + "employer_financial_supports", "enhanced_referrals", "success_rate", "client_id" + ] + return dict(zip(columns, client)) \ No newline at end of file diff --git a/app/clients/service/update_model.py b/app/clients/service/update_model.py index 719d595e..375a1b4b 100644 --- a/app/clients/service/update_model.py +++ b/app/clients/service/update_model.py @@ -26,6 +26,14 @@ class ClientUpdateModel(BaseModel): substance_use: str = Field(None, example="No") time_unemployed: int = Field(None, example=0) need_mental_health_support_bool: str = Field(None, example="No") + employment_assistance: int = Field(None, example=1) + life_stabilization: int = Field(None, example=1) + retention_services: int = Field(None, example=1) + specialized_services: int = Field(None, example=1) + employment_related_financial_supports: int = Field(None, example=1) + employer_financial_supports: int = Field(None, example=1) + enhanced_referrals: int = Field(None, example=1) + success_rate: int = Field(None, example=100) class Config: orm_mode = True diff --git a/mydatabase.db b/mydatabase.db new file mode 100644 index 0000000000000000000000000000000000000000..6919a41c163f023270d3cad1d0cfa87ffca6e0b2 GIT binary patch literal 40960 zcmeHQU5p#ob)G*CXJ&txtyY#S$?LUbS*st*_YQZL9Fn%Rl6P&{vb0gwb)=?ds3j$B z#U&+jSGJtA2_MoHZXVjAMjJmEeJGFyEsCH;Q5YytAZXz}6>WhA1>B-V8$Yx_fVOCp zhdi|3IhVs7E=dUpgrpAc$P~HDIdks)?sv`^!QYwtnNMHX*s9H5Z?-loTeDf?Q-*08 zXJ%&&!#ILhfR~S#gV$8|9dCS?@rV1oA2Ckf{gnxvV2y3sW1C}tmj38IXX~#t0vZ90 zfJQ(gpb^jrXaqC@KS>CzTJFA+C(SEcm234{`%WEq-sN`f&UUS_zWYb}+~wt^wdL8h zrLz~7XLp@D*QjjPPQmi{FBk3fzNIDeS53a=x#s3(v$51}*V^sPT4QUi*{qk>$lK^| ztUgbp&zqeKW^q(*)Mn3LTw8u|`SL0L#f@5{T5HX&Eq``R{o>7L>sGn;*6mtrgC^F0 z_IjmJsaDE#PVKG!ud21%<;M1A|1Z1eUu(7+2^Z@dTN`iJ8s+v@Wox^g_@Z9BTdS9w z*URnoo0$7X<3{4k>vbT#RjxH|)Hm8U6JNAyl`2k+zkzY3zH8j=+qKHA2b}X}3&Xnq zWiY>5tFnG?n2Yu1=I!mRT5H&MTdhi?eY@Ghd^ehn@-@sYG5+;Rt9E1KE=+fQakIJI z?#E!G0XJY>bGwD5*;%yfwR*F0Z?}DAYYVGOb5eLF4A!?>EudMySFUZ|t~c-1s);Y# z+t;u(Sp4#KJLzR>1HQK#J&QgfHEOkLnOIw?mv7c8^{t!b_V#U>^zLCPdpfmTX}33& ztA5_pH?Cur2#SsR#@h^9|M#sLX2w{yYpuH*h`;{tvEJ(&71&}-!_P%k{BZW4Oca;tW|)@oJiyDrq;UdM>r>eA&av#%^)nO%Bq?b7*+2>4f*FRlg0ky}%ES()!qmJKpLP#@?AeP{$U0vZ90 zfJQ(gpb^jrXaqC@8Uc-fMnEI*6M#T^6O(Xq^#A6~%s(0UPahfqjetf#BcKt`2xtT} z0vZ90fJQ(gpb^jrd@>Pu+_aah(V8CPwHztov zd_3`c6U~Wm{C~zjFy0;i;`o_yYwW|Zua2#b?G64ucrSP(IO2cof5X4+XT9%xA9!E# z&U&fzU#7pBzMS6g{)79zTXB!3{w?+UsXM8x^L^(}oi97fPGJ9){cHA`z2ExC`fcmF zb=>@}`ArNWc{DzC*xYNH{?x(j9+Qr#se?~UP3@WTrhIjn#>}hG=%lQR&2vf%bhfg7 z6B?G*zkK;??b&MWMypo4QfmivTx?!!UM96i+C5k}@k(j%T}4%R&rG(6df{d%tLZ4f|X9~A*{JbP>TjH=S*O6io zWDVr!uCGn1{nD9s))TuRgrK7+Mw_WrdXrgp6P+rLaXQ%EA^WkOfS4M$B$$ z$fmI(EjSHj;P?S18^Noz#B?EyM+~<~Q%n{ahTSfQLBOs8-}ZdWHV?m21SQF~?6%Jq zgP{SF+ZUwolVc%$&+;F&Bw=!n8yfb_{c3iOz+dCqyIwJNzyz5XiPH6`zS6 z53DXuq(7!{R;JTf-n>kEK^lJN=P?E>?@HnH2xFQttu98r6uPc0L{7MA-%SH?=7yXr zJk7SlXw`Fk2DB1lQKVci<$@z2`L*Rt@ze;viTOScS^=#E>7;xIqbJfkV--u*h&-Ej|@{-DS&ODF{s?0soPsB6sS*tiC zR&o@Z#yp5c!Cko5P4BrDc?&`mW5-`WiIzPjEC_m zk$6JTNGu4)^BvBy1oUcQhP@7t!EV-(oU!IbKx9TR5>U9-T{Mp0T|YFQ{O``Ne~ zhzUw3W_*%MArN+a%NBB;nI%eveSLm+2+hQzSTc+Qq{A0nPysw9H$+sXPJQ+D_$nz|1f8cP^5@j2r0hnpqex}(s7R*PpI61g&}51 zEHFW4$*aYQ*mxJSm>g=AW>rlBFKwchz#3C1V$|dDsL43NtOPwC=aK|kT3{B^HHt9N zV=g}i+nw8;B(-{ZXNF1=#tCBK9W|KZzgh~CUil_VmC=OP$5crYG61uP8$4iKE{uG( zW;VtIVW<-mKE!1y53NS%`*0dW73V zG#ucfloM&9C2}Q2KPDU$UAFDxbeCDZ1%6c3q($ixE$I%87Z`VFsS$(bSW*RTKQn7V zq==P#i$?jbRB#f0lc_Sn=zWS<^SIe@QIl|k#*wsAv=X+v?l>4KWRAV6tPnMblysLx zq!cqb6UHOT3XPa@^fZ+fP=g3bW;Ei}hw{%3eG!d-MnEH=5zq)|1T+E~0gZr0KqH_L z&%Ae z=WXYVW7{9v@7R|z-^%=A=H*Og@^2=8bMkYOvlHK$`1-`HiPPi%IsQlEZ;n4ZZjSxM z*gInv$EJe63%(Xy4G#JL>VMt8<)8Nc+xrvm3*Pfk{7^r@rSu;6+wObr8}8xM$En}7 z_d@&szE!b~n*VP8fqBQw8s9TM__4yEoofUh1_IGitQ}%;*p5akp&dHN+97DeARdAe zN2zcjN;?zle_-0L9qMV84s=CnA;d$04e20>r69vwEj-W0gVN!i49_zjWj!m-w;_hN zpbldZt}TntB^*O|l#m_v9Pd}cBdEflJc0mFsc<1Y%0CO^e&JCR9 zFo*|9c?6kNiQRSut;3~okFaDhF9K35iio9Qwpb|^;}H+a%9Pp>QYj@_%t3%pLU=V> zlCu>GBa9OY+-W6eT@)UK@T9jUZI~1@#V9#uVAX9_p#F;3@dehiF32>5@Fcf}k{1_n zARNq(a7@N5X*G>kl%RE9xKP4FNDI+1BwM7i-h8m}i!vYy?*gWr=0yV1UXXB)T zPc0JZ|9IeNln9}qgNhznXA9DQ2=Pb(2=Q>?R-fk`N+*WBEIsNv$fE0j6zvh1MY6PN z;ph5nlhWZHUN3}cYB~tfbl?PND#XPP!2NN;DnV&`OI%(rAKg ztHqnKU!~DZQaWCzvtf#w4lrmZWnrNf05wUpuNH13jT6LVg1{U?yd~*yuR(~e1F%}m zlSmT+z~c3I(8}>JIqaFxCchFr2LXhKGK(%6lHsouYGh0R5B9T=u9O@6;&~s! zLqS~@#EWdSD4XZLA1EeV;*EjV!qF^zB7-m z13yI~d;~FFAS_&q=Q}JCmD>^vJagze@Eiyp5yd#rVo|6J_#F_37FpoADAHgwH2C=( z++u;}il1j&hPO|Xnh3;#5_m317nFurGRMS1S4qCyH|QNkCo(|9NqQbl2Px!eMJ%{| z;4^Zx;*}$-g`XWg-#GqroTI@i5E>~Ov;w;k{)Nx=J5CZhj^M>^jz-S` z_$)vxtPs{W_cO5N6iu}1=X30LfwiGoh}lV14m(=h(p7ef2;3oa%&FMvJZESbqUQi) zhMlrSAI!5?;vx5}dr2pDi*5sMlalB<0H2AJoU@?8FMKv(-X(Nbbqs6zV;Z4K({Sm5 z)2cvV*2NBv1z-Tl~QObhKdZzf(vEM=4*soAoI*+acP#Sp$ z<$)V53a{PIF~R{6{0x|dHU~5vfYP7`$^+@jtI-QSAZu=9ShXwNF58@+f{rucFo2+nS;Sn zu8)COSYSjES{8sbwGC2T2@4kz#=97;ZnR9}DKLvv+7dSa0iXu6=!a_Y6}IgQMILry zm5!?WN<9xquub?GZc|>(P zSfJZG z28kvsR!S>Lr|K3hW=HIFmU|r*sMP_bkd_Cuq#Kq}w9M8cf>15+<6H~psn>yOp=ue# z^1d+Ocz{?lTne+)>p-P2Ptye{ONDbI+!9heYSVa(OJPX84pa)cmjzk7R65JH!@~hs z(OttKRSG5bI#4O3W+l)H4R{aZA3gdS8Uc-fMnEH=5zq)|1T+E~0gZr0KqH_L&y7cUPl+7ggHr@NmWyz4_Fpb^jrXaqC@8Uc-fMnEH=5zq)|1T+E~fln|36Jxe< z!uf$QRtR2)e*d5S-|=tyC%o@^-}JV;DE+_bZ>7JIKA)a+|JHriece5f`p3+lXMPD? z0ppW@J^34xuTAcs_-Nw&iO)|Q9{%uM86@#a5jX}A|xfOkkw+xVB?{J#aM=S7>x>WEg^@q9NHX$rLd606~C19 z%)-H*bhxMf3q5)sy?`Fhre_^Po{Ar!{V)6i8~5~Z!_*meVJyFfC_ z_{sky|10J1k+IUT(x8XQI}e6ps9qO`%gH_FnSA?al=8&!lG-Yhff?X06 zweVx9a3}V=L#lgH4PE)U0;K-Yn7W*e`5%Q92I8m(k2Z(O|Ive_k~W z$x_rL(;>V{u;V;hS!k9@fFI|bQlo=M=szeeA9;woKaFh}xgVlJU6W!*J?kH#x{HVj$0Bq%1P}wBhps9Ss<6FU?5M~6-!}%H zkv7GEfa2Jv7(n|Q4%(BUqaOF)ORhW!;uF?24!_f(ML`e=*Yzj{r~?icgEl+r&=dcM zVMes*2H?{YL6C=*JxxJC4R8p2F`e&VQ!rJI6q9>-JrVh*!T?c_Zol4lzB4oB$oOFSjGj> zC4RFC9~US^9ojbF0Teqf5fONJe*ku4{M?Xc(@NiW+W@uFbE<6+kM9TkU`&o%(x_pn zqxKDmC%uChMjfy8G1WIn`$D9t-;(+}FmV{GodXb-qx1Ar+&D-N|97cI#d&%<>R|u6 aQQ0=We>loLJje&BG`7OvL@luu#{U88L3*+P literal 0 HcmV?d00001 From b13b42f5f0a01c727d136f171abd6f050b7362f2 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 3 Dec 2024 09:34:48 -0800 Subject: [PATCH 29/38] test: create and retrieve --- app/clients/mapper.py | 1 - app/clients/service/retrieve.py | 7 ++--- tests/test_create_client.py | 38 +++++++++++++++++++++++++ tests/test_delete_client.py | 19 +++++++++++-- tests/test_retrieve_client.py | 50 +++++++++++++++------------------ 5 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 tests/test_create_client.py diff --git a/app/clients/mapper.py b/app/clients/mapper.py index 81aa06be..ccaaf37e 100644 --- a/app/clients/mapper.py +++ b/app/clients/mapper.py @@ -82,4 +82,3 @@ def create_client_in_db(client_data: ClientUpdateModel) -> int: raise Exception(f"Database error occurred: {str(e)}") from e finally: conn.close() - diff --git a/app/clients/service/retrieve.py b/app/clients/service/retrieve.py index 18ed36ba..64aff73e 100644 --- a/app/clients/service/retrieve.py +++ b/app/clients/service/retrieve.py @@ -21,10 +21,7 @@ async def retrieve_client(client_id: str): # Return the client data as JSON return client_data except Exception as e: - raise HTTPException( - status_code=500, - detail="An unexpected error occurred while retrieving the client." - ) from e + raise e def map_client_data(client: tuple) -> dict: @@ -38,4 +35,4 @@ def map_client_data(client: tuple) -> dict: "retention_services", "specialized_services", "employment_related_financial_supports", "employer_financial_supports", "enhanced_referrals", "success_rate", "client_id" ] - return dict(zip(columns, client)) \ No newline at end of file + return dict(zip(columns, client)) diff --git a/tests/test_create_client.py b/tests/test_create_client.py new file mode 100644 index 00000000..e4aa0bca --- /dev/null +++ b/tests/test_create_client.py @@ -0,0 +1,38 @@ +"""Test functions for the create api""" +import pytest +from fastapi import HTTPException +from app.clients.service.create import create_client +from app.clients.service.update_model import ClientUpdateModel + +# Create Client Tests +@pytest.mark.asyncio +async def test_create_client_success(): + """Test successful client creation.""" + client_data = ClientUpdateModel( + age=30, + gender="Male", + work_experience=5, + canada_workex=3, + fluent_english="Yes", + level_of_schooling="Bachelor's", + currently_employed="Yes" + ) + + result = await create_client(client_data) + + # Assertions + assert result["success"] is True + assert "Client successfully created with ID" in result["message"] + assert "client_id" in result + +@pytest.mark.asyncio +async def test_create_client_missing_required_fields(): + """Test client creation with insufficient data.""" + # Create a client model with minimal or missing required fields + client_data = ClientUpdateModel() + + with pytest.raises(HTTPException) as excinfo: + await create_client(client_data) + + # Assertions + assert excinfo.value.status_code == 500 diff --git a/tests/test_delete_client.py b/tests/test_delete_client.py index 112fc9af..11b5ead8 100644 --- a/tests/test_delete_client.py +++ b/tests/test_delete_client.py @@ -2,6 +2,8 @@ import pytest from fastapi import HTTPException from app.clients.service.delete import delete_client +from app.clients.service.create import create_client +from app.clients.service.update_model import ClientUpdateModel @pytest.mark.asyncio async def test_delete_client_success(): @@ -9,9 +11,20 @@ async def test_delete_client_success(): Test successful deletion of a client. """ - # NOTE: this id should be changed each time running the test - # please ensure the id is exist in current db - client_id = "61" + client_data = ClientUpdateModel( + age=30, + gender="Male", + work_experience=5, + canada_workex=3, + fluent_english="Yes", + level_of_schooling="Bachelor's", + currently_employed="Yes" + ) + + result = await create_client(client_data) + client_id = str(result['client_id']) + + # Delete the client created before result = await delete_client(client_id) # Assertions diff --git a/tests/test_retrieve_client.py b/tests/test_retrieve_client.py index e616c545..558ae2e3 100644 --- a/tests/test_retrieve_client.py +++ b/tests/test_retrieve_client.py @@ -1,48 +1,42 @@ -"""Test functions for the retrieve API""" +"""Test functions for the retrieve api""" import pytest from fastapi import HTTPException -from app.clients.service.retrieve import retrieve_client - -# Mock database for testing -database = { - "123": {"name": "John Doe", "email": "john@example.com"}, - "456": {"name": "Jane Smith", "email": "jane@example.com"} -} +from app.clients.service.retrieve import retrieve_client, check_valid_input +# Retrieve Client Tests @pytest.mark.asyncio async def test_retrieve_client_success(): - """ - Test successful retrieval of a client. - """ - client_id = "123" # Ensure this ID exists in the mock database + """Test successful retrieval of a client.""" + client_id = "123" # Assuming this is a valid client ID in your test database + result = await retrieve_client(client_id) # Assertions - assert result["success"] is True - assert result["data"] == {"name": "John Doe", "email": "john@example.com"} + assert isinstance(result, dict) + assert "client_id" in result + assert result["client_id"] == int(client_id) @pytest.mark.asyncio async def test_retrieve_client_not_found(): - """ - Test retrieval when the client is not found in the database. - """ - client_id = "999" # Ensure this ID does not exist in the mock database + """Test retrieval of a non-existent client.""" + client_id = "999999" # An ID that should not exist in the database + with pytest.raises(HTTPException) as excinfo: await retrieve_client(client_id) # Assertions assert excinfo.value.status_code == 404 - assert excinfo.value.detail == f"Client with ID {client_id} not found" + assert f"Client with ID {client_id} not found" in excinfo.value.detail @pytest.mark.asyncio async def test_retrieve_client_invalid_id(): - """ - Test retrieval with an invalid client_id format. - """ - client_id = "" # Simulate an invalid ID - with pytest.raises(HTTPException) as excinfo: - await retrieve_client(client_id) + """Test retrieval with an invalid client ID format.""" + invalid_client_ids = ["abc", "12.3", "-45", ""] - # Assertions - assert excinfo.value.status_code == 400 - assert excinfo.value.detail == "Invalid client_id format." + for client_id in invalid_client_ids: + with pytest.raises(HTTPException) as excinfo: + check_valid_input(client_id) + + # Assertions + assert excinfo.value.status_code == 400 + assert excinfo.value.detail == "Invalid client_id format." From c7baac1c45195b676db5dab3f38e12387a94c678 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 3 Dec 2024 09:38:05 -0800 Subject: [PATCH 30/38] test: create and retrieve --- tests/test_delete_client.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_delete_client.py b/tests/test_delete_client.py index 11b5ead8..0712ca12 100644 --- a/tests/test_delete_client.py +++ b/tests/test_delete_client.py @@ -16,11 +16,7 @@ async def test_delete_client_success(): gender="Male", work_experience=5, canada_workex=3, - fluent_english="Yes", - level_of_schooling="Bachelor's", - currently_employed="Yes" ) - result = await create_client(client_data) client_id = str(result['client_id']) From 4d0af686b6213f0fe1dfe915c69bb4ba09040451 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 3 Dec 2024 09:39:53 -0800 Subject: [PATCH 31/38] test: create and retrieve --- tests/test_delete_client.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_delete_client.py b/tests/test_delete_client.py index 0712ca12..9bfdf0f5 100644 --- a/tests/test_delete_client.py +++ b/tests/test_delete_client.py @@ -12,10 +12,10 @@ async def test_delete_client_success(): """ client_data = ClientUpdateModel( - age=30, - gender="Male", - work_experience=5, - canada_workex=3, + age=23, + gender="Female", + work_experience=2, + canada_workex=5, ) result = await create_client(client_data) client_id = str(result['client_id']) From 5087ac41a438133d73a5e0797764cfbd5580daf0 Mon Sep 17 00:00:00 2001 From: ChenluRu <144400953+ChenluRu@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:19:30 -0800 Subject: [PATCH 32/38] Update README.md --- README.md | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 390b64e1..7f6b61fc 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,101 @@ This also has an API file to interact with the front end, and logic in order to # Documentation +## POST `/clients` + +### Description +Creates a new client in the database with the provided information. + +--- + +### Request Body + +The fields for the new client must be passed as a JSON object and are encapsulated in the `ClientUpdateModel` located in `update_model.py`. All fields are optional. + +| Field | Type | Example | Description | +|--------------------------------|---------|------------------------|-----------------------------------------| +| `age` | integer | `32` | Age of the client. | +| `gender` | string | `"Male"` | Gender of the client. | +| `work_experience` | integer | `7` | Years of total work experience. | +| `canada_workex` | integer | `3` | Years of work experience in Canada. | +| `dep_num` | integer | `1` | Number of dependents. | +| `canada_born` | string | `"No"` | Whether the client was born in Canada. | +| `citizen_status` | string | `"Citizen"` | Citizenship status of the client. | +| `level_of_schooling` | string | `"Master's degree"` | Highest level of education completed. | +| `fluent_english` | string | `"Yes"` | Whether the client is fluent in English. | +| `reading_english_scale` | integer | `10` | English reading skill level (1-10). | +| `speaking_english_scale` | integer | `10` | English speaking skill level (1-10). | +| `writing_english_scale` | integer | `10` | English writing skill level (1-10). | +| `numeracy_scale` | integer | `10` | Numeracy skill level (1-10). | +| `computer_scale` | integer | `10` | Computer skill level (1-10). | +| `transportation_bool` | string | `"No"` | Whether transportation support is needed. | +| `caregiver_bool` | string | `"No"` | Whether the client is a primary caregiver. | +| `housing` | string | `"Homeowner"` | Housing status of the client. | +| `income_source` | string | `"Employment"` | Source of income. | +| `felony_bool` | string | `"No"` | Whether the client has a felony record. | +| `attending_school` | string | `"No"` | Whether the client is currently a student. | +| `currently_employed` | string | `"Yes"` | Employment status of the client. | +| `substance_use` | string | `"No"` | Whether the client has a substance use disorder. | +| `time_unemployed` | integer | `0` | Time unemployed in years. | +| `need_mental_health_support_bool` | string | `"No"` | Whether the client needs mental health support. | + +--- + +### Request Example + +**URL:** + +POST /clients + +**Request Body:** + +```json +{ + "age": 30, + "gender": "Male", + "work_experience": 5, + "canada_workex": 3, + "level_of_schooling": "Bachelor's", + "fluent_english": "Yes", + "currently_employed": "Yes" +} +``` + +**Responses** +**201 Created** +The client was successfully created. + +**Response Example:** +```json +{ + "success": true, + "message": "Client successfully created with ID 101", + "client_id": "101" +} +``` + +**400 Bad Request** +The input data is invalid or improperly formatted. + +**Response Example:** +```json +{ + "detail": "Invalid input data." +} +``` + +**500 Internal Server Error** +An unexpected error occurred during client creation. + +**Response Example:** +```json +{ + "detail": "An error message describing the issue." +} +``` + +Ensure all required fields are included and properly formatted in the request body. Missing or invalid fields will result in a 400 Bad Request response. + ### **PUT /clients/{client_id}** #### **Description** @@ -23,7 +118,7 @@ Updates specific fields of an existing client record. ### **Request Body** -The fields to be updated must be passed as a JSON object. Unspecified fields will remain unchanged. +The fields to be updated must be passed as a JSON object and are encapsulated in the `ClientUpdateModel` located in `update_model.py`. Unspecified fields will remain unchanged. | Field | Type | Example | Description | |--------------------------------|---------|------------------------|-----------------------------------------| From cfc9c168d2265954a69a343c9312f1b902d0509e Mon Sep 17 00:00:00 2001 From: Kevin990001 <79775196+Kevin990001@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:50:24 -0800 Subject: [PATCH 33/38] update readme --- README.md | 52 +--------------------------------------------------- 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/README.md b/README.md index 7f6b61fc..8408c593 100644 --- a/README.md +++ b/README.md @@ -285,54 +285,4 @@ Retrieves information for a client with the specified `client_id` from the datab |------------|--------|---------------------------------------| | `client_id` | string | The unique ID of the client to retrieve. | ---- - -### **Request Example** - -**URL**: - -```http -GET /clients/123 - -Responses -200 OK -The client information was successfully retrieved. - -Response Example: -{ - "success": true, - "data": { - "name": "John Doe", - "email": "john@example.com" - } -} - -404 Not Found -No client with the specified client_id exists. - -Response Example: -{ - "detail": "Client with ID 123 not found." -} - -400 Bad Request -The client_id is invalid or improperly formatted. - -Response Example: -{ - "detail": "Invalid client_id format." -} - - -500 Internal Server Error -An unexpected error occurred on the server. - -Response Example: -{ - "detail": "An error message describing the issue." -} - -Notes -The client_id must be a valid string that matches the expected format in the database. -Ensure the client_id exists before making the request to avoid a 404 error. -Copy code +--- \ No newline at end of file From 72070a63258919d05263e177609d76a836510daa Mon Sep 17 00:00:00 2001 From: Kevin990001 <79775196+Kevin990001@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:53:57 -0800 Subject: [PATCH 34/38] update --- README.md | 60 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 8408c593..a751245a 100644 --- a/README.md +++ b/README.md @@ -230,21 +230,56 @@ Deletes a client with the specified `client_id` from the database. ``` DELETE /clients/1 ``` -## Responses -**200 OK** +``` + +**404 Not Found** -The client was successfully deleted. +No client with the specified `client_id` exists. **Response Example:** ```json { - "success": true, - "message": "Client 1 successfully deleted", - "client_id": "1" + "detail": "Client with ID 1 not found." } ``` +**500 Internal Server Error** + +An unexpected error occurred on the server. + +**Response Example:** +```json +{ + "detail": "An error message describing the issue." +} +``` + +### Notes + +This endpoint requires the `client_id` to be a valid **integer string**. + +## GET `/clients/{client_id}` + +### Description +Gets a client with the specified `client_id` from the database. + +### Path Parameters + +| Parameter | Type | Description | +|-------------|----------|----------------------------------------| +| `client_id` | `string` | The unique ID of the client to get. | + +### Request Example + +**URL:** + +``` +GET /clients/1 +``` + +``` + **404 Not Found** No client with the specified `client_id` exists. @@ -272,17 +307,4 @@ An unexpected error occurred on the server. This endpoint requires the `client_id` to be a valid **integer string**. -### **GET /clients/{client_id}** - -#### **Description** -Retrieves information for a client with the specified `client_id` from the database. - ---- - -### **Path Parameters** - -| Parameter | Type | Description | -|------------|--------|---------------------------------------| -| `client_id` | string | The unique ID of the client to retrieve. | ---- \ No newline at end of file From 22bbdfb1459d4c91889499bef3e179b02a171612 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 3 Dec 2024 16:04:38 -0800 Subject: [PATCH 35/38] feat: search --- app/clients/mapper.py | 51 +++++++++++++++++++++++++++++++++ app/clients/router.py | 7 ++++- app/clients/service/retrieve.py | 10 ++++++- 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/app/clients/mapper.py b/app/clients/mapper.py index ccaaf37e..ac6dcf1e 100644 --- a/app/clients/mapper.py +++ b/app/clients/mapper.py @@ -19,6 +19,57 @@ def get_client(client_id: str) -> Optional[tuple]: finally: conn.close() +def get_all_clients_with_info(criteria): + """ + Search for clients based on given criteria. + + :param criteria: A dictionary where keys are column names and values are the filter values. + :return: A list of matching clients as dictionaries. + """ + try: + conn = sqlite3.connect(DATABASE) + cursor = conn.cursor() + + if isinstance(criteria, ClientUpdateModel): + client_dict = criteria.model_dump(exclude_unset=True) + else: + validated_data = ClientUpdateModel(**criteria) + client_dict = validated_data.model_dump(exclude_unset=True) + + # Build the WHERE clause dynamically + where_clauses = [] + values = [] + for key, value in client_dict.items(): + if value is not None: + where_clauses.append(f"{key} = ?") + values.append(value) + + where_statement = " AND ".join(where_clauses) + if where_clauses: + query = f"SELECT * FROM CommonAssessmentTool_Table WHERE {where_statement}" + else: + query = "SELECT * FROM CommonAssessmentTool_Table" + + cursor.execute(query, tuple(values)) + rows = cursor.fetchall() + + # Map each row to a dictionary + columns = [ + "age", "gender", "work_experience", "canada_workex", "dep_num", "canada_born", + "citizen_status", "level_of_schooling", "fluent_english", "reading_english_scale", + "speaking_english_scale", "writing_english_scale", "numeracy_scale", "computer_scale", + "transportation_bool", "caregiver_bool", "housing", "income_source", "felony_bool", + "attending_school", "currently_employed", "substance_use", "time_unemployed", + "need_mental_health_support_bool", "employment_assistance", "life_stabilization", + "retention_services", "specialized_services", "employment_related_financial_supports", + "employer_financial_supports", "enhanced_referrals", "success_rate", "client_id" + ] + return [dict(zip(columns, row)) for row in rows] + except sqlite3.Error as e: + raise Exception(f"Database error occurred: {str(e)}") from e + finally: + conn.close() + def delete_client_from_db(client_id: str): """ Delete a client record from the database by client_id. diff --git a/app/clients/router.py b/app/clients/router.py index cbb24c77..477156dd 100644 --- a/app/clients/router.py +++ b/app/clients/router.py @@ -6,7 +6,7 @@ from app.clients.service.logic import interpret_and_calculate from app.clients.service.delete import delete_client from app.clients.schema import PredictionInput -from app.clients.service.retrieve import retrieve_client +from app.clients.service.retrieve import retrieve_client, search_clients from app.clients.service.update import update_client from app.clients.service.update_model import ClientUpdateModel @@ -43,3 +43,8 @@ async def query_client(client_id: str): async def create_client_endpoint(client_data: ClientUpdateModel = Body(...)): print("Create new client") return await create_client(client_data) + +@router.post("/search") +async def search_clients_endpoint(criteria: ClientUpdateModel = Body(...)): + print("Search clients with given info") + return await search_clients(criteria) diff --git a/app/clients/service/retrieve.py b/app/clients/service/retrieve.py index 64aff73e..94524e8b 100644 --- a/app/clients/service/retrieve.py +++ b/app/clients/service/retrieve.py @@ -1,10 +1,18 @@ from fastapi import HTTPException -from app.clients.mapper import get_client +from app.clients.mapper import get_client, get_all_clients_with_info def check_valid_input(client_id): if not client_id.isdigit(): raise HTTPException(status_code=400, detail="Invalid client_id format.") +async def search_clients(criteria): + try: + clients = get_all_clients_with_info(criteria) + if not clients: + raise HTTPException(status_code=404, detail="No clients found matching the criteria.") + return clients + except Exception as e: + raise e async def retrieve_client(client_id: str): try: From d4348ef4d81ac16448be1e546fe0980e4289a7e8 Mon Sep 17 00:00:00 2001 From: Guotong Liao <815190191@qq.com> Date: Tue, 3 Dec 2024 16:16:59 -0800 Subject: [PATCH 36/38] fix: tests --- tests/test_retrieve_client.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/test_retrieve_client.py b/tests/test_retrieve_client.py index 558ae2e3..71d8c3c7 100644 --- a/tests/test_retrieve_client.py +++ b/tests/test_retrieve_client.py @@ -1,13 +1,14 @@ """Test functions for the retrieve api""" import pytest from fastapi import HTTPException -from app.clients.service.retrieve import retrieve_client, check_valid_input +from app.clients.service.retrieve import retrieve_client, check_valid_input, search_clients +from app.clients.service.update_model import ClientUpdateModel # Retrieve Client Tests @pytest.mark.asyncio async def test_retrieve_client_success(): """Test successful retrieval of a client.""" - client_id = "123" # Assuming this is a valid client ID in your test database + client_id = "123" result = await retrieve_client(client_id) @@ -16,6 +17,21 @@ async def test_retrieve_client_success(): assert "client_id" in result assert result["client_id"] == int(client_id) +# Retrieve Client Tests +@pytest.mark.asyncio +async def test_search_client_success(): + """Test successful retrieval of a client.""" + client_data = ClientUpdateModel( + age=30, + housing="None" + ) + + with pytest.raises(HTTPException) as excinfo: + await search_clients(client_data) + + # Assertions + assert excinfo.value.status_code == 404 + @pytest.mark.asyncio async def test_retrieve_client_not_found(): """Test retrieval of a non-existent client.""" From 57d7d4bcf7e67ac7743cef6f31ffaf482aefcb25 Mon Sep 17 00:00:00 2001 From: ChenluRu <144400953+ChenluRu@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:07:57 -0800 Subject: [PATCH 37/38] Update README.md --- README.md | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/README.md b/README.md index a751245a..9fa155fd 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,14 @@ The fields for the new client must be passed as a JSON object and are encapsulat | `substance_use` | string | `"No"` | Whether the client has a substance use disorder. | | `time_unemployed` | integer | `0` | Time unemployed in years. | | `need_mental_health_support_bool` | string | `"No"` | Whether the client needs mental health support. | +| `employment_assistance` | integer | `1` | Employment assistance score (1-10). | +| `life_stabilization` | integer | `1` | Life stabilization score (1-10). | +| `retention_services` | integer | `1` | Retention services score (1-10). | +| `specialized_services` | integer | `1` | Specialized services score (1-10). | +| `employment_related_financial_supports` | integer | `1` | Employment-related financial support score (1-10). | +| `employer_financial_supports` | integer | `1` | Employer financial support score (1-10). | +| `enhanced_referrals` | integer | `1` | Enhanced referrals score (1-10). | +| `success_rate` | integer | `100` | Client's success rate (percentage). | --- @@ -68,6 +76,7 @@ POST /clients **Responses** **201 Created** + The client was successfully created. **Response Example:** @@ -80,6 +89,7 @@ The client was successfully created. ``` **400 Bad Request** + The input data is invalid or improperly formatted. **Response Example:** @@ -90,6 +100,7 @@ The input data is invalid or improperly formatted. ``` **500 Internal Server Error** + An unexpected error occurred during client creation. **Response Example:** @@ -146,6 +157,14 @@ The fields to be updated must be passed as a JSON object and are encapsulated in | `substance_use` | string | `"No"` | Whether the client has a substance use disorder. | | `time_unemployed` | integer | `0` | Time unemployed in years. | | `need_mental_health_support_bool` | string | `"No"` | Whether the client needs mental health support. | +| `employment_assistance` | integer | `1` | Employment assistance score (1-10). | +| `life_stabilization` | integer | `1` | Life stabilization score (1-10). | +| `retention_services` | integer | `1` | Retention services score (1-10). | +| `specialized_services` | integer | `1` | Specialized services score (1-10). | +| `employment_related_financial_supports` | integer | `1` | Employment-related financial support score (1-10). | +| `employer_financial_supports` | integer | `1` | Employer financial support score (1-10). | +| `enhanced_referrals` | integer | `1` | Enhanced referrals score (1-10). | +| `success_rate` | integer | `100` | Client's success rate (percentage). | --- @@ -307,4 +326,110 @@ An unexpected error occurred on the server. This endpoint requires the `client_id` to be a valid **integer string**. +## POST `/search` + +### Description +Searches for a list of clients based on the provided JSON criteria. Returns all clients that match the specified conditions. +The criteria are encapsulated in the `ClientUpdateModel` defined in `update_model.py`. +Any combination of fields can be provided in the JSON request body for filtering clients. Fields left empty will not be used as filters. + +### Request Body + +| Field | Type | Example | Description | +|--------------------------------|---------|------------------------|-----------------------------------------| +| `age` | integer | `32` | Age of the client. | +| `gender` | string | `"Male"` | Gender of the client. | +| `work_experience` | integer | `7` | Years of total work experience. | +| `canada_workex` | integer | `3` | Years of work experience in Canada. | +| `dep_num` | integer | `1` | Number of dependents. | +| `canada_born` | string | `"No"` | Whether the client was born in Canada. | +| `citizen_status` | string | `"Citizen"` | Citizenship status of the client. | +| `level_of_schooling` | string | `"Master's degree"` | Highest level of education completed. | +| `fluent_english` | string | `"Yes"` | Whether the client is fluent in English. | +| `reading_english_scale` | integer | `10` | English reading skill level (1-10). | +| `speaking_english_scale` | integer | `10` | English speaking skill level (1-10). | +| `writing_english_scale` | integer | `10` | English writing skill level (1-10). | +| `numeracy_scale` | integer | `10` | Numeracy skill level (1-10). | +| `computer_scale` | integer | `10` | Computer skill level (1-10). | +| `transportation_bool` | string | `"No"` | Whether transportation support is needed. | +| `caregiver_bool` | string | `"No"` | Whether the client is a primary caregiver. | +| `housing` | string | `"Homeowner"` | Housing status of the client. | +| `income_source` | string | `"Employment"` | Source of income. | +| `felony_bool` | string | `"No"` | Whether the client has a felony record. | +| `attending_school` | string | `"No"` | Whether the client is currently a student. | +| `currently_employed` | string | `"Yes"` | Employment status of the client. | +| `substance_use` | string | `"No"` | Whether the client has a substance use disorder. | +| `time_unemployed` | integer | `0` | Time unemployed in years. | +| `need_mental_health_support_bool` | string | `"No"` | Whether the client needs mental health support. | +| `employment_assistance` | integer | `1` | Employment assistance score (1-10). | +| `life_stabilization` | integer | `1` | Life stabilization score (1-10). | +| `retention_services` | integer | `1` | Retention services score (1-10). | +| `specialized_services` | integer | `1` | Specialized services score (1-10). | +| `employment_related_financial_supports` | integer | `1` | Employment-related financial support score (1-10). | +| `employer_financial_supports` | integer | `1` | Employer financial support score (1-10). | +| `enhanced_referrals` | integer | `1` | Enhanced referrals score (1-10). | +| `success_rate` | integer | `100` | Client's success rate (percentage). | + + + +### Request Example + +**URL**: + +POST /search + + +**Request Body**: +```json +{ + "age": 30, + "housing": "None" +} + +**Responses** +**200 OK** + +The list of clients matching the criteria was successfully retrieved. + +**Response Example:** + +```json +[ + { + "client_id": "1", + "age": 30, + "housing": "None", + "work_experience": 5, + "success_rate": 80 + }, + { + "client_id": "2", + "age": 30, + "housing": "None", + "work_experience": 6, + "success_rate": 85 + } +] + +``` +**404 Not Found** + +No clients matched the given criteria. + +**Response Example:** +```json +{ + "detail": "No clients found matching the criteria." +} +``` + +**500 Internal Server Error** +An unexpected error occurred on the server. + +**Response Example:** +```json +{ + "detail": "An error message describing the issue." +} +``` From 80ed7c6bd2feff83f2ec300359758df65f3b2aaf Mon Sep 17 00:00:00 2001 From: ChenluRu <144400953+ChenluRu@users.noreply.github.com> Date: Tue, 3 Dec 2024 20:11:10 -0800 Subject: [PATCH 38/38] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fa155fd..bb1665c1 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ POST /clients ``` **Responses** -**201 Created** +**200 Created** The client was successfully created.