Skip to content

Commit

Permalink
Fix linters
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-thom committed Sep 12, 2024
1 parent eb7b42a commit b1d8d09
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 87 deletions.
14 changes: 14 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.2.1
hooks:
# Run the linter.
- id: ruff
args: [ --fix ]
# Run the formatter.
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "chronify"
version = "0.0.0"
version = "0.0.1"
description = "Time series store and mapping libray"
readme = "README.md"
requires-python = ">=3.10, <3.13"
Expand Down
1 change: 0 additions & 1 deletion src/chronify/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from time_series_store.models import TableSchema
5 changes: 2 additions & 3 deletions src/chronify/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pydantic import BaseModel, ConfigDict, Field, field_validator
from typing_extensions import Annotated

from chronify.time_configs import AnnualTimeRange, TimeBaseModel, TimeConfig
from chronify.time_configs import TimeConfig


def make_model_config(**kwargs) -> ConfigDict:
Expand All @@ -16,7 +16,7 @@ def make_model_config(**kwargs) -> ConfigDict:
use_enum_values=False,
arbitrary_types_allowed=True,
populate_by_name=True,
**kwargs,
**kwargs, # type: ignore
)


Expand Down Expand Up @@ -79,7 +79,6 @@ def check_columns(cls, columns: list[str]) -> list[str]:
# TODO: print example tables here.



def _check_name(name: str) -> None:
if not REGEX_NAME_REQUIREMENT.search(name):
msg = f"A name can only have alphanumeric characters: {name=}"
Expand Down
8 changes: 3 additions & 5 deletions src/chronify/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ def __init__(self, engine: Optional[Engine] = None, **connect_args) -> None:
else:
self._engine = engine

#def add_time_series(self, data: np.ndarray) -> None:
#"""Add a time series array to the store."""
# def add_time_series(self, data: np.ndarray) -> None:
# """Add a time series array to the store."""

def create_view_from_parquet(self, name: str, path: Path) -> None:
"""Create a view in the database from a Parquet file."""
Expand Down Expand Up @@ -67,9 +67,7 @@ def update_table_schema(self) -> None:
def _check_table_schema(self, schema: TableSchema) -> None:
table = Table(schema.name, g_metadata)
expected_columns = set(
schema.time_array_id_columns
+ schema.time_config.time_columns
+ schema.value_columns
schema.time_array_id_columns + schema.time_config.time_columns + schema.value_columns
)
existing_columns = {x.name for x in table.columns}
diff = expected_columns - existing_columns
Expand Down
29 changes: 18 additions & 11 deletions src/chronify/time.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ class RepresentativePeriodFormat(StrEnum):
# number of permutations (seasons, weekend day vs week day, sub-hour time, etc).

ONE_WEEK_PER_MONTH_BY_HOUR = "one_week_per_month_by_hour"
ONE_WEEKDAY_DAY_AND_ONE_WEEKEND_DAY_PER_MONTH_BY_HOUR = "one_weekday_day_and_one_weekend_day_per_month_by_hour",
ONE_WEEKDAY_DAY_AND_ONE_WEEKEND_DAY_PER_MONTH_BY_HOUR = (
"one_weekday_day_and_one_weekend_day_per_month_by_hour",
)


class LeapDayAdjustmentType(StrEnum):
Expand Down Expand Up @@ -63,14 +65,14 @@ class TimeIntervalType(StrEnum):
# https://github.com/Smart-DS/R2PD/blob/master/R2PD/tshelpers.py#L15

PERIOD_ENDING = "period_ending"
#description="A time interval that is period ending is coded by the end time. E.g., 2pm (with"
#" freq=1h) represents a period of time between 1-2pm.",
# description="A time interval that is period ending is coded by the end time. E.g., 2pm (with"
# " freq=1h) represents a period of time between 1-2pm.",
PERIOD_BEGINNING = "period_beginning"
#description="A time interval that is period beginning is coded by the beginning time. E.g.,"
#" 2pm (with freq=01:00:00) represents a period of time between 2-3pm. This is the dsgrid"
#" default.",
# description="A time interval that is period beginning is coded by the beginning time. E.g.,"
# " 2pm (with freq=01:00:00) represents a period of time between 2-3pm. This is the dsgrid"
# " default.",
INSTANTANEOUS = "instantaneous"
#description="The time record value represents measured, instantaneous time",
# description="The time record value represents measured, instantaneous time",


