Ultralearning / app.py
DHEIVER's picture
Create app.py
1b63e6c verified
raw
history blame
14.4 kB
# config.py
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
DEBUG = os.getenv('DEBUG', 'False').lower() == 'true'
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
MODELS_CACHE_DIR = os.getenv('MODELS_CACHE_DIR', './models')
HISTORY_FILE = os.getenv('HISTORY_FILE', 'learning_path_history.json')
MAX_AUDIO_LENGTH = int(os.getenv('MAX_AUDIO_LENGTH', '600')) # segundos
MAX_TEXT_LENGTH = int(os.getenv('MAX_TEXT_LENGTH', '1000'))
SUPPORTED_AUDIO_FORMATS = ['.wav', '.mp3', '.ogg', '.flac']
# Configurações de visualização
MAX_TOPICS = int(os.getenv('MAX_TOPICS', '10'))
MAX_SUBTOPICS = int(os.getenv('MAX_SUBTOPICS', '5'))
FIGURE_DPI = int(os.getenv('FIGURE_DPI', '300'))
# Configurações de modelo
MODEL_TRANSCRIBER = os.getenv('MODEL_TRANSCRIBER', 'openai/whisper-base')
MODEL_GENERATOR = os.getenv('MODEL_GENERATOR', 'gpt2')
# Configurações de retry
MAX_RETRIES = int(os.getenv('MAX_RETRIES', '3'))
RETRY_DELAY = int(os.getenv('RETRY_DELAY', '1'))
# utils.py
import logging
import json
from typing import Dict, Any, Optional, List, Tuple
import os
from config import Config
class Utils:
@staticmethod
def setup_logging() -> logging.Logger:
logger = logging.getLogger("LearningPathGenerator")
logger.setLevel(getattr(logging, Config.LOG_LEVEL))
# Configuração do arquivo de log
handler = logging.FileHandler("app.log")
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
@staticmethod
def save_json(data: Dict[str, Any], filename: str) -> bool:
try:
with open(filename, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return True
except Exception as e:
logging.error(f"Erro ao salvar JSON: {str(e)}")
return False
@staticmethod
def load_json(filename: str) -> Optional[Dict[str, Any]]:
try:
with open(filename, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
logging.error(f"Erro ao carregar JSON: {str(e)}")
return None
# validators.py
import os
import soundfile as sf
from typing import Tuple, Optional
from config import Config
class Validators:
@staticmethod
def validate_audio_file(file_path: str) -> Tuple[bool, Optional[str]]:
try:
if not os.path.exists(file_path):
return False, "Arquivo não encontrado"
# Verifica extensão
ext = os.path.splitext(file_path)[1].lower()
if ext not in Config.SUPPORTED_AUDIO_FORMATS:
return False, f"Formato não suportado. Use: {Config.SUPPORTED_AUDIO_FORMATS}"
# Verifica conteúdo
data, samplerate = sf.read(file_path)
duration = len(data) / samplerate
if duration > Config.MAX_AUDIO_LENGTH:
return False, f"Áudio muito longo. Máximo: {Config.MAX_AUDIO_LENGTH}s"
return True, None
except Exception as e:
return False, f"Erro na validação: {str(e)}"
@staticmethod
def validate_path_name(name: str) -> Tuple[bool, Optional[str]]:
if not name:
return True, None
if len(name) > 100:
return False, "Nome muito longo. Máximo: 100 caracteres"
if not all(c.isprintable() for c in name):
return False, "Nome contém caracteres inválidos"
return True, None
# models.py
from transformers import pipeline
import torch
from typing import Dict, Optional
import logging
from config import Config
class ModelManager:
def __init__(self):
self.logger = logging.getLogger("ModelManager")
self.models: Dict[str, Any] = {}
self._initialize_models()
def _initialize_models(self):
try:
# Verifica GPU
device = 0 if torch.cuda.is_available() else -1
self.models["transcriber"] = pipeline(
"automatic-speech-recognition",
model=Config.MODEL_TRANSCRIBER,
device=device
)
self.models["generator"] = pipeline(
"text-generation",
model=Config.MODEL_GENERATOR,
device=device
)
except Exception as e:
self.logger.error(f"Erro ao inicializar modelos: {str(e)}")
raise
def get_model(self, name: str):
return self.models.get(name)
# visualization.py
import networkx as nx
import matplotlib.pyplot as plt
from io import BytesIO
import base64
from typing import Dict, List
from config import Config
class Visualizer:
@staticmethod
def create_mind_map(topics: List[str],
subtopics: Dict[str, List[str]]) -> Optional[str]:
try:
plt.figure(figsize=(15, 10), dpi=Config.FIGURE_DPI)
G = nx.DiGraph()
# Adiciona nós e arestas
for i, topic in enumerate(topics[:Config.MAX_TOPICS]):
G.add_node(topic, level=i)
if topic in subtopics:
for subtopic in subtopics[topic][:Config.MAX_SUBTOPICS]:
subtopic_name = f"{topic}:\n{subtopic}"
G.add_node(subtopic_name, level=i)
G.add_edge(topic, subtopic_name)
if i > 0:
G.add_edge(topics[i-1], topic)
# Layout e estilo
pos = nx.spring_layout(G, k=2, iterations=50)
# Desenha nós principais
nx.draw_networkx_nodes(G, pos,
nodelist=[n for n in G.nodes() if ":" not in n],
node_color='lightblue',
node_size=3000)
# Desenha subtópicos
nx.draw_networkx_nodes(G, pos,
nodelist=[n for n in G.nodes() if ":" in n],
node_color='lightgreen',
node_size=2000)
# Desenha arestas e labels
nx.draw_networkx_edges(G, pos, edge_color='gray', arrows=True)
nx.draw_networkx_labels(G, pos, font_size=8, font_weight='bold')
# Salva imagem
buf = BytesIO()
plt.savefig(buf, format='png', bbox_inches='tight')
buf.seek(0)
plt.close()
return f"data:image/png;base64,{base64.b64encode(buf.getvalue()).decode()}"
except Exception as e:
logging.error(f"Erro na visualização: {str(e)}")
return None
# main.py
import gradio as gr
from typing import Dict, Any
import sys
import logging
from config import Config
from utils import Utils
from validators import Validators
from models import ModelManager
from visualization import Visualizer
class LearningPathGenerator:
def __init__(self):
self.logger = Utils.setup_logging()
self.model_manager = ModelManager()
self.history_file = Config.HISTORY_FILE
# Inicialização do histórico
if not os.path.exists(self.history_file):
Utils.save_json([], self.history_file)
def process_audio(self,
audio_path: str,
path_name: str = "",
difficulty: str = "intermediate",
include_resources: bool = True) -> Dict[str, Any]:
try:
# Validação do áudio
valid_audio, audio_error = Validators.validate_audio_file(audio_path)
if not valid_audio:
return self._error_response(audio_error)
# Validação do nome
valid_name, name_error = Validators.validate_path_name(path_name)
if not valid_name:
return self._error_response(name_error)
# Transcrição
transcriber = self.model_manager.get_model("transcriber")
transcription = transcriber(audio_path)["text"]
# Geração da análise
generator = self.model_manager.get_model("generator")
analysis = self._generate_analysis(generator, transcription, difficulty)
# Criação do mapa mental
topics, subtopics = self._extract_topics(analysis)
mind_map = Visualizer.create_mind_map(topics, subtopics)
# Salva no histórico
if path_name:
self._save_to_history(transcription, analysis, path_name)
return {
"transcription": transcription,
"analysis": analysis,
"mind_map": mind_map
}
except Exception as e:
self.logger.error(f"Erro no processamento: {str(e)}")
return self._error_response(str(e))
def create_interface(self):
with gr.Blocks(theme=gr.themes.Soft()) as app:
gr.Markdown("""
# 🎓 Gerador de Trilha de Aprendizado
Carregue um arquivo de áudio descrevendo seus objetivos de aprendizado
e receba uma trilha personalizada com recursos!
""")
with gr.Tab("Gerar Trilha"):
with gr.Row():
with gr.Column(scale=2):
audio_input = gr.Audio(
type="filepath",
label="Upload do Áudio",
description="Grave ou faça upload de um áudio descrevendo seus objetivos"
)
with gr.Row():
path_name = gr.Textbox(
label="Nome da Trilha",
placeholder="Dê um nome para sua trilha (opcional)"
)
difficulty = gr.Dropdown(
choices=["iniciante", "intermediário", "avançado"],
value="intermediário",
label="Nível de Dificuldade"
)
with gr.Row():
include_resources = gr.Checkbox(
label="Incluir Recursos Recomendados",
value=True
)
process_btn = gr.Button(
"Gerar Trilha de Aprendizado",
variant="primary"
)
with gr.Row():
text_output = gr.Textbox(
label="Transcrição do Áudio",
lines=4
)
with gr.Row():
analysis_output = gr.Textbox(
label="Análise e Trilha de Aprendizado",
lines=10
)
with gr.Row():
mind_map_output = gr.Image(
label="Mapa Mental da Trilha",
elem_id="mind_map"
)
with gr.Tab("Histórico"):
gr.Markdown("Trilhas Anteriores")
history_table = gr.Dataframe(
headers=["Data", "Nome", "Transcrição", "Análise"],
label="Histórico de Trilhas"
)
refresh_btn = gr.Button("Atualizar Histórico")
# Event handlers
process_btn.click(
fn=self.process_audio,
inputs=[audio_input, path_name, difficulty, include_resources],
outputs={
"transcription": text_output,
"analysis": analysis_output,
"mind_map": mind_map_output
}
)
refresh_btn.click(
fn=self._load_history,
outputs=[history_table]
)
return app
def _generate_analysis(self,
generator,
text: str,
difficulty: str) -> str:
prompt = f"""
Com base no seguinte texto, crie uma trilha de aprendizado detalhada
para nível {difficulty}:
{text[:Config.MAX_TEXT_LENGTH]}
Trilha de aprendizado:
"""
response = generator(
prompt,
max_length=300,
num_return_sequences=1
)[0]["generated_text"]
if include_resources:
response += self._generate_resources()
return response
def _generate_resources(self) -> str:
return """
Recursos Recomendados:
1. Livros:
- "Guia Essencial do Tema"
- "Técnicas Avançadas"
2. Cursos Online:
- Coursera: "Especialização no Tema"
- edX: "Curso Avançado"
3. Recursos Práticos:
- Tutoriais interativos
- Exercícios práticos
- Projetos do mundo real
"""
def _error_response(self, error_msg: str) -> Dict[str, Any]:
return {
"transcription": f"Erro: {error_msg}",
"analysis": "Não foi possível gerar a análise devido a um erro.",
"mind_map": None
}
# Execução do app
if __name__ == "__main__":
try:
generator = LearningPathGenerator()
app = generator.create_interface()
app.launch(debug=Config.DEBUG)
except Exception as e: