Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e18fb12
CRUD Updates
Nov 5, 2024
c7e5938
CRUD Updates
ya603J Nov 6, 2024
2235975
Add SQL file to create table
jeshwang Nov 9, 2024
764ca05
Merge branch 'main' into ya_ji
ya603J Nov 12, 2024
51fe2d3
Modified update and delete methods to raise an error and display erro…
ya603J Nov 12, 2024
9c469d1
Database.py
BossJoeZz Nov 12, 2024
7473f0d
sql syntax loading data
BossJoeZz Nov 12, 2024
4973409
python script reading the data in the csv file
BossJoeZz Nov 12, 2024
d2d4ace
Configured test database and wrote test cases for CRUD and endpoints
ya603J Nov 12, 2024
1ccae37
Merge pull request #12 from frankluo123/ya_ji
frankluo123 Nov 12, 2024
335d41a
Merge pull request #13 from frankluo123/zheng
frankluo123 Nov 12, 2024
767bc4f
tool to add csv data to database
BossJoeZz Nov 16, 2024
b86533b
modified tests
ya603J Nov 18, 2024
b75cecd
Merge branch 'main' into ya_ji
ya603J Nov 18, 2024
9440782
fixed syntax errors in response info
ya603J Nov 19, 2024
ae93b4a
Fixed syntax errors in response messages - Merge PR#14 from frankluo1…
ya603J Nov 19, 2024
cc4fdae
Adding Continuous Improvement Pipeline
Nov 20, 2024
0a0e3fe
Update continuous_improvement.yml
frankluo123 Nov 20, 2024
00b2a1a
Update continuous_improvement.yml
frankluo123 Nov 20, 2024
973f8ba
Update continuous_improvement.yml
frankluo123 Nov 20, 2024
1715658
Fixing Python version and updating setup-python action
Nov 20, 2024
247c7ea
Debugging
Nov 20, 2024
b87b94f
New continuous integration file. Remove previous 'continuous improvem…
jeshwang Nov 25, 2024
54244b1
Changed versions for continuous integration
jeshwang Nov 25, 2024
4aec4be
Rearranged test order
jeshwang Nov 25, 2024
f5b3ffa
Revised spelling
jeshwang Nov 25, 2024
a88b8ce
Keep only local unit testings of CRUD functions
ya603J Nov 25, 2024
b76039e
Merge branch 'main' into ya_ji
ya603J Nov 25, 2024
bf00b10
Fixed lint issues for crud.py
jeshwang Nov 25, 2024
bad4be2
Fixed lint issues for models.py
jeshwang Nov 25, 2024
46b9cbd
Modified and added necessary __init__.py files
ya603J Nov 25, 2024
8f42334
Fixed lint issues for router.py
jeshwang Nov 25, 2024
bb84e11
Updates workflow to adjust to current program
ya603J Nov 25, 2024
1f2f047
Update continuous_integration.yml
ya603J Nov 25, 2024
0798535
Merge branch 'ya_ji' of https://github.com/frankluo123/CommonAssessme…
ya603J Nov 25, 2024
f0febc3
Merge pull request #15 from frankluo123/ya_ji
ya603J Nov 25, 2024
6d6e3fb
fix indentation errors
ya603J Nov 25, 2024
3a8fee5
Lint changes to schema and logic
jeshwang Nov 25, 2024
5e55caa
Merge branch 'main' of github.com:frankluo123/CommonAssessmentTool
jeshwang Nov 25, 2024
720f625
Combine jobs in continuous integration
jeshwang Nov 25, 2024
494180b
Minor edits in continuous integration
jeshwang Nov 25, 2024
d2f04c1
Large batch of lint changes
jeshwang Nov 25, 2024
c867dcc
Lint changes to database.py
jeshwang Nov 25, 2024
e6f39a7
Minor changes to make unit tests run
jeshwang Nov 25, 2024
cf94740
Minor edit to database.py
jeshwang Nov 25, 2024
53d533e
Minor edit to test_crud.py
jeshwang Nov 25, 2024
1b6ddda
Add Dockerfile
jeshwang Nov 27, 2024
abd9da5
Update fastapi.sql
BossJoeZz Dec 2, 2024
49f5fd4
Update and rename fastapi.sql to data_source.sql
BossJoeZz Dec 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/continuous_integration.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Continuous Integration Pipeline

# Triggering the workflow on push or pull request to the main branch
on:
pull_request:
branches:
- main
push:
branches:
- main

jobs:
setup-and-build:
name: Continuous Integration
runs-on: ubuntu-latest

