From 83ec6ad6c1fac1f00b64519d1b90f9de17c516bc Mon Sep 17 00:00:00 2001 From: Artemis Date: Sat, 24 Oct 2026 20:25:24 +0000 Subject: [PATCH] Greifvogel-Alarm berichtigt, Wartezeit von 60 auf 5 Sekunden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Das Abzeichen hing an der Seltenheit statt an der Vogelgruppe: "Ein Greifvogel oder eine Eule vor der Linse" wurde fuer jede epische oder legendaere Art vergeben. Von 148 Arten sind 21 Greifvoegel oder Eulen, 96 sind episch oder legendaer. Aufgefallen an einem Weissstorch. Die Warteschlange fragte alle 60 Sekunden nach Arbeit. Das war nie eine Hintergrundaufgabe, sondern die Zeit, die ein Mensch vor "Der Detektor schaut sich das an …" wartet, nachdem er ein Foto geschickt hat. Jetzt 5 Sekunden; die Abfrage ist ein kurzes JSON gegen die eigene Instanz. Drei Pruefungen fuer das Abzeichen, eine davon mit dem Storch. Co-Authored-By: Claude Opus 5 --- detector/__main__.py | 4 ++-- detector/warteschlange.py | 14 ++++++++++++- web/dex/scoring.py | 17 ++++++++++++++-- web/dex/tests.py | 43 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/detector/__main__.py b/detector/__main__.py index ae7f47e..243ce19 100644 --- a/detector/__main__.py +++ b/detector/__main__.py @@ -644,8 +644,8 @@ def main(argv: list[str] | None = None) -> int: help="Token zum Zurueckmelden (derselbe wie beim Einliefern)") q.add_argument("--lese-token", default=env("LESE_TOKEN"), help="Token zum Abrufen der offenen Fotos") - q.add_argument("--alle", type=float, default=60.0, - help="Sekunden zwischen zwei Abfragen (Standard 60)") + q.add_argument("--alle", type=float, default=5.0, + help="Sekunden zwischen zwei Abfragen (Standard 5)") q.add_argument("--anzahl", type=int, default=20, help="Hoechstens so viele Fotos je Durchlauf (Standard 20)") q.add_argument("--einmal", action="store_true", diff --git a/detector/warteschlange.py b/detector/warteschlange.py index afd8498..64ce784 100644 --- a/detector/warteschlange.py +++ b/detector/warteschlange.py @@ -16,6 +16,18 @@ Foto. Die ganze Logik aus ``watcher.py`` — Bewegung als Auslöser, Besuch übe viele Einzelbilder, Mittelwert der Vogel-Werte — hat hier keine Entsprechung und wäre bei einem Einzelbild sogar falsch. Benutzt wird deshalb direkt ``Pipeline.run()``, genau wie bei ``python -m detector test``. + +🔴 **Das Intervall ist eine Wartezeit vor den Augen eines Menschen, keine +Hintergrundaufgabe.** Es stand auf 60 Sekunden — gedacht als schonende +Hintergrundabfrage, tatsächlich aber die Zeit, die jemand vor dem Bildschirm +„Der Detektor schaut sich das an …" wartet, nachdem er ein Foto geschickt +hat. Christian hat das zweimal bemängelt, bevor es jemand als Fehler gelesen +hat. + +Jetzt 5 Sekunden, und ein Durchlauf, der etwas gefunden hat, fragt **sofort** +noch einmal nach: Mehrere Fotos hintereinander sollen nicht je 5 Sekunden +kosten. Die Abfrage ist ein kurzes JSON gegen die eigene Instanz — bei 5 +Sekunden sind das 720 Anfragen in der Stunde, für einen Django-Server nichts. """ from __future__ import annotations @@ -39,7 +51,7 @@ class Warteschlange: lese_token: str, schreib_token: str, pipeline: Pipeline, - intervall: float = 60.0, + intervall: float = 5.0, anzahl: int = 20, timeout: int = 20, ): diff --git a/web/dex/scoring.py b/web/dex/scoring.py index f141b5e..26fba7b 100644 --- a/web/dex/scoring.py +++ b/web/dex/scoring.py @@ -343,9 +343,22 @@ def evaluate_badges(sighting: Sighting) -> list[Badge]: species = sighting.effective_species if species: - if "specht" in species.slug: + if species.typ == "specht" or "specht" in species.slug: add("klopfgeist") - if species.rarity in ("episch", "legendaer"): + # 🔴 **Hier stand die Seltenheit statt der Vogelgruppe.** Das Abzeichen + # heißt "Greifvogel-Alarm" und verspricht "ein Greifvogel oder eine + # Eule vor der Linse" — vergeben wurde es für jede epische oder + # legendäre Art. Von 148 Arten im Katalog sind 21 Greifvögel oder + # Eulen, aber 75 weitere epische; die bekamen es zu Unrecht. + # + # Aufgefallen am 24.10.2026, als Christian in der App einen + # Weißstorch bestimmte und fragte, ob der ein Greifvogel sei. Der + # Befund stand seit dem 01.10.2026 in meinen Notizen und war als + # "Nebenbefund" liegengeblieben. + # + # 🔑 Eine Regel, die etwas anderes tut als ihre Beschreibung, ist + # nicht ungenau — sie sagt dem Nutzer etwas Falsches. + if species.typ in ("greifvogel", "eule"): add("greifvogel_alarm") if species.rarity in ("selten", "episch", "legendaer"): add("seltener_gast") diff --git a/web/dex/tests.py b/web/dex/tests.py index c0eafff..e20f691 100644 --- a/web/dex/tests.py +++ b/web/dex/tests.py @@ -181,6 +181,49 @@ class BadgeTests(TestCase): EarnedBadge.objects.filter(badge__code="erster_gast").count(), 1 ) + def test_greifvogel_alarm_braucht_einen_greifvogel(self): + """🔴 Die Regel hing an der Seltenheit statt an der Vogelgruppe. + + Das Abzeichen verspricht „ein Greifvogel oder eine Eule vor der + Linse" und wurde für jede epische Art vergeben — von 148 Arten sind + 21 Greifvögel oder Eulen, 75 weitere sind episch. Aufgefallen am + 24.10.2026 an einem Weißstorch. + """ + bussard = make_species( + slug="maeusebussard", german="Mäusebussard", + scientific="Buteo buteo", rarity="episch", typ="greifvogel", xp=45, + ) + scoring.register(make_sighting(bussard)) + self.assertTrue( + EarnedBadge.objects.filter(badge__code="greifvogel_alarm").exists() + ) + + def test_storch_bekommt_keinen_greifvogel_alarm(self): + storch = make_species( + slug="weissstorch", german="Weißstorch", + scientific="Ciconia ciconia", rarity="episch", + typ="schreitvogel", xp=150, + ) + scoring.register(make_sighting(storch)) + self.assertFalse( + EarnedBadge.objects.filter(badge__code="greifvogel_alarm").exists(), + "Ein Schreitvogel ist kein Greifvogel — auch nicht, wenn er selten ist", + ) + # „Seltener Gast" hängt zu Recht an der Seltenheit und bleibt. + self.assertTrue( + EarnedBadge.objects.filter(badge__code="seltener_gast").exists() + ) + + def test_eule_zaehlt_mit(self): + kauz = make_species( + slug="waldkauz", german="Waldkauz", scientific="Strix aluco", + rarity="selten", typ="eule", xp=40, + ) + scoring.register(make_sighting(kauz)) + self.assertTrue( + EarnedBadge.objects.filter(badge__code="greifvogel_alarm").exists() + ) + def test_rare_species_earns_rare_guest(self): gimpel = make_species( slug="gimpel", german="Gimpel", scientific="Pyrrhula pyrrhula",