amauricunha commited on
Commit
f55e3b7
·
verified ·
1 Parent(s): 42b8553

Update flask_app_hf.py

Browse files
Files changed (1) hide show
  1. flask_app_hf.py +360 -365
flask_app_hf.py CHANGED
@@ -1,366 +1,361 @@
1
- # --- STUDY PLANNER (PLANO DE ESTUDOS) ---
2
-
3
- STUDY_PLAN_PATH = 'hf_data/study_plan.json'
4
-
5
- @app.route('/study-plan', methods=['GET', 'POST'])
6
- def study_plan():
7
- """Salvar ou carregar plano de estudos global (sem autenticação)"""
8
- if request.method == 'POST':
9
- try:
10
- data = request.get_json()
11
- with open(STUDY_PLAN_PATH, 'w', encoding='utf-8') as f:
12
- json.dump(data, f, ensure_ascii=False, indent=2)
13
- return jsonify({'success': True, 'message': 'Study plan saved'})
14
- except Exception as e:
15
- print(f"Erro ao salvar study plan: {e}")
16
- return jsonify({'success': False, 'message': 'Failed to save study plan'}), 500
17
- else:
18
- try:
19
- if os.path.exists(STUDY_PLAN_PATH):
20
- with open(STUDY_PLAN_PATH, 'r', encoding='utf-8') as f:
21
- plan = json.load(f)
22
- return jsonify({'success': True, 'plan': plan})
23
- else:
24
- return jsonify({'success': True, 'plan': None})
25
- except Exception as e:
26
- print(f"Erro ao carregar study plan: {e}")
27
- return jsonify({'success': False, 'message': 'Failed to load study plan'}), 500
28
- # flask_app_hf.py - English Helper HF Spaces (Sem Autenticação)
29
- import os
30
- import io
31
- import json
32
- import base64
33
- from datetime import datetime
34
- from flask import Flask, request, jsonify, send_file, render_template_string, redirect, url_for, Response
35
- from gtts import gTTS
36
- from groq import Groq
37
- import google.generativeai as genai
38
- from google.generativeai.types import GenerationConfig
39
-
40
- # --- CONFIGURAÇÃO INICIAL ---
41
- app = Flask(__name__)
42
-
43
- # Configuração simplificada para HF Spaces
44
- app.config['SECRET_KEY'] = 'hf-simple-key-no-auth'
45
- app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
46
-
47
- print(f"✅ Flask HF app inicializado")
48
- print(f"✅ Working directory: {os.getcwd()}")
49
-
50
- # --- UTILITÁRIOS DE ARMAZENAMENTO ---
51
-
52
- def save_user_data(user_id, data_type, data):
53
- """Salvar dados do usuário em arquivos JSON"""
54
- try:
55
- user_dir = f"hf_data/{data_type}"
56
- os.makedirs(user_dir, exist_ok=True)
57
-
58
- file_path = f"{user_dir}/{user_id}.json"
59
-
60
- # Carregar dados existentes
61
- existing_data = []
62
- if os.path.exists(file_path):
63
- with open(file_path, 'r', encoding='utf-8') as f:
64
- existing_data = json.load(f)
65
-
66
- # Adicionar novos dados
67
- data['timestamp'] = datetime.now().isoformat()
68
- existing_data.append(data)
69
-
70
- # Manter apenas os últimos 100 itens
71
- if len(existing_data) > 100:
72
- existing_data = existing_data[-100:]
73
-
74
- # Salvar
75
- with open(file_path, 'w', encoding='utf-8') as f:
76
- json.dump(existing_data, f, ensure_ascii=False, indent=2)
77
-
78
- return True
79
- except Exception as e:
80
- print(f"Erro ao salvar dados: {e}")
81
- return False
82
-
83
- def load_user_data(user_id, data_type):
84
- """Carregar dados do usuário"""
85
- try:
86
- file_path = f"hf_data/{data_type}/{user_id}.json"
87
- if os.path.exists(file_path):
88
- with open(file_path, 'r', encoding='utf-8') as f:
89
- return json.load(f)
90
- return []
91
- except Exception as e:
92
- print(f"Erro ao carregar dados: {e}")
93
- return []
94
-
95
- def get_all_users():
96
- """Obter lista de todos os usuários"""
97
- users = set()
98
- for data_type in ['flashcards', 'conversations', 'analytics']:
99
- data_dir = f"hf_data/{data_type}"
100
- if os.path.exists(data_dir):
101
- for filename in os.listdir(data_dir):
102
- if filename.endswith('.json'):
103
- users.add(filename[:-5]) # Remove .json
104
- return sorted(list(users))
105
-
106
- # --- CONFIGURAÇÃO DE APIs ---
107
-
108
- # Configurar APIs
109
- groq_client = None
110
- genai_client = None
111
-
112
- try:
113
- groq_api_key = os.environ.get('GROQ_API_KEY')
114
- if groq_api_key:
115
- groq_client = Groq(api_key=groq_api_key)
116
- print("✅ Groq API configurada")
117
- except Exception as e:
118
- print(f"⚠️ Groq API não configurada: {e}")
119
-
120
- try:
121
- gemini_api_key = os.environ.get('GEMINI_API_KEY')
122
- if gemini_api_key:
123
- genai.configure(api_key=gemini_api_key)
124
- genai_client = genai
125
- print(" Gemini API configurada")
126
- except Exception as e:
127
- print(f"⚠️ Gemini API não configurada: {e}")
128
-
129
- # --- ROTAS PRINCIPAIS ---
130
-
131
- @app.route('/')
132
- def index():
133
- return send_file('../templates_hf/index.html')
134
-
135
- @app.route('/admin')
136
- def admin():
137
- return send_file('../templates_hf/admin.html')
138
-
139
- @app.route('/status')
140
- def status():
141
- return send_file('../templates_hf/status.html')
142
-
143
- # --- API ENDPOINTS ---
144
-
145
- @app.route('/list-models', methods=['GET'])
146
- def list_models():
147
- """Listar modelos disponíveis"""
148
- available_models = []
149
-
150
- if groq_client:
151
- available_models.extend([
152
- {"name": "Llama 3.2 90B (Ultra Fast)", "value": "groq:llama-3.2-90b-text-preview"},
153
- {"name": "Llama 3.2 11B Vision (Fast)", "value": "groq:llama-3.2-11b-vision-preview"},
154
- {"name": "Llama 3.1 70B (Fast)", "value": "groq:llama-3.1-70b-versatile"},
155
- {"name": "Mixtral 8x7B (Fast)", "value": "groq:mixtral-8x7b-32768"}
156
- ])
157
-
158
- if genai_client:
159
- available_models.extend([
160
- {"name": "Gemini 2.5 Flash (Recommended)", "value": "gemini:gemini-2.5-flash-latest"},
161
- {"name": "Gemini 2.5 Pro Experimental", "value": "gemini:gemini-2.5-pro-exp"},
162
- {"name": "Gemini 1.5 Flash", "value": "gemini:gemini-1.5-flash-latest"},
163
- {"name": "Gemini 1.5 Pro", "value": "gemini:gemini-1.5-pro-latest"}
164
- ])
165
-
166
- return jsonify(available_models)
167
-
168
- # Cache de áudio TTS em memória
169
- tts_cache = {}
170
-
171
- @app.route('/tts-proxy', methods=['POST'])
172
- def tts_proxy():
173
- data = request.get_json()
174
- text = data.get('text', '')
175
- tld = data.get('tld', 'co.uk')
176
- if not text: return jsonify({"error": "No text provided"}), 400
177
-
178
- if len(text) > 10000:
179
- return jsonify({"error": "Text is too long. Maximum 10,000 characters allowed."}), 400
180
-
181
- import hashlib
182
- cache_key = hashlib.md5(f"{text}_{tld}".encode()).hexdigest()
183
-
184
- try:
185
- if cache_key in tts_cache:
186
- print(f"🎵 TTS Cache HIT: {len(text)} chars")
187
- cached_audio = tts_cache[cache_key]
188
- audio_fp = io.BytesIO(cached_audio)
189
- return send_file(audio_fp, mimetype='audio/mpeg', as_attachment=False)
190
-
191
- print(f"🎵 TTS Cache MISS: Gerando áudio para {len(text)} chars, tld: {tld}")
192
-
193
- tts = gTTS(text=text, lang='en', tld=tld)
194
- mp3_fp = io.BytesIO()
195
- tts.write_to_fp(mp3_fp)
196
- mp3_fp.seek(0)
197
-
198
- audio_data = mp3_fp.read()
199
- tts_cache[cache_key] = audio_data
200
-
201
- if len(tts_cache) > 50:
202
- oldest_key = next(iter(tts_cache))
203
- del tts_cache[oldest_key]
204
-
205
- audio_fp = io.BytesIO(audio_data)
206
- return send_file(audio_fp, mimetype='audio/mpeg', as_attachment=False)
207
-
208
- except Exception as e:
209
- print(f"❌ TTS Error: {e}")
210
- return jsonify({"error": f"Failed to generate audio via gTTS: {e}"}), 500
211
-
212
- @app.route('/explain-proxy', methods=['POST'])
213
- def explain_proxy():
214
- """Gerar explicação/flashcard"""
215
- data = request.get_json()
216
- selected_text = data.get('selectedText', '')
217
- model_info = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
218
-
219
- if len(model_info) != 2:
220
- return jsonify({"error": "Invalid model format"}), 400
221
-
222
- model_provider, model_name = model_info
223
-
224
- if not selected_text:
225
- return jsonify({"error": "No text selected"}), 400
226
-
227
- try:
228
- prompt = f"""
229
- Create a comprehensive flashcard for the English term/phrase: "{selected_text}"
230
-
231
- Provide:
232
- 1. Clear definition in English
233
- 2. Translation to Portuguese
234
- 3. Example sentence using the term
235
- 4. Same sentence with the term replaced by "____" for practice
236
-
237
- Return as JSON with keys: definition, translation, context_sentence, gapped_sentence
238
- """
239
-
240
- if model_provider == 'gemini' and genai_client:
241
- model = genai_client.GenerativeModel(model_name)
242
- response = model.generate_content(prompt)
243
-
244
- # Extrair JSON da resposta
245
- response_text = response.text
246
- if '```json' in response_text:
247
- json_start = response_text.find('```json') + 7
248
- json_end = response_text.find('```', json_start)
249
- response_text = response_text[json_start:json_end].strip()
250
-
251
- result = json.loads(response_text)
252
- result['term'] = selected_text
253
-
254
- return jsonify(result)
255
-
256
- elif model_provider == 'groq' and groq_client:
257
- response = groq_client.chat.completions.create(
258
- messages=[{"role": "user", "content": prompt}],
259
- model=model_name,
260
- temperature=0.3
261
- )
262
-
263
- response_text = response.choices[0].message.content
264
- if '```json' in response_text:
265
- json_start = response_text.find('```json') + 7
266
- json_end = response_text.find('```', json_start)
267
- response_text = response_text[json_start:json_end].strip()
268
-
269
- result = json.loads(response_text)
270
- result['term'] = selected_text
271
-
272
- return jsonify(result)
273
-
274
- else:
275
- return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured"}), 503
276
-
277
- except Exception as e:
278
- print(f"AI ANALYSIS ERROR: {e}")
279
- return jsonify({"error": str(e)}), 500
280
-
281
- # --- ROUTES DE DADOS (SEM AUTENTICAÇÃO) ---
282
-
283
- @app.route('/users', methods=['GET'])
284
- def get_users():
285
- """Obter lista de usuários"""
286
- users = get_all_users()
287
- return jsonify({'users': users, 'total': len(users)})
288
-
289
- @app.route('/user/<user_id>/flashcards', methods=['GET', 'POST'])
290
- def user_flashcards(user_id):
291
- """Gerenciar flashcards do usuário"""
292
- if request.method == 'POST':
293
- data = request.get_json()
294
- if save_user_data(user_id, 'flashcards', data):
295
- return jsonify({'success': True, 'message': 'Flashcard saved'})
296
- else:
297
- return jsonify({'success': False, 'message': 'Failed to save flashcard'}), 500
298
- else:
299
- flashcards = load_user_data(user_id, 'flashcards')
300
- return jsonify({'flashcards': flashcards})
301
-
302
- @app.route('/user/<user_id>/conversations', methods=['GET', 'POST'])
303
- def user_conversations(user_id):
304
- """Gerenciar conversas do usuário"""
305
- if request.method == 'POST':
306
- data = request.get_json()
307
- if save_user_data(user_id, 'conversations', data):
308
- return jsonify({'success': True, 'message': 'Conversation saved'})
309
- else:
310
- return jsonify({'success': False, 'message': 'Failed to save conversation'}), 500
311
- else:
312
- conversations = load_user_data(user_id, 'conversations')
313
- return jsonify({'conversations': conversations})
314
-
315
- @app.route('/user/<user_id>/analytics', methods=['GET'])
316
- def user_analytics(user_id):
317
- """Obter analytics do usuário"""
318
- analytics = load_user_data(user_id, 'analytics')
319
- flashcards = load_user_data(user_id, 'flashcards')
320
- conversations = load_user_data(user_id, 'conversations')
321
-
322
- return jsonify({
323
- 'total_flashcards': len(flashcards),
324
- 'total_conversations': len(conversations),
325
- 'total_sessions': len(analytics),
326
- 'recent_activity': analytics[-10:] if analytics else []
327
- })
328
-
329
- # --- STATUS E ADMIN ---
330
-
331
- @app.route('/admin/stats', methods=['GET'])
332
- def admin_stats():
333
- """Estatísticas do sistema"""
334
- users = get_all_users()
335
- stats = {
336
- 'total_users': len(users),
337
- 'users': []
338
- }
339
-
340
- for user_id in users:
341
- flashcards = len(load_user_data(user_id, 'flashcards'))
342
- conversations = len(load_user_data(user_id, 'conversations'))
343
- stats['users'].append({
344
- 'user_id': user_id,
345
- 'flashcards': flashcards,
346
- 'conversations': conversations
347
- })
348
-
349
- return jsonify(stats)
350
-
351
- @app.route('/system/status', methods=['GET'])
352
- def system_status():
353
- """Status do sistema"""
354
- return jsonify({
355
- 'status': 'running',
356
- 'version': 'HF-Simplified-1.0',
357
- 'apis': {
358
- 'groq': groq_client is not None,
359
- 'gemini': genai_client is not None
360
- },
361
- 'storage': 'file_based',
362
- 'auth': 'disabled'
363
- })
364
-
365
- if __name__ == '__main__':
366
  app.run(host='0.0.0.0', port=7860, debug=True)
 
