diff --git a/meshview/store.py b/meshview/store.py index 711f532..b5deba3 100644 --- a/meshview/store.py +++ b/meshview/store.py @@ -171,29 +171,41 @@ async def get_total_node_count(channel: str = None) -> int: async def get_top_traffic_nodes(): - async with database.async_session() as session: - result = await session.execute(text(""" - SELECT - n.node_id, - n.long_name, - n.role, - COUNT(p.id) AS packet_count - FROM - packet p - JOIN - node n - ON - p.from_node_id = n.node_id - WHERE - p.import_time >= DATETIME('now', 'localtime', '-1 day') - GROUP BY - n.long_name, n.role - ORDER BY - packet_count DESC - LIMIT 100; - """)) + try: + async with database.async_session() as session: # Assuming this is your DB session + result = await session.execute(text(""" + SELECT + n.node_id, + n.long_name, + n.short_name, + n.channel, + COUNT(DISTINCT p.id) AS total_packets_sent, + COUNT(ps.packet_id) AS total_times_seen + FROM node n + LEFT JOIN packet p ON n.node_id = p.from_node_id + AND p.import_time >= DATETIME('now', '-24 hours') + LEFT JOIN packet_seen ps ON p.id = ps.packet_id + GROUP BY n.node_id, n.long_name, n.short_name + ORDER BY total_times_seen DESC; + """)) + + rows = result.fetchall() + + nodes = [{ + 'node_id': row[0], + 'long_name': row[1], + 'short_name': row[2], + 'channel': row[3], + 'total_packets_sent': row[4], + 'total_times_seen': row[5] + } for row in rows] + return nodes + + except Exception as e: + print(f"Error retrieving top traffic nodes: {str(e)}") + return [] + - return result.fetchall() # Returns a list of tuples async def get_node_traffic(node_id: int): try: diff --git a/meshview/templates/base.html b/meshview/templates/base.html index 9b422e4..b45b3ac 100644 --- a/meshview/templates/base.html +++ b/meshview/templates/base.html @@ -10,8 +10,6 @@ - - {% block head %} {% endblock %} @@ -34,7 +32,7 @@ {% endblock %} - +
{{ site_config["site"]["title"] }} {{ site_config["site"]["domain"] }}
{{ site_config["site"]["message"] }}
Quick Links:   @@ -50,7 +48,6 @@ {% if site_config["site"]["top"] == "True" %}Top Traffic{% endif %}

- {% block body %} {% endblock %}
Visit Meshview on Github.

diff --git a/meshview/templates/top.html b/meshview/templates/top.html index b875f77..61f77ba 100644 --- a/meshview/templates/top.html +++ b/meshview/templates/top.html @@ -1,65 +1,211 @@ {% extends "base.html" %} + {% block css %} -.table-title { - font-size: 2rem; - text-align: center; - margin-bottom: 20px; - } +/* General table styling */ +table { + width: 100%; + border-collapse: collapse; + margin-top: 20px; +} - .traffic-table { - width: 60%; - border-collapse: collapse; - margin: 0 auto; - font-family: Arial, sans-serif; - } +table th, table td { + padding: 12px; + text-align: left; + border: 1px solid #ddd; + cursor: pointer; /* Makes the column headers clickable */ +} - .traffic-table th, - .traffic-table td { - padding: 10px 15px; - text-align: left; - border: 1px solid #474b4e; - } +table th { + background-color: #333; + color: #fff; + font-weight: bold; +} - .traffic-table th { - background-color: #272b2f; - color: white; - } +table tbody tr:nth-child(odd) { + background-color: #272b2f; /* Slightly lighter than #2a2a2a */ +} - .traffic:nth-of-type(odd) { - background-color: #272b2f; /* Lighter than #2a2a2a */ - } +table tbody tr:nth-child(even) { + background-color: #212529; /* Slightly lighter than #181818 */ +} - .traffic { - border: 1px solid #474b4e; - padding: 8px; - margin-bottom: 4px; - border-radius: 8px; - } +table tbody tr:hover { + background-color: #555; /* Light hover effect */ +} + +table td { + color: #ddd; +} + +table th, table td { + border-radius: 8px; +} + +/* Responsive Table for smaller screens */ +@media (max-width: 768px) { + table th, table td { + padding: 8px; + } +} + +h1 { + text-align: center; + color: #ddd; + margin-top: 20px; + margin-bottom: 20px; +} + +/* Bell curve chart container */ +#bellCurveChart { + height: 400px; + width: 100%; + margin-top: 30px; +} + +#stats { + text-align: center; /* Centers the content inside the #stats container */ + margin-top: 20px; /* Adds a 20px margin at the top */ + color: #fff +} - .traffic:nth-of-type(even) { - background-color: #212529; /* Slightly lighter than the previous #181818 */ - } {% endblock %} {% block body %} -

Top Traffic Nodes (last 24 hours)

- +

Top Traffic Nodes

+ + +
+ + +
+

Mean:

+

Standard Deviation:

+
+ + {% if nodes %} +
+
- - - - - + + + + + + + - {% for node in nodes %} - - - - - - {% endfor %} + {% for node in nodes %} + + + + + + + + {% endfor %} -
Node NameRolePacket Count
Long NameShort NameChannelPackets SentTimes Seen
{{ node[1] }} {{ node[2] }}{{ node[3] }}
{{ node.long_name }}{{ node.short_name }}{{ node.channel }}{{ node.total_packets_sent }}{{ node.total_times_seen }}
+ + + {% else %} +

No top traffic nodes available.

+ {% endif %} + + {% endblock %} diff --git a/meshview/web.py b/meshview/web.py index 220b371..f7b3a06 100644 --- a/meshview/web.py +++ b/meshview/web.py @@ -1163,7 +1163,7 @@ async def net(request): # Filter packets: exclude "seq \d+$" but include those containing Tag filtered_packets = [ p for p in ui_packets - if not seq_pattern.match(p.payload) and CONFIG["site"]["net_tag"] in p.payload.lower() + if not seq_pattern.match(p.payload) and (CONFIG["site"]["net_tag"]).lower() in p.payload.lower() ] # Render template