· 6 years ago · Nov 19, 2019, 11:52 AM
1
2from flask import Flask, render_template, redirect, request
3import time
4import datetime
5import sched
6import pyttsx3
7import requests
8import json
9
10ALARM_LIST = []
11PASSED_ALARM_LIST = []
12ALARM_TIMES = []
13ALARM_NAMES = []
14EVENTS = []
15
16app = Flask(__name__)
17
18s = sched.scheduler(time.time, time.sleep)
19
20
21def APIkeys() -> str:
22 '''
23 This function gets the api keys and website URLs from an external json
24 config file to neaten the code and hide the API key from users. The
25 return of the function is API keys and URLs.
26 '''
27 with open('config.json') as config_file:
28 data = json.load(config_file)
29 APInews = data['APInews']
30 APIweather = data['APIweather']
31 NewsURL = data['NewsURL']
32 WeatherURL = data['WeatherURL']
33 return APInews, APIweather, NewsURL, WeatherURL
34
35
36def read_alarms() -> list:
37 '''
38 This function reads in alarms stored in an external text file and stores
39 them is seperate lists for the alarm name, time and a list formatted for
40 users to read with both.
41 '''
42 global ALARM_NAMES
43 global ALARM_TIMES
44 global ALARM_LIST
45 file_name = "OldNews.txt"
46 f = open(file_name, 'a+').close
47 ALARM_LIST.clear()
48 setAlarms = "SetAlarms.txt"
49 f = open(setAlarms, 'a+').close
50 lines = [line.rstrip() for line in open(setAlarms, "r+")]
51 i = 0
52 while i < len(lines):
53 ALARM_NAMES.append(lines[i])
54 ALARM_TIMES.append(lines[i + 1])
55 ALARM_LIST.append(ALARM_NAMES[-1] + " " + ALARM_TIMES[-1])
56 i += 2
57 return(ALARM_LIST)
58
59
60def alarmNotif():
61 '''
62 This function is called when a scheduled event reaches it's schedule
63 time. It searches through the upcoming alarms to find the one which
64 has been activated and removes it from the list of upcoming alarms as
65 well as adding it to the list of passed alarms.
66 Finally, this function uses the imported text to speach module, pyttsx3,
67 to say out loud that the specific alarm has activated.
68 '''
69 global ALARM_NAMES
70 global ALARM_TIMES
71 global ALARM_LIST
72 global EVENTS
73 alarm_name = ""
74
75 for i in range(len(EVENTS)):
76 alarmfile = "SetAlarms.txt"
77
78 datetimeF = time.strptime(ALARM_TIMES[i], '%a %b %d %H:%M:%S %Y')
79 epochTimeAlarm = time.mktime(datetimeF)
80
81 datetimeF = time.strptime(ALARM_TIMES[i], '%a %b %d %H:%M:%S %Y')
82 epochTimeNow = int(time.time())
83
84 if epochTimeNow >= epochTimeAlarm: # and epochTimeNow + 20 > epochTimeAlarm:
85 alarm_name = ALARM_NAMES[i]
86 PASSED_ALARM_LIST.insert(i, (ALARM_NAMES[i] + " " + ALARM_TIMES[i])) # this
87
88 print("=====Alarm!=====")
89 engine = pyttsx3.init()
90 engine.say("Bing Bong Bing Bong")
91 print("alarm name : " + alarm_name)
92 engine.say("Your alarm ")
93 engine.say(str(alarm_name))
94 engine.say("has activated")
95 engine.runAndWait()
96 return home("Your alarm, " + alarm_name + ", has activated")
97 return home("")
98
99
100def set_read_alarms(reset) -> list:
101 '''
102 If called for the first time, his function uses checks the list
103 of all alarms and organises it into alarms which has passed and
104 ones which are still to come. If called for a subsequent time,
105 this function will check if any of the list of upcoming alarms
106 has activated and so needs to be moved to the list of passed
107 alarms, them implements this.
108
109 Arguments:
110 reset - if passed in as 0 then code is run which is only required
111 on the first time this function is activated.
112 '''
113 global ALARM_NAMES
114 global ALARM_TIMES
115 global ALARM_LIST
116 global EVENTS
117 alarmWork = ""
118 alarmlen = len(ALARM_TIMES)
119 i = 0
120 j = 0
121 while j + i < alarmlen:
122
123 ttime = datetime.datetime.now()
124 datetimeF = time.strptime(ALARM_TIMES[i], '%a %b %d %H:%M:%S %Y')
125 epochTimeAlarm = time.mktime(datetimeF)
126 datetimeF = time.strptime(ALARM_TIMES[i], '%a %b %d %H:%M:%S %Y')
127 epochTimeNow = int(time.time())
128
129 print(ALARM_LIST)
130 if epochTimeAlarm < epochTimeNow - 60 and epochTimeAlarm > 1000:
131 PASSED_ALARM_LIST.append("Alarm expired : " + ALARM_NAMES[i] + " " + ALARM_TIMES[i]) # this
132 if len(PASSED_ALARM_LIST) > 5:
133 PASSED_ALARM_LIST.pop(0)
134 ALARM_LIST.pop(i)
135 ALARM_TIMES.pop(i)
136 ALARM_NAMES.pop(i)
137 j += 1
138 else:
139 if reset == 0:
140 scheduledEvent = s.enterabs(epochTimeAlarm, 1, alarmNotif)
141 EVENTS.append(scheduledEvent)
142
143 i += 1
144
145 s.run(blocking=False)
146 return(EVENTS)
147
148read_alarms()
149
150set_read_alarms(0)
151
152
153@app.route('/')
154def home(alarmMessage="") -> render_template:
155 '''
156 This is the main function of my program. This function is called every
157 time the HTML page is refreshed and calls the set_read_alarms function
158 as well as the news and weather API subs. For the news API, this function
159 uses an external file to test whether there have been any updates to the
160 headlines and puts these as new notifications.
161
162 Arguments:
163 alarmMessage - This variable stores the output message when an alarm is
164 creted or deleted. Some other functions return this function
165 with a custom alarm message.
166 '''
167 global ALARM_NAMES
168 global ALARM_TIMES
169 global ALARM_LIST
170 global EVENTS
171 print(ALARM_LIST)
172 set_read_alarms(1)
173 print(ALARM_LIST)
174 file_name = "OldNews.txt"
175 f = open(file_name, 'r+')
176 old_line = f.read()
177 f.close()
178 newslines = NewsAPI()
179 if not old_line == str(newslines[0]):
180 PASSED_ALARM_LIST.append("News Update : " + str(newslines[0])[2:-2])
181 PASSED_ALARM_LIST.pop(0)
182
183 weatherRes = WeatherAPI()
184
185 s.run(blocking=False)
186
187 return render_template("homepage.html", weatherRes=weatherRes,
188 alarmMessage=alarmMessage, newslines=newslines, ALARM_LIST=ALARM_LIST,
189 PASSED_ALARM_LIST=PASSED_ALARM_LIST)
190
191
192@app.route('/cancelalarm')
193def cancel_alarm():
194 '''
195 This function is called when the user tries to cancel an alarm.
196 If the user enters an invalid alarm index or invalid data type
197 then an error message is displayed, otherwise the alarm of the
198 index entered by the user is cleared from the scheduler and list
199 of upcoming alarms.
200 '''
201 global ALARM_NAMES
202 global ALARM_TIMES
203 global ALARM_LIST
204 global EVENTS
205 file_write = []
206 alarm_name = ""
207 file_name = "Setalarms.txt"
208 try:
209 cancelAlarm = int(request.args.get('cancelIndex'))
210 except:
211 return home("Please enter the number of the alarm you want to cancel")
212
213 s.cancel(EVENTS.pop(cancelAlarm - 1))
214 alarm_name = ALARM_LIST[cancelAlarm - 1]
215 f = open(file_name, "w+")
216 i = 0
217 for line in f:
218 if not str(line) == str(alarm_name):
219 file_write.append(line)
220 i += 1
221
222 for i in range(len(file_write)):
223 f.write(file_write[i] + "\n")
224 f.close
225
226 ALARM_LIST.pop(cancelAlarm - 1)
227 alarmGone = ALARM_NAMES.pop(cancelAlarm - 1)
228 ALARM_TIMES.pop(cancelAlarm - 1)
229 f = open
230 return home("Your alarm, " + str(alarmGone) + ", has been cancelled")
231
232
233@app.route('/setalarm')
234def set_alarm():
235 '''
236 This function is called when the user tries to set an alarm. The alarm
237 data is requested from the HTML textboxes and used as the basis for the
238 alarm name and date stored in the main program. Provided the user enters
239 a valid name and date, the data is added to lists for the alarm names
240 and times and an alarm is scheduled.
241 Also, the externat file containing names and dates of alarms is appended
242 with the new data.
243 '''
244 global ALARM_NAMES
245 global ALARM_TIMES
246 global ALARM_LIST
247 global EVENTS
248 dupe = False
249
250 try:
251 get_name = request.args.get('alarmName')
252 get_date = request.args.get('alarmDate')
253 except:
254 return home("Please enter valid alarm information")
255 #alarm_date_use = get_date
256 try:
257 datetimeF = time.strptime(get_date, '%Y-%m-%dT%H:%M')
258 except:
259 return home("Please enter valid alarm information")
260 epochTime = time.mktime(datetimeF)
261 waitTime = time.ctime(epochTime)
262 for i in range(len(ALARM_NAMES)):
263
264 if str(get_name) == str(ALARM_NAMES[i]) and str(waitTime) == str(ALARM_TIMES[i]): # this
265 dupe = True
266
267 if not dupe:
268 print(ALARM_NAMES, ALARM_TIMES, "~~~~~~~~~~~~~++", get_name)
269 ALARM_NAMES.append(get_name)
270 ALARM_TIMES.append(waitTime)
271 print(ALARM_NAMES, ALARM_TIMES, "~~~~~~~~~~~~~")
272 try:
273 scheduledEvent = s.enterabs(epochTime, 1, alarmNotif)
274 except:
275 print(ALARM_NAMES, ALARM_TIMES, "~~~~~~~~~~~~~##")
276 ALARM_NAMES.pop(-1)
277 ALARM_TIMES.pop(-1)
278 print(ALARM_NAMES, ALARM_TIMES, "~~~~~~~~~~~~~##")
279 return home("Please enter valid alarm information")
280
281 EVENTS.append(scheduledEvent)
282 print(ALARM_NAMES, ALARM_TIMES, "~~~~~~~~~~~~~")
283 ALARM_LIST.append(ALARM_NAMES[-1] + " " + ALARM_TIMES[-1])
284
285 setAlarms = "SetAlarms.txt"
286 f = open(setAlarms, "a+")
287 f.write(ALARM_NAMES[-1] + "\n")
288 f.write(ALARM_TIMES[-1] + "\n")
289 f.close()
290
291 s.run(blocking=False)
292 return home("Your alarm, " + get_name + ", has been set for " + waitTime)
293
294
295def NewsAPI() -> list:
296 '''
297 This functions uses an api key and url stored in the config file
298 to get news data from the api 'newsapi.org'. The most recent news
299 headline is stored in an external file for comparisson to see if
300 there has been a news update.
301 '''
302 APIdata = APIkeys()
303 API = APIdata[0]
304 nurl = APIdata[2]
305
306 newslines = []
307
308 url = (str(nurl) + str(API))
309
310 Nresponse = requests.get(url)
311 response = Nresponse.json()
312
313 newslines = Fnews(response)
314
315 file_name = "OldNews.txt"
316 f = open(file_name, 'w+')
317 f.write(str(newslines[0]))
318 f.close
319
320 return newslines
321
322
323def Fnews(news) -> list:
324 '''
325 This function formats the data collected by the news api to store just the
326 recent headlines and their publish dates and return a list containing this
327 information.
328
329 Arguments:
330 news - the json format (dictionary) of news data to be selected from
331 '''
332 newsStories = []
333 i = 0
334 for item in news['articles']:
335 i += 1
336 hold = []
337 hold.append(item['title'])
338 hold.append(item['publishedAt'])
339 hold[-1] = hold[-1][11:19]
340 newsStories.append(hold)
341 if i >= 8:
342 break
343 return newsStories
344
345
346def WeatherAPI() -> list:
347 '''
348 This functions uses an api key and a url stored in the config file to get
349 weather data from the api 'openweathermap.org'.
350 '''
351 APIdata = APIkeys()
352 API = APIdata[1]
353 nurl = APIdata[3]
354
355 weatherRes = []
356
357 url = (str(nurl) + str(API))
358
359 Wresponse = requests.get(url)
360 response = Wresponse.json()
361
362 weatherRes = Fweather(response)
363
364 return weatherRes
365
366
367def Fweather(weather) -> list:
368 '''
369 This function formats the data collected by the weather api to store just
370 the current temperature (which is converted from kelvin to celcius), the
371 weather description (e.g. cloudy), and the wind speed. A list is returned
372 containing this information.
373
374 Arguments:
375 weather - the json format (dictionary) of weather data to be selected from
376 '''
377 weatherData = []
378 i = 0
379
380 hold = []
381 hold.append(weather['main']['temp'])
382 hold[-1] -= 273.15
383 hold[-1] = round(hold[-1], 1)
384 hold.append(weather['weather'][0]['main'])
385 hold.append(weather['wind']['speed'])
386 weatherData.append(hold)
387
388 return weatherData
389
390
391if __name__ == "__main__":
392 app.run(debug=True)