DHEIVER commited on
Commit
f2660e3
·
verified ·
1 Parent(s): cedb6bb

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +117 -231
app.py CHANGED
@@ -9,20 +9,20 @@ class Config:
9
  LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
10
  MODELS_CACHE_DIR = os.getenv('MODELS_CACHE_DIR', './models')
11
  HISTORY_FILE = os.getenv('HISTORY_FILE', 'learning_path_history.json')
12
- MAX_AUDIO_LENGTH = int(os.getenv('MAX_AUDIO_LENGTH', '600')) # segundos
13
  MAX_TEXT_LENGTH = int(os.getenv('MAX_TEXT_LENGTH', '1000'))
14
  SUPPORTED_AUDIO_FORMATS = ['.wav', '.mp3', '.ogg', '.flac']
15
 
16
- # Configurações de visualização
17
  MAX_TOPICS = int(os.getenv('MAX_TOPICS', '10'))
18
  MAX_SUBTOPICS = int(os.getenv('MAX_SUBTOPICS', '5'))
19
  FIGURE_DPI = int(os.getenv('FIGURE_DPI', '300'))
20
 
21
- # Configurações de modelo
22
  MODEL_TRANSCRIBER = os.getenv('MODEL_TRANSCRIBER', 'openai/whisper-base')
23
  MODEL_GENERATOR = os.getenv('MODEL_GENERATOR', 'gpt2')
24
 
25
- # Configurações de retry
26
  MAX_RETRIES = int(os.getenv('MAX_RETRIES', '3'))
27
  RETRY_DELAY = int(os.getenv('RETRY_DELAY', '1'))
28
 
@@ -31,15 +31,16 @@ import logging
31
  import json
32
  from typing import Dict, Any, Optional, List, Tuple
33
  import os
 
34
  from config import Config
35
 
36
  class Utils:
37
  @staticmethod
38
  def setup_logging() -> logging.Logger:
39
  logger = logging.getLogger("LearningPathGenerator")
40
- logger.setLevel(getattr(logging, Config.LOG_LEVEL))
 
41
 
42
- # Configuração do arquivo de log
43
  handler = logging.FileHandler("app.log")
