fix: fallback bucket

#1
by Sarolanda - opened
Files changed (1) hide show
  1. 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
- _hf_data = Path("/data")
17
- DATA_DIR = _hf_data if (_hf_data.exists() and os.access(_hf_data, os.W_OK)) else Path("./data")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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]