Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test_helper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,19 +197,19 @@ jobs:
- name: dcpy main pytests
run: |
docker exec -u 0 de \
python3 -m pytest dcpy/test --ignore dcpy/test/library -svv --cov-config=pyproject.toml --cov=dcpy --cov-report=xml
python3 -m pytest dcpy/test --ignore dcpy/test/library -svv --cov-config=pyproject.toml --cov=dcpy --cov-report=term --cov-report=xml

# separate because of issues w/ gdal and pyarrow
# errors occur when writing/reading parquet files after importing gdal
- name: dcpy library pytests
run: |
docker exec -u 0 de \
python3 -m pytest dcpy/test/library -svv --cov-config=pyproject.toml --cov=dcpy --cov-report=xml --cov-append
python3 -m pytest dcpy/test/library -svv --cov-config=pyproject.toml --cov=dcpy --cov-report=term --cov-report=xml --cov-append

- name: dcpy integration pytests
run: |
docker exec -u 0 de \
python3 -m pytest dcpy/test_integration -svv --cov-config=pyproject.toml --cov=dcpy --cov-report=xml --cov-append
python3 -m pytest dcpy/test_integration -svv --cov-config=pyproject.toml --cov=dcpy --cov-report=term --cov-report=xml --cov-append

- name: Upload dcpy test coverage to Codecov
uses: codecov/codecov-action@v4
Expand Down
32 changes: 32 additions & 0 deletions dcpy/test/utils/test_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ def test_parsing_valid_versions(self):
format=versions.DateVersionFormat.quarter,
),
],
[
"FY2020",
versions.Date(
date=date(2020, 1, 1),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a calendar year?

Copy link
Member Author

@damonmcc damonmcc Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup all the DateVersionFormat formats are declared this way. like 31 is what makes this a fiscal_year version

Since the actual start of the city's fiscal year doesn't factor into how we version our data products, I went with January 1st (no extra logic for) for now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, sorry, I spaced a little bit. (read too fast, and thought this was a date default, rather than a test case). All good.

format=versions.DateVersionFormat.fiscal_year,
),
],
[
"26prelim",
versions.CapitalBudget(
Expand Down Expand Up @@ -62,6 +69,8 @@ def test_parsing_invalid_version(self):
versions.parse("23v")
with self.assertRaises(Exception):
versions.parse("2v12")
with self.assertRaises(Exception):
versions.parse("FY26")
with self.assertRaises(Exception):
versions.parse("20231212")
with self.assertRaises(Exception):
Expand Down Expand Up @@ -158,6 +167,7 @@ def test_sort_invalid_versions(self):
def test_is_newer_valid_versions(self):
for version_1, version_2, bool_expected in [
["23v2", "22v3.4", True],
["FY2019", "FY2020", False],
["23Q1", "23Q2", False],
["23v2.0.1", "23v2", True],
["23Q1.1", "23Q1", True],
Expand All @@ -182,6 +192,7 @@ def test_bumping_versions(self):
["major", None, "23v2.1", "23v3"],
["minor", None, "23v2", "23v2.1"],
["minor", None, "23v2.1", "23v2.2"],
[None, 1, "FY2020", "FY2021"],
[None, 1, "23Q1", "23Q2"],
[None, 2, "23Q4", "24Q2"],
[None, 7, "23Q2", "25Q1"],
Expand All @@ -206,6 +217,27 @@ def test_bumping_versions(self):
]:
self.assertEqual(v_expected, versions.bump(v, bumped_part, bump_by).label)

def test_bumping_versions_errors(self):
with self.assertRaises(NotImplementedError):
versions.bump("2023-01-01")

with self.assertRaises(ValueError):
versions.bump(versions.Date(date(2023, 1, 1), format="unsupported"))

with self.assertRaises(ValueError):
versions.bump(
versions.Date(
date(2023, 1, 1), format=versions.DateVersionFormat.fiscal_year
),
bump_type=versions.VersionSubType.major,
)

class UnsupportedVersion:
pass

with self.assertRaises(ValueError):
versions.bump(UnsupportedVersion())

def test_group_versions_by_base(self):
for version, versions_list, expected_output in [
[
Expand Down
20 changes: 19 additions & 1 deletion dcpy/utils/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@


class DateVersionFormat(StrEnum):
fiscal_year = "Fiscal Year"
quarter = "Quarter"
month = "Month"
date = "Date"
Expand Down Expand Up @@ -159,6 +160,11 @@ class Date(Version):
@property
def label(self) -> str:
match self.format:
case DateVersionFormat.fiscal_year:
if self.patch == 0:
return f"FY{self.date.strftime('%Y')}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remind me what the label is used for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like it's used in

  • connectors/edm/publishing.py when promoting a build to a draft
  • lifecycle/builds/plan.py to resolve previous and current versions when planning a build

else:
return f"FY{self.date.strftime('%Y')}.{self.patch}"
case DateVersionFormat.quarter:
if self.patch == 0:
return (
Expand All @@ -176,6 +182,8 @@ def label(self) -> str:
return self.date.strftime("%Y-%m-%d")
else:
return f"{self.date.strftime('%Y-%m-%d')}.{self.patch}"
case _:
raise ValueError(f"Unsupported DateVersionFormat '{self.format}'")

def __lt__(self, other) -> bool:
match other:
Expand Down Expand Up @@ -253,6 +261,11 @@ def parse(v: str) -> Version:
return MajorMinor(
year=int(m[1]), major=int(m[2]), minor=int(m[3]), patch=int(m[4])
)
case r"^FY(\d{4})$" as m:
return Date(
date(int(m[1]), 1, 1),
format=DateVersionFormat.fiscal_year,
)
case r"^(\d{2})Q(\d)$" as m:
return Date(
date(2000 + int(m[1]), int(m[2]) * 3 - 2, 1),
Expand Down Expand Up @@ -403,6 +416,11 @@ def bump(
minor=previous_version.minor,
patch=previous_version.patch + bump_by,
)
case Date(format=DateVersionFormat.fiscal_year), None:
return Date(
date=previous_version.date + relativedelta(years=bump_by),
format=previous_version.format,
)
case Date(format=DateVersionFormat.quarter), None:
return Date(
date=previous_version.date + relativedelta(months=bump_by * 3),
Expand Down Expand Up @@ -436,7 +454,7 @@ def bump(
case Date(), None:
raise ValueError(f"Unsupported date format {previous_version.format}")
case Date(), _:
raise Exception(
raise ValueError(
f"Version subtype {bump_type} not applicable for Date versions"
)
case CapitalBudget(), None:
Expand Down