Skip to content

Commit fe96fbd

Browse files
authored
Merge pull request #60 from artshumrc/31-is_approximate-is_uncertain
Properly set qualification properties
2 parents d779dea + e99813c commit fe96fbd

File tree

5 files changed

+133
-18
lines changed

5 files changed

+133
-18
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ jobs:
100100

101101
- name: Publish benchmark results
102102
uses: benchmark-action/github-action-benchmark@v1
103-
if: github.event_name != 'pull_request'
103+
if: github.event_name == 'pull_request' && github.repository == 'ixc/python-edtf'
104104
with:
105105
tool: 'pytest'
106106
auto-push: true
@@ -112,6 +112,7 @@ jobs:
112112
summary-always: true
113113

114114
- name: Comment on benchmark results without publishing
115+
if: github.event_name != 'pull_request' || github.repository != 'ixc/python-edtf'
115116
uses: benchmark-action/github-action-benchmark@v1
116117
with:
117118
tool: 'pytest'

README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,51 @@ One can interpret uncertain or approximate dates as 'plus or minus a [level of p
342342

343343
If a date is both uncertain __and__ approximate, the padding is applied twice, i.e. it gets 100% * 2 padding, or 'plus or minus two [levels of precision]'.
344344

345+
### Qualification properties
346+
EDTF objects support properties that provide an overview of how the object is qualified:
347+
- `.is_uncertain (?)`
348+
- `.is_approximate (~)`
349+
- `.is_uncertain_and_approximate (%)`
350+
These properties represent whether the any part of the date object is uncertain, approximate, or uncertain and approximate. For ranges, the properties are true if any part of the range (lower or upper section) is qualified as such. A date is not necessarily uncertain and approximate if it is separately both uncertain and approximate - it must have the "%" qualifier to be considered uncertain and aproximate.
351+
```python
352+
>>> parse_edtf("2006-06-11")
353+
Date: '2006-06-11'
354+
>>> parse_edtf("2006-06-11").is_uncertain
355+
False
356+
>>> parse_edtf("2006-06-11").is_approximate
357+
False
358+
359+
>>> parse_edtf("1984?")
360+
UncertainOrApproximate: '1984?'
361+
>>> parse_edtf("1984?").is_approximate
362+
False
363+
>>> parse_edtf("1984?").is_uncertain
364+
True
365+
>>> parse_edtf("1984?").is_uncertain_and_approximate
366+
False
367+
368+
>>> parse_edtf("1984%").is_uncertain
369+
False
370+
>>> parse_edtf("1984%").is_uncertain_and_approximate
371+
True
372+
373+
>>> parse_edtf("1984~/2004-06")
374+
Level1Interval: '1984~/2004-06'
375+
>>> parse_edtf("1984~/2004-06").is_approximate
376+
True
377+
>>> parse_edtf("1984~/2004-06").is_uncertain
378+
False
379+
380+
>>> parse_edtf("2004?-~06-~04")
381+
PartialUncertainOrApproximate: '2004?-~06-~04'
382+
>>> parse_edtf("2004?-~06-~04").is_approximate
383+
True
384+
>>> parse_edtf("2004?-~06-~04").is_uncertain
385+
True
386+
>>> parse_edtf("2004?-~06-~04").is_uncertain_and_approximate
387+
False
388+
```
389+
345390
### Seasons
346391

347392
Seasons are interpreted as Northern Hemisphere by default. To change this, override the month mapping in `appsettings.py`.

edtf/fields.py

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,12 @@ def __init__(
4848
**kwargs,
4949
):
5050
kwargs["max_length"] = 2000
51-
(
52-
self.natural_text_field,
53-
self.direct_input_field,
54-
self.lower_strict_field,
55-
self.upper_strict_field,
56-
self.lower_fuzzy_field,
57-
self.upper_fuzzy_field,
58-
) = (
59-
natural_text_field,
60-
direct_input_field,
61-
lower_strict_field,
62-
upper_strict_field,
63-
lower_fuzzy_field,
64-
upper_fuzzy_field,
65-
)
51+
self.natural_text_field = natural_text_field
52+
self.direct_input_field = direct_input_field
53+
self.lower_strict_field = lower_strict_field
54+
self.upper_strict_field = upper_strict_field
55+
self.lower_fuzzy_field = lower_fuzzy_field
56+
self.upper_fuzzy_field = upper_fuzzy_field
6657
super().__init__(verbose_name, name, **kwargs)
6758

6859
description = (
@@ -74,6 +65,8 @@ def deconstruct(self):
7465
name, path, args, kwargs = super().deconstruct()
7566
if self.natural_text_field:
7667
kwargs["natural_text_field"] = self.natural_text_field
68+
if self.direct_input_field:
69+
kwargs["direct_input_field"] = self.direct_input_field
7770

7871
for attr in DATE_ATTRS:
7972
field = f"{attr}_field"
@@ -152,7 +145,7 @@ def update_values(self, instance, *args, **kwargs):
152145
):
153146
edtf = parse_edtf(
154147
edtf_string, fail_silently=True
155-
) # potetial ParseException if invalid; should this be raised?
148+
) # potential ParseException if invalid; should this be raised?
156149
else:
157150
edtf = existing_value
158151
else:

edtf/parser/parser_classes.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def apply_delta(op, time_struct, delta):
9191

9292
class EDTFObject:
9393
"""
94-
Object to attact to a parser to become instantiated when the parser
94+
Object to attach to a parser to become instantiated when the parser
9595
completes.
9696
"""
9797

