Skip to content

Commit 7809974

Browse files
tracyboehrerVirtual-Josh
authored andcommitted
Added 40.timex resolution (#430)
* Unfinished push until recognizers-text is updated. * Added 40.timex-resolution
1 parent f34d85a commit 7809974

File tree

9 files changed

+341
-0
lines changed

9 files changed

+341
-0
lines changed

samples/40.timex-resolution/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Timex Resolution
2+
3+
This sample shows how to use TIMEX expressions.
4+
5+
## Concepts introduced in this sample
6+
7+
### What is a TIMEX expression?
8+
9+
A TIMEX expression is an alpha-numeric expression derived in outline from the standard date-time representation ISO 8601.
10+
The interesting thing about TIMEX expressions is that they can represent various degrees of ambiguity in the date parts. For example, May 29th, is not a
11+
full calendar date because we haven't said which May 29th - it could be this year, last year, any year in fact.
12+
TIMEX has other features such as the ability to represent ranges, date ranges, time ranges and even date-time ranges.
13+
14+
### Where do TIMEX expressions come from?
15+
16+
TIMEX expressions are produced as part of the output of running a DateTimeRecognizer against some natural language input. As the same
17+
Recognizers are run in LUIS the result returned in the JSON from a call to LUIS also contains the TIMEX expressions.
18+
19+
### What can the library do?
20+
21+
It turns out that TIMEX expressions are not that simple to work with in code. This library attempts to address that. One helpful way to
22+
think about a TIMEX expression is as a partially filled property bag. The properties might be such things as "day of week" or "year."
23+
Basically the more properties we have captured in the expression the less ambiguity we have.
24+
25+
The library can do various things:
26+
27+
- Parse TIMEX expressions to give you the properties contained there in.
28+
- Generate TIMEX expressions based on setting raw properties.
29+
- Generate natural language from the TIMEX expression. (This is logically the reverse of the Recognizer.)
30+
- Resolve TIMEX expressions to produce example date-times. (This produces the same result as the Recognizer (and therefore LUIS)).
31+
- Evaluate TIMEX expressions against constraints such that new more precise TIMEX expressions are produced.
32+
33+
### Where is the source code?
34+
35+
The TIMEX expression library is contained in the same GitHub repo as the recognizers. Refer to the further reading section below.
36+
37+
## Running the sample
38+
- Clone the repository
39+
```bash
40+
git clone https://github.com/Microsoft/botbuilder-python.git
41+
```
42+
- Activate your desired virtual environment
43+
- Bring up a terminal, navigate to `botbuilder-python\samples\40.timex-resolution` folder
44+
- In the terminal, type `pip install -r requirements.txt`
45+
- In the terminal, type `python main.py`
46+
47+
## Further reading
48+
49+
- [TIMEX](https://en.wikipedia.org/wiki/TimeML#TIMEX3)
50+
- [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601)
51+
- [Recognizers Text](https://github.com/Microsoft/recognizers-text)
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from recognizers_date_time import recognize_datetime, Culture
5+
6+
7+
class Ambiguity:
8+
"""
9+
TIMEX expressions are designed to represent ambiguous rather than definite dates. For
10+
example: "Monday" could be any Monday ever. "May 5th" could be any one of the possible May
11+
5th in the past or the future. TIMEX does not represent ambiguous times. So if the natural
12+
language mentioned 4 o'clock it could be either 4AM or 4PM. For that the recognizer (and by
13+
extension LUIS) would return two TIMEX expressions. A TIMEX expression can include a date and
14+
time parts. So ambiguity of date can be combined with multiple results. Code that deals with
15+
TIMEX expressions is frequently dealing with sets of TIMEX expressions.
16+
"""
17+
18+
@staticmethod
19+
def date_ambiguity():
20+
# Run the recognizer.
21+
results = recognize_datetime(
22+
"Either Saturday or Sunday would work.", Culture.English
23+
)
24+
25+
# We should find two results in this example.
26+
for result in results:
27+
# The resolution includes two example values: going backwards and forwards from NOW in the calendar.
28+
# Each result includes a TIMEX expression that captures the inherent date but not time ambiguity.
29+
# We are interested in the distinct set of TIMEX expressions.
30+
# There is also either a "value" property on each value or "start" and "end".
31+
distinct_timex_expressions = {
32+
value["timex"]
33+
for value in result.resolution["values"]
34+
if "timex" in value
35+
}
36+
print(f"{result.text} ({','.join(distinct_timex_expressions)})")
37+
38+
@staticmethod
39+
def time_ambiguity():
40+
# Run the recognizer.
41+
results = recognize_datetime(
42+
"We would like to arrive at 4 o'clock or 5 o'clock.", Culture.English
43+
)
44+
45+
# We should find two results in this example.
46+
for result in results:
47+
# The resolution includes two example values: one for AM and one for PM.
48+
# Each result includes a TIMEX expression that captures the inherent date but not time ambiguity.
49+
# We are interested in the distinct set of TIMEX expressions.
50+
distinct_timex_expressions = {
51+
value["timex"]
52+
for value in result.resolution["values"]
53+
if "timex" in value
54+
}
55+
56+
# TIMEX expressions don't capture time ambiguity so there will be two distinct expressions for each result.
57+
print(f"{result.text} ({','.join(distinct_timex_expressions)})")
58+
59+
@staticmethod
60+
def date_time_ambiguity():
61+
# Run the recognizer.
62+
results = recognize_datetime(
63+
"It will be ready Wednesday at 5 o'clock.", Culture.English
64+
)
65+
66+
# We should find a single result in this example.
67+
for result in results:
68+
# The resolution includes four example values: backwards and forward in the calendar and then AM and PM.
69+
# Each result includes a TIMEX expression that captures the inherent date but not time ambiguity.
70+
# We are interested in the distinct set of TIMEX expressions.
71+
distinct_timex_expressions = {
72+
value["timex"]
73+
for value in result.resolution["values"]
74+
if "timex" in value
75+
}
76+
77+
# TIMEX expressions don't capture time ambiguity so there will be two distinct expressions for each result.
78+
print(f"{result.text} ({','.join(distinct_timex_expressions)})")
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import datetime
5+
6+
from datatypes_timex_expression import TimexRangeResolver, TimexCreator
7+
8+
9+
class Constraints:
10+
"""
11+
The TimexRangeResolved can be used in application logic to apply constraints to a set of TIMEX expressions.
12+
The constraints themselves are TIMEX expressions. This is designed to appear a little like a database join,
13+
of course its a little less generic than that because dates can be complicated things.
14+
"""
15+
16+
@staticmethod
17+
def examples():
18+
"""
19+
When you give the recognizer the text "Wednesday 4 o'clock" you get these distinct TIMEX values back.
20+
But our bot logic knows that whatever the user says it should be evaluated against the constraints of
21+
a week from today with respect to the date part and in the evening with respect to the time part.
22+
"""
23+
24+
resolutions = TimexRangeResolver.evaluate(
25+
["XXXX-WXX-3T04", "XXXX-WXX-3T16"],
26+
[TimexCreator.week_from_today(), TimexCreator.EVENING],
27+
)
28+
29+
today = datetime.datetime.now()
30+
for resolution in resolutions:
31+
print(resolution.to_natural_language(today))
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import datetime
5+
6+
from datatypes_timex_expression import Timex
7+
8+
9+
class LanguageGeneration:
10+
"""
11+
This language generation capabilities are the logical opposite of what the recognizer does.
12+
As an experiment try feeding the result of language generation back into a recognizer.
13+
You should get back the same TIMEX expression in the result.
14+
"""
15+
16+
@staticmethod
17+
def examples():
18+
LanguageGeneration.__describe(Timex("2019-05-29"))
19+
LanguageGeneration.__describe(Timex("XXXX-WXX-6"))
20+
LanguageGeneration.__describe(Timex("XXXX-WXX-6T16"))
21+
LanguageGeneration.__describe(Timex("T12"))
22+
23+
LanguageGeneration.__describe(Timex.from_date(datetime.datetime.now()))
24+
LanguageGeneration.__describe(
25+
Timex.from_date(datetime.datetime.now() + datetime.timedelta(days=1))
26+
)
27+
28+
@staticmethod
29+
def __describe(timex: Timex):
30+
# Note natural language is often relative, for example the sentence "Yesterday all my troubles seemed so far
31+
# away." Having your bot say something like "next Wednesday" in a response can make it sound more natural.
32+
reference_date = datetime.datetime.now()
33+
print(f"{timex.timex_value()} : {timex.to_natural_language(reference_date)}")

samples/40.timex-resolution/main.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from ambiguity import Ambiguity
5+
from constraints import Constraints
6+
from language_generation import LanguageGeneration
7+
from parsing import Parsing
8+
from ranges import Ranges
9+
from resolution import Resolution
10+
11+
if __name__ == "__main__":
12+
# Creating TIMEX expressions from natural language using the Recognizer package.
13+
Ambiguity.date_ambiguity()
14+
Ambiguity.time_ambiguity()
15+
Ambiguity.date_time_ambiguity()
16+
Ranges.date_range()
17+
Ranges.time_range()
18+
19+
# Manipulating TIMEX expressions in code using the TIMEX Datatype package.
20+
Parsing.examples()
21+
LanguageGeneration.examples()
22+
Resolution.examples()
23+
Constraints.examples()
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from datatypes_timex_expression import Timex, Constants
5+
6+
7+
class Parsing:
8+
"""
9+
The Timex class takes a TIMEX expression as a string argument in its constructor.
10+
This pulls all the component parts of the expression into properties on this object. You can
11+
then manipulate the TIMEX expression via those properties.
12+
The "types" property infers a datetimeV2 type from the underlying set of properties.
13+
If you take a TIMEX with date components and add time components you add the
14+
inferred type datetime (its still a date).
15+
Logic can be written against the inferred type, perhaps to have the bot ask the user for
16+
disambiguation.
17+
"""
18+
19+
@staticmethod
20+
def __describe(timex_pattern: str):
21+
timex = Timex(timex_pattern)
22+
23+
print(timex.timex_value(), end=" ")
24+
25+
if Constants.TIMEX_TYPES_DATE in timex.types:
26+
if Constants.TIMEX_TYPES_DEFINITE in timex.types:
27+
print("We have a definite calendar date.", end=" ")
28+
else:
29+
print("We have a date but there is some ambiguity.", end=" ")
30+
31+
if Constants.TIMEX_TYPES_TIME in timex.types:
32+
print("We have a time.")
33+
else:
34+
print("")
35+
36+
@staticmethod
37+
def examples():
38+
"""
39+
Print information an various TimeX expressions.
40+
:return: None
41+
"""
42+
Parsing.__describe("2017-05-29")
43+
Parsing.__describe("XXXX-WXX-6")
44+
Parsing.__describe("XXXX-WXX-6T16")
45+
Parsing.__describe("T12")

samples/40.timex-resolution/ranges.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
from recognizers_date_time import recognize_datetime
5+
from recognizers_text import Culture
6+
7+
8+
class Ranges:
9+
"""
10+
TIMEX expressions can represent date and time ranges. Here are a couple of examples.
11+
"""
12+
13+
@staticmethod
14+
def date_range():
15+
# Run the recognizer.
16+
results = recognize_datetime(
17+
"Some time in the next two weeks.", Culture.English
18+
)
19+
20+
# We should find a single result in this example.
21+
for result in results:
22+
# The resolution includes a single value because there is no ambiguity.
23+
# We are interested in the distinct set of TIMEX expressions.
24+
distinct_timex_expressions = {
25+
value["timex"]
26+
for value in result.resolution["values"]
27+
if "timex" in value
28+
}
29+
30+
# The TIMEX expression can also capture the notion of range.
31+
print(f"{result.text} ({','.join(distinct_timex_expressions)})")
32+
33+
@staticmethod
34+
def time_range():
35+
# Run the recognizer.
36+
results = recognize_datetime(
37+
"Some time between 6pm and 6:30pm.", Culture.English
38+
)
39+
40+
# We should find a single result in this example.
41+
for result in results:
42+
# The resolution includes a single value because there is no ambiguity.
43+
# We are interested in the distinct set of TIMEX expressions.
44+
distinct_timex_expressions = {
45+
value["timex"]
46+
for value in result.resolution["values"]
47+
if "timex" in value
48+
}
49+
50+
# The TIMEX expression can also capture the notion of range.
51+
print(f"{result.text} ({','.join(distinct_timex_expressions)})")
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
recognizers-text>=1.0.2a2
2+
datatypes-timex-expression>=1.0.2a2
3+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import datetime
5+
6+
from datatypes_timex_expression import TimexResolver
7+
8+
9+
class Resolution:
10+
"""
11+
Given the TIMEX expressions it is easy to create the computed example values that the recognizer gives.
12+
"""
13+
14+
@staticmethod
15+
def examples():
16+
# When you give the recognizer the text "Wednesday 4 o'clock" you get these distinct TIMEX values back.
17+
18+
today = datetime.datetime.now()
19+
resolution = TimexResolver.resolve(["XXXX-WXX-3T04", "XXXX-WXX-3T16"], today)
20+
21+
print(f"Resolution Values: {len(resolution.values)}")
22+
23+
for value in resolution.values:
24+
print(value.timex)
25+
print(value.type)
26+
print(value.value)

0 commit comments

Comments
 (0)