Compare commits

..

2 Commits
ocr ... main

Author SHA1 Message Date
67fae641a3 Models Asafe 2025-05-22 20:23:14 -04:00
9e9d8958c1 Template MVC básico com Jinja 2025-04-03 11:59:32 -04:00
15 changed files with 246 additions and 211 deletions

View File

@ -1,4 +1,25 @@
from flask import Flask from flask import Flask
from app.database import db
# Importação dos blueprints
from app.routes.usuario import usuarios_bp
from app.routes.organizacao import organizacoes_bp
from app.routes.documentos import documentos_bp
def create_app():
app = Flask(__name__)
app.config.from_object("config")
# Inicializa a extensão SQLAlchemy
db.init_app(app)
# Registra os blueprints
app.register_blueprint(usuarios_bp)
app.register_blueprint(organizacoes_bp)
app.register_blueprint(documentos_bp)
return app
from flask import Flask
from flasgger import Swagger from flasgger import Swagger
from app.database import db from app.database import db
from app.models.user import User from app.models.user import User

View File

@ -1,49 +0,0 @@
from flask import Flask, render_template, request, redirect, url_for, session, flash
import os
app = Flask(__name__)
app.secret_key = 'segredo-super-seguro'
# Rota de login
@app.route('/', methods=['GET', 'POST'])
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
email = request.form['email']
password = request.form['password']
if email == 'admin@admin.com' and password == '123456':
session['user'] = 'Admin'
return redirect(url_for('dashboard'))
else:
flash('Credenciais inválidas.', 'danger')
return render_template('login.html')
# Rota protegida
@app.route('/dashboard')
def dashboard():
if 'user' not in session:
return redirect(url_for('login'))
return render_template('dashboard.html', user_name=session['user'])
# Rota de upload
@app.route('/upload', methods=['GET', 'POST'])
def upload():
if 'user' not in session:
return redirect(url_for('login'))
if request.method == 'POST':
file = request.files['file']
if file:
filepath = os.path.join('uploads', file.filename)
os.makedirs('uploads', exist_ok=True)
file.save(filepath)
flash('Arquivo enviado com sucesso!', 'success')
return render_template('upload.html')
# Logout
@app.route('/logout')
def logout():
session.clear()
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True, port=5003)

15
app/models/Documento.py Normal file
View File

@ -0,0 +1,15 @@
# documento.py
from app.database import db
import uuid
class Documento(db.Model): #
__tablename__ = 'documentos'
id = db.Column(db.UUID(as_uuid=True), primary_key=True, default=uuid.uuid4, unique=True, nullable=False)
nome_arquivo = db.Column(db.String, nullable=False)
caminho_arquivo = db.Column(db.String, nullable=False)
usuario_id = db.Column(db.Integer, db.ForeignKey('usuario.id'), nullable=False)
criado_em = db.Column(db.DateTime(timezone=True), server_default=db.func.now(), nullable=False)
usuario = db.relationship('Usuario', back_populates='documentos')

13
app/models/Organizacao.py Normal file
View File

@ -0,0 +1,13 @@
from app.database import db
class Organizacao(db.Model):
__tablename__ = 'organizacao'
id = db.Column(db.Integer, primary_key=True,
autoincrement=True, unique=True)
nome = db.Column(db.String, nullable=False, unique=True)
criado_em = db.Column(db.DateTime(timezone=True),
server_default=db.func.now(), nullable=False)
usuarios = db.relationship('Usuario', back_populates='organizacao')

24
app/models/usuario.py Normal file
View File

@ -0,0 +1,24 @@
# usuario.py
from app.database import db
class Usuario(db.Model):
__tablename__ = 'usuario'
id = db.Column(db.Integer, primary_key=True,
autoincrement=True, unique=True)
nome = db.Column(db.String, nullable=False)
email = db.Column(db.String, nullable=False, unique=True)
senha_hash = db.Column(db.String, nullable=False)
organizacao_id = db.Column(db.Integer, db.ForeignKey(
'organizacao.id'), nullable=False)
criado_em = db.Column(db.DateTime(timezone=True),
server_default=db.func.now(), nullable=False)
atualizado_em = db.Column(db.DateTime(
timezone=True), onupdate=db.func.now())
organizacao = db.relationship('Organizacao', back_populates='usuarios')
documentos = db.relationship('Documento', back_populates='usuario')
def __repr__(self):
return f'<Usuario {self.nome}>'

44
app/routes/documentos.py Normal file
View File

