Skip to content

aimbat.types

Custom types used in AIMBAT.

Classes:

Name Description
EventParameter

AimbatEvent enum for CLI arg typing.

SAPandasTimedelta

SQLAlchemy TypeDecorator for pandas.Timedelta.

SAPandasTimestamp

SQLAlchemy TypeDecorator for pandas.Timestamp.

SeismogramParameter

AimbatSeismogramParameters enum for CLI arg typing.

EventParameter

Bases: StrEnum

AimbatEvent enum for CLI arg typing.

Source code in src/aimbat/types/_enums.py
class EventParameter(StrEnum):
    """[`AimbatEvent`][aimbat.models.AimbatEvent] enum for CLI arg typing."""

    COMPLETED = auto()
    MIN_CC = auto()
    WINDOW_PRE = auto()
    WINDOW_POST = auto()
    RAMP_WIDTH = auto()
    BANDPASS_APPLY = auto()
    BANDPASS_FMIN = auto()
    BANDPASS_FMAX = auto()
    CORNERS = auto()
    MCCC_DAMP = auto()
    MCCC_MIN_CC = auto()

SAPandasTimedelta

Bases: TypeDecorator[Timedelta]

SQLAlchemy TypeDecorator for pandas.Timedelta.

Stores duration as an integer of nanoseconds for maximum precision.

Methods:

Name Description
process_bind_param

Convert a value to an integer count of nanoseconds for storage.

process_result_value

Convert a stored nanosecond count back to a Timedelta.

Source code in src/aimbat/types/_sqlalchemy.py
class SAPandasTimedelta(TypeDecorator[Timedelta]):
    """SQLAlchemy TypeDecorator for pandas.Timedelta.

    Stores duration as an integer of nanoseconds for maximum precision.
    """

    impl = BigInteger
    cache_ok = True

    def process_bind_param(self, value: Any, dialect: Dialect) -> int | None:
        """Convert a value to an integer count of nanoseconds for storage.

        A bare number (or numeric string) is interpreted as **seconds**, via
        `coerce_to_timedelta` - the same rule `PydanticTimedelta` uses - so a
        value reaching this column without going through Pydantic validation
        (table models skip it) is not silently misread as nanoseconds.

        Args:
            value: Value to store. `None` and pandas null values are passed
                through unchanged.
            dialect: The dialect in use for the current connection.

        Returns:
            Duration in nanoseconds, or `None` if `value` is null.

        Raises:
            ValueError: If `value` is a non-null input that coerces to `NaT`
                (e.g. the string `"NaT"`), matching `PydanticTimedelta`.
        """
        if pd.isnull(value):
            return None

        result = coerce_to_timedelta(value)
        if pd.isnull(result):
            # A string like "NaT" slips past the null check above, but pandas
            # parses it to a Timedelta NaT that would otherwise be stored as
            # the iNaT sentinel integer.
            raise ValueError("Timedelta value cannot be NaT")

        # Explicit int cast for safety with some SQL drivers
        return int(result.value)

    def process_result_value(self, value: Any, dialect: Dialect) -> Timedelta | None:
        """Convert a stored nanosecond count back to a `Timedelta`.

        Args:
            value: Value read from the database.
            dialect: The dialect in use for the current connection.

        Returns:
            The corresponding `Timedelta`, or `None` if `value` is `None`.
        """
        if value is None:
            return None
        # Construct pd.Timedelta from the nanosecond integer
        return Timedelta(value).as_unit("ns")

process_bind_param

process_bind_param(
    value: Any, dialect: Dialect
) -> int | None

Convert a value to an integer count of nanoseconds for storage.

A bare number (or numeric string) is interpreted as seconds, via coerce_to_timedelta - the same rule PydanticTimedelta uses - so a value reaching this column without going through Pydantic validation (table models skip it) is not silently misread as nanoseconds.

Parameters:

Name Type Description Default
value Any

Value to store. None and pandas null values are passed through unchanged.

required
dialect Dialect

The dialect in use for the current connection.

required

Returns:

Type Description
int | None