@@ -470,6 +470,11 @@ class UncertainOrApproximate(EDTFObject):
470470
def __init__(self, date, ua):
471471
self.date = date
472472
self.ua = ua
473+
self.is_uncertain = ua.is_uncertain if ua else False
474+
self.is_approximate = ua.is_approximate if ua else False
475+
self.is_uncertain_and_approximate = (
476+
ua.is_uncertain_and_approximate if ua else False
477+
)
473478

474479
def __str__(self):
475480
if self.ua:
@@ -558,6 +563,11 @@ def __init__(
558563
**kwargs,
559564
)
560565
self.ua = ua
566+
self.is_uncertain = ua.is_uncertain if ua else False
567+
self.is_approximate = ua.is_approximate if ua else False
568+
self.is_uncertain_and_approximate = (
569+
ua.is_uncertain_and_approximate if ua else False
570+
)
561571
self.negative = self.year.startswith("-")
562572

563573
def __str__(self):
@@ -709,6 +719,12 @@ def __init__(self, lower=None, upper=None):
709719
self.upper = UnspecifiedIntervalSection(
710720
False, UncertainOrApproximate(**lower)
711721
)
722+
self.is_approximate = self.lower.is_approximate or self.upper.is_approximate
723+
self.is_uncertain = self.lower.is_uncertain or self.upper.is_uncertain
724+
self.is_uncertain_and_approximate = (
725+
self.lower.is_uncertain_and_approximate
726+
or self.upper.is_uncertain_and_approximate
727+
)
712728

713729
def _get_fuzzy_padding(self, lean):
714730
if lean == EARLIEST:
@@ -840,6 +856,27 @@ def __init__(
840856

841857
self.all_ua = all_ua
842858

859+
uas = [
860+
year_ua,
861+
month_ua,
862+
day_ua,
863+
year_month_ua,
864+
month_day_ua,
865+
season_ua,
866+
all_ua,
867+
]
868+
self.is_uncertain = any(
869+
item.is_uncertain for item in uas if hasattr(item, "is_uncertain")
870+
)
871+
self.is_approximate = any(
872+
item.is_approximate for item in uas if hasattr(item, "is_approximate")
873+
)
874+
self.is_uncertain_and_approximate = any(
875+
item.is_uncertain_and_approximate
876+
for item in uas
877+
if hasattr(item, "is_uncertain_and_approximate")
878+
)
879+
843880
def __str__(self):
844881
if self.season_ua:
845882
return f"{self.season}{self.season_ua}"
@@ -1046,6 +1083,12 @@ def __init__(self, lower, upper):
10461083
self.upper = upper[0]
10471084
else:
10481085
self.upper = upper
1086+
self.is_approximate = self.lower.is_approximate or self.upper.is_approximate
1087+
self.is_uncertain = self.lower.is_uncertain or self.upper.is_uncertain
1088+
self.is_uncertain_and_approximate = (
1089+
self.lower.is_uncertain_and_approximate
1090+
or self.upper.is_uncertain_and_approximate
1091+
)
10491092

10501093

10511094
class Level2Season(Season):

edtf/parser/tests.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,25 @@
240240
"2001-29",
241241
)
242242

243+
APPROXIMATE_UNCERTAIN_EXAMPLES = (
244+
# first part of tuple is the input EDTF string, second part is a tuple of booleans:
245+
# uncertain ?, approximate ~, both uncertain and approximate %
246+
("2004", (False, False, False)),
247+
("2006-06-11", (False, False, False)),
248+
("-0999", (False, False, False)),
249+
("1984?", (True, False, False)),
250+
("2004-06-11?", (True, False, False)),
251+
("1984~", (False, True, False)),
252+
("1984%", (False, False, True)),
253+
("1984~/2004-06", (False, True, False)),
254+
("2004-%06", (False, False, True)),
255+
("2004?-~06-~04", (True, True, False)),
256+
("2004?-06-04", (True, False, False)),
257+
("2011-~06-~04", (False, True, False)),
258+
("2004-06-~01/2004-06-~20", (False, True, False)),
259+
("156X~", (False, True, False)),
260+
)
261+
243262
BAD_EXAMPLES = (
244263
# parentheses are not used for group qualification in the 2018 spec
245264
None,
@@ -379,3 +398,17 @@ def test_comparisons():
379398
def test_benchmark_parser(benchmark, test_input):
380399
"""Benchmark parsing of selected EDTF strings."""
381400
benchmark(parse, test_input)
401+
402+
403+
@pytest.mark.parametrize("test_input,expected_tuple", APPROXIMATE_UNCERTAIN_EXAMPLES)
404+
def test_approximate_uncertain(test_input, expected_tuple):
405+
"""Test parsing of EDTF strings and check .is_uncertain, .is_approximate,
406+
and .is_uncertain_and_approximate properties. The expected_tuple should have three
407+
values, the first should be a boolean indicating if the date is uncertain,
408+
the second should be a boolean indicating if the date is approximate, and the
409+
third should be a boolean indicating if the date is both uncertain and approximate."""
410+
result = parse(test_input)
411+
assert isinstance(result, EDTFObject), "Result should be an instance of EDTFObject"
412+
assert result.is_uncertain == expected_tuple[0]
413+
assert result.is_approximate == expected_tuple[1]
414+
assert result.is_uncertain_and_approximate == expected_tuple[2]

0 commit comments

Comments
 (0)