class MeasurementType(StrEnum):
Expand All @@ -80,9 +82,9 @@ class MeasurementType(StrEnum):
MIN = "min"
MAX = "max"
MEASURED = "measured"
#description="Data values represent the measured value at that reported time",
# description="Data values represent the measured value at that reported time",
TOTAL = "total"
#description="Data values represent the sum of values in a time range",
# description="Data values represent the sum of values in a time range",


class TimeZone(StrEnum):
Expand Down Expand Up @@ -162,7 +164,9 @@ def get_standard_time(tz: TimeZone) -> TimeZone:
case TimeZone.ARIZONA:
return TimeZone.ARIZONA
case _:
raise NotImplementedError(f"BUG: case not covered: {tz}")
msg = f"BUG: case not covered: {tz}"
raise NotImplementedError(msg)


def get_prevailing_time(tz: TimeZone) -> TimeZone:
"""get equivalent prevailing time"""
Expand All @@ -184,7 +188,9 @@ def get_prevailing_time(tz: TimeZone) -> TimeZone:
case TimeZone.ARIZONA:
return TimeZone.ARIZONA
case _:
raise NotImplementedError(f"BUG: case not covered: {tz}")
msg = f"BUG: case not covered: {tz}"
raise NotImplementedError(msg)


_STANDARD_TIME_ZONES = {
TimeZone.UTC,
Expand All @@ -205,6 +211,7 @@ def get_prevailing_time(tz: TimeZone) -> TimeZone:
TimeZone.ARIZONA,
}


def is_standard(tz: TimeZone) -> bool:
return tz in _STANDARD_TIME_ZONES

