Migue1804 commited on
Commit
8f9bbae
·
verified ·
1 Parent(s): 4d4a7fb

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +94 -178
src/streamlit_app.py CHANGED
@@ -10,6 +10,7 @@ import pandas as pd
10
  import random
11
  import os
12
  import io
 
13
 
14
  # Configuración de la página
15
  st.set_page_config(page_title="🌍 Analizador de tiempos y movimientos Avanzado", layout="wide")
@@ -29,12 +30,17 @@ app_image = load_app_image()
29
  if app_image:
30
  st.image(app_image, use_container_width=True)
31
 
32
- # Función para procesar GPX
33
- def process_gpx_data(gpx_content):
34
- """Procesa contenido GPX y extrae datos para mapas"""
35
  try:
36
- # Parsear GPX
37
- gpx = gpxpy.parse(gpx_content)
 
 
 
 
 
38
 
39
  # Extraer datos
40
  heatmap_data = []
@@ -155,191 +161,101 @@ 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
- # ========== 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)
 
10
  import random
11
  import os
12
  import io
13
+ import tempfile
14
 
15
  # Configuración de la página
16
  st.set_page_config(page_title="🌍 Analizador de tiempos y movimientos Avanzado", layout="wide")
 
30
  if app_image:
31
  st.image(app_image, use_container_width=True)
32
 
33
+ # Función para procesar GPX sin caché primero para debugging
34
+ def process_gpx_simple(file_content_or_path, is_file_path=False):
35
+ """Procesa archivo GPX de manera simple y directa"""
36
  try:
37
+ if is_file_path:
38
+ # Es una ruta de archivo (ejemplo)
39
+ with open(file_content_or_path, 'r', encoding='utf-8') as f:
40
+ gpx = gpxpy.parse(f)
41
+ else:
42
+ # Es contenido de archivo (cargado)
43
+ gpx = gpxpy.parse(file_content_or_path)
44
 
45
  # Extraer datos
46
  heatmap_data = []
 
161
  if os.path.exists(examples_folder):
162
  example_files = [f for f in os.listdir(examples_folder) if f.endswith(".gpx")]
163
 
164
+ # Interface de carga
165
+ uploaded_file = st.file_uploader("📁 Cargar archivo GPX", type="gpx")
166
+ if uploaded_file is not None:
167
+ selected_example = "Ninguno" # Forzar que no se use el ejemplo si se carga archivo
168
+ else:
169
+ selected_example = st.selectbox("📋 O selecciona un ejemplo:", ["Ninguno"] + example_files)
 
 
 
 
 
 
 
 
170
 
171
+ gpx_data = None
172
+ file_name = None
 
173
 
174
+ if uploaded_file is not None:
175
+ selected_example = "Ninguno" # Ignorar ejemplos si se carga archivo
176
+ file_name = uploaded_file.name
177
+ st.info(f"📄 Archivo seleccionado: {file_name}")
178
  try:
179
+ # Leer contenido como texto directamente
180
+ file_bytes = uploaded_file.read()
181
+ file_content = file_bytes.decode("utf-8", errors="ignore")
182
+
183
+ if file_content and "<?xml" in file_content:
184
+ st.success(" Archivo GPX leído correctamente")
185
+ with st.spinner("🔄 Procesando archivo GPX..."):
186
+ gpx_data = process_gpx_simple(file_content)
187
+ else:
188
+ st.error("❌ El archivo no parece ser un GPX válido")
 
189
  except Exception as e:
190
+ st.error(f"Error procesando archivo: {e}")
191
 
192
+ elif selected_example != "Ninguno":
193
+ file_name = selected_example
194
+ file_path = os.path.join(examples_folder, selected_example)
195
+ st.info(f"📄 Ejemplo seleccionado: {file_name}")
196
+ with st.spinner("🔄 Procesando archivo de ejemplo..."):
197
+ gpx_data = process_gpx_simple(file_path, is_file_path=True)
198
 
199
+ # MOSTRAR RESULTADOS
200
+ if gpx_data and gpx_data['success']:
201
+ st.success("🎉 ¡Archivo procesado exitosamente!")
202
+
203
+ # Métricas
204
+ heatmap_data = gpx_data['heatmap_data']
205
+ track_segments = gpx_data['track_segments']
206
+ total_distance = gpx_data['total_distance']
207
+ total_time = gpx_data['total_time']
208
+ center = gpx_data['center']
209
+
210
+ # Dashboard
211
+ st.markdown("### 📊 Resumen de la Ruta")
212
+ col1, col2, col3 = st.columns(3)
213
+
214
+ with col1:
215
+ st.metric("📏 Distancia", f"{total_distance/1000:.2f} km" if total_distance > 0 else "N/A")
216
+ with col2:
217
+ hours = int(total_time // 3600)
218
+ minutes = int((total_time % 3600) // 60)
219
+ st.metric("⏱️ Tiempo", f"{hours}h {minutes}m" if total_time > 0 else "N/A")
220
+ with col3:
221
+ st.metric("📍 Puntos GPS", f"{len(heatmap_data):,}")
222
+
223
+ # Crear y mostrar mapas
224
+ st.markdown("### 🗺️ Visualización de Movimientos")
225
+
226
+ with st.spinner("🗺️ Generando mapas..."):
227
+ heat_map, spaghetti_map = create_maps(heatmap_data, track_segments, center)
228
+
229
+ if heat_map and spaghetti_map:
230
+ col1, col2 = st.columns(2)
231
 
232
+ with col1:
233
+ st.markdown("#### 🔥 Mapa de Calor")
234
+ folium_static(heat_map, width=600, height=500)
 
235
 
236
+ with col2:
237
+ st.markdown("#### 🍝 Diagrama de Spaghetti")
238
+ folium_static(spaghetti_map, width=600, height=500)
 
 
 
 
 
 
 
 
 
 
239
 
240
+ # Info adicional
241
+ with st.expander("ℹ️ Información técnica"):
242
+ st.write(f"**Tracks:** {len(set(seg['track_idx'] for seg in track_segments))}")
243
+ st.write(f"**Segmentos:** {len(track_segments)}")
244
+ st.write(f"**Centro:** {center[0]:.6f}, {center[1]:.6f}")
245
+ else:
246
+ st.error("❌ Error generando los mapas")
247
+
248
+ elif gpx_data and not gpx_data['success']:
249
+ st.error(f"❌ Error: {gpx_data['error']}")
250
 
 
 
 
251
  else:
252
+ st.info("📤 Por favor, carga un archivo GPX o selecciona un ejemplo para comenzar.")
253
 
254
+ if example_files:
255
+ st.markdown("### 📋 Ejemplos disponibles:")
256
+ for i, file in enumerate(example_files, 1):
257
+ st.write(f"{i}. **{file}**")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  # Footer
260
  st.markdown("---")
261
+ st.markdown("🚀 **Optimizado para Hugging Face Spaces**", unsafe_allow_html=True)