Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [0.0.3] - 2026-06-23
- Fixes in modifiy_timezone
- Disarm dependencies to ease compatibility to designer development
- Enhance documentation

## [0.0.2] - 2026-05-21
- Include helpers for metadata
- Include helper function for modify timezone
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ writing component code.
Once you are done writing your code, including unit tests, use `./run check` to see if your code quality is sufficient.

### Documentation
Fr documentation we use the tool sphinx. Please apply `./run build_docs` to create the current state of documentation. It will be stored in **docs**. You can open the documentation by opening `docs/index.html`, e.g. with your browser.
For documentation we use the tool sphinx. Please apply `./run build_docs` to create the current state of documentation. It will be stored in **docs**. You can open the documentation by opening `docs/index.html`, e.g. with your browser.

### Build, Release and Publish
The first step for publishing a new package version is creating and merging a pull request from develop to main.
Expand All @@ -96,9 +96,21 @@ To **publish** the build from the `dist` subdirectory to PyPI,

1) tag your main branch with the specified package version

2) use `uv publish --index testpypi --token <API-token>`. You need a (Test-)PyPI account with a token and you need maintainer/owner access to the [hdhelpers (Test-)PyPI project](https://pypi.org/project/hdhelpers/).
2) use `uv publish --index testpypi --token <API-token>`. You need a Test-PyPI account with a token and you need maintainer/owner access to the [hdhelpers Test-PyPI project](https://test.pypi.org/project/hdhelpers/).

3) After publishing please communicate to the hetida designer team so upgrade there dependencies.
3) verifiy that package can be installed by using:
```bash
uv run --with 'hdhelpers==<version>'
--refresh-package hdhelpers
--default-index https://test.pypi.org/simple/
--index https://test.pypi.org/simple/
--no-project
-- python -c "import hdhelpers; print(hdhelpers.__version__)"
```

