feat: migrate backend from SQLite to Supabase PostgreSQL
This commit is contained in:
@@ -1,5 +1,11 @@
|
|||||||
SECRET_KEY=your-super-secret-key-change-this-in-production
|
SECRET_KEY=your-super-secret-key-change-this-in-production
|
||||||
DEBUG=true
|
DEBUG=true
|
||||||
DATABASE_PATH=/data/basura.db
|
|
||||||
|
# Supabase PostgreSQL
|
||||||
|
SUPABASE_URL=https://qckndtzudciejpnwqfzt.supabase.co
|
||||||
|
SUPABASE_ANON_KEY=sb_publishable_FQR0WXK6joM043Qve9gz3A_pJfAH...
|
||||||
|
SUPABASE_SERVICE_KEY=sb_secret_2y3a_...
|
||||||
|
|
||||||
|
# Simulador
|
||||||
SIM_TICK_SECONDS=10
|
SIM_TICK_SECONDS=10
|
||||||
SIM_ETA_ALERT_MINUTES=10
|
SIM_ETA_ALERT_MINUTES=10
|
||||||
@@ -1,134 +1,138 @@
|
|||||||
"""
|
"""
|
||||||
Capa de Datos — SQLite con soporte de caché
|
Capa de Datos — Supabase PostgreSQL con caché
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from app.db.database import get_connection
|
from app.db.database import get_db
|
||||||
from app.domain.entities.ruta import (
|
from app.domain.entities.ruta import (
|
||||||
Coordenada, EstadoCamion, ETAResult,
|
Coordenada, EstadoCamion, ETAResult,
|
||||||
NotificationPreferences, PuntoRuta, Ruta, TruckStatus,
|
NotificationPreferences, PuntoRuta, Ruta, TruckStatus,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SQLiteRutaRepository:
|
class SupabaseRutaRepository:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.db = get_db()
|
||||||
|
|
||||||
# ── Rutas y puntos ────────────────────────────────────────────────
|
# ── Rutas y puntos ────────────────────────────────────────────────
|
||||||
|
|
||||||
def obtener_ruta(self, route_id: str) -> Optional[Ruta]:
|
def obtener_ruta(self, route_id: str) -> Optional[Ruta]:
|
||||||
conn = get_connection()
|
try:
|
||||||
row = conn.execute(
|
ruta_data = self.db.table("rutas").select("*").eq("id", route_id).execute()
|
||||||
"SELECT * FROM rutas WHERE id = ?", (route_id,)
|
if not ruta_data.data:
|
||||||
).fetchone()
|
return None
|
||||||
if not row:
|
|
||||||
conn.close()
|
ruta_row = ruta_data.data[0]
|
||||||
return None
|
puntos_data = self.db.table("puntos_ruta").select("*").eq("ruta_id", route_id).order("orden").execute()
|
||||||
puntos = [
|
|
||||||
PuntoRuta(
|
puntos = [
|
||||||
orden=p["orden"],
|
PuntoRuta(
|
||||||
nombre=p["nombre"],
|
orden=p["orden"],
|
||||||
coordenada=Coordenada(p["lat"], p["lng"]),
|
nombre=p["nombre"],
|
||||||
tiempo_estimado_min=p["tiempo_estimado_min"],
|
coordenada=Coordenada(p["lat"], p["lng"]),
|
||||||
|
tiempo_estimado_min=p["tiempo_estimado_min"],
|
||||||
|
)
|
||||||
|
for p in puntos_data.data
|
||||||
|
]
|
||||||
|
|
||||||
|
return Ruta(
|
||||||
|
id=ruta_row["id"],
|
||||||
|
nombre=ruta_row["nombre"],
|
||||||
|
puntos=puntos,
|
||||||
|
turno=ruta_row["turno"]
|
||||||
)
|
)
|
||||||
for p in conn.execute(
|
except Exception as e:
|
||||||
"SELECT * FROM puntos_ruta WHERE ruta_id=? ORDER BY orden",
|
print(f"Error obtener_ruta: {e}")
|
||||||
(route_id,),
|
return None
|
||||||
).fetchall()
|
|
||||||
]
|
|
||||||
conn.close()
|
|
||||||
return Ruta(id=row["id"], nombre=row["nombre"],
|
|
||||||
puntos=puntos, turno=row["turno"])
|
|
||||||
|
|
||||||
def obtener_ruta_por_address(self, address_id: int) -> Optional[Ruta]:
|
def obtener_ruta_por_address(self, address_id: int) -> Optional[Ruta]:
|
||||||
conn = get_connection()
|
try:
|
||||||
row = conn.execute(
|
addr_data = self.db.table("addresses").select("route_id").eq("id", address_id).execute()
|
||||||
"SELECT route_id FROM addresses WHERE id = ?", (address_id,)
|
if not addr_data.data:
|
||||||
).fetchone()
|
return None
|
||||||
conn.close()
|
return self.obtener_ruta(addr_data.data[0]["route_id"])
|
||||||
if not row:
|
except Exception as e:
|
||||||
|
print(f"Error obtener_ruta_por_address: {e}")
|
||||||
return None
|
return None
|
||||||
return self.obtener_ruta(row["route_id"])
|
|
||||||
|
|
||||||
# ── truck_status ──────────────────────────────────────────
|
# ── truck_status ──────────────────────────────────────────
|
||||||
|
|
||||||
def obtener_truck_status(self, route_id: str) -> Optional[TruckStatus]:
|
def obtener_truck_status(self, route_id: str) -> Optional[TruckStatus]:
|
||||||
conn = get_connection()
|
try:
|
||||||
row = conn.execute(
|
ts_data = self.db.table("truck_status").select("*").eq("route_id", route_id).execute()
|
||||||
"SELECT * FROM truck_status WHERE route_id = ?", (route_id,)
|
if not ts_data.data:
|
||||||
).fetchone()
|
return None
|
||||||
conn.close()
|
|
||||||
if not row:
|
row = ts_data.data[0]
|
||||||
|
return TruckStatus(
|
||||||
|
route_id=row["route_id"],
|
||||||
|
current_position_id=row["current_position_id"],
|
||||||
|
last_update=datetime.fromisoformat(row["last_update"].replace("Z", "+00:00")),
|
||||||
|
status=EstadoCamion(row["status"]),
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error obtener_truck_status: {e}")
|
||||||
return None
|
return None
|
||||||
return TruckStatus(
|
|
||||||
route_id=row["route_id"],
|
|
||||||
current_position_id=row["current_position_id"],
|
|
||||||
last_update=datetime.fromisoformat(row["last_update"]),
|
|
||||||
status=EstadoCamion(row["status"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
def guardar_truck_status(self, ts: TruckStatus) -> None:
|
def guardar_truck_status(self, ts: TruckStatus) -> None:
|
||||||
conn = get_connection()
|
try:
|
||||||
conn.execute("""
|
self.db.table("truck_status").upsert({
|
||||||
INSERT INTO truck_status (route_id, current_position_id, last_update, status)
|
"route_id": ts.route_id,
|
||||||
VALUES (?, ?, ?, ?)
|
"current_position_id": ts.current_position_id,
|
||||||
ON CONFLICT(route_id) DO UPDATE SET
|
"last_update": ts.last_update.isoformat(),
|
||||||
current_position_id = excluded.current_position_id,
|
"status": ts.status.value,
|
||||||
last_update = excluded.last_update,
|
}).execute()
|
||||||
status = excluded.status
|
|
||||||
""", (
|
# Invalidar caché
|
||||||
ts.route_id,
|
from app.core.cache import invalidate_route_cache
|
||||||
ts.current_position_id,
|
import asyncio
|
||||||
ts.last_update.isoformat(),
|
asyncio.create_task(invalidate_route_cache(ts.route_id))
|
||||||
ts.status.value,
|
except Exception as e:
|
||||||
))
|
print(f"Error guardar_truck_status: {e}")
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# Invalidar caché de esta ruta
|
|
||||||
from app.core.cache import invalidate_route_cache
|
|
||||||
import asyncio
|
|
||||||
asyncio.create_task(invalidate_route_cache(ts.route_id))
|
|
||||||
|
|
||||||
# ── Preferencias de notificación ─────────────────────────────────
|
# ── Preferencias de notificación ─────────────────────────────────
|
||||||
|
|
||||||
def obtener_preferencias(self, user_id: int) -> NotificationPreferences:
|
def obtener_preferencias(self, user_id: str) -> NotificationPreferences:
|
||||||
conn = get_connection()
|
try:
|
||||||
row = conn.execute(
|
prefs_data = self.db.table("notification_preferences").select("*").eq("user_id", user_id).execute()
|
||||||
"SELECT * FROM notification_preferences WHERE user_id = ?",
|
if not prefs_data.data:
|
||||||
(user_id,),
|
return NotificationPreferences(user_id=user_id)
|
||||||
).fetchone()
|
|
||||||
conn.close()
|
row = prefs_data.data[0]
|
||||||
if not row:
|
return NotificationPreferences(
|
||||||
|
user_id=user_id,
|
||||||
|
notify_proximity=row["notify_proximity"],
|
||||||
|
notify_breakdown=row["notify_breakdown"],
|
||||||
|
notify_delay=row["notify_delay"],
|
||||||
|
notify_route_start=row["notify_route_start"],
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error obtener_preferencias: {e}")
|
||||||
return NotificationPreferences(user_id=user_id)
|
return NotificationPreferences(user_id=user_id)
|
||||||
return NotificationPreferences(
|
|
||||||
user_id=user_id,
|
|
||||||
notify_proximity=bool(row["notify_proximity"]),
|
|
||||||
notify_breakdown=bool(row["notify_breakdown"]),
|
|
||||||
notify_delay=bool(row["notify_delay"]),
|
|
||||||
notify_route_start=bool(row["notify_route_start"]),
|
|
||||||
)
|
|
||||||
|
|
||||||
def obtener_usuarios_por_ruta(self, route_id: str) -> list[dict]:
|
def obtener_usuarios_por_ruta(self, route_id: str) -> list[dict]:
|
||||||
conn = get_connection()
|
try:
|
||||||
rows = conn.execute(
|
users_data = self.db.table("addresses").select("id, user_id").eq("route_id", route_id).execute()
|
||||||
"SELECT id as address_id, user_id FROM addresses WHERE route_id = ?",
|
return [{"address_id": u["id"], "user_id": u["user_id"]} for u in users_data.data]
|
||||||
(route_id,),
|
except Exception as e:
|
||||||
).fetchall()
|
print(f"Error obtener_usuarios_por_ruta: {e}")
|
||||||
conn.close()
|
return []
|
||||||
return [dict(r) for r in rows]
|
|
||||||
|
|
||||||
# ── Templates de notificación ─────────────────────────────────────
|
# ── Templates de notificación ─────────────────────────────────────
|
||||||
|
|
||||||
def obtener_template(self, trigger_event: str) -> Optional[dict]:
|
def obtener_template(self, trigger_event: str) -> Optional[dict]:
|
||||||
conn = get_connection()
|
try:
|
||||||
row = conn.execute(
|
template_data = self.db.table("notification_templates").select("*").eq("trigger_event", trigger_event).execute()
|
||||||
"SELECT * FROM notification_templates WHERE trigger_event = ?",
|
if not template_data.data:
|
||||||
(trigger_event,),
|
return None
|
||||||
).fetchone()
|
return template_data.data[0]
|
||||||
conn.close()
|
except Exception as e:
|
||||||
return dict(row) if row else None
|
print(f"Error obtener_template: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
# ── ETA calculado con soporte de caché ────────────────────────────
|
# ── ETA calculado ────────────────────────────────────────────────
|
||||||
|
|
||||||
def calcular_eta(self, address_id: int) -> Optional[ETAResult]:
|
def calcular_eta(self, address_id: int) -> Optional[ETAResult]:
|
||||||
ruta = self.obtener_ruta_por_address(address_id)
|
ruta = self.obtener_ruta_por_address(address_id)
|
||||||
@@ -151,7 +155,6 @@ class SQLiteRutaRepository:
|
|||||||
|
|
||||||
eta_min = max(0, ultimo.tiempo_estimado_min - actual.tiempo_estimado_min)
|
eta_min = max(0, ultimo.tiempo_estimado_min - actual.tiempo_estimado_min)
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
ahora = datetime.now()
|
ahora = datetime.now()
|
||||||
llegada = ahora + timedelta(minutes=eta_min)
|
llegada = ahora + timedelta(minutes=eta_min)
|
||||||
v_ini = (llegada - timedelta(minutes=7)).strftime("%I:%M %p").lstrip("0")
|
v_ini = (llegada - timedelta(minutes=7)).strftime("%I:%M %p").lstrip("0")
|
||||||
@@ -178,12 +181,18 @@ class SQLiteRutaRepository:
|
|||||||
def guardar_notificacion(self, tipo: str, route_id: str,
|
def guardar_notificacion(self, tipo: str, route_id: str,
|
||||||
address_id: int, mensaje: str,
|
address_id: int, mensaje: str,
|
||||||
eta_minutos: Optional[int]) -> None:
|
eta_minutos: Optional[int]) -> None:
|
||||||
conn = get_connection()
|
try:
|
||||||
conn.execute("""
|
self.db.table("notificaciones").insert({
|
||||||
INSERT INTO notificaciones
|
"tipo": tipo,
|
||||||
(tipo, ruta_id, address_id, mensaje, eta_minutos, creada_en)
|
"ruta_id": route_id,
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
"address_id": address_id,
|
||||||
""", (tipo, route_id, address_id, mensaje,
|
"mensaje": mensaje,
|
||||||
eta_minutos, datetime.utcnow().isoformat()))
|
"eta_minutos": eta_minutos,
|
||||||
conn.commit()
|
"creada_en": datetime.utcnow().isoformat(),
|
||||||
conn.close()
|
}).execute()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error guardar_notificacion: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# Alias para compatibilidad
|
||||||
|
SQLiteRutaRepository = SupabaseRutaRepository
|
||||||
|
|||||||
@@ -1,142 +1,82 @@
|
|||||||
"""
|
"""
|
||||||
Base de datos SQLite — esquema unificado con Persona A.
|
Base de datos Supabase PostgreSQL — conexión y utilidades.
|
||||||
Tablas propias del módulo B: truck_status, notificaciones, ws_sessions.
|
|
||||||
"""
|
"""
|
||||||
import sqlite3
|
import os
|
||||||
from pathlib import Path
|
from supabase import create_client, Client
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
DB_PATH = Path("basura.db")
|
load_dotenv()
|
||||||
|
|
||||||
|
SUPABASE_URL = os.getenv("SUPABASE_URL", "https://qckndtzudciejpnwqfzt.supabase.co")
|
||||||
|
SUPABASE_KEY = os.getenv("SUPABASE_ANON_KEY", "sb_publishable_FQR0WXK6joM043Qve9gz3A_pJfAH...")
|
||||||
|
|
||||||
|
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
|
||||||
|
|
||||||
|
|
||||||
def get_connection() -> sqlite3.Connection:
|
def get_db() -> Client:
|
||||||
conn = sqlite3.connect(DB_PATH, check_same_thread=False)
|
"""Retorna cliente Supabase."""
|
||||||
conn.row_factory = sqlite3.Row
|
return supabase
|
||||||
conn.execute("PRAGMA journal_mode=WAL")
|
|
||||||
conn.execute("PRAGMA foreign_keys=ON")
|
|
||||||
return conn
|
|
||||||
|
|
||||||
|
|
||||||
def init_db() -> None:
|
async def init_db() -> None:
|
||||||
conn = get_connection()
|
"""
|
||||||
conn.executescript("""
|
Inicializa BD. En Supabase, tablas ya existen en el schema_supabase.sql.
|
||||||
-- ── Tablas de Persona A (las creamos aquí para que el módulo B
|
Esta función valida conexión y seed data si es necesario.
|
||||||
-- pueda leerlas aunque A no haya corrido aún) ──────────────
|
"""
|
||||||
|
try:
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
# Valida conexión leyendo rutas
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
result = supabase.table("rutas").select("id").eq("id", "RUTA-01").execute()
|
||||||
email TEXT UNIQUE NOT NULL,
|
if not result.data:
|
||||||
phone TEXT,
|
# Seed data si no existe
|
||||||
password_hash TEXT NOT NULL,
|
_seed_datos_demo()
|
||||||
fcm_token TEXT,
|
print("✓ BD Supabase inicializada")
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
except Exception as e:
|
||||||
);
|
print(f"✗ Error inicialización BD: {e}")
|
||||||
|
raise
|
||||||
CREATE TABLE IF NOT EXISTS addresses (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
alias TEXT,
|
|
||||||
lat REAL NOT NULL,
|
|
||||||
lng REAL NOT NULL,
|
|
||||||
route_id TEXT NOT NULL,
|
|
||||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS notification_preferences (
|
|
||||||
user_id INTEGER PRIMARY KEY,
|
|
||||||
notify_proximity BOOLEAN DEFAULT 1,
|
|
||||||
notify_breakdown BOOLEAN DEFAULT 1,
|
|
||||||
notify_delay BOOLEAN DEFAULT 1,
|
|
||||||
notify_route_start BOOLEAN DEFAULT 1,
|
|
||||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS notification_templates (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
trigger_event TEXT UNIQUE,
|
|
||||||
title TEXT,
|
|
||||||
body TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
-- ── Tablas del módulo B ───────────────────────────────────────
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS truck_status (
|
|
||||||
route_id TEXT PRIMARY KEY,
|
|
||||||
current_position_id INTEGER DEFAULT 1,
|
|
||||||
last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
||||||
status TEXT DEFAULT 'EN_RUTA'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS rutas (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
nombre TEXT NOT NULL,
|
|
||||||
turno TEXT NOT NULL DEFAULT 'mañana'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS puntos_ruta (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
ruta_id TEXT NOT NULL REFERENCES rutas(id),
|
|
||||||
orden INTEGER NOT NULL,
|
|
||||||
nombre TEXT NOT NULL,
|
|
||||||
lat REAL NOT NULL,
|
|
||||||
lng REAL NOT NULL,
|
|
||||||
tiempo_estimado_min INTEGER NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS notificaciones (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
tipo TEXT NOT NULL,
|
|
||||||
ruta_id TEXT NOT NULL,
|
|
||||||
address_id INTEGER,
|
|
||||||
mensaje TEXT NOT NULL,
|
|
||||||
eta_minutos INTEGER,
|
|
||||||
creada_en TEXT NOT NULL
|
|
||||||
);
|
|
||||||
""")
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
_seed_datos_demo()
|
|
||||||
|
|
||||||
|
|
||||||
def _seed_datos_demo() -> None:
|
def _seed_datos_demo() -> None:
|
||||||
conn = get_connection()
|
"""Inserta datos demo si no existen."""
|
||||||
existe = conn.execute("SELECT 1 FROM rutas WHERE id='RUTA-01'").fetchone()
|
try:
|
||||||
if existe:
|
# Rutas
|
||||||
conn.close()
|
supabase.table("rutas").insert({
|
||||||
return
|
"id": "RUTA-01",
|
||||||
|
"nombre": "Ruta 01 — Sector Centro",
|
||||||
|
"turno": "mañana"
|
||||||
|
}).execute()
|
||||||
|
|
||||||
conn.executescript("""
|
# Puntos ruta
|
||||||
-- Ruta de demo (Celaya, Guanajuato)
|
puntos = [
|
||||||
INSERT INTO rutas VALUES ('RUTA-01', 'Ruta 01 — Sector Centro', 'mañana');
|
{"ruta_id": "RUTA-01", "orden": 1, "nombre": "Estación Central", "lat": 20.5238, "lng": -100.8143, "tiempo_estimado_min": 0},
|
||||||
|
{"ruta_id": "RUTA-01", "orden": 2, "nombre": "Col. Independencia", "lat": 20.5255, "lng": -100.8090, "tiempo_estimado_min": 8},
|
||||||
|
{"ruta_id": "RUTA-01", "orden": 3, "nombre": "Blvd. A. López Mateos", "lat": 20.5271, "lng": -100.8021, "tiempo_estimado_min": 18},
|
||||||
|
{"ruta_id": "RUTA-01", "orden": 4, "nombre": "Col. Jardines del Bosque", "lat": 20.5290, "lng": -100.7965, "tiempo_estimado_min": 28},
|
||||||
|
{"ruta_id": "RUTA-01", "orden": 5, "nombre": "Mercado Hidalgo", "lat": 20.5310, "lng": -100.7910, "tiempo_estimado_min": 38},
|
||||||
|
]
|
||||||
|
for punto in puntos:
|
||||||
|
supabase.table("puntos_ruta").insert(punto).execute()
|
||||||
|
|
||||||
INSERT INTO puntos_ruta (ruta_id, orden, nombre, lat, lng, tiempo_estimado_min)
|
# Truck status
|
||||||
VALUES
|
supabase.table("truck_status").insert({
|
||||||
('RUTA-01', 1, 'Estación Central', 20.5238, -100.8143, 0),
|
"route_id": "RUTA-01",
|
||||||
('RUTA-01', 2, 'Col. Independencia', 20.5255, -100.8090, 8),
|
"current_position_id": 1,
|
||||||
('RUTA-01', 3, 'Blvd. A. López Mateos', 20.5271, -100.8021, 18),
|
"status": "EN_RUTA"
|
||||||
('RUTA-01', 4, 'Col. Jardines del Bosque', 20.5290, -100.7965, 28),
|
}).execute()
|
||||||
('RUTA-01', 5, 'Mercado Hidalgo', 20.5310, -100.7910, 38);
|
|
||||||
|
|
||||||
INSERT INTO truck_status VALUES ('RUTA-01', 1, CURRENT_TIMESTAMP, 'EN_RUTA');
|
# Notification templates
|
||||||
|
templates = [
|
||||||
|
{"trigger_event": "ruta_iniciada", "title": "Ruta iniciada", "body": "El camión ha comenzado su ruta. Prepárate."},
|
||||||
|
{"trigger_event": "aproximandose", "title": "¡Camión cerca!", "body": "El camión llega en ~{eta} minutos. Saca tu basura."},
|
||||||
|
{"trigger_event": "falla_mecanica", "title": "Aviso de servicio", "body": "El camión reportó una falla. Te notificaremos cuando se reanude."},
|
||||||
|
{"trigger_event": "ruta_tarde", "title": "Cambio de horario", "body": "El camión de la mañana pasará en el turno de la tarde."},
|
||||||
|
{"trigger_event": "completado", "title": "Ruta completada", "body": "El camión completó su paso por tu zona. ¡Hasta mañana!"},
|
||||||
|
]
|
||||||
|
for template in templates:
|
||||||
|
try:
|
||||||
|
supabase.table("notification_templates").insert(template).execute()
|
||||||
|
except:
|
||||||
|
pass # Ignorar duplicados
|
||||||
|
|
||||||
-- Usuario de demo
|
print("✓ Datos demo insertados")
|
||||||
INSERT INTO users (email, phone, password_hash)
|
except Exception as e:
|
||||||
VALUES ('demo@basura.app', '4611234567', 'hashed_demo');
|
print(f"✗ Error seed data: {e}")
|
||||||
|
|
||||||
-- Domicilio de demo asignado a RUTA-01
|
|
||||||
INSERT INTO addresses (user_id, alias, lat, lng, route_id)
|
|
||||||
VALUES (1, 'Casa', 20.5285, -100.7980, 'RUTA-01');
|
|
||||||
|
|
||||||
-- Preferencias por defecto para usuario demo
|
|
||||||
INSERT INTO notification_preferences VALUES (1, 1, 1, 1, 1);
|
|
||||||
|
|
||||||
-- Templates de notificación
|
|
||||||
INSERT INTO notification_templates (trigger_event, title, body) VALUES
|
|
||||||
('ruta_iniciada', 'Ruta iniciada', 'El camión ha comenzado su ruta. Prepárate.'),
|
|
||||||
('aproximandose', '¡Camión cerca!', 'El camión llega en ~{eta} minutos. Saca tu basura.'),
|
|
||||||
('falla_mecanica', 'Aviso de servicio', 'El camión reportó una falla. Te notificaremos cuando se reanude.'),
|
|
||||||
('ruta_tarde', 'Cambio de horario', 'El camión de la mañana pasará en el turno de la tarde.'),
|
|
||||||
('completado', 'Ruta completada', 'El camión completó su paso por tu zona. ¡Hasta mañana!');
|
|
||||||
""")
|
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ pyjwt==2.8.0
|
|||||||
bcrypt==4.1.2
|
bcrypt==4.1.2
|
||||||
email-validator==2.1.0
|
email-validator==2.1.0
|
||||||
redis==5.0.1
|
redis==5.0.1
|
||||||
hiredis==2.3.2
|
hiredis==2.3.2
|
||||||
|
supabase==2.3.4
|
||||||
|
psycopg2-binary==2.9.9
|
||||||
Reference in New Issue
Block a user