Skip to content

Commit

Permalink
Merge pull request #20 from yihong1120/feature/flask-to-fastapi-migra…
Browse files Browse the repository at this point in the history
…tion

Feature/flask to fastapi migration
  • Loading branch information
yihong1120 authored Nov 15, 2024
2 parents 2a3e9b0 + b36afa1 commit 5ed7d3a
Show file tree
Hide file tree
Showing 38 changed files with 2,708 additions and 1,933 deletions.
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DATABASE_URL='mysql://user:passcode@localhost/construction_hazard_detection'
DATABASE_URL='mysql+asyncmy://user:passcode@localhost/construction_hazard_detection'
API_USERNAME='user'
API_PASSWORD='passcode'
API_URL = "http://localhost:5000"
Expand Down
9 changes: 6 additions & 3 deletions README-zh-tw.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
<a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.HDBSCAN.html">
<img src="https://img.shields.io/badge/HDBSCAN-sklearn-orange?logo=scikit-learn" alt="HDBSCAN sklearn">
</a>
<a href="https://flask.palletsprojects.com/en/3.0.x/">
<img src="https://img.shields.io/badge/flask-3.0.3-blue?logo=flask" alt="Flask 3.0.3">
<a href="https://fastapi.tiangolo.com/">
<img src="https://img.shields.io/badge/fastapi-0.115.4-blue?logo=fastapi" alt="FastAPI 0.115.4">
</a>
<a href="https://github.com/pre-commit/pre-commit">
<img src="https://img.shields.io/badge/pre--commit-4.0.1-blue?logo=pre-commit" alt="Pre-commit 4.0.1">
Expand All @@ -35,6 +35,9 @@
<a href="https://codecov.io/github/yihong1120/Construction-Hazard-Detection" >
<img src="https://codecov.io/github/yihong1120/Construction-Hazard-Detection/graph/badge.svg?token=E0M66BUS8D"/>
</a>
<a href="https://universe.roboflow.com/object-detection-qn97p/construction-hazard-detection">
<img src="https://app.roboflow.com/images/download-dataset-badge.svg" alt="Download Dataset from Roboflow">
</a>
</div>

<br>
Expand Down Expand Up @@ -299,7 +302,7 @@
| ------- | --------------------- | ------------------ | ------------------ | ----------------- | ----------------- |
| YOLO11n | 640 | 54.1 | 31.0 | 2.6 | 6.5 |
| YOLO11s | 640 | 70.1 | 44.8 | 9.4 | 21.6 |
| YOLO11m | 640 | // | // | 20.1 | 68.0 |
| YOLO11m | 640 | 73.3 | 42.6 | 20.1 | 68.0 |
| YOLO11l | 640 | // | // | 25.3 | 86.9 |
| YOLO11x | 640 | 76.8 | 52.5 | 56.9 | 194.9 |

Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
<a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.HDBSCAN.html">
<img src="https://img.shields.io/badge/HDBSCAN-sklearn-orange?logo=scikit-learn" alt="HDBSCAN sklearn">
</a>
<a href="https://flask.palletsprojects.com/en/3.0.x/">
<img src="https://img.shields.io/badge/flask-3.0.3-blue?logo=flask" alt="Flask 3.0.3">
<a href="https://fastapi.tiangolo.com/">
<img src="https://img.shields.io/badge/fastapi-0.115.4-blue?logo=fastapi" alt="FastAPI 0.115.4">
</a>
<a href="https://github.com/pre-commit/pre-commit">
<img src="https://img.shields.io/badge/pre--commit-4.0.1-blue?logo=pre-commit" alt="Pre-commit 4.0.1">
Expand All @@ -38,6 +38,9 @@
<a href="https://codebeat.co/projects/github-com-yihong1120-construction-hazard-detection-main">
<img alt="codebeat badge" src="https://codebeat.co/badges/383396a9-e2cb-4604-8990-c1707e5870cf" />
</a>
<a href="https://universe.roboflow.com/object-detection-qn97p/construction-hazard-detection">
<img src="https://app.roboflow.com/images/download-dataset-badge.svg" alt="Download Dataset from Roboflow">
</a>
</div>

<br>
Expand Down Expand Up @@ -249,7 +252,7 @@ Now, you could launch the hazard-detection system in Docker or Python env:

