This commit is contained in:
death916 2025-11-05 04:28:37 -08:00
parent 0b12a928f5
commit 2b09d76131
4 changed files with 138 additions and 66 deletions

View file

@ -11,12 +11,12 @@ from typing import Any, Dict, List
import reflex as rx import reflex as rx
from utils.news import News from utils.news import News
from utils.scores import NBAScores, mlbScores, nflScores from utils.scores import NBAScores, mlbScores, nflScores
# --- Import your Weather utility ---
from utils.weather import Weather from utils.weather import Weather
# --- Constants --- # --- Constants ---
WEATHER_IMAGE_PATH = "/weather.jpg" # Web path in assets folder WEATHER_IMAGE_PATH = "/weather.jpg" # Web path in assets folder
WEATHER_FETCH_INTERVAL = 360 WEATHER_FETCH_INTERVAL = 360
@ -28,12 +28,13 @@ logging.basicConfig(
class State(rx.State): class State(rx.State):
# --- State Variables --- # --- State Variables ---
current_time: str = ( current_time: str = ""
"" # Note: rx.moment replaces the need for this if used for display
)
alarm_time: str = "" alarm_time: str = ""
alarms: list = [] alarms: List = []
news: List[Dict[str, Any]] = [] news: List[Dict[str, Any]] = []
current_news_index: int = 0
news_rotate_interval: int = 10
news_text: str = ""
nba_scores: List[Dict[str, Any]] = [] nba_scores: List[Dict[str, Any]] = []
mlb_scores: List[Dict[str, Any]] = [] mlb_scores: List[Dict[str, Any]] = []
nfl_scores: List[Dict[str, Any]] = [] nfl_scores: List[Dict[str, Any]] = []
@ -75,7 +76,12 @@ class State(rx.State):
) # trying to test themes remove after ) # trying to test themes remove after
logging.info("Triggering background tasks: Weather") logging.info("Triggering background tasks: Weather")
# Return a list containing the handler references # Return a list containing the handler references
return [State.fetch_weather, State.fetch_sports] return [
State.fetch_weather,
State.fetch_sports,
State.fetch_news,
State.cycle_news,
]
# --- Sports Background Task --- # --- Sports Background Task ---
@ -140,10 +146,8 @@ class State(rx.State):
) )
await asyncio.sleep(500) await asyncio.sleep(500)
"""@rx.event(background=True) @rx.event(background=True)
async def fetch_news(self): async def fetch_news(self):
# Fetches news periodically
# Placeholder for the actual news fetching logic
while True: while True:
try: try:
logging.info("Fetching news...") logging.info("Fetching news...")
@ -152,11 +156,13 @@ class State(rx.State):
logging.warning("No news items fetched.") logging.warning("No news items fetched.")
async with self: async with self:
self.news = [] self.news = []
yield # Update frontend self.current_news_index = 0
yield
else: else:
logging.info(f"Fetched {len(news_items)} news items.") logging.info(f"Fetched {len(news_items)} news items.")
async with self: async with self:
self.news = news_items self.news = news_items
self.current_news_index = 0
yield yield
except Exception as e: except Exception as e:
@ -164,7 +170,22 @@ class State(rx.State):
f"Error in fetch_news background task: {e}", exc_info=True f"Error in fetch_news background task: {e}", exc_info=True
) )
await asyncio.sleep(500) await asyncio.sleep(500)
"""
@rx.event(background=True)
async def cycle_news(self):
while True:
try:
if self.news:
async with self:
self.current_news_index = (self.current_news_index + 1) % len(
self.news
)
yield
except Exception as e:
logging.error(
f"Error in cycle_news background task: {e}", exc_info=True
)
await asyncio.sleep(self.news_rotate_interval)
# --- Weather Background Task --- # --- Weather Background Task ---
@rx.event(background=True) @rx.event(background=True)
@ -350,6 +371,36 @@ def index() -> rx.Component:
background_color="#6f42c1", background_color="#6f42c1",
) )
news_card = rx.card(
rx.box(
rx.text("News Headlines"),
rx.center(
rx.cond(
State.news,
rx.text(
State.news[State.current_news_index]["title"],
font_size="lg",
color="gray.500",
no_of_lines=1,
white_space="nowrap",
overflow="hidden",
text_overflow="ellipsis",
),
rx.text(
"No news available",
font_size="lg",
color="gray.500",
),
),
width="100%",
min_height="3.25rem",
),
padding="4",
),
variant="surface",
width="100%",
)
# Compose the page # Compose the page
page = rx.container( # pyright: ignore[reportReturnType] page = rx.container( # pyright: ignore[reportReturnType]
rx.theme_panel(default_open=False), rx.theme_panel(default_open=False),
@ -357,6 +408,7 @@ def index() -> rx.Component:
rx.vstack( rx.vstack(
clock_button, clock_button,
main_flex, main_flex,
news_card,
align="center", align="center",
spacing="4", spacing="4",
) )

View file

@ -15,6 +15,7 @@ dependencies = [
"mlb-statsapi>=1.8.1", "mlb-statsapi>=1.8.1",
"reflex>=0.6.8", "reflex>=0.6.8",
"aiohttp>=3.11.18", "aiohttp>=3.11.18",
"reflex-text-loop>=0.0.1",
] ]
[dependency-groups] [dependency-groups]

View file

