Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__

.env
venv
13 changes: 13 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
59 changes: 39 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
![WATTIO](http://wattio.com.br/web/image/1204-212f47c3/Logo%20Wattio.png)
![WATTIO](./img/banner-preview.png)

## Como executar a aplicação

Para executar a aplicação, siga os seguintes passos:

1. **Pré-requisitos:**
* [Docker](https://www.docker.com/)
* [Docker Compose](https://docs.docker.com/compose/install/)

2. **Clone o repositório:**
```bash
git clone https://github.com/lucasaguiar-la/desafio-backend-wattio
```

3. **Inicie a aplicação:**
```bash
docker-compose up
```
A API estará disponível em `http://localhost:8000/`

4. **Cadastrar um filme**
Ao fazer uma requisição [POST] para `/filmes`, envie o seguinte JSON no corpo da requisição:

```json
{
"movie_name": "O Senhor dos Anéis: O Retorno do Rei",
"synopsis": "Gandalf e Aragorn lideram o Mundo dos Homens contra o exército de Sauron para desviar o olhar de Frodo e Sam quando eles se aproximam á Montanha da Perdição com o Um Anel.",
"duration": 201,
"year_release": 2003,
"movie_gender": "Fantasia"
}
```
---

#### Descrição

Expand All @@ -15,23 +48,9 @@ O Objetivo é te desafiar e reconhecer seu esforço para aprender e se adaptar.
#### Sugestão de Ferramentas
Não é obrigatório utilizar todas as as tecnologias sugeridas, mas será um diferencial =]

- Orientação a objetos (utilizar objetos, classes para manipular os filmes)
- [FastAPI](https://fastapi.tiangolo.com/) (API com documentação auto gerada)
- [Docker](https://www.docker.com/) / [Docker-compose](https://docs.docker.com/compose/install/) (Aplicação deverá ficar em um container docker, e o start deverá seer com o comando ``` docker-compose up ```
- Integração com banco de dados (persistir as informações em json (iniciante) /[SqLite](https://www.sqlite.org/index.html) / [SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/#sql-relational-databases) / outros DB)


#### Como começar?

- Fork do repositório
- Criar branch com seu nome ``` git checkout -b feature/ana ```
- Faça os commits de suas alterações ``` git commit -m "[ADD] Funcionalidade" ```
- Envie a branch para seu repositório ``` git push origin feature/ana ```
- Navegue até o [Github](https://github.com/), crie seu Pull Request apontando para a branch **```main```**
- Atualize o README.md descrevendo como subir sua aplicação

#### Dúvidas?

Qualquer dúvida / sugestão / melhoria / orientação adicional só enviar email para [email protected]
- [x] Orientação a objetos (utilizar objetos, classes para manipular os filmes)
- [x] [FastAPI](https://fastapi.tiangolo.com/) (API com documentação auto gerada)
- [x] [Docker](https://www.docker.com/) / [Docker-compose](https://docs.docker.com/compose/install/) (Aplicação deverá ficar em um container docker, e o start deverá seer com o comando ``` docker-compose up ```
- [x] Integração com banco de dados (persistir as informações em json (iniciante) /[SqLite](https://www.sqlite.org/index.html) / [SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/#sql-relational-databases) / outros DB)

Salve!
---
37 changes: 37 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
version: "3.8"

services:
db:
image: postgres:15
container_name: postgres_movies
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: moviesdb
volumes:
- postgres_data:/var/lib/postgresql/data
- ./src/database/schema.sql:/docker-entrypoint-initdb.d/schema.sql
ports:
- "5433:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5

api:
build: .
container_name: fastapi_movies
depends_on:
db:
condition: service_healthy
ports:
- "8000:8000"
env_file:
- .env
volumes:
- .:/app

volumes:
postgres_data:
Binary file added img/banner-preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from fastapi import FastAPI
from src.controller import movie_controller

app = FastAPI()
app.include_router(movie_controller.router)

@app.get('/')
def root():
return {
'Status': 'API rodando...'
}
Binary file added requirements.txt
Binary file not shown.
32 changes: 32 additions & 0 deletions src/controller/movie_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from src.models.movie import MovieModel
from src.repositories.movie_repository import MovieRepository

from fastapi import APIRouter, HTTPException
from psycopg2.errors import UniqueViolation

router = APIRouter()
movie_repository = MovieRepository()

@router.post('/filmes', status_code=201)
def register_movie(movie: MovieModel):
try:
movie_repository.create(movie)
return {
'Mensagem': 'Filme registrado com sucesso!'
}
except UniqueViolation:
raise HTTPException(status_code=409, detail=f'O filme {movie.movie_name} já está cadastrado.')
except Exception as e:
raise HTTPException(status_code=500, detail='Ocorreu um erro interno no servidor.')

@router.get('/filmes')
def get_all_movies():
return movie_repository.get_all()

@router.get('/filmes/{id}')
def get_one_movie(id: int):
movie = movie_repository.get_by_id(id)
if movie:
return movie
else:
raise HTTPException(status_code=404, detail='Filme não encontrado :(')
8 changes: 8 additions & 0 deletions src/database/connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import psycopg2
from dotenv import load_dotenv
import os

load_dotenv()

def get_connection():
return psycopg2.connect(os.getenv('DATABASE_URL'))
8 changes: 8 additions & 0 deletions src/database/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS movies(
id SERIAL PRIMARY KEY,
movie_name TEXT NOT NULL UNIQUE,
synopsis TEXT,
duration INT,
year_release INT,
movie_gender TEXT
);
8 changes: 8 additions & 0 deletions src/models/movie.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from pydantic import BaseModel

class MovieModel(BaseModel):
movie_name: str
synopsis: str
duration: int
year_release: int
movie_gender: str
71 changes: 71 additions & 0 deletions src/repositories/movie_repository.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from src.database.connection import get_connection
from src.models.movie import MovieModel

class MovieRepository:
def __init__(self):
self.conn = get_connection()

def get_all(self):
cur = self.conn.cursor()

cur.execute('''
SELECT id, movie_name, synopsis, duration, year_release, movie_gender
FROM movies;
''')

rows = cur.fetchall()
cur.close()

movies = []
for row in rows:
movies.append({
'id': row[0],
'movie_name': row[1],
'synopsis': row[2],
'duration': row[3],
'year_release': row[4],
'movie_gender': row[5]
})

return movies

def get_by_id(self, id: int):
cur = self.conn.cursor()

cur.execute('''
SELECT id, movie_name, synopsis, duration, year_release, movie_gender
FROM movies
WHERE id = %s;
''', (id,))

row = cur.fetchone()
cur.close()

if row:
return {
'id': row[0],
'movie_name': row[1],
'synopsis': row[2],
'duration': row[3],
'year_release': row[4],
'movie_gender': row[5]
}
return None

def create(self, movie: MovieModel):
cur = self.conn.cursor()

cur.execute('''
INSERT INTO movies (movie_name, synopsis, duration, year_release, movie_gender)
VALUES (%s, %s, %s, %s, %s);
''', (movie.movie_name, movie.synopsis, movie.duration, movie.year_release, movie.movie_gender))

self.conn.commit()
cur.close()

def __del__(self):
try:
if hasattr(self, 'conn') and self.conn:
self.conn.close()
except:
pass