Skip to content

Model module

Context

Contexts are used to store the state machine execution context.

They can either be a custom defined pydantic model or Dict[str, Any].

StatemachineConfig

Placeholder generic type for state machine configurations.

ActivePeriod pydantic-model

Active period union type.

This class can be used in pydantic models to allow loading of ComplexActivePeriod or SimpleActivePeriod.

in_active_period(self, to_check)

Checks wether the given datetime is within this active period.

The actual check is delegated to the underlying sub types own checking logic.

Parameters:

Name Type Description Default
to_check datetime

The datetime to check

required

Returns:

Type Description
bool

bool: True if inside the active period False otherwise

Source code in simulation/model.py
def in_active_period(self, to_check: datetime) -> bool:
    """Checks wether the given datetime is within this active period.

    The actual check is delegated to the underlying sub types own
    checking logic.

    Args:
        to_check (datetime): The datetime to check

    Returns:
        bool: `True` if inside the active period `False` otherwise
    """
    return self.__root__.in_active_period(to_check)

ApproximateFloat pydantic-model

Approximate float value within a given open interval

max: float pydantic-field required

The upper boundary for the float value

min: float pydantic-field required

The lower boundary for the float value

value: float property readonly

Gets a random float within the approximate float range

Returns:

Type Description
float

A float x for which min <= x <= max

convert(value) classmethod

Coerces a single float value to its ApproximateFloat equivalent.

i.e., value = min = max

Parameters:

Name Type Description Default
value float

The float value to convert

required

Returns:

Type Description
ApproximateFloat

Approximate(min=value, max=value)

Source code in simulation/model.py
@classmethod
def convert(cls, value: float) -> "ApproximateFloat":
    """Coerces a single float value to its ApproximateFloat equivalent.

    i.e., value = min = max

    Args:
        value: The float value to convert

    Returns:
        `Approximate(min=value, max=value)`
    """
    return ApproximateFloat(min=value, max=value)

validate_min_le_max(v, values, **kwargs) classmethod

Custom validator to ensure min <= max

Source code in simulation/model.py
@validator("max")
def validate_min_le_max(cls, v: float, values, **kwargs) -> float:
    """Custom validator to ensure min <= max"""
    assert values["min"] <= v, "Invalid boundaries must be min <= max"
    return v

ComplexActivePeriod pydantic-model

A ComplexActivePeriod is defined by a set of WeekdayActivePeriod.

This makes it possible to configure active times for each week day separately.

week_days: WeekdayActivePeriod pydantic-field required

Set of active periods, each week day can only have one configuration

in_active_period(self, to_check)

Checks wether the given datetime is within this active period.

This is True if the datetime is within the scope of one of the WeekdayActivePeriods.

Parameters:

Name Type Description Default
to_check datetime

The datetime to check

required

Returns:

Type Description
bool

bool: True if inside the active period False otherwise

Source code in simulation/model.py
def in_active_period(self, to_check: datetime) -> bool:
    """Checks wether the given datetime is within this active period.

       This is `True` if the datetime is within the scope of **one** of
       the [`WeekdayActivePeriods`][cr_kyoushi.simulation.model.WeekdayActivePeriod].


    Args:
        to_check (datetime): The datetime to check

    Returns:
        bool: `True` if inside the active period `False` otherwise
    """
    return any(period.in_active_period(to_check) for period in self.week_days)

LogLevel

An enumeration.

SimpleActivePeriod pydantic-model

Similar to ComplexActivePeriod, but each week day has the same active time period.

time_period: TimePeriod pydantic-field

The daylie active time period (if this is not set the whole days are considered active).

week_days: Weekday pydantic-field required

A set of active week days.

in_active_period(self, to_check)

Checks wether the given datetime is within this active period.

This is True if the datetime matches one of the active week days and the active time period.

Parameters:

Name Type Description Default
to_check datetime

The datetime to check

required

Returns:

Type Description
bool

bool: True if inside the active period False otherwise

Source code in simulation/model.py
def in_active_period(self, to_check: datetime) -> bool:
    """Checks wether the given datetime is within this active period.

       This is `True` if the datetime matches **one** of the active week days
       and the active time period.


    Args:
        to_check (datetime): The datetime to check

    Returns:
        bool: `True` if inside the active period `False` otherwise
    """
    return Weekday(to_check.weekday()) in self.week_days and (
        self.time_period is None or self.time_period.in_period(to_check.time())
    )

TimePeriod pydantic-model

A time period as defined by a start and end time.

end_time: time pydantic-field required

The end time of the period

start_time: time pydantic-field required

The start time of the period

in_period(self, to_check)

Checks wether the given time of the day is within the scope of this time period.

Parameters:

Name Type Description Default
to_check time

The time of the day to check

required

Returns:

Type Description
bool

bool: True if within the time period False otherwise

Source code in simulation/model.py
def in_period(self, to_check: time) -> bool:
    """Checks wether the given time of the day is within the scope of this time period.

    Args:
        to_check (time): The time of the day to check

    Returns:
        bool: `True` if within the time period `False` otherwise
    """
    if self.start_time <= self.end_time:
        return self.start_time <= to_check and self.end_time > to_check
    # start > end means our time period is between two days
    return self.start_time <= to_check or self.end_time > to_check

Weekday

Enumeration for representing the days of the week.

Weekdays are represented as the integers 0-6 and can be constructed from either their int representations or their english names.

WeekdayActivePeriod pydantic-model

A WeekdayActivePeriod defines its active time based on the configure week day and time period.

time_period: TimePeriod pydantic-field

The active time period (if this is not set the whole day is considered active)