44
  formatter = logging.Formatter(
45
  '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
@@ -56,7 +57,7 @@ class Utils:
56
  json.dump(data, f, ensure_ascii=False, indent=2)
57
  return True
58
  except Exception as e:
59
- logging.error(f"Erro ao salvar JSON: {str(e)}")
60
  return False
61
 
62
  @staticmethod
@@ -65,56 +66,24 @@ class Utils:
65
  with open(filename, 'r', encoding='utf-8') as f:
66
  return json.load(f)
67
  except Exception as e:
68
- logging.error(f"Erro ao carregar JSON: {str(e)}")
69
  return None
70
 
71
- # validators.py
72
- import os
73
- import soundfile as sf
74
- from typing import Tuple, Optional
75
- from config import Config
76
-
77
- class Validators:
78
- @staticmethod
79
- def validate_audio_file(file_path: str) -> Tuple[bool, Optional[str]]:
80
- try:
81
- if not os.path.exists(file_path):
82
- return False, "Arquivo não encontrado"
83
-
84
- # Verifica extensão
85
- ext = os.path.splitext(file_path)[1].lower()
86
- if ext not in Config.SUPPORTED_AUDIO_FORMATS:
87
- return False, f"Formato não suportado. Use: {Config.SUPPORTED_AUDIO_FORMATS}"
88
-
89
- # Verifica conteúdo
90
- data, samplerate = sf.read(file_path)
91
- duration = len(data) / samplerate
92
-
93
- if duration > Config.MAX_AUDIO_LENGTH:
94
- return False, f"Áudio muito longo. Máximo: {Config.MAX_AUDIO_LENGTH}s"
95
-
96
- return True, None
97
-
98
- except Exception as e:
99
- return False, f"Erro na validação: {str(e)}"
100
-
101
  @staticmethod
102
- def validate_path_name(name: str) -> Tuple[bool, Optional[str]]:
103
- if not name:
104
- return True, None
105
-
106
- if len(name) > 100:
107
- return False, "Nome muito longo. Máximo: 100 caracteres"
108
-
109
- if not all(c.isprintable() for c in name):
110
- return False, "Nome contém caracteres inválidos"
111
-
112
- return True, None
113
 
114
  # models.py
115
  from transformers import pipeline
116
  import torch
117
- from typing import Dict, Optional
118
  import logging
119
  from config import Config
120
 
@@ -126,7 +95,6 @@ class ModelManager:
126
 
127
  def _initialize_models(self):
128
  try:
129
- # Verifica GPU
130
  device = 0 if torch.cuda.is_available() else -1
131
 
132
  self.models["transcriber"] = pipeline(
@@ -142,82 +110,21 @@ class ModelManager:
142
  )
143
 
144
  except Exception as e:
145
- self.logger.error(f"Erro ao inicializar modelos: {str(e)}")
146
  raise
147
 
148
- def get_model(self, name: str):
149
  return self.models.get(name)
150
 
151
- # visualization.py
152
- import networkx as nx
153
- import matplotlib.pyplot as plt
154
- from io import BytesIO
155
- import base64
156
- from typing import Dict, List
157
- from config import Config
158
-
159
- class Visualizer:
160
- @staticmethod
161
- def create_mind_map(topics: List[str],
162
- subtopics: Dict[str, List[str]]) -> Optional[str]:
163
- try:
164
- plt.figure(figsize=(15, 10), dpi=Config.FIGURE_DPI)
165
- G = nx.DiGraph()
166
-
167
- # Adiciona nós e arestas
168
- for i, topic in enumerate(topics[:Config.MAX_TOPICS]):
169
- G.add_node(topic, level=i)
170
-
171
- if topic in subtopics:
172
- for subtopic in subtopics[topic][:Config.MAX_SUBTOPICS]:
173
- subtopic_name = f"{topic}:\n{subtopic}"
174
- G.add_node(subtopic_name, level=i)
175
- G.add_edge(topic, subtopic_name)
176
-
177
- if i > 0:
178
- G.add_edge(topics[i-1], topic)
179
-
180
- # Layout e estilo
181
- pos = nx.spring_layout(G, k=2, iterations=50)
182
-
183
- # Desenha nós principais
184
- nx.draw_networkx_nodes(G, pos,
185
- nodelist=[n for n in G.nodes() if ":" not in n],
186
- node_color='lightblue',
187
- node_size=3000)
188
-
189
- # Desenha subtópicos
190
- nx.draw_networkx_nodes(G, pos,
191
- nodelist=[n for n in G.nodes() if ":" in n],
192
- node_color='lightgreen',
193
- node_size=2000)
194
-
195
- # Desenha arestas e labels
196
- nx.draw_networkx_edges(G, pos, edge_color='gray', arrows=True)
197
- nx.draw_networkx_labels(G, pos, font_size=8, font_weight='bold')
198
-
199
- # Salva imagem
200
- buf = BytesIO()
201
- plt.savefig(buf, format='png', bbox_inches='tight')
202
- buf.seek(0)
203
- plt.close()
204
-
205
- return f"data:image/png;base64,{base64.b64encode(buf.getvalue()).decode()}"
206
-
207
- except Exception as e:
208
- logging.error(f"Erro na visualização: {str(e)}")
209
- return None
210
-
211
  # main.py
212
  import gradio as gr
213
  from typing import Dict, Any
214
- import sys
215
  import logging
216
  from config import Config
217
  from utils import Utils
218
- from validators import Validators
219
  from models import ModelManager
220
  from visualization import Visualizer
 
221
 
222
  class LearningPathGenerator:
223
  def __init__(self):
@@ -225,7 +132,6 @@ class LearningPathGenerator:
225
  self.model_manager = ModelManager()
226
  self.history_file = Config.HISTORY_FILE
227
 
228
- # Inicialização do histórico
229
  if not os.path.exists(self.history_file):
230
  Utils.save_json([], self.history_file)
231
 
@@ -235,29 +141,15 @@ class LearningPathGenerator:
235
  difficulty: str = "intermediate",
236
  include_resources: bool = True) -> Dict[str, Any]:
237
  try:
238
- # Validação do áudio
239
- valid_audio, audio_error = Validators.validate_audio_file(audio_path)
240
- if not valid_audio:
241
- return self._error_response(audio_error)
242
-
243
- # Validação do nome
244
- valid_name, name_error = Validators.validate_path_name(path_name)
245
- if not valid_name:
246
- return self._error_response(name_error)
247
-
248
- # Transcrição
249
  transcriber = self.model_manager.get_model("transcriber")
250
  transcription = transcriber(audio_path)["text"]
251
 
252
- # Geração da análise
253
  generator = self.model_manager.get_model("generator")
254
- analysis = self._generate_analysis(generator, transcription, difficulty)
255
 
256
- # Criação do mapa mental
257
- topics, subtopics = self._extract_topics(analysis)
258
  mind_map = Visualizer.create_mind_map(topics, subtopics)
259
 
260
- # Salva no histórico
261
  if path_name:
