Migue1804 commited on
Commit
4d4a7fb
·
verified ·
1 Parent(s): 7e445a9

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +150 -132
src/streamlit_app.py CHANGED
@@ -155,173 +155,191 @@ example_files = []
155
  if os.path.exists(examples_folder):
156
  example_files = [f for f in os.listdir(examples_folder) if f.endswith(".gpx")]
157
 
158
- # ========== LÓGICA EXACTA DE APPTEST.PY ==========
159
 
160
- # Opciones de selección de datos de ejemplo - EXACTAMENTE COMO APPTEST.PY
161
  st.subheader("Datos de Ejemplo")
162
- usar_datos_exp = st.checkbox("Usar datos de ejemplo: datos_exp.csv") if len(example_files) > 0 else False
163
- usar_datos_process = st.checkbox("Usar datos de ejemplo: datos_process.csv") if len(example_files) > 1 else False
164
 
165
- # Para GPX, adaptamos pero mantenemos la estructura:
166
  if example_files:
167
- for i, example_file in enumerate(example_files):
168
- if i == 0:
169
- usar_datos_exp = st.checkbox(f"Usar datos de ejemplo: {example_file}")
170
- elif i == 1:
171
- usar_datos_process = st.checkbox(f"Usar datos de ejemplo: {example_file}")
 
172
 
173
- # Inicializar variable de datos - EXACTAMENTE COMO APPTEST.PY
174
  data = None
 
175
 
176
- # Verificar qué checkbox está seleccionado y cargar el archivo correspondiente - EXACTAMENTE COMO APPTEST.PY
177
- if usar_datos_exp and not usar_datos_process:
178
- if example_files:
179
- try:
180
- example_path = os.path.join(examples_folder, example_files[0])
181
- with open(example_path, 'r', encoding='utf-8') as f:
182
- gpx_content = f.read()
183
- # Simular DataFrame como en apptest.py
184
- data = pd.DataFrame({'source': ['example'], 'filename': [example_files[0]], 'loaded': [True]})
185
- st.success(f"Datos de ejemplo ({example_files[0]}) cargados exitosamente.")
186
- st.dataframe(data, use_container_width=True)
187
- except Exception as e:
188
- st.error(f"Error cargando ejemplo: {e}")
189
-
190
- elif usar_datos_process and not usar_datos_exp:
191
- if len(example_files) > 1:
192
- try:
193
- example_path = os.path.join(examples_folder, example_files[1])
194
- with open(example_path, 'r', encoding='utf-8') as f:
195
- gpx_content = f.read()
196
- # Simular DataFrame como en apptest.py
197
- data = pd.DataFrame({'source': ['example'], 'filename': [example_files[1]], 'loaded': [True]})
198
- st.success(f"Datos de ejemplo ({example_files[1]}) cargados exitosamente.")
199
- st.dataframe(data, use_container_width=True)
200
- except Exception as e:
201
- st.error(f"Error cargando ejemplo: {e}")
202
-
203
- elif usar_datos_exp and usar_datos_process:
204
- st.error("Por favor, selecciona solo un conjunto de datos de ejemplo a la vez.")
205
 
206
- # Opción para cargar datos personalizados - EXACTAMENTE COMO APPTEST.PY
207
  st.subheader("Carga tus Datos")
208
  uploaded_file = st.file_uploader("Carga un archivo GPX con los datos experimentales:", type="gpx")
209
 
210
  if uploaded_file is not None:
211
  try:
212
- # Leer datos cargados - EXACTAMENTE COMO APPTEST.PY
213
- # En apptest.py usa: data = pd.read_csv(uploaded_file)
214
- # Aquí adaptamos para GPX pero manteniendo la estructura:
215
-
216
- # Método que funciona en HF Spaces
217
- file_content = uploaded_file.read()
218
-
219
- # Detectar encoding
220
- import chardet
221
- detected = chardet.detect(file_content)
222
- encoding = detected.get('encoding', 'utf-8')
223
-
224
  try:
225
- gpx_content = file_content.decode(encoding)
226
- except:
227
- gpx_content = file_content.decode('utf-8', errors='ignore')
228
-
229
- # Crear DataFrame simulado como en apptest.py
230
- data = pd.DataFrame({
231
- 'source': ['uploaded'],
232
- 'filename': [uploaded_file.name],
233
- 'size_bytes': [len(file_content)],
234
- 'encoding': [encoding],
235
- 'loaded': [True]
236
- })
 
 
 
 
237
 
238
- st.success("Datos cargados exitosamente desde el archivo proporcionado.")
239
- st.dataframe(data, use_container_width=True)
 
 
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  except Exception as e:
242
  st.error(f"Error leyendo archivo: {e}")
243
- data = None
244
- gpx_content = None
 
 
245
 
