Breed-Enricher: verbesserte Wikipedia-Suche + retry_missed für ki_enriched=2
- _NAME_ALIASES-Dict mit 20 Rassen-Mappings (z.B. Zwergpinscher → Miniature Pinscher) - exsentences=5 statt exintro=1 + Threshold 80 statt 150 Zeichen (fixiert Zwergpinscher, Spinone, Thai Bangkaew) - Neue Fallback-Kette: direkt DE/EN → name_de → Aliasse → normalisierte Namen → opensearch DE/EN - Neue Funktion retry_missed(limit) für ki_enriched=2 ohne DB-Reset - CLI-Flags --retry-missed und --dry-run
This commit is contained in:
parent
5aba366b21
commit
77f6af8817
1 changed files with 304 additions and 122 deletions
|
|
@ -2,7 +2,7 @@
|
|||
BAN YARO — Rassen-Anreicherung (Wikipedia-grounded)
|
||||
|
||||
Strategie:
|
||||
1. Wikipedia-Einleitungstext abrufen (de → en Fallback)
|
||||
1. Wikipedia-Einleitungstext abrufen (de → en Fallback + opensearch + Aliasse)
|
||||
2. Claude Haiku extrahiert Fakten NUR aus dem Quelltext
|
||||
3. Kein Wikipedia-Artikel → ki_enriched=2, ki_source='none' (nicht veröffentlichen)
|
||||
|
||||
|
|
@ -31,6 +31,30 @@ _HAIKU_MODEL = "claude-haiku-4-5-20251001"
|
|||
|
||||
_WP_HEADERS = {"User-Agent": "Banyaro/1.0 (https://banyaro.de; mail@banyaro.de) httpx"}
|
||||
|
||||
# Bekannte Name-Mappings: DB-Name → Liste von Wikipedia-Titeln die zu versuchen sind
|
||||
_NAME_ALIASES: dict[str, list[str]] = {
|
||||
"Staffordshire Bullterrier": ["Staffordshire Bull Terrier"],
|
||||
"Tibet-Spaniel": ["Tibetan Spaniel"],
|
||||
"Tschechischer Terrier": ["Český teriér", "Czech Terrier", "Böhmischer Terrier"],
|
||||
"Weißer Schweizer Schäferhund": ["White Swiss Shepherd Dog", "Berger Blanc Suisse"],
|
||||
"Zwergpinscher": ["Miniature Pinscher"],
|
||||
"wire-haired dachshund": ["Wire-haired Dachshund", "Dachshund"],
|
||||
"Spinone Italiano": ["Spinone"],
|
||||
"Thai Bangkaew Dog": ["Thai Bangkaew"],
|
||||
"Terceira-Dogge": ["Fila da Terceira"],
|
||||
"Ungarische Bracke - Transylvanischer Laufhund": ["Transylvanian Hound"],
|
||||
"anglo-français de moyenne vénerie": ["Anglo-Français de Petite Vénerie", "Anglo-Français de moyenne vénerie"],
|
||||
"artésien-normand": ["Basset Artésien Normand"],
|
||||
"kanadischer Eskinohund": ["Canadian Eskimo Dog"],
|
||||
"Wäller": ["Wäller"],
|
||||
"Serbischer Laufhund": ["Srpski gonič", "Serbian Hound"],
|
||||
"Serbian sheep dog": ["Sharplaninac", "Šarplaninac"],
|
||||
"Slovenský hrubosrstý stavač": ["Slovak Rough-haired Pointer"],
|
||||
"Spino siciliano": ["Cirneco dell'Etna"],
|
||||
"Svensk vit älghund": ["Jamthund", "Jämthund", "Swedish Elkhound"],
|
||||
"Walker Foxhound": ["Treeing Walker Coonhound"],
|
||||
}
|
||||
|
||||
# Übersetzungstabelle für englische TheDogAPI-Temperamentwörter
|
||||
_TEMPER_DE: dict[str, str] = {
|
||||
"adaptable": "anpassungsfähig",
|
||||
|
|
@ -175,60 +199,164 @@ def _parse_json(raw: str) -> dict:
|
|||
raise ValueError(f"Kein gültiges JSON in Antwort gefunden: {raw[:200]}")
|
||||
|
||||
|
||||
async def _fetch_wikipedia_text(name: str) -> tuple[str | None, str | None]:
|
||||
"""Holt den Einleitungstext eines Wikipedia-Artikels (de → en Fallback).
|
||||
def _normalize_name(name: str) -> list[str]:
|
||||
"""Erzeugt normalisierte Suchvarianten für einen Rassennamen."""
|
||||
variants = []
|
||||
# Bindestriche durch Leerzeichen ersetzen
|
||||
no_dash = name.replace("-", " ")
|
||||
if no_dash != name:
|
||||
variants.append(no_dash)
|
||||
# Gängige Suffixe abschneiden und Rest behalten
|
||||
suffixes = [
|
||||
" Sheepdog", " Dog", " Hound", " Terrier", " Spaniel",
|
||||
" Shepherd", " Pointer", " Retriever", " Bulldog",
|
||||
]
|
||||
for suffix in suffixes:
|
||||
if name.lower().endswith(suffix.lower()) and len(name) > len(suffix) + 3:
|
||||
variants.append(name[: -len(suffix)].strip())
|
||||
break
|
||||
return variants
|
||||
|
||||
|
||||
async def _wp_direct(client: httpx.AsyncClient, lang: str, title: str) -> tuple[str | None, str | None]:
|
||||
"""Holt Artikeltext via titles-Parameter (direkte Suche).
|
||||
|
||||
Nutzt exsentences=5 statt exintro=1, damit Artikel mit sehr kurzer Einleitung
|
||||
(z.B. Zwergpinscher DE: 143 Zeichen) trotzdem genug Text liefern.
|
||||
"""
|
||||
try:
|
||||
resp = await client.get(
|
||||
f"https://{lang}.wikipedia.org/w/api.php",
|
||||
params={
|
||||
"action": "query",
|
||||
"titles": title,
|
||||
"prop": "extracts",
|
||||
"exsentences": 5,
|
||||
"explaintext": 1,
|
||||
"format": "json",
|
||||
"redirects": 1,
|
||||
},
|
||||
)
|
||||
pages = resp.json().get("query", {}).get("pages", {})
|
||||
for page in pages.values():
|
||||
if page.get("pageid", -1) == -1:
|
||||
continue
|
||||
text = page.get("extract", "").strip()
|
||||
if len(text) > 80:
|
||||
return text[:3000], lang
|
||||
except Exception as e:
|
||||
logger.debug("WP direkt (%s/%s) fehlgeschlagen: %s", lang, title, e)
|
||||
return None, None
|
||||
|
||||
|
||||
async def _wp_opensearch(client: httpx.AsyncClient, lang: str, query: str) -> tuple[str | None, str | None]:
|
||||
"""Sucht via opensearch und holt dann den ersten Treffer-Artikel."""
|
||||
try:
|
||||
resp = await client.get(
|
||||
f"https://{lang}.wikipedia.org/w/api.php",
|
||||
params={
|
||||
"action": "opensearch",
|
||||
"search": query,
|
||||
"limit": 1,
|
||||
"format": "json",
|
||||
},
|
||||
)
|
||||
results = resp.json()
|
||||
# opensearch gibt [query, [titles], [descriptions], [urls]] zurück
|
||||
titles = results[1] if len(results) > 1 else []
|
||||
if not titles:
|
||||
return None, None
|
||||
found_title = titles[0]
|
||||
logger.debug("Opensearch (%s) '%s' → '%s'", lang, query, found_title)
|
||||
return await _wp_direct(client, lang, found_title)
|
||||
except Exception as e:
|
||||
logger.debug("Opensearch (%s/%s) fehlgeschlagen: %s", lang, query, e)
|
||||
return None, None
|
||||
|
||||
|
||||
async def _fetch_wikipedia_text(
|
||||
name: str, name_de: str | None = None
|
||||
) -> tuple[str | None, str | None]:
|
||||
"""Holt den Einleitungstext eines Wikipedia-Artikels.
|
||||
|
||||
Fallback-Kette:
|
||||
1. Direkte Suche DE (Originalname)
|
||||
2. Direkte Suche EN (Originalname)
|
||||
3. Direkte Suche DE (name_de, falls vorhanden und verschieden)
|
||||
4. Aliasse aus _NAME_ALIASES (DE + EN je Alias)
|
||||
5. Normalisierte Namensvarianten direkt DE + EN
|
||||
6. Opensearch DE (Originalname)
|
||||
7. Opensearch EN (Originalname)
|
||||
|
||||
Returns: (text, lang) oder (None, None) wenn kein Artikel gefunden.
|
||||
"""
|
||||
for lang in ("de", "en"):
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10, headers=_WP_HEADERS) as client:
|
||||
resp = await client.get(
|
||||
f"https://{lang}.wikipedia.org/w/api.php",
|
||||
params={
|
||||
"action": "query",
|
||||
"titles": name,
|
||||
"prop": "extracts",
|
||||
"exintro": 1,
|
||||
"explaintext": 1,
|
||||
"format": "json",
|
||||
"redirects": 1,
|
||||
},
|
||||
)
|
||||
pages = resp.json().get("query", {}).get("pages", {})
|
||||
for page in pages.values():
|
||||
if page.get("pageid", -1) == -1:
|
||||
continue
|
||||
text = page.get("extract", "").strip()
|
||||
if len(text) > 150:
|
||||
return text[:3000], lang
|
||||
except Exception as e:
|
||||
logger.debug("Wikipedia-Text (%s) fehlgeschlagen für %s: %s", lang, name, e)
|
||||
async with httpx.AsyncClient(timeout=10, headers=_WP_HEADERS) as client:
|
||||
|
||||
# 1+2: Direkte Suche mit Originalnamen
|
||||
for lang in ("de", "en"):
|
||||
text, lang_found = await _wp_direct(client, lang, name)
|
||||
if text:
|
||||
return text, lang_found
|
||||
|
||||
# 3: Direkte Suche mit name_de (falls vorhanden und nicht identisch)
|
||||
if name_de and name_de.strip() and name_de.strip() != name:
|
||||
for lang in ("de", "en"):
|
||||
text, lang_found = await _wp_direct(client, lang, name_de.strip())
|
||||
if text:
|
||||
logger.debug("name_de-Treffer (%s): %s → %s", lang, name, name_de)
|
||||
return text, lang_found
|
||||
|
||||
# 4: Bekannte Aliasse
|
||||
for alias in _NAME_ALIASES.get(name, []):
|
||||
for lang in ("de", "en"):
|
||||
text, lang_found = await _wp_direct(client, lang, alias)
|
||||
if text:
|
||||
logger.debug("Alias-Treffer (%s): %s → %s", lang, name, alias)
|
||||
return text, lang_found
|
||||
|
||||
# 5: Normalisierte Namensvarianten
|
||||
for variant in _normalize_name(name):
|
||||
for lang in ("de", "en"):
|
||||
text, lang_found = await _wp_direct(client, lang, variant)
|
||||
if text:
|
||||
logger.debug("Normalisierungs-Treffer (%s): %s → %s", lang, name, variant)
|
||||
return text, lang_found
|
||||
|
||||
# 6+7: Opensearch als letzter Ausweg
|
||||
for lang in ("de", "en"):
|
||||
text, lang_found = await _wp_opensearch(client, lang, name)
|
||||
if text:
|
||||
logger.debug("Opensearch-Treffer (%s): %s", lang, name)
|
||||
return text, lang_found
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
async def _fetch_wikimedia_photo(name: str) -> str | None:
|
||||
"""Sucht ein lizenzfreies Foto via Wikipedia pageimages API (de → en Fallback)."""
|
||||
# Auch Aliasse probieren
|
||||
names_to_try = [name] + _NAME_ALIASES.get(name, [])
|
||||
for lang in ("de", "en"):
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=8, headers=_WP_HEADERS) as client:
|
||||
resp = await client.get(
|
||||
f"https://{lang}.wikipedia.org/w/api.php",
|
||||
params={
|
||||
"action": "query",
|
||||
"titles": name,
|
||||
"prop": "pageimages",
|
||||
"format": "json",
|
||||
"pithumbsize": 800,
|
||||
"redirects": 1,
|
||||
},
|
||||
)
|
||||
pages = resp.json().get("query", {}).get("pages", {})
|
||||
for page in pages.values():
|
||||
if "thumbnail" in page:
|
||||
return page["thumbnail"]["source"]
|
||||
except Exception as e:
|
||||
logger.debug("Wikimedia-Foto (%s) fehlgeschlagen für %s: %s", lang, name, e)
|
||||
for title in names_to_try:
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=8, headers=_WP_HEADERS) as client:
|
||||
resp = await client.get(
|
||||
f"https://{lang}.wikipedia.org/w/api.php",
|
||||
params={
|
||||
"action": "query",
|
||||
"titles": title,
|
||||
"prop": "pageimages",
|
||||
"format": "json",
|
||||
"pithumbsize": 800,
|
||||
"redirects": 1,
|
||||
},
|
||||
)
|
||||
pages = resp.json().get("query", {}).get("pages", {})
|
||||
for page in pages.values():
|
||||
if "thumbnail" in page:
|
||||
return page["thumbnail"]["source"]
|
||||
except Exception as e:
|
||||
logger.debug("Wikimedia-Foto (%s/%s) fehlgeschlagen: %s", lang, title, e)
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -258,7 +386,89 @@ async def _haiku_complete(prompt: str) -> str:
|
|||
return resp.content[0].text.strip()
|
||||
|
||||
|
||||
async def enrich_breeds(limit: int = 10) -> int:
|
||||
async def _enrich_one(rasse: dict, dry_run: bool = False) -> bool:
|
||||
"""Reichert eine einzelne Rasse an. Gibt True zurück wenn erfolgreich."""
|
||||
name = rasse["name"]
|
||||
name_de = rasse.get("name_de")
|
||||
rasse_id = rasse["id"]
|
||||
|
||||
# 1. Wikipedia-Text holen
|
||||
wiki_text, wiki_lang = await _fetch_wikipedia_text(name, name_de)
|
||||
|
||||
if not wiki_text:
|
||||
if not dry_run:
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
"UPDATE wiki_rassen SET ki_enriched=2, ki_source='none' WHERE id=?",
|
||||
(rasse_id,),
|
||||
)
|
||||
logger.info("Kein Wikipedia-Artikel: %s → übersprungen", name)
|
||||
await asyncio.sleep(0.5)
|
||||
return False
|
||||
|
||||
if dry_run:
|
||||
logger.info("[DRY-RUN] Gefunden: %s (WP-%s, %d Zeichen)", name, wiki_lang.upper(), len(wiki_text))
|
||||
return True
|
||||
|
||||
# 2. Haiku extrahiert Fakten aus dem Quelltext
|
||||
prompt = _PROMPT.format(name=name, lang=wiki_lang.upper(), wiki_text=wiki_text)
|
||||
try:
|
||||
raw = await _haiku_complete(prompt)
|
||||
except Exception as e:
|
||||
logger.error("Haiku-Anfrage fehlgeschlagen für %s: %s", name, e)
|
||||
await asyncio.sleep(3)
|
||||
return False
|
||||
|
||||
try:
|
||||
data = _parse_json(raw)
|
||||
except ValueError as e:
|
||||
logger.warning("JSON-Parsing fehlgeschlagen für %s: %s", name, e)
|
||||
await asyncio.sleep(2)
|
||||
return False
|
||||
|
||||
# 3. DB-Update
|
||||
updates = {
|
||||
k: v for k, v in data.items()
|
||||
if k in _DIRECT_FIELDS and v is not None
|
||||
}
|
||||
if "temperament" in updates:
|
||||
updates["temperament"] = translate_temperament(updates["temperament"])
|
||||
updates["ki_enriched"] = 1
|
||||
updates["ki_model"] = _HAIKU_MODEL
|
||||
updates["ki_source"] = f"wikipedia_{wiki_lang}"
|
||||
|
||||
cols = ", ".join(f"{k}=?" for k in updates)
|
||||
values = list(updates.values()) + [rasse_id]
|
||||
|
||||
try:
|
||||
with db() as conn:
|
||||
conn.execute(f"UPDATE wiki_rassen SET {cols} WHERE id=?", values)
|
||||
logger.info("Rasse angereichert: %s (%d Felder, WP-%s)",
|
||||
name, len(updates) - 2, wiki_lang.upper())
|
||||
except Exception as e:
|
||||
logger.error("DB-Update fehlgeschlagen für %s: %s", name, e)
|
||||
await asyncio.sleep(2)
|
||||
return False
|
||||
|
||||
# 4. Foto holen wenn noch keins vorhanden
|
||||
if not rasse.get("foto_url"):
|
||||
foto_url = await _fetch_wikimedia_photo(name)
|
||||
if foto_url:
|
||||
try:
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
"UPDATE wiki_rassen SET foto_url=? WHERE id=?",
|
||||
(foto_url, rasse_id),
|
||||
)
|
||||
logger.info("Wikimedia-Foto gesetzt: %s", name)
|
||||
except Exception as e:
|
||||
logger.error("Foto-Update fehlgeschlagen für %s: %s", name, e)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
return True
|
||||
|
||||
|
||||
async def enrich_breeds(limit: int = 10, dry_run: bool = False) -> int:
|
||||
"""
|
||||
Reichert bis zu `limit` Rassen an (ki_enriched = 0).
|
||||
|
||||
|
|
@ -269,7 +479,7 @@ async def enrich_breeds(limit: int = 10) -> int:
|
|||
"""
|
||||
with db() as conn:
|
||||
rassen = conn.execute(
|
||||
"""SELECT id, name, slug, herkunft, foto_url FROM wiki_rassen
|
||||
"""SELECT id, name, slug, herkunft, foto_url, name_de FROM wiki_rassen
|
||||
WHERE ki_enriched = 0
|
||||
ORDER BY name ASC
|
||||
LIMIT ?""",
|
||||
|
|
@ -281,81 +491,42 @@ async def enrich_breeds(limit: int = 10) -> int:
|
|||
return 0
|
||||
|
||||
enriched_count = 0
|
||||
|
||||
for rasse in rassen:
|
||||
name = rasse["name"]
|
||||
rasse_id = rasse["id"]
|
||||
|
||||
# 1. Wikipedia-Text holen
|
||||
wiki_text, wiki_lang = await _fetch_wikipedia_text(name)
|
||||
|
||||
if not wiki_text:
|
||||
# Kein Artikel → markieren und überspringen
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
"UPDATE wiki_rassen SET ki_enriched=2, ki_source='none' WHERE id=?",
|
||||
(rasse_id,),
|
||||
)
|
||||
logger.info("Kein Wikipedia-Artikel: %s → übersprungen", name)
|
||||
await asyncio.sleep(0.5)
|
||||
continue
|
||||
|
||||
# 2. Haiku extrahiert Fakten aus dem Quelltext
|
||||
prompt = _PROMPT.format(name=name, lang=wiki_lang.upper(), wiki_text=wiki_text)
|
||||
try:
|
||||
raw = await _haiku_complete(prompt)
|
||||
except Exception as e:
|
||||
logger.error("Haiku-Anfrage fehlgeschlagen für %s: %s", name, e)
|
||||
await asyncio.sleep(3)
|
||||
continue
|
||||
|
||||
try:
|
||||
data = _parse_json(raw)
|
||||
except ValueError as e:
|
||||
logger.warning("JSON-Parsing fehlgeschlagen für %s: %s", name, e)
|
||||
await asyncio.sleep(2)
|
||||
continue
|
||||
|
||||
# 3. DB-Update
|
||||
updates = {
|
||||
k: v for k, v in data.items()
|
||||
if k in _DIRECT_FIELDS and v is not None
|
||||
}
|
||||
if "temperament" in updates:
|
||||
updates["temperament"] = translate_temperament(updates["temperament"])
|
||||
updates["ki_enriched"] = 1
|
||||
updates["ki_model"] = _HAIKU_MODEL
|
||||
updates["ki_source"] = f"wikipedia_{wiki_lang}"
|
||||
|
||||
cols = ", ".join(f"{k}=?" for k in updates)
|
||||
values = list(updates.values()) + [rasse_id]
|
||||
|
||||
try:
|
||||
with db() as conn:
|
||||
conn.execute(f"UPDATE wiki_rassen SET {cols} WHERE id=?", values)
|
||||
logger.info("Rasse angereichert: %s (%d Felder, WP-%s)",
|
||||
name, len(updates) - 2, wiki_lang.upper())
|
||||
if await _enrich_one(rasse, dry_run=dry_run):
|
||||
enriched_count += 1
|
||||
except Exception as e:
|
||||
logger.error("DB-Update fehlgeschlagen für %s: %s", name, e)
|
||||
await asyncio.sleep(2)
|
||||
continue
|
||||
|
||||
# 4. Foto holen wenn noch keins vorhanden
|
||||
if not rasse["foto_url"]:
|
||||
foto_url = await _fetch_wikimedia_photo(name)
|
||||
if foto_url:
|
||||
try:
|
||||
with db() as conn:
|
||||
conn.execute(
|
||||
"UPDATE wiki_rassen SET foto_url=? WHERE id=?",
|
||||
(foto_url, rasse_id),
|
||||
)
|
||||
logger.info("Wikimedia-Foto gesetzt: %s", name)
|
||||
except Exception as e:
|
||||
logger.error("Foto-Update fehlgeschlagen für %s: %s", name, e)
|
||||
return enriched_count
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
async def retry_missed(limit: int = 70, dry_run: bool = False) -> int:
|
||||
"""
|
||||
Versucht erneut, Rassen anzureichern die beim ersten Durchlauf
|
||||
keinen Wikipedia-Artikel hatten (ki_enriched = 2).
|
||||
|
||||
Setzt ki_enriched NICHT zurück — verarbeitet direkt.
|
||||
Bei Erfolg wird ki_enriched=1 gesetzt (überschreibt 2).
|
||||
Bei erneutem Misserfolg bleibt ki_enriched=2.
|
||||
|
||||
Returns: Anzahl erfolgreich angereicherter Rassen.
|
||||
"""
|
||||
with db() as conn:
|
||||
rassen = conn.execute(
|
||||
"""SELECT id, name, slug, herkunft, foto_url, name_de FROM wiki_rassen
|
||||
WHERE ki_enriched = 2
|
||||
ORDER BY name ASC
|
||||
LIMIT ?""",
|
||||
(limit,),
|
||||
).fetchall()
|
||||
|
||||
if not rassen:
|
||||
logger.info("Keine übersprungenen Rassen zum Retry gefunden.")
|
||||
return 0
|
||||
|
||||
logger.info("Retry für %d Rassen mit ki_enriched=2 ...", len(rassen))
|
||||
enriched_count = 0
|
||||
for rasse in rassen:
|
||||
if await _enrich_one(rasse, dry_run=dry_run):
|
||||
enriched_count += 1
|
||||
|
||||
return enriched_count
|
||||
|
||||
|
|
@ -405,14 +576,25 @@ if __name__ == "__main__":
|
|||
|
||||
logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
|
||||
parser = argparse.ArgumentParser(description="Rassen-Anreicherung (Wikipedia-grounded)")
|
||||
parser.add_argument("--limit", type=int, default=10)
|
||||
parser.add_argument("--limit", type=int, default=10,
|
||||
help="Maximale Anzahl zu verarbeitender Rassen")
|
||||
parser.add_argument("--reset-gemma", action="store_true",
|
||||
help="Gemma-Einträge zurücksetzen bevor angereichert wird")
|
||||
parser.add_argument("--retry-missed", action="store_true",
|
||||
help="Rassen mit ki_enriched=2 erneut versuchen")
|
||||
parser.add_argument("--dry-run", action="store_true",
|
||||
help="Nur Wikipedia-Suche testen, keine DB-Änderungen")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.reset_gemma:
|
||||
n = reset_gemma_entries()
|
||||
print(f"Reset: {n} Gemma-Einträge zurückgesetzt")
|
||||
|
||||
count = asyncio.run(enrich_breeds(args.limit))
|
||||
print(f"Angereichert: {count} Rassen")
|
||||
if args.retry_missed:
|
||||
count = asyncio.run(retry_missed(args.limit, dry_run=args.dry_run))
|
||||
prefix = "[DRY-RUN] " if args.dry_run else ""
|
||||
print(f"{prefix}Retry: {count} von {args.limit} Rassen angereichert")
|
||||
else:
|
||||
count = asyncio.run(enrich_breeds(args.limit, dry_run=args.dry_run))
|
||||
prefix = "[DRY-RUN] " if args.dry_run else ""
|
||||
print(f"{prefix}Angereichert: {count} Rassen")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue