global: snapshot

This commit is contained in:
nym21
2026-02-24 12:21:20 +01:00
parent 3b7aa8242a
commit cefc8cfd42
24 changed files with 3561 additions and 1073 deletions
+218 -145
View File
@@ -3,7 +3,7 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import TypeVar, Generic, Any, Optional, List, Literal, TypedDict, Union, Protocol, overload, Iterator, Tuple, TYPE_CHECKING
from typing import TypeVar, Generic, Any, Dict, Optional, List, Iterator, Literal, TypedDict, Union, Protocol, overload, Tuple, TYPE_CHECKING
from http.client import HTTPSConnection, HTTPConnection
from urllib.parse import urlparse
from datetime import date, datetime, timedelta, timezone
@@ -1085,16 +1085,16 @@ class BrkClientBase:
"""Make a GET request and return text."""
return self.get(path).decode()
def close(self):
def close(self) -> None:
"""Close the HTTP client."""
if self._conn:
self._conn.close()
self._conn = None
def __enter__(self):
def __enter__(self) -> BrkClientBase:
return self
def __exit__(self, exc_type, exc_val, exc_tb):
def __exit__(self, exc_type: Optional[type], exc_val: Optional[BaseException], exc_tb: Optional[Any]) -> None:
self.close()
@@ -1199,7 +1199,7 @@ def _date_to_index(index: str, d: Union[date, datetime]) -> int:
@dataclass
class MetricData(Generic[T]):
"""Metric data with range information."""
"""Metric data with range information. Always int-indexed."""
version: int
index: Index
total: int
@@ -1213,63 +1213,96 @@ class MetricData(Generic[T]):
"""Whether this metric uses a date-based index."""
return self.index in _DATE_INDEXES
def dates(self) -> list:
"""Get dates for the index range. Date-based indexes only, throws otherwise."""
return [_index_to_date(self.index, i) for i in range(self.start, self.end)]
def indexes(self) -> List[int]:
"""Get raw index numbers."""
return list(range(self.start, self.end))
def keys(self) -> list:
"""Get keys: dates for date-based indexes, index numbers otherwise."""
return self.dates() if self.is_date_based else self.indexes()
def keys(self) -> List[int]:
"""Get keys as index numbers."""
return self.indexes()
def items(self) -> list:
"""Get (key, value) pairs: keys are dates for date-based, numbers otherwise."""
return list(zip(self.keys(), self.data))
def items(self) -> List[Tuple[int, T]]:
"""Get (index, value) pairs."""
return list(zip(self.indexes(), self.data))
def to_dict(self) -> dict:
"""Return {key: value} dict: keys are dates for date-based, numbers otherwise."""
return dict(zip(self.keys(), self.data))
def to_dict(self) -> Dict[int, T]:
"""Return {index: value} dict."""
return dict(zip(self.indexes(), self.data))
def __iter__(self):
"""Iterate over (key, value) pairs. Keys are dates for date-based, numbers otherwise."""
return iter(zip(self.keys(), self.data))
def __iter__(self) -> Iterator[Tuple[int, T]]:
"""Iterate over (index, value) pairs."""
return iter(zip(self.indexes(), self.data))
def __len__(self) -> int:
return len(self.data)
def to_polars(self) -> pl.DataFrame:
"""Convert to Polars DataFrame with 'index' and 'value' columns."""
try:
import polars as pl # type: ignore[import-not-found]
except ImportError:
raise ImportError("polars is required: pip install polars")
return pl.DataFrame({"index": self.indexes(), "value": self.data})
def to_pandas(self) -> pd.DataFrame:
"""Convert to Pandas DataFrame with 'index' and 'value' columns."""
try:
import pandas as pd # type: ignore[import-not-found]
except ImportError:
raise ImportError("pandas is required: pip install pandas")
return pd.DataFrame({"index": self.indexes(), "value": self.data})
@dataclass
class DateMetricData(MetricData[T]):
"""Metric data with date-based index. Extends MetricData with date methods."""
def dates(self) -> List[Union[date, datetime]]:
"""Get dates for the index range. Returns datetime for sub-daily indexes, date for daily+."""
return [_index_to_date(self.index, i) for i in range(self.start, self.end)]
def date_items(self) -> List[Tuple[Union[date, datetime], T]]:
"""Get (date, value) pairs."""
return list(zip(self.dates(), self.data))
def to_date_dict(self) -> Dict[Union[date, datetime], T]:
"""Return {date: value} dict."""
return dict(zip(self.dates(), self.data))
def to_polars(self, with_dates: bool = True) -> pl.DataFrame:
"""Convert to Polars DataFrame. Requires polars to be installed.
"""Convert to Polars DataFrame.
Returns a DataFrame with columns:
- 'date' and 'value' if with_dates=True and index is date-based
- 'date' and 'value' if with_dates=True (default)
- 'index' and 'value' otherwise
"""
try:
import polars as pl # type: ignore[import-not-found]
except ImportError:
raise ImportError("polars is required: pip install polars")
if with_dates and self.is_date_based:
if with_dates:
return pl.DataFrame({"date": self.dates(), "value": self.data})
return pl.DataFrame({"index": self.indexes(), "value": self.data})
def to_pandas(self, with_dates: bool = True) -> pd.DataFrame:
"""Convert to Pandas DataFrame. Requires pandas to be installed.
"""Convert to Pandas DataFrame.
Returns a DataFrame with columns:
- 'date' and 'value' if with_dates=True and index is date-based
- 'date' and 'value' if with_dates=True (default)
- 'index' and 'value' otherwise
"""
try:
import pandas as pd # type: ignore[import-not-found]
except ImportError:
raise ImportError("pandas is required: pip install pandas")
if with_dates and self.is_date_based:
if with_dates:
return pd.DataFrame({"date": self.dates(), "value": self.data})
return pd.DataFrame({"index": self.indexes(), "value": self.data})
# Type alias for non-generic usage
# Type aliases for non-generic usage
AnyMetricData = MetricData[Any]
AnyDateMetricData = DateMetricData[Any]
class _EndpointConfig:
@@ -1303,9 +1336,15 @@ class _EndpointConfig:
p = self.path()
return f"{p}?{query}" if query else p
def get_metric(self) -> MetricData:
def _new(self, start: Optional[int] = None, end: Optional[int] = None) -> _EndpointConfig:
return _EndpointConfig(self.client, self.name, self.index, start, end)
def get_metric(self) -> MetricData[Any]:
return MetricData(**self.client.get_json(self._build_path()))
def get_date_metric(self) -> DateMetricData[Any]:
return DateMetricData(**self.client.get_json(self._build_path()))
def get_csv(self) -> str:
return self.client.get_text(self._build_path(format='csv'))
@@ -1349,10 +1388,7 @@ class SkippedBuilder(Generic[T]):
def take(self, n: int) -> RangeBuilder[T]:
"""Take n items after the skipped position."""
start = self._config.start or 0
return RangeBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
start, start + n
))
return RangeBuilder(self._config._new(start, start + n))
def fetch(self) -> MetricData[T]:
"""Fetch from skipped position to end."""
@@ -1363,29 +1399,35 @@ class SkippedBuilder(Generic[T]):
return self._config.get_csv()
class MetricEndpointBuilder(Generic[T]):
"""Builder for metric endpoint queries.
class DateRangeBuilder(RangeBuilder[T]):
"""Range builder that returns DateMetricData."""
def fetch(self) -> DateMetricData[T]:
return self._config.get_date_metric()
Use method chaining to specify the data range, then call fetch() or fetch_csv() to execute.
class DateSingleItemBuilder(SingleItemBuilder[T]):
"""Single item builder that returns DateMetricData."""
def fetch(self) -> DateMetricData[T]:
return self._config.get_date_metric()
class DateSkippedBuilder(SkippedBuilder[T]):
"""Skipped builder that returns DateMetricData."""
def take(self, n: int) -> DateRangeBuilder[T]:
start = self._config.start or 0
return DateRangeBuilder(self._config._new(start, start + n))
def fetch(self) -> DateMetricData[T]:
return self._config.get_date_metric()
class MetricEndpointBuilder(Generic[T]):
"""Builder for metric endpoint queries with int-based indexing.
Examples:
# Fetch all data
data = endpoint.fetch()
# Single item access
data = endpoint[5].fetch()
# Slice syntax (Python-native)
data = endpoint[:10].fetch() # First 10
data = endpoint[-5:].fetch() # Last 5
data = endpoint[100:110].fetch() # Range
# Convenience methods (pandas-style)
data = endpoint.head().fetch() # First 10 (default)
data = endpoint.head(20).fetch() # First 20
data = endpoint.tail(5).fetch() # Last 5
# Iterator-style chaining
data = endpoint[:10].fetch()
data = endpoint.head(20).fetch()
data = endpoint.skip(100).take(10).fetch()
"""
@@ -1397,66 +1439,30 @@ class MetricEndpointBuilder(Generic[T]):
@overload
def __getitem__(self, key: slice) -> RangeBuilder[T]: ...
def __getitem__(self, key: Union[int, slice, date, datetime]) -> Union[SingleItemBuilder[T], RangeBuilder[T]]:
"""Access single item or slice. Accepts dates for date-based indexes.
Examples:
endpoint[5] # Single item at index 5
endpoint[:10] # First 10
endpoint[-5:] # Last 5
endpoint[100:110] # Range 100-109
endpoint[date(2020, 1, 1):date(2023, 1, 1)] # Date range
endpoint[date(2020, 1, 1):] # Since date
"""
if isinstance(key, (date, datetime)):
idx = _date_to_index(self._config.index, key)
return SingleItemBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
idx, idx + 1
))
def __getitem__(self, key: Union[int, slice]) -> Union[SingleItemBuilder[T], RangeBuilder[T]]:
"""Access single item or slice by integer index."""
if isinstance(key, int):
return SingleItemBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
key, key + 1
))
start, stop = key.start, key.stop
if isinstance(start, (date, datetime)):
start = _date_to_index(self._config.index, start)
if isinstance(stop, (date, datetime)):
stop = _date_to_index(self._config.index, stop)
return RangeBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
start, stop
))
return SingleItemBuilder(self._config._new(key, key + 1))
return RangeBuilder(self._config._new(key.start, key.stop))
def head(self, n: int = 10) -> RangeBuilder[T]:
"""Get the first n items (pandas-style)."""
return RangeBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
None, n
))
"""Get the first n items."""
return RangeBuilder(self._config._new(end=n))
def tail(self, n: int = 10) -> RangeBuilder[T]:
"""Get the last n items (pandas-style)."""
start, end = (None, 0) if n == 0 else (-n, None)
return RangeBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
start, end
))
"""Get the last n items."""
return RangeBuilder(self._config._new(end=0) if n == 0 else self._config._new(start=-n))
def skip(self, n: int) -> SkippedBuilder[T]:
"""Skip the first n items. Chain with take() to get a range."""
return SkippedBuilder(_EndpointConfig(
self._config.client, self._config.name, self._config.index,
n, None
))
"""Skip the first n items."""
return SkippedBuilder(self._config._new(start=n))
def fetch(self) -> MetricData[T]:
"""Fetch all data as parsed JSON."""
"""Fetch all data."""
return self._config.get_metric()
def fetch_csv(self) -> str:
"""Fetch all data as CSV string."""
"""Fetch all data as CSV."""
return self._config.get_csv()
def path(self) -> str:
@@ -1464,8 +1470,72 @@ class MetricEndpointBuilder(Generic[T]):
return self._config.path()
# Type alias for non-generic usage
class DateMetricEndpointBuilder(Generic[T]):
"""Builder for metric endpoint queries with date-based indexing.
Accepts dates in __getitem__ and returns DateMetricData from fetch().
Examples:
data = endpoint.fetch()
data = endpoint[date(2020, 1, 1)].fetch()
data = endpoint[date(2020, 1, 1):date(2023, 1, 1)].fetch()
data = endpoint[:10].fetch()
"""
def __init__(self, client: BrkClientBase, name: str, index: Index):
self._config = _EndpointConfig(client, name, index)
@overload
def __getitem__(self, key: int) -> DateSingleItemBuilder[T]: ...
@overload
def __getitem__(self, key: datetime) -> DateSingleItemBuilder[T]: ...
@overload
def __getitem__(self, key: date) -> DateSingleItemBuilder[T]: ...
@overload
def __getitem__(self, key: slice) -> DateRangeBuilder[T]: ...
def __getitem__(self, key: Union[int, slice, date, datetime]) -> Union[DateSingleItemBuilder[T], DateRangeBuilder[T]]:
"""Access single item or slice. Accepts int, date, or datetime."""
if isinstance(key, (date, datetime)):
idx = _date_to_index(self._config.index, key)
return DateSingleItemBuilder(self._config._new(idx, idx + 1))
if isinstance(key, int):
return DateSingleItemBuilder(self._config._new(key, key + 1))
start, stop = key.start, key.stop
if isinstance(start, (date, datetime)):
start = _date_to_index(self._config.index, start)
if isinstance(stop, (date, datetime)):
stop = _date_to_index(self._config.index, stop)
return DateRangeBuilder(self._config._new(start, stop))
def head(self, n: int = 10) -> DateRangeBuilder[T]:
"""Get the first n items."""
return DateRangeBuilder(self._config._new(end=n))
def tail(self, n: int = 10) -> DateRangeBuilder[T]:
"""Get the last n items."""
return DateRangeBuilder(self._config._new(end=0) if n == 0 else self._config._new(start=-n))
def skip(self, n: int) -> DateSkippedBuilder[T]:
"""Skip the first n items."""
return DateSkippedBuilder(self._config._new(start=n))
def fetch(self) -> DateMetricData[T]:
"""Fetch all data."""
return self._config.get_date_metric()
def fetch_csv(self) -> str:
"""Fetch all data as CSV."""
return self._config.get_csv()
def path(self) -> str:
"""Get the base endpoint path."""
return self._config.path()
# Type aliases for non-generic usage
AnyMetricEndpointBuilder = MetricEndpointBuilder[Any]
AnyDateMetricEndpointBuilder = DateMetricEndpointBuilder[Any]
class MetricPattern(Protocol[T]):
@@ -1527,25 +1597,28 @@ _i37 = ('emptyaddressindex',)
def _ep(c: BrkClientBase, n: str, i: Index) -> MetricEndpointBuilder[Any]:
return MetricEndpointBuilder(c, n, i)
def _dep(c: BrkClientBase, n: str, i: Index) -> DateMetricEndpointBuilder[Any]:
return DateMetricEndpointBuilder(c, n, i)
# Index accessor classes
class _MetricPattern1By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def minute1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute1')
def minute5(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute5')
def minute10(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute10')
def minute30(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute30')
def hour1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour1')
def hour4(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour4')
def hour12(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour12')
def day1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'day1')
def day3(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'day3')
def week1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'week1')
def month1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month1')
def month3(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month3')
def month6(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month6')
def year1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'year1')
def year10(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'year10')
def minute1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute1')
def minute5(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute5')
def minute10(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute10')
def minute30(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute30')
def hour1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour1')
def hour4(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour4')
def hour12(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour12')
def day1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'day1')
def day3(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'day3')
def week1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'week1')
def month1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month1')
def month3(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month3')
def month6(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month6')
def year1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'year1')
def year10(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'year10')
def halvingepoch(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'halvingepoch')
def difficultyepoch(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'difficultyepoch')
def height(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'height')
@@ -1560,21 +1633,21 @@ class MetricPattern1(Generic[T]):
class _MetricPattern2By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def minute1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute1')
def minute5(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute5')
def minute10(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute10')
def minute30(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute30')
def hour1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour1')
def hour4(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour4')
def hour12(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour12')
def day1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'day1')
def day3(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'day3')
def week1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'week1')
def month1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month1')
def month3(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month3')
def month6(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month6')
def year1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'year1')
def year10(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'year10')
def minute1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute1')
def minute5(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute5')
def minute10(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute10')
def minute30(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute30')
def hour1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour1')
def hour4(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour4')
def hour12(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour12')
def day1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'day1')
def day3(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'day3')
def week1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'week1')
def month1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month1')
def month3(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month3')
def month6(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month6')
def year1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'year1')
def year10(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'year10')
def halvingepoch(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'halvingepoch')
def difficultyepoch(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'difficultyepoch')
@@ -1588,7 +1661,7 @@ class MetricPattern2(Generic[T]):
class _MetricPattern3By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def minute1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute1')
def minute1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute1')
class MetricPattern3(Generic[T]):
by: _MetricPattern3By[T]
@@ -1600,7 +1673,7 @@ class MetricPattern3(Generic[T]):
class _MetricPattern4By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def minute5(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute5')
def minute5(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute5')
class MetricPattern4(Generic[T]):
by: _MetricPattern4By[T]
@@ -1612,7 +1685,7 @@ class MetricPattern4(Generic[T]):
class _MetricPattern5By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def minute10(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute10')
def minute10(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute10')
class MetricPattern5(Generic[T]):
by: _MetricPattern5By[T]
@@ -1624,7 +1697,7 @@ class MetricPattern5(Generic[T]):
class _MetricPattern6By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def minute30(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'minute30')
def minute30(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'minute30')
class MetricPattern6(Generic[T]):
by: _MetricPattern6By[T]
@@ -1636,7 +1709,7 @@ class MetricPattern6(Generic[T]):
class _MetricPattern7By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def hour1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour1')
def hour1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour1')
class MetricPattern7(Generic[T]):
by: _MetricPattern7By[T]
@@ -1648,7 +1721,7 @@ class MetricPattern7(Generic[T]):
class _MetricPattern8By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def hour4(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour4')
def hour4(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour4')
class MetricPattern8(Generic[T]):
by: _MetricPattern8By[T]
@@ -1660,7 +1733,7 @@ class MetricPattern8(Generic[T]):
class _MetricPattern9By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def hour12(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'hour12')
def hour12(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'hour12')
class MetricPattern9(Generic[T]):
by: _MetricPattern9By[T]
@@ -1672,7 +1745,7 @@ class MetricPattern9(Generic[T]):
class _MetricPattern10By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def day1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'day1')
def day1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'day1')
class MetricPattern10(Generic[T]):
by: _MetricPattern10By[T]
@@ -1684,7 +1757,7 @@ class MetricPattern10(Generic[T]):
class _MetricPattern11By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def day3(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'day3')
def day3(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'day3')
class MetricPattern11(Generic[T]):
by: _MetricPattern11By[T]
@@ -1696,7 +1769,7 @@ class MetricPattern11(Generic[T]):
class _MetricPattern12By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def week1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'week1')
def week1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'week1')
class MetricPattern12(Generic[T]):
by: _MetricPattern12By[T]
@@ -1708,7 +1781,7 @@ class MetricPattern12(Generic[T]):
class _MetricPattern13By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def month1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month1')
def month1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month1')
class MetricPattern13(Generic[T]):
by: _MetricPattern13By[T]
@@ -1720,7 +1793,7 @@ class MetricPattern13(Generic[T]):
class _MetricPattern14By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def month3(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month3')
def month3(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month3')
class MetricPattern14(Generic[T]):
by: _MetricPattern14By[T]
@@ -1732,7 +1805,7 @@ class MetricPattern14(Generic[T]):
class _MetricPattern15By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def month6(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'month6')
def month6(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'month6')
class MetricPattern15(Generic[T]):
by: _MetricPattern15By[T]
@@ -1744,7 +1817,7 @@ class MetricPattern15(Generic[T]):
class _MetricPattern16By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def year1(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'year1')
def year1(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'year1')
class MetricPattern16(Generic[T]):
by: _MetricPattern16By[T]
@@ -1756,7 +1829,7 @@ class MetricPattern16(Generic[T]):
class _MetricPattern17By(Generic[T]):
def __init__(self, c: BrkClientBase, n: str): self._c, self._n = c, n
def year10(self) -> MetricEndpointBuilder[T]: return _ep(self._c, self._n, 'year10')
def year10(self) -> DateMetricEndpointBuilder[T]: return _dep(self._c, self._n, 'year10')
class MetricPattern17(Generic[T]):
by: _MetricPattern17By[T]
+404 -162
View File
@@ -1,18 +1,20 @@
# Tests for MetricData helper methods including polars/pandas conversion
# Tests for MetricData and DateMetricData helper methods including polars/pandas conversion
# Run: uv run pytest tests/test_metric_data.py -v
from datetime import date, datetime, timezone
from datetime import date, datetime, timezone, timedelta
import pytest
from brk_client import MetricData
from brk_client import MetricData, DateMetricData
# ============ Fixtures ============
# Test data fixtures
@pytest.fixture
def date_based_metric():
"""MetricData with day1 (date-based)."""
return MetricData(
def day1_metric():
"""DateMetricData with day1 (date-based, daily)."""
return DateMetricData(
version=1,
index="day1",
total=100,
@@ -24,7 +26,7 @@ def date_based_metric():
@pytest.fixture
def height_based_metric():
def height_metric():
"""MetricData with height (non-date-based)."""
return MetricData(
version=1,
@@ -38,9 +40,9 @@ def height_based_metric():
@pytest.fixture
def month_based_metric():
"""MetricData with month1."""
return MetricData(
def month1_metric():
"""DateMetricData with month1."""
return DateMetricData(
version=1,
index="month1",
total=200,
@@ -51,103 +53,139 @@ def month_based_metric():
)
# ============ is_date_based tests ============
@pytest.fixture
def hour1_metric():
"""DateMetricData with hour1 (sub-daily)."""
return DateMetricData(
version=1,
index="hour1",
total=200000,
start=0,
end=3,
stamp="2024-01-01T00:00:00Z",
data=[10.0, 20.0, 30.0],
)
@pytest.fixture
def minute5_metric():
"""DateMetricData with minute5 (sub-daily)."""
return DateMetricData(
version=1,
index="minute5",
total=500000,
start=0,
end=3,
stamp="2024-01-01T00:00:00Z",
data=[1, 2, 3],
)
@pytest.fixture
def week1_metric():
"""DateMetricData with week1."""
return DateMetricData(
version=1,
index="week1",
total=800,
start=0,
end=3,
stamp="2024-01-01T00:00:00Z",
data=[5, 10, 15],
)
@pytest.fixture
def year1_metric():
"""DateMetricData with year1."""
return DateMetricData(
version=1,
index="year1",
total=20,
start=0,
end=3,
stamp="2024-01-01T00:00:00Z",
data=[100, 200, 300],
)
@pytest.fixture
def day3_metric():
"""DateMetricData with day3."""
return DateMetricData(
version=1,
index="day3",
total=2000,
start=0,
end=3,
stamp="2024-01-01T00:00:00Z",
data=[7, 8, 9],
)
@pytest.fixture
def empty_metric():
"""MetricData with empty data."""
return MetricData(
version=1,
index="day1",
total=100,
start=5,
end=5,
stamp="2024-01-01T00:00:00Z",
data=[],
)
# ============ is_date_based ============
class TestIsDateBased:
"""Test the is_date_based property."""
def test_day1(self, day1_metric):
assert day1_metric.is_date_based is True
def test_day1_is_date_based(self, date_based_metric):
assert date_based_metric.is_date_based is True
def test_month1(self, month1_metric):
assert month1_metric.is_date_based is True
def test_height_is_not_date_based(self, height_based_metric):
assert height_based_metric.is_date_based is False
def test_hour1(self, hour1_metric):
assert hour1_metric.is_date_based is True
def test_month1_is_date_based(self, month_based_metric):
assert month_based_metric.is_date_based is True
def test_minute5(self, minute5_metric):
assert minute5_metric.is_date_based is True
def test_week1(self, week1_metric):
assert week1_metric.is_date_based is True
def test_year1(self, year1_metric):
assert year1_metric.is_date_based is True
def test_day3(self, day3_metric):
assert day3_metric.is_date_based is True
def test_height(self, height_metric):
assert height_metric.is_date_based is False
# ============ Date conversion tests ============
# ============ MetricData (int-indexed) ============
class TestIndexToDate:
"""Test the _index_to_date function via MetricData.dates()."""
class TestMetricData:
def test_keys(self, height_metric):
assert height_metric.keys() == [800000, 800001, 800002, 800003, 800004]
def test_day1_zero(self, date_based_metric):
"""day1 0 is genesis: Jan 3, 2009."""
dates = date_based_metric.dates()
assert dates[0] == date(2009, 1, 3)
def test_day1_one(self, date_based_metric):
"""day1 1 is Jan 9, 2009 (6 day gap after genesis)."""
dates = date_based_metric.dates()
assert dates[1] == date(2009, 1, 9)
def test_day1_two(self, date_based_metric):
"""day1 2 is Jan 10, 2009."""
dates = date_based_metric.dates()
assert dates[2] == date(2009, 1, 10)
def test_month1_dates(self, month_based_metric):
"""month1 returns correct dates."""
dates = month_based_metric.dates()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
# ============ Smart keys/items/to_dict/iter tests ============
class TestSmartHelpers:
"""Test smart MetricData helpers that auto-detect date vs numeric keys."""
def test_keys_date_based(self, date_based_metric):
"""keys() returns dates for date-based metric."""
keys = date_based_metric.keys()
assert len(keys) == 5
assert keys[0] == date(2009, 1, 3) # genesis
assert keys[1] == date(2009, 1, 9) # day1 1
def test_keys_height_based(self, height_based_metric):
"""keys() returns index numbers for non-date-based metric."""
keys = height_based_metric.keys()
assert keys == [800000, 800001, 800002, 800003, 800004]
def test_items_date_based(self, date_based_metric):
"""items() returns (date, value) pairs for date-based metric."""
items = date_based_metric.items()
assert items[0] == (date(2009, 1, 3), 100)
assert items[1] == (date(2009, 1, 9), 200)
def test_items_height_based(self, height_based_metric):
"""items() returns (index, value) pairs for height-based metric."""
items = height_based_metric.items()
def test_items(self, height_metric):
items = height_metric.items()
assert items[0] == (800000, 1.5)
assert items[4] == (800004, 5.5)
assert items[-1] == (800004, 5.5)
def test_to_dict_date_based(self, date_based_metric):
"""to_dict() returns {date: value} for date-based metric."""
d = date_based_metric.to_dict()
assert d[date(2009, 1, 3)] == 100
assert d[date(2009, 1, 9)] == 200
def test_to_dict_height_based(self, height_based_metric):
"""to_dict() returns {index: value} for height-based metric."""
d = height_based_metric.to_dict()
def test_to_dict(self, height_metric):
d = height_metric.to_dict()
assert d[800000] == 1.5
assert d[800004] == 5.5
assert len(d) == 5
def test_iter_date_based(self, date_based_metric):
"""Default iteration yields (date, value) for date-based metric."""
result = list(date_based_metric)
assert result[0] == (date(2009, 1, 3), 100)
assert result[1] == (date(2009, 1, 9), 200)
assert len(result) == 5
def test_iter_height_based(self, height_based_metric):
"""Default iteration yields (index, value) for height-based metric."""
result = list(height_based_metric)
def test_iter(self, height_metric):
result = list(height_metric)
assert result == [
(800000, 1.5),
(800001, 2.5),
@@ -156,130 +194,334 @@ class TestSmartHelpers:
(800004, 5.5),
]
def test_len(self, height_metric):
assert len(height_metric) == 5
# ============ Explicit indexes/dates tests ============
def test_indexes(self, height_metric):
assert height_metric.indexes() == [800000, 800001, 800002, 800003, 800004]
def test_empty_data(self, empty_metric):
assert len(empty_metric) == 0
assert empty_metric.keys() == []
assert empty_metric.items() == []
assert empty_metric.to_dict() == {}
assert list(empty_metric) == []
assert empty_metric.indexes() == []
class TestExplicitAccessors:
"""Test explicit indexes() and dates() methods."""
def test_indexes_returns_range(self, date_based_metric):
"""indexes() returns list of index values."""
assert date_based_metric.indexes() == [0, 1, 2, 3, 4]
def test_indexes_with_offset(self, height_based_metric):
"""indexes() respects start/end offsets."""
assert height_based_metric.indexes() == [800000, 800001, 800002, 800003, 800004]
def test_dates_raises_for_non_date(self, height_based_metric):
"""dates() raises for non-date-based index."""
with pytest.raises(ValueError):
height_based_metric.dates()
# ============ DateMetricData inheritance ============
# ============ Polars tests ============
class TestDateMetricDataInheritance:
def test_isinstance(self, day1_metric):
assert isinstance(day1_metric, DateMetricData)
assert isinstance(day1_metric, MetricData)
def test_int_keys_inherited(self, day1_metric):
assert day1_metric.keys() == [0, 1, 2, 3, 4]
def test_int_items_inherited(self, day1_metric):
items = day1_metric.items()
assert items[0] == (0, 100)
assert items[4] == (4, 500)
def test_int_iter_inherited(self, day1_metric):
result = list(day1_metric)
assert result[0] == (0, 100)
def test_len_inherited(self, day1_metric):
assert len(day1_metric) == 5
def test_indexes_inherited(self, day1_metric):
assert day1_metric.indexes() == [0, 1, 2, 3, 4]
def test_to_dict_inherited(self, day1_metric):
d = day1_metric.to_dict()
assert d[0] == 100
assert isinstance(list(d.keys())[0], int)
# ============ _index_to_date conversions ============
class TestIndexToDate:
"""Test date conversion for all index types."""
def test_day1_genesis(self, day1_metric):
"""Day1 index 0 = 2009-01-03 (genesis)."""
dates = day1_metric.dates()
assert dates[0] == date(2009, 1, 3)
def test_day1_index_one(self, day1_metric):
"""Day1 index 1 = 2009-01-09 (6-day gap after genesis)."""
dates = day1_metric.dates()
assert dates[1] == date(2009, 1, 9)
def test_day1_consecutive(self, day1_metric):
"""Day1 indexes 2+ are consecutive days after index 1."""
dates = day1_metric.dates()
assert dates[2] == date(2009, 1, 10)
assert dates[3] == date(2009, 1, 11)
assert dates[4] == date(2009, 1, 12)
def test_day1_returns_date_type(self, day1_metric):
dates = day1_metric.dates()
assert type(dates[0]) is date
def test_month1(self, month1_metric):
dates = month1_metric.dates()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
assert type(dates[0]) is date
def test_week1(self, week1_metric):
dates = week1_metric.dates()
assert dates[0] == date(2009, 1, 3) # genesis
assert dates[1] == date(2009, 1, 10) # +7 days
assert dates[2] == date(2009, 1, 17) # +14 days
assert type(dates[0]) is date
def test_year1(self, year1_metric):
dates = year1_metric.dates()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2010, 1, 1)
assert dates[2] == date(2011, 1, 1)
assert type(dates[0]) is date
def test_day3(self, day3_metric):
dates = day3_metric.dates()
assert dates[0] == date(2009, 1, 1) # epoch
assert dates[1] == date(2009, 1, 4) # +3 days
assert dates[2] == date(2009, 1, 7) # +6 days
def test_hour1_returns_datetime(self, hour1_metric):
"""Sub-daily indexes return datetime, not date."""
dates = hour1_metric.dates()
assert isinstance(dates[0], datetime)
# hour1 index 0 = epoch (2009-01-01 00:00:00 UTC)
assert dates[0] == datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
assert dates[1] == datetime(2009, 1, 1, 1, 0, 0, tzinfo=timezone.utc)
assert dates[2] == datetime(2009, 1, 1, 2, 0, 0, tzinfo=timezone.utc)
def test_minute5_returns_datetime(self, minute5_metric):
dates = minute5_metric.dates()
assert isinstance(dates[0], datetime)
assert dates[0] == datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
assert dates[1] == datetime(2009, 1, 1, 0, 5, 0, tzinfo=timezone.utc)
assert dates[2] == datetime(2009, 1, 1, 0, 10, 0, tzinfo=timezone.utc)
# ============ _date_to_index conversions ============
class TestDateToIndex:
"""Test reverse date-to-index conversion."""
def test_day1_genesis(self):
from brk_client import _date_to_index
assert _date_to_index("day1", date(2009, 1, 3)) == 0
def test_day1_before_day_one(self):
from brk_client import _date_to_index
# Dates before day1 1 map to 0
assert _date_to_index("day1", date(2009, 1, 5)) == 0
def test_day1_index_one(self):
from brk_client import _date_to_index
assert _date_to_index("day1", date(2009, 1, 9)) == 1
def test_day1_later(self):
from brk_client import _date_to_index
assert _date_to_index("day1", date(2009, 1, 10)) == 2
def test_month1(self):
from brk_client import _date_to_index
assert _date_to_index("month1", date(2009, 1, 1)) == 0
assert _date_to_index("month1", date(2009, 2, 1)) == 1
assert _date_to_index("month1", date(2010, 1, 1)) == 12
def test_year1(self):
from brk_client import _date_to_index
assert _date_to_index("year1", date(2009, 1, 1)) == 0
assert _date_to_index("year1", date(2010, 6, 15)) == 1
assert _date_to_index("year1", date(2020, 1, 1)) == 11
def test_week1(self):
from brk_client import _date_to_index
assert _date_to_index("week1", date(2009, 1, 3)) == 0
assert _date_to_index("week1", date(2009, 1, 10)) == 1
def test_hour1_with_datetime(self):
from brk_client import _date_to_index
epoch = datetime(2009, 1, 1, tzinfo=timezone.utc)
assert _date_to_index("hour1", epoch) == 0
assert _date_to_index("hour1", epoch + timedelta(hours=1)) == 1
assert _date_to_index("hour1", epoch + timedelta(hours=24)) == 24
def test_minute5_with_datetime(self):
from brk_client import _date_to_index
epoch = datetime(2009, 1, 1, tzinfo=timezone.utc)
assert _date_to_index("minute5", epoch) == 0
assert _date_to_index("minute5", epoch + timedelta(minutes=5)) == 1
assert _date_to_index("minute5", epoch + timedelta(minutes=12)) == 2 # floor
def test_hour1_with_plain_date(self):
"""Plain date is treated as midnight UTC for sub-daily."""
from brk_client import _date_to_index
# 2009-01-01 as date → midnight UTC → index 0
assert _date_to_index("hour1", date(2009, 1, 1)) == 0
# 2009-01-02 as date → midnight UTC → 24 hours later
assert _date_to_index("hour1", date(2009, 1, 2)) == 24
def test_roundtrip_day1(self):
"""date → index → date roundtrip for day1."""
from brk_client import _date_to_index, _index_to_date
for i in range(10):
d = _index_to_date("day1", i)
assert _date_to_index("day1", d) == i
def test_roundtrip_month1(self):
from brk_client import _date_to_index, _index_to_date
for i in range(24):
d = _index_to_date("month1", i)
assert _date_to_index("month1", d) == i
def test_roundtrip_hour1(self):
from brk_client import _date_to_index, _index_to_date
for i in range(48):
d = _index_to_date("hour1", i)
assert _date_to_index("hour1", d) == i
# ============ DateMetricData date methods ============
class TestDateMetricDataMethods:
def test_date_items(self, day1_metric):
items = day1_metric.date_items()
assert items[0] == (date(2009, 1, 3), 100)
assert items[1] == (date(2009, 1, 9), 200)
assert len(items) == 5
def test_to_date_dict(self, day1_metric):
d = day1_metric.to_date_dict()
assert d[date(2009, 1, 3)] == 100
assert d[date(2009, 1, 9)] == 200
assert len(d) == 5
# Keys should be date objects
assert type(list(d.keys())[0]) is date
def test_date_items_sub_daily(self, hour1_metric):
items = hour1_metric.date_items()
assert isinstance(items[0][0], datetime)
assert items[0] == (datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc), 10.0)
def test_to_date_dict_sub_daily(self, hour1_metric):
d = hour1_metric.to_date_dict()
key = datetime(2009, 1, 1, 0, 0, 0, tzinfo=timezone.utc)
assert d[key] == 10.0
assert isinstance(list(d.keys())[0], datetime)
# ============ Polars ============
class TestPolarsConversion:
"""Test MetricData.to_polars() conversion."""
@pytest.fixture(autouse=True)
def check_polars(self):
"""Skip if polars not installed."""
pytest.importorskip("polars")
def test_to_polars_with_dates(self, date_based_metric):
"""to_polars() includes date column for date-based index."""
def test_metric_data_to_polars(self, height_metric):
import polars as pl
df = height_metric.to_polars()
assert isinstance(df, pl.DataFrame)
assert list(df.columns) == ["index", "value"]
assert df["index"].to_list() == [800000, 800001, 800002, 800003, 800004]
assert df["value"].to_list() == [1.5, 2.5, 3.5, 4.5, 5.5]
df = date_based_metric.to_polars()
def test_date_metric_to_polars_with_dates(self, day1_metric):
import polars as pl
df = day1_metric.to_polars()
assert isinstance(df, pl.DataFrame)
assert "date" in df.columns
assert "value" in df.columns
assert "index" not in df.columns
assert len(df) == 5
assert df["value"].to_list() == [100, 200, 300, 400, 500]
def test_to_polars_without_dates(self, date_based_metric):
"""to_polars(with_dates=False) uses index column."""
def test_date_metric_to_polars_without_dates(self, day1_metric):
import polars as pl
df = date_based_metric.to_polars(with_dates=False)
df = day1_metric.to_polars(with_dates=False)
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].to_list() == [0, 1, 2, 3, 4]
def test_to_polars_non_date_index(self, height_based_metric):
"""to_polars() uses index column for non-date-based index."""
import polars as pl
df = height_based_metric.to_polars()
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].to_list() == [800000, 800001, 800002, 800003, 800004]
assert df["value"].to_list() == [1.5, 2.5, 3.5, 4.5, 5.5]
def test_to_polars_month1(self, month_based_metric):
"""to_polars() works with month1."""
import polars as pl
df = month_based_metric.to_polars()
def test_month1_to_polars(self, month1_metric):
df = month1_metric.to_polars()
assert "date" in df.columns
assert len(df) == 3
dates = df["date"].to_list()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
def test_sub_daily_to_polars(self, hour1_metric):
df = hour1_metric.to_polars()
assert "date" in df.columns
assert len(df) == 3
# ============ Pandas tests ============
def test_empty_to_polars(self, empty_metric):
df = empty_metric.to_polars()
assert len(df) == 0
assert list(df.columns) == ["index", "value"]
# ============ Pandas ============
class TestPandasConversion:
"""Test MetricData.to_pandas() conversion."""
@pytest.fixture(autouse=True)
def check_pandas(self):
"""Skip if pandas not installed."""
pytest.importorskip("pandas")
def test_to_pandas_with_dates(self, date_based_metric):
"""to_pandas() includes date column for date-based index."""
def test_metric_data_to_pandas(self, height_metric):
import pandas as pd
df = height_metric.to_pandas()
assert isinstance(df, pd.DataFrame)
assert list(df.columns) == ["index", "value"]
assert df["index"].tolist() == [800000, 800001, 800002, 800003, 800004]
assert df["value"].tolist() == [1.5, 2.5, 3.5, 4.5, 5.5]
df = date_based_metric.to_pandas()
def test_date_metric_to_pandas_with_dates(self, day1_metric):
import pandas as pd
df = day1_metric.to_pandas()
assert isinstance(df, pd.DataFrame)
assert "date" in df.columns
assert "value" in df.columns
assert len(df) == 5
assert df["value"].tolist() == [100, 200, 300, 400, 500]
def test_to_pandas_without_dates(self, date_based_metric):
"""to_pandas(with_dates=False) uses index column."""
def test_date_metric_to_pandas_without_dates(self, day1_metric):
import pandas as pd
df = date_based_metric.to_pandas(with_dates=False)
df = day1_metric.to_pandas(with_dates=False)
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].tolist() == [0, 1, 2, 3, 4]
def test_to_pandas_non_date_index(self, height_based_metric):
"""to_pandas() uses index column for non-date-based index."""
import pandas as pd
df = height_based_metric.to_pandas()
assert "index" in df.columns
assert "date" not in df.columns
assert df["index"].tolist() == [800000, 800001, 800002, 800003, 800004]
assert df["value"].tolist() == [1.5, 2.5, 3.5, 4.5, 5.5]
def test_to_pandas_month1(self, month_based_metric):
"""to_pandas() works with month1."""
import pandas as pd
df = month_based_metric.to_pandas()
def test_month1_to_pandas(self, month1_metric):
df = month1_metric.to_pandas()
assert "date" in df.columns
assert len(df) == 3
dates = df["date"].tolist()
assert dates[0] == date(2009, 1, 1)
assert dates[1] == date(2009, 2, 1)
assert dates[2] == date(2009, 3, 1)
def test_sub_daily_to_pandas(self, hour1_metric):
df = hour1_metric.to_pandas()
assert "date" in df.columns
assert len(df) == 3
def test_empty_to_pandas(self, empty_metric):
df = empty_metric.to_pandas()
assert len(df) == 0
assert list(df.columns) == ["index", "value"]