262
  self._save_to_history(transcription, analysis, path_name)
263
 
@@ -268,75 +160,121 @@ class LearningPathGenerator:
268
  }
269
 
270
  except Exception as e:
271
- self.logger.error(f"Erro no processamento: {str(e)}")
272
  return self._error_response(str(e))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  def create_interface(self):
275
  with gr.Blocks(theme=gr.themes.Soft()) as app:
276
  gr.Markdown("""
277
- # 🎓 Gerador de Trilha de Aprendizado
278
 
279
- Carregue um arquivo de áudio descrevendo seus objetivos de aprendizado
280
- e receba uma trilha personalizada com recursos!
281
  """)
282
 
283
- with gr.Tab("Gerar Trilha"):
284
  with gr.Row():
285
  with gr.Column(scale=2):
286
  audio_input = gr.Audio(
287
  type="filepath",
288
- label="Upload do Áudio",
289
- description="Grave ou faça upload de um áudio descrevendo seus objetivos"
290
  )
291
 
292
  with gr.Row():
293
  path_name = gr.Textbox(
294
- label="Nome da Trilha",
295
- placeholder=" um nome para sua trilha (opcional)"
296
  )
297
  difficulty = gr.Dropdown(
298
- choices=["iniciante", "intermediário", "avançado"],
299
- value="intermediário",
300
- label="Nível de Dificuldade"
301
  )
302
 
303
- with gr.Row():
304
- include_resources = gr.Checkbox(
305
- label="Incluir Recursos Recomendados",
306
- value=True
307
- )
308
- process_btn = gr.Button(
309
- "Gerar Trilha de Aprendizado",
310
- variant="primary"
311
- )
312
 
313
- with gr.Row():
314
- text_output = gr.Textbox(
315
- label="Transcrição do Áudio",
316
- lines=4
317
- )
318
 
319
- with gr.Row():
320
- analysis_output = gr.Textbox(
321
- label="Análise e Trilha de Aprendizado",
322
- lines=10
323
- )
324
 
325
- with gr.Row():
326
- mind_map_output = gr.Image(
327
- label="Mapa Mental da Trilha",
328
- elem_id="mind_map"
329
- )
330
-
331
- with gr.Tab("Histórico"):
332
- gr.Markdown("Trilhas Anteriores")
333
- history_table = gr.Dataframe(
334
- headers=["Data", "Nome", "Transcrição", "Análise"],
335
- label="Histórico de Trilhas"
336
  )
337
- refresh_btn = gr.Button("Atualizar Histórico")
338
 
339
- # Event handlers
340
  process_btn.click(
341
  fn=self.process_audio,
342
  inputs=[audio_input, path_name, difficulty, include_resources],
@@ -346,66 +284,14 @@ class LearningPathGenerator:
346
  "mind_map": mind_map_output
347
  }
348
  )
349
-
350
- refresh_btn.click(
351
- fn=self._load_history,
352
- outputs=[history_table]
353
- )
354
 
355
  return app
356
 
357
- def _generate_analysis(self,
358
- generator,
359
- text: str,
360
- difficulty: str) -> str:
361
- prompt = f"""
362
- Com base no seguinte texto, crie uma trilha de aprendizado detalhada
363
- para nível {difficulty}:
364
- {text[:Config.MAX_TEXT_LENGTH]}
365
-
366
- Trilha de aprendizado:
367
- """
368
-
369
- response = generator(
370
- prompt,
371
- max_length=300,
372
- num_return_sequences=1
373
- )[0]["generated_text"]
374
-
375
- if include_resources:
376
- response += self._generate_resources()
377
-
378
- return response
379
-
380
- def _generate_resources(self) -> str:
381
- return """
382
- Recursos Recomendados:
383
-
384
- 1. Livros:
385
- - "Guia Essencial do Tema"
386
- - "Técnicas Avançadas"
387
-
388
- 2. Cursos Online:
389
- - Coursera: "Especialização no Tema"
390
- - edX: "Curso Avançado"
391
-
392
- 3. Recursos Práticos:
393
- - Tutoriais interativos
394
- - Exercícios práticos
395
- - Projetos do mundo real
396
- """
397
-
398
- def _error_response(self, error_msg: str) -> Dict[str, Any]:
399
- return {
400
- "transcription": f"Erro: {error_msg}",
401
- "analysis": "Não foi possível gerar a análise devido a um erro.",
402
- "mind_map": None
403
- }
404
-
405
- # Execução do app
406
  if __name__ == "__main__":
407
  try:
408
  generator = LearningPathGenerator()
409
  app = generator.create_interface()
