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 = [] @app.route('/') def index(): return app.send_static_file('index.html') @app.route('/health') def health_check(): return jsonify({ 'status': 'healthy', 'timestamp': datetime.now().isoformat() }) @app.route('/api/species', methods=['GET']) def get_species(): return jsonify([s.to_dict() for s in SPECIES_DATA]) @app.route('/api/captures', methods=['POST']) 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 @app.route('/api/sync', methods=['POST']) 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 @app.route('/debug/files') 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