Spaces:
Runtime error
Runtime error
fix: fallback bucket
#1
by Sarolanda - opened
- core/database.py +31 -13
core/database.py
CHANGED
|
@@ -13,8 +13,36 @@ import numpy as np
|
|
| 13 |
from PIL import Image
|
| 14 |
|
| 15 |
# βββ Paths βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
DB_PATH = DATA_DIR / "viralata.db"
|
| 19 |
PHOTOS_DIR = DATA_DIR / "photos"
|
| 20 |
SCHEMA = Path(__file__).parent.parent / "db" / "schema.sql"
|
|
@@ -74,7 +102,6 @@ class Database:
|
|
| 74 |
conn.execute("ALTER TABLE animals ADD COLUMN name TEXT")
|
| 75 |
|
| 76 |
# βββ Photos ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 77 |
-
|
| 78 |
def save_photo(self, image: Image.Image, animal_id: Optional[int] = None) -> str:
|
| 79 |
"""Salva foto organizada por animal_id. Ex: photos/animal_42/abc123.jpg"""
|
| 80 |
from datetime import datetime
|
|
@@ -97,7 +124,6 @@ class Database:
|
|
| 97 |
return str(full) if full.exists() else None
|
| 98 |
|
| 99 |
# βββ Animals βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 100 |
-
|
| 101 |
def create_animal(self, description: dict, embedding: list,
|
| 102 |
name: Optional[str] = None) -> int:
|
| 103 |
emb_blob = np.array(embedding, dtype=np.float32).tobytes()
|
|
@@ -149,7 +175,6 @@ class Database:
|
|
| 149 |
return result
|
| 150 |
|
| 151 |
# βββ Sightings βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 152 |
-
|
| 153 |
def add_sighting(
|
| 154 |
self,
|
| 155 |
animal_id: int,
|
|
@@ -206,11 +231,9 @@ class Database:
|
|
| 206 |
return {"animal": animal, "sightings": sightings, "help_events": help_events}
|
| 207 |
|
| 208 |
# βββ Map data ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 209 |
-
|
| 210 |
def get_map_data(self, species: str = "all", timeframe: str = "all") -> list:
|
| 211 |
filters = []
|
| 212 |
params = []
|
| 213 |
-
|
| 214 |
if species in ("dog", "cat"):
|
| 215 |
filters.append("a.species = ?")
|
| 216 |
params.append(species)
|
|
@@ -218,9 +241,7 @@ class Database:
|
|
| 218 |
filters.append("date(a.last_seen) = date('now')")
|
| 219 |
elif timeframe == "week":
|
| 220 |
filters.append("a.last_seen >= datetime('now', '-7 days')")
|
| 221 |
-
|
| 222 |
where = ("WHERE " + " AND ".join(filters)) if filters else ""
|
| 223 |
-
|
| 224 |
sql = (
|
| 225 |
"SELECT"
|
| 226 |
" a.id,"
|
|
@@ -243,10 +264,8 @@ class Database:
|
|
| 243 |
" " + where +
|
| 244 |
" ORDER BY a.last_seen DESC"
|
| 245 |
)
|
| 246 |
-
|
| 247 |
with self._conn() as conn:
|
| 248 |
rows = conn.execute(sql, params).fetchall()
|
| 249 |
-
|
| 250 |
result = []
|
| 251 |
for row in rows:
|
| 252 |
d = dict(row)
|
|
@@ -267,7 +286,6 @@ class Database:
|
|
| 267 |
return result
|
| 268 |
|
| 269 |
# βββ Animals list ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 270 |
-
|
| 271 |
def get_recent_animals(self, limit: int = 30) -> list:
|
| 272 |
sql = (
|
| 273 |
"SELECT"
|
|
@@ -296,4 +314,4 @@ class Database:
|
|
| 296 |
|
| 297 |
def total_animals(self) -> int:
|
| 298 |
with self._conn() as conn:
|
| 299 |
-
return conn.execute("SELECT COUNT(*) FROM animals").fetchone()[0]
|
|
|
|
| 13 |
from PIL import Image
|
| 14 |
|
| 15 |
# βββ Paths βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 16 |
+
def _is_writable(path: Path) -> bool:
|
| 17 |
+
"""Testa escrita REAL (nΓ£o sΓ³ os.access).
|
| 18 |
+
|
| 19 |
+
O HF Storage Bucket pode reportar W_OK=True nos bits de permissao
|
| 20 |
+
mas falhar em runtime com Errno 5 (EIO) β ex.: acesso revogado apos
|
| 21 |
+
o hackathon. O que vale Γ© a escrita de verdade.
|
| 22 |
+
"""
|
| 23 |
+
try:
|
| 24 |
+
path.mkdir(parents=True, exist_ok=True)
|
| 25 |
+
probe = path / ".write_test"
|
| 26 |
+
probe.write_text("ok") # o que realmente importa: a ESCRITA
|
| 27 |
+
except OSError:
|
| 28 |
+
return False
|
| 29 |
+
try:
|
| 30 |
+
probe.unlink() # limpeza best-effort
|
| 31 |
+
except OSError:
|
| 32 |
+
pass
|
| 33 |
+
return True
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def _pick_data_dir() -> Path:
|
| 37 |
+
"""Escolhe o primeiro destino realmente gravΓ‘vel."""
|
| 38 |
+
for candidate in (Path("/data"), Path("./data"), Path("/tmp/viralata-data")):
|
| 39 |
+
if _is_writable(candidate):
|
| 40 |
+
print(f"[database] DATA_DIR = {candidate}")
|
| 41 |
+
return candidate
|
| 42 |
+
return Path("/tmp/viralata-data")
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
DATA_DIR = _pick_data_dir()
|
| 46 |
DB_PATH = DATA_DIR / "viralata.db"
|
| 47 |
PHOTOS_DIR = DATA_DIR / "photos"
|
| 48 |
SCHEMA = Path(__file__).parent.parent / "db" / "schema.sql"
|
|
|
|
| 102 |
conn.execute("ALTER TABLE animals ADD COLUMN name TEXT")
|
| 103 |
|
| 104 |
# βββ Photos ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 105 |
def save_photo(self, image: Image.Image, animal_id: Optional[int] = None) -> str:
|
| 106 |
"""Salva foto organizada por animal_id. Ex: photos/animal_42/abc123.jpg"""
|
| 107 |
from datetime import datetime
|
|
|
|
| 124 |
return str(full) if full.exists() else None
|
| 125 |
|
| 126 |
# βββ Animals βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 127 |
def create_animal(self, description: dict, embedding: list,
|
| 128 |
name: Optional[str] = None) -> int:
|
| 129 |
emb_blob = np.array(embedding, dtype=np.float32).tobytes()
|
|
|
|
| 175 |
return result
|
| 176 |
|
| 177 |
# βββ Sightings βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 178 |
def add_sighting(
|
| 179 |
self,
|
| 180 |
animal_id: int,
|
|
|
|
| 231 |
return {"animal": animal, "sightings": sightings, "help_events": help_events}
|
| 232 |
|
| 233 |
# βββ Map data ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 234 |
def get_map_data(self, species: str = "all", timeframe: str = "all") -> list:
|
| 235 |
filters = []
|
| 236 |
params = []
|
|
|
|
| 237 |
if species in ("dog", "cat"):
|
| 238 |
filters.append("a.species = ?")
|
| 239 |
params.append(species)
|
|
|
|
| 241 |
filters.append("date(a.last_seen) = date('now')")
|
| 242 |
elif timeframe == "week":
|
| 243 |
filters.append("a.last_seen >= datetime('now', '-7 days')")
|
|
|
|
| 244 |
where = ("WHERE " + " AND ".join(filters)) if filters else ""
|
|
|
|
| 245 |
sql = (
|
| 246 |
"SELECT"
|
| 247 |
" a.id,"
|
|
|
|
| 264 |
" " + where +
|
| 265 |
" ORDER BY a.last_seen DESC"
|
| 266 |
)
|
|
|
|
| 267 |
with self._conn() as conn:
|
| 268 |
rows = conn.execute(sql, params).fetchall()
|
|
|
|
| 269 |
result = []
|
| 270 |
for row in rows:
|
| 271 |
d = dict(row)
|
|
|
|
| 286 |
return result
|
| 287 |
|
| 288 |
# βββ Animals list ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 289 |
def get_recent_animals(self, limit: int = 30) -> list:
|
| 290 |
sql = (
|
| 291 |
"SELECT"
|
|
|
|
| 314 |
|
| 315 |
def total_animals(self) -> int:
|
| 316 |
with self._conn() as conn:
|
| 317 |
+
return conn.execute("SELECT COUNT(*) FROM animals").fetchone()[0]
|