From 3d6821682bcbd0e5d99efdf7d6a45fbf92c08620 Mon Sep 17 00:00:00 2001
From: Thais Ferreira
Date: Thu, 27 Mar 2025 17:38:08 -0300
Subject: [PATCH] =?UTF-8?q?atualiza=C3=A7=C3=A3o=20relatorios?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
app.py | 3 +-
docs/reports/create.yml | 99 +++++++++++++++++++++++++++++
docs/reports/delete.yml | 60 ++++++++++++++++++
docs/reports/list.yml | 107 ++++++++++++++++++++++++++++++++
docs/reports/update.yml | 90 +++++++++++++++++++++++++++
models/reports.py | 22 +++++++
routes/reports.py | 134 ++++++++++++++++++++++++++++++++++++++++
seeds/reports_seed.py | 96 ++++++++++++++++++++++++++++
seeds/run_seed.py | 2 +
seeds/service_roles.py | 14 -----
10 files changed, 612 insertions(+), 15 deletions(-)
create mode 100644 docs/reports/create.yml
create mode 100644 docs/reports/delete.yml
create mode 100644 docs/reports/list.yml
create mode 100644 docs/reports/update.yml
create mode 100644 models/reports.py
create mode 100644 routes/reports.py
create mode 100644 seeds/reports_seed.py
diff --git a/app.py b/app.py
index 8663a15..a773c47 100644
--- a/app.py
+++ b/app.py
@@ -20,6 +20,7 @@ from routes.service import service_bp
from routes.service_roles import service_role_bp
from routes.cameras import camera_bp
from routes.ambiente import ambiente_bp
+from routes.reports import employee_bp
import logging
from dotenv import load_dotenv
from werkzeug.exceptions import BadRequest
@@ -142,4 +143,4 @@ if __name__ == "__main__":
with app.app_context():
db.create_all() # Cria as tabelas
run_all_seeds()
- app.run(debug=True, host="0.0.0.0", port=5001)
+ app.run(debug=True, host="0.0.0.0")
diff --git a/docs/reports/create.yml b/docs/reports/create.yml
new file mode 100644
index 0000000..70337e6
--- /dev/null
+++ b/docs/reports/create.yml
@@ -0,0 +1,99 @@
+ tags:
+ - Relatórios de Horários
+ summary: Criar novo relatório de ponto
+ description: Registra um novo relatório de horário para um colaborador
+ security:
+ - bearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ required:
+ - employee_id
+ - entry_time
+ - exit_time
+ properties:
+ employee_id:
+ type: integer
+ description: ID do colaborador
+ example: 1
+ entry_time:
+ type: string
+ description: Horário de entrada
+ example: "08:05"
+ exit_time:
+ type: string
+ description: Horário de saída
+ example: "17:10"
+ date:
+ type: string
+ format: date
+ description: Data do relatório (opcional, padrão é data atual)
+ example: "2024-03-27"
+ status:
+ type: string
+ description: Status do relatório
+ enum: ['Regular', 'Atrasado', 'Saída Antecipada']
+ example: "Regular"
+ responses:
+ '201':
+ description: Relatório criado com sucesso
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ message:
+ type: string
+ example: "Relatório criado com sucesso"
+ report_id:
+ type: integer
+ example: 10
+ '400':
+ description: Requisição inválida
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Dados do relatório inválidos"
+ '401':
+ description: Não autorizado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Token de autenticação inválido"
+ '404':
+ description: Colaborador não encontrado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Colaborador não encontrado"
+ '500':
+ description: Erro interno do servidor
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Erro ao processar a solicitação"
+ components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
\ No newline at end of file
diff --git a/docs/reports/delete.yml b/docs/reports/delete.yml
new file mode 100644
index 0000000..1ce4faf
--- /dev/null
+++ b/docs/reports/delete.yml
@@ -0,0 +1,60 @@
+ tags:
+ - Relatórios de Horários
+ summary: Excluir relatório de ponto
+ description: Remove um relatório de horário específico
+ security:
+ - bearerAuth: []
+ parameters:
+ - in: path
+ name: report_id
+ required: true
+ schema:
+ type: integer
+ description: ID do relatório a ser excluído
+ responses:
+ '200':
+ description: Relatório excluído com sucesso
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ message:
+ type: string
+ example: "Relatório deletado com sucesso"
+ '401':
+ description: Não autorizado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Token de autenticação inválido"
+ '404':
+ description: Relatório não encontrado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Relatório não encontrado"
+ '500':
+ description: Erro interno do servidor
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Erro ao processar a solicitação"
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
\ No newline at end of file
diff --git a/docs/reports/list.yml b/docs/reports/list.yml
new file mode 100644
index 0000000..d662a2b
--- /dev/null
+++ b/docs/reports/list.yml
@@ -0,0 +1,107 @@
+ tags:
+ - Relatórios de Horários
+ summary: Consulta de relatórios de ponto eletrônico
+ description: Recupera lista de todos os relatórios de horários dos colaboradores
+ security:
+ - bearerAuth: []
+ parameters:
+ - in: query
+ name: start_date
+ schema:
+ type: string
+ format: date
+ description: Data inicial para filtro de relatórios
+ - in: query
+ name: end_date
+ schema:
+ type: string
+ format: date
+ description: Data final para filtro de relatórios
+ - in: query
+ name: status
+ schema:
+ type: array
+ items:
+ type: string
+ enum: ['Regular', 'Atrasado', 'Saída Antecipada']
+ description: Filtro por status do relatório
+ responses:
+ '200':
+ description: Lista de relatórios recuperada com sucesso
+ content:
+ application/json:
+ schema:
+ type: array
+ items:
+ type: object
+ properties:
+ id:
+ type: integer
+ description: ID do relatório
+ example: 1
+ employee_name:
+ type: string
+ description: Nome do colaborador
+ example: "João Silva"
+ group:
+ type: string
+ description: Grupo/Departamento
+ example: "Drogasil"
+ contract_type:
+ type: string
+ description: Tipo de contrato
+ example: "Interno"
+ date:
+ type: string
+ format: date
+ description: Data do relatório
+ example: "2024-03-27"
+ entry_time:
+ type: string
+ description: Horário de entrada
+ example: "08:05"
+ exit_time:
+ type: string
+ description: Horário de saída
+ example: "17:10"
+ status:
+ type: string
+ description: Status do relatório
+ enum: ['Regular', 'Atrasado', 'Saída Antecipada']
+ example: "Atrasado"
+ '400':
+ description: Requisição inválida
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Parâmetros de filtro inválidos"
+ '401':
+ description: Não autorizado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Token de autenticação inválido"
+ '500':
+ description: Erro interno do servidor
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Erro ao processar a solicitação"
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
\ No newline at end of file
diff --git a/docs/reports/update.yml b/docs/reports/update.yml
new file mode 100644
index 0000000..53e7df5
--- /dev/null
+++ b/docs/reports/update.yml
@@ -0,0 +1,90 @@
+ tags:
+ - Relatórios de Horários
+ summary: Atualizar relatório de ponto
+ description: Atualiza um relatório de horário existente
+ security:
+ - bearerAuth: []
+ parameters:
+ - in: path
+ name: report_id
+ required: true
+ schema:
+ type: integer
+ description: ID do relatório a ser atualizado
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ entry_time:
+ type: string
+ description: Novo horário de entrada
+ example: "08:10"
+ exit_time:
+ type: string
+ description: Novo horário de saída
+ example: "17:15"
+ status:
+ type: string
+ description: Novo status do relatório
+ enum: ['Regular', 'Atrasado', 'Saída Antecipada']
+ example: "Atrasado"
+ responses:
+ '200':
+ description: Relatório atualizado com sucesso
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ message:
+ type: string
+ example: "Relatório atualizado com sucesso"
+ '400':
+ description: Requisição inválida
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Dados de atualização inválidos"
+ '401':
+ description: Não autorizado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Token de autenticação inválido"
+ '404':
+ description: Relatório não encontrado
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Relatório não encontrado"
+ '500':
+ description: Erro interno do servidor
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ error:
+ type: string
+ example: "Erro ao processar a solicitação"
+components:
+ securitySchemes:
+ bearerAuth:
+ type: http
+ scheme: bearer
+ bearerFormat: JWT
\ No newline at end of file
diff --git a/models/reports.py b/models/reports.py
new file mode 100644
index 0000000..b2694aa
--- /dev/null
+++ b/models/reports.py
@@ -0,0 +1,22 @@
+from extensions import db
+from sqlalchemy import Column, Integer, String, DateTime, Float, ForeignKey
+from sqlalchemy.orm import relationship
+from datetime import datetime
+
+class Employee(db.Model):
+ __tablename__ = 'employee'
+
+ id = db.Column(db.Integer, primary_key=True, autoincrement=True)
+ username = db.Column(db.String(80), unique=True, nullable=True)
+ email = db.Column(db.String(120), unique=True, nullable=False)
+ group = db.Column(db.String(80), unique=True, nullable=True)
+ contract_type = db.Column(db.String(80), unique=True, nullable=False)
+ date = db.Column(db.Date, nullable=True)
+ expected_entry_time = db.Column(db.DateTime, nullable=False)
+ expected_exit_time = db.Column(db.DateTime, nullable=False)
+
+ # Relacionamento hierárquico
+ parent_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
+
+ def __repr__(self):
+ return f""
\ No newline at end of file
diff --git a/routes/reports.py b/routes/reports.py
new file mode 100644
index 0000000..c1d5116
--- /dev/null
+++ b/routes/reports.py
@@ -0,0 +1,134 @@
+from flask import Blueprint, request, jsonify
+from flask_jwt_extended import jwt_required
+from flasgger.utils import swag_from
+from models.reports import Employee
+from models.user import db,User
+
+employee_bp = Blueprint('employee', __name__, url_prefix='/employee')
+
+@employee_bp.route('/', methods=['POST'])
+@jwt_required()
+@swag_from('../docs/reports/create.yml')
+def create_employee():
+ name = data.get('name')
+ group = data.get('group')
+ contract = data.get('contract')
+ data = request.get_json()
+ entry = data.get('entry')
+ exit = data.get('exit')
+ status = data.get('status')
+
+ # Cria um novo ServiceRole
+ employee = Employee(
+ id = 1,
+ name = name,
+ group = group,
+ contract = contract,
+ data = data,
+ entry = entry,
+ exit = exit,
+ status = status
+ )
+
+ # Adiciona e comita no banco de dados
+ db.session.add(employee)
+ db.session.commit()
+
+ return jsonify({'message': 'employee criado com sucesso', 'employee_id': employee.id}), 201
+
+@employee_bp.route('/', methods=['GET'])
+@jwt_required()
+@swag_from('../docs/reports/list.yml')
+def list_employee():
+ page = request.args.get('page', 1, type=int)
+ per_page = request.args.get('per_page', 10, type=int)
+
+ pagination = Employee.query.paginate(page=page, per_page=per_page, error_out=False)
+ employee = pagination.items
+
+ return jsonify({
+ 'employee': [
+ {
+ 'id': employee.service_id,
+ 'name': employee.name,
+ 'group': employee.group,
+ 'contract': employee.contract,
+ 'data': employee.data,
+ 'entry': employee.entry,
+ 'exit': employee.exit,
+ 'status': employee.status
+
+ } for employee in employee
+ ],
+ 'total': pagination.total,
+ 'page': pagination.page,
+ 'per_page': pagination.per_page
+ }), 200
+
+@employee_bp.route('/', methods=['GET'])
+@jwt_required()
+@swag_from('../docs/reports/get.yml')
+def get_employee(employee_id):
+ employee = Employee.query.get(employee_id)
+ if not employee:
+ return jsonify({'error': 'Relatório não encontrada'}), 404
+
+ return jsonify({
+ 'id': employee.service_id,
+ 'name': employee.name,
+ 'group': employee.group,
+ 'contract': employee.contract,
+ 'data': employee.data,
+ 'entry': employee.entry,
+ 'exit': employee.exit,
+ 'status': employee.status
+ }), 200
+
+@employee_bp.route('/', methods=['PUT'])
+@jwt_required()
+@swag_from('../docs/reports/update.yml')
+def update_employee(employee_id):
+ name = data.get('name')
+ group = data.get('group')
+ contract = data.get('contract')
+ data = request.get_json()
+ entry = data.get('entry')
+ exit = data.get('exit')
+ status = data.get('status')
+
+ employee = Employee.query.get(employee_id)
+ if not employee:
+ return jsonify({'error': 'Relatório não encontrado'}), 404
+
+ # Atualiza os campos fornecidos
+ if name:
+ employee.name = name
+ if group:
+ employee.group = group
+ if contract:
+ employee.contract = contract
+ if data:
+ employee.data = data
+ if entry:
+ employee.entry = entry
+ if exit:
+ employee.name = exit
+ if status:
+ employee.name = status
+
+ db.session.commit()
+
+ return jsonify({'message': 'Relatório atualizado com sucesso', 'employee_id': employee.id}), 200
+
+@employee_bp.route('/', methods=['DELETE'])
+@jwt_required()
+@swag_from('../docs/reports/delete.yml')
+def delete_employee(employee_id):
+ employee = Employee.query.get(employee_id)
+ if not employee:
+ return jsonify({'error': 'Relatório não encontrado'}), 404
+
+ db.session.delete(employee)
+ db.session.commit()
+
+ return jsonify({'message': 'Relatório deletado com sucesso'}), 200
\ No newline at end of file
diff --git a/seeds/reports_seed.py b/seeds/reports_seed.py
new file mode 100644
index 0000000..5fa4ef0
--- /dev/null
+++ b/seeds/reports_seed.py
@@ -0,0 +1,96 @@
+from extensions import db
+from models.reports import Employee
+
+def reports_seed():
+ employee = [
+ # Usuário 1
+ {
+ "parent_id": 1,
+ "name":'João Silva',
+ "group":'Drogasil',
+ "contract_type":'Interno',
+ "expected_entry_time":'08:00',
+ "expected_exit_time":'17:00',
+ "status":'Ausente'
+ },
+ # Usuário 2
+ {
+ "parent_id": 2,
+ "name":'João Silva',
+ "group":'Drogasil',
+ "contract_type":'Interno',
+ "expected_entry_time":'08:00',
+ "expected_exit_time":'17:00',
+ "status":'Ausente'
+ },
+
+ # Usuário 3
+ {
+ "parent_id": 3,
+ "name":'João Silva',
+ "group":'Drogasil',
+ "contract_type":'Interno',
+ "expected_entry_time":'08:00',
+ "expected_exit_time":'17:00',
+ "status":'Ausente'
+ },
+ # Usuário 4
+ {
+ "parent_id": 4,
+ "name":'João Silva',
+ "group":'Drogasil',
+ "contract_type":'Interno',
+ "expected_entry_time":'08:00',
+ "expected_exit_time":'17:00',
+ "status":'Ausente'
+ },
+
+ # Usuário 5
+ {
+ "parent_id": 5,
+ "name":'João Silva',
+ "group":'Drogasil',
+ "contract_type":'Interno',
+ "expected_entry_time":'08:00',
+ "expected_exit_time":'17:00',
+ "status":'Ausente'
+ },
+
+ # Usuário 6
+ {
+ "parent_id": 6,
+ "name":'João Silva',
+ "group":'Drogasil',
+ "contract_type":'Interno',
+ "expected_entry_time":'08:00',
+ "expected_exit_time":'17:00',
+ "status":'Ausente'
+ },
+ ]
+ for employee_data in employee:
+ # Verifica se já existe um papel para o service_id e name fornecidos
+ existing_employee = db.session.query(Employee).filter_by(
+ name=employee_data["name"],
+ group=employee_data["group"],
+ contract=employee_data["contract"],
+ data=employee_data["data"],
+ entry=employee_data["entry"],
+ exit=employee_data["exit"],
+ status=employee_data["status"]
+ ).first()
+
+ # Se não existe, cria o novo papel
+ if not existing_employee:
+ employee = Employee(
+ name=employee_data["name"],
+ group=employee_data["group"],
+ contract=employee_data["contract"],
+ data=employee_data["data"],
+ entry=employee_data["entry"],
+ exit=employee_data["exit"],
+ status=employee_data["status"]
+ )
+ db.session.add(employee)
+
+ # Commit a transação para salvar os dados no banco
+ db.session.commit()
diff --git a/seeds/run_seed.py b/seeds/run_seed.py
index 3a36eff..9b8c6ae 100644
--- a/seeds/run_seed.py
+++ b/seeds/run_seed.py
@@ -12,6 +12,7 @@ from .user_service_roles import seed_user_service_roles
from .services_seed import seed_services
from .camera_seed import seed_cameras
from .ambiente_seed import seed_ambiente
+from .reports_seed import reports_seed
from extensions import db
@@ -31,6 +32,7 @@ def run_all_seeds():
seed_licenses()
seed_cameras()
seed_ambiente()
+ reports_seed()
print("Seeds executados com sucesso!")
except Exception as e:
db.session.rollback()
diff --git a/seeds/service_roles.py b/seeds/service_roles.py
index 8cad372..d32a2a8 100644
--- a/seeds/service_roles.py
+++ b/seeds/service_roles.py
@@ -58,20 +58,6 @@ def seed_service_roles():
"name": "Admin",
"description": "Coordenador de plataforma."
},
-
- # Disciplinas Role: Coordenador de Disciplinas
- #{
- # "service_id": 1,
- # "name": "Disciplinas",
- # "description": "Coordenador de disciplinas com acesso para atribuir professores às disciplinas e controlar o conteúdo programático de cada matéria."
- #},
-
- # Professores Role: Professor
- #{
- # "service_id": 1,
- # "name": "Professores",
- # "description": "Professor com acesso para lecionar disciplinas, criar e corrigir provas e acompanhar o desempenho dos alunos nas suas matérias."
- #},
]
# Verificar se o role já existe antes de criar