Skip to content

Commit

Permalink
Merge branch 'v5' into natural-language
Browse files Browse the repository at this point in the history
  • Loading branch information
ColeDCrawford authored May 21, 2024
2 parents e8b6433 + ee1d07d commit ed2a3f6
Show file tree
Hide file tree
Showing 28 changed files with 1,280 additions and 777 deletions.
12 changes: 9 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
Expand All @@ -28,11 +28,17 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install .[test]
- name: Check Python linting (Ruff)
run: ruff check --output-format=github

- name: Check Python formatting (Ruff)
run: ruff format --check

- name: Run unit tests
run: |
pytest
- name: Run Django integration tests
working-directory: ./edtf_django_tests
run: |
Expand Down
22 changes: 22 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: end-of-file-fixer
exclude: "business-facing/layer"
- id: trailing-whitespace
exclude: "business-facing/layer"
- id: check-yaml
exclude: "business-facing/layer"
- id: check-json
exclude: "business-facing/layer"

- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.4
hooks:
# Run the linter, and enable lint fixes
- id: ruff
args: [ --fix ]
# Run the formatter.
- id: ruff-format
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,3 +365,20 @@ Example usage:


Since the `EDTFField` and the `_earliest` and `_latest` field values are set automatically, you may want to make them readonly, or not visible in your model admin.

## To develop
### Setup
- Clone the repository: `git clone https://github.com/ixc/python-edtf.git`
- Set up a virtual environment: `python3 -m venv venv`
- Install the dependencies: `pip install -r dev-requirements.txt`
- Install precommit hooks: `pre-commit install`

### Running tests
- From `python-edtf`, run the unit tests: `pytest`
- From `python-edtf/edtf_django_tests`, run the integration tests: `python manage.py test edtf_integration`

### Linting and formatting
- Check linting: `ruff check --output-format=github --config pyproject.toml`
- Check formatting: `ruff format --check --config pyproject.toml`
- Fix formatting: `ruff format --config pyproject.toml`
- Linting and formatting checks and attempted fixes are also run as precommit hooks if you installed them.
5 changes: 5 additions & 0 deletions dev-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-r requirements.txt # Include all main requirements
django>=4.2,<5.0
pytest
ruff
pre-commit
77 changes: 72 additions & 5 deletions edtf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,73 @@
from edtf.parser.grammar import parse_edtf
from edtf.natlang import text_to_edtf
from edtf.parser.parser_classes import *
from edtf.convert import dt_to_struct_time, struct_time_to_date, \
struct_time_to_datetime, trim_struct_time, struct_time_to_jd, \
jd_to_struct_time, old_specs_to_new_specs_expression
from edtf.parser import (
UA,
Consecutives,
Date,
DateAndTime,
EarlierConsecutives,
EDTFObject,
EDTFParseException,
ExponentialYear,
Interval,
LaterConsecutives,
Level1Interval,
Level2Interval,
Level2Season,
LongYear,
MultipleDates,
OneOfASet,
PartialUncertainOrApproximate,
PartialUnspecified,
Season,
UncertainOrApproximate,
Unspecified,
UnspecifiedIntervalSection,
parse_edtf,
)

from .convert import (
dt_to_struct_time,
jd_to_struct_time,
old_specs_to_new_specs_expression,
struct_time_to_date,
struct_time_to_datetime,
struct_time_to_jd,
trim_struct_time,
)

# public
__all__ = [
"dt_to_struct_time",
"jd_to_struct_time",
"old_specs_to_new_specs_expression",
"struct_time_to_date",
"struct_time_to_datetime",
"struct_time_to_jd",
"trim_struct_time",
"text_to_edtf",
"parse_edtf",
# parser_exceptions
"EDTFParseException",
# parser_classes
"EDTFObject",
"Date",
"DateAndTime",
"Interval",
"UA",
"UncertainOrApproximate",
"UnspecifiedIntervalSection",
"Unspecified",
"Level1Interval",
"LongYear",
"Season",
"PartialUncertainOrApproximate",
"PartialUnspecified",
"Consecutives",
"EarlierConsecutives",
"LaterConsecutives",
"OneOfASet",
"MultipleDates",
"Level2Interval",
"Level2Season",
"ExponentialYear",
]
53 changes: 31 additions & 22 deletions edtf/appsettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

try:
from django.core.exceptions import ImproperlyConfigured

try:
from django.conf import settings
EDTF = getattr(settings, 'EDTF', {})

EDTF = getattr(settings, "EDTF", {})
except ImproperlyConfigured:
EDTF = {}
except ImportError:
EDTF = {}