@ -6,13 +6,13 @@ from time import localtime, strftime
import socket import socket
import aiohttp import aiohttp
def print_time(): def print_time():
print(strftime("%B %d, %I:%M %p", localtime())) print(strftime("%B %d, %I:%M %p", localtime()))
class News: class News:
def __init__(self): def __init__(self):
self._news_dict = {}
self._news_dict_length = 0
socket.setdefaulttimeout(10) # Set default timeout for socket operations socket.setdefaulttimeout(10) # Set default timeout for socket operations
async def _fetch_feed(self, session, feed): async def _fetch_feed(self, session, feed):
@ -30,7 +30,7 @@ class News:
text = await response.text() text = await response.text()
d = feedparser.parse(text) d = feedparser.parse(text)
if hasattr(d, 'status') and d.status != 200: if hasattr(d, "status") and d.status != 200:
print(f"Skip feed {feed}: status {d.status}") print(f"Skip feed {feed}: status {d.status}")
return [] return []
@ -40,12 +40,18 @@ class News:
if i >= max_entries: if i >= max_entries:
break # Stop parsing if we've reached the limit break # Stop parsing if we've reached the limit
feed_entries.append({ feed_entries.append(
'title': post.title, {
'source': d.feed.title if hasattr(d.feed, 'title') else 'Unknown', "title": post.title,
'publish_date': post.published if hasattr(post, 'published') else '', "source": d.feed.title
'summary': post.summary if hasattr(post, 'summary') else '' if hasattr(d.feed, "title")
}) else "Unknown",
"publish_date": post.published
if hasattr(post, "published")
else "",
"summary": post.summary if hasattr(post, "summary") else "",
}
)
print(f"Added {len(feed_entries)} entries from {feed}") print(f"Added {len(feed_entries)} entries from {feed}")
return feed_entries return feed_entries
@ -88,20 +94,19 @@ class News:
if not all_entries: if not all_entries:
print("No entries collected") print("No entries collected")
return {} return []
if len(all_entries) > 30: if len(all_entries) > 30:
all_entries = random.sample(all_entries, 30) all_entries = random.sample(all_entries, 30)
for entry in all_entries:
self._news_dict[entry['title']] = entry
try: try:
async with aiofiles.open("news.txt", "w") as f: async with aiofiles.open("news.txt", "w") as f:
print("Writing news to file...") print("Writing news to file...")
for entry in self._news_dict.values(): for entry in all_entries:
await f.write(f"[{entry['publish_date']}] {entry['source']}: {entry['title']}\n") await f.write(
f"[{entry['publish_date']}] {entry['source']}: {entry['title']}\n"
)
except Exception as e: except Exception as e:
print(f"Error writing to news.txt: {e}") print(f"Error writing to news.txt: {e}")
return self._news_dict return all_entries

14
uv.lock generated
View file

@ -268,6 +268,7 @@ dependencies = [
{ name = "nba-api" }, { name = "nba-api" },
{ name = "playwright" }, { name = "playwright" },
{ name = "reflex" }, { name = "reflex" },
{ name = "reflex-text-loop" },
{ name = "requests" }, { name = "requests" },
{ name = "rich" }, { name = "rich" },
] ]
@ -287,6 +288,7 @@ requires-dist = [
{ name = "nba-api", specifier = ">=1.6.1,<2" }, { name = "nba-api", specifier = ">=1.6.1,<2" },
{ name = "playwright", specifier = ">=1.49.1,<2" }, { name = "playwright", specifier = ">=1.49.1,<2" },
{ name = "reflex", specifier = ">=0.6.8" }, { name = "reflex", specifier = ">=0.6.8" },
{ name = "reflex-text-loop", specifier = ">=0.0.1" },
{ name = "requests", specifier = ">=2.31.0,<3" }, { name = "requests", specifier = ">=2.31.0,<3" },
{ name = "rich", specifier = ">=13.7.1,<14" }, { name = "rich", specifier = ">=13.7.1,<14" },
] ]
@ -1099,6 +1101,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/0a/4d/5526f5a21a01b6179e3ebe9abc46dce01d9692ef655e22002a08e10de8b7/reflex_hosting_cli-0.1.42-py3-none-any.whl", hash = "sha256:2742d9eb3576fa001ea2a4971cef1e1db56a08aad619aa035af3b6cc8f80de4c", size = 38668, upload-time = "2025-04-11T17:28:02.296Z" }, { url = "https://files.pythonhosted.org/packages/0a/4d/5526f5a21a01b6179e3ebe9abc46dce01d9692ef655e22002a08e10de8b7/reflex_hosting_cli-0.1.42-py3-none-any.whl", hash = "sha256:2742d9eb3576fa001ea2a4971cef1e1db56a08aad619aa035af3b6cc8f80de4c", size = 38668, upload-time = "2025-04-11T17:28:02.296Z" },
] ]
[[package]]
name = "reflex-text-loop"
version = "0.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "reflex" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/eb/2bf65bab5474452b374951497f82338abc95569a5451dfade2ba52e05133/reflex_text_loop-0.0.1.tar.gz", hash = "sha256:4be9cb2b626a329f4de04dddc7c2571c1d204b6be2b6cc9eb87ed926c82ee47b", size = 6042, upload-time = "2024-09-13T09:48:59.681Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/e6/fc55aec3e78e34d7f226a60719d1e01da8d4a78c2743ec4716af2c74ca0d/reflex_text_loop-0.0.1-py3-none-any.whl", hash = "sha256:37772b301ca87b8195fd37c09e848299bafa8484e104b9c14d5e1182b2cf40b7", size = 6426, upload-time = "2024-09-13T09:48:57.768Z" },
]
[[package]] [[package]]
name = "requests" name = "requests"
version = "2.32.3" version = "2.32.3"