6. Run object detection API:
```bash
gunicorn -w 1 -b 0.0.0.0:8001 "examples.YOLO_server_api.app:app"
uvicorn examples.YOLO_server.app:sio_app --host 0.0.0.0 --port 8001
```

7. Run the main application with a specific configuration file:
Expand All @@ -262,7 +265,7 @@ Now, you could launch the hazard-detection system in Docker or Python env:

For linux users:
```bash
gunicorn -w 1 -k eventlet -b 127.0.0.1:8002 "examples.streaming_web.app:streaming-web-app"
uvicorn examples.streaming_web.app:sio_app --host 0.0.0.0 --port 8002
```

For windows users:
Expand Down Expand Up @@ -307,7 +310,7 @@ The primary dataset for training this model is the [Construction Site Safety Ima
| ------- | --------------------- | ------------------ | ------------------ | ----------------- | ----------------- |
| YOLO11n | 640 | 54.1 | 31.0 | 2.6 | 6.5 |
| YOLO11s | 640 | 70.1 | 44.8 | 9.4 | 21.6 |
| YOLO11m | 640 | // | // | 20.1 | 68.0 |
| YOLO11m | 640 | 73.3 | 42.6 | 20.1 | 68.0 |
| YOLO11l | 640 | // | // | 25.3 | 86.9 |
| YOLO11x | 640 | 76.8 | 52.5 | 56.9 | 194.9 |

Expand Down
34 changes: 17 additions & 17 deletions config/nginx_config_example.conf
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ server {
location /upload/ {
# Define the local directory containing uploaded static files
alias /home/youruser/Documents/Construction-Hazard-Detection/static/uploads/;

# Enable directory indexing to allow viewing of all files within the directory
autoindex on;

# Ensure unrestricted access to this location for all users
allow all;
}
Expand All @@ -32,26 +32,26 @@ server {
location /ws/ {
# Proxy requests to the FastAPI application running on localhost, port 8000
proxy_pass http://127.0.0.1:8000;

# Specify HTTP version 1.1 to ensure compatibility with WebSocket protocol
proxy_http_version 1.1;

# Set headers for WebSocket upgrade to switch protocols as needed
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

# Pass the original host header to the proxied server
proxy_set_header Host $host;

# Capture the client’s real IP address and forward it to the proxied server
proxy_set_header X-Real-IP $remote_addr;

# Forward additional client information, including the originating IP addresses
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Indicate the original protocol (HTTP or HTTPS) used by the client
proxy_set_header X-Forwarded-Proto $scheme;

# Disable buffering to immediately forward data to the client, improving real-time updates
proxy_buffering off;
}
Expand All @@ -60,28 +60,28 @@ server {
location / {
# Proxy all other HTTP requests to the FastAPI application on localhost, port 8000
proxy_pass http://127.0.0.1:8000;

# Use HTTP version 1.1 for improved connection handling
proxy_http_version 1.1;

# Forward the original host information to maintain request integrity
proxy_set_header Host $host;

# Pass on the real IP address of the client
proxy_set_header X-Real-IP $remote_addr;

# Add the client's forwarded IP addresses to the header
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

# Include information about the original protocol (HTTP or HTTPS)
proxy_set_header X-Forwarded-Proto $scheme;

# Specify SSL usage for the forwarded request
proxy_set_header X-Forwarded-SSL on;

# Include the original port number in the forwarded headers
proxy_set_header X-Forwarded-Port $server_port;

# Disable buffering to ensure immediate data forwarding
proxy_buffering off;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/YOLO_server_api/README-zh-tw.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
或者

```sh
gunicorn -w 1 -b 0.0.0.0:8000 "examples.object_detection__server_api.app:app"
uvicorn examples.YOLO_server.app:sio_app --host 127.0.0.1 --port 8000
```

2. **向 API 發送請求:**
Expand Down
2 changes: 1 addition & 1 deletion examples/YOLO_server_api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This section an example implementation of a YOLO Server API, designed to facilit
or

```sh
gunicorn -w 1 -b 0.0.0.0:8000 "examples.object_detection_server_api.app:app"
uvicorn examples.YOLO_server.app:sio_app --host 127.0.0.1 --port 8000
```

4. **Send a request to the API:**
Expand Down
Empty file.
137 changes: 93 additions & 44 deletions examples/YOLO_server_api/app.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,111 @@
from __future__ import annotations

import atexit
import secrets
import os
from contextlib import asynccontextmanager
from typing import AsyncGenerator

import pymysql
import redis.asyncio as redis
import socketio
from apscheduler.schedulers.background import BackgroundScheduler
from flask import Flask
from flask_jwt_extended import JWTManager

from .auth import auth_blueprint
from .config import Config
from .detection import detection_blueprint
from .model_downloader import models_blueprint
from .models import db
from fastapi import FastAPI
from fastapi_jwt import JwtAccessBearer
from fastapi_limiter import FastAPILimiter

from .auth import auth_router
from .config import Settings
from .detection import detection_router
from .model_downloader import models_router
from .models import Base
from .models import engine
from .security import update_secret_key

# Use pymysql as MySQLdb for compatibility with SQLAlchemy
pymysql.install_as_MySQLdb()
# Instantiate the FastAPI app
app = FastAPI()

app = Flask(__name__) # Initialise the Flask application
# Register API routers for different functionalities
app.include_router(auth_router)
app.include_router(detection_router)
app.include_router(models_router)

# Securely generate a random JWT secret key using secrets library
app.config['JWT_SECRET_KEY'] = secrets.token_urlsafe(16)
# Set up JWT authentication
jwt_access = JwtAccessBearer(secret_key=Settings().authjwt_secret_key)

# Load configurations from Config class
app.config.from_object(Config)

# Initialise JWTManager with the Flask app
jwt = JWTManager(app)

# Initialise the database with the Flask app
db.init_app(app)

# Register authentication-related routes
app.register_blueprint(auth_blueprint)

# Register object detection-related routes
app.register_blueprint(detection_blueprint)

# Register object models-related routes
app.register_blueprint(models_blueprint)

# Set up a background scheduler
# Configure background scheduler to refresh secret keys every 30 days
scheduler = BackgroundScheduler()

# Schedule a job to update the JWT secret key every 30 days
scheduler.add_job(
func=lambda: update_secret_key(app),
trigger='interval',
days=30,
)

# Start the scheduler
scheduler.start()

# Ensure the scheduler is shut down gracefully upon exiting the application
atexit.register(lambda: scheduler.shutdown())

# Initialise Socket.IO server for real-time events
sio = socketio.AsyncServer(async_mode='asgi')
sio_app = socketio.ASGIApp(sio, app)

# Define Socket.IO events
@sio.event
async def connect(sid: str, environ: dict) -> None:
"""
Handles client connection event to the Socket.IO server.
Args:
sid (str): The session ID of the connected client.
environ (dict): The environment dictionary for the connection.
"""
print('Client connected:', sid)


@sio.event
async def disconnect(sid: str) -> None:
"""
Handles client disconnection from the Socket.IO server.
Args:
sid (str): The session ID of the disconnected client.
"""
print('Client disconnected:', sid)

# Define lifespan event to manage the application startup and shutdown
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
"""
Context manager to handle application startup and shutdown tasks.
Args:
app (FastAPI): The FastAPI application instance.
"""
# Initialise Redis connection pool for rate limiting
redis_host = os.getenv('REDIS_HOST', '127.0.0.1')
redis_port = os.getenv('REDIS_PORT', '6379')
redis_password = os.getenv('REDIS_PASSWORD', '')

redis_url = f"redis://:{redis_password}@{redis_host}:{redis_port}/0"

# Initialise Redis connection pool for rate limiting
app.state.redis_pool = await redis.from_url(
redis_url,
encoding='utf-8',
decode_responses=True,
)
await FastAPILimiter.init(app.state.redis_pool)

# Create database tables
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)

# Yield control to allow application operation
yield

# Shutdown the scheduler and Redis connection pool
# upon application termination
scheduler.shutdown()
await app.state.redis_pool.close()

# Assign lifespan context to the FastAPI app
app.router.lifespan_context = lifespan

# Main entry point for running the app
if __name__ == '__main__':
# Run the Flask application on all available IPs at port 5000
app.run(Thread=True, host='0.0.0.0', port=5000)
import uvicorn
uvicorn.run(sio_app, host='0.0.0.0', port=5000)
Loading

0 comments on commit 5ed7d3a

Please sign in to comment.