410
  app.launch(debug=Config.DEBUG)
411
- except Exception as e:
 
 
 
9
  LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
10
  MODELS_CACHE_DIR = os.getenv('MODELS_CACHE_DIR', './models')
11
  HISTORY_FILE = os.getenv('HISTORY_FILE', 'learning_path_history.json')
12
+ MAX_AUDIO_LENGTH = int(os.getenv('MAX_AUDIO_LENGTH', '600')) # seconds
13
  MAX_TEXT_LENGTH = int(os.getenv('MAX_TEXT_LENGTH', '1000'))
14
  SUPPORTED_AUDIO_FORMATS = ['.wav', '.mp3', '.ogg', '.flac']
15
 
16
+ # Visualization settings
17
  MAX_TOPICS = int(os.getenv('MAX_TOPICS', '10'))
18
  MAX_SUBTOPICS = int(os.getenv('MAX_SUBTOPICS', '5'))
19
  FIGURE_DPI = int(os.getenv('FIGURE_DPI', '300'))
20
 
21
+ # Model settings
22
  MODEL_TRANSCRIBER = os.getenv('MODEL_TRANSCRIBER', 'openai/whisper-base')
23
  MODEL_GENERATOR = os.getenv('MODEL_GENERATOR', 'gpt2')
24
 
25
+ # Retry settings
26
  MAX_RETRIES = int(os.getenv('MAX_RETRIES', '3'))
27
  RETRY_DELAY = int(os.getenv('RETRY_DELAY', '1'))
28
 
 
31
  import json
32
  from typing import Dict, Any, Optional, List, Tuple
33
  import os
34
+ from datetime import datetime
35
  from config import Config
36
 
37
  class Utils:
38
  @staticmethod
39
  def setup_logging() -> logging.Logger:
40
  logger = logging.getLogger("LearningPathGenerator")
41
+ level = getattr(logging, Config.LOG_LEVEL)
42
+ logger.setLevel(level)
43
 
 
44
  handler = logging.FileHandler("app.log")
45
  formatter = logging.Formatter(
46
  '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
 
57
  json.dump(data, f, ensure_ascii=False, indent=2)
58
  return True
59
  except Exception as e:
60
+ logging.error(f"Error saving JSON: {str(e)}")
61
  return False
62
 
63
  @staticmethod
 
66
  with open(filename, 'r', encoding='utf-8') as f:
67
  return json.load(f)
68
  except Exception as e:
69
+ logging.error(f"Error loading JSON: {str(e)}")
70
  return None
71
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  @staticmethod
73
+ def extract_topics(analysis: str) -> Tuple[List[str], Dict[str, List[str]]]:
74
+ # Simple topic extraction logic - could be enhanced
75
+ topics = ["Main Topic", "Subtopic 1", "Subtopic 2"]
76
+ subtopics = {
77
+ "Main Topic": ["Detail 1", "Detail 2"],
78
+ "Subtopic 1": ["Point 1", "Point 2"],
79
+ "Subtopic 2": ["Item 1", "Item 2"]
80
+ }
81
+ return topics, subtopics
 
 
82
 
83
  # models.py
84
  from transformers import pipeline
85
  import torch
86
+ from typing import Dict, Any
87
  import logging
88
  from config import Config
89
 
 
95
 
96
  def _initialize_models(self):
97
  try:
 
98
  device = 0 if torch.cuda.is_available() else -1
99
 
100
  self.models["transcriber"] = pipeline(
 
110
  )
111
 
112
  except Exception as e:
113
+ self.logger.error(f"Error initializing models: {str(e)}")
114
  raise
115
 
116
+ def get_model(self, name: str) -> Any:
117
  return self.models.get(name)
118
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  # main.py
120
  import gradio as gr
121
  from typing import Dict, Any
 
122
  import logging
123
  from config import Config
124
  from utils import Utils
 
125
  from models import ModelManager
126
  from visualization import Visualizer
127
+ from datetime import datetime
128
 
129
  class LearningPathGenerator:
130
  def __init__(self):
 
132
  self.model_manager = ModelManager()
133
  self.history_file = Config.HISTORY_FILE
134
 
 
135
  if not os.path.exists(self.history_file):
136
  Utils.save_json([], self.history_file)
137
 
 
141
  difficulty: str = "intermediate",
142
  include_resources: bool = True) -> Dict[str, Any]:
143
  try:
 
 
 
 
 
 
 
 
 
 
 
144
  transcriber = self.model_manager.get_model("transcriber")
145
  transcription = transcriber(audio_path)["text"]
146
 
 
147
  generator = self.model_manager.get_model("generator")