services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: common_assess
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping --silent"
--health-interval=10s
--health-timeout=5s
--health-retries=3

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4.8.0
with:
python-version: 3.10.11

- name: Check versions
run: |
python --version
pip --version

- name: Upgrade pip
run: |
python -m pip install --upgrade pip

- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pymysql

- name: Linter Check
run: |
pip install pylint
pylint $(git ls-files '*.py')

- name: Configure Test Enviroment
run: |
pip install pytest
pytest

- name: Wait for MySQL to be ready
run: |
for i in {1..30}; do
mysqladmin ping -h 127.0.0.1 --silent && break
echo "Waiting for MySQL..." && sleep 2
done

- name: Apply Database Migrations
run: |
python app/database.py

- name: Run Application
run: |
nohup uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload &
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM python:3.11

# Set the working directory
WORKDIR /app

# Copy the requirements file
COPY app/requirements.txt .

# Install the dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy rest of the app code
COPY . /app

# Expose the port
EXPOSE 8000

# Command to run application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
3 changes: 3 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""
Package for client-related functionality (CRUD operations, schemas, etc.).
"""
34 changes: 34 additions & 0 deletions app/clients/clients_create_tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
CREATE SCHEMA IF NOT EXISTS Clients;

USE Clients;

DROP TABLE IF EXISTS Persons;

CREATE TABLE Persons (
person_id BIGINT AUTO_INCREMENT,
age INT,
gender VARCHAR(255),
work_experience INT,
canada_workex INT,
dep_num INT,
canada_born VARCHAR(255),
citizen_status VARCHAR(255),
level_of_schooling VARCHAR(255),
fluent_english VARCHAR(255),
reading_english_scale INT,
speaking_english_scale INT,
writing_english_scale INT,
numeracy_scale INT,
computer_scale INT,
transportation_bool VARCHAR(255),
caregiver_bool VARCHAR(255),
housing VARCHAR(255),
income_source VARCHAR(255),
felony_bool VARCHAR(255),
attending_school VARCHAR(255),
currently_employed VARCHAR(255),
substance_use VARCHAR(255),
time_unemployed INT,
need_mental_health_support_bool VARCHAR(255),
CONSTRAINT pk_Persons_PersonId PRIMARY KEY (person_id)
);
51 changes: 51 additions & 0 deletions app/clients/crud.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Module providing CRUD functions."""

from sqlalchemy.orm import Session
from .models import Client
from .schema import ClientCreate, ClientUpdate


def create_client(db: Session, client: ClientCreate):
'''Creates a new client record in the database'''
db_client = Client(**client.dict())
db.add(db_client)
db.commit()
db.refresh(db_client)
return db_client


def get_client(db: Session, client_id: int):
'''Retrieves a client record by client_id'''
return db.query(Client).filter(Client.id == client_id).first()


def get_clients(db: Session, skip: int = 0, limit: int = 10):
'''Retrieves a list of client records with custom formatting.

Args:
skip (int): Number of client records to skip from start of list
limit (int): Maximum number of client records to return
'''
return db.query(Client).offset(skip).limit(limit).all()


def update_client(db: Session, client_id: int, client: ClientUpdate):
'''Updates an existing client record with only the fields provided in client'''
db_client = db.query(Client).filter(Client.id == client_id).first()
if not db_client:
# Raise an error if the client is not found
raise ValueError(f"Client with ID {client_id} not found.")
# Update the client with the provided data
for key, value in client.dict(exclude_unset=True).items():
setattr(db_client, key, value)
db.commit()
db.refresh(db_client)
return db_client

