zeitreiher-models 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- zeitreiher_models-0.1.0/.gitattributes +9 -0
- zeitreiher_models-0.1.0/.github/dependabot.yml +14 -0
- zeitreiher_models-0.1.0/.github/workflows/dependabot_automerge.yml +18 -0
- zeitreiher_models-0.1.0/.github/workflows/go-lint.yml +28 -0
- zeitreiher_models-0.1.0/.github/workflows/go-test.yml +26 -0
- zeitreiher_models-0.1.0/.github/workflows/packaging-test.yml +20 -0
- zeitreiher_models-0.1.0/.github/workflows/proto.yml +27 -0
- zeitreiher_models-0.1.0/.github/workflows/python-coverage.yml +23 -0
- zeitreiher_models-0.1.0/.github/workflows/python-formatting.yml +28 -0
- zeitreiher_models-0.1.0/.github/workflows/python-lint.yml +35 -0
- zeitreiher_models-0.1.0/.github/workflows/python-publish.yml +52 -0
- zeitreiher_models-0.1.0/.github/workflows/python-test.yml +24 -0
- zeitreiher_models-0.1.0/.gitignore +30 -0
- zeitreiher_models-0.1.0/LICENSE +21 -0
- zeitreiher_models-0.1.0/PKG-INFO +235 -0
- zeitreiher_models-0.1.0/README.md +209 -0
- zeitreiher_models-0.1.0/buf.yaml +13 -0
- zeitreiher_models-0.1.0/doc.go +24 -0
- zeitreiher_models-0.1.0/docs/superpowers/plans/2026-10-27-m1a-models.md +495 -0
- zeitreiher_models-0.1.0/domain-specific-terms.txt +5 -0
- zeitreiher_models-0.1.0/errors.go +244 -0
- zeitreiher_models-0.1.0/fields.go +214 -0
- zeitreiher_models-0.1.0/fields_test.go +55 -0
- zeitreiher_models-0.1.0/fixtures_test.go +328 -0
- zeitreiher_models-0.1.0/go.mod +7 -0
- zeitreiher_models-0.1.0/go.sum +4 -0
- zeitreiher_models-0.1.0/golden_test.go +56 -0
- zeitreiher_models-0.1.0/models_test.go +342 -0
- zeitreiher_models-0.1.0/ndjson.go +125 -0
- zeitreiher_models-0.1.0/proto/zeitreiher/v1/query.proto +153 -0
- zeitreiher_models-0.1.0/proto/zeitreiher/v1/results.proto +59 -0
- zeitreiher_models-0.1.0/proto/zeitreiher/v1/selector.proto +20 -0
- zeitreiher_models-0.1.0/proto/zeitreiher/v1/series_service.proto +44 -0
- zeitreiher_models-0.1.0/proto/zeitreiher/v1/system.proto +19 -0
- zeitreiher_models-0.1.0/pyproject.toml +100 -0
- zeitreiher_models-0.1.0/records.go +531 -0
- zeitreiher_models-0.1.0/request.go +268 -0
- zeitreiher_models-0.1.0/results.go +342 -0
- zeitreiher_models-0.1.0/scalars.go +193 -0
- zeitreiher_models-0.1.0/selector.go +194 -0
- zeitreiher_models-0.1.0/src/_zeitreiher_models_version.py +1 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/__init__.py +99 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/_base.py +115 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/errors.py +106 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/ndjson.py +65 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/py.typed +0 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/records.py +136 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/requests.py +89 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/results.py +90 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/scalars.py +148 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/selectors.py +95 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/system.py +18 -0
- zeitreiher_models-0.1.0/src/zeitreiher_models/timestamps.py +131 -0
- zeitreiher_models-0.1.0/system.go +93 -0
- zeitreiher_models-0.1.0/testdata/error_codes.json +34 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/invalid/code_not_snake_case.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/invalid/message_not_string.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/invalid/missing_message.json +5 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/invalid/not_enveloped.json +4 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/read_only/unknown_code.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/cursor_expired.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/cursor_invalid.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/cursor_mismatch.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/cursor_stale.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/forbidden.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/internal.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/invalid_request.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/method_not_allowed.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/not_found.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/request_too_large.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/unauthorized.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/upstream_timeout.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/upstream_unreachable.json +6 -0
- zeitreiher_models-0.1.0/testdata/error_envelope/valid/with_request_id.json +7 -0
- zeitreiher_models-0.1.0/testdata/invalid_reasons.json +1466 -0
- zeitreiher_models-0.1.0/testdata/proto_fields.json +114 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/compact_offset.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/cursor_empty.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_by_uppercase.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_param_invalid_utf8.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_param_lone_surrogate.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_params_empty_key.json +12 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_params_non_string.json +12 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_params_null_value.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_selector_reserved_by.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/custom_selector_without_params.json +9 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/date_only.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/day_32.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/empty_select.json +5 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/fractional_seconds.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/from_after_to.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/from_equals_to.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/from_number.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/from_trailing_newline.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/from_underscore_key.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/from_unicode_digits.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/hour_24.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/interval_daily.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/interval_empty.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/interval_hours.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/interval_unicode_digits.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/leap_second.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/lowercase_t.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/malo_unicode_digits.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/malo_wrong_length.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/minus_0000_offset.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/minute_60.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/missing_from.json +9 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/missing_select.json +4 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/month_13.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/naive_from.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/naive_to.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/null_to.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/offset_hour_24.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/offset_minute_60.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_all_zeros.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_number_not_string.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_selector_non_digits.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_too_long.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_trailing_newline.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_unicode_digits.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/profile_zero.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/selector_by_array.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/selector_by_number.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/selector_by_object.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/selector_missing_by.json +9 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/selector_not_object.json +7 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/selector_unknown_field.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/space_separator.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_control_char.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_degree.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_empty.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_four_chars.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_inner_space.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_kvarh.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_kwh_external.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_lowercase.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_micromolar.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_nbsp.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_one_char.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_padded.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_percent.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_six_chars.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_superscript_digit.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_symbol.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unit_too_long.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/unknown_field.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/uppercase_from_key.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_as_of_not_supported.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_current_not_empty.json +15 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_current_true.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_empty.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_null_sibling.json +14 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_number_float.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_number_null.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_number_string.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_number_zero.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_two_keys.json +14 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/version_unknown_key.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/year_0000.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/year_0001.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/year_10000_in_utc.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/year_1899.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/invalid/year_1899_in_utc.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/custom_selector.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/custom_selector_empty_params.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/custom_selector_params_key_order.json +15 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/dst_autumn_day.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/dst_spring_day.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/duplicate_key_last_wins.canonical.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/duplicate_key_last_wins.json +12 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/full_spec_example.canonical.json +19 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/full_spec_example.json +20 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/minimal.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/offset_mixed.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/offset_plus_0000.canonical.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/offset_plus_0000.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/offset_plus_0200.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/offset_z.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/profile_leading_zeros.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/three_selectors.json +21 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/unit_kvr.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/unit_kwh.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/unit_two_chars.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/unit_with_digit.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/version_current.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/version_number.json +13 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/with_cursor.json +11 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/year_1900.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/year_1900_in_utc.json +10 -0
- zeitreiher_models-0.1.0/testdata/query_request/valid/year_9999_in_utc.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/end_count_float.json +7 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/end_missing_next.json +6 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/end_negative_values.json +7 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/end_next_empty.json +7 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/error_code_not_snake_case.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/error_missing_code.json +9 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/error_missing_select.json +6 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/error_missing_selector.json +9 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/error_profile_empty.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/missing_type.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/not_an_object.json +13 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_attribute_not_string.json +31 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_attribute_null.json +35 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_from_offset_plus_0200.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_interval_p1d.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_interval_unicode_digits.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_missing_attributes.json +28 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_missing_select.json +25 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_missing_versions.json +17 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_naive_to.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_profile_leading_zero.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_profile_unicode_digits.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_role_empty.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_role_missing.json +28 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_role_number.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_select_by_array.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_select_param_null.json +35 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_select_profile_leading_zero.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_selector_negative.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_empty.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_lowercase.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_missing.json +28 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_raw_control_char.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_raw_empty.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_raw_missing.json +28 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_raw_null.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/series_unit_raw_too_long.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/type_not_string.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/unknown_type.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_comma.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_exponent.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_fraction_trailing_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_from_after_to.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_hour_24.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_leading_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_missing_status.json +8 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_negative_trailing_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_negative_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_negative_zero_unicode.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_number_not_string.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_offset_not_z.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_offset_plus_0000.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_plus_sign.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_series_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_status_duplicate.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_status_empty_code.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_status_unsorted.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_trailing_dot.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_trailing_newline.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_trailing_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_unicode_digits.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_version_missing_key.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_version_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_year_1899.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/value_zero_fraction.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_created_at_inconsistent.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_created_at_null_but_known.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_description_invalid_utf8.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_description_lone_high_surrogate.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_description_lone_low_surrogate.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_description_reversed_surrogates.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_missing_to_key.json +28 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_number_zero.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/invalid/version_to_before_from.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/read_only/error_unknown_code.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/end_empty_page.json +7 -0
- zeitreiher_models-0.1.0/testdata/records/valid/end_spec_example.json +7 -0
- zeitreiher_models-0.1.0/testdata/records/valid/end_with_cursor.json +7 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_access_denied.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_ambiguous_local_time.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_assignment_unaligned.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_bad_selector.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_badi_failed.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_conversion_not_allowed.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_interval_size_unsupported.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_no_assignment.json +9 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_profile_not_found.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_profile_time_zone_missing.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_selector_not_found.json +9 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_unknown_selector.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_version_deleted.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_version_not_found.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/error_window_unaligned.json +10 -0
- zeitreiher_models-0.1.0/testdata/records/valid/from_underscore_ignored.canonical.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/from_underscore_ignored.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_custom_selector_attributes.json +35 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_escaping_and_key_order.json +41 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_no_versions.json +18 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_select_unknown_key_ignored.canonical.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_select_unknown_key_ignored.json +30 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_spec_example.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_unit_converted.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_unit_kvarh.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_unit_raw_free_text.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_unit_unmapped.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_version_closed.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_version_created_at_unknown.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/series_version_deleted.json +29 -0
- zeitreiher_models-0.1.0/testdata/records/valid/unknown_field_ignored.canonical.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/unknown_field_ignored.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/valid/uppercase_key_ignored.canonical.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/uppercase_key_ignored.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_duplicate_key_last_wins.canonical.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_duplicate_key_last_wins.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_fraction_decimal.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_from_version.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_negative_decimal.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_no_status.json +9 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_spec_example.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_two_statuses.json +12 -0
- zeitreiher_models-0.1.0/testdata/records/valid/value_zero.json +11 -0
- zeitreiher_models-0.1.0/testdata/records/valid/version_description_surrogate_pair.json +29 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/cursor_not_allowed.json +11 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/custom_selector_without_params.json +9 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/empty_select.json +5 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/from_after_to.json +10 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/naive_from.json +10 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/selector_by_object.json +10 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/unknown_field.json +11 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/invalid/version_field_not_allowed.json +13 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/valid/minimal.json +10 -0
- zeitreiher_models-0.1.0/testdata/resolve_request/valid/two_selectors.json +17 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/assignment_from_without_to_with_error.json +20 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/assignment_missing_to.json +17 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/assignment_profile_leading_zero.json +18 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/both_assignments_and_error.json +15 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/error_without_code.json +12 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/missing_results.json +1 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/neither.json +11 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/invalid/selector_negative.json +12 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/assignment_error_access_denied.json +24 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/assignment_error_with_window.json +21 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/empty_assignments.json +15 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/empty_results.json +3 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/spec_example.json +28 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/versions_key_ignored.canonical.json +18 -0
- zeitreiher_models-0.1.0/testdata/resolve_response/valid/versions_key_ignored.json +30 -0
- zeitreiher_models-0.1.0/testdata/serialized.golden +410 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/blank_line.ndjson +5 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/crlf.ndjson +4 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/empty.ndjson +0 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/end_not_last.ndjson +3 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/invalid_record.ndjson +3 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/missing_end.ndjson +3 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/pretty_printed_record.ndjson +21 -0
- zeitreiher_models-0.1.0/testdata/streams/invalid/two_ends.ndjson +5 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/dst_autumn_day_pt15m.ndjson +102 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/dst_spring_day_pt15m.ndjson +94 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/gaps.ndjson +86 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/no_trailing_newline.ndjson +3 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/only_errors.ndjson +2 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/paged_page1.ndjson +4 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/paged_page2.ndjson +4 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/spec_example.ndjson +6 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/two_series_profile_change.ndjson +8 -0
- zeitreiher_models-0.1.0/testdata/streams/valid/version_number.ndjson +6 -0
- zeitreiher_models-0.1.0/testdata/system_info/invalid/empty_business_time_zone.json +13 -0
- zeitreiher_models-0.1.0/testdata/system_info/invalid/interval_p1d.json +12 -0
- zeitreiher_models-0.1.0/testdata/system_info/invalid/missing_sid.json +12 -0
- zeitreiher_models-0.1.0/testdata/system_info/valid/minimal.json +13 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/cursor_not_allowed.json +11 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/custom_selector_without_params.json +9 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/empty_select.json +5 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/from_after_to.json +10 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/naive_from.json +10 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/unknown_field.json +11 -0
- zeitreiher_models-0.1.0/testdata/versions_request/invalid/version_field_not_allowed.json +13 -0
- zeitreiher_models-0.1.0/testdata/versions_request/valid/minimal.json +10 -0
- zeitreiher_models-0.1.0/testdata/versions_request/valid/two_selectors.json +17 -0
- zeitreiher_models-0.1.0/testdata/versions_response/invalid/error_with_versions.json +31 -0
- zeitreiher_models-0.1.0/testdata/versions_response/invalid/missing_versions.json +18 -0
- zeitreiher_models-0.1.0/testdata/versions_response/invalid/version_number_zero.json +30 -0
- zeitreiher_models-0.1.0/testdata/versions_response/valid/assignment_error.json +19 -0
- zeitreiher_models-0.1.0/testdata/versions_response/valid/assignment_no_versions_empty_list.json +19 -0
- zeitreiher_models-0.1.0/testdata/versions_response/valid/deleted_and_created_at_unknown.json +40 -0
- zeitreiher_models-0.1.0/testdata/versions_response/valid/spec_example_with_versions.json +40 -0
- zeitreiher_models-0.1.0/timestamps.go +132 -0
- zeitreiher_models-0.1.0/timestamps_test.go +125 -0
- zeitreiher_models-0.1.0/uv.lock +745 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: "gomod"
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: "weekly"
|
|
7
|
+
- package-ecosystem: "uv"
|
|
8
|
+
directory: "/"
|
|
9
|
+
schedule:
|
|
10
|
+
interval: "weekly"
|
|
11
|
+
- package-ecosystem: "github-actions"
|
|
12
|
+
directory: "/"
|
|
13
|
+
schedule:
|
|
14
|
+
interval: "weekly"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
name: Dependabot auto-approve / -merge
|
|
2
|
+
on: pull_request
|
|
3
|
+
|
|
4
|
+
jobs:
|
|
5
|
+
dependabot:
|
|
6
|
+
permissions:
|
|
7
|
+
contents: write
|
|
8
|
+
pull-requests: write
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
env:
|
|
11
|
+
PR_URL: ${{github.event.pull_request.html_url}}
|
|
12
|
+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
|
13
|
+
if: ${{ github.actor == 'dependabot[bot]' }}
|
|
14
|
+
steps:
|
|
15
|
+
- name: Approve a PR
|
|
16
|
+
run: gh pr review --approve "$PR_URL"
|
|
17
|
+
- name: Enable auto-merge for Dependabot PRs
|
|
18
|
+
run: gh pr merge --auto --squash "$PR_URL"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: "Go Lint"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request: {}
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
golangci:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
name: lint
|
|
13
|
+
env:
|
|
14
|
+
# Build/run the linter with the toolchain the module targets (go 1.27.0).
|
|
15
|
+
GOTOOLCHAIN: go1.27.0
|
|
16
|
+
steps:
|
|
17
|
+
- name: Install Go
|
|
18
|
+
uses: actions/setup-go@v7
|
|
19
|
+
with:
|
|
20
|
+
go-version: "1.27.x"
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
- name: golangci-lint
|
|
23
|
+
uses: golangci/golangci-lint-action@v9
|
|
24
|
+
with:
|
|
25
|
+
version: v2.15.0
|
|
26
|
+
args: --enable dupl,gocyclo
|
|
27
|
+
- name: fmt
|
|
28
|
+
run: golangci-lint fmt --diff --enable gofmt
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: "Go Tests"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: Install Go
|
|
16
|
+
uses: actions/setup-go@v7
|
|
17
|
+
with:
|
|
18
|
+
go-version: "1.27.x"
|
|
19
|
+
- name: Checkout code
|
|
20
|
+
uses: actions/checkout@v7
|
|
21
|
+
- name: Vet
|
|
22
|
+
run: go vet ./...
|
|
23
|
+
- name: Test
|
|
24
|
+
run: go test -race -covermode=atomic -coverprofile=coverage.out ./...
|
|
25
|
+
- name: Coverage
|
|
26
|
+
run: go tool cover -func=coverage.out | tail -1
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
name: "Packaging Test"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
check_packaging:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7
|
|
13
|
+
- name: Install uv
|
|
14
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.15"
|
|
17
|
+
- name: Build distributions
|
|
18
|
+
run: uv build
|
|
19
|
+
- name: Check distributions
|
|
20
|
+
run: uv publish --dry-run
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: "Proto"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
buf:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v7
|
|
16
|
+
- name: Install Go
|
|
17
|
+
uses: actions/setup-go@v7
|
|
18
|
+
with:
|
|
19
|
+
go-version: "1.27.x"
|
|
20
|
+
- name: Install buf
|
|
21
|
+
run: go install github.com/bufbuild/buf/cmd/buf@v1.75.0
|
|
22
|
+
- name: buf build
|
|
23
|
+
run: buf build
|
|
24
|
+
- name: buf lint
|
|
25
|
+
run: buf lint
|
|
26
|
+
- name: buf format
|
|
27
|
+
run: buf format --diff --exit-code
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: "Python Coverage"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
coverage:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7
|
|
13
|
+
- name: Install uv
|
|
14
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.15"
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
run: uv sync --group coverage
|
|
19
|
+
- name: Run Tests and Record Coverage
|
|
20
|
+
run: |
|
|
21
|
+
uv run coverage run -m pytest unittests
|
|
22
|
+
uv run coverage html --omit 'unittests/*,.venv/*'
|
|
23
|
+
uv run coverage report --fail-under 80 --omit 'unittests/*,.venv/*'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: "Python Formatting"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
format:
|
|
10
|
+
name: "format (${{ matrix.tool }})"
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
include:
|
|
15
|
+
- tool: ruff-format
|
|
16
|
+
command: "ruff format --check ."
|
|
17
|
+
- tool: ruff-isort
|
|
18
|
+
command: "ruff check --select I ."
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v7
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.15"
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: uv sync --group linting
|
|
27
|
+
- name: ${{ matrix.tool }}
|
|
28
|
+
run: uv run ${{ matrix.command }}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: "Python Lint"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pylint:
|
|
10
|
+
name: "Python Code Quality and Lint (${{ matrix.linter-env }})"
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
include:
|
|
15
|
+
- linter-env: linting
|
|
16
|
+
commands: |
|
|
17
|
+
uv run ruff check src/zeitreiher_models unittests
|
|
18
|
+
- linter-env: type_check
|
|
19
|
+
commands: |
|
|
20
|
+
uv run mypy --show-error-codes src/zeitreiher_models --strict
|
|
21
|
+
uv run mypy --show-error-codes unittests --strict
|
|
22
|
+
- linter-env: spell_check
|
|
23
|
+
commands: |
|
|
24
|
+
uv run codespell --ignore-words=domain-specific-terms.txt src
|
|
25
|
+
uv run codespell --ignore-words=domain-specific-terms.txt README.md
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v7
|
|
28
|
+
- name: Install uv
|
|
29
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
30
|
+
with:
|
|
31
|
+
python-version: "3.15"
|
|
32
|
+
- name: Install Dependencies
|
|
33
|
+
run: uv sync --group ${{ matrix.linter-env }}
|
|
34
|
+
- name: Run ${{ matrix.linter-env }}
|
|
35
|
+
run: ${{ matrix.commands }}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: "Publish to PyPI"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
tests:
|
|
9
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v7
|
|
13
|
+
- name: Install uv
|
|
14
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
15
|
+
with:
|
|
16
|
+
python-version: "3.15"
|
|
17
|
+
- name: Install dependencies
|
|
18
|
+
run: uv sync --group tests --group linting --group coverage --group type_check
|
|
19
|
+
- name: Run tests
|
|
20
|
+
run: uv run pytest unittests
|
|
21
|
+
- name: Run linting
|
|
22
|
+
run: |
|
|
23
|
+
uv run ruff check src/zeitreiher_models unittests
|
|
24
|
+
- name: Run type_check
|
|
25
|
+
run: |
|
|
26
|
+
uv run mypy --show-error-codes src/zeitreiher_models --strict
|
|
27
|
+
uv run mypy --show-error-codes unittests --strict
|
|
28
|
+
- name: Run coverage
|
|
29
|
+
run: |
|
|
30
|
+
uv run coverage run -m pytest unittests
|
|
31
|
+
uv run coverage report --fail-under 80 --omit 'unittests/*,.venv/*'
|
|
32
|
+
|
|
33
|
+
build-n-publish:
|
|
34
|
+
name: Build and publish Python distributions to PyPI
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: release
|
|
37
|
+
permissions:
|
|
38
|
+
id-token: write
|
|
39
|
+
needs: tests
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/checkout@v7
|
|
42
|
+
with:
|
|
43
|
+
fetch-depth: 0 # hatch-vcs derives the package version from the git tag
|
|
44
|
+
- name: Install uv
|
|
45
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
46
|
+
with:
|
|
47
|
+
python-version: "3.15"
|
|
48
|
+
- name: Build wheel and source distributions
|
|
49
|
+
run: uv build
|
|
50
|
+
- name: Publish distribution to PyPI
|
|
51
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: "Python Tests"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request: {}
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pytest:
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.11", "3.12", "3.13", "3.14", "3.15"]
|
|
14
|
+
os: [ubuntu-latest]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v7
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v10.3.2
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python-version }}
|
|
21
|
+
- name: Install Dependencies
|
|
22
|
+
run: uv sync --group tests
|
|
23
|
+
- name: Run the Unit Tests
|
|
24
|
+
run: uv run pytest unittests
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Go
|
|
2
|
+
*.exe
|
|
3
|
+
*.exe~
|
|
4
|
+
*.dll
|
|
5
|
+
*.so
|
|
6
|
+
*.dylib
|
|
7
|
+
*.test
|
|
8
|
+
*.out
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*.egg-info/
|
|
14
|
+
dist/
|
|
15
|
+
build/
|
|
16
|
+
.tox/
|
|
17
|
+
.mypy_cache/
|
|
18
|
+
htmlcov/
|
|
19
|
+
.coverage
|
|
20
|
+
src/_zeitreiher_models_version.py
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
*.swp
|
|
26
|
+
|
|
27
|
+
# Env
|
|
28
|
+
.env
|
|
29
|
+
.venv/
|
|
30
|
+
coverage.out
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hochfrequenz Unternehmensberatung GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: zeitreiher-models
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Hand-written public data models of the Zeitreiher API (SAP IS-U EDM profile values)
|
|
5
|
+
Project-URL: Changelog, https://github.com/Hochfrequenz/zeitreiher-models/releases
|
|
6
|
+
Project-URL: Homepage, https://github.com/Hochfrequenz/zeitreiher-models
|
|
7
|
+
Author-email: Hochfrequenz Unternehmensberatung GmbH <info+github@hochfrequenz.de>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: edm,is-u,lastgang,sap,time series,zeitreiher
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: pydantic>=2.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# zeitreiher-models
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+

