From 4e21f32e6fa53ff94071374eabdea600881123af Mon Sep 17 00:00:00 2001 From: Fernanda Date: Tue, 7 Apr 2026 18:07:06 -0300 Subject: [PATCH] Commit inicial: estrutura o projeto MVC --- app.py | 75 ++++-------------------- models/task.py => controller/__init__.py | 0 controller/dispositivo_controller.py | 44 ++++++++++++++ controller/usuario_controller.py | 38 ++++++++++++ dao/__init__.py | 0 dao/dispositivo_dao.py | 30 ++++++++++ dao/usuario_dao.py | 26 ++++++++ database.py | 3 + model/__init__.py | 0 model/dispositivo.py | 19 ++++++ model/leitura.py | 15 +++++ model/usuario.py | 21 +++++++ requirements.txt | 4 ++ service/__init__.py | 0 service/dispositivo_service.py | 40 +++++++++++++ service/usuario_service.py | 34 +++++++++++ 16 files changed, 286 insertions(+), 63 deletions(-) rename models/task.py => controller/__init__.py (100%) create mode 100644 controller/dispositivo_controller.py create mode 100644 controller/usuario_controller.py create mode 100644 dao/__init__.py create mode 100644 dao/dispositivo_dao.py create mode 100644 dao/usuario_dao.py create mode 100644 database.py create mode 100644 model/__init__.py create mode 100644 model/dispositivo.py create mode 100644 model/leitura.py create mode 100644 model/usuario.py create mode 100644 requirements.txt create mode 100644 service/__init__.py create mode 100644 service/dispositivo_service.py create mode 100644 service/usuario_service.py diff --git a/app.py b/app.py index b67caf0..5f729f9 100644 --- a/app.py +++ b/app.py @@ -1,68 +1,17 @@ -from flask import Flask, request, jsonify +from flask import Flask +from database import db +from controller.usuario_controller import usuario_bp +from controller.dispositivo_controller import dispositivo_bp -# Define os parametros utilizadas na API app = Flask(__name__) -tasks = [] # Lista para armazenar as tarefas -task_id_control = 1 # Controlador de IDs para garantir unicidade +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///graphics_analytics.db' -# Algoritimo para uma rota basica '/' para teste -@app.route("/", methods=["GET"]) -def hello(): - return "Hello, World!" +db.init_app(app) +app.register_blueprint(usuario_bp) +app.register_blueprint(dispositivo_bp) -# Algoritimo para rota '/tasks' sendo um algoritimo simples de gravação de cabeçalho para depois ser consultado por metodo GET -@app.route("/tasks", methods=["POST"]) -def create_task(): - global task_id_control - data = request.get_json() # Pega os dados enviados no corpo da requisição - new_task = { - "id": task_id_control, - "title": data.get("title"), # Obtém o título enviado - "description": data.get("description", ""), # Descrição opcional - "completed": False # Define que a tarefa começa como incompleta - } - tasks.append(new_task) # Adiciona a nova tarefa à lista - task_id_control += 1 # Incrementa o ID para a próxima tarefa - return jsonify({"message": "Tarefa criada com sucesso!", "task": new_task}), 201 +with app.app_context(): + db.create_all() -# Algoritmo simples para consulta simples para consulta de total de "tasks" gravadas na API -@app.route("/tasks", methods=["GET"]) -def get_tasks(): - return jsonify({"tasks": tasks, "total": len(tasks)}) - -# Algoritimo simples para consulta do dado gravado dado um "ID" de valor inteiro para consulta -@app.route("/tasks/", methods=["GET"]) -def get_task(task_id): - # Procura a tarefa pelo ID - task = next((t for t in tasks if t["id"] == task_id), None) - if not task: - return jsonify({"message": "Tarefa não encontrada"}), 404 - return jsonify(task) - -# Algoritimo simples para atualizar dados de uma tarefa já gravada utilizando o "ID" da tarefa a ser editada -@app.route("/tasks/", methods=["PUT"]) -def update_task(task_id): - task = next((t for t in tasks if t["id"] == task_id), None) - if not task: - return jsonify({"message": "Tarefa não encontrada"}), 404 - - data = request.get_json() - # Atualiza os campos da tarefa com os dados enviados - task["title"] = data.get("title", task["title"]) - task["description"] = data.get("description", task["description"]) - task["completed"] = data.get("completed", task["completed"]) - return jsonify({"message": "Tarefa atualizada com sucesso!", "task": task}) - -# Algoritimo simples para deletar uma tarefa pela "ID" -@app.route("/tasks/", methods=["DELETE"]) -def delete_task(task_id): - global tasks - task = next((t for t in tasks if t["id"] == task_id), None) - if not task: - return jsonify({"message": "Tarefa não encontrada"}), 404 - tasks = [t for t in tasks if t["id"] != task_id] - return jsonify({"message": "Tarefa deletada com sucesso!"}) - -# Essa parte do código deverá estar no final do projeto para realizar o algoritimo acima -if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file +if __name__ == '__main__': + app.run(debug=True) diff --git a/models/task.py b/controller/__init__.py similarity index 100% rename from models/task.py rename to controller/__init__.py diff --git a/controller/dispositivo_controller.py b/controller/dispositivo_controller.py new file mode 100644 index 0000000..77e5fd8 --- /dev/null +++ b/controller/dispositivo_controller.py @@ -0,0 +1,44 @@ +from flask import Blueprint, request, jsonify +from service.dispositivo_service import DispositivoService +from model.dispositivo import DispositivoNaoEncontrado + +dispositivo_bp = Blueprint('dispositivo', __name__) + + +@dispositivo_bp.route('/dispositivos', methods=['POST']) +def criar(): + dados = request.get_json() + dispositivo = DispositivoService.instancia().criar( + usuario_id=dados['usuario_id'], + nome=dados['nome'], + descricao=dados.get('descricao') + ) + return jsonify({ + "id": dispositivo.id, + "usuario_id": dispositivo.usuario_id, + "nome": dispositivo.nome, + "descricao": dispositivo.descricao, + "chave": dispositivo.chave, + "ativo": dispositivo.ativo + }), 201 + + +@dispositivo_bp.route('/dispositivos/', methods=['GET']) +def listar(usuario_id): + dispositivos = DispositivoService.instancia().listar_por_usuario(usuario_id) + return jsonify([{ + "id": d.id, + "nome": d.nome, + "descricao": d.descricao, + "chave": d.chave, + "ativo": d.ativo + } for d in dispositivos]) + + +@dispositivo_bp.route('/dispositivos/', methods=['DELETE']) +def deletar(id): + try: + DispositivoService.instancia().deletar(id) + return jsonify({"mensagem": "Dispositivo deletado"}), 200 + except DispositivoNaoEncontrado: + return jsonify({"erro": "Dispositivo nao encontrado"}), 404 diff --git a/controller/usuario_controller.py b/controller/usuario_controller.py new file mode 100644 index 0000000..0c8a1a7 --- /dev/null +++ b/controller/usuario_controller.py @@ -0,0 +1,38 @@ +from flask import Blueprint, request, jsonify +from service.usuario_service import UsuarioService +from model.usuario import EmailJaCadastrado, UsuarioNaoEncontrado + +usuario_bp = Blueprint('usuario', __name__) + + +@usuario_bp.route('/auth/registro', methods=['POST']) +def registrar(): + dados = request.get_json() + try: + usuario = UsuarioService.instancia().registrar( + nome=dados['nome'], + email=dados['email'], + senha=dados['senha'] + ) + return jsonify({ + "id": usuario.id, + "nome": usuario.nome, + "email": usuario.email, + "token_api": usuario.token_api + }), 201 + except EmailJaCadastrado: + return jsonify({"erro": "Email ja cadastrado"}), 409 + + +@usuario_bp.route('/auth/usuario/', methods=['GET']) +def buscar(id): + try: + usuario = UsuarioService.instancia().buscar_por_id(id) + return jsonify({ + "id": usuario.id, + "nome": usuario.nome, + "email": usuario.email, + "token_api": usuario.token_api + }) + except UsuarioNaoEncontrado: + return jsonify({"erro": "Usuario nao encontrado"}), 404 diff --git a/dao/__init__.py b/dao/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dao/dispositivo_dao.py b/dao/dispositivo_dao.py new file mode 100644 index 0000000..5a64536 --- /dev/null +++ b/dao/dispositivo_dao.py @@ -0,0 +1,30 @@ +from model.dispositivo import Dispositivo +from database import db + + +class DispositivoDao: + _instancia = None + + @classmethod + def instancia(cls): + if cls._instancia is None: + cls._instancia = cls() + return cls._instancia + + def criar(self, dispositivo): + db.session.add(dispositivo) + db.session.commit() + return dispositivo + + def buscar_por_id(self, id): + return db.session.get(Dispositivo, id) + + def buscar_por_chave(self, chave): + return Dispositivo.query.filter_by(chave=chave).first() + + def listar_por_usuario(self, usuario_id): + return Dispositivo.query.filter_by(usuario_id=usuario_id).all() + + def deletar(self, dispositivo): + db.session.delete(dispositivo) + db.session.commit() diff --git a/dao/usuario_dao.py b/dao/usuario_dao.py new file mode 100644 index 0000000..17bdfa0 --- /dev/null +++ b/dao/usuario_dao.py @@ -0,0 +1,26 @@ +from model.usuario import Usuario +from database import db + + +class UsuarioDao: + _instancia = None + + @classmethod + def instancia(cls): + if cls._instancia is None: + cls._instancia = cls() + return cls._instancia + + def criar(self, usuario): + db.session.add(usuario) + db.session.commit() + return usuario + + def buscar_por_id(self, id): + return db.session.get(Usuario, id) + + def buscar_por_email(self, email): + return Usuario.query.filter_by(email=email).first() + + def buscar_por_token(self, token_api): + return Usuario.query.filter_by(token_api=token_api).first() diff --git a/database.py b/database.py new file mode 100644 index 0000000..f0b13d6 --- /dev/null +++ b/database.py @@ -0,0 +1,3 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() diff --git a/model/__init__.py b/model/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/model/dispositivo.py b/model/dispositivo.py new file mode 100644 index 0000000..9ddef05 --- /dev/null +++ b/model/dispositivo.py @@ -0,0 +1,19 @@ +from database import db +from datetime import datetime +import uuid + + +class DispositivoNaoEncontrado(Exception): + pass + + +class Dispositivo(db.Model): + __tablename__ = 'dispositivos' + + id = db.Column(db.Integer, primary_key=True) + usuario_id = db.Column(db.Integer, db.ForeignKey('usuarios.id'), nullable=False) + nome = db.Column(db.String(100), nullable=False) + descricao = db.Column(db.String(200)) + chave = db.Column(db.String(36), unique=True, default=lambda: str(uuid.uuid4()), nullable=False) + ativo = db.Column(db.Boolean, default=True) + criado_em = db.Column(db.DateTime, default=datetime.utcnow) diff --git a/model/leitura.py b/model/leitura.py new file mode 100644 index 0000000..b3388e6 --- /dev/null +++ b/model/leitura.py @@ -0,0 +1,15 @@ +from database import db +from datetime import datetime + + +class LeituraNaoEncontrada(Exception): + pass + + +class Leitura(db.Model): + __tablename__ = 'leituras' + + id = db.Column(db.Integer, primary_key=True) + dispositivo_id = db.Column(db.Integer, db.ForeignKey('dispositivos.id'), nullable=False) + payload = db.Column(db.JSON, nullable=False) + criado_em = db.Column(db.DateTime, default=datetime.utcnow) diff --git a/model/usuario.py b/model/usuario.py new file mode 100644 index 0000000..c7fd51f --- /dev/null +++ b/model/usuario.py @@ -0,0 +1,21 @@ +from database import db +from datetime import datetime +import uuid + +class UsuarioNaoEncontrado(Exception): + pass + + +class EmailJaCadastrado(Exception): + pass + + +class Usuario(db.Model): + __tablename__ = 'usuarios' + + id = db.Column(db.Integer, primary_key=True) + nome = db.Column(db.String(100), nullable=False) + email = db.Column(db.String(150), unique=True, nullable=False) + senha_hash = db.Column(db.String(255), nullable=False) + token_api = db.Column(db.String(36), unique=True, default=lambda: str(uuid.uuid4())) + criado_em = db.Column(db.DateTime, default=datetime.utcnow) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..0e10c93 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask +flask-sqlalchemy +psycopg2-binary +bcrypt diff --git a/service/__init__.py b/service/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/service/dispositivo_service.py b/service/dispositivo_service.py new file mode 100644 index 0000000..13ca75f --- /dev/null +++ b/service/dispositivo_service.py @@ -0,0 +1,40 @@ +from model.dispositivo import Dispositivo, DispositivoNaoEncontrado +from dao.dispositivo_dao import DispositivoDao + + +class DispositivoService: + _instancia = None + + @classmethod + def instancia(cls): + if cls._instancia is None: + cls._instancia = cls() + return cls._instancia + + def __init__(self): + self.dao = DispositivoDao.instancia() + + def criar(self, usuario_id, nome, descricao=None): + dispositivo = Dispositivo(usuario_id=usuario_id, nome=nome, descricao=descricao) + return self.dao.criar(dispositivo) + + def buscar_por_id(self, id): + dispositivo = self.dao.buscar_por_id(id) + if not dispositivo: + raise DispositivoNaoEncontrado() + return dispositivo + + def buscar_por_chave(self, chave): + dispositivo = self.dao.buscar_por_chave(chave) + if not dispositivo: + raise DispositivoNaoEncontrado() + return dispositivo + + def listar_por_usuario(self, usuario_id): + return self.dao.listar_por_usuario(usuario_id) + + def deletar(self, id): + dispositivo = self.dao.buscar_por_id(id) + if not dispositivo: + raise DispositivoNaoEncontrado() + self.dao.deletar(dispositivo) diff --git a/service/usuario_service.py b/service/usuario_service.py new file mode 100644 index 0000000..d8ee027 --- /dev/null +++ b/service/usuario_service.py @@ -0,0 +1,34 @@ +from model.usuario import Usuario, UsuarioNaoEncontrado, EmailJaCadastrado +from dao.usuario_dao import UsuarioDao +import bcrypt + + +class UsuarioService: + _instancia = None + + @classmethod + def instancia(cls): + if cls._instancia is None: + cls._instancia = cls() + return cls._instancia + + def __init__(self): + self.dao = UsuarioDao.instancia() + + def registrar(self, nome, email, senha): + if self.dao.buscar_por_email(email): + raise EmailJaCadastrado() + + senha_hash = bcrypt.hashpw( + senha.encode('utf-8'), + bcrypt.gensalt() + ).decode('utf-8') + + usuario = Usuario(nome=nome, email=email, senha_hash=senha_hash) + return self.dao.criar(usuario) + + def buscar_por_id(self, id): + usuario = self.dao.buscar_por_id(id) + if not usuario: + raise UsuarioNaoEncontrado() + return usuario