Expand Down
115 changes: 52 additions & 63 deletions src/chronify/time_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@ class LocalTimeAsStrings(BaseModel):
They are aligned for each geography when adjusted for time zone but staggered
in an absolute time scale."""

format_type: Literal[DatetimeFormat.LOCAL_AS_STRINGS] = (
DatetimeFormat.LOCAL_AS_STRINGS
)
format_type: Literal[DatetimeFormat.LOCAL_AS_STRINGS] = DatetimeFormat.LOCAL_AS_STRINGS

data_str_format: Annotated[
str,
Expand All @@ -70,23 +68,21 @@ class LocalTimeAsStrings(BaseModel):
),
]

@field_validator("data_str_format")
@classmethod
def check_data_str_format(cls, data_str_format):
raise NotImplementedError(
"DatetimeFormat.LOCAL_AS_STRINGS is not fully implemented."
)
dsf = data_str_format
if (
"x" not in dsf
and "X" not in dsf
and "Z" not in dsf
and "z" not in dsf
and "V" not in dsf
and "O" not in dsf
):
raise ValueError("data_str_format must provide time zone or zone offset.")
return data_str_format
# @field_validator("data_str_format")
# @classmethod
# def check_data_str_format(cls, data_str_format):
# raise NotImplementedError("DatetimeFormat.LOCAL_AS_STRINGS is not fully implemented.")
# dsf = data_str_format
# if (
# "x" not in dsf
# and "X" not in dsf
# and "Z" not in dsf
# and "z" not in dsf
# and "V" not in dsf
# and "O" not in dsf
# ):
# raise ValueError("data_str_format must provide time zone or zone offset.")
# return data_str_format


class DaylightSavingAdjustment(BaseModel):
Expand Down Expand Up @@ -199,9 +195,7 @@ def iter_timestamps(self) -> Generator[datetime, None, None]:
tz_info = self.start.tzinfo
for i in range(self.length):
cur = self.start.astimezone(ZoneInfo("UTC")) + i * self.frequency
cur = adjust_timestamp_by_dst_offset(
cur.astimezone(tz_info), self.frequency
)
cur = adjust_timestamp_by_dst_offset(cur.astimezone(tz_info), self.frequency)
month = cur.month
day = cur.day
if not (
Expand Down Expand Up @@ -238,7 +232,6 @@ def iter_timestamps(self) -> Generator[int, None, None]:


class IndexTimeRange(TimeBaseModel):

time_type: Literal[TimeType.INDEX] = TimeType.INDEX
start: int
frequency: timedelta
Expand All @@ -248,39 +241,39 @@ class IndexTimeRange(TimeBaseModel):
measurement_type: MeasurementType

# TODO DT: totally wrong
def iter_timestamps(self) -> Generator[datetime, None, None]:
cur = self.start.to_pydatetime().astimezone(ZoneInfo("UTC"))
cur_idx = self.start_index
end = (
self.end.to_pydatetime().astimezone(ZoneInfo("UTC")) + self.frequency
) # to make end time inclusive

while cur < end:
cur_tz = cur.astimezone(self.tzinfo)
cur_tz = adjust_timestamp_by_dst_offset(cur_tz, self.frequency)
month = cur_tz.month
day = cur_tz.day
if not (
self.time_based_data_adjustment.leap_day_adjustment
== LeapDayAdjustmentType.DROP_FEB29
and month == 2
and day == 29
):
if not (
self.time_based_data_adjustment.leap_day_adjustment
== LeapDayAdjustmentType.DROP_DEC31
and month == 12
and day == 31
):
if not (
self.time_based_data_adjustment.leap_day_adjustment
== LeapDayAdjustmentType.DROP_JAN1
and month == 1
and day == 1
):
yield cur_idx
cur += self.frequency
cur_idx += 1
# def iter_timestamps(self) -> Generator[datetime, None, None]:
# cur = self.start.to_pydatetime().astimezone(ZoneInfo("UTC"))
# cur_idx = self.start_index
# end = (
# self.end.to_pydatetime().astimezone(ZoneInfo("UTC")) + self.frequency
# ) # to make end time inclusive

# while cur < end:
# cur_tz = cur.astimezone(self.tzinfo)
# cur_tz = adjust_timestamp_by_dst_offset(cur_tz, self.frequency)
# month = cur_tz.month
# day = cur_tz.day
# if not (
# self.time_based_data_adjustment.leap_day_adjustment
# == LeapDayAdjustmentType.DROP_FEB29
# and month == 2
# and day == 29
# ):
# if not (
# self.time_based_data_adjustment.leap_day_adjustment
# == LeapDayAdjustmentType.DROP_DEC31
# and month == 12
# and day == 31
# ):
# if not (
# self.time_based_data_adjustment.leap_day_adjustment
# == LeapDayAdjustmentType.DROP_JAN1
# and month == 1
# and day == 1
# ):
# yield cur_idx
# cur += self.frequency
# cur_idx += 1


class RepresentativePeriodTimeRange(TimeBaseModel):
Expand All @@ -293,19 +286,15 @@ class RepresentativePeriodTimeRange(TimeBaseModel):


TimeConfig = Annotated[
Union[
AnnualTimeRange, DatetimeRange, IndexTimeRange, RepresentativePeriodTimeRange
],
Union[AnnualTimeRange, DatetimeRange, IndexTimeRange, RepresentativePeriodTimeRange],
Field(
description="Defines the times in a time series table.",
discriminator="time_type",
),
]


def adjust_timestamp_by_dst_offset(
timestamp: datetime, frequency: timedelta
) -> datetime:
def adjust_timestamp_by_dst_offset(timestamp: datetime, frequency: timedelta) -> datetime:
"""Reduce the timestamps within the daylight saving range by 1 hour.
Used to ensure that a time series at daily (or lower) frequency returns each day at the
same timestamp in prevailing time, an expected behavior in most standard libraries.
Expand Down
6 changes: 4 additions & 2 deletions src/chronify/time_series_checker.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sqlalchemy import Connection, Engine, text

from chronify import TableSchema
from chronify.exceptions import InvalidTable
from chronify.models import TableSchema


class TimeSeriesChecker:
Expand Down Expand Up @@ -62,5 +62,7 @@ def _run_timestamp_checks_on_tmp_table(schema: TableSchema, conn: Connection, ta
result3 = conn.execute(text(query3)).all()
actual_count = result3[0][0]
if actual_count != schema.time_config.length:
msg = "Time arrays must have length={schema.time_config.length}. Actual = {actual_count}"
msg = (
"Time arrays must have length={schema.time_config.length}. Actual = {actual_count}"
)
raise InvalidTable(msg)
4 changes: 3 additions & 1 deletion src/chronify/time_series_mapper.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from chronify.time_configs import TimeConfig


class TimeSeriesMapper:
"""Maps time series data from one configuration to another."""

Expand All @@ -9,4 +12,3 @@ def map_time_series(
to_table: str,
) -> None:
pass

0 comments on commit b1d8d09

Please sign in to comment.