Skip to content
This repository has been archived by the owner on Sep 4, 2024. It is now read-only.

Commit

Permalink
consistent style across library
Browse files Browse the repository at this point in the history
  • Loading branch information
seekinginfiniteloop committed Dec 25, 2023
1 parent d041015 commit 7821df1
Show file tree
Hide file tree
Showing 12 changed files with 610 additions and 489 deletions.
42 changes: 21 additions & 21 deletions fedcal/_civpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
`FedPayDay` calculates and provides federal civilian biweekly paydays
and outputs them in a variety of formats.
"""

from __future__ import annotations
from typing import TYPE_CHECKING

import pandas as pd
from attrs import define, field

from fedcal import constants, time_utils

if TYPE_CHECKING:
from pandas import DatetimeIndex, Series, Timestamp


@define(order=True)
class FedPayDay:
Expand Down Expand Up @@ -65,23 +67,21 @@ class FedPayDay:
"""

reference_date: pd.Timestamp = field(
reference_date: Timestamp = field(
default=constants.FEDPAYDAY_REFERENCE_DATE,
converter=time_utils.to_timestamp,
)
end_date: pd.Timestamp = field(
end_date: Timestamp = field(
default=pd.Timestamp(year=2040, month=9, day=30),
converter=time_utils.to_timestamp,
)

paydays: pd.DatetimeIndex = field(init=False)
paydays: DatetimeIndex = field(init=False)

def __attrs_post_init__(self) -> None:
self.paydays: pd.DatetimeIndex = self._generate_paydays()
self.paydays: DatetimeIndex = self._generate_paydays()

def _generate_paydays(
self, end_date: pd.Timestamp | None = None
) -> pd.DatetimeIndex:
def _generate_paydays(self, end_date: Timestamp | None = None) -> DatetimeIndex:
"""
Generates federal paydays between the reference date and the specified
end date.
Expand All @@ -92,15 +92,15 @@ def _generate_paydays(
Returns
-------
pd.DatetimeIndex of generated paydays.
DatetimeIndex of generated paydays.
"""
end_date: pd.Timestamp = end_date or self.end_date
end_date: Timestamp = end_date or self.end_date
if end_date > self.reference_date:
return pd.date_range(start=self.reference_date, end=end_date, freq="2W-FRI")
raise ValueError("fedcal only supports dates after 1970-1-1.")

def is_fed_payday(self, date: pd.Timestamp | None = None) -> bool:
def is_fed_payday(self, date: Timestamp | None = None) -> bool:
"""
Checks if a given date is a federal payday.
Expand All @@ -120,9 +120,9 @@ def is_fed_payday(self, date: pd.Timestamp | None = None) -> bool:

def get_paydays_as_index(
self,
start: pd.Timestamp | None = None,
end: pd.Timestamp | None = None,
) -> pd.DatetimeIndex:
start: Timestamp | None = None,
end: Timestamp | None = None,
) -> DatetimeIndex:
"""
Returns a DatetimeIndex of paydays between the start and end dates.
Expand All @@ -145,9 +145,9 @@ def get_paydays_as_index(

def get_paydays_as_list(
self,
start: pd.Timestamp | None = None,
end: pd.Timestamp | None = None,
) -> list[pd.Timestamp]:
start: Timestamp | None = None,
end: Timestamp | None = None,
) -> list[Timestamp]:
"""
Returns a list of federal paydays between the start and end dates.
Expand All @@ -167,9 +167,9 @@ def get_paydays_as_list(

def get_paydays_as_series(
self,
start: pd.Timestamp | None = None,
end: pd.Timestamp | None = None,
) -> pd.Series:
start: Timestamp | None = None,
end: Timestamp | None = None,
) -> Series:
"""
Returns a list of federal paydays between the start and end dates.
Expand Down
113 changes: 102 additions & 11 deletions fedcal/_cls_utils.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,111 @@
from __future__ import annotations
# fedcal _cls_utils.py
#
# Copyright (c) 2023 Adam Poulemanos. All rights reserved.
#
# fedcal is open source software subject to the terms of the
# MIT license, found in the
# [GitHub source directory](https://github.com/psuedomagi/fedcal)
# in the LICENSE.md file.
#
# It may be freely distributed, reused, modified, and distributed under the
# terms of that license, but must be accompanied by the license and the
# accompanying copyright notice.

from funcy import decorator
"""
The private _cls_utils module provides supporting utilities to add
functionality for fedcal classes and API experience. Not yet implemented;
includes SeriesExporter and to_series, a class and decorator for
converting numpy arrays and pandas indexes to pandas Series with the
datetimeindex of the function/class as its index.
Where we planned to use this, the output is currently in property methods
(i.e. FedIndex); which interferes with the wrapper and we probably need to
rethink the approach.
"""

from typing import TYPE_CHECKING

import numpy as np
import pandas as pd
from attr import define, field
from typing import TYPE_CHECKING, Any
from funcy import decorator

from fedcal._meta import MagicDelegator

if TYPE_CHECKING:
import numpy as np
from typing import Any

from numpy.typing import NDArray
from pandas import DatetimeIndex, Series


class NPArrayImposter(
metaclass=MagicDelegator, delegate_to="array", delegate_class=np.ndarray
):
def __init__(
self,
array: NDArray | None = None,
datetimeindex: DatetimeIndex | None = None,
) -> None:
self.array: NDArray = array or np.array(object=[])
self.datetimeindex: DatetimeIndex | None = datetimeindex or None

def __getattr__(self, name: str) -> Any:
"""
Delegates attribute access to the pdtimestamp attribute. This lets
FedStamp objects use any methods/attributes of Timestamp.
Parameters
----------
name : The name of the attribute to retrieve.
Returns
-------
The value of the attribute.
"""

# this shouldn't be necessary, but... seems to be until I can debug
if name in self.__class__.__dict__:
return self.__class__.__dict__[name].__get__(self, self.__class__)

if hasattr(self.array, name):
return getattr(self.array, name)
raise AttributeError(
f"'{type(self).__name__}' object has no attribute '{name}'"
)

def __getattribute__(self, name: str) -> Any:
"""
We set __getattribute__ manually to ensure it overrides
any delegation to pd.Timestamp from our metaclass.
(It shouldn't, I know, but I swear it was. Requires further
investigation.)
Parameters
----------
name
name of attribute
Returns
-------
attribute if found.
"""
return object.__getattribute__(self, name)

def to_series(self, name: str = None, dtype: Any = None) -> Series:
if not hasattr(self.array, "__iter__"):
self.array = [self.array]
return pd.Series(
data=self.array, index=self.datetimeindex, name=name, dtype=dtype
)


@define()
class SeriesExporter:
array: "np.ndarray" | pd.Index | pd.PeriodIndex | pd.Series | None = field()
datetimeindex: pd.DatetimeIndex | None = field(default=None)
array: NDArray | pd.Index | pd.PeriodIndex | pd.Series | None = field()
datetimeindex: DatetimeIndex | None = field(default=None)

def array_to_series(self, name: str = None, dtype: Any = None) -> pd.Series:
def array_to_series(self, name: str = None, dtype: Any = None) -> Series:
if not hasattr(self.array, "__iter__"):
self.array = [self.array]
return pd.Series(
Expand All @@ -25,12 +116,12 @@ def array_to_series(self, name: str = None, dtype: Any = None) -> pd.Series:
@decorator
def to_series(
call, to_series: bool = False, name: str = None, dtype: Any = None
) -> pd.Series | "np.ndarray" | pd.Index | pd.PeriodIndex:
) -> Series | np.ndarray | pd.Index | pd.PeriodIndex:
result: "np.ndarray" = call()

def get_datetimeindex() -> pd.DatetimeIndex | None:
def fetch_datetimeindex() -> DatetimeIndex | None:
for source in [call._kwargs, call._locals, getattr(call._func, "self", None)]:
datetimeindex: pd.DatetimeIndex = source.get("datetimeindex") or source.get(
datetimeindex: DatetimeIndex = source.get("datetimeindex") or source.get(
"dates"
)
if isinstance(datetimeindex, pd.DatetimeIndex):
Expand All @@ -45,7 +136,7 @@ def get_datetimeindex() -> pd.DatetimeIndex | None:
return attr

if to_series:
datetimeindex: pd.DatetimeIndex | None = get_datetimeindex()
datetimeindex: DatetimeIndex | None = fetch_datetimeindex()
exporter = SeriesExporter(array=result, datetimeindex=datetimeindex)
return exporter.array_to_series(name=name, dtype=dtype)
return result
Loading

0 comments on commit 7821df1

Please sign in to comment.