Spaces:
Running
Running
| import os | |
| import csv | |
| import re | |
| from datetime import datetime | |
| from flask import Flask, request, jsonify, send_from_directory | |
| from flask_cors import CORS | |
| from models import Capture, Species, ClosedSeason | |
| app = Flask(__name__, static_folder='static', static_url_path='') | |
| CORS(app) | |
| def slugify(text): | |
| """Simple slugify for species IDs.""" | |
| text = text.lower() | |
| text = re.sub(r'[^\w\s-]', '', text) | |
| return re.sub(r'[-\s]+', '-', text).strip('-') | |
| def load_species(): | |
| """Load unique species from the provided CSV file.""" | |
| species_list = [] | |
| seen_products = set() | |
| csv_path = 'produccion_pesca_limpia_refined.csv' | |
| # Pre-defined base species (with more metadata if available) | |
| base_metadata = { | |
| 'chillo': {'sci': 'Lutjanus campechanus', 'img': 'assets/species/chillo.jpg', 'veda': ClosedSeason(start='04-01', end='06-30', description='Veda de reproducci贸n')}, | |
| 'dorado': {'sci': 'Coryphaena hippurus', 'img': 'assets/species/dorado.jpg'}, | |
| 'langosta-comun-del-caribe': {'sci': 'Panulirus argus', 'img': 'assets/species/langosta.jpg', 'veda': ClosedSeason(start='03-01', end='06-30', description='Veda de reproducci贸n')} | |
| } | |
| if os.path.exists(csv_path): | |
| with open(csv_path, mode='r', encoding='utf-8') as f: | |
| reader = csv.DictReader(f) | |
| for row in reader: | |
| product_name = row['Producto'] | |
| category = row['categoria'] | |
| if product_name not in seen_products: | |
| species_id = slugify(product_name) | |
| metadata = base_metadata.get(species_id, {}) | |
| species_list.append(Species( | |
| id=species_id, | |
| commonName=product_name, | |
| scientificName=metadata.get('sci', ''), | |
| category=category, | |
| imageUrl=metadata.get('img', 'assets/species/placeholder.jpg'), | |
| protected=False, | |
| closedSeason=metadata.get('veda') | |
| )) | |
| seen_products.add(product_name) | |
| # If CSV failed or is empty, use defaults | |
| if not species_list: | |
| print("CSV not found or empty, using expanded fallback list") | |
| species_list = [ | |
| Species(id='chillo', commonName='Chillo', scientificName='Lutjanus campechanus', category='Peces', imageUrl='assets/species/chillo.jpg', protected=False, closedSeason=ClosedSeason(start='04-01', end='06-30', description='Veda de reproducci贸n')), | |
| Species(id='dorado', commonName='Dorado', scientificName='Coryphaena hippurus', category='Peces', imageUrl='assets/species/dorado.jpg', protected=False), | |
| Species(id='langosta', commonName='Langosta del Caribe', scientificName='Panulirus argus', category='Crust谩ceos', imageUrl='assets/species/langosta.jpg', protected=False, closedSeason=ClosedSeason(start='03-01', end='06-30', description='Veda de reproducci贸n')), | |
| Species(id='mero', commonName='Mero', scientificName='Epinephelus itajara', category='Peces', imageUrl='assets/species/placeholder.jpg', protected=False), | |
| Species(id='atun', commonName='At煤n', scientificName='Thunnus', category='Peces', imageUrl='assets/species/placeholder.jpg', protected=False), | |
| Species(id='pulpo', commonName='Pulpo', scientificName='Octopus vulgaris', category='Moluscos', imageUrl='assets/species/placeholder.jpg', protected=False), | |
| Species(id='lamb铆', commonName='Lamb铆', scientificName='Lobatus gigas', category='Moluscos', imageUrl='assets/species/placeholder.jpg', protected=False), | |
| ] | |
| return species_list | |
| # Load Data | |
| SPECIES_DATA = load_species() | |
| captures_storage = [] | |
| def index(): | |
| return app.send_static_file('index.html') | |
| def health_check(): | |
| return jsonify({ | |
| 'status': 'healthy', | |
| 'timestamp': datetime.now().isoformat() | |
| }) | |
| def get_species(): | |
| return jsonify([s.to_dict() for s in SPECIES_DATA]) | |
| def create_capture(): | |
| try: | |
| data = request.get_json() | |
| capture = Capture.from_dict(data) | |
| captures_storage.append(capture) | |
| return jsonify({'success': True, 'id': capture.id}), 201 | |
| except Exception as e: | |
| return jsonify({'success': False, 'error': str(e)}), 400 | |
| def sync_captures(): | |
| """Batch synchronization endpoint with Google Sheets forwarding.""" | |
| try: | |
| data = request.get_json() | |
| captures_data = data.get('captures', []) | |
| # Store locally (in-memory for now) | |
| for c_data in captures_data: | |
| capture = Capture.from_dict(c_data) | |
| captures_storage.append(capture) | |
| # Forward to Google Sheets if configured | |
| gas_url = os.environ.get('GAS_DEPLOYMENT_URL') | |
| if gas_url and captures_data: | |
| print(f"Forwarding {len(captures_data)} captures to Google Sheets...") | |
| for c in captures_data: | |
| # Explicitly log that these are device coordinates being forwarded | |
| print(f" [DEVICE SYNC] Capture {c.get('id')}:") | |
| print(f" - Port: {c.get('port')}") | |
| print(f" - Latitude: {c.get('latitude')} (captured on device)") | |
| print(f" - Longitude: {c.get('longitude')} (captured on device)") | |
| print(f" - Place: {c.get('placeName')}") | |
| try: | |
| import requests | |
| # Forward the exact payload expected by the GAS script | |
| print(f"DEBUG: Forwarding to GAS URL: {gas_url[:50]}...") | |
| response = requests.post(gas_url, json={'captures': captures_data}, timeout=15) | |
| print(f"DEBUG: GAS Response Status: {response.status_code}") | |
| print(f"DEBUG: GAS Response Body: {response.text}") | |
| if not response.ok: | |
| print(f"ERROR: Google Sheets sync failed. Status: {response.status_code}, Body: {response.text}") | |
| else: | |
| print(f"SUCCESS: Data accepted by Google Sheets: {response.text}") | |
| except Exception as e: | |
| print(f"CRITICAL: Error communicating with GAS: {str(e)}") | |
| synced_ids = [c.get('id') for c in captures_data] | |
| return jsonify({ | |
| 'success': True, | |
| 'synced': synced_ids, | |
| 'total': len(synced_ids) | |
| }), 200 | |
| except Exception as e: | |
| print(f"Sync error: {str(e)}") | |
| return jsonify({'success': False, 'error': str(e)}), 400 | |
| def debug_files(): | |
| files = [] | |
| for root, _, filenames in os.walk('static'): | |
| for f in filenames: | |
| rel_path = os.path.relpath(os.path.join(root, f), 'static') | |
| files.append({ | |
| "path": rel_path, | |
| "size": os.path.getsize(os.path.join(root, f)) | |
| }) | |
| return jsonify(files) | |
| if __name__ == '__main__': | |
| port = int(os.environ.get('PORT', 7860)) | |
| app.run(host='0.0.0.0', port=port) | |
| # Deployment trigger: 2026-01-23 18:55 | |