From a0531431435c54b85ec6a672ac654c65836ed0d3 Mon Sep 17 00:00:00 2001 From: Suriya Date: Tue, 30 Apr 2024 20:57:57 +0530 Subject: [PATCH] project structure modified --- .gitignore | 4 +- Dockerfile | 5 +- app/main.py | 7 +-- app/router/qr_code_generator.py | 85 -------------------------------- app/router/qr_code_router.py | 50 +++++++++++++++++++ app/schema/schemas.py | 5 +- app/service/qr_code_service.py | 87 +++++++++++++++++++++++++++++++++ conf/gunicorn.conf.py | 6 +++ docker-compose.yml | 2 +- requirements.txt | 4 +- 10 files changed, 159 insertions(+), 96 deletions(-) delete mode 100644 app/router/qr_code_generator.py create mode 100644 app/router/qr_code_router.py create mode 100644 app/service/qr_code_service.py create mode 100644 conf/gunicorn.conf.py diff --git a/.gitignore b/.gitignore index fac3d6d..e44a217 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /env /app/__pycache__ /app/router/__pycache__ -/app/schema/__pycache__ \ No newline at end of file +/app/schema/__pycache__ +/app/service/__pycache__ +/conf/__pycache__ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a41741e..44829b7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ WORKDIR /app # Copy current directory contents into the container at /app COPY ./app /app +COPY ./conf/gunicorn.conf.py /etc/gunicorn/gunicorn.conf.py COPY ./requirements.txt /app # Install dependencies @@ -21,5 +22,5 @@ RUN pip install --upgrade pip && \ # Expose ports and define startup commands based on selected framework EXPOSE 8000 -# Specify the entry point (Replace "mail" with your actual file name) -CMD ["uvicorn", "main:app", "--host=0.0.0.0", "--reload"] \ No newline at end of file +# Specify the entry point +CMD ["gunicorn", "main:app", "-c", "/etc/gunicorn/gunicorn.conf.py"] \ No newline at end of file diff --git a/app/main.py b/app/main.py index d6afc0e..58add8d 100644 --- a/app/main.py +++ b/app/main.py @@ -1,13 +1,14 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from router import qr_code_generator from fastapi.responses import UJSONResponse +from router import qr_code_router + app = FastAPI(default_response_class=UJSONResponse) origins = [ - '*', + "*", ] app.add_middleware( @@ -19,6 +20,6 @@ ) app.include_router( - qr_code_generator.router, + qr_code_router.router, prefix="/api/v1" ) \ No newline at end of file diff --git a/app/router/qr_code_generator.py b/app/router/qr_code_generator.py deleted file mode 100644 index 30c2b8c..0000000 --- a/app/router/qr_code_generator.py +++ /dev/null @@ -1,85 +0,0 @@ -from fastapi import APIRouter, Request -import io -from fastapi.responses import StreamingResponse -import segno -from segno import helpers - -from schema.schemas import UrlData, WifiData, ContactData, GeoLocationData, EmailData - - -router = APIRouter() - -# URL to QR code generator function -@router.post("/url_to_qr") -async def url_to_qr_generator(request: Request, info: UrlData) -> StreamingResponse: - - data: str = info.url - qr_data: bytes = segno.make_qr(data, error = 'H') - qr_buffer = io.BytesIO() - qr_data.save(qr_buffer, kind='png', scale=10) - qr_buffer.seek(0) - return StreamingResponse(content=qr_buffer, media_type="image/png") - # return templates.TemplateResponse("home.html", {"request": request, "qr_data": qr_data}) - - -# Wifi Data to QR code generator function -@router.post("/wifi_to_qr") -async def wifi_to_qr_generator(request: Request, info: WifiData) -> StreamingResponse: - - name: str = info.wifi_name - password: str = info.wifi_password - data: bytes = helpers.make_wifi_data(ssid=name, password=password, security='WPA') - qr_data: bytes = segno.make(data, error = 'H') - qr_buffer = io.BytesIO() - qr_data.save(qr_buffer, kind='png', scale=10) - qr_buffer.seek(0) - return StreamingResponse(content=qr_buffer, media_type="image/png") - - -# Contact details to QR code generator function -@router.post("/contact_to_qr") -async def contact_to_qr_generator(request: Request, info: ContactData) -> StreamingResponse: - - data: bytes = helpers.make_vcard_data( - displayname = info.name, - name = info.name, - email = info.email, - phone = info.phone, - city = info.city, - org = info.org, - title = info.title, - url = info.url - ) - qr_data: bytes = segno.make(data, error = 'H') - qr_buffer = io.BytesIO() - qr_data.save(qr_buffer, kind='png', scale=10) - qr_buffer.seek(0) - return StreamingResponse(content=qr_buffer, media_type="image/png") - - -# Geographic location to QR code generator function -@router.post("/geo_to_qr") -async def geo_to_qr_generator(request: Request, info: GeoLocationData) -> StreamingResponse: - - data: bytes = helpers.make_geo_data(info.latitude, info.longitude) - qr_data: bytes = segno.make(data, error = 'H') - qr_buffer = io.BytesIO() - qr_data.save(qr_buffer, kind='png', scale=10) - qr_buffer.seek(0) - return StreamingResponse(content=qr_buffer, media_type="image/png") - - -# Email to QR QR code generator function -@router.post("/email_to_qr") -async def email_to_qr_generator(request: Request, info: EmailData) -> StreamingResponse: - - qr_data: bytes = helpers.make_email( - to = info.to, - subject = info.subject, - body = info.body, - cc = info.cc - ) - qr_buffer = io.BytesIO() - qr_data.save(qr_buffer, kind='png', scale=10) - qr_buffer.seek(0) - return StreamingResponse(content=qr_buffer, media_type="image/png") \ No newline at end of file diff --git a/app/router/qr_code_router.py b/app/router/qr_code_router.py new file mode 100644 index 0000000..20215bb --- /dev/null +++ b/app/router/qr_code_router.py @@ -0,0 +1,50 @@ +from fastapi import APIRouter, Request +from fastapi.responses import StreamingResponse + +from schema.schemas import UrlData, WifiData, ContactData, GeoLocationData, EmailData, HealthCheck +from service.qr_code_service import url_to_qr_service, wifi_to_qr_service, contact_to_qr_service, geo_to_qr_service, email_to_qr_service + + +router = APIRouter() + +@router.post("/url_to_qr") +async def url_to_qr(request: Request, + info: UrlData) -> StreamingResponse: + + return await url_to_qr_service(request, info) + + +@router.post("/wifi_to_qr") +async def wifi_to_qr(request: Request, + info: WifiData) -> StreamingResponse: + + return await wifi_to_qr_service(request, info) + + +@router.post("/contact_to_qr") +async def contact_to_qr(request: Request, + info: ContactData) -> StreamingResponse: + + return await contact_to_qr_service(request, info) + + +@router.post("/geo_to_qr") +async def geo_to_qr(request: Request, + info: GeoLocationData) -> StreamingResponse: + + return await geo_to_qr_service(request, info) + + +@router.post("/email_to_qr") +async def email_to_qr(request: Request, + info: EmailData) -> StreamingResponse: + + return await email_to_qr_service(request, info) + + +@router.get("/health") +async def get_health() -> HealthCheck: + + return HealthCheck(status="OK") + +# return templates.TemplateResponse("home.html", {"request": request, "qr_data": qr_data}) \ No newline at end of file diff --git a/app/schema/schemas.py b/app/schema/schemas.py index 5c57894..fda07c2 100644 --- a/app/schema/schemas.py +++ b/app/schema/schemas.py @@ -25,4 +25,7 @@ class EmailData(BaseModel): to: str subject: str body: str - cc: str = None \ No newline at end of file + cc: str = None + +class HealthCheck(BaseModel): + status: str = "OK" \ No newline at end of file diff --git a/app/service/qr_code_service.py b/app/service/qr_code_service.py new file mode 100644 index 0000000..ae5bac4 --- /dev/null +++ b/app/service/qr_code_service.py @@ -0,0 +1,87 @@ +import io +import segno +from fastapi import Request, status +from fastapi.responses import StreamingResponse +from segno import helpers + +from schema.schemas import UrlData, WifiData, ContactData, GeoLocationData, EmailData + + +async def url_to_qr_service(request: Request, + info: UrlData) -> StreamingResponse: + + data: str = info.url + qr_data: bytes = segno.make_qr(data, error = 'H') + qr_buffer = io.BytesIO() + qr_data.save(qr_buffer, kind='png', scale=10) + qr_buffer.seek(0) + return StreamingResponse(status_code=status.HTTP_201_CREATED, + content=qr_buffer, + media_type="image/png") + + +async def wifi_to_qr_service(request: Request, + info: WifiData) -> StreamingResponse: + + name: str = info.wifi_name + password: str = info.wifi_password + data: bytes = helpers.make_wifi_data(ssid=name, password=password, security='WPA') + qr_data: bytes = segno.make(data, error = 'H') + qr_buffer = io.BytesIO() + qr_data.save(qr_buffer, kind='png', scale=10) + qr_buffer.seek(0) + return StreamingResponse(status_code=status.HTTP_201_CREATED, + content=qr_buffer, + media_type="image/png") + + +async def contact_to_qr_service(request: Request, + info: ContactData) -> StreamingResponse: + + data: bytes = helpers.make_vcard_data( + displayname = info.name, + name = info.name, + email = info.email, + phone = info.phone, + city = info.city, + org = info.org, + title = info.title, + url = info.url + ) + qr_data: bytes = segno.make(data, error = 'H') + qr_buffer = io.BytesIO() + qr_data.save(qr_buffer, kind='png', scale=10) + qr_buffer.seek(0) + return StreamingResponse(status_code=status.HTTP_201_CREATED, + content=qr_buffer, + media_type="image/png") + + +async def geo_to_qr_service(request: Request, + info: GeoLocationData) -> StreamingResponse: + + data: bytes = helpers.make_geo_data(info.latitude, info.longitude) + qr_data: bytes = segno.make(data, error = 'H') + qr_buffer = io.BytesIO() + qr_data.save(qr_buffer, kind='png', scale=10) + qr_buffer.seek(0) + return StreamingResponse(status_code=status.HTTP_201_CREATED, + content=qr_buffer, + media_type="image/png") + + +async def email_to_qr_service(request: Request, + info: EmailData) -> StreamingResponse: + + qr_data: bytes = helpers.make_email( + to = info.to, + subject = info.subject, + body = info.body, + cc = info.cc + ) + qr_buffer = io.BytesIO() + qr_data.save(qr_buffer, kind='png', scale=10) + qr_buffer.seek(0) + return StreamingResponse(status_code=status.HTTP_201_CREATED, + content=qr_buffer, + media_type="image/png") \ No newline at end of file diff --git a/conf/gunicorn.conf.py b/conf/gunicorn.conf.py new file mode 100644 index 0000000..c2ef1f8 --- /dev/null +++ b/conf/gunicorn.conf.py @@ -0,0 +1,6 @@ +import multiprocessing +import uvicorn + +workers = multiprocessing.cpu_count() * 2 + 1 +bind = '0.0.0.0:8000' +worker_class = 'uvicorn.workers.UvicornWorker' \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 6721008..b6c458f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.3' +version: '3.8' services: app: diff --git a/requirements.txt b/requirements.txt index 1be66b3..a4f0beb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ anyio==4.3.0 click==8.1.7 colorama==0.4.6 fastapi==0.110.2 +gunicorn==22.0.0 h11==0.14.0 httptools==0.6.1 idna==3.7 @@ -13,9 +14,6 @@ packaging==24.0 pillow==10.3.0 pydantic==2.7.1 pydantic_core==2.18.2 -pypng==0.20220715.0 -pytz==2024.1 -PyYAML==6.0.1 segno==1.6.1 sniffio==1.3.1 starlette==0.37.2