|
|
31
|
+
[](https://pkg.go.dev/github.com/hochfrequenz/zeitreiher-models)
|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+

|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
Public data models of the **Zeitreiher API, version 1**: a read-only API over SAP IS-U EDM profile
|
|
39
|
+
values (Lastgänge) — profile metadata plus many `[from, to)` intervals with values.
|
|
40
|
+
|
|
41
|
+
This repository holds
|
|
42
|
+
|
|
43
|
+
- **Go** structs (package `zeitreihermodels`, module `github.com/hochfrequenz/zeitreiher-models`),
|
|
44
|
+
- **Python** frozen pydantic v2 models (package `zeitreiher_models`),
|
|
45
|
+
- the shared **fixtures** in [`testdata/`](testdata) that define the JSON contract, and
|
|
46
|
+
- the **gRPC contract** in [`proto/zeitreiher/v1`](proto/zeitreiher/v1).
|
|
47
|
+
|
|
48
|
+
Everything is **written by hand**. No code is generated and no generated code is committed: every
|
|
49
|
+
type, validator and (un)marshaller is ordinary, readable code. Both languages parse every valid
|
|
50
|
+
fixture, write it back and compare the result with the fixture, and reject every invalid fixture —
|
|
51
|
+
so Go and Python agree on the contract by construction.
|
|
52
|
+
|
|
53
|
+
## The contract in short
|
|
54
|
+
|
|
55
|
+
| Topic | Rule |
|
|
56
|
+
|---|---|
|
|
57
|
+
| Intervals | Half-open everywhere: `[from, to)`; `from` must be before `to`. |
|
|
58
|
+
| Request timestamps | RFC 3339, whole seconds, **explicit offset** (`Z` or `±hh:mm`), ASCII digits only, hours 00–23. The instant must lie in the years 1900–9999 **in UTC**, so that it can be echoed in a response (`1900-01-01T00:00:00+01:00` is 1899 in UTC and rejected). Kept as sent (`+02:00` stays `+02:00`). A missing offset (`2026-01-01T00:00:00`) and the "unknown offset" `-00:00` are rejected. |
|
|
59
|
+
| Response timestamps | RFC 3339, whole seconds, always UTC (`Z`). Instants built in code must survive the round trip (no offsets with seconds, years 1900–9999). |
|
|
60
|
+
| Values | Already-scaled decimal **strings** in their **shortest canonical form** (`"160"`, `"0.022"`, `"25.25"`, `"-3.5"`), never JSON numbers. No exponent, no plus sign, no leading zeros, no trailing zeros after the decimal point (`"160.0"` is rejected: after parsing it equals `"160"`), no negative zero. |
|
|
61
|
+
| Profile numbers | In requests 1–18 digits, leading zeros allowed, not all zeros; in responses without leading zeros. |
|
|
62
|
+
| Units | `unit` is a **canonical unit code**: a UN/ECE Recommendation 20 code such as `KWH`, `MWH` or `KVR` (2 or 3 upper-case ASCII letters or digits, strict). Requests send canonical codes; the server maps them to the customer's SAP unit (an unknown code is a 400). In series records `unit` is always present and `null` when the SAP unit has no mapping; the series is delivered anyway. `unit_raw` (series records) is the SAP unit key (T006 `MSEHI`) as SAP delivers it, mapped or not, read leniently: any non-empty text of at most 30 characters (code points) without control characters (Unicode `Cc`). |
|
|
63
|
+
| Series role | Every `series` record carries `role`, the role of its profile assignment (SAP `EPROFASS` role, e.g. `"0001"`; any non-empty text), or `null` when the series was not resolved through a role (e.g. `by: profile`). For `by: malo` the roles to deliver are configurable (default: all). |
|
|
64
|
+
| Selectors | `{"by":"profile","profile":"…"}`, `{"by":"malo","malo":"<11 digits>"}`, or a customer resolver `{"by":"<snake_case name>","params":{"<key>":"<string>"}}`. |
|
|
65
|
+
| Version selector | Exactly one of `{"current":{}}` (default) or `{"number":N}`; no `null` members. v1 has no as-of selector: `{"as_of":…}` is rejected. `{"number":N}` returns the current values overlaid with the stored values of versions ≥ N (the lowest wins), only inside N's covered range (gaps outside). Each value record says its source: `"version":<n>` for a stored version row, `"version":null` for a current value. |
|
|
66
|
+
| Intervals (`interval`) | Sub-daily ISO 8601 durations in minutes: `PT15M`, `PT60M`, … Hours are written in minutes: `PT60M`, never `PT1H` (rejected, so that every interval has one spelling). |
|
|
67
|
+
| Text | Every string is valid Unicode and is written unchanged. Parsers reject invalid UTF-8 and unpaired `\uD800`–`\uDFFF` escapes. Go refuses to marshal a string that is not valid UTF-8 (instead of writing U+FFFD); Python rejects a string with a lone surrogate when the model is built (`ValidationError`). |
|
|
68
|
+
| Unknown keys | Requests **reject** them (a typo fails loudly). Responses **ignore** them (additive server changes do not break older clients). |
|
|
69
|
+
| Duplicate keys | Not rejected: when an object repeats a key, the **last** occurrence wins and earlier ones are ignored without validation (`{"unit":"K H","unit":"MWH"}` is read as `"MWH"`), in both languages and on both sides. Producers must not write duplicate keys; the models never do. Pinned by the `*duplicate_key_last_wins*` fixtures. |
|
|
70
|
+
| `null` | Request fields that are optional may be omitted or `null`. In responses, `series.role`, `series.unit`, `value.version`, `end.next`, `version.to` and `version.created_at` are always present and may be `null`; other optional response fields are omitted when unset. |
|
|
71
|
+
| Error codes | Request-level codes in the error envelope `{"error":{"code","message","request_id"?}}` — btpingo's vocabulary (`invalid_request`, `unauthorized`, `forbidden`, `not_found`, `method_not_allowed`, `request_too_large`, `upstream_unreachable`, `internal`), shared by the Hochfrequenz BTP services, plus `cursor_invalid`, `cursor_mismatch`, `cursor_expired`, `cursor_stale` and `upstream_timeout`; record-level codes in `error` records and resolve/versions results. The known codes are listed in [`testdata/error_codes.json`](testdata/error_codes.json). **Strict on write, lenient on read:** parsers accept unknown snake_case codes so that a newer server does not break older clients (Go `IsKnown()`, Python `RecordErrorCode | str`); writers refuse every code outside the public list (Go `MarshalJSON` returns an error, Python JSON serialization raises `PydanticSerializationError`; `model_dump()` to a dict keeps them). |
|
|
72
|
+
| NDJSON | One record per line, lines end with `\n` (the last one optionally), no `\r`, no blank lines. A response that does not end with exactly one `end` record was cut off and **must be treated as a failure**. The `end` record's `series`, `values` and `errors` count the records **on this page** only; a series record re-sent at the start of a continuation page counts again. |
|
|
73
|
+
|
|
74
|
+
The full design lives in the `zeitreiher-go` repository (`docs/superpowers/specs/2026-10-27-zeitreiher-design.md`).
|
|
75
|
+
|
|
76
|
+
## Go
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
go get github.com/hochfrequenz/zeitreiher-models
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```go
|
|
83
|
+
import zrm "github.com/hochfrequenz/zeitreiher-models"
|
|
84
|
+
|
|
85
|
+
cet := time.FixedZone("CET", 3600)
|
|
86
|
+
req := zrm.QueryRequest{
|
|
87
|
+
Select: []zrm.Selector{zrm.MaLoSelector("87253749908"), zrm.ProfileSelector("50000000000056")},
|
|
88
|
+
From: time.Date(2026, 1, 1, 0, 0, 0, 0, cet),
|
|
89
|
+
To: time.Date(2026, 2, 1, 0, 0, 0, 0, cet),
|
|
90
|
+
Interval: "PT60M",
|
|
91
|
+
}
|
|
92
|
+
body, err := json.Marshal(req) // validates; an invalid request does not marshal
|
|
93
|
+
|
|
94
|
+
records, err := zrm.ReadStream(resp.Body) // errors.Is(err, zrm.ErrMissingEnd) for a cut-off stream
|
|
95
|
+
for _, rec := range records {
|
|
96
|
+
switch r := rec.(type) {
|
|
97
|
+
case *zrm.SeriesRecord:
|
|
98
|
+
case *zrm.ValueRecord: // r.Value is a zrm.Decimal (string)
|
|
99
|
+
case *zrm.ErrorRecord: // r.Code.IsKnown()
|
|
100
|
+
case *zrm.EndRecord: // r.Next != nil → request the next page with Cursor = *r.Next
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`ReadRecords(io.Reader)` yields records one by one (`iter.Seq2[Record, error]`) for streaming;
|
|
106
|
+
`WriteRecord` writes one NDJSON line. Every type validates itself in `UnmarshalJSON` and refuses
|
|
107
|
+
to marshal when it is invalid.
|
|
108
|
+
|
|
109
|
+
## Python
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pip install zeitreiher-models # once published to PyPI
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from datetime import datetime, timedelta, timezone
|
|
117
|
+
|
|
118
|
+
from zeitreiher_models import MaLoSelector, QueryRequest, ValueRecord, iter_records, read_stream
|
|
119
|
+
|
|
120
|
+
cet = timezone(timedelta(hours=1))
|
|
121
|
+
request = QueryRequest(
|
|
122
|
+
select=[MaLoSelector(malo="87253749908")],
|
|
123
|
+
from_=datetime(2026, 1, 1, tzinfo=cet),
|
|
124
|
+
to=datetime(2026, 2, 1, tzinfo=cet),
|
|
125
|
+
interval="PT60M",
|
|
126
|
+
)
|
|
127
|
+
body = request.model_dump_json() # {"select":[…],"from":"2026-01-01T00:00:00+01:00",…}
|
|
128
|
+
|
|
129
|
+
for record in read_stream(response_bytes): # raises MissingEndError for a cut-off stream
|
|
130
|
+
if isinstance(record, ValueRecord):
|
|
131
|
+
print(record.from_, record.decimal) # decimal.Decimal, no float rounding
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`from` is a Python keyword, so the attribute is `from_`; the JSON key is always `from`.
|
|
135
|
+
|
|
136
|
+
## Fixtures
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
testdata/
|
|
140
|
+
error_codes.json the known request- and record-level codes
|
|
141
|
+
query_request/{valid,invalid}/ request bodies (DST days, +02:00 vs Z, naive timestamps, custom selectors, versions, …)
|
|
142
|
+
resolve_request/, versions_request/
|
|
143
|
+
records/{valid,invalid}/ single NDJSON records, one valid fixture per record-level error code
|
|
144
|
+
streams/{valid,invalid}/ whole NDJSON responses (DST days with 92/100 quarter hours, gaps, versions, paging, …)
|
|
145
|
+
resolve_response/, versions_response/
|
|
146
|
+
error_envelope/{valid,invalid}/ one valid fixture per request-level error code
|
|
147
|
+
records/read_only/, error_envelope/read_only/
|
|
148
|
+
unknown error codes: they parse, but writing them is refused
|
|
149
|
+
system_info/
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`X.canonical.json` next to `X.json` is the expected re-serialization when it differs from the input
|
|
153
|
+
(e.g. `+00:00` becomes `Z`, `"cursor": null` is omitted, unknown response keys are dropped).
|
|
154
|
+
Consumers in other languages (the Go service, future Rust/C#/TS models) should run the same fixtures.
|
|
155
|
+
|
|
156
|
+
`testdata/serialized.golden` pins the exact bytes both languages write for every valid fixture
|
|
157
|
+
(`<fixture>\t<JSON>` per line), so Go and Python output is byte-for-byte identical: compact JSON,
|
|
158
|
+
keys in model order, map keys (`params`, `attributes`) sorted, and Go's string escaping — `<`, `>`,
|
|
159
|
+
`&`, U+2028 and U+2029 are written as `<`, `>`, `&`, `
`, `
`; all other
|
|
160
|
+
non-ASCII characters are written as UTF-8. Python's `model_dump_json()` and `dump_record()` apply
|
|
161
|
+
that escaping; in Go, `json.Marshal`, `MarshalRecord` and a default `json.Encoder` produce it (an
|
|
162
|
+
encoder with `SetEscapeHTML(false)` would not).
|
|
163
|
+
|
|
164
|
+
Every invalid fixture has an entry in `testdata/invalid_reasons.json`, so that a fixture cannot pass by
|
|
165
|
+
being rejected for the wrong reason:
|
|
166
|
+
|
|
167
|
+
- `go`: a substring of the Go error (the field path and the failed rule);
|
|
168
|
+
- `python`: the pydantic error that must be among `ValidationError.errors()` — its `loc` (joined with
|
|
169
|
+
`.`), its `type` and, only for the error types whose messages the models write themselves
|
|
170
|
+
(`value_error`, `instant`, `unknown_key`, `version_selector`), `msg`, a substring of its message.
|
|
171
|
+
pydantic's own messages are not compared, so rewording them in a pydantic upgrade does not break
|
|
172
|
+
the fixtures.
|
|
173
|
+
|
|
174
|
+
### Regenerating the pinned files
|
|
175
|
+
|
|
176
|
+
- **`serialized.golden`** — after an intended change to the output (or a new valid fixture), run
|
|
177
|
+
`go test -run Golden -update .`, review the diff, then `uv run pytest unittests` to confirm that
|
|
178
|
+
Python writes the same bytes. The file is only ever written by Go; Python compares against it.
|
|
179
|
+
- **`invalid_reasons.json`** — maintained by hand, not generated, because each entry states the
|
|
180
|
+
*intended* reason. For a new invalid fixture, the failing Go test prints the actual error
|
|
181
|
+
(`go test -run InvalidFixtures .`) and the failing Python test prints a ready-made `python` entry
|
|
182
|
+
(`uv run pytest unittests -k invalid_fixture`); check that both name the rule the fixture is about
|
|
183
|
+
before adding them. Keep the file sorted, two-space indented and ASCII-only
|
|
184
|
+
(`json.dumps(..., indent=2, sort_keys=True, ensure_ascii=True)`).
|
|
185
|
+
- **After a pydantic upgrade** — run `uv run pytest unittests`. A failing `invalid_fixture` test means
|
|
186
|
+
that pydantic changed an error's `loc` or `type` (messages are not compared), or that a fixture is
|
|
187
|
+
now rejected for a different reason; compare the printed `errors()` with the entry and update the
|
|
188
|
+
entry only if the fixture is still rejected for the intended reason. `serialized.golden` failures
|
|
189
|
+
after an upgrade mean that the output bytes changed and need a fix in the models, not a new golden
|
|
190
|
+
file.
|
|
191
|
+
|
|
192
|
+
## gRPC
|
|
193
|
+
|
|
194
|
+
`proto/zeitreiher/v1/*.proto` (package `zeitreiher.v1`, service `SeriesService` with `Query`,
|
|
195
|
+
`Resolve`, `ListVersions`, `System`) is the published gRPC contract. Field names equal the JSON keys.
|
|
196
|
+
Request timestamps are RFC 3339 strings (so the explicit-offset rule holds for gRPC too); response
|
|
197
|
+
timestamps are `google.protobuf.Timestamp`. Conformance with the JSON contract is defined through
|
|
198
|
+
the hand-written models (fixture → model → proto → model → JSON), not through protojson. Generate
|
|
199
|
+
code in the consuming project, e.g. with buf managed mode.
|
|
200
|
+
|
|
201
|
+
A failed call (and a `Query` stream cut off before its `EndRecord`) ends with a gRPC status that
|
|
202
|
+
carries a `google.rpc.ErrorInfo` detail with `reason` = the public request-level code and
|
|
203
|
+
`domain` = `"zeitreiher.hochfrequenz.de"`; `series_service.proto` lists the status code for each
|
|
204
|
+
code. Clients switch on `reason`.
|
|
205
|
+
|
|
206
|
+
## Versioning
|
|
207
|
+
|
|
208
|
+
`v1` is in the URL path (`/api/v1/…`), the proto package (`zeitreiher.v1`) and the models' major
|
|
209
|
+
version. Additive changes (new optional fields, new error codes) stay in v1; breaking changes need v2.
|
|
210
|
+
|
|
211
|
+
The Go package `zeitreihermodels` lives at the **repository root** (import path
|
|
212
|
+
`github.com/hochfrequenz/zeitreiher-models`); there is no `v1` sub-package. Its major version follows
|
|
213
|
+
Go module versioning: v1 is released as tags `v1.x.y` of this module path, and a breaking v2 becomes
|
|
214
|
+
the module `github.com/hochfrequenz/zeitreiher-models/v2` (tags `v2.x.y`). The Python package takes its
|
|
215
|
+
version from the same tags.
|
|
216
|
+
|
|
217
|
+
## Development
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
go test ./... # Go
|
|
221
|
+
golangci-lint run && golangci-lint fmt --diff
|
|
222
|
+
uv sync --group dev # Python
|
|
223
|
+
uv run pytest unittests
|
|
224
|
+
uv run ruff check src unittests && uv run ruff format --check .
|
|
225
|
+
uv run mypy --strict src/zeitreiher_models unittests
|
|
226
|
+
buf lint && buf build && buf format --diff --exit-code # buf v1.75.0
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The Go tests (`fixtures_test.go`) and the Python tests (`unittests/test_fixtures.py`) use the same
|
|
230
|
+
table of fixture kinds; a new fixture directory must be added to both. `unittests/test_proto_contract.py`
|
|
231
|
+
fails when a model and its proto message disagree on field names.
|
|
232
|
+
|
|
233
|
+
## License
|
|
234
|
+
|
|
235
|
+
MIT
|