week_day: Weekday pydantic-field required

The active day of the week

__eq__(self, other) special

Return self==value.

Source code in simulation/model.py
def __eq__(self, other) -> bool:
    return self.__class__ == other.__class__ and self.week_day == other.week_day

__hash__(self) special

Return hash(self).

Source code in simulation/model.py
def __hash__(self):
    return hash(self.week_day)

in_active_period(self, to_check)

Checks wether the given datetime is within the scope of this active period.

This will be True if the week day matches and given time of the day is within the time period.

Parameters:

Name Type Description Default
to_check datetime

The datetime to check

required

Returns:

Type Description
bool

bool: True if inside the active period False otherwise

Source code in simulation/model.py
def in_active_period(self, to_check: datetime) -> bool:
    """Checks wether the given datetime is within the scope of this active period.

       This will be True if the week day matches and given time of the day is
       within the time period.

    Args:
        to_check (datetime): The datetime to check

    Returns:
        bool: `True` if inside the active period `False` otherwise
    """
    return Weekday(to_check.weekday()) is self.week_day and (
        self.time_period is None or self.time_period.in_period(to_check.time())
    )

WorkHours pydantic-model

A special type of time period that does not allow over night periods

validate_start_before_end(v, values, **kwargs) classmethod

Validates that the start time is before the end time.

This restricts work hours to be within a single day i.e., work hours can not be defined to start on one day and end on the following day.

Parameters:

Name Type Description Default
v time

The parsed end time to check

required
values Dict[str, Any]

A dictionary containing previously parsed and validated fields. See Pydantic for details.

required

Returns:

Type Description
time

The end time if it is valid

Source code in simulation/model.py
@validator("end_time")
def validate_start_before_end(
    cls,
    v: time,
    values: Dict[str, Any],
    **kwargs,
) -> time:
    """Validates that the start time is before the end time.

    This restricts work hours to be within a single day i.e., work hours
    can not be defined to start on one day and end on the following day.

    Args:
        v: The parsed end time to check
        values: A dictionary containing previously parsed and validated fields.
                See [Pydantic](https://pydantic-docs.helpmanual.io/usage/validators/) for details.

    Returns:
        The end time if it is valid
    """
    # if start time is not in values it failed to validate
    # so we skip the check and let validation fail
    if "start_time" in values:
        assert values["start_time"] < v, "End time must be after start time"

    return v

WorkSchedule pydantic-model

A weekly work schedule represented by the week days and work hours for each day

work_days: WorkHours pydantic-field required

Dictionary containing the work hours for each weekday

is_work_day(self, weekday)

Checks wether the given weekday is a work day.

Parameters:

Name Type Description Default
weekday Weekday

The weekday to check encoded as integer (0-6)

required

Returns:

Type Description
bool

True if it is a work day False otherwise

Source code in simulation/model.py
def is_work_day(self, weekday: Weekday) -> bool:
    """Checks wether the given weekday is a work day.

    Args:
        weekday: The weekday to check encoded as integer (0-6)

    Returns:
        `True` if it is a work day `False` otherwise
    """
    return weekday in self.work_days

is_work_time(self, to_check)

Checks wether the given datetime is work time.

Something is considered to be work time if it is a work day and the time is within the work hours of that work day.

Parameters:

Name Type Description Default
to_check datetime

The datetime to check

required

Returns:

Type Description
bool

True if the given datetime is work time False otherwise

Source code in simulation/model.py
def is_work_time(self, to_check: datetime) -> bool:
    """Checks wether the given datetime is work time.

    Something is considered to be work time if it is a work day
    and the time is within the work hours of that work day.

    Args:
        to_check: The datetime to check

    Returns:
        `True` if the given datetime is work time `False` otherwise
    """
    weekday: Weekday = Weekday(to_check.weekday())
    # check if the datetime is on a work day
    if self.is_work_day(weekday):
        # if its on a workday check if its with the days work hours
        return self.work_days[weekday].in_period(to_check.time())

    return False

next_work_start(self, to_check)

Gets the next work time, relative to the given datetime.

If the given datetime is within work hours the start time of that work day is returned.

Parameters:

Name Type Description Default
to_check datetime

The datetime to find the next work time for

required

Returns:

Type Description
Optional[datetime.datetime]

The next work time or None if there is no work time

Source code in simulation/model.py
def next_work_start(self, to_check: datetime) -> Optional[datetime]:
    """Gets the next work time, relative to the given datetime.

    If the given datetime is within work hours the start time
    of that work day is returned.

    Args:
        to_check: The datetime to find the next work time for

    Returns:
        The next work time or `None` if there is no work time
    """

    weekday: Weekday = Weekday(to_check.weekday())

    if (
        # if the given datetime is a workday
        self.is_work_day(weekday)
        and (
            # and work has not begun yet
            to_check.time() <= self.work_days[weekday].start_time
            # or if we are still within work time
            # we return the given days start time
            or self.is_work_time(to_check)
        )
    ):
        return datetime.combine(to_check.date(), self.work_days[weekday].start_time)

    # otherwise next work start must be some day after
    # the given day so we check the next 7 days
    # (we might only work once a week)
    for i in range(1, 8):
        # increment weekday and be sure to start from 0 once we pass sunday (int: 6)
        weekday = Weekday((weekday + 1) % 7)
        if self.is_work_day(weekday):
            # add the days till the next work day to the given date
            next_work: datetime = to_check + timedelta(i)
            # get the start time for that day
            start_time = self.work_days[weekday].start_time

            return datetime.combine(next_work.date(), start_time)

    # if we got here then no workday is set so we will never start
    return None