SEASON_MONTHS_RANGE = EDTF.get('SEASON_MONTHS_RANGE', {
SEASON_MONTHS_RANGE = EDTF.get(
"SEASON_MONTHS_RANGE",
{
# season id: [earliest_month, last_month]
21: [3, 5],
22: [6, 8],
Expand All @@ -20,10 +24,12 @@
# For simplicity, we assume it falls at the end of the year, esp since the
# spec says that sort order goes spring > summer > autumn > winter
24: [12, 12],
}
},
)

SEASON_L2_MONTHS_RANGE = EDTF.get('SEASON_L2_MONTHS_RANGE', {
SEASON_L2_MONTHS_RANGE = EDTF.get(
"SEASON_L2_MONTHS_RANGE",
{
# season id: [earliest_month, last_month]
21: [3, 5],
22: [6, 8],
Expand Down Expand Up @@ -57,28 +63,31 @@
38: [5, 8],
39: [9, 12],
40: [1, 6],
41: [7, 12]
}
41: [7, 12],
},
)

DAY_FIRST = EDTF.get('DAY_FIRST', False) # Americans!
DAY_FIRST = EDTF.get("DAY_FIRST", False) # Americans!

SEASONS = EDTF.get('SEASONS', {
21: "spring",
22: "summer",
23: "autumn",
24: "winter",
})
INVERSE_SEASONS = EDTF.get('INVERSE_SEASONS', {v: k for k, v in SEASONS.items()})
SEASONS = EDTF.get(
"SEASONS",
{
21: "spring",
22: "summer",
23: "autumn",
24: "winter",
},
)
INVERSE_SEASONS = EDTF.get("INVERSE_SEASONS", {v: k for k, v in SEASONS.items()})
# also need to interpret `fall`
INVERSE_SEASONS['fall'] = 23
INVERSE_SEASONS["fall"] = 23

# changing these will break tests
PADDING_DAY_PRECISION = EDTF.get('PADDING_DAY_PRECISION', relativedelta(days=1))
PADDING_MONTH_PRECISION = EDTF.get('PADDING_MONTH_PRECISION', relativedelta(months=1))
PADDING_YEAR_PRECISION = EDTF.get('PADDING_YEAR_PRECISION', relativedelta(years=1))
PADDING_SEASON_PRECISION = EDTF.get('PADDING_SEASON_PRECISION', relativedelta(weeks=12))
MULTIPLIER_IF_UNCERTAIN = EDTF.get('MULTIPLIER_IF_UNCERTAIN', 1.0)
MULTIPLIER_IF_APPROXIMATE = EDTF.get('MULTIPLIER_IF_APPROXIMATE', 1.0)
MULTIPLIER_IF_BOTH = EDTF.get('MULTIPLIER_IF_BOTH', 2.0)
PADDING_DAY_PRECISION = EDTF.get("PADDING_DAY_PRECISION", relativedelta(days=1))
PADDING_MONTH_PRECISION = EDTF.get("PADDING_MONTH_PRECISION", relativedelta(months=1))
PADDING_YEAR_PRECISION = EDTF.get("PADDING_YEAR_PRECISION", relativedelta(years=1))
PADDING_SEASON_PRECISION = EDTF.get("PADDING_SEASON_PRECISION", relativedelta(weeks=12))
MULTIPLIER_IF_UNCERTAIN = EDTF.get("MULTIPLIER_IF_UNCERTAIN", 1.0)
MULTIPLIER_IF_APPROXIMATE = EDTF.get("MULTIPLIER_IF_APPROXIMATE", 1.0)
MULTIPLIER_IF_BOTH = EDTF.get("MULTIPLIER_IF_BOTH", 2.0)
DELTA_IF_UNKNOWN = EDTF.get("DELTA_IF_UNKNOWN", relativedelta(years=10))
18 changes: 8 additions & 10 deletions edtf/convert.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from time import struct_time
from datetime import date, datetime
from time import struct_time

from edtf import jdutil


TIME_EMPTY_TIME = [0, 0, 0] # tm_hour, tm_min, tm_sec
TIME_EMPTY_EXTRAS = [0, 0, -1] # tm_wday, tm_yday, tm_isdst


def old_specs_to_new_specs_expression(expression):
expression = expression.replace("unknown", "")
expression = expression.replace("open", "..")
Expand All @@ -32,16 +32,15 @@ def dt_to_struct_time(dt):
"""
if isinstance(dt, datetime):
return struct_time(
[dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second] +
TIME_EMPTY_EXTRAS
[dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]
+ TIME_EMPTY_EXTRAS
)
elif isinstance(dt, date):
return struct_time(
[dt.year, dt.month, dt.day] + TIME_EMPTY_TIME + TIME_EMPTY_EXTRAS
)
else:
raise NotImplementedError(
"Cannot convert %s to `struct_time`" % type(dt))
raise NotImplementedError(f"Cannot convert {type(dt)} to `struct_time`")


def struct_time_to_date(st):
Expand Down Expand Up @@ -112,12 +111,11 @@ def jd_to_struct_time(jd):
# This conversion can return negative values for items we do not want to be
# negative: month, day, hour, minute, second.
year, month, day, hour, minute, second = _roll_negative_time_fields(
year, month, day, hour, minute, second)

return struct_time(
[year, month, day, hour, minute, second] + TIME_EMPTY_EXTRAS
year, month, day, hour, minute, second
)

return struct_time([year, month, day, hour, minute, second] + TIME_EMPTY_EXTRAS)


def _roll_negative_time_fields(year, month, day, hour, minute, second):
"""
Expand Down
Loading

0 comments on commit ed2a3f6

Please sign in to comment.