fix: Stamm-Besetzer der Zentrale kommt bei Freigabe zurueck
"Zurueck auf mein Tuerschild" hat die Zentrale nur ausgeschaltet, statt ihren eigentlichen Besetzer wieder darauf zu setzen. Wer normalerweise am Empfang sitzt, blieb nach einer Vertretung ohne Zuordnung. Die Zentrale hat jetzt einen Stamm-Besetzer, ohne dass es dafuer eine eigene Einstellung braucht: wer beim ersten Verdraengen keinen anderen Platz gemerkt hat, bekommt das Zentral-Segment als Heimatplatz vermerkt. Wird die Zentrale wieder frei, kommt er automatisch zurueck - egal wie viele Vertretungen sich zwischenzeitlich abgewechselt haben. Der Vermerk des Stamm-Besetzers bleibt dabei dauerhaft stehen, anders als bei einer Vertretung: die Zentrale ist sein normaler Platz, nicht eine Station, aus der er zurueckkehrt. Klickt er selbst "zurueck auf mein Tuerschild", aendert sich deshalb nichts und die Seite erklaert es. Damit gilt in beide Richtungen dieselbe Regel: wird ein Platz frei, kommt zurueck, fuer wen er gemerkt ist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -205,11 +205,19 @@ erst per Knopfdruck.
|
|||||||
Segment ausgeschaltet.
|
Segment ausgeschaltet.
|
||||||
- **Zurueck auf mein Tuerschild**: Der gemerkte Platz wird wiederhergestellt und die
|
- **Zurueck auf mein Tuerschild**: Der gemerkte Platz wird wiederhergestellt und die
|
||||||
Zentrale freigegeben (Farbe `abwesend`, Standard: aus).
|
Zentrale freigegeben (Farbe `abwesend`, Standard: aus).
|
||||||
- **Vorgaenger**: Wer die Zentrale abgibt, wird dabei automatisch auf sein eigenes
|
- **Vorgaenger mit eigenem Tuerschild**: Wer die Zentrale abgibt, wird dabei
|
||||||
Tuerschild zurueckgesetzt. Er muss also nicht erst selbst "zurueck" klicken, damit
|
automatisch auf sein eigenes Tuerschild zurueckgesetzt. Er muss also nicht erst
|
||||||
der Nachfolger uebernehmen kann. Ist sein Tuerschild inzwischen jemand anderem
|
selbst "zurueck" klicken, damit der Nachfolger uebernehmen kann. Ist sein Tuerschild
|
||||||
zugeordnet oder war keines gemerkt, bleibt er ohne Zuordnung - die Meldung auf der
|
inzwischen jemand anderem zugeordnet, bleibt er ohne Zuordnung - die Meldung nennt
|
||||||
Seite sagt, welcher der beiden Faelle vorliegt.
|
dann, wer dort sitzt.
|
||||||
|
- **Stamm-Besetzer der Zentrale**: Wer normalerweise auf der Zentrale sitzt und kein
|
||||||
|
eigenes Tuerschild hat, wird beim ersten Verdraengen als Stamm-Besetzer vermerkt.
|
||||||
|
Er kommt automatisch zurueck, sobald die Zentrale wieder freigegeben wird - egal
|
||||||
|
wie viele Vertretungen sich zwischenzeitlich abgewechselt haben. Sein Vermerk bleibt
|
||||||
|
dauerhaft stehen, denn die Zentrale ist sein normaler Platz und keine Vertretung.
|
||||||
|
- **Der Stamm-Besetzer selbst** braucht "Zurueck auf mein Tuerschild" nicht: sein
|
||||||
|
Platz ist die Zentrale. Klickt er es trotzdem, aendert sich nichts und die Seite
|
||||||
|
sagt es ihm.
|
||||||
- **Belegter Heimatplatz**: Ist das eigene Tuerschild inzwischen jemand anderem
|
- **Belegter Heimatplatz**: Ist das eigene Tuerschild inzwischen jemand anderem
|
||||||
zugeordnet, wird die Rueckkehr abgelehnt und nichts geaendert. Dann muss der
|
zugeordnet, wird die Rueckkehr abgelehnt und nichts geaendert. Dann muss der
|
||||||
Administrator die Zuordnung aufloesen.
|
Administrator die Zuordnung aufloesen.
|
||||||
|
|||||||
@@ -569,6 +569,44 @@ def _zentrale_view(request, conn, target, extension="", user=None, error=None, m
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
def _zentrale_owner_row(conn, target):
|
||||||
|
"""Heimateintrag, der auf das Zentral-Segment zeigt - der Stamm-Besetzer."""
|
||||||
|
if not target:
|
||||||
|
return None
|
||||||
|
return conn.execute("""
|
||||||
|
SELECT h.user_azure_id, u.display_name
|
||||||
|
FROM home_assignments h JOIN users u ON u.azure_id = h.user_azure_id
|
||||||
|
WHERE h.wled_device_id = ? AND h.segment = ? AND u.active = 1
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM assignments a WHERE a.user_azure_id = h.user_azure_id)
|
||||||
|
ORDER BY h.created_at, h.user_azure_id
|
||||||
|
LIMIT 1
|
||||||
|
""", (target["id"], target["segment"])).fetchone()
|
||||||
|
|
||||||
|
|
||||||
|
def _restore_zentrale_owner(conn, target):
|
||||||
|
"""Stamm-Besetzer zurueckholen, sobald das Zentral-Segment frei ist.
|
||||||
|
|
||||||
|
Als Stamm-Besetzer gilt, fuer wen das Zentral-Segment als Heimatplatz
|
||||||
|
gemerkt ist. Sein Eintrag bleibt dabei absichtlich stehen: die Zentrale
|
||||||
|
ist sein normaler Platz, nicht eine Vertretung, aus der er zurueckkehrt.
|
||||||
|
"""
|
||||||
|
if not target:
|
||||||
|
return None
|
||||||
|
busy = conn.execute(
|
||||||
|
"SELECT 1 FROM assignments WHERE wled_device_id = ? AND segment = ?",
|
||||||
|
(target["id"], target["segment"]),
|
||||||
|
).fetchone()
|
||||||
|
if busy:
|
||||||
|
return None
|
||||||
|
owner = _zentrale_owner_row(conn, target)
|
||||||
|
if not owner:
|
||||||
|
return None
|
||||||
|
conn.execute("""
|
||||||
|
INSERT INTO assignments (user_azure_id, wled_device_id, segment) VALUES (?, ?, ?)
|
||||||
|
""", (owner["user_azure_id"], target["id"], target["segment"]))
|
||||||
|
return owner
|
||||||
|
|
||||||
|
|
||||||
@app.get("/zentrale", response_class=HTMLResponse)
|
@app.get("/zentrale", response_class=HTMLResponse)
|
||||||
async def zentrale_page(request: Request):
|
async def zentrale_page(request: Request):
|
||||||
"""Self-Service-Seite fuer den Empfangsbereich. Absichtlich ohne Admin-Login."""
|
"""Self-Service-Seite fuer den Empfangsbereich. Absichtlich ohne Admin-Login."""
|
||||||
@@ -651,13 +689,23 @@ async def zentrale_takeover(request: Request, extension: str = Form(...)):
|
|||||||
# bis dahin haette er gar kein Licht.
|
# bis dahin haette er gar kein Licht.
|
||||||
pred_back = None
|
pred_back = None
|
||||||
pred_blocked = None
|
pred_blocked = None
|
||||||
|
pred_waiting = False
|
||||||
if predecessor and predecessor["azure_id"] != user["azure_id"]:
|
if predecessor and predecessor["azure_id"] != user["azure_id"]:
|
||||||
pred_home = conn.execute("""
|
pred_home = conn.execute("""
|
||||||
SELECT h.wled_device_id, h.segment, d.hostname, d.name
|
SELECT h.wled_device_id, h.segment, d.hostname, d.name
|
||||||
FROM home_assignments h JOIN wled_devices d ON d.id = h.wled_device_id
|
FROM home_assignments h JOIN wled_devices d ON d.id = h.wled_device_id
|
||||||
WHERE h.user_azure_id = ?
|
WHERE h.user_azure_id = ?
|
||||||
""", (predecessor["azure_id"],)).fetchone()
|
""", (predecessor["azure_id"],)).fetchone()
|
||||||
if pred_home:
|
|
||||||
|
is_owner = pred_home and (pred_home["wled_device_id"], pred_home["segment"]) \
|
||||||
|
== (target["id"], target["segment"])
|
||||||
|
|
||||||
|
if is_owner:
|
||||||
|
# Sein Stammplatz IST die Zentrale, die jetzt der Nachfolger hat.
|
||||||
|
# Der Heimateintrag bleibt stehen, damit er zurueckkommt, sobald
|
||||||
|
# die Zentrale wieder frei wird.
|
||||||
|
pred_waiting = True
|
||||||
|
elif pred_home:
|
||||||
taken = conn.execute("""
|
taken = conn.execute("""
|
||||||
SELECT u.display_name FROM assignments a JOIN users u ON u.azure_id = a.user_azure_id
|
SELECT u.display_name FROM assignments a JOIN users u ON u.azure_id = a.user_azure_id
|
||||||
WHERE a.wled_device_id = ? AND a.segment = ?
|
WHERE a.wled_device_id = ? AND a.segment = ?
|
||||||
@@ -671,6 +719,19 @@ async def zentrale_takeover(request: Request, extension: str = Form(...)):
|
|||||||
conn.execute("DELETE FROM home_assignments WHERE user_azure_id = ?",
|
conn.execute("DELETE FROM home_assignments WHERE user_azure_id = ?",
|
||||||
(predecessor["azure_id"],))
|
(predecessor["azure_id"],))
|
||||||
pred_back = pred_home
|
pred_back = pred_home
|
||||||
|
else:
|
||||||
|
# Kein anderer Platz gemerkt: die Zentrale ist sein Stammplatz.
|
||||||
|
# Merken, damit er automatisch zurueckkommt, wenn sie frei wird.
|
||||||
|
claimed = conn.execute("""
|
||||||
|
SELECT 1 FROM home_assignments
|
||||||
|
WHERE wled_device_id = ? AND segment = ? AND user_azure_id != ?
|
||||||
|
""", (target["id"], target["segment"], predecessor["azure_id"])).fetchone()
|
||||||
|
if not claimed:
|
||||||
|
conn.execute("""
|
||||||
|
INSERT OR REPLACE INTO home_assignments (user_azure_id, wled_device_id, segment)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
""", (predecessor["azure_id"], target["id"], target["segment"]))
|
||||||
|
pred_waiting = True
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
@@ -694,6 +755,7 @@ async def zentrale_takeover(request: Request, extension: str = Form(...)):
|
|||||||
predecessor["display_name"] if predecessor else "-",
|
predecessor["display_name"] if predecessor else "-",
|
||||||
"zurueck auf eigenem Tuerschild" if pred_back
|
"zurueck auf eigenem Tuerschild" if pred_back
|
||||||
else f"Tuerschild belegt von {pred_blocked}" if pred_blocked
|
else f"Tuerschild belegt von {pred_blocked}" if pred_blocked
|
||||||
|
else "wartet auf die Zentrale" if pred_waiting
|
||||||
else "kein Tuerschild gemerkt")
|
else "kein Tuerschild gemerkt")
|
||||||
|
|
||||||
msg = f"{user['display_name']} sitzt jetzt auf der Zentrale ({target['name']})."
|
msg = f"{user['display_name']} sitzt jetzt auf der Zentrale ({target['name']})."
|
||||||
@@ -705,6 +767,9 @@ async def zentrale_takeover(request: Request, extension: str = Form(...)):
|
|||||||
elif pred_blocked:
|
elif pred_blocked:
|
||||||
msg += (f" {predecessor['display_name']} konnte nicht zurueckgesetzt werden, "
|
msg += (f" {predecessor['display_name']} konnte nicht zurueckgesetzt werden, "
|
||||||
f"weil das eigene Tuerschild inzwischen {pred_blocked} zugeordnet ist.")
|
f"weil das eigene Tuerschild inzwischen {pred_blocked} zugeordnet ist.")
|
||||||
|
elif pred_waiting:
|
||||||
|
msg += (f" {predecessor['display_name']} gehoert auf die Zentrale und kommt automatisch "
|
||||||
|
"zurueck, sobald sie wieder freigegeben wird.")
|
||||||
elif predecessor and predecessor["azure_id"] != user["azure_id"]:
|
elif predecessor and predecessor["azure_id"] != user["azure_id"]:
|
||||||
msg += (f" Fuer {predecessor['display_name']} war kein eigenes Tuerschild gemerkt, "
|
msg += (f" Fuer {predecessor['display_name']} war kein eigenes Tuerschild gemerkt, "
|
||||||
"er ist jetzt ohne Zuordnung.")
|
"er ist jetzt ohne Zuordnung.")
|
||||||
@@ -741,11 +806,30 @@ async def zentrale_release(request: Request, extension: str = Form(...)):
|
|||||||
error="Es ist kein Heimat-Tuerschild gespeichert und du sitzt nicht auf der Zentrale.")
|
error="Es ist kein Heimat-Tuerschild gespeichert und du sitzt nicht auf der Zentrale.")
|
||||||
# Nur von der Zentrale abmelden, es gibt keinen Platz zum Zurueckgehen
|
# Nur von der Zentrale abmelden, es gibt keinen Platz zum Zurueckgehen
|
||||||
conn.execute("DELETE FROM assignments WHERE user_azure_id = ?", (user["azure_id"],))
|
conn.execute("DELETE FROM assignments WHERE user_azure_id = ?", (user["azure_id"],))
|
||||||
|
returning = _restore_zentrale_owner(conn, target)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
scheduler.clear_segment(target["hostname"], target["segment"], conn)
|
if returning:
|
||||||
logger.info("Zentrale verlassen von %s (kein Heimat-Tuerschild)", user["display_name"])
|
scheduler.push_user_light(returning["user_azure_id"], returning["display_name"],
|
||||||
|
target["hostname"], target["segment"], conn)
|
||||||
|
else:
|
||||||
|
scheduler.clear_segment(target["hostname"], target["segment"], conn)
|
||||||
|
logger.info("Zentrale verlassen von %s (kein Heimat-Tuerschild), zurueck auf der Zentrale: %s",
|
||||||
|
user["display_name"], returning["display_name"] if returning else "-")
|
||||||
|
msg = f"{user['display_name']} ist von der Zentrale abgemeldet. Ein eigenes Tuerschild war nicht gespeichert."
|
||||||
|
if returning:
|
||||||
|
msg += f" {returning['display_name']} sitzt wieder auf der Zentrale."
|
||||||
|
return _zentrale_view(request, conn, target, extension=extension, user=user, message=msg)
|
||||||
|
|
||||||
|
# Der Stamm-Besetzer der Zentrale hat keinen anderen Platz, auf den er
|
||||||
|
# zurueckgehen koennte - sein Heimatplatz IST die Zentrale.
|
||||||
|
if target and (home["wled_device_id"], home["segment"]) == (target["id"], target["segment"]):
|
||||||
|
if was_here:
|
||||||
|
return _zentrale_view(request, conn, target, extension=extension, user=user,
|
||||||
|
message=("Die Zentrale ist dein eigener Platz - du sitzt schon dort. "
|
||||||
|
"Es gibt nichts zurueckzusetzen."))
|
||||||
return _zentrale_view(request, conn, target, extension=extension, user=user,
|
return _zentrale_view(request, conn, target, extension=extension, user=user,
|
||||||
message=f"{user['display_name']} ist von der Zentrale abgemeldet. Ein eigenes Tuerschild war nicht gespeichert.")
|
error=("Die Zentrale ist dein eigener Platz, aktuell hat sie jemand anderes. "
|
||||||
|
"Mit \"Zentrale uebernehmen\" holst du sie zurueck."))
|
||||||
|
|
||||||
# Ist der Heimatplatz inzwischen von jemand anderem belegt?
|
# Ist der Heimatplatz inzwischen von jemand anderem belegt?
|
||||||
blocker = conn.execute("""
|
blocker = conn.execute("""
|
||||||
@@ -764,19 +848,29 @@ async def zentrale_release(request: Request, extension: str = Form(...)):
|
|||||||
INSERT INTO assignments (user_azure_id, wled_device_id, segment) VALUES (?, ?, ?)
|
INSERT INTO assignments (user_azure_id, wled_device_id, segment) VALUES (?, ?, ?)
|
||||||
""", (user["azure_id"], home["wled_device_id"], home["segment"]))
|
""", (user["azure_id"], home["wled_device_id"], home["segment"]))
|
||||||
conn.execute("DELETE FROM home_assignments WHERE user_azure_id = ?", (user["azure_id"],))
|
conn.execute("DELETE FROM home_assignments WHERE user_azure_id = ?", (user["azure_id"],))
|
||||||
|
|
||||||
|
# Die Zentrale ist jetzt frei - ihr Stamm-Besetzer kommt zurueck.
|
||||||
|
returning = _restore_zentrale_owner(conn, target) if was_here else None
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
scheduler.push_user_light(user["azure_id"], user["display_name"],
|
scheduler.push_user_light(user["azure_id"], user["display_name"],
|
||||||
home["hostname"], home["segment"], conn)
|
home["hostname"], home["segment"], conn)
|
||||||
if was_here:
|
if was_here:
|
||||||
scheduler.clear_segment(target["hostname"], target["segment"], conn)
|
if returning:
|
||||||
|
scheduler.push_user_light(returning["user_azure_id"], returning["display_name"],
|
||||||
|
target["hostname"], target["segment"], conn)
|
||||||
|
else:
|
||||||
|
scheduler.clear_segment(target["hostname"], target["segment"], conn)
|
||||||
|
|
||||||
logger.info("Zentrale verlassen von %s, zurueck auf %s Segment %s",
|
logger.info("Zentrale verlassen von %s, zurueck auf %s Segment %s; Zentrale nun: %s",
|
||||||
user["display_name"], home["hostname"], home["segment"])
|
user["display_name"], home["hostname"], home["segment"],
|
||||||
|
returning["display_name"] if returning else "frei")
|
||||||
|
|
||||||
return _zentrale_view(request, conn, target, extension=extension, user=user,
|
msg = (f"{user['display_name']} ist zurueck auf dem eigenen Tuerschild "
|
||||||
message=(f"{user['display_name']} ist zurueck auf dem eigenen Tuerschild "
|
f"({home['name'] or home['hostname']}, Segment {home['segment']}).")
|
||||||
f"({home['name'] or home['hostname']}, Segment {home['segment']})."))
|
if returning:
|
||||||
|
msg += f" {returning['display_name']} sitzt wieder auf der Zentrale."
|
||||||
|
return _zentrale_view(request, conn, target, extension=extension, user=user, message=msg)
|
||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user