Source code for deode.suites.suite_utils

"""Module for utility functions used in the suite definition scripts."""

from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import Generator, Iterator, List, Tuple, Union

from ..datetime_utils import as_datetime, as_timedelta
from ..logs import logger


[docs] @dataclass(frozen=True, kw_only=True) class Cycle: """Class for representing a cycle.""" day: str time: str validtime: str basetime: str
[docs] @dataclass class Cycles: """Class for generating and iterating over Cycle objects.""" first_cycle: str # Format parseable by as_datetime last_cycle: str # Format parseable by as_datetime cycle_length: str # ISO 8601 format _current_index: int = field(init=False, default=0) _cycles: List[Cycle] = field(init=False, default_factory=list) def __post_init__(self): # Generate cycles. self._generate_cycles() def _generate_cycles(self): """Generate cycles.""" # Convert attributes to datetime objects. cycle_time = as_datetime(self.first_cycle) last_cycle_time = as_datetime(self.last_cycle) cycle_length_timedelta = as_timedelta(self.cycle_length) # Generate cycles. while cycle_time <= last_cycle_time: logger.debug("cycle_time {}", cycle_time) self._cycles.append( Cycle( day=cycle_time.strftime("%Y%m%d"), time=cycle_time.strftime("%H%M"), validtime=cycle_time.strftime("%Y-%m-%dT%H:%M:%SZ"), basetime=cycle_time.strftime("%Y-%m-%dT%H:%M:%SZ"), ) ) cycle_time += cycle_length_timedelta @property def current_index(self) -> int: """Return the current cycle index.""" return self._current_index @property def current_cycle(self) -> Cycle: """Return the current Cycle object.""" return self._cycles[self._current_index] @property def next_cycle(self) -> Cycle: """Return the next Cycle object. Raises: StopIteration: If there are no more cycles. """ if self._current_index < len(self._cycles) - 1: return self._cycles[self._current_index + 1] raise StopIteration @property def end_of_month(self) -> bool: """Return True if the next cycle is in a different month. Returns: bool: True if the next cycle is in a different month """ _end_of_month = False try: current_cycle = self.current_cycle next_cycle = self.next_cycle if as_datetime(current_cycle.day).strftime("%m") != as_datetime( next_cycle.day ).strftime("%m"): _end_of_month = True except StopIteration: logger.debug("It is last cycle") return _end_of_month def __iter__(self) -> Iterator[Cycle]: """Return an iterator over the cycles.""" self._current_index = 0 while self._current_index < len(self._cycles): yield self._cycles[self._current_index] self._current_index += 1
[docs] def lbc_times_generator( basetime: datetime, endtime: datetime, step: timedelta, mode: str = "start", do_prep: bool = True, ) -> Generator[Union[int, datetime], None, None]: """Generate lbc times. For each of them there will be LBC[NN] family. Args: basetime: The base time. endtime: The end time. step: The step size. mode: The mode of the workflow. do_prep: Whether to do prep. Yields: datetime: The time period for which the next LBC will be computed. Returns: datetime: The time period for which the last LBC will be computed. """ if mode == "restart" or (mode == "start" and not do_prep): basetime += step while basetime <= endtime: # Yield the updated basetime yield basetime basetime += step # Return the last basetime return basetime
[docs] def bd_generator( bdint: timedelta, mode: str = "start", do_prep: bool = True, ) -> Generator[Tuple[int, int, int, int], None, None]: """Generate batch numbers. Args: bdint: The batch interval. mode: The mode of the workflow. do_prep: Whether to do prep. Yields: Tuple[int, int, int, int]: """ inthourbdint = int(bdint.total_seconds() // 3600) intminbdint = int(bdint.total_seconds() % 3600 // 60) intsecbdint = int(bdint.total_seconds() % 60) # we don't need LBC000 if this is not first cycle or mode != cold_start if mode == "restart" or (mode == "start" and not do_prep): bdnr = inthourbdint bd_nr = 1 subbdnr = intminbdint if (intminbdint or intsecbdint) else None subminbdnr = intsecbdint if intsecbdint else None else: bdnr = 0 bd_nr = 0 subbdnr = 0 if (intminbdint or intsecbdint) else None subminbdnr = 0 if intsecbdint else None while True: yield ( bdnr, subbdnr, subminbdnr, bd_nr, ) if subbdnr is not None: subbdnr += intminbdint if subminbdnr is not None: subminbdnr += intsecbdint if subminbdnr >= 60: subbdnr += 1 subminbdnr -= 60 if subbdnr >= 60: bdnr += 1 subbdnr -= 60 bdnr += inthourbdint bd_nr += 1