@ -0,0 +1,44 @@
from flask import Blueprint, request, jsonify
from app.database import db
from app.models import Documento
documentos_bp = Blueprint('documentos', __name__, url_prefix='/documentos')
@documentos_bp.route('/', methods=['GET'])
def get_documentos():
documentos = Documento.query.all()
documentos_data = [
{
'id': str(doc.id),
'nome_arquivo': doc.nome_arquivo,
'caminho_arquivo': doc.caminho_arquivo,
'usuario_id': doc.usuario_id,
'criado_em': doc.criado_em
}
for doc in documentos
]
return jsonify(documentos_data), 200
@documentos_bp.route('/', methods=['POST'])
def create_documento():
data = request.get_json()
if not all(k in data for k in ('nome_arquivo', 'caminho_arquivo', 'usuario_id')):
return jsonify({'error': 'Campos obrigatórios ausentes'}), 400
try:
novo_documento = Documento(
nome_arquivo=data['nome_arquivo'],
caminho_arquivo=data['caminho_arquivo'],
usuario_id=data['usuario_id']
)
db.session.add(novo_documento)
db.session.commit()
return jsonify({
'message': 'documento criado com sucesso',
'id': str(novo_documento.id)
}), 201
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500

43
app/routes/organizacao.py Normal file
View File

@ -0,0 +1,43 @@
from flask import Blueprint, request, jsonify
from app.database import db
from app.models import Organizacao # ajuste conforme sua estrutura
organizacoes_bp = Blueprint(
'organizacoes', __name__, url_prefix='/organizacoes')
@organizacoes_bp.route('/', methods=['GET'])
def get_organizacoes():
organizacoes = Organizacao.query.all()
data = [
{
'id': org.id,
'nome': org.nome,
'criado_em': org.criado_em
}
for org in organizacoes
]
return jsonify(data), 200
@organizacoes_bp.route('/', methods=['POST'])
def create_organizacao():
data = request.get_json()
if not data or 'nome' not in data:
return jsonify({'error': 'O campo "nome" é obrigatório'}), 400
if Organizacao.query.filter_by(nome=data['nome']).first():
return jsonify({'error': 'Já existe uma organização com este nome'}), 409
try:
nova_org = Organizacao(nome=data['nome'])
db.session.add(nova_org)
db.session.commit()
return jsonify({
'message': 'Organização criada com sucesso',
'id': nova_org.id
}), 201
except Exception as e:
db.session.rollback()
return jsonify({'error': str(e)}), 500

44
app/routes/usuario.py Normal file
View File

@ -0,0 +1,44 @@
from flask import Blueprint, request, jsonify
from app.database import db
from app.models.usuario import Usuario # nome correto do modelo
usuarios_bp = Blueprint('usuarios', __name__, url_prefix='/usuarios')
@usuarios_bp.route('/', methods=['GET'])
def get_usuarios():
usuarios = Usuario.query.all()
usuarios_data = [
{
'id': usuario.id,
'nome': usuario.nome,
'email': usuario.email,
'organizacao_id': usuario.organizacao_id,
'criado_em': usuario.criado_em,
'atualizado_em': usuario.atualizado_em
}
for usuario in usuarios
]
return jsonify(usuarios_data), 200
@usuarios_bp.route('/', methods=['POST'])
def create_usuario():
data = request.get_json()
if not all(k in data for k in ('nome', 'email', 'senha_hash', 'organizacao_id')):
return jsonify({'error': 'Campos obrigatórios ausentes'}), 400
if Usuario.query.filter_by(email=data['email']).first():
return jsonify({'error': 'E-mail já está em uso'}), 409
novo_usuario = Usuario(
nome=data['nome'],
email=data['email'],
senha_hash=data['senha_hash'],
organizacao_id=data['organizacao_id']
)
db.session.add(novo_usuario)
db.session.commit()
return jsonify({'message': 'Usuário criado com sucesso', 'id': novo_usuario.id}), 201

View File

