pawmap / core /seed.py
Sarolanda's picture
adds lotties
c02f736
"""
seed.py — Dados de demonstração para o mapa de Brasília, DF.
Chamado no startup do app.py. Garante que os 6 animais fixos estão sempre no banco.
"""
import json
import logging
import shutil
from datetime import datetime, timedelta
from pathlib import Path
import numpy as np
log = logging.getLogger(__name__)
_SEED_ANIMALS = [
{
"species": "dog",
"name": "Pretão",
"photo_filename": "pretao.jpg",
"description": {
"species": "dog",
"breed_estimate": "SRD",
"size": "médio",
"primary_color": "preto",
"secondary_colors": ["caramelo"],
"distinctive_marks": ["focinho caramelo", "patas caramelo"],
"condition": "saudável",
"description_text": "medium black dog with caramel markings on face and paws, calm and friendly",
"_ai_success": True,
},
"sightings": [
{"lat": -15.7560, "lng": -47.8920, "days_ago": 10, "notes": "Deitado na calçada da 406 Norte, aceitou carinho"},
{"lat": -15.7555, "lng": -47.8912, "days_ago": 3, "notes": "Voltou pro mesmo ponto, parece ter território fixo"},
],
},
{
"species": "dog",
"name": "Mel",
"photo_filename": "mel.webp",
"description": {
"species": "dog",
"breed_estimate": "SRD",
"size": "médio",
"primary_color": "branco",
"secondary_colors": ["caramelo"],
"distinctive_marks": ["manchas caramelo nas costas", "orelhas caramelo"],
"condition": "saudável",
"description_text": "medium white dog with caramel patches, resting near closed shop",
"_ai_success": True,
},
"sightings": [
{"lat": -15.8450, "lng": -47.8700, "days_ago": 20, "notes": "Próximo à feira do Lago Sul, dócil"},
{"lat": -15.8442, "lng": -47.8692, "days_ago": 8, "notes": "Mesma área, aparenta bem alimentada"},
{"lat": -15.8438, "lng": -47.8705, "days_ago": 1, "notes": "Vista descansando na entrada de um comércio"},
],
},
{
"species": "dog",
"name": "Caramelo",
"photo_filename": "caramelo.jpg",
"description": {
"species": "dog",
"breed_estimate": "SRD",
"size": "grande",
"primary_color": "caramelo",
"secondary_colors": [],
"distinctive_marks": ["pelagem densa", "cauda enrolada"],
"condition": "saudável",
"description_text": "large golden caramel mixed breed dog, standing alert near road in Taguatinga",
"_ai_success": True,
},
"sightings": [
{"lat": -15.8300, "lng": -47.9900, "days_ago": 45, "notes": "Visto na marginal de Taguatinga, sozinho"},
{"lat": -15.8312, "lng": -47.9888, "days_ago": 15, "notes": "Ainda na área, precisa de atenção"},
],
},
{
"species": "dog",
"name": "Manchinha",
"photo_filename": "manchinha.jpg",
"description": {
"species": "dog",
"breed_estimate": "SRD",
"size": "médio",
"primary_color": "branco",
"secondary_colors": ["preto"],
"distinctive_marks": ["manchas pretas na cabeça", "focinho malhado", "coleira verde"],
"condition": "saudável",
"description_text": "medium white dog with black patches on head, spotted muzzle, wearing green collar, very friendly",
"_ai_success": True,
},
"sightings": [
{"lat": -15.8010, "lng": -47.8960, "days_ago": 5, "notes": "Super dócil, tem coleira mas sem identificação — 308 Sul"},
{"lat": -15.8005, "lng": -47.8950, "days_ago": 1, "notes": "Brincando com passantes perto do comércio"},
],
},
{
"species": "cat",
"name": "Cinzinha",
"photo_filename": "cinzinha.jpg",
"description": {
"species": "cat",
"breed_estimate": "SRD",
"size": "pequeno",
"primary_color": "cinza",
"secondary_colors": [],
"distinctive_marks": ["listras tigradas", "olhos amarelos grandes"],
"condition": "saudável",
"description_text": "small grey tabby kitten with large yellow eyes, timid but curious",
"_ai_success": True,
},
"sightings": [
{"lat": -15.7620, "lng": -47.8875, "days_ago": 2, "notes": "Filhote na W3 Norte, aceita comida com cuidado"},
],
},
{
"species": "cat",
"name": "Laranja",
"photo_filename": "laranja.jpg",
"description": {
"species": "cat",
"breed_estimate": "SRD",
"size": "médio",
"primary_color": "laranja",
"secondary_colors": [],
"distinctive_marks": ["pelagem totalmente ruiva", "parte de colônia"],
"condition": "saudável",
"description_text": "medium orange tabby cat, part of a feral colony near dumpsters in Ceilândia",
"_ai_success": True,
},
"sightings": [
{"lat": -15.8170, "lng": -48.1080, "days_ago": 7, "notes": "Colônia de gatos ruivos perto da feira de Ceilândia"},
{"lat": -15.8165, "lng": -48.1072, "days_ago": 0, "notes": "Dois gatos laranja, parecem saudáveis"},
],
},
]
def _random_embedding():
v = np.random.randn(384).astype(np.float32)
v /= np.linalg.norm(v)
return v.tobytes()
def _copy_seed_photos():
"""Copia fotos de static/seed-photos/ (no repo) para DATA_DIR/photos/seed/ (runtime)."""
from core.database import DATA_DIR, PHOTOS_DIR
src_dir = Path(__file__).parent.parent / "static" / "seed-photos"
dst_dir = PHOTOS_DIR / "seed"
dst_dir.mkdir(parents=True, exist_ok=True)
for src in src_dir.iterdir():
dst = dst_dir / src.name
if not dst.exists():
shutil.copy2(src, dst)
log.info("Foto copiada: %s", src.name)
def seed_if_empty(db):
"""Garante que os animais fixos estão sempre no banco.
Insere apenas os que ainda nao existem (por nome).
Dados reais dos usuarios nunca sao removidos.
"""
with db._conn() as conn:
existing_names = {
row[0] for row in conn.execute(
"SELECT name FROM animals WHERE name IS NOT NULL"
)
}
# Sempre copia fotos (idempotente) — garante que estao disponiveis apos restart
_copy_seed_photos()
to_insert = [a for a in _SEED_ANIMALS if a["name"] not in existing_names]
if not to_insert:
log.info("Animais fixos ja presentes — seed ignorado.")
return
log.info("Inserindo %d animais fixos de demonstracao...", len(to_insert))
now = datetime.utcnow()
with db._conn() as conn:
for animal_data in to_insert:
sightings = animal_data["sightings"]
desc = animal_data["description"]
photo_path = "photos/seed/" + animal_data["photo_filename"]
most_recent_days = min(s["days_ago"] for s in sightings)
last_seen = now - timedelta(days=most_recent_days)
first_seen = now - timedelta(days=max(s["days_ago"] for s in sightings))
cur = conn.execute(
"INSERT INTO animals"
" (species, name, description, embedding, first_seen, last_seen, sighting_count)"
" VALUES (?, ?, ?, ?, ?, ?, ?)",
(
animal_data["species"],
animal_data["name"],
json.dumps(desc, ensure_ascii=False),
_random_embedding(),
first_seen.strftime("%Y-%m-%d %H:%M:%S"),
last_seen.strftime("%Y-%m-%d %H:%M:%S"),
len(sightings),
),
)
animal_id = cur.lastrowid
for i, s in enumerate(sightings):
created_at = now - timedelta(days=s["days_ago"])
s_photo = photo_path if i == len(sightings) - 1 else None
conn.execute(
"INSERT INTO sightings"
" (animal_id, photo_path, latitude, longitude, notes, created_at)"
" VALUES (?, ?, ?, ?, ?, ?)",
(
animal_id,
s_photo,
s["lat"],
s["lng"],
s.get("notes", ""),
created_at.strftime("%Y-%m-%d %H:%M:%S"),
),
)
log.info("Seed concluido: %d animais inseridos (%d avistamentos).",
len(to_insert),
sum(len(a["sightings"]) for a in to_insert))