246
- # Validación para asegurarse de que se cargaron datos - EXACTAMENTE COMO APPTEST.PY
247
  if data is None:
248
  st.warning("No se han cargado datos. Por favor, selecciona un archivo o usa datos de ejemplo.")
249
  else:
250
  st.write("### Datos listos para su análisis.")
251
 
252
- # Aquí es donde apptest.py procesa los datos del DataFrame
253
- # Nosotros procesamos el contenido GPX
254
-
255
- if 'gpx_content' in locals() and gpx_content:
256
- # Procesar GPX
257
- with st.spinner("🔄 Procesando datos GPX..."):
258
- gpx_data = process_gpx_data(gpx_content)
259
-
260
- if gpx_data and gpx_data['success']:
261
- st.success("🎉 ¡Archivo GPX procesado exitosamente!")
262
-
263
- # Extraer datos procesados
264
- heatmap_data = gpx_data['heatmap_data']
265
- track_segments = gpx_data['track_segments']
266
- total_distance = gpx_data['total_distance']
267
- total_time = gpx_data['total_time']
268
- center = gpx_data['center']
269
-
270
- # Dashboard de métricas
271
- st.markdown("### 📊 Resumen de la Ruta")
272
- col1, col2, col3 = st.columns(3)
273
-
274
- with col1:
275
- st.metric("📏 Distancia", f"{total_distance/1000:.2f} km" if total_distance > 0 else "N/A")
276
- with col2:
277
- hours = int(total_time // 3600) if total_time > 0 else 0
278
- minutes = int((total_time % 3600) // 60) if total_time > 0 else 0
279
- st.metric("⏱️ Tiempo", f"{hours}h {minutes}m" if total_time > 0 else "N/A")
280
- with col3:
281
- st.metric("📍 Puntos GPS", f"{len(heatmap_data):,}")
282
-
283
- # Crear y mostrar mapas
284
- st.markdown("### 🗺️ Visualización de Movimientos")
285
-
286
- with st.spinner("🗺️ Generando mapas..."):
287
- heat_map, spaghetti_map = create_maps(heatmap_data, track_segments, center)
288
 
289
- if heat_map and spaghetti_map:
290
- col1, col2 = st.columns(2)
291
 
292
- with col1:
293
- st.markdown("#### 🔥 Mapa de Calor")
294
- folium_static(heat_map, width=600, height=500)
 
 
 
 
 
 
 
295
 
 
 
296
  with col2:
297
- st.markdown("#### 🍝 Diagrama de Spaghetti")
298
- folium_static(spaghetti_map, width=600, height=500)
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
- # Información técnica adicional
301
- with st.expander("ℹ️ Información técnica"):
302
- st.write(f"**Archivo:** {data['filename'].iloc[0]}")
303
- st.write(f"**Fuente:** {data['source'].iloc[0]}")
304
- st.write(f"**Tracks:** {len(set(seg['track_idx'] for seg in track_segments))}")
305
- st.write(f"**Segmentos:** {len(track_segments)}")
306
- st.write(f"**Centro:** {center[0]:.6f}, {center[1]:.6f}")
307
- st.write(f"**Puntos totales:** {len(heatmap_data)}")
308
 
309
- else:
310
- st.error(" Error generando los mapas")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
- elif gpx_data and not gpx_data['success']:
313
- st.error(f" Error procesando GPX: {gpx_data['error']}")
314
- else:
315
- st.error(" Error desconocido procesando el archivo GPX")
316
- else:
317
- st.error(" No se pudo extraer el contenido GPX")
 
 
 
 
 
 
 
 
318
 
319
- # Mostrar ejemplos disponibles
320
- if example_files and data is None:
321
  st.markdown("### 📋 Ejemplos disponibles:")
322
  for i, file in enumerate(example_files, 1):
323
  st.write(f"{i}. **{file}**")
324
 
325
  # Footer
326
  st.markdown("---")
327
- st.markdown("🚀 **Optimizado para Hugging Face Spaces** | 📊 **Estructura idéntica a apptest.py**", unsafe_allow_html=True)
 
155
  if os.path.exists(examples_folder):
156
  example_files = [f for f in os.listdir(examples_folder) if f.endswith(".gpx")]
157
 
158
+ # ========== INICIO LÓGICA EXACTA DE APPTEST.PY CON ADAPTACIONES PARA HF SPACES ==========
159
 
160
+ # Opciones de selección de datos de ejemplo
161
  st.subheader("Datos de Ejemplo")
162
+ usar_datos_ejemplo = False
163
+ archivo_ejemplo_seleccionado = None
164
 
 
165
  if example_files:
166
+ # Crear checkbox para cada archivo de ejemplo - IGUAL QUE EN APPTEST.PY
167
+ for example_file in example_files:
168
+ if st.checkbox(f"Usar datos de ejemplo: {example_file}"):
169
+ if not usar_datos_ejemplo: # Solo permitir uno a la vez
170
+ usar_datos_ejemplo = True
171
+ archivo_ejemplo_seleccionado = example_file
172
 
173
+ # Inicializar variables de datos - IGUAL QUE EN APPTEST.PY
174
  data = None
175
+ gpx_content = None
176
 
177
+ # Cargar archivo de ejemplo si está seleccionado - IGUAL QUE EN APPTEST.PY
178
+ if usar_datos_ejemplo and archivo_ejemplo_seleccionado:
179
+ try:
180
+ example_path = os.path.join(examples_folder, archivo_ejemplo_seleccionado)
181
+ # USANDO PANDAS COMO EN APPTEST.PY para mantener consistencia
182
+ with open(example_path, 'r', encoding='utf-8') as f:
183
+ gpx_content = f.read()
184
+
185
+ st.success(f"Datos de ejemplo ({archivo_ejemplo_seleccionado}) cargados exitosamente.")
186
+
187
+ # Crear un DataFrame simulado para mantener la lógica de apptest.py
188
+ data = pd.DataFrame({'status': ['loaded'], 'source': ['example'], 'filename': [archivo_ejemplo_seleccionado]})
189
+ st.dataframe(data, use_container_width=True)
190
+
191
+ except Exception as e:
192
+ st.error(f"Error cargando archivo de ejemplo: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
+ # Opción para cargar datos personalizados - EXACTO COMO APPTEST.PY
195
  st.subheader("Carga tus Datos")
196
  uploaded_file = st.file_uploader("Carga un archivo GPX con los datos experimentales:", type="gpx")
197
 
198
  if uploaded_file is not None:
199
  try:
200
+ # MÉTODO EXACTO DE APPTEST.PY PARA HUGGING FACE SPACES
201
+ # Primero intentar como lo hace apptest.py con pandas (aunque no sea CSV)
 
 
 
 
 
 
 
 
 
 
202
  try:
203
+ # Leer como bytes primero
204
+ file_bytes = uploaded_file.getvalue()
205
+
206
+ # Intentar decodificar con diferentes encodings como hace apptest.py internamente
207
+ gpx_content = None
208
+ for encoding in ['utf-8', 'latin-1', 'cp1252', 'iso-8859-1']:
209
+ try:
210
+ gpx_content = file_bytes.decode(encoding)
211
+ break
212
+ except UnicodeDecodeError:
213
+ continue
214
+
215
+ if gpx_content is None:
216
+ # Fallback: usar el método original de streamlit
217
+ uploaded_file.seek(0) # Reset file pointer
218
+ gpx_content = uploaded_file.read().decode('utf-8')
219
 
220
+ except Exception as e:
221
+ # Último recurso: método directo
222
+ uploaded_file.seek(0)
223
+ gpx_content = uploaded_file.read().decode('utf-8')
224
 
225
+ if gpx_content:
226
+ st.success("Datos cargados exitosamente desde el archivo proporcionado.")
227
+
228
+ # Crear DataFrame simulado para mantener consistencia con apptest.py
229
+ data = pd.DataFrame({
230
+ 'status': ['loaded'],
231
+ 'source': ['uploaded'],
232
+ 'filename': [uploaded_file.name],
233
+ 'size': [len(gpx_content)]
234
+ })
235
+ st.dataframe(data, use_container_width=True)
236
+ else:
237
+ st.error("No se pudo leer el contenido del archivo")
238
+
239
  except Exception as e:
240
  st.error(f"Error leyendo archivo: {e}")
241
+ # Debug information
242
+ st.write("Información de debug:")
243
+ st.write(f"Tipo de archivo: {type(uploaded_file)}")
244
+ st.write(f"Nombre de archivo: {uploaded_file.name if uploaded_file else 'None'}")
245
 
246
+ # Validación para asegurarse de que se cargaron datos - EXACTO COMO APPTEST.PY
247
  if data is None:
248
  st.warning("No se han cargado datos. Por favor, selecciona un archivo o usa datos de ejemplo.")
249
  else:
250
  st.write("### Datos listos para su análisis.")
251
 
252
+ # VERIFICAR QUE TENEMOS CONTENIDO GPX VÁLIDO
253
+ if gpx_content is None:
254
+ st.error("Error: No se pudo extraer el contenido GPX del archivo")
255
+ else:
256
+ # Validar que es contenido GPX válido
257
+ if not ("<?xml" in gpx_content and ("gpx" in gpx_content.lower() or "trk" in gpx_content.lower())):
258
+ st.error("El archivo no parece contener datos GPX válidos")
259
+ with st.expander("Ver contenido del archivo para debug"):
260
+ st.text(gpx_content[:500])
261
+ else:
262
+ # PROCESAR DATOS GPX
263
+ with st.spinner("🔄 Procesando datos GPX..."):
264
+ gpx_data = process_gpx_data(gpx_content)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
 
266
+ if gpx_data and gpx_data['success']:
267
+ st.success("🎉 ¡Archivo GPX procesado exitosamente!")
268
 
269
+ # Extraer datos procesados
270
+ heatmap_data = gpx_data['heatmap_data']
271
+ track_segments = gpx_data['track_segments']
272
+ total_distance = gpx_data['total_distance']
273
+ total_time = gpx_data['total_time']
274
+ center = gpx_data['center']
275
+
276
+ # Dashboard de métricas
277
+ st.markdown("### 📊 Resumen de la Ruta")
278
+ col1, col2, col3 = st.columns(3)
279
 
280
+ with col1:
281
+ st.metric("📏 Distancia", f"{total_distance/1000:.2f} km" if total_distance > 0 else "N/A")
282
  with col2:
283
+ hours = int(total_time // 3600) if total_time > 0 else 0
284
+ minutes = int((total_time % 3600) // 60) if total_time > 0 else 0
285
+ st.metric("⏱️ Tiempo", f"{hours}h {minutes}m" if total_time > 0 else "N/A")
286
+ with col3:
287
+ st.metric("📍 Puntos GPS", f"{len(heatmap_data):,}")
288
+
289
+ # Crear y mostrar mapas
290
+ st.markdown("### 🗺️ Visualización de Movimientos")
291
+
292
+ with st.spinner("🗺️ Generando mapas..."):
293
+ heat_map, spaghetti_map = create_maps(heatmap_data, track_segments, center)
294
+
295
+ if heat_map and spaghetti_map:
296
+ col1, col2 = st.columns(2)
297
 
298
+ with col1:
299
+ st.markdown("#### 🔥 Mapa de Calor")
300
+ folium_static(heat_map, width=600, height=500)
 
 
 
 
 
301
 
302
+ with col2:
303
+ st.markdown("#### 🍝 Diagrama de Spaghetti")
304
+ folium_static(spaghetti_map, width=600, height=500)
305
+
306
+ # Información técnica adicional
307
+ with st.expander("ℹ️ Información técnica"):
308
+ st.write(f"**Archivo:** {uploaded_file.name if uploaded_file else archivo_ejemplo_seleccionado}")
309
+ st.write(f"**Fuente:** {'Archivo cargado' if uploaded_file else 'Archivo de ejemplo'}")
310
+ st.write(f"**Tracks:** {len(set(seg['track_idx'] for seg in track_segments))}")
311
+ st.write(f"**Segmentos:** {len(track_segments)}")
312
+ st.write(f"**Centro:** {center[0]:.6f}, {center[1]:.6f}")
313
+ st.write(f"**Puntos totales:** {len(heatmap_data)}")
314
+ st.write(f"**Tamaño del archivo:** {len(gpx_content)} caracteres")
315
+
316
+ else:
317
+ st.error("❌ Error generando los mapas")
318
+
319
+ elif gpx_data and not gpx_data['success']:
320
+ st.error(f"❌ Error procesando GPX: {gpx_data['error']}")
321
 
322
+ # Debug completo
323
+ with st.expander("🔧 Información completa de debugging"):
324
+ st.write("**Información del archivo:**")
325
+ st.write(f"- Nombre: {uploaded_file.name if uploaded_file else archivo_ejemplo_seleccionado}")
326
+ st.write(f"- Tamaño: {len(gpx_content)} caracteres")
327
+ st.write(f"- Tipo de fuente: {'Cargado' if uploaded_file else 'Ejemplo'}")
328
+
329
+ st.write("**Primeros 1000 caracteres del archivo:**")
330
+ st.text(gpx_content[:1000] if gpx_content else "Sin contenido")
331
+
332
+ st.write("**Últimos 500 caracteres del archivo:**")
333
+ st.text(gpx_content[-500:] if gpx_content and len(gpx_content) > 500 else "Archivo muy corto")
334
+ else:
335
+ st.error("❌ Error desconocido procesando el archivo GPX")
336
 
337
+ # Mostrar ejemplos disponibles si no hay datos cargados
338
+ if data is None and example_files:
339
  st.markdown("### 📋 Ejemplos disponibles:")
340
  for i, file in enumerate(example_files, 1):
341
  st.write(f"{i}. **{file}**")
342
 
343
  # Footer
344
  st.markdown("---")
345
+ st.markdown("🚀 **Optimizado para Hugging Face Spaces** | 📊 **Compatible con apptest.py**", unsafe_allow_html=True)