def make_notifications() -> None: ''' Returns ------- None This function calls on the news_filter and weather_update modules to collect the data form the relevant API's. It then unpacks them an appends them to the NOTIFICATIONS list for rendering.''' global NOTIFICATIONS NOTIFICATIONS = [] api = Cov19API(filters=covid_data["nationally"], structure=covid_data["cases_and_deaths"]) data = api.get_json()['data'][0] NOTIFICATIONS.append({'title': 'Covid Data',\ 'content': Markup( f'<p style="text-align:left;">The COVID data for the\ {data["areaName"]} as of {data["Date"]} is:\ <br />Cases:\ <br /> Total Cases: {str(data["cases"]["cumCasesByPublishDate"])}\ <br /> New Cases: {str(data["cases"]["newCasesByPublishDate"])}\ <br />Deaths:\ <br /> Total Deaths: {str(data["deaths"]["cumDeaths28DaysByPublishDate"])}\ <br /> New Deaths: {str(data["deaths"]["newDeaths28DaysByPublishDate"])}</p>')}) NOTIFICATIONS.append({'title':'Weather',\ 'content':Markup(get_weather(location['city'], API_keys['weather']))}) NOTIFICATIONS.append({'title':'Headlines',\ 'content':Markup(get_news(location['country'], API_keys['news'], True))}) for notification in get_news(location['country'], API_keys['news']): NOTIFICATIONS.append(notification) logging.info('Notifications have been reloaded.')
def update_api(): """ Function update_api , check if api are updates by using config file """ with open('config.json', 'r') as config_file: config_data = json.load(config_file) update_date = config_data["updates_api"][0]["last_update_time"] # If the api never was updated if update_date == "empty": news_filter.get_news() weather_update.get_weather() uk_covid_19.local_covid_data() config_data["updates"][0]["last_update_time"] = current_time else: # If the update date is expired ( more than 1 hour) if date_to_seconds(current_time) - date_to_seconds( update_date) >= 3600: news_filter.get_news() weather_update.get_weather() uk_covid_19.local_covid_data() config_data["updates"][0]["last_update_time"] = current_time with open('config.json', 'w') as config_file: json.dump(config_data, config_file)
def trigger_alarm(data: dict) -> None: ''' This function is used to trigger an alarm, it is only called on when an alarm is meant to go off keyword argument: data -- the alarm information such as content, title etc. ''' logging.info('Alarm called %s is going off at %s', data['title'], datetime.now()) engine = pyttsx3.init() # initialise the tts engine engine.say('Your alarm called %s is going off' % data['title']) # announce that the alarm is going off if data['weather']: # if the weather is enabled try: engine.say(''' The current temperature is {0} degrees celcius. The level of humidity is {1} percent. And lastly, the weather is described as {2} '''.format(CURRENT_WEATHER['temperature'], CURRENT_WEATHER['humidity'], CURRENT_WEATHER['description'])) # announce the weather except: logging.error('%s:An error occured with the weather API', datetime.now()) if data['news']: # if the news is enabled try: three_articles = random.sample( get_news(get_news_from_api(news_api_key).json()), 3) news_content = set([a['title'] for a in three_articles]) engine.say('Here are some top news articles: {0}'.format( '. '.join(news_content))) # announce 3 random news articles except: logging.error('%s:An error occured with the news API', datetime.now()) try: covid = get_covid() # get the covid data for england cases = covid['newCasesByPublishDate'] # get cases today deaths = covid[ 'newDeaths28DaysByPublishDate'] # get deaths today for people who tested positive in the last 28 days cum_cases = covid['cumCasesByPublishDate'] # get total cases cum_deaths = covid[ 'cumDeaths28DaysByPublishDate'] # get total deaths for people who tested positive in the last 28 days engine.say(''' In regards, to COVID-19, there have been {0} cases and {1} deaths today. In total, there have bee {2} cases and {3} deaths. '''.format(cases, deaths, cum_cases, cum_deaths)) # announce the covid data except: logging.error('%s:An error occured with the covid API', datetime.now()) engine.runAndWait() del data['alarm_object'] # delete the scheduler event from the dictionary alarms.remove(data) # delete the alarm from the alarms list
def announce_weather_and_news(announcement): """ This function says out loud the content of the alarm that is expected to say also the weather and the news. It also says out loud the weather and the news. """ logging.info("Alarm with news and weather has been said") try: engine.endLoop() except: logging.error('PyTTSx3 Endloop error') engine.say(announcement + get_weather() + get_news()) engine.runAndWait()
def print_news(): """ This function adds every six hours new weather, news and cases notifications into the notifications list that will be later displayed on the web server. """ news_function = get_news() weather_funtion = get_weather() cases_function = get_cases() notifications.append({ 'title': current_time() + " - Weather", 'content': weather_funtion }) notifications.append({ 'title': current_time() + " - Top news", 'content': news_function }) notifications.append({ 'title': current_time() + " - Cases", 'content': cases_function }) logging.info("Notifications added to notifications list") s.enter(6 * 3600, 5, print_news, [])
def test_wrong_country(): assert get_news( "odosfsdo", "b21e04fe3eef409a9f4570081aa0ca0d") == "Error country tag not valid or no news from selected news sources avaliable, consider changing your sources in the config file!"
def test_api_key(): assert get_news( "gb", "3243423423424") == "Error country tag not valid or api key not valid or no news from selected news sources or the open news api is offline!"
def test_good_input(): assert get_news( "gb", "b21e04fe3eef409a9f4570081aa0ca0d") != "Error country tag not valid or api key not valid or no news from selected news sources or the open news api is offline!"
def test_bad_input(): assert get_news( 123, 12312313) == "Error country tag not valid or api key not valid or no news from selected news sources or the open news api is offline!"
def run_alarm(alarm_label: str, news: bool = False, weather: bool = False) -> None: ''' When an alarm is run we set off a noise aswell as showing any updates we need in news or weather ''' text_to_read = [] title = "" alarm_active = False # Find the alarm in the ALARMS list and remove it for alarm in ALARMS: if alarm['title'] == alarm_label: ALARMS.remove(alarm) alarm_active = True continue # If we can't find the alarm then return an error if not alarm_active: # Return error to log only, error alarm doesn't exist write_to_log( "ERROR TECH - No alarm found", False) return index write_to_log( "ALARM - Alarm: " + alarm_label + " has rung") # Add the alarm too the notifcation pannel title = "Alarm - " + alarm_label NOTIFICATIONS.append({"title": title, "content": ( "Alarm " + alarm_label + " has rung")}) # Add the alarm to the text to speech list text_to_read.append(("Alarm " + alarm_label + " has rung!")) # If news is enabled show the news and if Covid brief enabled show this too if news: # Same logic as the earlier brieifing just adds # the content too the text too speech text_to_read.append("News Update") # Make sure to still run if an api error is caught try: news_update, covid_update = get_news( CURRENT_COUNTRY, NEWS_KEY, NEWS_PROVIDER_PREFERENCES) except ValueError as e: message = news_update, covid_update = get_news( CURRENT_COUNTRY, NEWS_KEY, NEWS_PROVIDER_PREFERENCES) write_to_log(message) text_to_read.append(message) # Read out all loaded text so far tts_request(text_to_read) LOGGED_ERRORS.append( message) NOTIFICATIONS.append( {"title": ("Error - UID:" + str(len(LOGGED_ERRORS))), "content": message}) # Save the NOTIFICATIONS and the removed alarm too the config write_config() return index for article in news_update: title = "News Update - " + \ str(datetime.now().strftime("%d/%m/%Y %H:%M")) +\ " - Story " + str(news_update.index(article) + 1) NOTIFICATIONS.append({"title": title, "content": article}) text_to_read.append(article) if COVID_BRIEF: text_to_read.append("COVID-19 Update") # Make sure to run through even if the api is down or we have wrong information try: total_cases, cases_today = get_covid_19_cases(CURRENT_CITY) except ValueError as e: message = get_covid_19_cases(CURRENT_CITY) write_to_log(message) text_to_read.append(message) # Read out all loaded text so far tts_request(text_to_read) LOGGED_ERRORS.append( message) NOTIFICATIONS.append( {"title": ("Error - UID:" + str(len(LOGGED_ERRORS))), "content": message}) # Save the NOTIFICATIONS and the removed alarm too the config write_config() return index message = "Covid-19 cases today in " + CURRENT_CITY + " " + \ str(cases_today) + " giving a cumulative total of " + \ str(total_cases) + " in " + CURRENT_CITY text_to_read.append(message) NOTIFICATIONS.append({"title": "COVID Statistics Update - " + str( datetime.now().strftime("%d/%m/%Y %H:%M")), "content": message}) for covid_article in covid_update: title = "COVID News Update - " + \ str(datetime.now().strftime("%d/%m/%Y %H:%M")) +\ " - Story " + str(covid_update.index(covid_article) + 1) NOTIFICATIONS.append( {"title": title, "content": covid_article}) text_to_read.append(covid_article) # If weather brief is on then display the weather too if weather: # Same logic as the earlier brieifing just adds # the content too the text too speech text_to_read.append("Weather Update") # We make sure that there is a correct api key or city set otherwise we call an error try: current_temperature, current_pressure, current_humidiy, \ weather_description = get_weather(CURRENT_CITY, WEATHER_KEY) except ValueError as e: message = get_weather(CURRENT_CITY, WEATHER_KEY) write_to_log(message) LOGGED_ERRORS.append( message) text_to_read.append(message) # Read out all loaded text so far tts_request(text_to_read) NOTIFICATIONS.append( {"title": ("Error - UID:" + str(len(LOGGED_ERRORS))), "content": message}) # Save the NOTIFICATIONS and the removed alarm too the config write_config() return index NOTIFICATIONS.append( {"title": ("Weather Update - " + str(datetime.now().strftime("%d/%m/%Y %H:%M"))), "content": ("Temperature (in degrees): " + str(int(current_temperature) - 273) + "\n | Atmospheric pressure (in hPa unit): " + str(current_pressure) + "\n | Humidity (as a percentage): " + str(current_humidiy) + "\n | Current weather: " + str(weather_description))}) text_to_read.append(("Temperature (in degrees) | " + str(int(current_temperature) - 273) + "\n | Atmospheric pressure (in hPa unit) | " + str(current_pressure) + "\n | Humidity (as a percentage) | " + str(current_humidiy) + "\n | Current weather | " + str(weather_description))) # Save the NOTIFICATIONS and the removed alarm too the config write_config() # Read out the alarm aswell as any annoucements we added tts_request(text_to_read) # Refresh the main page return index
def run_briefing(time_select: str, date_time) -> None: ''' When a briefing time is reched we get the current news stories and weather aswell as covid-19 stories if this enabled by the user and display them silently in the NOTIFICATIONS ''' # Load in the news and covid stories from the news api try: news_update, covid_update = get_news( CURRENT_COUNTRY, NEWS_KEY, NEWS_PROVIDER_PREFERENCES) except ValueError as e: message = get_news( CURRENT_COUNTRY, NEWS_KEY, NEWS_PROVIDER_PREFERENCES) write_to_log(message) LOGGED_ERRORS.append( message) NOTIFICATIONS.append( {"title": ("Error - UID:" + str(len(LOGGED_ERRORS))), "content": message}) # Save the NOTIFICATIONS and the removed alarm too the config write_config() return index # Loop through all the news stories for article in news_update: # Add the news story as content along with a custom title # (effectively a UID for the notification) to the notification pannel title = "News Update - " + \ str(datetime.now().strftime("%d/%m/%Y %H:%M")) +\ " - Story " + str(news_update.index(article) + 1) NOTIFICATIONS.append({"title": title, "content": article}) # If the user has enabled Covid-19 updates in their config (On by default) if COVID_BRIEF: # Make sure to run through even if the api is down or we have wrong information try: # Load in the daily covid cases for the current/nearest city total_cases, cases_today = get_covid_19_cases(CURRENT_CITY) except ValueError as e: message = get_covid_19_cases(CURRENT_CITY) write_to_log(message) LOGGED_ERRORS.append( message) NOTIFICATIONS.append( {"title": ("Error - UID:" + str(len(LOGGED_ERRORS))), "content": message}) # Save the NOTIFICATIONS and the removed alarm too the config write_config() return index # Then create a message to show the covid cases for the day # aswell as the total covid cases message = "Covid-19 cases today in " + CURRENT_CITY + " " + \ str(cases_today) + " giving a cumulative total of " + \ str(total_cases) + " in " + CURRENT_CITY # Add this message along with a custom # dated title too the NOTIFICATIONS NOTIFICATIONS.append({"title": "COVID Statistics Update - " + str( datetime.now().strftime("%d/%m/%Y %H:%M")), "content": message}) # Then loop through the covid-19 news articles for covid_article in covid_update: # Give the article a title using the date and time as UID title = "COVID News Update - " + \ str(datetime.now().strftime("%d/%m/%Y %H:%M")) +\ " - Story " + str(covid_update.index(covid_article) + 1) NOTIFICATIONS.append( {"title": title, "content": covid_article}) # Run the get_weather to open weather api # to get the current weather conditions try: current_temperature, current_pressure, current_humidiy, \ weather_description = get_weather(CURRENT_CITY, WEATHER_KEY) except ValueError as e: message = get_weather(CURRENT_CITY, WEATHER_KEY) write_to_log(message) LOGGED_ERRORS.append( message) NOTIFICATIONS.append( {"title": ("Error - UID:" + str(len(LOGGED_ERRORS))), "content": message}) # Save the NOTIFICATIONS and the removed alarm too the config write_config() return index # Add the weather to the NOTIFICATIONS NOTIFICATIONS.append( {"title": ("Weather Update - " + str(datetime.now().strftime("%d/%m/%Y %H:%M"))), "content": ("Temperature (in degrees): " + str(int(current_temperature) - 273) + "\n | Atmospheric pressure (in hPa unit): " + str(current_pressure) + "\n | Humidity (as a percentage): " + str(current_humidiy) + "\n | Current weather: " + str(weather_description))}) # Add a day too the current briefing time date_time = date_time + timedelta(days=1) # Calculate the time too the next briefing # and then re-add it too the scheduler time_to_alarm = (date_time - datetime.now()).total_seconds() S.enter(time_to_alarm, 1, run_briefing, (time_select, date_time)) # Refresh the page return index
def test_one(): news_json = json.load(open('gb-news.json')) news = get_news(news_json) assert len(news) >= 1
def test_two(): news_json = json.load(open('gb-news.json')) news = get_news(news_json) assert news[0]['title'] != None
def update_notifications() -> None: ''' This function is used to update notifications with information from the news, weather and covid api ''' try: for article in get_news(get_news_from_api(news_api_key).json( )): # for each article in the articles from the api if article not in notifications and article not in old_notifications: # if the notification isn't in current notifications and not in old notifications notifications.insert(0, { 'title': article['title'], 'content': article['content'] }) # insert the notification in the list of notifications logging.info('%s:"%s" was added to the list of notifications', datetime.now(), article['title']) except: logging.error('%s:An error occured with the news API', datetime.now()) try: new_weather = get_weather( get_weather_from_api( weather_api_key, config['city']).json()) # get the weather for the current city global CURRENT_WEATHER # set the variable as a global variable if new_weather != CURRENT_WEATHER: # if the weather has changed content = '' if new_weather['temperature'] != CURRENT_WEATHER[ 'temperature']: # if the temperature changed content += ' The temperature has changed from {0}°C to {1}°C.'.format( str(CURRENT_WEATHER['temperature']), str(new_weather['temperature'])) if new_weather['humidity'] != CURRENT_WEATHER[ 'humidity']: # if the humidity changed content += ' The level of humidity has changed from {0}% to {1}%.'.format( CURRENT_WEATHER['humidity'], new_weather['humidity']) if new_weather['description'] != CURRENT_WEATHER[ 'description']: # if the description changed content += ' The description of the weather has changed from {0} to {1}.'.format( CURRENT_WEATHER['description'], new_weather['description']) notifications.insert( 0, { 'title': 'Weather Update - {0}'.format( datetime.now().strftime('%d/%m/%Y %H:%M:%S')), 'content': content }) # insert the weather update to the notifications CURRENT_WEATHER = new_weather # update the current weather variable logging.info('%s:"%s" was added to the list of notifications', datetime.now(), notifications[0]['title']) except: logging.error('%s:An error occured with the weather API', datetime.now()) try: covid = get_covid() # get the covid data for england cases = covid['newCasesByPublishDate'] # get cases today deaths = covid[ 'newDeaths28DaysByPublishDate'] # get deaths today for people who tested positive in the last 28 days cases_threshold = config[ 'covid_infected_threshold'] # get the covid infected threshold from the config file deaths_threshold = config[ 'covid_death_threshold'] # get the covid death threshold from the config file deaths = deaths if deaths else 0 # if deaths is None, set it as 0 if cases >= cases_threshold or deaths >= deaths_threshold: # if the cases or deaths is higher than the thresholds covid_content = 'Thare are currently {0} new cases today, and {1} new deaths today'.format( cases, deaths) covid_notif = {'title': 'COVID Update', 'content': covid_content} if (covid_notif not in notifications) and ( covid_notif not in old_notifications): # if the notification is new notifications.insert( 0, covid_notif ) # insert the covid update to the notifications logging.info('%s:"%s" was added to the list of notifications', datetime.now(), covid_notif['title']) except: logging.error('%s:An error occured with the covid API', datetime.now())