Commit 6e74d79f authored by Guilherme de Moraes Machado Pereira Silva's avatar Guilherme de Moraes Machado Pereira Silva Committed by Arthur Sudbrack Ibarra
Browse files

E01-US01-pdf-receive

parent b295ea0c
Showing with 240 additions and 108 deletions
+240 -108
# from fastapi import HTTPException
from app.api.services.car_service import CarService
from app.api.models.car import Car
# Controller class.
#
# This class is responsible for handling the requests and responses.
# It will call the service layer to get the data and return the response.
# It will also handle any exceptions that are raised by the service layer.
class CarController:
def __init__(self, service: CarService):
self._service = service
def get_all(self):
# Call the service to get the cars.
pass
def get_by_id(self, car_id: int):
pass
def create(self, car_data: Car):
pass
def update(self, car_id: int, car_data: Car):
pass
def delete(self, car_id: int):
pass
# from fastapi import HTTPException
from app.api.services.veiculo_service import VeiculoService
from app.api.models.veiculo import Veiculo
# Controller class.
#
# This class is responsible for handling the requests and responses.
# It will call the service layer to get the data and return the response.
# It will also handle any exceptions that are raised by the service layer.
class VeiculoController:
def __init__(self, service: VeiculoService):
self._service = service
def get_all(self) -> list[Veiculo]:
return self._service.get_all()
def get_by_sigla(self, sigla: str) -> Veiculo:
return self._service.get_by_sigla(sigla)
def create(self, veiculo_data: Veiculo) -> None:
self._service.create(veiculo_data)
def update(self, sigla: int, veiculo_data: Veiculo) -> None:
self._service.update(sigla, veiculo_data)
def delete(self, sigla: int) -> None:
self._service.delete(sigla)
from pydantic import BaseModel
from typing import Optional
# Car model.
#
# This model represents a car object in the database.
# Add any fields that you want to store in the database.
class Car(BaseModel):
id: str
name: str
description: Optional[str] = ""
from pydantic import BaseModel
class Combustivel(BaseModel):
potencia: str
tipo_combustivel: str
class Motor(BaseModel):
cilindradas: str
nro_cilindradas: str
combustiveis: list[Combustivel]
class Veiculo(BaseModel):
desc_cat: str
renavam_desc: str
sigla: str
pacote_def_modelo: str
versao: str
ano: str
marca: str
linha: str
motor: Motor
carga: str
num_passag: str
num_portas: str
num_renavam: str
status: str
pdf_names: list[str]
from app.api.models.car import Car
from pymongo.database import Database
# CarRepository class
#
# This class is responsible for accessing the database to get the cars.
# It will be called by the service layer to get the data.
# It will also handle any exceptions that are raised by the database.
CAR_COLLECTION = "cars"
class CarRepository:
def __init__(self, database: Database):
self._database = database
def get_all(self):
# Access the database to get the cars.
# Reminder: Transform the data that comes as a cursor into a list of Car objects.
# Ex: list(db.find())
pass
def get_by_id(self, car_id: int):
pass
def create(self, car_data: Car):
# On create we need to convert car_data to dict and then insert it into the database.
# Ex: db.insert_one(car_data.dict())
pass
def update(self, car_id: int, car_data: Car):
pass
def delete(self, car_id: int):
pass
from app.api.models.veiculo import Veiculo
from pymongo.database import Database
from pymongo.results import UpdateResult, DeleteResult
# CarRepository class
#
# This class is responsible for accessing the database to get the cars.
# It will be called by the service layer to get the data.
# It will also handle any exceptions that are raised by the database.
VEICULO_COLLECTION = "Veiculos"
class VeiculoRepository:
def __init__(self, database: Database):
self._collection = database[VEICULO_COLLECTION]
def get_all(self) -> list[Veiculo]:
veiculos = []
veiculos_dict = list(self._collection.find())
for veiculo in veiculos_dict:
veiculos.append(Veiculo.parse_obj(veiculo))
return veiculos
def get_by_sigla(self, sigla: str) -> Veiculo:
veiculo_dict = self._collection.find_one({"sigla": sigla})
return Veiculo.parse_obj(veiculo_dict)
def create(self, veiculo_data: Veiculo):
# On create we need to convert car_data to dict and then insert it into the database.
# Ex: db.insert_one(car_data.dict())
self._collection.insert_one(veiculo_data.dict())
def update(self, sigla: int, veiculo_data: Veiculo) -> UpdateResult:
return self._collection.update_one({"sigla": sigla}, {"$set": veiculo_data.dict()})
def delete(self, sigla: int) -> DeleteResult:
return self._collection.delete_one({"sigla": sigla})
from fastapi import APIRouter
from app.api.models.car import Car
from app.database.mongo import get_database
from app.api.repositories.car_repository import CarRepository
from app.api.services.car_service import CarService
from app.api.controllers.car_controller import CarController
# Car router.
#
# Here we define the routes for the car resource.
# We call the CarController to handle the requests.
# These variables start with an underscore to indicate that they are 'private'.
# They are not meant to be used outside of this file.
_database = get_database()
_repository = CarRepository(_database)
_service = CarService(_repository)
_car_controller = CarController(_service)
_car_router = APIRouter(prefix="/cars")
@_car_router.get("/")
def get_cars():
return _car_controller.get_all()
@_car_router.get("/{car_id}")
def get_car(car_id: str):
return _car_controller.get_by_id(car_id)
@_car_router.post("/")
def create_car(car_data: Car):
_car_controller.create(car_data)
@_car_router.put("/{car_id}")
def update_car(car_id: str, car_data: Car):
return _car_controller.update(car_id, car_data)
@_car_router.delete("/{car_id}")
def delete_car(car_id: str):
return _car_controller.delete(car_id)
# This function is used to get the car router.
# It is used in the main.py file to include the router in the FastAPI app.
def get_car_router() -> APIRouter:
return _car_router
from fastapi import APIRouter, HTTPException
from pydantic import ValidationError
from fastapi import FastAPI, File, UploadFile
from app.api.models.veiculo import Veiculo
from app.database.mongo import get_database
from app.api.repositories.veiculo_repository import VeiculoRepository
from app.api.services.veiculo_service import VeiculoService
from app.api.controllers.veiculo_controller import VeiculoController
# Car router.
#
# Here we define the routes for the car resource.
# We call the CarController to handle the requests.
# These variables start with an underscore to indicate that they are 'private'.
# They are not meant to be used outside of this file.
_database = get_database()
_repository = VeiculoRepository(_database)
_service = VeiculoService(_repository)
_veiculo_controller = VeiculoController(_service)
_veiculo_router = APIRouter(prefix="/veiculos")
@_veiculo_router.get("/")
def get_veiculos() -> list[Veiculo]:
return _veiculo_controller.get_all()
@_veiculo_router.get("/{sigla}")
def get_veiculo(sigla: str) -> Veiculo:
return _veiculo_controller.get_by_sigla(sigla)
@_veiculo_router.post("/")
def create_veiculo(veiculo_data: Veiculo) -> None:
_veiculo_controller.create(veiculo_data)
@_veiculo_router.put("/{sigla}")
def update_veiculo(sigla: str, veiculo_data: Veiculo) -> None:
_veiculo_controller.update(sigla, veiculo_data)
@_veiculo_router.delete("/{sigla}")
def delete_veiculo(sigla: str) -> None:
_veiculo_controller.delete(sigla)
# It contains a single endpoint that receives the PDF file.
@_veiculo_router.post("/pdf/")
def create_upload_file(form_data: UploadFile = File(...)):
contents = form_data.file.read() # This function read the pdf bytes.
contents # Here we have the pdf bytes saved in the application memory. The ideia is to call a funtion which will handle the pdf bytes and extract them.
name : str
name = form_data.filename # This is the file name in memory. It will be used to save the veiculo JSON in the database.
return {"filename": form_data.filename}
# This function is used to get the car router.
# It is used in the main.py file to include the router in the FastAPI app.
def get_veiculo_router() -> APIRouter:
return _veiculo_router
from app.api.repositories.car_repository import CarRepository
from app.api.models.car import Car
# Service class.
#
# This class is responsible for handling the business logic.
# It will call the repository layer to get the data and return the response.
# It will also handle any exceptions that are raised by the repository layer.
class CarService:
def __init__(self, repository: CarRepository):
self._repository = repository
def get_all(self):
# Call the repository to get the cars.
pass
def get_by_id(self, car_id: int):
pass
def create(self, car_data: Car):
pass
def update(self, car_id: int, car_data: Car):
pass
def delete(self, car_id: int):
pass
from fastapi import HTTPException
from app.api.repositories.veiculo_repository import VeiculoRepository
from app.api.models.veiculo import Veiculo
# Service class.
#
# This class is responsible for handling the business logic.
# It will call the repository layer to get the data and return the response.
# It will also handle any exceptions that are raised by the repository layer.
class VeiculoService:
def __init__(self, repository: VeiculoRepository):
self._repository = repository
def get_all(self) -> list[Veiculo]:
# Call the repository to get the cars.
return self._repository.get_all()
def get_by_sigla(self, sigla: str) -> Veiculo:
try:
return self._repository.get_by_sigla(sigla)
except Exception as e:
raise HTTPException(status_code=404, detail="Veiculo nao encontrado")
def create(self, veiculo_data: Veiculo) -> None:
self._repository.create(veiculo_data)
def update(self, sigla: int, veiculo_data: Veiculo) -> None:
response = self._repository.update(sigla, veiculo_data)
if response.modified_count == 0:
raise HTTPException(status_code=400, detail="Nenhum dado encontrado ou modificado")
def delete(self, sigla: int) -> None:
response = self._repository.delete(sigla)
if response.deleted_count == 0:
raise HTTPException(status_code=400, detail="Dado nao encontrado para deletar")
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from mangum import Mangum
from app.api.routers.car_router import get_car_router
from app.api.routers.veiculo_router import get_veiculo_router
# Initialize the FastAPI app.
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["OPTIONS", "GET", "POST", "PUT", "DELETE"],
# Headers should be pdf or json, but more testing is required
# "Content-Type", "application/pdf", "application/json"
allow_headers=["*"]
)
# Create a handler for AWS Lambda.
handler = Mangum(app)
# Include the car router.
app.include_router(get_car_router())
app.include_router(get_veiculo_router())
# Start the server.
if __name__ == "__main__":
......
import pytest
from os import environ
from fastapi.testclient import TestClient
from app.api.models.car import Car
from app.api.models.veiculo import Veiculo
from app.main import app
......@@ -16,15 +16,15 @@ def test_app():
yield TestClient(app)
def test_create_car(test_app: TestClient):
# Send a POST request to the /cars endpoint.
# The request body represents a car object.
car_data = Car(
id="1",
name="Fiat Uno",
description="Small and cheap car."
)
response = test_app.post("/cars", json=car_data.dict())
# Assert that the response status code is 200, for example.
assert response.status_code == 200
# Assert more things as needed...
# def test_create_car(test_app: TestClient):
# # Send a POST request to the /cars endpoint.
# # The request body represents a car object.
# veiculo_data = Veiculo(
# id="1",
# name="Fiat Uno",
# description="Small and cheap car."
# )
# response = test_app.post("/veiculos", json=veiculo_data.dict())
# # Assert that the response status code is 200, for example.
# assert response.status_code == 200
# # Assert more things as needed...
......@@ -592,6 +592,21 @@ files = [
[package.dependencies]
six = ">=1.5"
[[package]]
name = "python-multipart"
version = "0.0.6"
description = "A streaming multipart parser for Python"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "python_multipart-0.0.6-py3-none-any.whl", hash = "sha256:ee698bab5ef148b0a760751c261902cd096e57e10558e11aca17646b74ee1c18"},
{file = "python_multipart-0.0.6.tar.gz", hash = "sha256:e9925a80bb668529f1b67c7fdb0a5dacdd7cbfc6fb0bff3ea443fe22bdd62132"},
]
[package.extras]
dev = ["atomicwrites (==1.2.1)", "attrs (==19.2.0)", "coverage (==6.5.0)", "hatch", "invoke (==1.7.3)", "more-itertools (==4.3.0)", "pbr (==4.3.0)", "pluggy (==1.0.0)", "py (==1.11.0)", "pytest (==7.2.0)", "pytest-cov (==4.0.0)", "pytest-timeout (==2.1.0)", "pyyaml (==5.1)"]
[[package]]
name = "pytz"
version = "2023.3"
......@@ -744,4 +759,4 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)",
[metadata]
lock-version = "2.0"
python-versions = "^3.9.0"
content-hash = "bc07cce2ed1ad0520891c3252d7928e00dfbba28df3553be6173b994ee503bee"
content-hash = "89e974075321546e7841055b5ac967f55304124a2276439cc602e7252ade0f3f"
......@@ -14,6 +14,7 @@ pydantic = "1.10.7"
pymongo = "4.3.3"
tabula-py = "2.7.0"
httpx = "0.23.3"
python-multipart = "0.0.6"
[tool.poetry.group.dev.dependencies]
autopep8 = "2.0.2"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment