Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ wheels/

# Output files
*.csv
*.xlsx
tests/output/
*.jsonl
*.jsonl.gz
Expand Down
36 changes: 36 additions & 0 deletions nuh_helper/date_shift/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@
logger = logging.getLogger(__name__)


class DateTooFarBack(Exception):
def __init__(self, value: pd.Timestamp) -> None:
message = f"the date {value} is too far in the past"
super().__init__(message)
self._message = message


class DateTooFarAhead(Exception):
def __init__(self, value: pd.Timestamp) -> None:
message = f"the date {value} is too far in the future"
super().__init__(message)
self._message = message


def _get_patient_ids_and_shift_mappings(
input_file: str,
patient_sheet: str,
Expand Down Expand Up @@ -336,6 +350,8 @@ def shift_excel_dates_inplace(
seed: int | None = None,
patient_header_row: int = 0,
patient_skip_rows: list[int] | None = None,
sanity_date_latest: datetime | pd.Timestamp | None = None,
sanity_date_earliest: datetime | pd.Timestamp | None = None,
) -> None:
"""
Shift dates in an Excel file, preserving all cell formatting.
Expand Down Expand Up @@ -364,7 +380,20 @@ def shift_excel_dates_inplace(
seed: Optional random seed for generating shifts.
patient_header_row: Zero-based header row index for the patient sheet (default: 0).
patient_skip_rows: Optional zero-based row indices to exclude from patient data.
sanity_date_latest, sanity_date_earliest: latest and earliest dates allowed in the data. used as a sanity check

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also needs adding to the readme


""" # noqa: E501

if sanity_date_latest is None:
sanity_date_latest = datetime.now()
if not isinstance(sanity_date_latest, pd.Timestamp):
sanity_date_latest = pd.Timestamp(sanity_date_latest)

if sanity_date_earliest is None:
sanity_date_earliest = datetime(1900, 1, 1)
if not isinstance(sanity_date_earliest, pd.Timestamp):
sanity_date_earliest = pd.Timestamp(sanity_date_earliest)

logger.info("Shifting dates in-place: '%s' → '%s'", input_file, output_file)
logger.debug(
"Shift range: %d to %d days, seed=%s",
Expand Down Expand Up @@ -491,6 +520,13 @@ def shift_excel_dates_inplace(
cell.value = None
continue

# only check the dates we're shifting
# ... other branches handle non-dates and dates we're not shifting
if parsed <= sanity_date_earliest:
raise DateTooFarBack(parsed)
if parsed >= sanity_date_latest:
raise DateTooFarAhead(parsed)

if shift_days is None:
continue
Comment thread
github-code-quality[bot] marked this conversation as resolved.
Fixed

Expand Down
2 changes: 2 additions & 0 deletions tests/data/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
!*.csv
!*.xlsx
Binary file added tests/data/date_sanity/too_far_ahead.xlsx
Binary file not shown.
Binary file added tests/data/date_sanity/too_far_back.xlsx
Binary file not shown.
79 changes: 79 additions & 0 deletions tests/test_date_sanity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from pathlib import Path

import pytest

from nuh_helper import shift_excel_dates_inplace
from nuh_helper.date_shift import (
DateTooFarAhead,
DateTooFarBack,
)


def test_too_far_ahead(tmp_path: Path) -> None:

source_file = Path(__file__).parent / "data/date_sanity/too_far_ahead.xlsx"
output_path = tmp_path / "target.xlsx"
linking_table_old = tmp_path / "linking_table_old.csv"
linking_table_out = tmp_path / "linking_table_out.csv"

sheet_configs = {
"tofarr": {
"patient_id_col": "patient",
"date_columns": [
"dobirth",
],
"header_row": 0,
"skip_rows_after_header": [],
},
}
with pytest.raises(DateTooFarAhead) as info:
shift_excel_dates_inplace(
input_file=str(source_file),
output_file=str(output_path),
patient_sheet="tofarr",
patient_id_col="patient",
sheet_configs=sheet_configs,
min_shift_days=-20,
max_shift_days=-1,
seed=14333,
linking_table_path=str(linking_table_old),
linking_table_output=str(linking_table_out),
)

assert (
info.value._message == "the date 2072-12-10 00:00:00 is too far in the future"
)


def test_too_far_back(tmp_path: Path) -> None:

source_file = Path(__file__).parent / "data/date_sanity/too_far_back.xlsx"
output_path = tmp_path / "target.xlsx"
linking_table_old = tmp_path / "linking_table_old.csv"
linking_table_out = tmp_path / "linking_table_out.csv"

sheet_configs = {
"Sheet1": {
"patient_id_col": "patient",
"date_columns": [
"dob",
],
"header_row": 0,
"skip_rows_after_header": [],
},
}
with pytest.raises(DateTooFarBack) as info:
shift_excel_dates_inplace(
input_file=str(source_file),
output_file=str(output_path),
patient_sheet="Sheet1",
patient_id_col="patient",
sheet_configs=sheet_configs,
min_shift_days=-20,
max_shift_days=-1,
seed=14333,
linking_table_path=str(linking_table_old),
linking_table_output=str(linking_table_out),
)

assert info.value._message == "the date 1007-02-05 00:00:00 is too far in the past"
Loading