@ -1,72 +0,0 @@
import os
import uuid
from typing import Dict
from PIL import Image
from pdf2image import convert_from_path
from app.services.correction_service import correct_text
from app.services.engines.tesseract_ocr import ocr_tesseract
from app.services.engines.easyocr_ocr import ocr_easyocr
from app.services.engines.paddleocr_ocr import ocr_paddleocr
# from app.services.engines.mmocr_ocr import ocr_mmocr # Opcional
from app.utils import save_text_file
UPLOAD_DIR = "app/static/uploads"
TEXTS_DIR = "app/static/texts"
def process_document(file_path: str) -> Dict[str, str]:
"""
Processa um arquivo PDF ou imagem, aplica OCR com diferentes engines e salva os textos extraídos.
Retorna os textos por engine e o texto corrigido.
Args:
file_path (str): Caminho do arquivo a ser processado.
Returns:
Dict[str, str]: Dicionário com os textos por engine e o texto corrigido.
"""
try:
if not os.path.exists(file_path):
raise FileNotFoundError(f"Arquivo não encontrado: {file_path}")
filename = os.path.basename(file_path)
base_name = os.path.splitext(filename)[0]
output_folder = os.path.join(TEXTS_DIR, base_name)
os.makedirs(output_folder, exist_ok=True)
# Converte PDF para imagens ou carrega imagem única
images = convert_from_path(file_path) if file_path.lower().endswith(".pdf") else [Image.open(file_path)]
results = {
"tesseract": "",
"easyocr": "",
"paddleocr": "",
# "mmocr": ""
}
# Processa cada página/imagem
for i, image in enumerate(images):
temp_img_path = os.path.join(output_folder, f"page_{i}.png")
image.save(temp_img_path)
results["tesseract"] += ocr_tesseract(image) + "\n"
results["easyocr"] += ocr_easyocr(temp_img_path) + "\n"
results["paddleocr"] += ocr_paddleocr(temp_img_path) + "\n"
# results["mmocr"] += ocr_mmocr(temp_img_path) + "\n"
os.remove(temp_img_path)
# Salva os textos extraídos
for engine, text in results.items():
save_text_file(text, os.path.join(output_folder, f"{engine}.txt"))
# Aplica correção no texto do tesseract
corrected = correct_text(results["tesseract"])
save_text_file(corrected, os.path.join(output_folder, "corrigido.txt"))
results["corrigido"] = corrected
return results
except Exception as e:
print(f"[ERRO] Falha ao processar documento: {e}")
return {"error": str(e)}

View File

@ -1,27 +0,0 @@
body {
background-color: #f8f9fa;
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
}
h3 {
color: #343a40;
}
form {
background: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.1);
}
.navbar-brand {
font-weight: bold;
}
button.btn {
transition: background-color 0.3s ease;
}
button.btn:hover {
opacity: 0.9;
}

View File

@ -1,21 +0,0 @@
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<title>{% block title %}OCR App{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark px-4">
<a class="navbar-brand" href="#">OCR App</a>
</nav>
<div class="container mt-4">
{% block content %}{% endblock %}
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -1,9 +1,13 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="pt">
{% block title %}Dashboard - OCR App{% endblock %} <head>
<meta charset="UTF-8">
{% block content %} <meta name="viewport" content="width=device-width, initial-scale=1.0">
<h3>Bem-vindo(a), {{ user_name }}</h3> <title>Dashboard</title>
<p>Escolha uma ação no menu ou envie um novo documento para OCR.</p> </head>
<a href="/upload" class="btn btn-outline-primary">Enviar novo documento</a> <body>
{% endblock %} <h1>Bem-vindo ao Dashboard!</h1>
<p>Usuário logado: {{ user_email }}</p>
<a href="{{ url_for('login_form.login') }}">Sair</a>
</body>
</html>

View File

@ -1,22 +1,32 @@
{% extends "base.html" %} <!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div class="login-container">
<h2>Login</h2>
{% block title %}Login - OCR App{% endblock %} {% with messages = get_flashed_messages(with_categories=True) %}
{% if messages %}
{% for category, message in messages %}
<div class="alert alert-{{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
{% block content %} <form method="POST">
<div class="row justify-content-center"> <label for="email">E-mail:</label>
<div class="col-md-4"> <input type="email" name="email" required>
<h3 class="mb-4">Login</h3>
<form method="POST" action="/login"> <label for="password">Senha:</label>
<div class="mb-3"> <input type="password" name="password" required>
<label>Email:</label>
<input type="email" name="email" class="form-control" required> <button type="submit">Entrar</button>
</div>
<div class="mb-3">
<label>Senha:</label>
<input type="password" name="password" class="form-control" required>
</div>
<button class="btn btn-primary w-100" type="submit">Entrar</button>
</form> </form>
</div> </div>
</div> </body>
{% endblock %} </html>

View File

@ -1,14 +0,0 @@
{% extends "base.html" %}
{% block title %}Upload de Arquivo - OCR App{% endblock %}
{% block content %}
<h3>Upload de Documento</h3>
<form method="POST" action="/upload" enctype="multipart/form-data">
<div class="mb-3">
<label for="file" class="form-label">Selecionar Arquivo</label>
<input class="form-control" type="file" id="file" name="file" accept=".pdf,.jpg,.png,.jpeg" required>
</div>
<button class="btn btn-success" type="submit">Enviar</button>
</form>
{% endblock %}

2
run.py
View File

@ -10,4 +10,4 @@ if __name__ == "__main__":
create_default_user() create_default_user()
print("Banco de dados criado com sucesso!") print("Banco de dados criado com sucesso!")
app.run(debug=True, port=5003) app.run(debug=True)