148
+ analysis = self._generate_analysis(generator, transcription, difficulty, include_resources)
149
 
150
+ topics, subtopics = Utils.extract_topics(analysis)
 
151
  mind_map = Visualizer.create_mind_map(topics, subtopics)
152
 
 
153
  if path_name:
154
  self._save_to_history(transcription, analysis, path_name)
155
 
 
160
  }
161
 
162
  except Exception as e:
163
+ self.logger.error(f"Processing error: {str(e)}")
164
  return self._error_response(str(e))
165
+
166
+ def _generate_analysis(self,
167
+ generator: Any,
168
+ text: str,
169
+ difficulty: str,
170
+ include_resources: bool) -> str:
171
+ prompt = f"""
172
+ Based on the following text, create a detailed learning path
173
+ for {difficulty} level:
174
+ {text[:Config.MAX_TEXT_LENGTH]}
175
+
176
+ Learning path:
177
+ """
178
+
179
+ response = generator(
180
+ prompt,
181
+ max_length=300,
182
+ num_return_sequences=1
183
+ )[0]["generated_text"]
184
+
185
+ if include_resources:
186
+ response += self._generate_resources()
187
+
188
+ return response
189
 
190
+ def _generate_resources(self) -> str:
191
+ return """
192
+ Recommended Resources:
193
+
194
+ 1. Books:
195
+ - "Essential Guide"
196
+ - "Advanced Techniques"
197
+
198
+ 2. Online Courses:
199
+ - Coursera: "Topic Specialization"
200
+ - edX: "Advanced Course"
201
+
202
+ 3. Practical Resources:
203
+ - Interactive tutorials
204
+ - Practice exercises
205
+ - Real-world projects
206
+ """
207
+
208
+ def _save_to_history(self, transcription: str, analysis: str, path_name: str):
209
+ history = Utils.load_json(self.history_file) or []
210
+ history.append({
211
+ "date": datetime.now().isoformat(),
212
+ "name": path_name,
213
+ "transcription": transcription,
214
+ "analysis": analysis
215
+ })
216
+ Utils.save_json(history, self.history_file)
217
+
218
+ def _error_response(self, error_msg: str) -> Dict[str, Any]:
219
+ return {
220
+ "transcription": f"Error: {error_msg}",
221
+ "analysis": "Could not generate analysis due to an error.",
222
+ "mind_map": None
223
+ }
224
+
225
  def create_interface(self):
226
  with gr.Blocks(theme=gr.themes.Soft()) as app:
227
  gr.Markdown("""
228
+ # 🎓 Learning Path Generator
229
 
230
+ Upload an audio file describing your learning goals
231
+ and receive a personalized learning path with resources!
232
  """)
233
 
234
+ with gr.Tab("Generate Path"):
235
  with gr.Row():
236
  with gr.Column(scale=2):
237
  audio_input = gr.Audio(
238
  type="filepath",
239
+ label="Audio Upload",
240
+ description="Record or upload an audio describing your goals"
241
  )
242
 
243
  with gr.Row():
244
  path_name = gr.Textbox(
245
+ label="Path Name",
246
+ placeholder="Give your learning path a name (optional)"
247
  )
248
  difficulty = gr.Dropdown(
249
+ choices=["beginner", "intermediate", "advanced"],
250
+ value="intermediate",
251
+ label="Difficulty Level"
252
  )
253
 
254
+ include_resources = gr.Checkbox(
255
+ label="Include Recommended Resources",
256
+ value=True
257
+ )
258
+
259
+ process_btn = gr.Button(
260
+ "Generate Learning Path",
261
+ variant="primary"
262
+ )
263
 
264
+ text_output = gr.Textbox(
265
+ label="Audio Transcription",
266
+ lines=4
267
+ )
 
268
 
269
+ analysis_output = gr.Textbox(
270
+ label="Analysis and Learning Path",
271
+ lines=10
272
+ )
 
273
 
274
+ mind_map_output = gr.Image(
275
+ label="Learning Path Mind Map"
 
 
 
 
 
 
 
 
 
276
  )
 
277
 
 
278
  process_btn.click(
279
  fn=self.process_audio,
280
  inputs=[audio_input, path_name, difficulty, include_resources],
 
284
  "mind_map": mind_map_output
285
  }
286
  )
 
 
 
 
 
287
 
288
  return app
289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  if __name__ == "__main__":
291
  try:
292
  generator = LearningPathGenerator()
293
  app = generator.create_interface()
294
  app.launch(debug=Config.DEBUG)
295
+ except Exception as e:
296
+ logging.error(f"Application error: {str(e)}")
297
+ raise