4) use `uv publish dist/*` to upload the package on [hdhelpers PyPI project](https://pypi.org/project/hdhelpers/).

5) After publishing please communicate to the hetida designer team so upgrade there dependencies.
The hetida designer docker compose setup installs hdhelpers from [PyPI](https://pypi.org) as it does with any dependency listed in `runtime/requirements.in`.

### Notes
Expand Down
2 changes: 1 addition & 1 deletion docs/searchindex.js

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ exclude = [

[project]
name = "hdhelpers"
version = "0.0.2"
version = "0.0.3"
description = "Streamlines metadata & timezone handling, and plotting in hetida designer components"
readme = "README.md"
maintainers = [
Expand All @@ -32,11 +32,11 @@ maintainers = [

requires-python = ">=3.12"
dependencies = [
"pandas>=2,<3",
"plotly>=6,<7",
"pydantic>=2,<3",
"pandas",
"plotly",
"pydantic",
"glom>25,<26",
"numpy>=2.3.3",
"numpy",
]
license = "MIT"
license-files = ["LICENSE"]
Expand Down
2 changes: 1 addition & 1 deletion src/hdhelpers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from .plot_target_settings import StatusColors

# do not edit line of __version__ as it is automatically modified by running ./run build_package
__version__ = "0.0.2"
__version__ = "0.0.3"

# function can be automated with from hdhelpers import *
__all__ = [
Expand Down
19 changes: 13 additions & 6 deletions src/hdhelpers/helpers/timezone_handling.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import logging
from functools import singledispatch

Expand All @@ -19,8 +20,9 @@ def _convert_to_optional_timezone(object_to_convert, to_timezone: str | None):
)


@_convert_to_optional_timezone.register(pd.Timestamp | pd.DatetimeIndex)
@_convert_to_optional_timezone.register(pd.Timestamp | pd.DatetimeIndex | datetime.datetime)
def _[T: (pd.Timestamp, pd.DatetimeIndex)](object_to_convert: T, to_timezone: str | None) -> T:
object_to_convert = pd.to_datetime(object_to_convert)
if to_timezone is None:
if object_to_convert.tz is None:
return object_to_convert.tz_localize("UTC")
Expand All @@ -41,7 +43,7 @@ def _(object_to_convert: pd.Series, to_timezone: str | None) -> pd.Series:
return object_to_convert.dt.tz_convert(to_timezone)


def modify_timezone[T: (pd.Timestamp, pd.Series, pd.DataFrame)]( # noqa: PLR0912
def modify_timezone[T: (pd.Timestamp, datetime.datetime, pd.Series, pd.DataFrame)]( # noqa: PLR0912
object_to_convert: T,
to_timezone: str | None = None,
column_names: list[str] | None = None,
Expand Down Expand Up @@ -73,9 +75,11 @@ def modify_timezone[T: (pd.Timestamp, pd.Series, pd.DataFrame)]( # noqa: PLR091
3600
"""

if not isinstance(object_to_convert, pd.Timestamp | pd.Series | pd.DataFrame):
if not isinstance(
object_to_convert, pd.Timestamp | pd.Series | pd.DataFrame | datetime.datetime
):
raise TypeError(
f"object_to_convert is {type(object_to_convert)} not pd.Series | pd.DataFrame"
f"object_to_convert is {type(object_to_convert)} not pd.Timestamp | pd.Series | pd.DataFrame | datetime.datetime"
)
if column_names is None:
column_names = []
Expand All @@ -86,9 +90,12 @@ def modify_timezone[T: (pd.Timestamp, pd.Series, pd.DataFrame)]( # noqa: PLR091
if plot_target_settings.plot_target_timezone is not None:
to_timezone = plot_target_settings.plot_target_timezone

if isinstance(object_to_convert, pd.Timestamp):
if isinstance(object_to_convert, pd.Timestamp | datetime.datetime):
return _convert_to_optional_timezone(object_to_convert, to_timezone)

if object_to_convert.empty:
return object_to_convert

if isinstance(object_to_convert, pd.Series):
new_object = object_to_convert.to_frame(name=object_to_convert.name)
else:
Expand All @@ -99,7 +106,7 @@ def modify_timezone[T: (pd.Timestamp, pd.Series, pd.DataFrame)]( # noqa: PLR091
new_object.index = _convert_to_optional_timezone(
pd.to_datetime(new_object.index), to_timezone
)
msg = f"Converted index to datetime starting with {object_to_convert.index[0]}"
msg = f"Converted index to datetime starting with {object_to_convert.index.min()}"
logger.debug(msg=msg)
elif isinstance(new_object, pd.DataFrame) and "timestamp" in new_object.columns:
new_object["timestamp"] = _convert_to_optional_timezone(
Expand Down
18 changes: 18 additions & 0 deletions tests/helpers/test_timezone_handling.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,18 @@ def test_modify_timezone_wrong_tzname(series_summer):
_ = modify_timezone(series_summer, to_timezone="Europe/Berlin2")


def test_empty_series():
data = pd.Series()
modified_data = modify_timezone(data, to_timezone="Europe/Berlin")
assert modified_data.empty


def test_empty_dataframe():
data = pd.DataFrame()
modified_data = modify_timezone(data, to_timezone="Europe/Berlin", column_names=["timestamp"])
assert modified_data.empty


def test_named_series(series_summer):
data = pd.Series(series_summer.index)
data.name = "timestamp"
Expand Down Expand Up @@ -182,3 +194,9 @@ def test_modify_timestamp():
pd.to_datetime("2023-03-25 23:00", utc=True), to_timezone="Europe/Berlin"
)
assert modified_timestamp.utcoffset() == datetime.timedelta(seconds=3600)


def test_modify_timestamp_datetime():
example_date = pd.to_datetime("2023-03-25 23:00", utc=True)
modified_timestamp = modify_timezone(example_date.to_pydatetime(), to_timezone="Europe/Berlin")
assert modified_timestamp.utcoffset() == datetime.timedelta(seconds=3600)
10 changes: 5 additions & 5 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.