diff --git a/_docs/blueprints.md b/_docs/blueprints.md index 8772036..0ddb9f3 100644 --- a/_docs/blueprints.md +++ b/_docs/blueprints.md @@ -20,7 +20,7 @@ This blueprint will provide the data source for the UK Carbon Intensity as provi #### Default -[Install blueprint)](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fbottlecapdave.github.io%2FHomeAssistant-TargetTimeframes%2Fblueprints%2Ftarget_timeframes_octopus_energy.yaml) | [Source](./blueprints/target_timeframes_octopus_energy.yaml) +[Install blueprint](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fbottlecapdave.github.io%2FHomeAssistant-TargetTimeframes%2Fblueprints%2Ftarget_timeframes_octopus_energy.yaml) | [Source](./blueprints/target_timeframes_octopus_energy.yaml) This blueprint will provide the data source for Octopus Energy rates as provided by the [Octopus Energy](https://github.com/BottlecapDave/HomeAssistant-OctopusEnergy) integration. This is for accounts that don't have the [free electricity sensor](https://bottlecapdave.github.io/HomeAssistant-OctopusEnergy/entities/octoplus/#free-electricity-session-events) available. @@ -46,4 +46,4 @@ This blueprint will provide the data source for Octopus Energy rates as provided !!! warning - This automation will run when any of the underlying entities update. This make take a while initially. If you want the data available immediately, then you'll need to run the automation manually. \ No newline at end of file + This automation will run when any of the underlying entities update. This make take a while initially. If you want the data available immediately, then you'll need to run the automation manually. diff --git a/custom_components/target_timeframes/entities/__init__.py b/custom_components/target_timeframes/entities/__init__.py index 81057a3..813785f 100644 --- a/custom_components/target_timeframes/entities/__init__.py +++ b/custom_components/target_timeframes/entities/__init__.py @@ -33,17 +33,17 @@ def apply_offset(date_time: datetime, offset: str, inverse = False): return date_time + timedelta(hours=hours, minutes=minutes, seconds=seconds) -def is_target_timeframe_complete_in_period(current_date: datetime, applicable_time_periods: list | None, target_timeframes: list | None): - if applicable_time_periods is None or target_timeframes is None or len(applicable_time_periods) < 1 or len(target_timeframes) < 1: +def is_target_timeframe_complete_in_period(current_date: datetime, start_time: datetime, end_time: datetime, target_timeframes: list | None): + if target_timeframes is None or len(target_timeframes) < 1: return False return ( - applicable_time_periods[0]["start"] <= target_timeframes[0]["start"] and - applicable_time_periods[-1]["end"] >= target_timeframes[-1]["end"] and + start_time <= target_timeframes[0]["start"] and + end_time >= target_timeframes[-1]["end"] and target_timeframes[-1]["end"] <= current_date ) -def get_fixed_applicable_time_periods(current_date: datetime, target_start_time: str, target_end_time: str, time_period_values: list, is_rolling_target = True): +def get_start_and_end_times(current_date: datetime, target_start_time: str, target_end_time: str, start_time_not_in_past = True): if (target_start_time is not None): target_start = parse_datetime(current_date.strftime(f"%Y-%m-%dT{target_start_time}:00%z")) else: @@ -65,7 +65,7 @@ def get_fixed_applicable_time_periods(current_date: datetime, target_start_time: target_end = target_end + timedelta(days=1) # If our start date has passed, reset it to current_date to avoid picking a slot in the past - if (is_rolling_target == True and target_start < current_date and current_date < target_end): + if (start_time_not_in_past == True and target_start < current_date and current_date < target_end): _LOGGER.debug(f'Rolling target and {target_start} is in the past. Setting start to {current_date}') target_start = current_date @@ -74,6 +74,9 @@ def get_fixed_applicable_time_periods(current_date: datetime, target_start_time: target_start = target_start + timedelta(days=1) target_end = target_end + timedelta(days=1) + return (target_start, target_end) + +def get_fixed_applicable_time_periods(target_start: datetime, target_end: datetime, time_period_values: list): _LOGGER.debug(f'Finding rates between {target_start} and {target_end}') # Retrieve the rates that are applicable for our target rate @@ -434,4 +437,4 @@ def should_evaluate_target_timeframes(current_date: datetime, target_timeframes: return ((evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_PAST and all_rates_in_past) or (evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALL_IN_FUTURE_OR_PAST and (one_rate_in_past == False or all_rates_in_past)) or - (evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS)) + (evaluation_mode == CONFIG_TARGET_TARGET_TIMES_EVALUATION_MODE_ALWAYS)) \ No newline at end of file diff --git a/custom_components/target_timeframes/entities/target_timeframe.py b/custom_components/target_timeframes/entities/target_timeframe.py index a76552c..9b9b5e0 100644 --- a/custom_components/target_timeframes/entities/target_timeframe.py +++ b/custom_components/target_timeframes/entities/target_timeframe.py @@ -48,6 +48,7 @@ create_weighting, extract_config, get_fixed_applicable_time_periods, + get_start_and_end_times, get_target_time_period_info, is_target_timeframe_complete_in_period, should_evaluate_target_timeframes @@ -166,22 +167,24 @@ async def async_update(self): if CONFIG_TARGET_MAX_VALUE in self._config: max_rate = self._config[CONFIG_TARGET_MAX_VALUE] + target_start, target_end = get_start_and_end_times(current_local_date, start_time, end_time, True) applicable_time_periods = get_fixed_applicable_time_periods( - current_local_date, - start_time, - end_time, - self._data_source_data, - is_rolling_target + target_start, + target_end, + self._data_source_data ) - is_target_timeframe_complete = is_rolling_target == False and is_target_timeframe_complete_in_period(current_local_date, applicable_time_periods, self._target_timeframes) + # Make sure we haven't already completed for the current target timeframe + applicable_target_start, applicable_target_end = get_start_and_end_times(current_local_date, start_time, end_time, False) + is_target_timeframe_complete = is_rolling_target == False and is_target_timeframe_complete_in_period(current_local_date, applicable_target_start, applicable_target_end, self._target_timeframes) if applicable_time_periods is not None and is_target_timeframe_complete == False: number_of_slots = math.ceil(target_hours * 2) weighting = create_weighting(self._config[CONFIG_TARGET_WEIGHTING] if CONFIG_TARGET_WEIGHTING in self._config else None, number_of_slots) + proposed_target_timeframes = None if (self._config[CONFIG_TARGET_TYPE] == CONFIG_TARGET_TYPE_CONTINUOUS): - self._target_timeframes = calculate_continuous_times( + proposed_target_timeframes = calculate_continuous_times( applicable_time_periods, target_hours, find_highest_values, @@ -192,7 +195,7 @@ async def async_update(self): hours_mode = self._config[CONFIG_TARGET_HOURS_MODE] ) elif (self._config[CONFIG_TARGET_TYPE] == CONFIG_TARGET_TYPE_INTERMITTENT): - self._target_timeframes = calculate_intermittent_times( + proposed_target_timeframes = calculate_intermittent_times( applicable_time_periods, target_hours, find_highest_values, @@ -204,6 +207,7 @@ async def async_update(self): else: _LOGGER.error(f"Unexpected target type: {self._config[CONFIG_TARGET_TYPE]}") + self._target_timeframes = proposed_target_timeframes self._attributes["target_times"] = self._target_timeframes self._attributes["target_times_last_evaluated"] = current_date _LOGGER.debug(f"calculated rates: {self._target_timeframes}") diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 7e9b2a1..bb920c1 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -8,7 +8,7 @@ _LOGGER = logging.getLogger(__name__) def create_data_source_data(period_from: datetime, period_to: datetime, expected_values: list): - rates = [] + values = [] current_valid_from = period_from current_valid_to = None @@ -16,7 +16,7 @@ def create_data_source_data(period_from: datetime, period_to: datetime, expected while current_valid_to is None or current_valid_to < period_to: current_valid_to = current_valid_from + timedelta(minutes=30) - rates.append({ + values.append({ "start": current_valid_from, "end": current_valid_to, "value": expected_values[rate_index], @@ -28,19 +28,19 @@ def create_data_source_data(period_from: datetime, period_to: datetime, expected if (rate_index > (len(expected_values) - 1)): rate_index = 0 - return rates + return values def get_start(rate): return rate["start"] def values_to_thirty_minute_increments(items: list, period_from: datetime, period_to: datetime): - """Process the collection of rates to ensure they're in 30 minute periods""" + """Process the collection of values to ensure they're in 30 minute periods""" starting_period_from = period_from results = [] items.sort(key=get_start) - # We need to normalise our data into 30 minute increments so that all of our rates across all tariffs are the same and it's + # We need to normalise our data into 30 minute increments so that all of our values across all tariffs are the same and it's # easier to calculate our target rate sensors for item in items: value = float(item["value"]) @@ -53,7 +53,7 @@ def values_to_thirty_minute_increments(items: list, period_from: datetime, perio else: start = starting_period_from - # Some rates don't have end dates, so we should treat this as our period to target + # Some values don't have end dates, so we should treat this as our period to target if "end" in item and item["end"] is not None: target_date = as_utc(parse_datetime(item["end"])) diff --git a/tests/unit/target_rates/test_calculate_continuous_times.py b/tests/unit/target_rates/test_calculate_continuous_times.py index 854766c..195fbc1 100644 --- a/tests/unit/target_rates/test_calculate_continuous_times.py +++ b/tests/unit/target_rates/test_calculate_continuous_times.py @@ -4,7 +4,7 @@ import pytest from unit import (create_data_source_data, default_time_periods, values_to_thirty_minute_increments) -from custom_components.target_timeframes.entities import calculate_continuous_times, get_fixed_applicable_time_periods +from custom_components.target_timeframes.entities import calculate_continuous_times, get_fixed_applicable_time_periods, get_start_and_end_times @pytest.mark.asyncio @pytest.mark.parametrize("current_date,target_start_time,target_end_time,expected_first_valid_from,is_rolling_target,find_last_rates",[ @@ -62,7 +62,7 @@ async def test_when_continuous_times_present_then_next_continuous_times_returned period_to = datetime.strptime("2022-02-11T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 0.2, 0.3, 0.2, 0.2, 0.1] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -71,12 +71,12 @@ async def test_when_continuous_times_present_then_next_continuous_times_returned # Restrict our time block target_hours = 1 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, is_rolling_target) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - is_rolling_target + target_start_datetime, + target_end_datetime, + values ) # Act @@ -154,7 +154,7 @@ async def test_when_continuous_times_present_and_highest_price_required_then_nex period_to = datetime.strptime("2022-02-11T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 0.2, 0.3] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -163,12 +163,12 @@ async def test_when_continuous_times_present_and_highest_price_required_then_nex # Restrict our time block target_hours = 1 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, is_rolling_target) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - is_rolling_target + target_start_datetime, + target_end_datetime, + values ) # Act @@ -210,7 +210,7 @@ async def test_when_continuous_times_present_and_highest_price_required_then_nex ]) async def test_readme_examples(current_date, target_start_time, target_end_time, expected_first_valid_from, is_rolling_target): # Arrange - rates = values_to_thirty_minute_increments( + values = values_to_thirty_minute_increments( [ { "value": 6, @@ -275,12 +275,12 @@ async def test_readme_examples(current_date, target_start_time, target_end_time, # Restrict our time block target_hours = 1 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, is_rolling_target) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - is_rolling_target + target_start_datetime, + target_end_datetime, + values ) # Act @@ -328,12 +328,12 @@ async def test_when_last_rate_is_currently_active_and_target_is_rolling_then_rat # Restrict our time block target_hours = 0.5 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - True + target_start_datetime, + target_end_datetime, + default_time_periods ) # Act @@ -361,12 +361,12 @@ async def test_when_available_rates_are_too_low_then_no_times_are_returned(): # Restrict our time block target_hours = 3 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - False + target_start_datetime, + target_end_datetime, + default_time_periods ) # Act @@ -391,18 +391,18 @@ async def test_when_min_value_is_provided_then_result_does_not_include_any_rate_ target_end_time = "22:00" min_value = 19 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.1, 20] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -437,18 +437,18 @@ async def test_when_max_value_is_provided_then_result_does_not_include_any_rate_ target_end_time = "22:00" max_value = 19.9 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.1, 20] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -496,18 +496,18 @@ async def test_when_weighting_specified_then_result_is_adjusted(weighting: list, target_start_time = "09:00" target_end_time = "22:00" - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), possible_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -542,18 +542,18 @@ async def test_when_target_hours_zero_then_result_is_adjusted(weighting): target_start_time = "09:00" target_end_time = "22:00" - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.1, 15.1, 20] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -578,18 +578,18 @@ def test_when_hour_mode_is_maximum_and_not_enough_hours_available_then_reduced_t possible_values = [19.1, 18.9, 21.1, 15.1, 20] expected_values = [19.1, 18.9] - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), possible_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -621,18 +621,18 @@ def test_when_hour_mode_is_maximum_and_more_than_enough_hours_available_then_tar possible_values = [19.1, 18.9, 19.1, 15.1, 20] expected_values = [18.9, 19.1, 15.1] - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), possible_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -662,18 +662,18 @@ def test_when_hour_mode_is_minimum_and_not_enough_hours_available_then_no_target target_end_time = "22:00" possible_values = [19.1, 18.9, 21.1, 15.1, 20] - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), possible_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -698,18 +698,18 @@ def test_when_hour_mode_is_minimum_and_more_than_enough_hours_available_then_tar possible_values = [19.1, 18.9, 19.1, 15.1, 20] expected_values = [19.1, 18.9, 19.1, 15.1] - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), possible_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act diff --git a/tests/unit/target_rates/test_calculate_intermittent_times.py b/tests/unit/target_rates/test_calculate_intermittent_times.py index 40ff4af..c158307 100644 --- a/tests/unit/target_rates/test_calculate_intermittent_times.py +++ b/tests/unit/target_rates/test_calculate_intermittent_times.py @@ -4,7 +4,7 @@ import pytest from unit import (create_data_source_data, default_time_periods) -from custom_components.target_timeframes.entities import calculate_intermittent_times, get_fixed_applicable_time_periods +from custom_components.target_timeframes.entities import calculate_intermittent_times, get_fixed_applicable_time_periods, get_start_and_end_times @pytest.mark.asyncio @pytest.mark.parametrize("current_date,target_start_time,target_end_time,expected_first_valid_from,is_rolling_target,find_last_rates",[ @@ -63,7 +63,7 @@ async def test_when_intermittent_times_present_then_next_intermittent_times_retu period_to = datetime.strptime("2022-02-11T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 0.2, 0.3] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -72,12 +72,12 @@ async def test_when_intermittent_times_present_then_next_intermittent_times_retu # Restrict our time block target_hours = 1 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, is_rolling_target) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - is_rolling_target + target_start_datetime, + target_end_datetime, + values ) # Act @@ -155,7 +155,7 @@ async def test_when_intermittent_times_present_and_highest_prices_are_true_then_ period_to = datetime.strptime("2022-02-11T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 0.2, 0.3] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -164,12 +164,12 @@ async def test_when_intermittent_times_present_and_highest_prices_are_true_then_ # Restrict our time block target_hours = 1 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, is_rolling_target) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - is_rolling_target + target_start_datetime, + target_end_datetime, + values ) # Act @@ -198,7 +198,7 @@ async def test_when_current_time_has_not_enough_time_left_then_no_intermittent_t period_to = datetime.strptime("2022-02-10T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 0.2, 0.3] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -210,12 +210,12 @@ async def test_when_current_time_has_not_enough_time_left_then_no_intermittent_t target_end_time = "18:00" target_hours = 1 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Act @@ -238,12 +238,12 @@ async def test_when_using_agile_times_then_lowest_rates_are_picked(): # Restrict our time block target_hours = 3 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - False + target_start_datetime, + target_end_datetime, + default_time_periods ) # Act @@ -290,12 +290,12 @@ async def test_when_available_rates_are_too_low_then_no_times_are_returned(): # Restrict our time block target_hours = 3 + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - False + target_start_datetime, + target_end_datetime, + default_time_periods ) # Act @@ -320,18 +320,18 @@ async def test_when_min_value_is_provided_then_result_does_not_include_any_rate_ target_end_time = "16:00" min_value = 19 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.5, 17.9, 16.5, 20.1] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Act @@ -362,18 +362,18 @@ async def test_when_max_value_is_provided_then_result_does_not_include_any_rate_ target_end_time = "16:00" max_value = 20.0 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.5, 17.9, 16.5, 20.1] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Act @@ -401,18 +401,18 @@ async def test_when_hour_mode_is_maximum_and_not_enough_hours_available_then_red target_end_time = "16:00" max_value = 19 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.5, 18.9, 20.1, 18.9] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Act @@ -442,18 +442,18 @@ async def test_when_hour_mode_is_maximum_and_more_than_enough_hours_available_th target_start_time = "16:00" target_end_time = "16:00" - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.5, 18.9, 20.1, 18.9] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Act @@ -483,18 +483,18 @@ async def test_when_hour_mode_is_minimum_and_not_enough_hours_available_then_no_ target_end_time = "16:00" max_value = 19 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.5, 18.9, 20.1, 18.9] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Act @@ -518,18 +518,18 @@ async def test_when_hour_mode_is_minimum_and_more_than_enough_hours_available_th target_end_time = "16:00" max_value = 19 - rates = create_data_source_data( + values = create_data_source_data( datetime.strptime("2022-10-22T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), datetime.strptime("2022-10-23T16:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z"), [19.1, 18.9, 19.5, 18.9, 20.1, 18.9] ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + applicable_time_periods = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Act diff --git a/tests/unit/target_rates/test_get_fixed_applicable_time_periods.py b/tests/unit/target_rates/test_get_fixed_applicable_time_periods.py index 53e79e8..17a8193 100644 --- a/tests/unit/target_rates/test_get_fixed_applicable_time_periods.py +++ b/tests/unit/target_rates/test_get_fixed_applicable_time_periods.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta import pytest -from custom_components.target_timeframes.entities import get_fixed_applicable_time_periods +from custom_components.target_timeframes.entities import get_fixed_applicable_time_periods, get_start_and_end_times from unit import create_data_source_data, default_time_periods, values_to_thirty_minute_increments @pytest.mark.asyncio @@ -39,19 +39,19 @@ async def test_when_continuous_times_present_then_next_continuous_times_returned period_to = datetime.strptime("2022-02-11T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 20, 0.3, 20, 20, 0.1] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, is_rolling_target) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - is_rolling_target + target_start_datetime, + target_end_datetime, + values ) assert result is not None @@ -71,13 +71,13 @@ async def test_when_start_time_is_after_end_time_then_rates_are_overnight(): expected_first_valid_from = datetime.strptime("2022-10-21T23:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z") + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - False + target_start_datetime, + target_end_datetime, + default_time_periods ) # Assert @@ -99,13 +99,13 @@ async def test_when_start_time_and_end_time_is_same_then_rates_are_shifted(): expected_first_valid_from = datetime.strptime("2022-10-21T23:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z") + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - False + target_start_datetime, + target_end_datetime, + default_time_periods ) # Assert @@ -125,7 +125,7 @@ async def test_when_start_time_is_after_end_time_and_rolling_target_then_rates_a expected_first_valid_from = datetime.strptime("2022-10-21T23:30:00+00:00", "%Y-%m-%dT%H:%M:%S%z") - rates = values_to_thirty_minute_increments( + values = values_to_thirty_minute_increments( [ { "value": 15.1, @@ -147,13 +147,13 @@ async def test_when_start_time_is_after_end_time_and_rolling_target_then_rates_a datetime.strptime("2022-10-24T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Assert @@ -174,7 +174,7 @@ async def test_when_start_time_and_end_time_is_same_and_rolling_target_then_rate expected_first_valid_from = datetime.strptime("2022-10-22T02:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z") - rates = values_to_thirty_minute_increments( + values = values_to_thirty_minute_increments( [ { "value": 15.1, @@ -201,13 +201,13 @@ async def test_when_start_time_and_end_time_is_same_and_rolling_target_then_rate datetime.strptime("2022-10-24T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, True) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - True + target_start_datetime, + target_end_datetime, + values ) # Assert @@ -227,13 +227,13 @@ async def test_when_available_rates_are_too_low_then_no_times_are_returned(): target_start_time = "16:00" target_end_time = "16:00" + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - default_time_periods, - False + target_start_datetime, + target_end_datetime, + default_time_periods ) # Assert @@ -250,19 +250,19 @@ async def test_when_times_are_in_bst_then_rates_are_shifted(): period_to = datetime.strptime("2024-04-07T00:00:00+00:00", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 20, 0.3, 20, 20, 0.1] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values ) + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) + # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Assert @@ -283,7 +283,7 @@ async def test_when_clocks_go_back_then_correct_times_are_selected(): target_start_time = "23:00" target_end_time = "23:00" - rates = [ + values = [ { "value": 13.797, "start": datetime.strptime("2024-10-27T22:30:00Z", "%Y-%m-%dT%H:%M:%S%z"), @@ -536,15 +536,15 @@ async def test_when_clocks_go_back_then_correct_times_are_selected(): }, ] - rates.sort(key=lambda x: (x["start"].timestamp(), x["start"].fold)) + values.sort(key=lambda x: (x["start"].timestamp(), x["start"].fold)) + + (target_start_datetime, target_end_datetime) = get_start_and_end_times(current_date, target_start_time, target_end_time, False) # Act result = get_fixed_applicable_time_periods( - current_date, - target_start_time, - target_end_time, - rates, - False + target_start_datetime, + target_end_datetime, + values ) # Assert diff --git a/tests/unit/target_rates/test_get_start_and_end_times.py b/tests/unit/target_rates/test_get_start_and_end_times.py new file mode 100644 index 0000000..ea373f6 --- /dev/null +++ b/tests/unit/target_rates/test_get_start_and_end_times.py @@ -0,0 +1,295 @@ +from datetime import datetime +import pytest +import logging + +from custom_components.target_timeframes.entities import get_start_and_end_times + +_LOGGER = logging.getLogger(__name__) + +@pytest.mark.asyncio +async def test_when_target_times_are_none_and_start_time_not_in_past_is_false_then_returns_full_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = None + target_end_time = None + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_target_times_are_none_and_start_time_not_in_past_is_true_then_returns_half_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = None + target_end_time = None + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_start_time_provided_and_start_time_not_in_past_is_true_then_returns_specified_times(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = None + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_start_time_provided_and_start_time_not_in_past_is_false_then_returns_specified_times(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = None + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_end_time_provided_and_start_time_not_in_past_is_true_then_returns_specified_times(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = None + target_end_time = "18:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T18:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_end_time_provided_and_start_time_not_in_past_is_false_then_returns_specified_times(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = None + target_end_time = "18:00" + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T00:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T18:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_target_times_provided_and_start_time_not_in_past_is_false_then_returns_specified_times(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_target_times_provided_and_start_time_not_in_past_is_true_then_returns_specified_times(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_start_time_after_end_time_and_start_after_current_and_start_time_not_in_past_is_true_then_start_moves_to_previous_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "22:00" + target_end_time = "04:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T22:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T04:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_start_time_after_end_time_and_start_before_current_and_start_time_not_in_past_is_false_then_end_moves_to_next_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T23:30:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "22:00" + target_end_time = "04:00" + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T22:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T04:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_start_time_after_end_time_and_start_before_current_and_start_time_not_in_past_is_true_then_end_moves_to_next_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T23:30:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "22:00" + target_end_time = "04:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T23:30:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T04:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_rolling_target_and_start_time_in_past_and_end_time_in_future_then_start_becomes_current_time(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:30:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = current_date + expected_end = datetime.strptime("2023-11-16T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_non_rolling_target_and_start_time_in_past_and_end_time_in_future_then_start_stays_in_past(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:30:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_both_start_and_end_times_in_past_then_moves_to_next_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T16:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-17T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-17T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_times_are_same_day_with_current_between_them_and_non_rolling_target(): + # Arrange + current_date = datetime.strptime("2023-11-16T12:30:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = False + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end + +@pytest.mark.asyncio +async def test_when_current_is_before_both_start_and_end_times_same_day(): + # Arrange + current_date = datetime.strptime("2023-11-16T08:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + target_start_time = "10:00" + target_end_time = "15:00" + start_time_not_in_past = True + + # Act + target_start, target_end = get_start_and_end_times(current_date, target_start_time, target_end_time, start_time_not_in_past) + + # Assert + expected_start = datetime.strptime("2023-11-16T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + expected_end = datetime.strptime("2023-11-16T15:00:00Z", "%Y-%m-%dT%H:%M:%S%z") + + assert target_start == expected_start + assert target_end == expected_end \ No newline at end of file diff --git a/tests/unit/target_rates/test_get_target_rate_info.py b/tests/unit/target_rates/test_get_target_rate_info.py index 9589ec2..d424914 100644 --- a/tests/unit/target_rates/test_get_target_rate_info.py +++ b/tests/unit/target_rates/test_get_target_rate_info.py @@ -10,7 +10,7 @@ @pytest.mark.asyncio async def test_when_called_before_rates_then_not_active_returned(): # Arrange - rates = [ + values = [ { "start": datetime.strptime("2022-02-09T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z"), "end": datetime.strptime("2022-02-09T10:30:00Z", "%Y-%m-%dT%H:%M:%S%z"), @@ -33,7 +33,7 @@ async def test_when_called_before_rates_then_not_active_returned(): # Act result = get_target_time_period_info( current_date, - rates + values ) # Assert @@ -49,7 +49,7 @@ async def test_when_called_before_rates_then_not_active_returned(): assert result["current_min_value"] == None assert result["current_max_value"] == None - assert result["next_time"] == rates[0]["start"] + assert result["next_time"] == values[0]["start"] assert result["next_duration_in_hours"] == 1 assert result["next_average_value"] == 7.5 assert result["next_min_value"] == 5 @@ -96,7 +96,7 @@ async def test_when_called_before_rates_then_not_active_returned(): ]) async def test_when_called_during_rates_then_active_returned(test): # Arrange - rates = [ + values = [ { "start": datetime.strptime("2022-02-09T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z"), "end": datetime.strptime("2022-02-09T10:30:00Z", "%Y-%m-%dT%H:%M:%S%z"), @@ -131,7 +131,7 @@ async def test_when_called_during_rates_then_active_returned(test): result = get_target_time_period_info( test["current_date"], - rates + values ) # Assert @@ -160,7 +160,7 @@ async def test_when_called_after_rates_then_not_active_returned(): period_to = datetime.strptime("2022-02-09T12:00:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.1, 0.2] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -171,7 +171,7 @@ async def test_when_called_after_rates_then_not_active_returned(): # Act result = get_target_time_period_info( current_date, - rates + values ) # Assert @@ -196,7 +196,7 @@ async def test_when_offset_set_then_active_at_correct_current_time(): # Arrange offset = "-01:00:00" - rates = [ + values = [ { "start": datetime.strptime("2022-02-09T10:00:00Z", "%Y-%m-%dT%H:%M:%S%z"), "end": datetime.strptime("2022-02-09T10:30:00Z", "%Y-%m-%dT%H:%M:%S%z"), @@ -219,7 +219,7 @@ async def test_when_offset_set_then_active_at_correct_current_time(): result = get_target_time_period_info( current_date, - rates, + values, offset ) @@ -239,13 +239,13 @@ async def test_when_offset_set_then_active_at_correct_current_time(): assert result["next_min_value"] == 10 assert result["next_max_value"] == 15 - # Check where's within our rates and our offset + # Check where's within our values and our offset for minutes_to_add in range(60): - current_date = rates[0]["start"] - timedelta(hours=1) + timedelta(minutes=minutes_to_add) + current_date = values[0]["start"] - timedelta(hours=1) + timedelta(minutes=minutes_to_add) result = get_target_time_period_info( current_date, - rates, + values, offset ) @@ -266,11 +266,11 @@ async def test_when_offset_set_then_active_at_correct_current_time(): assert result["next_max_value"] == 5 # Check when within rate but after offset - current_date = rates[0]["start"] + timedelta(minutes=1) + current_date = values[0]["start"] + timedelta(minutes=1) result = get_target_time_period_info( current_date, - rates, + values, offset ) @@ -296,7 +296,7 @@ async def test_when_current_date_is_equal_to_last_end_date_then_not_active(): period_from = datetime.strptime("2022-10-09T00:30:00Z", "%Y-%m-%dT%H:%M:%S%z") period_to = datetime.strptime("2022-10-09T04:30:00Z", "%Y-%m-%dT%H:%M:%S%z") expected_values = [0.16511, 0.16512, 0.16999] - rates = create_data_source_data( + values = create_data_source_data( period_from, period_to, expected_values @@ -306,7 +306,7 @@ async def test_when_current_date_is_equal_to_last_end_date_then_not_active(): result = get_target_time_period_info( current_date, - rates, + values, None ) @@ -335,7 +335,7 @@ async def test_when_clocks_go_back_then_correct_result_returned(): applicable_time_periods.sort(key=lambda x: (x["start"].timestamp(), x["start"].fold)) # Act - rates = calculate_intermittent_times( + values = calculate_intermittent_times( applicable_time_periods, 12, False, @@ -348,7 +348,7 @@ async def test_when_clocks_go_back_then_correct_result_returned(): # Act result = get_target_time_period_info( current_date, - rates + values ) # Assert diff --git a/tests/unit/target_rates/test_is_target_timeframe_complete_in_period.py b/tests/unit/target_rates/test_is_target_timeframe_complete_in_period.py index 0567c1f..204d2eb 100644 --- a/tests/unit/target_rates/test_is_target_timeframe_complete_in_period.py +++ b/tests/unit/target_rates/test_is_target_timeframe_complete_in_period.py @@ -7,24 +7,12 @@ async def test_when_both_lists_are_none_then_false_is_returned(): # Arrange current_date = datetime.now() - applicable_time_periods = None + start_time = None + end_time = None target_timeframes = None # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) - - # Assert - assert result is False - -@pytest.mark.asyncio -async def test_when_applicable_time_periods_is_empty_then_false_is_returned(): - # Arrange - current_date = datetime.now() - applicable_time_periods = [] - target_timeframes = [{"start": datetime.now(), "end": datetime.now() + timedelta(hours=1)}] - - # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is False @@ -33,11 +21,12 @@ async def test_when_applicable_time_periods_is_empty_then_false_is_returned(): async def test_when_target_timeframes_is_empty_then_false_is_returned(): # Arrange current_date = datetime.now() - applicable_time_periods = [{"start": datetime.now(), "end": datetime.now() + timedelta(hours=1)}] + start_time = datetime.now() + end_time = datetime.now() + timedelta(hours=1) target_timeframes = [] # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is False @@ -46,14 +35,8 @@ async def test_when_target_timeframes_is_empty_then_false_is_returned(): async def test_when_target_timeframe_is_within_applicable_periods_and_complete_then_true_is_returned(): # Arrange current_date = datetime.now() - start_time = current_date - timedelta(hours=2) - end_time = current_date - timedelta(minutes=30) - - applicable_time_periods = [ - {"start": start_time - timedelta(minutes=30), "end": start_time + timedelta(minutes=30)}, - {"start": start_time + timedelta(minutes=30), "end": start_time + timedelta(minutes=60)}, - {"start": start_time + timedelta(minutes=60), "end": end_time + timedelta(minutes=30)} - ] + start_time = current_date - timedelta(hours=2, minutes=30) + end_time = current_date target_timeframes = [ {"start": start_time, "end": start_time + timedelta(minutes=30)}, @@ -61,7 +44,7 @@ async def test_when_target_timeframe_is_within_applicable_periods_and_complete_t ] # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is True @@ -73,19 +56,13 @@ async def test_when_target_timeframe_start_before_applicable_periods_then_false_ start_time = current_date - timedelta(hours=2) end_time = current_date - timedelta(minutes=30) - applicable_time_periods = [ - {"start": start_time, "end": start_time + timedelta(minutes=30)}, - {"start": start_time + timedelta(minutes=30), "end": start_time + timedelta(minutes=60)}, - {"start": start_time + timedelta(minutes=60), "end": end_time} - ] - target_timeframes = [ {"start": start_time - timedelta(minutes=30), "end": start_time + timedelta(minutes=30)}, {"start": start_time + timedelta(minutes=30), "end": end_time} ] # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is False @@ -96,20 +73,14 @@ async def test_when_target_timeframe_end_after_applicable_periods_then_false_is_ current_date = datetime.now() start_time = current_date - timedelta(hours=2) end_time = current_date - timedelta(minutes=30) - - applicable_time_periods = [ - {"start": start_time, "end": start_time + timedelta(minutes=30)}, - {"start": start_time + timedelta(minutes=30), "end": start_time + timedelta(minutes=60)}, - {"start": start_time + timedelta(minutes=60), "end": end_time} - ] - + target_timeframes = [ {"start": start_time, "end": start_time + timedelta(minutes=30)}, {"start": start_time + timedelta(minutes=30), "end": end_time + timedelta(minutes=30)} ] # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is False @@ -118,22 +89,16 @@ async def test_when_target_timeframe_end_after_applicable_periods_then_false_is_ async def test_when_target_timeframe_end_not_in_past_then_false_is_returned(): # Arrange current_date = datetime.now() - start_time = current_date - timedelta(hours=2) - end_time = current_date + timedelta(minutes=30) - - applicable_time_periods = [ - {"start": start_time - timedelta(minutes=30), "end": start_time + timedelta(minutes=30)}, - {"start": start_time + timedelta(minutes=30), "end": start_time + timedelta(minutes=60)}, - {"start": start_time + timedelta(minutes=60), "end": end_time + timedelta(minutes=30)} - ] - + start_time = current_date - timedelta(hours=2, minutes=30) + end_time = current_date + timedelta(hours=1) + target_timeframes = [ {"start": start_time, "end": start_time + timedelta(minutes=30)}, {"start": start_time + timedelta(minutes=30), "end": end_time} ] # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is False @@ -145,18 +110,13 @@ async def test_when_target_timeframe_is_exactly_equal_to_applicable_periods_and_ start_time = current_date - timedelta(hours=2) end_time = current_date - timedelta(minutes=30) - applicable_time_periods = [ - {"start": start_time, "end": start_time + timedelta(minutes=30)}, - {"start": start_time + timedelta(minutes=30), "end": end_time} - ] - target_timeframes = [ {"start": start_time, "end": start_time + timedelta(minutes=30)}, {"start": start_time + timedelta(minutes=30), "end": end_time} ] # Act - result = is_target_timeframe_complete_in_period(current_date, applicable_time_periods, target_timeframes) + result = is_target_timeframe_complete_in_period(current_date, start_time, end_time, target_timeframes) # Assert assert result is True \ No newline at end of file diff --git a/tests/unit/target_rates/test_is_target_timeframes_started.py b/tests/unit/target_rates/test_is_target_timeframes_started.py new file mode 100644 index 0000000..e69de29