Duration in nanoseconds, or None if value is null.

Raises:

Type Description
ValueError

If value is a non-null input that coerces to NaT (e.g. the string "NaT"), matching PydanticTimedelta.

Source code in src/aimbat/types/_sqlalchemy.py
def process_bind_param(self, value: Any, dialect: Dialect) -> int | None:
    """Convert a value to an integer count of nanoseconds for storage.

    A bare number (or numeric string) is interpreted as **seconds**, via
    `coerce_to_timedelta` - the same rule `PydanticTimedelta` uses - so a
    value reaching this column without going through Pydantic validation
    (table models skip it) is not silently misread as nanoseconds.

    Args:
        value: Value to store. `None` and pandas null values are passed
            through unchanged.
        dialect: The dialect in use for the current connection.

    Returns:
        Duration in nanoseconds, or `None` if `value` is null.

    Raises:
        ValueError: If `value` is a non-null input that coerces to `NaT`
            (e.g. the string `"NaT"`), matching `PydanticTimedelta`.
    """
    if pd.isnull(value):
        return None

    result = coerce_to_timedelta(value)
    if pd.isnull(result):
        # A string like "NaT" slips past the null check above, but pandas
        # parses it to a Timedelta NaT that would otherwise be stored as
        # the iNaT sentinel integer.
        raise ValueError("Timedelta value cannot be NaT")

    # Explicit int cast for safety with some SQL drivers
    return int(result.value)

process_result_value

process_result_value(
    value: Any, dialect: Dialect
) -> Timedelta | None

Convert a stored nanosecond count back to a Timedelta.

Parameters:

Name Type Description Default
value Any

Value read from the database.

required
dialect Dialect

The dialect in use for the current connection.

required

Returns:

Type Description
Timedelta | None

The corresponding Timedelta, or None if value is None.

Source code in src/aimbat/types/_sqlalchemy.py
def process_result_value(self, value: Any, dialect: Dialect) -> Timedelta | None:
    """Convert a stored nanosecond count back to a `Timedelta`.

    Args:
        value: Value read from the database.
        dialect: The dialect in use for the current connection.

    Returns:
        The corresponding `Timedelta`, or `None` if `value` is `None`.
    """
    if value is None:
        return None
    # Construct pd.Timedelta from the nanosecond integer
    return Timedelta(value).as_unit("ns")

SAPandasTimestamp

Bases: TypeDecorator[Timestamp]

SQLAlchemy TypeDecorator for pandas.Timestamp.

Ensures timezone-aware UTC storage in a DateTime column.

Methods:

Name Description
process_bind_param

Convert a value to a UTC-aware datetime for storage.

process_result_value

Convert a stored value back to a UTC-aware Timestamp.

Source code in src/aimbat/types/_sqlalchemy.py
class SAPandasTimestamp(TypeDecorator[Timestamp]):
    """SQLAlchemy TypeDecorator for pandas.Timestamp.

    Ensures timezone-aware UTC storage in a DateTime column.
    """

    impl = DateTime(timezone=True)
    cache_ok = True

    def process_bind_param(self, value: Any, dialect: Dialect) -> datetime | None:
        """Convert a value to a UTC-aware `datetime` for storage.

        Naive values are assumed to already be UTC; aware values are
        converted to UTC. Precision is truncated to microseconds, since
        `datetime` cannot represent nanoseconds.

        `PydanticTimestamp` validation is expected to have already rejected
        naive values before they reach this point; the naive-to-UTC handling
        here is a defensive fallback, not the primary validation point.

        Args:
            value: Value to store. `None` and pandas null values are passed
                through unchanged.
            dialect: The dialect in use for the current connection.

        Returns:
            A UTC-aware `datetime`, or `None` if `value` is null.
        """
        if pd.isnull(value):
            return None

        ts = value if isinstance(value, Timestamp) else Timestamp(value)

        # If naive (no TZ), localize to UTC. If aware, convert to UTC.
        if ts.tzinfo is None:
            ts = ts.tz_localize(UTC)
        else:
            ts = ts.tz_convert(UTC)

        # Round to microseconds: datetime lacks nanosecond precision.
        return ts.round("us").to_pydatetime()

    def process_result_value(self, value: Any, dialect: Dialect) -> Timestamp | None:
        """Convert a stored value back to a UTC-aware `Timestamp`.

        Args:
            value: Value read from the database.
            dialect: The dialect in use for the current connection.

        Returns:
            A UTC-aware `Timestamp`, or `None` if `value` is `None`.
        """
        if value is None:
            return None

        ts = Timestamp(value)
        # Ensure the returned pandas object is always UTC aware
        if ts.tzinfo is None:
            return ts.tz_localize(UTC)
        return ts.tz_convert(UTC)

