parkapi-sources 0.2.0__tar.gz → 0.3.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.
- parkapi_sources-0.2.0/.github/workflows/lint.yml → parkapi_sources-0.3.0/.github/workflows/lint-test.yml +29 -9
- parkapi_sources-0.3.0/CHANGELOG.md +24 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/PKG-INFO +51 -3
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/README.md +50 -2
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/_version.py +2 -2
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/__init__.py +5 -1
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/__init__.py +1 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/base_converter/push/parkapi_json_converter.py +69 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/heidelberg/converter.py +1 -0
- {parkapi_sources-0.2.0/src/parkapi_sources/converters/mannheim → parkapi_sources-0.3.0/src/parkapi_sources/converters/kienzler}/__init__.py +1 -1
- parkapi_sources-0.3.0/src/parkapi_sources/converters/kienzler/converter.py +78 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/kienzler/models.py +44 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/mannheim_buchen/__init__.py +6 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/mannheim_buchen/converter.py +68 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/mannheim_buchen/models.py +99 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/neckarsulm/converter.py +1 -1
- parkapi_sources-0.3.0/src/parkapi_sources/converters/neckarsulm_bike/__init__.py +6 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/neckarsulm_bike/converter.py +83 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/pum_bw/__init__.py +6 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/pum_bw/converter.py +73 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/reutlingen/converter.py +5 -15
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/reutlingen/validation.py +17 -21
- parkapi_sources-0.3.0/src/parkapi_sources/converters/reutlingen_bike/__init__.py +6 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/reutlingen_bike/converter.py +69 -0
- parkapi_sources-0.3.0/src/parkapi_sources/converters/reutlingen_bike/validation.py +43 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/parking_site_inputs.py +27 -27
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/parkapi_sources.py +11 -3
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/__init__.py +1 -0
- parkapi_sources-0.3.0/src/parkapi_sources/validators/list_validator.py +25 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/PKG-INFO +51 -3
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/SOURCES.txt +28 -5
- parkapi_sources-0.3.0/tests/converters/data/buchen.json +34 -0
- parkapi_sources-0.3.0/tests/converters/data/kienzler.json +1 -0
- parkapi_sources-0.3.0/tests/converters/data/mannheim.json +816 -0
- parkapi_sources-0.3.0/tests/converters/data/neckarsulm_bike.csv +237 -0
- parkapi_sources-0.3.0/tests/converters/data/pum_bw.xlsx +0 -0
- parkapi_sources-0.3.0/tests/converters/data/reutlingen_bike.csv +335 -0
- parkapi_sources-0.3.0/tests/converters/kienzler_test.py +60 -0
- parkapi_sources-0.3.0/tests/converters/mannheim_buchen_test.py +54 -0
- parkapi_sources-0.3.0/tests/converters/neckarsulm_bike_test.py +32 -0
- parkapi_sources-0.3.0/tests/converters/pum_bw_test.py +31 -0
- parkapi_sources-0.3.0/tests/converters/reutlingen_bike_test.py +32 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/reutlingen_test.py +2 -3
- parkapi_sources-0.2.0/src/parkapi_sources/converters/mannheim/converter.py +0 -54
- parkapi_sources-0.2.0/tests/converters/data/mannheim.html +0 -744
- parkapi_sources-0.2.0/tests/converters/mannheim_test.py +0 -45
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/.github/dependabot.yml +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/.github/workflows/build-publish.yml +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/.gitignore +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/LICENCE.txt +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/Makefile +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/freiburg.geojson +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/heidelberg.geojson +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/mannheim.geojson +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/ulm.geojson +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/dev/parkapi.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/dev/test-pull-converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/dev/test-push-converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/pyproject.toml +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/requirements-dev.txt +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/requirements.txt +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/setup.cfg +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/mapper.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/base_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/pull_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/pull_scraper_mixin.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/static_geojson_data_mixin/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/static_geojson_data_mixin/mixin.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/static_geojson_data_mixin/models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/csv_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/json_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/normalized_xlsx_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/push_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/xlsx_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/xml_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/base_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/base_models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/bike_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/bike_models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/car_converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/car_models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/freiburg/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/freiburg/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/freiburg/models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/heidelberg/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/heidelberg/validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/files/ca.crt.pem +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/neckarsulm/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/neckarsulm/models.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/mapper.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/validation.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pforzheim/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pforzheim/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pforzheim/validation.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/reutlingen/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/stuttgart/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/stuttgart/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/ulm/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/ulm/converter.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/vrs_p_r/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/vrs_p_r/vrs_p_r.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/exceptions.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/enums.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/source_info.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/xlsx_inputs.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/scripts/parkapi.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/__init__.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/config_helper.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/encoding.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/xml_helper.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/boolean_validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/date_validator.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/datetime_validator.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/decimal_validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/integer_validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/noneable.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/string_validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/time_validators.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/dependency_links.txt +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/requires.txt +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/top_level.txt +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/conftest.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/bahn_v2_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/bfrk_bw_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/conftest.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/bahn_v2.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/bfrk_bw_bike.csv +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/bfrk_bw_car.csv +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/freiburg.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/heidelberg.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/karlsruhe.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/neckarsulm.csv +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/catalog-city.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-10.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-11.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-12.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-15.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-17.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-18.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-19.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-31.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-32.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-33.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-34.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-4.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-42.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-48.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-49.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-51.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-52.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-60.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-61.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-7.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-71.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-9.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-dynamic-all.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pforzheim.json +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/reutlingen.csv +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/stuttgart-realtime.xml +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/stuttgart-static.xml +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/ulm.html +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/vrs_p_r.xlsx +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/freiburg_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/heidelberg_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/helper.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/karlsruhe_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/neckarsulm_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/pbw_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/pforzheim_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/stuttgart_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/ulm_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/vrs_p_r_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/models/xlsx_inputs_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/util/data_xml_helper.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/util/encoding_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/util/xml_helper_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/boolean_validators_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/date_validators_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/datetime_validators_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/integer_validators_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/noneable_tests.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/string_validators_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/time_validators_test.py +0 -0
- {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tox.ini +0 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
name: lint
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- '*'
|
|
7
4
|
pull_request:
|
|
8
5
|
branches:
|
|
9
6
|
- '*'
|
|
@@ -13,17 +10,15 @@ on:
|
|
|
13
10
|
jobs:
|
|
14
11
|
lint:
|
|
15
12
|
runs-on: ubuntu-latest
|
|
16
|
-
|
|
17
|
-
matrix:
|
|
18
|
-
python-version:
|
|
19
|
-
- '3'
|
|
13
|
+
|
|
20
14
|
steps:
|
|
21
15
|
- name: checkout
|
|
22
16
|
uses: actions/checkout@v4
|
|
23
|
-
|
|
17
|
+
|
|
18
|
+
- name: setup Python v3.10
|
|
24
19
|
uses: actions/setup-python@v5
|
|
25
20
|
with:
|
|
26
|
-
python-version:
|
|
21
|
+
python-version: '3.10'
|
|
27
22
|
cache: 'pip'
|
|
28
23
|
|
|
29
24
|
- name: pip install
|
|
@@ -41,3 +36,28 @@ jobs:
|
|
|
41
36
|
# uses: uses: psf/black@stable
|
|
42
37
|
run: |
|
|
43
38
|
black -S --check --diff ./src ./tests
|
|
39
|
+
|
|
40
|
+
test:
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
|
|
43
|
+
steps:
|
|
44
|
+
|
|
45
|
+
- name: set timezone
|
|
46
|
+
uses: szenius/set-timezone@v2.0
|
|
47
|
+
with:
|
|
48
|
+
timezoneLinux: "Europe/Berlin"
|
|
49
|
+
|
|
50
|
+
- name: checkout
|
|
51
|
+
uses: actions/checkout@v4
|
|
52
|
+
|
|
53
|
+
- name: setup Python v3.10
|
|
54
|
+
uses: actions/setup-python@v5
|
|
55
|
+
with:
|
|
56
|
+
python-version: '3.10'
|
|
57
|
+
cache: 'pip'
|
|
58
|
+
|
|
59
|
+
- name: pip install
|
|
60
|
+
run: pip install -r requirements.txt -r requirements-dev.txt
|
|
61
|
+
|
|
62
|
+
- name: run pytest
|
|
63
|
+
run: python -m pytest tests
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
Released 2024-05-02
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
* Automated tests via CI pipeline
|
|
10
|
+
* Converter: [Neckarsulm bike](https://github.com/ParkenDD/parkapi-sources-v3/pull/27)
|
|
11
|
+
* Converter: [Kienzler](https://github.com/ParkenDD/parkapi-sources-v3/pull/22)
|
|
12
|
+
* Converter: [Mannheim and Buchen](https://github.com/ParkenDD/parkapi-sources-v3/pull/21)
|
|
13
|
+
* Converter: [Reutlingen bike](https://github.com/ParkenDD/parkapi-sources-v3/pull/28)
|
|
14
|
+
* Converter: [Baden-Württemberg: Park und Mitfahren](https://github.com/ParkenDD/parkapi-sources-v3/pull/18)
|
|
15
|
+
|
|
16
|
+
Fixes:
|
|
17
|
+
* [Fix required key at Heidelberg converter](https://github.com/ParkenDD/parkapi-sources-v3/pull/20)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## 0.2.0
|
|
21
|
+
|
|
22
|
+
Released 2024-04-18
|
|
23
|
+
|
|
24
|
+
First release including [public PyPI package](https://pypi.org/project/parkapi-sources/).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: parkapi_sources
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: ParkAPI Sources is a collection of converters from several different data sources to normalized ParkAPI data.
|
|
5
5
|
Author-email: "Ernesto Ruge, binary butterfly GmbH" <ernesto.ruge@binary-butterfly.de>
|
|
6
6
|
Maintainer-email: "Ernesto Ruge, binary butterfly GmbH" <ernesto.ruge@binary-butterfly.de>
|
|
@@ -47,14 +47,20 @@ We support following data sources:
|
|
|
47
47
|
| Barrierefreie Reisekette Baden-Württemberg: PKW-Parkplätze an Bushaltestellen | car | push (csv) | `bfrk_bw_spnv_car` | no |
|
|
48
48
|
| Barrierefreie Reisekette Baden-Württemberg: Fahrrad-Parkplätze an Bahnhöfen | bike | push (csv) | `bfrk_bw_oepnv_bike` | no |
|
|
49
49
|
| Barrierefreie Reisekette Baden-Württemberg: Fahrrad-Parkplätze an Bushaltestellen | bike | push (csv) | `bfrk_bw_spnv_bike` | no |
|
|
50
|
+
| Stadt Buchen | car | push (json) | `buchen` | yes |
|
|
50
51
|
| Stadt Freiburg | car | pull | `freiburg` | yes |
|
|
51
52
|
| Stadt Heidelberg | car | pull | `heidelberg` | yes |
|
|
52
53
|
| Stadt Karlsruhe | car | pull | `karlsruhe` | yes |
|
|
53
|
-
|
|
|
54
|
+
| Kienzler | bike | pull | `kienzler` | yes |
|
|
55
|
+
| Stadt Mannheim | car | push (json) | `mannheim` | yes |
|
|
56
|
+
| Stadt Neckarsulm: PKW-Parkplätze | car | pull | `neckarsulm` | no |
|
|
57
|
+
| Stadt Neckarsulm: Fahrrad-Abstellanlagen | bike | pull | `neckarsulm_bike` | no |
|
|
54
58
|
| Stadt Karlsruhe | car | push (csv) | `neckarsulm` | no |
|
|
59
|
+
| Baden-Württemberg: Parken und Mitfahren | car | push (xlsx) | `pum_bw` | no |
|
|
55
60
|
| Parkraumgesellschaft Baden-Württemberg | car | pull | `pbw` | yes |
|
|
56
61
|
| Stadt Pforzheim | car | push (csv) | `pforzheim` | no |
|
|
57
|
-
| Stadt Reutlingen
|
|
62
|
+
| Stadt Reutlingen: PKW-Parkplätze | car | push (csv) | `reutlingen` | no |
|
|
63
|
+
| Stadt Reutlingen: Fahrrad-Abstellanlagen | bike | push (csv) | `reutlingen_bike` | no |
|
|
58
64
|
| Stadt Stuttgart | car | push (json) | `stuttgart` | yes |
|
|
59
65
|
| Stadt Ulm | car | pull | `ulm` | yes |
|
|
60
66
|
| Verband Region Stuttgart: Park and Ride | car | pull | `vrs_p_r` | yes |
|
|
@@ -216,6 +222,48 @@ If you test a `PullConverter`, you will need no mock requests. This can be done
|
|
|
216
222
|
If you created new validators, these should be tested with different inputs. Usually, `pytest.parametrize` is a nice approach to do this.
|
|
217
223
|
|
|
218
224
|
|
|
225
|
+
### Migrate a converter
|
|
226
|
+
|
|
227
|
+
If you want to migrate a v1 or v2 converter, you can re-use some of the code. There is a paradigm change, though: `parkapi-source-v3`
|
|
228
|
+
enforces a strict validation after transforming the data, while v1 and v2 converters don't. ParkAPI v1 / v2 converters are always pull
|
|
229
|
+
converters, so the base class is always `PullConverter`.
|
|
230
|
+
|
|
231
|
+
Instead of defining `POOL`, you will set `source_info` at the same place. Attributes are almost the same, except for `id` was renamed to
|
|
232
|
+
`uid`, and there is the new attribute `has_realtime_data`, which has to be set.
|
|
233
|
+
|
|
234
|
+
ParkAPI v1 and v2 used two methods for static and realtime data, just as `parkapi-sources-v3`:
|
|
235
|
+
|
|
236
|
+
- the old static data handling `def get_lot_infos(self) -> List[LotInfo]:` is
|
|
237
|
+
`get_static_parking_sites(self) -> tuple[list[StaticParkingSiteInput], list[ImportParkingSiteException]]:` in `parkapi-sources-v3`.
|
|
238
|
+
- the old realtime data handling`def get_lot_data(self) -> List[LotData]:` is
|
|
239
|
+
`def get_realtime_parking_sites(self) -> tuple[list[RealtimeParkingSiteInput], list[ImportParkingSiteException]]:` in
|
|
240
|
+
`parkapi-sources-v3`.
|
|
241
|
+
|
|
242
|
+
The result objects have quite the same idea, too:
|
|
243
|
+
|
|
244
|
+
- `LotInfo` gets `StaticParkingSiteInput`
|
|
245
|
+
- `LotData` gets `RealtimeParkingSiteInput`
|
|
246
|
+
|
|
247
|
+
There's also a helper for scraped content: before, there was `self.request_soup(self.POOL.public_url)` in order to get a `BeautifulSoup`
|
|
248
|
+
element. Now, there is a helper mixin called `PullScraperMixin`. You can use it this way:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
class MyPullConverter(PullConverter, PullScraperMixin):
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Additionally, there is another mixin for the GeoJSON files you already know from v1 and v2 converters: `StaticGeojsonDataMixin`. Using this,
|
|
255
|
+
you can just define the static data method this way:
|
|
256
|
+
|
|
257
|
+
```
|
|
258
|
+
def get_static_parking_sites(self) -> tuple[list[StaticParkingSiteInput], list[ImportParkingSiteException]]:
|
|
259
|
+
return self._get_static_parking_site_inputs_and_exceptions(source_uid=self.source_info.uid)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Afterwards, you can put the GeoJSON file to `/data`.
|
|
263
|
+
|
|
264
|
+
Please keep in mind that you will have to add tests for the migrated scraper.
|
|
265
|
+
|
|
266
|
+
|
|
219
267
|
### Linting
|
|
220
268
|
|
|
221
269
|
As we try to keep a consistent code style, please lint your code before creating the merge request. We use `black` and `ruff` for linting.
|
|
@@ -13,14 +13,20 @@ We support following data sources:
|
|
|
13
13
|
| Barrierefreie Reisekette Baden-Württemberg: PKW-Parkplätze an Bushaltestellen | car | push (csv) | `bfrk_bw_spnv_car` | no |
|
|
14
14
|
| Barrierefreie Reisekette Baden-Württemberg: Fahrrad-Parkplätze an Bahnhöfen | bike | push (csv) | `bfrk_bw_oepnv_bike` | no |
|
|
15
15
|
| Barrierefreie Reisekette Baden-Württemberg: Fahrrad-Parkplätze an Bushaltestellen | bike | push (csv) | `bfrk_bw_spnv_bike` | no |
|
|
16
|
+
| Stadt Buchen | car | push (json) | `buchen` | yes |
|
|
16
17
|
| Stadt Freiburg | car | pull | `freiburg` | yes |
|
|
17
18
|
| Stadt Heidelberg | car | pull | `heidelberg` | yes |
|
|
18
19
|
| Stadt Karlsruhe | car | pull | `karlsruhe` | yes |
|
|
19
|
-
|
|
|
20
|
+
| Kienzler | bike | pull | `kienzler` | yes |
|
|
21
|
+
| Stadt Mannheim | car | push (json) | `mannheim` | yes |
|
|
22
|
+
| Stadt Neckarsulm: PKW-Parkplätze | car | pull | `neckarsulm` | no |
|
|
23
|
+
| Stadt Neckarsulm: Fahrrad-Abstellanlagen | bike | pull | `neckarsulm_bike` | no |
|
|
20
24
|
| Stadt Karlsruhe | car | push (csv) | `neckarsulm` | no |
|
|
25
|
+
| Baden-Württemberg: Parken und Mitfahren | car | push (xlsx) | `pum_bw` | no |
|
|
21
26
|
| Parkraumgesellschaft Baden-Württemberg | car | pull | `pbw` | yes |
|
|
22
27
|
| Stadt Pforzheim | car | push (csv) | `pforzheim` | no |
|
|
23
|
-
| Stadt Reutlingen
|
|
28
|
+
| Stadt Reutlingen: PKW-Parkplätze | car | push (csv) | `reutlingen` | no |
|
|
29
|
+
| Stadt Reutlingen: Fahrrad-Abstellanlagen | bike | push (csv) | `reutlingen_bike` | no |
|
|
24
30
|
| Stadt Stuttgart | car | push (json) | `stuttgart` | yes |
|
|
25
31
|
| Stadt Ulm | car | pull | `ulm` | yes |
|
|
26
32
|
| Verband Region Stuttgart: Park and Ride | car | pull | `vrs_p_r` | yes |
|
|
@@ -182,6 +188,48 @@ If you test a `PullConverter`, you will need no mock requests. This can be done
|
|
|
182
188
|
If you created new validators, these should be tested with different inputs. Usually, `pytest.parametrize` is a nice approach to do this.
|
|
183
189
|
|
|
184
190
|
|
|
191
|
+
### Migrate a converter
|
|
192
|
+
|
|
193
|
+
If you want to migrate a v1 or v2 converter, you can re-use some of the code. There is a paradigm change, though: `parkapi-source-v3`
|
|
194
|
+
enforces a strict validation after transforming the data, while v1 and v2 converters don't. ParkAPI v1 / v2 converters are always pull
|
|
195
|
+
converters, so the base class is always `PullConverter`.
|
|
196
|
+
|
|
197
|
+
Instead of defining `POOL`, you will set `source_info` at the same place. Attributes are almost the same, except for `id` was renamed to
|
|
198
|
+
`uid`, and there is the new attribute `has_realtime_data`, which has to be set.
|
|
199
|
+
|
|
200
|
+
ParkAPI v1 and v2 used two methods for static and realtime data, just as `parkapi-sources-v3`:
|
|
201
|
+
|
|
202
|
+
- the old static data handling `def get_lot_infos(self) -> List[LotInfo]:` is
|
|
203
|
+
`get_static_parking_sites(self) -> tuple[list[StaticParkingSiteInput], list[ImportParkingSiteException]]:` in `parkapi-sources-v3`.
|
|
204
|
+
- the old realtime data handling`def get_lot_data(self) -> List[LotData]:` is
|
|
205
|
+
`def get_realtime_parking_sites(self) -> tuple[list[RealtimeParkingSiteInput], list[ImportParkingSiteException]]:` in
|
|
206
|
+
`parkapi-sources-v3`.
|
|
207
|
+
|
|
208
|
+
The result objects have quite the same idea, too:
|
|
209
|
+
|
|
210
|
+
- `LotInfo` gets `StaticParkingSiteInput`
|
|
211
|
+
- `LotData` gets `RealtimeParkingSiteInput`
|
|
212
|
+
|
|
213
|
+
There's also a helper for scraped content: before, there was `self.request_soup(self.POOL.public_url)` in order to get a `BeautifulSoup`
|
|
214
|
+
element. Now, there is a helper mixin called `PullScraperMixin`. You can use it this way:
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
class MyPullConverter(PullConverter, PullScraperMixin):
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Additionally, there is another mixin for the GeoJSON files you already know from v1 and v2 converters: `StaticGeojsonDataMixin`. Using this,
|
|
221
|
+
you can just define the static data method this way:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
def get_static_parking_sites(self) -> tuple[list[StaticParkingSiteInput], list[ImportParkingSiteException]]:
|
|
225
|
+
return self._get_static_parking_site_inputs_and_exceptions(source_uid=self.source_info.uid)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Afterwards, you can put the GeoJSON file to `/data`.
|
|
229
|
+
|
|
230
|
+
Please keep in mind that you will have to add tests for the migrated scraper.
|
|
231
|
+
|
|
232
|
+
|
|
185
233
|
### Linting
|
|
186
234
|
|
|
187
235
|
As we try to keep a consistent code style, please lint your code before creating the merge request. We use `black` and `ruff` for linting.
|
|
@@ -9,11 +9,15 @@ from .bfrk_bw import BfrkBwOepnvBikePushConverter, BfrkBwOepnvCarPushConverter,
|
|
|
9
9
|
from .freiburg import FreiburgPullConverter
|
|
10
10
|
from .heidelberg import HeidelbergPullConverter
|
|
11
11
|
from .karlsruhe import KarlsruhePullConverter
|
|
12
|
-
from .
|
|
12
|
+
from .kienzler import KienzlerPullConverter
|
|
13
|
+
from .mannheim_buchen import BuchenPushConverter, MannheimPushConverter
|
|
13
14
|
from .neckarsulm import NeckarsulmPushConverter
|
|
15
|
+
from .neckarsulm_bike import NeckarsulmBikePushConverter
|
|
14
16
|
from .pbw import PbwPullConverter
|
|
15
17
|
from .pforzheim import PforzheimPushConverter
|
|
18
|
+
from .pum_bw import PumBwPushConverter
|
|
16
19
|
from .reutlingen import ReutlingenPushConverter
|
|
20
|
+
from .reutlingen_bike import ReutlingenBikePushConverter
|
|
17
21
|
from .stuttgart import StuttgartPushConverter
|
|
18
22
|
from .ulm import UlmPullConverter
|
|
19
23
|
from .vrs_p_r import VrsParkAndRidePushConverter
|
|
@@ -8,5 +8,6 @@ from .push_converter import PushConverter # noqa: I001
|
|
|
8
8
|
from .csv_converter import CsvConverter
|
|
9
9
|
from .json_converter import JsonConverter
|
|
10
10
|
from .normalized_xlsx_converter import NormalizedXlsxConverter
|
|
11
|
+
from .parkapi_json_converter import ParkApiConverter
|
|
11
12
|
from .xlsx_converter import XlsxConverter
|
|
12
13
|
from .xml_converter import XmlConverter
|
parkapi_sources-0.3.0/src/parkapi_sources/converters/base_converter/push/parkapi_json_converter.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024 binary butterfly GmbH
|
|
3
|
+
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from abc import ABC
|
|
7
|
+
|
|
8
|
+
from validataclass.dataclasses import validataclass
|
|
9
|
+
from validataclass.exceptions import ValidationError
|
|
10
|
+
from validataclass.validators import AnythingValidator, DataclassValidator, ListValidator
|
|
11
|
+
|
|
12
|
+
from parkapi_sources.converters.base_converter.push import JsonConverter
|
|
13
|
+
from parkapi_sources.exceptions import ImportParkingSiteException, ImportSourceException
|
|
14
|
+
from parkapi_sources.models import RealtimeParkingSiteInput, StaticParkingSiteInput
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@validataclass
|
|
18
|
+
class ParkingSiteItemsInput:
|
|
19
|
+
items: list[dict] = ListValidator(AnythingValidator(allowed_types=[dict]))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ParkApiConverter(JsonConverter, ABC):
|
|
23
|
+
parking_site_items_validator = DataclassValidator(ParkingSiteItemsInput)
|
|
24
|
+
static_parking_site_validator = DataclassValidator(StaticParkingSiteInput)
|
|
25
|
+
realtime_parking_site_validator = DataclassValidator(RealtimeParkingSiteInput)
|
|
26
|
+
|
|
27
|
+
def handle_json(
|
|
28
|
+
self,
|
|
29
|
+
data: dict | list,
|
|
30
|
+
) -> tuple[list[StaticParkingSiteInput | RealtimeParkingSiteInput], list[ImportParkingSiteException]]:
|
|
31
|
+
parking_site_inputs: list[StaticParkingSiteInput | RealtimeParkingSiteInput] = []
|
|
32
|
+
parking_site_errors: list[ImportParkingSiteException] = []
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
parking_site_item_inputs = self.parking_site_items_validator.validate(data)
|
|
36
|
+
except ValidationError as e:
|
|
37
|
+
raise ImportSourceException(source_uid=self.source_info.uid, message=f'Invalid data {e.to_dict()}') from e
|
|
38
|
+
|
|
39
|
+
for parking_site_dict in parking_site_item_inputs.items:
|
|
40
|
+
try:
|
|
41
|
+
static_parking_site_input = self.static_parking_site_validator.validate(parking_site_dict)
|
|
42
|
+
parking_site_inputs.append(static_parking_site_input)
|
|
43
|
+
except ValidationError as e:
|
|
44
|
+
parking_site_errors.append(
|
|
45
|
+
ImportParkingSiteException(
|
|
46
|
+
source_uid=self.source_info.uid,
|
|
47
|
+
parking_site_uid=parking_site_dict.get('uid'),
|
|
48
|
+
message=f'validation error for {parking_site_dict}: {e.to_dict()}',
|
|
49
|
+
),
|
|
50
|
+
)
|
|
51
|
+
# If there was an error, we don't proceed with realtime data
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
# No realtime data means no realtime data handling
|
|
55
|
+
if not static_parking_site_input.has_realtime_data:
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
parking_site_inputs.append(self.realtime_parking_site_validator.validate(parking_site_dict))
|
|
60
|
+
except ValidationError as e:
|
|
61
|
+
parking_site_errors.append(
|
|
62
|
+
ImportParkingSiteException(
|
|
63
|
+
source_uid=self.source_info.uid,
|
|
64
|
+
parking_site_uid=parking_site_dict.get('uid'),
|
|
65
|
+
message=f'validation error for {parking_site_dict}: {e.to_dict()}',
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return parking_site_inputs, parking_site_errors
|
|
@@ -15,6 +15,7 @@ from .validators import HeidelbergRealtimeInput, HeidelbergRealtimeUpdateInput
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class HeidelbergPullConverter(PullConverter, StaticGeojsonDataMixin):
|
|
18
|
+
required_config_keys = ['PARK_API_HEIDELBERG_API_KEY']
|
|
18
19
|
heidelberg_realtime_validator = DataclassValidator(HeidelbergRealtimeInput)
|
|
19
20
|
heidelberg_realtime_update_validator = DataclassValidator(HeidelbergRealtimeUpdateInput)
|
|
20
21
|
source_info = SourceInfo(
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024 binary butterfly GmbH
|
|
3
|
+
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from validataclass.exceptions import ValidationError
|
|
8
|
+
from validataclass.validators import AnythingValidator, DataclassValidator, ListValidator
|
|
9
|
+
|
|
10
|
+
from parkapi_sources.converters.base_converter.pull import PullConverter
|
|
11
|
+
from parkapi_sources.exceptions import ImportParkingSiteException
|
|
12
|
+
from parkapi_sources.models import RealtimeParkingSiteInput, SourceInfo, StaticParkingSiteInput
|
|
13
|
+
|
|
14
|
+
from .models import KienzlerInput
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class KienzlerPullConverter(PullConverter):
|
|
18
|
+
kienzler_list_validator = ListValidator(AnythingValidator(allowed_types=[dict]))
|
|
19
|
+
kienzler_item_validator = DataclassValidator(KienzlerInput)
|
|
20
|
+
|
|
21
|
+
required_config_keys = ['PARK_API_KIENZLER_USER', 'PARK_API_KIENZLER_PASSWORD', 'PARK_API_KIENZLER_IDS']
|
|
22
|
+
source_info = SourceInfo(
|
|
23
|
+
uid='kienzler',
|
|
24
|
+
name='Kienzler',
|
|
25
|
+
has_realtime_data=True,
|
|
26
|
+
public_url='https://www.bikeandridebox.de',
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def get_static_parking_sites(self) -> tuple[list[StaticParkingSiteInput], list[ImportParkingSiteException]]:
|
|
30
|
+
static_parking_site_inputs: list[StaticParkingSiteInput] = []
|
|
31
|
+
|
|
32
|
+
kienzler_parking_sites, static_parking_site_errors = self._get_kienzler_parking_sites()
|
|
33
|
+
for kienzler_parking_site in kienzler_parking_sites:
|
|
34
|
+
static_parking_site_inputs.append(kienzler_parking_site.to_static_parking_site())
|
|
35
|
+
|
|
36
|
+
return static_parking_site_inputs, static_parking_site_errors
|
|
37
|
+
|
|
38
|
+
def get_realtime_parking_sites(self) -> tuple[list[RealtimeParkingSiteInput], list[ImportParkingSiteException]]:
|
|
39
|
+
realtime_parking_site_inputs: list[RealtimeParkingSiteInput] = []
|
|
40
|
+
|
|
41
|
+
kienzler_parking_sites, static_parking_site_errors = self._get_kienzler_parking_sites()
|
|
42
|
+
for kienzler_parking_site in kienzler_parking_sites:
|
|
43
|
+
realtime_parking_site_inputs.append(kienzler_parking_site.to_realtime_parking_site())
|
|
44
|
+
|
|
45
|
+
return realtime_parking_site_inputs, static_parking_site_errors
|
|
46
|
+
|
|
47
|
+
def _get_kienzler_parking_sites(self) -> tuple[list[KienzlerInput], list[ImportParkingSiteException]]:
|
|
48
|
+
kienzler_item_inputs: list[KienzlerInput] = []
|
|
49
|
+
errors: list[ImportParkingSiteException] = []
|
|
50
|
+
|
|
51
|
+
parking_site_dicts = self.kienzler_list_validator.validate(self._request())
|
|
52
|
+
for parking_site_dict in parking_site_dicts:
|
|
53
|
+
try:
|
|
54
|
+
kienzler_item_inputs.append(self.kienzler_item_validator.validate(parking_site_dict))
|
|
55
|
+
except ValidationError as e:
|
|
56
|
+
errors.append(
|
|
57
|
+
ImportParkingSiteException(
|
|
58
|
+
source_uid=self.source_info.uid,
|
|
59
|
+
parking_site_uid=parking_site_dict.get('uid'),
|
|
60
|
+
message=f'validation error for {parking_site_dict}: {e.to_dict()}',
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
return kienzler_item_inputs, errors
|
|
64
|
+
|
|
65
|
+
def _request(self) -> list[dict]:
|
|
66
|
+
response = requests.post(
|
|
67
|
+
url='https://www.bikeandridebox.de/index.php?eID=JSONAPI',
|
|
68
|
+
json={
|
|
69
|
+
'user': self.config_helper.get('PARK_API_KIENZLER_USER'),
|
|
70
|
+
'password': self.config_helper.get('PARK_API_KIENZLER_PASSWORD'),
|
|
71
|
+
'action': 'capacity',
|
|
72
|
+
'context': 'unit',
|
|
73
|
+
'ids': self.config_helper.get('PARK_API_KIENZLER_IDS').split(','),
|
|
74
|
+
},
|
|
75
|
+
timeout=30,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
return response.json()
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024 binary butterfly GmbH
|
|
3
|
+
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
|
|
9
|
+
from validataclass.dataclasses import validataclass
|
|
10
|
+
from validataclass.validators import IntegerValidator, NumericValidator, StringValidator
|
|
11
|
+
|
|
12
|
+
from parkapi_sources.models import RealtimeParkingSiteInput, StaticParkingSiteInput
|
|
13
|
+
from parkapi_sources.models.enums import ParkingSiteType, PurposeType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@validataclass
|
|
17
|
+
class KienzlerInput:
|
|
18
|
+
id: str = StringValidator(min_length=1)
|
|
19
|
+
name: str = StringValidator()
|
|
20
|
+
lat: Decimal = NumericValidator()
|
|
21
|
+
long: Decimal = NumericValidator()
|
|
22
|
+
bookable: int = IntegerValidator(min_value=0)
|
|
23
|
+
sum_boxes: int = IntegerValidator(min_value=0)
|
|
24
|
+
|
|
25
|
+
def to_static_parking_site(self) -> StaticParkingSiteInput:
|
|
26
|
+
return StaticParkingSiteInput(
|
|
27
|
+
uid=self.id,
|
|
28
|
+
name=self.name,
|
|
29
|
+
purpose=PurposeType.ITEM if 'Schließfächer' in self.name else PurposeType.BIKE,
|
|
30
|
+
lat=self.lat,
|
|
31
|
+
lon=self.long,
|
|
32
|
+
has_realtime_data=True,
|
|
33
|
+
capacity=self.sum_boxes,
|
|
34
|
+
type=ParkingSiteType.LOCKERS,
|
|
35
|
+
static_data_updated_at=datetime.now(tz=timezone.utc),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def to_realtime_parking_site(self) -> RealtimeParkingSiteInput:
|
|
39
|
+
return RealtimeParkingSiteInput(
|
|
40
|
+
uid=self.id,
|
|
41
|
+
realtime_data_updated_at=datetime.now(tz=timezone.utc),
|
|
42
|
+
realtime_capacity=self.sum_boxes,
|
|
43
|
+
realtime_free_capacity=self.bookable,
|
|
44
|
+
)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024 binary butterfly GmbH
|
|
3
|
+
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from abc import ABC
|
|
7
|
+
|
|
8
|
+
from validataclass.exceptions import ValidationError
|
|
9
|
+
from validataclass.validators import AnythingValidator, DataclassValidator, ListValidator
|
|
10
|
+
|
|
11
|
+
from parkapi_sources.converters.base_converter.push import JsonConverter
|
|
12
|
+
from parkapi_sources.converters.mannheim_buchen.models import MannheimBuchenInput
|
|
13
|
+
from parkapi_sources.exceptions import ImportParkingSiteException, ImportSourceException
|
|
14
|
+
from parkapi_sources.models import RealtimeParkingSiteInput, SourceInfo, StaticParkingSiteInput
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MannheimBuchenBaseConverter(JsonConverter, ABC):
|
|
18
|
+
mannheim_buchen_list_validator = ListValidator(AnythingValidator(allowed_types=[dict]))
|
|
19
|
+
mannheim_buchen_validator = DataclassValidator(MannheimBuchenInput)
|
|
20
|
+
|
|
21
|
+
def handle_json(
|
|
22
|
+
self,
|
|
23
|
+
data: dict | list,
|
|
24
|
+
) -> tuple[list[StaticParkingSiteInput | RealtimeParkingSiteInput], list[ImportParkingSiteException]]:
|
|
25
|
+
parking_site_inputs: list[StaticParkingSiteInput | RealtimeParkingSiteInput] = []
|
|
26
|
+
parking_site_errors: list[ImportParkingSiteException] = []
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
parking_site_dicts = self.mannheim_buchen_list_validator.validate(data)
|
|
30
|
+
except ValidationError as e:
|
|
31
|
+
raise ImportSourceException(source_uid=self.source_info.uid, message=f'Invalid data {e.to_dict()}') from e
|
|
32
|
+
|
|
33
|
+
for parking_site_dict in parking_site_dicts:
|
|
34
|
+
try:
|
|
35
|
+
parking_site_input: MannheimBuchenInput = self.mannheim_buchen_validator.validate(parking_site_dict)
|
|
36
|
+
|
|
37
|
+
parking_site_inputs.append(parking_site_input.to_static_parking_site(city=self.source_info.name))
|
|
38
|
+
parking_site_inputs.append(parking_site_input.to_realtime_parking_site())
|
|
39
|
+
|
|
40
|
+
except ValidationError as e:
|
|
41
|
+
parking_site_errors.append(
|
|
42
|
+
ImportParkingSiteException(
|
|
43
|
+
source_uid=self.source_info.uid,
|
|
44
|
+
parking_site_uid=parking_site_dict.get('uid'),
|
|
45
|
+
message=f'validation error for {parking_site_dict}: {e.to_dict()}',
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
return parking_site_inputs, parking_site_errors
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class MannheimPushConverter(MannheimBuchenBaseConverter):
|
|
52
|
+
source_info = SourceInfo(
|
|
53
|
+
uid='mannheim',
|
|
54
|
+
name='Stadt Mannheim',
|
|
55
|
+
public_url='https://www.parken-mannheim.de',
|
|
56
|
+
timezone='Europe/Berlin',
|
|
57
|
+
has_realtime_data=True,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class BuchenPushConverter(MannheimBuchenBaseConverter):
|
|
62
|
+
source_info = SourceInfo(
|
|
63
|
+
uid='buchen',
|
|
64
|
+
name='Stadt Buchen',
|
|
65
|
+
public_url='https://www.buchen.de/ueber-buchen/kostenlos-parken.html',
|
|
66
|
+
timezone='Europe/Berlin',
|
|
67
|
+
has_realtime_data=True,
|
|
68
|
+
)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright 2024 binary butterfly GmbH
|
|
3
|
+
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from datetime import datetime, timezone
|
|
7
|
+
from decimal import Decimal
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from zoneinfo import ZoneInfo
|
|
10
|
+
|
|
11
|
+
from validataclass.dataclasses import DefaultUnset, validataclass
|
|
12
|
+
from validataclass.helpers import OptionalUnset
|
|
13
|
+
from validataclass.validators import (
|
|
14
|
+
AnyOfValidator,
|
|
15
|
+
DataclassValidator,
|
|
16
|
+
DateTimeValidator,
|
|
17
|
+
EnumValidator,
|
|
18
|
+
IntegerValidator,
|
|
19
|
+
ListValidator,
|
|
20
|
+
NumericValidator,
|
|
21
|
+
StringValidator,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
from parkapi_sources.models import RealtimeParkingSiteInput, StaticParkingSiteInput
|
|
25
|
+
from parkapi_sources.models.enums import ParkingSiteType
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MannheimParkingSiteType(Enum):
|
|
29
|
+
CAR_PARK = 'carPark'
|
|
30
|
+
|
|
31
|
+
def to_parking_site_type(self) -> ParkingSiteType:
|
|
32
|
+
return {
|
|
33
|
+
self.CAR_PARK: ParkingSiteType.CAR_PARK,
|
|
34
|
+
}.get(self)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@validataclass
|
|
38
|
+
class MannheimBuchenLocationInput:
|
|
39
|
+
coordinates: list[Decimal] = ListValidator(NumericValidator())
|
|
40
|
+
type: str = AnyOfValidator(allowed_values=['Point'])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@validataclass
|
|
44
|
+
class MannheimBuchenAddressInput:
|
|
45
|
+
street: OptionalUnset[str] = StringValidator(), DefaultUnset
|
|
46
|
+
houseNo: OptionalUnset[str] = StringValidator(), DefaultUnset
|
|
47
|
+
postalCode: OptionalUnset[str] = StringValidator(), DefaultUnset
|
|
48
|
+
city: OptionalUnset[str] = StringValidator(), DefaultUnset
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@validataclass
|
|
52
|
+
class MannheimBuchenInput:
|
|
53
|
+
id: str = StringValidator()
|
|
54
|
+
name: str = StringValidator()
|
|
55
|
+
operatorId: str = StringValidator()
|
|
56
|
+
dataType: str = AnyOfValidator(allowed_values=['parkingCar'])
|
|
57
|
+
location: MannheimBuchenLocationInput = DataclassValidator(MannheimBuchenLocationInput)
|
|
58
|
+
address: MannheimBuchenAddressInput = DataclassValidator(MannheimBuchenAddressInput)
|
|
59
|
+
description: str = StringValidator()
|
|
60
|
+
trafficType: str = AnyOfValidator(allowed_values=['car'])
|
|
61
|
+
type: MannheimParkingSiteType = EnumValidator(MannheimParkingSiteType)
|
|
62
|
+
capacity: int = IntegerValidator(min_value=0)
|
|
63
|
+
free: int = IntegerValidator(min_value=0)
|
|
64
|
+
timestamp: datetime = DateTimeValidator(local_timezone=ZoneInfo('Europe/Berlin'), target_timezone=timezone.utc)
|
|
65
|
+
|
|
66
|
+
def to_static_parking_site(self, city: str) -> StaticParkingSiteInput:
|
|
67
|
+
address = ''
|
|
68
|
+
if self.address.street:
|
|
69
|
+
address = self.address.street
|
|
70
|
+
if self.address.houseNo:
|
|
71
|
+
address += f' {self.address.houseNo}'
|
|
72
|
+
address += ', '
|
|
73
|
+
if self.address.postalCode:
|
|
74
|
+
address += f' {self.address.postalCode}'
|
|
75
|
+
if self.address.city:
|
|
76
|
+
address += f' {self.address.city}'
|
|
77
|
+
else:
|
|
78
|
+
address += f' {city}'
|
|
79
|
+
|
|
80
|
+
return StaticParkingSiteInput(
|
|
81
|
+
uid=self.id,
|
|
82
|
+
name=self.name,
|
|
83
|
+
address=address,
|
|
84
|
+
description=self.description,
|
|
85
|
+
lat=self.location.coordinates[1],
|
|
86
|
+
lon=self.location.coordinates[0],
|
|
87
|
+
has_realtime_data=True,
|
|
88
|
+
capacity=self.capacity,
|
|
89
|
+
type=self.type.to_parking_site_type(),
|
|
90
|
+
static_data_updated_at=self.timestamp,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def to_realtime_parking_site(self) -> RealtimeParkingSiteInput:
|
|
94
|
+
return RealtimeParkingSiteInput(
|
|
95
|
+
uid=self.id,
|
|
96
|
+
realtime_data_updated_at=self.timestamp,
|
|
97
|
+
realtime_capacity=self.capacity,
|
|
98
|
+
realtime_free_capacity=self.free,
|
|
99
|
+
)
|