Migue1804 commited on
Commit
7e445a9
·
verified ·
1 Parent(s): 29ddd00

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +132 -150
src/streamlit_app.py CHANGED
@@ -155,191 +155,173 @@ 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)
 
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)