1
+ import re
1
2
import pprint
2
3
import json
3
4
from datetime import datetime
5
+ from datetime import timedelta
4
6
import caldav
5
7
import urllib3
6
8
from caldav .elements import dav , cdav
9
+ from calendar import monthrange
7
10
from ics import Calendar
8
11
from flask import Flask
9
- from flask_ask import Ask , statement
10
- from datetime import timedelta
12
+ from flask_ask import Ask , statement , request
11
13
12
14
app = Flask (__name__ )
13
15
ask = Ask (app , '/' )
@@ -33,6 +35,18 @@ def connectCalendar():
33
35
34
36
return sorted (calendars ,key = lambda calendar : str (calendar .url ))
35
37
38
+ # split away TRIGGER and VALARM fields, since caldav library does not always parse them correctly
39
+ def filterEventTriggers (events ):
40
+ for r in events :
41
+ ev = "" ;
42
+ ev_data = str (r ._data ).splitlines ()
43
+ for line in ev_data :
44
+ if not line .lstrip ().startswith ("TRIGGER" ) and ":VALARM" not in line :
45
+ ev += (line + '\n ' )
46
+ r ._data = ev
47
+
48
+ return events
49
+
36
50
def getCalDavEvents (begin , end ):
37
51
calendars = connectCalendar ()
38
52
speech_text = ""
@@ -52,22 +66,24 @@ def getCalDavEvents(begin, end):
52
66
53
67
log (" -> " + str (len (results )) + " Termine \n " )
54
68
if len (results ) > 0 :
69
+ results = filterEventTriggers (results )
55
70
eventList = eventList + flatten ([Calendar (event ._data ).events for event in results ])
56
71
i = i + 1
57
72
58
73
if (len (eventList ) <= 0 ):
59
74
speech_text = "Es sind keine Termine eingetragen"
60
75
else :
76
+ log (" returning " + str (len (eventList )) + " event(s)\n " )
61
77
sortedEventList = sorted (eventList ,key = lambda icsEvent : icsEvent .begin )
62
78
63
79
# pp = pprint.PrettyPrinter(indent=4)
64
80
# pp.pprint(sortedEventList)
65
81
66
- speech_text = " <speak>\n "
67
- speech_text += ' Es sind folgende Termine auf dem Kalender:\n '
82
+ speech_text += ' <speak>\n '
83
+ speech_text += ' Es sind folgende Termine auf dem Kalender:\n '
68
84
for icsEvent in sortedEventList :
69
- speech_text += ' <break time="1s"/> ' + icsEvent .begin .humanize (locale = 'de' ) + " ist " + icsEvent . name + '. \n '
70
- speech_text += " </speak>"
85
+ speech_text += ' <break time="1s"/>' + icsEvent .begin .humanize (locale = 'de' ) + ' ist ' + getEventName ( icsEvent ) + '\n '
86
+ speech_text += ' </speak>'
71
87
72
88
return speech_text
73
89
@@ -86,10 +102,10 @@ def getDateEvents(date, enddate):
86
102
# in case that default "enddate" does not comply to "date",
87
103
# the enddate is set to end of the day of "date"
88
104
if date == None :
89
- date = datetime .now ()
105
+ date = datetime .now ()
90
106
91
107
if enddate == None or date >= enddate :
92
- enddate = datetime (date . year , date . month , date .day + 1 )
108
+ enddate = getEndDate (date , request . intent . slots . date .value )
93
109
94
110
log (" date: " + str (date ) + "\n " )
95
111
log (" endDate: " + str (enddate ) + "\n " )
@@ -99,6 +115,34 @@ def getDateEvents(date, enddate):
99
115
100
116
return statement (speech_text ).simple_card ('Kalendertermine' , speech_text )
101
117
118
+ def getEndDate (date , orig_date ):
119
+ log (" orig_date: " + str (orig_date ) + " " + str (type (orig_date )) + "\n " )
120
+ orig_date = re .sub ('X$' , '0' , orig_date )
121
+ if re .match ('^\d{4}-W\d{2}$' , orig_date ):
122
+ # this week
123
+ endDate = date + timedelta (days = 7 )
124
+ elif re .match ('\d{4}-W\d{2}-WE$' , orig_date ):
125
+ # this weekend
126
+ endDate = date + timedelta (days = 2 )
127
+ elif re .match ('^\d{4}-\d{2}$' , orig_date ):
128
+ # this month
129
+ last_day = monthRange (date .year , date .month )
130
+ endDate = dateTime .date (date .year , date .month , last_day )
131
+ elif re .match ('^\d{4}$' , orig_date ):
132
+ # this/next year
133
+ endDate = dateTime .date (date .year , 12 , 31 )
134
+ else :
135
+ endDate = datetime (date .year , date .month , date .day + 1 )
136
+
137
+ return endDate
138
+
139
+ def getEventName (event ):
140
+ # see: https://stackoverflow.com/que stions/40135637/error-unable-to-parse-the-provided-ssml-the-provided-text-is-not-valid-ssml
141
+ name = event .name
142
+ name = name .replace ('&' , ' und ' )
143
+ name = name .replace ('*' , '' )
144
+ return name
145
+
102
146
# We do have a minor problem here. There is no timezone information in the date/time objects...
103
147
# ... we assume the server's timezone, but it could be that this is wrong. So if created events are off by some hour(s)
104
148
# this is the reason. If someone wants to provide a simple PR then this would be great :-)
@@ -150,7 +194,7 @@ def setEvent(date, time, duration, eventtype, location):
150
194
log ("ERROR: " + speech_text + "\n " )
151
195
else :
152
196
# This could be sooo much easier if we had something like "if (calendar.isReadOnly())"
153
- i = 0 ;
197
+ i = 0
154
198
log (" gefundene Kalender: #" + str (len (calendars )) + "\n " )
155
199
for calendar in calendars :
156
200
log (" [" + str (i + 1 ) + "]: " + str (calendar ) + "\n " )
0 commit comments