From d792f5f4c5018853496a96307d8668873925901b Mon Sep 17 00:00:00 2001 From: karthikeyannhs <174426205+Karthikeyannhs@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:40:43 +0000 Subject: [PATCH] fix - ELI-674 - current iteration code use data and time --- .../model/campaign_config.py | 25 +++++++++++++------ .../test_campaign_config_validator.py | 23 ++++++++++++++++- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/eligibility_signposting_api/model/campaign_config.py b/src/eligibility_signposting_api/model/campaign_config.py index ce71cf18..2e9a282a 100644 --- a/src/eligibility_signposting_api/model/campaign_config.py +++ b/src/eligibility_signposting_api/model/campaign_config.py @@ -415,11 +415,22 @@ def check_start_and_end_dates_sensible(self) -> typing.Self: @model_validator(mode="after") def check_no_overlapping_iterations(self) -> typing.Self: - iterations_by_date = Counter([i.iteration_date for i in self.iterations]) - if multiple_found := next(((d, c) for d, c in iterations_by_date.most_common() if c > 1), None): - iteration_date, count = multiple_found - message = f"{count} iterations with iteration date {iteration_date} in campaign {self.id}" + date_time_pairs = [] + for i in self.iterations: + effective_time = i.iteration_time or self.iteration_time + date_time_pairs.append((i.iteration_date, effective_time)) + + counts = Counter(date_time_pairs) + + duplicate = next(((dt, c) for dt, c in counts.items() if c > 1), None) + + if duplicate: + (iteration_date, iteration_time), count = duplicate + message = ( + f"{count} iterations with iteration date/time {iteration_date} {iteration_time} in campaign {self.id}" + ) raise ValueError(message) + return self @cached_property @@ -429,9 +440,9 @@ def campaign_live(self) -> bool: @cached_property def current_iteration(self) -> Iteration: - today = datetime.now(tz=UTC).date() - iterations_by_date_descending = sorted(self.iterations, key=attrgetter("iteration_date"), reverse=True) - return next(i for i in iterations_by_date_descending if i.iteration_date <= today) + now = datetime.now(tz=UTC) + iterations_by_date_descending = sorted(self.iterations, key=attrgetter("iteration_datetime"), reverse=True) + return next(i for i in iterations_by_date_descending if i.iteration_datetime <= now) def __str__(self) -> str: return json.dumps(self.model_dump(by_alias=True), indent=2) diff --git a/tests/unit/validation/test_campaign_config_validator.py b/tests/unit/validation/test_campaign_config_validator.py index 4edf9ba8..8efc725b 100644 --- a/tests/unit/validation/test_campaign_config_validator.py +++ b/tests/unit/validation/test_campaign_config_validator.py @@ -1,6 +1,7 @@ -from datetime import datetime +from datetime import UTC, datetime import pytest +from freezegun import freeze_time from pydantic import ValidationError from rules_validation_api.validators.campaign_config_validator import CampaignConfigValidation @@ -320,3 +321,23 @@ def test_approval_minimum_greater_than_approval_maximum_is_invalid( data["ApprovalMinimum"] = approval_min data["ApprovalMaximum"] = approval_max CampaignConfigValidation(**data) + + @freeze_time("2026-03-30 01:00:00") + def test_get_current_iteration_by_iteration_date_time(self, valid_campaign_config_with_only_mandatory_fields): + data = valid_campaign_config_with_only_mandatory_fields.copy() + data["StartDate"] = "20260301" + data["EndDate"] = "20260630" + iteration_1 = data["Iterations"][0] + iteration_1["IterationDate"] = "20260329" + iteration_1["IterationTime"] = "01:00:00" + iteration_2 = data["Iterations"][1] + iteration_2["IterationDate"] = "20260329" + iteration_2["IterationTime"] = "01:00:02" + + model = CampaignConfigValidation(**data) + + expected = datetime.strptime( + iteration_2["IterationDate"] + iteration_2["IterationTime"], "%Y%m%d%H:%M:%S" + ).replace(tzinfo=UTC) + + assert model.current_iteration.iteration_datetime == expected