Refactor NOAA period name handling in WxCommand and enhance bot configuration retrieval

- Replaced the `abbreviate_noaa` method calls with a new `_noaa_period_display_name` method to improve the display of forecast period names, including handling federal holidays.
- Updated the `BotDataViewer` class to include the bot name in the returned configuration dictionary, ensuring consistent naming across the application.
- Made minor adjustments to HTML templates for better layout and added footer links for project visibility.
This commit is contained in:
agessaman
2026-02-16 16:05:45 -08:00
parent 8f7b46aee3
commit cd028a2160
4 changed files with 52 additions and 15 deletions

View File

@@ -875,7 +875,7 @@ class WxCommand(BaseCommand):
return "No forecast data available", weather_json
current = forecast[0]
day_name = self.abbreviate_noaa(current['name'])
day_name = self._noaa_period_display_name(current)
temp = current.get('temperature', 'N/A')
temp_unit = current.get('temperatureUnit', 'F')
short_forecast = current.get('shortForecast', 'Unknown')
@@ -1005,7 +1005,7 @@ class WxCommand(BaseCommand):
if is_current_night and today_period:
period = today_period[1]
# Always add today_period - it represents tomorrow's daytime when current is Tonight
period_name = self.abbreviate_noaa(period.get('name', 'Today'))
period_name = self._noaa_period_display_name(period)
period_temp = period.get('temperature', '')
period_short = period.get('shortForecast', '')
period_detailed = period.get('detailedForecast', '')
@@ -1063,7 +1063,7 @@ class WxCommand(BaseCommand):
if should_add_tonight:
period = tonight_period[1]
period_name = self.abbreviate_noaa(period.get('name', 'Tonight'))
period_name = self._noaa_period_display_name(period)
period_temp = period.get('temperature', '')
period_short = period.get('shortForecast', '')
period_detailed = period.get('detailedForecast', '')
@@ -1110,7 +1110,7 @@ class WxCommand(BaseCommand):
# Prioritize adding Tomorrow when current is Tonight to use more of the available message length
if tomorrow_period:
period = tomorrow_period[1]
period_name = self.abbreviate_noaa(period.get('name', 'Tomorrow'))
period_name = self._noaa_period_display_name(period)
period_temp = period.get('temperature', '')
period_short = period.get('shortForecast', '')
period_detailed = period.get('detailedForecast', '')
@@ -1467,7 +1467,7 @@ class WxCommand(BaseCommand):
# Build detailed forecast for tomorrow
parts = []
for period in tomorrow_periods:
period_name = self.abbreviate_noaa(period.get('name', 'Tomorrow'))
period_name = self._noaa_period_display_name(period)
temp = period.get('temperature', '')
temp_unit = period.get('temperatureUnit', 'F')
short_forecast = period.get('shortForecast', '')
@@ -3417,6 +3417,35 @@ class WxCommand(BaseCommand):
else:
return "🌤️" # Default weather emoji
# NOAA sometimes names forecast periods after federal holidays (e.g. "Washington's Birthday")
# instead of the weekday. Match these so we can resolve to weekday via startTime.
_NOAA_HOLIDAY_NAME_PATTERNS = (
"washington's birthday", "presidents day", "president's day",
"martin luther king", "mlk day", "memorial day", "labor day",
"independence day", "juneteenth", "columbus day", "veterans day",
"thanksgiving", "christmas day", "new year's day", "new year's eve",
)
def _noaa_period_display_name(self, period: dict) -> str:
"""Return display label for a NOAA forecast period. Resolves holiday names to weekday."""
name = period.get('name', '') or ''
start_time_str = period.get('startTime')
name_lower = name.lower()
is_holiday = any(p in name_lower for p in self._NOAA_HOLIDAY_NAME_PATTERNS)
if is_holiday and start_time_str:
try:
# startTime is ISO 8601, e.g. 2025-02-17T08:00:00-08:00
dt = datetime.fromisoformat(start_time_str.replace('Z', '+00:00'))
# Python weekday(): Mon=0 .. Sun=6
weekdays = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')
day_abbrev = weekdays[dt.weekday()]
if 'night' in name_lower or 'overnight' in name_lower:
return f"{day_abbrev} Night"
return day_abbrev
except (ValueError, TypeError):
pass
return self.abbreviate_noaa(name)
def abbreviate_noaa(self, text: str) -> str:
"""Replace long strings with shorter ones for display"""
replacements = {

View File

@@ -162,10 +162,14 @@ class BotDataViewer:
feed_manager_enabled = self.config.getboolean('Feed_Manager', 'feed_manager_enabled', fallback=False)
except (configparser.NoSectionError, configparser.NoOptionError, ValueError, TypeError):
feed_manager_enabled = False
return dict(greeter_enabled=greeter_enabled, feed_manager_enabled=feed_manager_enabled)
try:
bot_name = (self.config.get('Bot', 'bot_name', fallback='MeshCore Bot') or '').strip() or 'MeshCore Bot'
except (configparser.NoSectionError, configparser.NoOptionError):
bot_name = 'MeshCore Bot'
return dict(greeter_enabled=greeter_enabled, feed_manager_enabled=feed_manager_enabled, bot_name=bot_name)
except Exception as e:
self.logger.exception("Template context processor failed: %s", e)
return dict(greeter_enabled=False, feed_manager_enabled=False)
return dict(greeter_enabled=False, feed_manager_enabled=False, bot_name='MeshCore Bot')
def _init_databases(self):
"""Initialize database connections"""

View File

@@ -63,7 +63,6 @@
/* Apply theme variables */
html, body {
height: 100%;
margin: 0;
padding: 0;
}
@@ -72,8 +71,6 @@
background-color: var(--bg-color);
color: var(--text-color);
transition: background-color 0.3s ease, color 0.3s ease;
display: flex;
flex-direction: column;
}
.navbar-brand {
@@ -349,7 +346,7 @@
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">
<i class="fas fa-satellite-dish"></i> MeshCore Bot
<i class="fas fa-satellite-dish"></i> {{ bot_name|default('MeshCore Bot') }}
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
@@ -423,10 +420,17 @@
</nav>
<!-- Main Content -->
<div class="container-fluid mt-4 flex-grow-1" style="display: flex; flex-direction: column; min-height: 0;">
<div class="container-fluid mt-4">
{% block content %}{% endblock %}
</div>
<!-- Footer links (bottom of page, visible when scrolled down) -->
<footer class="text-center py-2" style="font-size: 0.7rem;">
<a href="https://github.com/agessaman/meshcore-bot" target="_blank" rel="noopener noreferrer" style="color: var(--text-muted, #6c757d); text-decoration: none;">meshcore-bot</a>
<span class="mx-1" style="color: var(--text-muted, #6c757d);"> for </span>
<a href="https://meshcore.co.uk/index.html" target="_blank" rel="noopener noreferrer" style="color: var(--text-muted, #6c757d); text-decoration: none;">MeshCore</a>
</footer>
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>

View File

@@ -5,11 +5,11 @@
{% block content %}
<style>
/* Make the mesh graph page use full height - scoped to this page only */
/* Fixed height so flex children (e.g. visualization-row) get remaining space; navbar + title + stats ~220px */
#mesh-graph-container {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
height: calc(100vh - 110px);
}
#map-view, #graph-view {
@@ -41,7 +41,7 @@
}
</style>
<div id="mesh-graph-container" class="d-flex flex-column" style="flex: 1; min-height: 0;">
<div id="mesh-graph-container" class="d-flex flex-column">
<div class="row">
<div class="col-12">
<h1 class="mb-4">