def delete_client(db: Session, client_id: int):
'''Deletes a client record by client_id'''
db_client = db.query(Client).filter(Client.id == client_id).first()
if db_client:
db.delete(db_client)
db.commit()
return db_client
62 changes: 62 additions & 0 deletions app/clients/data_source.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for clients
-- ----------------------------
DROP TABLE IF EXISTS `clients`;
CREATE TABLE `clients` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`age` int NULL DEFAULT NULL,
`gender` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`work_experience` int NULL DEFAULT NULL,
`canada_workex` int NULL DEFAULT NULL,
`dep_num` int NULL DEFAULT NULL,
`canada_born` tinyint(1) NULL DEFAULT NULL,
`citizen_status` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`level_of_schooling` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`fluent_english` tinyint(1) NULL DEFAULT NULL,
`reading_english_scale` int NULL DEFAULT NULL,
`speaking_english_scale` int NULL DEFAULT NULL,
`writing_english_scale` int NULL DEFAULT NULL,
`numeracy_scale` int NULL DEFAULT NULL,
`computer_scale` int NULL DEFAULT NULL,
`transportation_bool` tinyint(1) NULL DEFAULT NULL,
`caregiver_bool` tinyint(1) NULL DEFAULT NULL,
`housing` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`income_source` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`felony_bool` tinyint(1) NULL DEFAULT NULL,
`attending_school` tinyint(1) NULL DEFAULT NULL,
`currently_employed` tinyint(1) NULL DEFAULT NULL,
`substance_use` tinyint(1) NULL DEFAULT NULL,
`time_unemployed` int NULL DEFAULT NULL,
`need_mental_health_support_bool` tinyint(1) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `ix_clients_name`(`name`) USING BTREE,
INDEX `ix_clients_id`(`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of clients
-- ----------------------------
INSERT INTO `clients` VALUES (1, 'Joe', 25, '1', 6, 2, '1', '1', '1', '5', '1', 7, 6, 5, 4, 6, '1', '1', '3', '4', '1', '1', '1', '1', 12, '1');
INSERT INTO `clients` VALUES (2, 'Mike', 32, '2', 10, 5, '2', '0', '0', '8', '0', 8, 9, 7, 6, 7, '0', '0', '2', '2', '0', '0', '0', '0', 8, '0');
INSERT INTO `clients` VALUES (3, 'Leo', 24, '1', 2, 1, '3', '1', '1', '3', '1', 6, 4, 6, 5, 4, '1', '1', '5', '3', '1', '1', '1', '1', 10, '1');
INSERT INTO `clients` VALUES (4, 'Alice', 29, '2', 7, 3, '0', '0', '0', '4', '0', 7, 8, 5, 7, 9, '0', '0', '6', '5', '0', '0', '0', '0', 6, '0');
INSERT INTO `clients` VALUES (5, 'Clara', 31, '1', 5, 4, '4', '1', '1', '7', '1', 6, 5, 4, 8, 6, '1', '1', '4', '7', '1', '1', '1', '1', 15, '1');
INSERT INTO `clients` VALUES (6, 'Ella', 22, '2', 1, 0, '1', '0', '0', '2', '0', 7, 8, 6, 7, 5, '0', '0', '2', '1', '0', '0', '0', '0', 5, '0');
INSERT INTO `clients` VALUES (7, 'Bella', 34, '1', 12, 6, '3', '1', '1', '10', '1', 9, 8, 10, 9, 9, '1', '1', '7', '6', '1', '1', '1', '1', 0, '1');
INSERT INTO `clients` VALUES (8, 'Diana', 26, '2', 4, 2, '2', '1', '1', '3', '1', 5, 7, 6, 5, 8, '1', '1', '5', '3', '1', '1', '1', '1', 9, '1');
INSERT INTO `clients` VALUES (9, 'Fiona', 30, '1', 8, 3, '0', '0', '0', '6', '0', 10, 9, 8, 6, 7, '0', '0', '3', '2', '0', '0', '0', '0', 3, '0');
INSERT INTO `clients` VALUES (10, 'Grace', 27, '2', 6, 5, '1', '1', '1', '5', '1', 8, 6, 7, 8, 6, '1', '1', '1', '4', '1', '1', '1', '1', 4, '1');
INSERT INTO `clients` VALUES (11, 'India', 25, '1', 3, 1, '2', '0', '0', '4', '0', 6, 5, 4, 6, 5, '0', '0', '3', '3', '0', '0', '0', '0', '0', 7, '0');
INSERT INTO `clients` VALUES (12, 'Karen', 33, '2', 10, 7, '0', '1', '1', '9', '1', 9, 10, 8, 9, 10, '1', '1', '8', '7', '1', '1', '1', '1', 2, '1');
INSERT INTO `clients` VALUES (13, 'Hannah', 20, '1', 1, 0, '1', '0', '0', '1', '0', 5, 3, 5, 4, 3, '0', '0', '2', '1', '0', '0', '0', '0', 14, '0');
INSERT INTO `clients` VALUES (14, 'Julia', 36, '2', 11, 6, '2', '1', '1', '8', '1', 10, 9, 10, 8, 9, '1', '1', '5', '6', '1', '1', '1', '1', 1, '1');
INSERT INTO `clients` VALUES (15, 'Luna', 29, '1', 5, 2, '4', '1', '1', '5', '1', 8, 7, 6, 6, 8, '1', '1', '7', '5', '1', '1', '1', '1', 11, '1');
INSERT INTO `clients` VALUES (16, 'Mia', 23, '2', 2, 1, '1', '0', '0', '3', '0', 7, 6, 7, 5, 6, '0', '0', '4', '2', '0', '0', '0', '0', '0', 10, '0');
INSERT INTO `clients` VALUES (17, 'Olivia', 35, '1', 8, 4, '3', '1', '1', '7', '1', 9, 10, 9, 8, 9, '1', '1', '3', '6', '1', '1', '1', '1', 6, '1');
INSERT INTO `clients` VALUES (18, 'Qiana', 28, '2', 6, 2, '0', '0', '0', '6', '0', 6, 8, 5, 7, 7, '0', '0', '1', '4', '0', '0', '0', '0', 12, '0');
INSERT INTO `clients` VALUES (19, 'Nora', 21, '1', 2, 0, '2', '1', '1', '2', '1', 5, 4, 6, 3, 5, '1', '1', '2', '2', '1', '1', '1', '1', 13, '1');
INSERT INTO `clients` VALUES (20, 'Poppy', 24, '2', 3, 1, '0', '0', '0', '3', '0', 6, 5, 7, 6, 6, '0', '0', '3', '3', '0', '0', '0', '0', 16, '0');
43 changes: 43 additions & 0 deletions app/clients/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'''Module providing functions for building the client model'''

from sqlalchemy import Column, Integer, String, Boolean
from app.database import Base # Need to create database configuration module in app.database


# pylint: disable=too-few-public-methods
class Client(Base):
'''Class defining the client model'''
# Setting the table name for this model to 'clients'
__tablename__ = 'clients'

# Primary Key column, unique identifier for each client record
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
age = Column(Integer)
gender = Column(String)
work_experience = Column(Integer)
canada_workex = Column(Integer)
dep_num = Column(Integer)
canada_born = Column(Boolean)
citizen_status = Column(String)
level_of_schooling = Column(String)
fluent_english = Column(Boolean)
reading_english_scale = Column(Integer)
speaking_english_scale = Column(Integer)
writing_english_scale = Column(Integer)
numeracy_scale = Column(Integer)
computer_scale = Column(Integer)
transportation_bool = Column(Boolean)
caregiver_bool = Column(Boolean)
housing = Column(String)
income_source = Column(String)
felony_bool = Column(Boolean)
attending_school = Column(Boolean)
currently_employed = Column(Boolean)
substance_use = Column(Boolean)
time_unemployed = Column(Integer)
need_mental_health_support_bool = Column(Boolean)

def __repr__(self):
'''Provide string representation of the Client instance'''
return f"<Client(name={self.name}, age={self.age}, gender={self.gender})>"
52 changes: 49 additions & 3 deletions app/clients/router.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,61 @@
from fastapi import APIRouter
from fastapi.responses import HTMLResponse
'''Model containing functions for router'''

from typing import List
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.clients.service.logic import interpret_and_calculate
from app.clients.schema import PredictionInput
from app.clients.schema import PredictionInput, ClientCreate, ClientUpdate, Client
from app.clients.crud import create_client, get_client, get_clients, update_client, delete_client
from app.database import get_db # Need to create a get_db function to provide DB sessions


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

@router.post("/predictions")
async def predict(data: PredictionInput):
'''Prediction endpoint'''
print("HERE")
print(data.model_dump())
return interpret_and_calculate(data.model_dump())


@router.post("/", response_model=Client)
async def create_new_client(client: ClientCreate, db: Session = Depends(get_db)):
'''Create a new client'''
return create_client(db=db, client=client)


@router.get("/{client_id}", response_model=Client)
async def read_client(client_id: int, db: Session = Depends(get_db)):
'''Get a client by client_id'''
client = get_client(db=db, client_id=client_id)
if client is None:
raise HTTPException(status_code=404, detail="Client not found")
return client


@router.get("/", response_model=List[Client])
async def read_clients(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
'''Retrieve clients with options to skip a number of results and limit max results shown'''
return get_clients(db=db, skip=skip, limit=limit)


@router.put("/{client_id}", response_model=Client)
async def update_existing_client(
client_id: int,
client: ClientUpdate,
db: Session = Depends(get_db)):
'''Update a client's information by client_id'''
db_client = update_client(db=db, client_id=client_id, client=client)
if db_client is None:
raise HTTPException(status_code=404, detail="Client not found")
return {f"Client with ID {client_id} is updated successfully!"}


@router.delete("/{client_id}", response_model=Client)
async def delete_existing_client(client_id: int, db: Session = Depends(get_db)):
'''Delete a client by client_id'''
db_client = delete_client(db=db, client_id=client_id)
if db_client is None:
raise HTTPException(status_code=404, detail="Client not found")
return {f"Client with ID {client_id} is deleted successfully!"}
Loading