1
+ # flask_app_hf.py - English Helper HF Spaces (Sem Autenticação)
2
+ import os
3
+ import io
4
+ import json
5
+ import base64
6
+ from datetime import datetime
7
+ from flask import Flask, request, jsonify, send_file, render_template_string, redirect, url_for, Response
8
+ from gtts import gTTS
9
+ from groq import Groq
10
+ import google.generativeai as genai
11
+ from google.generativeai.types import GenerationConfig
12
+
13
+ # --- CONFIGURAÇÃO INICIAL ---
14
+ app = Flask(__name__)
15
+
16
+ # Configuração simplificada para HF Spaces
17
+ app.config['SECRET_KEY'] = 'hf-simple-key-no-auth'
18
+ app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 0
19
+
20
+ print(f"✅ Flask HF app inicializado")
21
+ print(f"✅ Working directory: {os.getcwd()}")
22
+
23
+ # --- UTILITÁRIOS DE ARMAZENAMENTO ---
24
+
25
+ def save_user_data(user_id, data_type, data):
26
+ """Salvar dados do usuário em arquivos JSON"""
27
+ try:
28
+ user_dir = f"hf_data/{data_type}"
29
+ os.makedirs(user_dir, exist_ok=True)
30
+
31
+ file_path = f"{user_dir}/{user_id}.json"
32
+
33
+ # Carregar dados existentes
34
+ existing_data = []
35
+ if os.path.exists(file_path):
36
+ with open(file_path, 'r', encoding='utf-8') as f:
37
+ existing_data = json.load(f)
38
+
39
+ # Adicionar novos dados
40
+ data['timestamp'] = datetime.now().isoformat()
41
+ existing_data.append(data)
42
+
43
+ # Manter apenas os últimos 100 itens
44
+ if len(existing_data) > 100:
45
+ existing_data = existing_data[-100:]
46
+
47
+ # Salvar
48
+ with open(file_path, 'w', encoding='utf-8') as f:
49
+ json.dump(existing_data, f, ensure_ascii=False, indent=2)
50
+
51
+ return True
52
+ except Exception as e:
53
+ print(f"Erro ao salvar dados: {e}")
54
+ return False
55
+
56
+ def load_user_data(user_id, data_type):
57
+ """Carregar dados do usuário"""
58
+ try:
59
+ file_path = f"hf_data/{data_type}/{user_id}.json"
60
+ if os.path.exists(file_path):
61
+ with open(file_path, 'r', encoding='utf-8') as f:
62
+ return json.load(f)
63
+ return []
64
+ except Exception as e:
65
+ print(f"Erro ao carregar dados: {e}")
66
+ return []
67
+
68
+ def get_all_users():
69
+ """Obter lista de todos os usuários"""
70
+ users = set()
71
+ for data_type in ['flashcards', 'conversations', 'analytics']:
72
+ data_dir = f"hf_data/{data_type}"
73
+ if os.path.exists(data_dir):
74
+ for filename in os.listdir(data_dir):
75
+ if filename.endswith('.json'):
76
+ users.add(filename[:-5]) # Remove .json
77
+ return sorted(list(users))
78
+
79
+ # --- CONFIGURAÇÃO DE APIs ---
80
+
81
+ # Configurar APIs
82
+ groq_client = None
83
+ genai_client = None
84
+
85
+ try:
86
+ groq_api_key = os.environ.get('GROQ_API_KEY')
87
+ if groq_api_key:
88
+ groq_client = Groq(api_key=groq_api_key)
89
+ print("✅ Groq API configurada")
90
+ except Exception as e:
91
+ print(f"⚠️ Groq API não configurada: {e}")
92
+
93
+ try:
94
+ gemini_api_key = os.environ.get('GEMINI_API_KEY')
95
+ if gemini_api_key:
96
+ genai.configure(api_key=gemini_api_key)
97
+ genai_client = genai
98
+ print("✅ Gemini API configurada")
99
+ except Exception as e:
100
+ print(f"⚠️ Gemini API não configurada: {e}")
101
+
102
+ # --- ROTAS PRINCIPAIS ---
103
+
104
+ @app.route('/')
105
+ def index():
106
+ return send_file('../templates_hf/index.html')
107
+
108
+ @app.route('/admin')
109
+ def admin():
110
+ return send_file('../templates_hf/admin.html')
111
+
112
+ @app.route('/status')
113
+ def status():
114
+ return send_file('../templates_hf/status.html')
115
+
116
+ # --- API ENDPOINTS ---
117
+
118
+ @app.route('/list-models', methods=['GET'])
119
+ def list_models():
120
+ """Listar modelos disponíveis"""
121
+ available_models = []
122
+
123
+ if groq_client:
124
+ available_models.extend([
125
+ {"name": "Llama 3.2 90B (Ultra Fast)", "value": "groq:llama-3.2-90b-text-preview"},
126
+ {"name": "Llama 3.2 11B Vision (Fast)", "value": "groq:llama-3.2-11b-vision-preview"},
127
+ {"name": "Llama 3.1 70B (Fast)", "value": "groq:llama-3.1-70b-versatile"},
128
+ {"name": "Mixtral 8x7B (Fast)", "value": "groq:mixtral-8x7b-32768"}
129
+ ])
130
+
131
+ if genai_client:
132
+ available_models.extend([
133
+ {"name": "Gemini 2.5 Flash (Recommended)", "value": "gemini:gemini-2.5-flash-latest"},
134
+ {"name": "Gemini 2.5 Pro Experimental", "value": "gemini:gemini-2.5-pro-exp"},
135
+ {"name": "Gemini 1.5 Flash", "value": "gemini:gemini-1.5-flash-latest"},
136
+ {"name": "Gemini 1.5 Pro", "value": "gemini:gemini-1.5-pro-latest"}
137
+ ])
138
+
139
+ return jsonify(available_models)
140
+
141
+ # Cache de áudio TTS em memória
142
+ tts_cache = {}
143
+
144
+ @app.route('/tts-proxy', methods=['POST'])
145
+ def tts_proxy():
146
+ data = request.get_json()
147
+ text = data.get('text', '')
148
+ tld = data.get('tld', 'co.uk')
149
+ if not text: return jsonify({"error": "No text provided"}), 400
150
+
151
+ if len(text) > 10000:
152
+ return jsonify({"error": "Text is too long. Maximum 10,000 characters allowed."}), 400
153
+
154
+ import hashlib
155
+ cache_key = hashlib.md5(f"{text}_{tld}".encode()).hexdigest()
156
+
157
+ try:
158
+ if cache_key in tts_cache:
159
+ print(f"🎵 TTS Cache HIT: {len(text)} chars")
160
+ cached_audio = tts_cache[cache_key]
161
+ audio_fp = io.BytesIO(cached_audio)
162
+ return send_file(audio_fp, mimetype='audio/mpeg', as_attachment=False)
163
+
164
+ print(f"🎵 TTS Cache MISS: Gerando áudio para {len(text)} chars, tld: {tld}")
165
+
166
+ tts = gTTS(text=text, lang='en', tld=tld)
167
+ mp3_fp = io.BytesIO()
168
+ tts.write_to_fp(mp3_fp)
169
+ mp3_fp.seek(0)
170
+
171
+ audio_data = mp3_fp.read()
172
+ tts_cache[cache_key] = audio_data
173
+
174
+ if len(tts_cache) > 50:
175
+ oldest_key = next(iter(tts_cache))
176
+ del tts_cache[oldest_key]
177
+
178
+ audio_fp = io.BytesIO(audio_data)
179
+ return send_file(audio_fp, mimetype='audio/mpeg', as_attachment=False)
180
+
181
+ except Exception as e:
182
+ print(f"❌ TTS Error: {e}")
183
+ return jsonify({"error": f"Failed to generate audio via gTTS: {e}"}), 500
184
+
185
+ @app.route('/explain-proxy', methods=['POST'])
186
+ def explain_proxy():
187
+ """Gerar explicação/flashcard"""
188
+ data = request.get_json()
189
+ selected_text = data.get('selectedText', '')
190
+ model_info = data.get('model', 'gemini:gemini-2.5-flash-latest').split(':', 1)
191
+
192
+ if len(model_info) != 2:
193
+ return jsonify({"error": "Invalid model format"}), 400
194
+
195
+ model_provider, model_name = model_info
196
+
197
+ if not selected_text:
198
+ return jsonify({"error": "No text selected"}), 400
199
+
200
+ try:
201
+ prompt = f"""
202
+ Create a comprehensive flashcard for the English term/phrase: "{selected_text}"
203
+
204
+ Provide:
205
+ 1. Clear definition in English
206
+ 2. Translation to Portuguese
207
+ 3. Example sentence using the term
208
+ 4. Same sentence with the term replaced by "____" for practice
209
+
210
+ Return as JSON with keys: definition, translation, context_sentence, gapped_sentence
211
+ """
212
+
213
+ if model_provider == 'gemini' and genai_client:
214
+ model = genai_client.GenerativeModel(model_name)
215
+ response = model.generate_content(prompt)
216
+
217
+ # Extrair JSON da resposta
218
+ response_text = response.text
219
+ if '```json' in response_text:
220
+ json_start = response_text.find('```json') + 7
221
+ json_end = response_text.find('```', json_start)
222
+ response_text = response_text[json_start:json_end].strip()
223
+
224
+ result = json.loads(response_text)
225
+ result['term'] = selected_text
226
+
227
+ return jsonify(result)
228
+
229
+ elif model_provider == 'groq' and groq_client:
230
+ response = groq_client.chat.completions.create(
231
+ messages=[{"role": "user", "content": prompt}],
232
+ model=model_name,
233
+ temperature=0.3
234
+ )
235
+
236
+ response_text = response.choices[0].message.content
237
+ if '```json' in response_text:
238
+ json_start = response_text.find('```json') + 7
239
+ json_end = response_text.find('```', json_start)
240
+ response_text = response_text[json_start:json_end].strip()
241
+
242
+ result = json.loads(response_text)
243
+ result['term'] = selected_text
244
+
245
+ return jsonify(result)
246
+
247
+ else:
248
+ return jsonify({"error": f"{model_provider.upper()}_API_KEY not configured"}), 503
249
+
250
+ except Exception as e:
251
+ print(f"AI ANALYSIS ERROR: {e}")
252
+ return jsonify({"error": str(e)}), 500
253
+
254
+ # --- ROUTES DE DADOS (SEM AUTENTICAÇÃO) ---
255
+
256
+ @app.route('/users', methods=['GET'])
257
+ def get_users():
258
+ """Obter lista de usuários"""
259
+ users = get_all_users()
260
+ return jsonify({'users': users, 'total': len(users)})
261
+
262
+ @app.route('/user/<user_id>/flashcards', methods=['GET', 'POST'])
263
+ def user_flashcards(user_id):
264
+ """Gerenciar flashcards do usuário"""
265
+ if request.method == 'POST':
266
+ data = request.get_json()
267
+ if save_user_data(user_id, 'flashcards', data):
268
+ return jsonify({'success': True, 'message': 'Flashcard saved'})
269
+ else:
270
+ return jsonify({'success': False, 'message': 'Failed to save flashcard'}), 500
271
+ else:
272
+ flashcards = load_user_data(user_id, 'flashcards')
273
+ return jsonify({'flashcards': flashcards})
274
+
275
+ @app.route('/user/<user_id>/conversations', methods=['GET', 'POST'])
276
+ def user_conversations(user_id):
277
+ """Gerenciar conversas do usuário"""
278
+ if request.method == 'POST':
279
+ data = request.get_json()
280
+ if save_user_data(user_id, 'conversations', data):
281
+ return jsonify({'success': True, 'message': 'Conversation saved'})
282
+ else:
283
+ return jsonify({'success': False, 'message': 'Failed to save conversation'}), 500
284
+ else:
285
+ conversations = load_user_data(user_id, 'conversations')
286
+ return jsonify({'conversations': conversations})
287
+
288
+ @app.route('/user/<user_id>/analytics', methods=['GET'])
289
+ def user_analytics(user_id):
290
+ """Obter analytics do usuário"""
291
+ analytics = load_user_data(user_id, 'analytics')
292
+ flashcards = load_user_data(user_id, 'flashcards')
293
+ conversations = load_user_data(user_id, 'conversations')
294
+
295
+ return jsonify({
296
+ 'total_flashcards': len(flashcards),
297
+ 'total_conversations': len(conversations),
298
+ 'total_sessions': len(analytics),
299
+ 'recent_activity': analytics[-10:] if analytics else []
300
+ })
301
+
302
+ # --- STATUS E ADMIN ---
303
+
304
+ @app.route('/admin/stats', methods=['GET'])
305
+ def admin_stats():
306
+ """Estatísticas do sistema"""
307
+ users = get_all_users()
308
+ stats = {
309
+ 'total_users': len(users),
310
+ 'users': []
311
+ }
312
+
313
+ for user_id in users:
314
+ flashcards = len(load_user_data(user_id, 'flashcards'))
315
+ conversations = len(load_user_data(user_id, 'conversations'))
316
+ stats['users'].append({
317
+ 'user_id': user_id,
318
+ 'flashcards': flashcards,
319
+ 'conversations': conversations
320
+ })
321
+
322
+ return jsonify(stats)
323
+
324
+ @app.route('/system/status', methods=['GET'])
325
+ def system_status():
326
+ """Status do sistema"""
327
+ return jsonify({
328
+ 'status': 'running',
329
+ 'version': 'HF-Simplified-1.0',
330
+ 'apis': {
331
+ 'groq': groq_client is not None,
332
+ 'gemini': genai_client is not None
333
+ },
334
+ 'storage': 'file_based',
335
+ 'auth': 'disabled'
336
+ })
337
+
338
+ from study_plan import save_study_plan, load_study_plan, generate_study_plan
339
+
340
+ @app.route('/study-plan', methods=['GET', 'POST'])
341
+ def study_plan():
342
+ """Salvar ou carregar plano de estudos global (sem autenticação)"""
343
+ if request.method == 'POST':
344
+ try:
345
+ data = request.get_json()
346
+ plan = generate_study_plan(data)
347
+ save_study_plan(plan)
348
+ return jsonify({'success': True, 'message': 'Study plan generated and saved', 'plan': plan})
349
+ except Exception as e:
350
+ print(f"Erro ao salvar study plan: {e}")
351
+ return jsonify({'success': False, 'message': 'Failed to save study plan'}), 500
352
+ else:
353
+ try:
354
+ plan = load_study_plan()
355
+ return jsonify({'success': True, 'plan': plan})
356
+ except Exception as e:
357
+ print(f"Erro ao carregar study plan: {e}")
358
+ return jsonify({'success': False, 'message': 'Failed to load study plan'}), 500
359
+
360
+ if __name__ == '__main__':
 
 
 
 
 
361
  app.run(host='0.0.0.0', port=7860, debug=True)