custom timeparser, 130 lines of the most annoying code.
This commit is contained in:
parent
b9ef486448
commit
3e71ac6421
@ -42,12 +42,14 @@ Using websockets for communication allows for two way communication where the se
|
|||||||
Link for example: https://stackoverflow.com/questions/53331127/python-websockets-send-to-client-and-keep-connection-alive
|
Link for example: https://stackoverflow.com/questions/53331127/python-websockets-send-to-client-and-keep-connection-alive
|
||||||
More examples (includes jwt authentication, though this is in node.js, still useful for figuring out how to do this stuff): https://www.linode.com/docs/guides/authenticating-over-websockets-with-jwt/
|
More examples (includes jwt authentication, though this is in node.js, still useful for figuring out how to do this stuff): https://www.linode.com/docs/guides/authenticating-over-websockets-with-jwt/
|
||||||
|
|
||||||
|
|
||||||
## Ideas
|
## Ideas
|
||||||
|
|
||||||
* Dashboard with api call counts (would require linking into all active skills, callbacks with class inheritance maybe?)
|
* Dashboard with api call counts (would require linking into all active skills, callbacks with class inheritance maybe?)
|
||||||
* Phone calls from Jarvis speaker
|
* Phone calls from Jarvis speaker
|
||||||
* JARVIS, initiate the House Party Protocol (takeover screen and show retro style text interface, possibly showing data from dashboard)
|
* JARVIS, initiate the House Party Protocol (takeover screen and show retro style text interface, possibly showing data from dashboard)
|
||||||
|
|
||||||
|
|
||||||
## Wants, but limitations prevent
|
## Wants, but limitations prevent
|
||||||
|
|
||||||
* *tumble weed bounces by* Oh, dear.
|
* *tumble weed bounces by* Oh, dear.
|
@ -10,11 +10,11 @@ import requests
|
|||||||
|
|
||||||
if __name__ == "__main__": # Handle running this script directly vs as a project
|
if __name__ == "__main__": # Handle running this script directly vs as a project
|
||||||
from config import ntfy_url
|
from config import ntfy_url
|
||||||
from utility import parsetime2
|
from utility import parsetime
|
||||||
from skill import Skill
|
from skill import Skill
|
||||||
else:
|
else:
|
||||||
from skills.config import ntfy_url
|
from skills.config import ntfy_url
|
||||||
from skills.utility import parsetime2
|
from skills.utility import parsetime
|
||||||
from skills.skill import Skill
|
from skills.skill import Skill
|
||||||
|
|
||||||
import threading
|
import threading
|
||||||
@ -96,7 +96,7 @@ class Alarms(Skill):
|
|||||||
def run(self, query="", duration_string="", name=""):
|
def run(self, query="", duration_string="", name=""):
|
||||||
if "add" in query:
|
if "add" in query:
|
||||||
# duration = time.mktime(parsetime2(duration_string).timetuple())
|
# duration = time.mktime(parsetime2(duration_string).timetuple())
|
||||||
duration = parsetime2(duration_string)
|
duration = parsetime(duration_string)
|
||||||
self._add_alarm(duration, name)
|
self._add_alarm(duration, name)
|
||||||
return True # Return true to indicate success
|
return True # Return true to indicate success
|
||||||
if "remove" in query:
|
if "remove" in query:
|
||||||
@ -110,6 +110,6 @@ class Alarms(Skill):
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
dur = Alarms()
|
dur = Alarms()
|
||||||
dur.run("add", "15 seconds", "test alarm")
|
dur.run("add", "1 47 in the afternoon", "test alarm")
|
||||||
# dur._add_alarm(123, "123")
|
# dur._add_alarm(123, "123")
|
||||||
# dur._trigger_alarm("123")
|
# dur._trigger_alarm("123")
|
@ -1,4 +1,7 @@
|
|||||||
# Copy & Rename this file to config.py and fill in data
|
# Copy & Rename this file to config.py and fill in data
|
||||||
ntfy_url="" # Obtained from ntfy.sh app (choose a random string of numbers/letters for better security)
|
ntfy_url="" # Obtained from ntfy.sh app (choose a random string of numbers/letters for better security)
|
||||||
deepl_api_key="" # Obtained from https://www.deepl.com/en/docs-api
|
deepl_api_key="" # Obtained from https://www.deepl.com/en/docs-api
|
||||||
google_api_key="" #Obtained from https://cloud.google.com/translate/pricing
|
google_api_key="" #Obtained from https://cloud.google.com/translate/pricing
|
||||||
|
default_morning_time = "08:00" #8am
|
||||||
|
default_afternoon_time = "13:00" #1pm
|
||||||
|
default_evening_time = "18:00" #6pm
|
219
backend/skills/regexTimeParser.py
Normal file
219
backend/skills/regexTimeParser.py
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
"""
|
||||||
|
Test Cases:
|
||||||
|
|
||||||
|
1 in the afternoon
|
||||||
|
147 in the afternoon
|
||||||
|
1223 in the morning
|
||||||
|
1 pm
|
||||||
|
132 pm
|
||||||
|
1426
|
||||||
|
half past 1 in the afternoon
|
||||||
|
quarter past 12 in the afternoon
|
||||||
|
tomorrow at 3 in the afternoon
|
||||||
|
tomorrow morning
|
||||||
|
yesterday morning
|
||||||
|
this afternoon
|
||||||
|
this morning
|
||||||
|
monday at 3 in the afternoon
|
||||||
|
wednesday at 930
|
||||||
|
1030
|
||||||
|
10 after 10 in the evening
|
||||||
|
10 before 10 in the evening
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
Expected Test Case Outputs (run on monday sept 11 at 7:37pm):
|
||||||
|
|
||||||
|
['11-09-2023 13:00', [['1'], [], ['afternoon'], [], [], []]]
|
||||||
|
['11-09-2023 13:47', [['147'], [], ['afternoon'], [], [], []]]
|
||||||
|
['11-09-2023 00:23', [['1223'], [], ['morning'], [], [], []]]
|
||||||
|
['11-09-2023 13:00', [['1'], [], ['pm'], [], [], []]]
|
||||||
|
['11-09-2023 13:32', [['132'], [], ['pm'], [], [], []]]
|
||||||
|
['11-09-2023 14:26', [['1426'], [], [], [], [], []]]
|
||||||
|
['11-09-2023 13:30', [['1'], [], ['afternoon'], ['half past'], [], []]]
|
||||||
|
['11-09-2023 12:15', [['12'], [], ['afternoon'], ['quarter past'], [], []]]
|
||||||
|
['12-09-2023 15:00', [['3'], [], ['afternoon'], [], ['tomorrow'], []]]
|
||||||
|
['12-09-2023 08:00', [[], [], ['morning'], [], ['tomorrow'], []]]
|
||||||
|
['10-09-2023 08:00', [[], [], ['morning'], [], ['yesterday'], []]]
|
||||||
|
['11-09-2023 13:00', [[], [], ['afternoon'], [], ['this'], []]]
|
||||||
|
['11-09-2023 08:00', [[], [], ['morning'], [], ['this'], []]]
|
||||||
|
['11-09-2023 15:00', [['3'], [], ['afternoon'], [], [], ['monday']]]
|
||||||
|
['13-09-2023 09:30', [['930'], [], [], [], [], ['wednesday']]]
|
||||||
|
['11-09-2023 10:30', [['1030'], [], [], [], [], []]]
|
||||||
|
['11-09-2023 10:10', [['10', '10'], ['after'], ['evening'], [], [], []]]
|
||||||
|
['11-09-2023 10:50', [['10', '10'], ['before'], ['evening'], [], [], []]]
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
Regex Breakdown:
|
||||||
|
|
||||||
|
|
||||||
|
([0-9]+)
|
||||||
|
(before|after)
|
||||||
|
(afternoon|morning|pm|am|evening)
|
||||||
|
(half past|quarter past|quarter to)
|
||||||
|
(today|yesterday|tomorrow|tonight|this)
|
||||||
|
(monday|tuesday|wednesday|thursday|friday|saturday|sunday)
|
||||||
|
|
||||||
|
after each word, add \b
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import time
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
from config import default_morning_time, default_afternoon_time, default_evening_time
|
||||||
|
else:
|
||||||
|
from skills.config import default_morning_time, default_afternoon_time, default_evening_time
|
||||||
|
|
||||||
|
|
||||||
|
morning_datetime = datetime.strptime(default_morning_time, "%H:%M")
|
||||||
|
afternoon_datetime = datetime.strptime(default_afternoon_time, "%H:%M")
|
||||||
|
evening_datetime = datetime.strptime(default_evening_time, "%H:%M")
|
||||||
|
|
||||||
|
|
||||||
|
dayofweek_to_number = {
|
||||||
|
"monday": 0,
|
||||||
|
"tuesday": 1,
|
||||||
|
"wednesday": 2,
|
||||||
|
"thursday": 3,
|
||||||
|
"friday": 4,
|
||||||
|
"saturday": 5,
|
||||||
|
"sunday": 6
|
||||||
|
}
|
||||||
|
|
||||||
|
class RegexTimeParser:
|
||||||
|
def __init__(self):
|
||||||
|
self.regex_string = r"([0-9]+)\b|(before\b|after\b)|(afternoon\b|morning\b|pm\b|am\b|evening\b)|(half past\b|quarter past\b||quarter to\b)|(today\b|yesterday\b|tomorrow\b|tonight\b|this\b)|(monday\b|tuesday\b|wednesday\b|thursday\b|friday\b|saturday\b|sunday\b)"
|
||||||
|
# self.regex_string = "([0-9]+)|(before|after)|(afternoon|morning|pm|am|evening)|(half past|quarter past)|(today|yesterday|tomorrow|tonight)|(monday|tuesday|wednesday|thursday|friday|saturday|sunday)"
|
||||||
|
self.regex_exp = re.compile(self.regex_string, re.IGNORECASE)
|
||||||
|
|
||||||
|
def _merge_findall(self, list_of_lists):
|
||||||
|
out = [[], [], [], [], [], []]
|
||||||
|
for pos in range(6):
|
||||||
|
for group in list_of_lists:
|
||||||
|
if group[pos] != '':
|
||||||
|
out[pos].append(group[pos])
|
||||||
|
return out
|
||||||
|
|
||||||
|
def _parse_time_string(self, phrase):
|
||||||
|
"""Takes time string and parses hours/minutes into dict"""
|
||||||
|
hours = 0
|
||||||
|
minutes = 0
|
||||||
|
match len(phrase):
|
||||||
|
case 1:
|
||||||
|
#hour
|
||||||
|
hours = int(phrase)
|
||||||
|
case 2:
|
||||||
|
#hour
|
||||||
|
hours = int(phrase)
|
||||||
|
case 3:
|
||||||
|
#hour first digit, last two digits minutes
|
||||||
|
hours = int(phrase[:1])
|
||||||
|
minutes = int(phrase[1:3])
|
||||||
|
case 4:
|
||||||
|
#hour first two digits, last two digits minutes
|
||||||
|
hours = int(phrase[:2])
|
||||||
|
minutes = int(phrase[2:4])
|
||||||
|
|
||||||
|
return {"hours": hours, "minutes": minutes}
|
||||||
|
|
||||||
|
def parse_time(self, phrase):
|
||||||
|
|
||||||
|
matches = self._merge_findall(self.regex_exp.findall(phrase))
|
||||||
|
date = datetime.now()
|
||||||
|
|
||||||
|
if matches[2]:
|
||||||
|
if "afternoon" in matches[2]:
|
||||||
|
#default afternoon time from config
|
||||||
|
date = date.replace(hour=afternoon_datetime.hour, minute=afternoon_datetime.minute)
|
||||||
|
elif "evening" in matches[2]:
|
||||||
|
#default evening time from config
|
||||||
|
date = date.replace(hour=evening_datetime.hour, minute=evening_datetime.minute)
|
||||||
|
elif "pm" in matches[2]:
|
||||||
|
#default afternoon time from config
|
||||||
|
date = date.replace(hour=afternoon_datetime.hour, minute=afternoon_datetime.minute)
|
||||||
|
elif "morning" in matches[2]:
|
||||||
|
#default morning time from config
|
||||||
|
date = date.replace(hour=morning_datetime.hour, minute=morning_datetime.minute)
|
||||||
|
elif "am" in matches[2]:
|
||||||
|
#default morning time from config
|
||||||
|
date = date.replace(hour=morning_datetime.hour, minute=morning_datetime.minute)
|
||||||
|
|
||||||
|
|
||||||
|
if len(matches[0]) > 1: #uses only first two number groups ([0-9]+)
|
||||||
|
t = self._parse_time_string(matches[0][0])
|
||||||
|
t2 = self._parse_time_string(matches[0][1])
|
||||||
|
if "after" in matches[1]:
|
||||||
|
date = date.replace(hour = t2['hours'], minute = int(matches[0][1]))
|
||||||
|
elif "before" in matches[1]:
|
||||||
|
date = date.replace(hour = t['hours'], minute = 60-int(matches[0][0]))
|
||||||
|
|
||||||
|
elif len(matches[0]) > 0: #([0-9]+)
|
||||||
|
t = self._parse_time_string(matches[0][0])
|
||||||
|
if "afternoon" in matches[2] or "evening" in matches[2] or "pm" in matches[2]:
|
||||||
|
if t["hours"] < 12:
|
||||||
|
t["hours"] = t["hours"]+12
|
||||||
|
elif "morning" in matches[2] or "am" in matches[2]:
|
||||||
|
if t["hours"] >= 12:
|
||||||
|
t["hours"] = t["hours"]-12
|
||||||
|
date = date.replace(hour = t["hours"], minute = t["minutes"])
|
||||||
|
|
||||||
|
|
||||||
|
if matches[3]: #(half past|quarter past|quarter to)
|
||||||
|
match matches[3][0]:
|
||||||
|
case "quarter past":
|
||||||
|
date = date.replace(minute = 15)
|
||||||
|
case "half past":
|
||||||
|
date = date.replace(minute = 30)
|
||||||
|
case "quarter to":
|
||||||
|
date = date.replace(minute = 45)
|
||||||
|
|
||||||
|
|
||||||
|
if matches[4]: #(today|yesterday|tomorrow|tonight|this)
|
||||||
|
match matches[4][0]:
|
||||||
|
case "yesterday":
|
||||||
|
yesterday_date = datetime.now() - timedelta(days = 1)
|
||||||
|
# print(yesterday_date)
|
||||||
|
# print(timedelta(1))
|
||||||
|
# print(datetime.now())
|
||||||
|
date = date.replace(day=yesterday_date.day, month=yesterday_date.month, year=yesterday_date.year)
|
||||||
|
case "tomorrow":
|
||||||
|
tomorrow_date = datetime.now() + timedelta(1)
|
||||||
|
date = date.replace(day=tomorrow_date.day, month=tomorrow_date.month, year=tomorrow_date.year)
|
||||||
|
|
||||||
|
if matches[5]: #(monday|tuesday|wednesday|thursday|friday|saturday|sunday)
|
||||||
|
date = date + timedelta(days = (dayofweek_to_number[matches[5][0]] - date.weekday() + 7) % 7)
|
||||||
|
|
||||||
|
return [date.strftime("%d-%m-%Y %H:%M"), matches]
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test = RegexTimeParser()
|
||||||
|
|
||||||
|
test_phrases = [
|
||||||
|
"1 in the afternoon", #done
|
||||||
|
"147 in the afternoon", #done
|
||||||
|
"1223 in the morning", #done
|
||||||
|
"1 pm", #done
|
||||||
|
"132 pm", #done
|
||||||
|
"1426", #done
|
||||||
|
"half past 1 in the afternoon", #done
|
||||||
|
"quarter past 12 in the afternoon", #done
|
||||||
|
"tomorrow at 3 in the afternoon", #done
|
||||||
|
"tomorrow morning", #done
|
||||||
|
"yesterday morning", #done
|
||||||
|
"this afternoon", #done
|
||||||
|
"this morning", #done
|
||||||
|
"monday at 3 in the afternoon", #done
|
||||||
|
"wednesday at 930", #done
|
||||||
|
"1030", #done
|
||||||
|
"10 after 10 in the evening", #done
|
||||||
|
"10 before 10 in the evening" #done
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
for ph in test_phrases:
|
||||||
|
print(test.parse_time(ph))
|
@ -8,6 +8,12 @@ Reading material for this:
|
|||||||
|
|
||||||
https://www.deepl.com/en/docs-api
|
https://www.deepl.com/en/docs-api
|
||||||
https://cloud.google.com/translate/docs/overview
|
https://cloud.google.com/translate/docs/overview
|
||||||
|
|
||||||
|
offline local:
|
||||||
|
https://github.com/argosopentech/argos-translate
|
||||||
|
|
||||||
|
use deep-translator to access both deepl and google translate and more
|
||||||
|
https://github.com/nidhaloff/deep-translator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class Translations(Skill):
|
class Translations(Skill):
|
||||||
|
@ -1,9 +1,23 @@
|
|||||||
from ctparse import ctparse #Used for parsing time (parsetime), https://github.com/comtravo/ctparse
|
from ctparse import ctparse #Used for parsing time (parsetime), https://github.com/comtravo/ctparse
|
||||||
import parsedatetime #Used for parsing time (parsetime2), https://github.com/bear/parsedatetime
|
import parsedatetime #Used for parsing time (parsetime2), https://github.com/bear/parsedatetime
|
||||||
|
import HumanTime
|
||||||
|
import arrow
|
||||||
|
import natural_time
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
"""
|
||||||
|
Reading Material:
|
||||||
|
|
||||||
|
https://github.com/nltk/nltk_contrib/blob/95d1806e2f4e89e960b76a685b1fba2eaa7d5142/nltk_contrib/timex.py#L29
|
||||||
|
This has a bunch of regex expressions for NLP of time, might be able to modify this + parsedatetime + ctparse into my
|
||||||
|
own custom implementation that does everything I want. Maybe if it works well enough I can post it to pypi as its own
|
||||||
|
module and github for contributions.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def parsetime(phrase):
|
def parsetime(phrase):
|
||||||
"""
|
"""
|
||||||
Takes in natrual language time phrase, outputs datetime object
|
Takes in natrual language time phrase, outputs datetime object
|
||||||
@ -28,12 +42,40 @@ def parsetime2(phrase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
time_struct, parse_status = parsedatetime.Calendar().parse(phrase)
|
time_struct, parse_status = parsedatetime.Calendar().parse(phrase)
|
||||||
print(time_struct, parse_status)
|
# print(time_struct, parse_status)
|
||||||
return datetime(*time_struct[:6])
|
return datetime(*time_struct[:6])
|
||||||
|
|
||||||
|
|
||||||
|
def parsetime3(phrase):
|
||||||
|
return HumanTime.parseTime(phrase)
|
||||||
|
|
||||||
|
def parsetime4(phrase):
|
||||||
|
return arrow.utcnow().dehumanize(phrase)
|
||||||
|
|
||||||
|
def parsetime5(phrase):
|
||||||
|
return natural_time.natural_time(phrase)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
t = parsetime('May 5th in the afternoon')
|
# t = parsetime('May 5th in the afternoon')
|
||||||
print(t)
|
# print(t)
|
||||||
|
|
||||||
|
# t55 = parsetime('set an alarm for 2:30 in the afternoon')
|
||||||
|
# print(t55)
|
||||||
|
|
||||||
|
# t66 = parsetime('147 in the afternoon')
|
||||||
|
# print(t66)
|
||||||
|
# t67 = parsetime2('147 in the afternoon')
|
||||||
|
# print(t67)
|
||||||
|
# t68 = parsetime2('147 after noon')
|
||||||
|
# print(t68)
|
||||||
|
t88 = parsetime('one forty seven in the afternoon')
|
||||||
|
print(t88)
|
||||||
|
# t89 = parsetime5('147 in the afternoon')
|
||||||
|
# print(t89)
|
||||||
|
# t90 = parsetime5('147 after noon')
|
||||||
|
# print(t90)
|
||||||
|
# print(time.mktime(t67.timetuple()))
|
||||||
|
|
||||||
# t5 = parsetime('in 5 minutes 30 seconds')
|
# t5 = parsetime('in 5 minutes 30 seconds')
|
||||||
# print(t5)
|
# print(t5)
|
||||||
@ -45,15 +87,15 @@ if __name__ == "__main__":
|
|||||||
# print(t2.resolution)
|
# print(t2.resolution)
|
||||||
|
|
||||||
|
|
||||||
t2 = parsetime2('now')
|
# t2 = parsetime2('now')
|
||||||
print(time.mktime(t2.timetuple()))
|
# print(time.mktime(t2.timetuple()))
|
||||||
|
|
||||||
t3 = parsetime2('in 5 minutes 30 seconds')
|
# t3 = parsetime2('in 5 minutes 30 seconds')
|
||||||
print(time.mktime(t3.timetuple()))
|
# print(time.mktime(t3.timetuple()))
|
||||||
|
|
||||||
t4 = parsetime2('4 in the afternoon')
|
# t4 = parsetime2('4 in the afternoon')
|
||||||
print(time.mktime(t4.timetuple()))
|
# print(time.mktime(t4.timetuple()))
|
||||||
print(t4)
|
# print(t4)
|
||||||
|
|
||||||
# print(time.strftime("%H:%M:%S", t3.timetuple()))
|
# print(time.strftime("%H:%M:%S", t3.timetuple()))
|
||||||
# for x in t:
|
# for x in t:
|
||||||
|
Loading…
Reference in New Issue
Block a user