Add observer detail rows with SNR/path data and UI polish

- Add path_len column to event_observers table with Alembic migration
- Create shared observer_utils.py for building observer responses
- Extract SNR and path_len from LetsMesh uploads via normalizer
- Pass snr/path_len from all 4 handlers to add_event_observer()
- Add expandable observer detail sub-tables to messages and ads pages
- Rename Receivers column to Observers with updated i18n key
- Add satellite dish icon to observer badges and detail rows
- Add mobile card observer detail toggle
- Show full datetime tooltip on hover for relative times
- Hide Path column from advertisement observer tables (not populated)
This commit is contained in:
Louis King committed 2026-04-26 13:02:39 +01:00
1 parent a0d59748f2
commit e867f0074a
30 files changed
+1208 -203

No files matched your search

@@ -0,0 +1,45 @@
"""add path_len to event_observers
Revision ID: a10dbca883a2
Revises: b1c2d3e4f5a6
Create Date: 2026-04-26 10:52:09.664958+00:00
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = "a10dbca883a2"
down_revision: Union[str, None] = "b1c2d3e4f5a6"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("event_observers", schema=None) as batch_op:
batch_op.add_column(sa.Column("path_len", sa.Integer(), nullable=True))
batch_op.drop_constraint(
batch_op.f("uq_event_receivers_hash_node"), type_="unique"
)
batch_op.create_unique_constraint(
"uq_event_observers_hash_node", ["event_hash", "observer_node_id"]
)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table("event_observers", schema=None) as batch_op:
batch_op.drop_constraint("uq_event_observers_hash_node", type_="unique")
batch_op.create_unique_constraint(
batch_op.f("uq_event_receivers_hash_node"),
["event_hash", "observer_node_id"],
)
batch_op.drop_column("path_len")
# ### end Alembic commands ###