From a304be20714b9a3e0dc65dbd8273cf1d138823e5 Mon Sep 17 00:00:00 2001 From: Death916 Date: Sat, 1 Mar 2025 04:44:44 -0800 Subject: [PATCH] delays --- deathclock/__pycache__/news.cpython-311.pyc | Bin 7217 -> 7660 bytes deathclock/app.py | 13 ++--- deathclock/news.py | 54 ++++++++++++-------- deathclock/news_module.py | 28 +++++++--- 4 files changed, 59 insertions(+), 36 deletions(-) diff --git a/deathclock/__pycache__/news.cpython-311.pyc b/deathclock/__pycache__/news.cpython-311.pyc index 5d0784cd14a9f88d849b43054eec6a70368423b0..bb5b2ce118411174f63e1fbbb20004509aaa634a 100644 GIT binary patch delta 3285 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 IQw`W|0Padq82|tP diff --git a/deathclock/app.py b/deathclock/app.py index e7de5da..91cb3cf 100644 --- a/deathclock/app.py +++ b/deathclock/app.py @@ -7,12 +7,12 @@ from clock_module import ClockModule def create_app(): app = Dash(__name__) - + app.layout = html.Div([ html.H1(id='clock-display', style={'textAlign': 'center', 'cursor': 'pointer'}), dcc.Input(id='time-input', type='time', style={'display': 'none', 'margin': '0 auto'}), html.Div(id='output-selected-time', style={'textAlign': 'center', 'marginBottom': '20px'}), - + html.Div([ html.Div([ html.Div([ @@ -22,26 +22,27 @@ def create_app(): html.Div(id='weather-display') ], id='scores-weather-container'), ]), + html.Div(id='news-ticker'), + dcc.Interval(id='clock-interval', interval=60000, n_intervals=0), dcc.Interval(id='weather-interval', interval=150000, n_intervals=0), dcc.Interval(id='news-interval', interval=300000, n_intervals=0), dcc.Interval(id='nba-interval', interval=300000, n_intervals=0) ]) - + ClockModule(app) WeatherModule(app) NewsModule(app) ScoresModule(app) alarm_module = AlarmModule(app) - + def check_alarms(): trigg = alarm_module.alarm_obj.check_alarm() if trigg: print("ALARM TRIGGERED!") - + check_alarms() - return app if __name__ == '__main__': diff --git a/deathclock/news.py b/deathclock/news.py index 8aa2765..695a9b5 100644 --- a/deathclock/news.py +++ b/deathclock/news.py @@ -13,37 +13,43 @@ class News: 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): """Fetches and parses a single feed asynchronously.""" - max_entries = 10 # Maximum number of entries to fetch from each feed + max_entries = 10 # Maximum number of entries to fetch from each feed + try: - async with session.get(feed) as response: + # Add timeout to the request + timeout = aiohttp.ClientTimeout(total=5) + async with session.get(feed, timeout=timeout) as response: if response.status != 200: print(f"Skip feed {feed}: status {response.status}") return [] + text = await response.text() d = feedparser.parse(text) - + if hasattr(d, 'status') and d.status != 200: print(f"Skip feed {feed}: status {d.status}") return [] - + feed_entries = [] # Limit the number of entries parsed for i, post in enumerate(d.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({ 'title': post.title, 'source': d.feed.title 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}") return feed_entries - + except aiohttp.ClientError as e: print(f"Error processing feed {feed}: {e}") return [] @@ -56,7 +62,7 @@ class News: feeds = [] self._news_dict = {} self._news_dict_length = 0 - + try: async with aiofiles.open("feeds.txt", "r") as f: async for line in f: @@ -64,28 +70,32 @@ class News: except Exception as e: print(f"Error reading feeds.txt: {e}") return {} - + + # Limit the number of feeds to process at once + if len(feeds) > 10: + feeds = random.sample(feeds, 10) + print("Getting news entries...") - async with aiohttp.ClientSession() as session: + timeout = aiohttp.ClientTimeout(total=15) + async with aiohttp.ClientSession(timeout=timeout) as session: tasks = [self._fetch_feed(session, feed) for feed in feeds] - all_feed_entries_list = await asyncio.gather(*tasks) - + all_feed_entries_list = await asyncio.gather(*tasks, return_exceptions=True) + all_entries = [] - for feed_entries in all_feed_entries_list: - if feed_entries: - #Now just add the entries, because we are already limited - all_entries.extend(feed_entries) - + for result in all_feed_entries_list: + if isinstance(result, list) and result: + all_entries.extend(result) + if not all_entries: print("No entries collected") return {} - + if len(all_entries) > 30: all_entries = random.sample(all_entries, 30) - + for entry in all_entries: self._news_dict[entry['title']] = entry - + try: async with aiofiles.open("news.txt", "w") as f: print("Writing news to file...") @@ -93,5 +103,5 @@ class News: await f.write(f"[{entry['publish_date']}] {entry['source']}: {entry['title']}\n") except Exception as e: print(f"Error writing to news.txt: {e}") - + return self._news_dict diff --git a/deathclock/news_module.py b/deathclock/news_module.py index 44fa002..244dfa8 100644 --- a/deathclock/news_module.py +++ b/deathclock/news_module.py @@ -9,16 +9,16 @@ class NewsModule: self.app = app self.news_obj = self.get_news_object() self._last_news_update = datetime.datetime(2000, 1, 1) - self._cached_news = self.create_loading_message() # Initial loading message + self._cached_news = self.create_loading_message() # Initial loading message self._initial_run = True self.setup_callbacks() - + def get_news_object(self): return News() - + def create_loading_message(self): return html.Div("Loading...") - + def setup_callbacks(self): @self.app.callback( Output('news-ticker', 'children'), @@ -29,13 +29,25 @@ class NewsModule: return self._cached_news current_time = datetime.datetime.now() + time_since_update = (current_time - self._last_news_update).total_seconds() + + # Only update if it's been more than 5 minutes or it's the initial run + if time_since_update < 300 and not self._initial_run: + return self._cached_news + try: print("UPDATING NEWS...") - # Execute the async function with asyncio.run - headlines_dict = asyncio.run(self.news_obj.get_news()) + # Create a new event loop for this request + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + headlines_dict = loop.run_until_complete(self.news_obj.get_news()) + loop.close() - combined_items = " | ".join([f"{data['source']}: {headline}" - for headline, data in headlines_dict.items()]) + if not headlines_dict: + return html.Div("No news available at this time.", className="ticker") + + combined_items = " | ".join([f"{data['source']}: {headline}" + for headline, data in headlines_dict.items()]) text_px = len(combined_items) * 8 scroll_speed = 75 duration = max(text_px / scroll_speed, 20)