process_bind_param

process_bind_param(
    value: Any, dialect: Dialect
) -> datetime | None

Convert a value to a UTC-aware datetime for storage.

Naive values are assumed to already be UTC; aware values are converted to UTC. Precision is truncated to microseconds, since datetime cannot represent nanoseconds.

PydanticTimestamp validation is expected to have already rejected naive values before they reach this point; the naive-to-UTC handling here is a defensive fallback, not the primary validation point.

Parameters:

Name Type Description Default
value Any

Value to store. None and pandas null values are passed through unchanged.

required
dialect Dialect

The dialect in use for the current connection.

required

Returns:

Type Description
datetime | None

A UTC-aware datetime, or None if value is null.

Source code in src/aimbat/types/_sqlalchemy.py
def process_bind_param(self, value: Any, dialect: Dialect) -> datetime | None:
    """Convert a value to a UTC-aware `datetime` for storage.

    Naive values are assumed to already be UTC; aware values are
    converted to UTC. Precision is truncated to microseconds, since
    `datetime` cannot represent nanoseconds.

    `PydanticTimestamp` validation is expected to have already rejected
    naive values before they reach this point; the naive-to-UTC handling
    here is a defensive fallback, not the primary validation point.

    Args:
        value: Value to store. `None` and pandas null values are passed
            through unchanged.
        dialect: The dialect in use for the current connection.

    Returns:
        A UTC-aware `datetime`, or `None` if `value` is null.
    """
    if pd.isnull(value):
        return None

    ts = value if isinstance(value, Timestamp) else Timestamp(value)

    # If naive (no TZ), localize to UTC. If aware, convert to UTC.
    if ts.tzinfo is None:
        ts = ts.tz_localize(UTC)
    else:
        ts = ts.tz_convert(UTC)

    # Round to microseconds: datetime lacks nanosecond precision.
    return ts.round("us").to_pydatetime()

process_result_value

process_result_value(
    value: Any, dialect: Dialect
) -> Timestamp | None

Convert a stored value back to a UTC-aware Timestamp.

Parameters:

Name Type Description Default
value Any

Value read from the database.

required
dialect Dialect

The dialect in use for the current connection.

required

Returns:

Type Description
Timestamp | None

A UTC-aware Timestamp, or None if value is None.

Source code in src/aimbat/types/_sqlalchemy.py
def process_result_value(self, value: Any, dialect: Dialect) -> Timestamp | None:
    """Convert a stored value back to a UTC-aware `Timestamp`.

    Args:
        value: Value read from the database.
        dialect: The dialect in use for the current connection.

    Returns:
        A UTC-aware `Timestamp`, or `None` if `value` is `None`.
    """
    if value is None:
        return None

    ts = Timestamp(value)
    # Ensure the returned pandas object is always UTC aware
    if ts.tzinfo is None:
        return ts.tz_localize(UTC)
    return ts.tz_convert(UTC)

SeismogramParameter

Bases: StrEnum

AimbatSeismogramParameters enum for CLI arg typing.

Source code in src/aimbat/types/_enums.py
class SeismogramParameter(StrEnum):
    """[`AimbatSeismogramParameters`][aimbat.models.AimbatSeismogramParameters] enum for CLI arg typing."""

    SELECT = auto()
    FLIP = auto()
    T1 = auto()