feat: Add dashboard support and enhance Discord webhook functionality
Build and Deploy Docker Container / build-and-deploy (push) Failing after 3m8s
Test Application / test (push) Failing after 28s

- Updated docker-compose.yml to expose dashboard on port 8080.
- Enhanced main.py with timezone database caching and improved state management.
- Introduced a minimal dashboard using Flask to display webhook status and notifications.
- Added templates.json for customizable embed messages in Discord notifications.
- Created templates.py for loading and saving notification templates.
- Implemented tests for dashboard rendering and main functionality.
- Added requirements for Flask and tzdata to support new features.
- Included test cases for timezone handling and template management.
This commit is contained in:
2026-05-09 21:59:52 +02:00
parent 983c7cde9d
commit e30920067f
13 changed files with 1143 additions and 44 deletions
+94
View File
@@ -0,0 +1,94 @@
import pytz
from datetime import datetime
import pandas as pd
def get_tz_info(tz_name: str, timezones: list[dict]) -> dict | None:
"""Get timezone info by name."""
return next((tz for tz in timezones if tz["zone_name"] == tz_name), None)
def get_country_info(country_code: str, countries: list[dict]) -> dict | None:
"""Get country info by country code."""
return next((c for c in countries if c["country_code"] == country_code), None)
def where_is_it_420(timezones: list[dict], countries: list[dict]) -> list[str]:
"""Get timezones where the current hour is 4 or 16, indicating it's 4:20 there.
Returns:
list[str]: A list of timezones where it's currently 4:20 PM or AM.
"""
tz_list = []
for tz in pytz.all_timezones:
now = datetime.now(pytz.timezone(tz))
if now.hour == 4 or now.hour == 16:
# Find the timezone in the loaded timezones
tz_info = get_tz_info(tz, timezones)
if tz_info:
country = get_country_info(tz_info["country_code"], countries)
if country:
country_name = country["country_name"].strip().strip('"')
if country_name not in tz_list:
tz_list.append(country_name)
return tz_list
def load_tz_file():
timezone_file = "./tzdb/TimeZoneDB.csv/time_zone.csv"
# column names in the csv
timezone_names = ["zone_name", "country_code",
"abbreviation", "time_start", "gmt_offset", "dst"]
# columns to load
load_columns = ["zone_name", "country_code"]
# read csv with pandas
df = pd.read_csv(timezone_file, names=timezone_names)
# drop all columns except load_columns
df = df[load_columns]
# distinct zone_names
df = df.drop_duplicates(subset=["zone_name"])
# reset index
df = df.reset_index(drop=True)
return df
def main():
# read csv with pandas
df_file = load_tz_file()
# split zone_name into components by "/"
df_file[['region', 'city']] = df_file['zone_name'].str.split(
'/', expand=True, n=1)
# drop regions with no country_code (like Etc, GMT, etc)
df_file = df_file[df_file['country_code'].notna()]
df_tz = pd.DataFrame(pytz.all_timezones)
# rename column to zone_name
df_tz = df_tz.rename(columns={0: 'zone_name'})
# split zone_name into components by "/"
df_tz[['region', 'city']] = df_tz['zone_name'].str.split(
'/', expand=True, n=1)
# drop regions with no city (like UTC, GMT, etc)
df_tz = df_tz[df_tz['city'].notna()]
# drop rows where region is 'Etc'
df_tz = df_tz[df_tz['region'] != 'Etc']
# join dataframes on region and city
df_merged = pd.merge(df_file, df_tz, on=[
'region', 'city'], how='inner', indicator=True)
# reorder columns
df_merged = df_merged[['region', 'city', 'country_code']]
# print merged dataframe
print(f"Merged timezones: {len(df_merged)}")
print(df_merged.sample(20).to_string(index=False))
regions = df_merged['region'].unique()
for region in regions:
df_region = df_merged[df_merged['region'] == region]
print(f"{len(df_region)} merged in {region}")
if __name__ == "__main__":
main()