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.
Files changed (197) hide show
  1. parkapi_sources-0.2.0/.github/workflows/lint.yml → parkapi_sources-0.3.0/.github/workflows/lint-test.yml +29 -9
  2. parkapi_sources-0.3.0/CHANGELOG.md +24 -0
  3. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/PKG-INFO +51 -3
  4. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/README.md +50 -2
  5. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/_version.py +2 -2
  6. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/__init__.py +5 -1
  7. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/__init__.py +1 -0
  8. parkapi_sources-0.3.0/src/parkapi_sources/converters/base_converter/push/parkapi_json_converter.py +69 -0
  9. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/heidelberg/converter.py +1 -0
  10. {parkapi_sources-0.2.0/src/parkapi_sources/converters/mannheim → parkapi_sources-0.3.0/src/parkapi_sources/converters/kienzler}/__init__.py +1 -1
  11. parkapi_sources-0.3.0/src/parkapi_sources/converters/kienzler/converter.py +78 -0
  12. parkapi_sources-0.3.0/src/parkapi_sources/converters/kienzler/models.py +44 -0
  13. parkapi_sources-0.3.0/src/parkapi_sources/converters/mannheim_buchen/__init__.py +6 -0
  14. parkapi_sources-0.3.0/src/parkapi_sources/converters/mannheim_buchen/converter.py +68 -0
  15. parkapi_sources-0.3.0/src/parkapi_sources/converters/mannheim_buchen/models.py +99 -0
  16. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/neckarsulm/converter.py +1 -1
  17. parkapi_sources-0.3.0/src/parkapi_sources/converters/neckarsulm_bike/__init__.py +6 -0
  18. parkapi_sources-0.3.0/src/parkapi_sources/converters/neckarsulm_bike/converter.py +83 -0
  19. parkapi_sources-0.3.0/src/parkapi_sources/converters/pum_bw/__init__.py +6 -0
  20. parkapi_sources-0.3.0/src/parkapi_sources/converters/pum_bw/converter.py +73 -0
  21. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/reutlingen/converter.py +5 -15
  22. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/reutlingen/validation.py +17 -21
  23. parkapi_sources-0.3.0/src/parkapi_sources/converters/reutlingen_bike/__init__.py +6 -0
  24. parkapi_sources-0.3.0/src/parkapi_sources/converters/reutlingen_bike/converter.py +69 -0
  25. parkapi_sources-0.3.0/src/parkapi_sources/converters/reutlingen_bike/validation.py +43 -0
  26. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/parking_site_inputs.py +27 -27
  27. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/parkapi_sources.py +11 -3
  28. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/__init__.py +1 -0
  29. parkapi_sources-0.3.0/src/parkapi_sources/validators/list_validator.py +25 -0
  30. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/PKG-INFO +51 -3
  31. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/SOURCES.txt +28 -5
  32. parkapi_sources-0.3.0/tests/converters/data/buchen.json +34 -0
  33. parkapi_sources-0.3.0/tests/converters/data/kienzler.json +1 -0
  34. parkapi_sources-0.3.0/tests/converters/data/mannheim.json +816 -0
  35. parkapi_sources-0.3.0/tests/converters/data/neckarsulm_bike.csv +237 -0
  36. parkapi_sources-0.3.0/tests/converters/data/pum_bw.xlsx +0 -0
  37. parkapi_sources-0.3.0/tests/converters/data/reutlingen_bike.csv +335 -0
  38. parkapi_sources-0.3.0/tests/converters/kienzler_test.py +60 -0
  39. parkapi_sources-0.3.0/tests/converters/mannheim_buchen_test.py +54 -0
  40. parkapi_sources-0.3.0/tests/converters/neckarsulm_bike_test.py +32 -0
  41. parkapi_sources-0.3.0/tests/converters/pum_bw_test.py +31 -0
  42. parkapi_sources-0.3.0/tests/converters/reutlingen_bike_test.py +32 -0
  43. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/reutlingen_test.py +2 -3
  44. parkapi_sources-0.2.0/src/parkapi_sources/converters/mannheim/converter.py +0 -54
  45. parkapi_sources-0.2.0/tests/converters/data/mannheim.html +0 -744
  46. parkapi_sources-0.2.0/tests/converters/mannheim_test.py +0 -45
  47. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/.github/dependabot.yml +0 -0
  48. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/.github/workflows/build-publish.yml +0 -0
  49. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/.gitignore +0 -0
  50. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/LICENCE.txt +0 -0
  51. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/Makefile +0 -0
  52. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/freiburg.geojson +0 -0
  53. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/heidelberg.geojson +0 -0
  54. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/mannheim.geojson +0 -0
  55. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/data/ulm.geojson +0 -0
  56. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/dev/parkapi.py +0 -0
  57. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/dev/test-pull-converter.py +0 -0
  58. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/dev/test-push-converter.py +0 -0
  59. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/pyproject.toml +0 -0
  60. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/requirements-dev.txt +0 -0
  61. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/requirements.txt +0 -0
  62. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/setup.cfg +0 -0
  63. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/__init__.py +0 -0
  64. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/__init__.py +0 -0
  65. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/converter.py +0 -0
  66. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/mapper.py +0 -0
  67. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bahn_v2/validators.py +0 -0
  68. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/__init__.py +0 -0
  69. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/base_converter.py +0 -0
  70. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/__init__.py +0 -0
  71. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/pull_converter.py +0 -0
  72. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/pull/pull_scraper_mixin.py +0 -0
  73. {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
  74. {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
  75. {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
  76. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/csv_converter.py +0 -0
  77. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/json_converter.py +0 -0
  78. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/normalized_xlsx_converter.py +0 -0
  79. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/push_converter.py +0 -0
  80. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/xlsx_converter.py +0 -0
  81. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/base_converter/push/xml_converter.py +0 -0
  82. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/__init__.py +0 -0
  83. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/base_converter.py +0 -0
  84. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/base_models.py +0 -0
  85. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/bike_converter.py +0 -0
  86. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/bike_models.py +0 -0
  87. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/car_converter.py +0 -0
  88. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/bfrk_bw/car_models.py +0 -0
  89. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/freiburg/__init__.py +0 -0
  90. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/freiburg/converter.py +0 -0
  91. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/freiburg/models.py +0 -0
  92. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/heidelberg/__init__.py +0 -0
  93. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/heidelberg/validators.py +0 -0
  94. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/__init__.py +0 -0
  95. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/converter.py +0 -0
  96. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/files/ca.crt.pem +0 -0
  97. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/karlsruhe/models.py +0 -0
  98. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/neckarsulm/__init__.py +0 -0
  99. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/neckarsulm/models.py +0 -0
  100. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/__init__.py +0 -0
  101. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/converter.py +0 -0
  102. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/mapper.py +0 -0
  103. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pbw/validation.py +0 -0
  104. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pforzheim/__init__.py +0 -0
  105. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pforzheim/converter.py +0 -0
  106. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/pforzheim/validation.py +0 -0
  107. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/reutlingen/__init__.py +0 -0
  108. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/stuttgart/__init__.py +0 -0
  109. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/stuttgart/converter.py +0 -0
  110. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/ulm/__init__.py +0 -0
  111. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/ulm/converter.py +0 -0
  112. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/vrs_p_r/__init__.py +0 -0
  113. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/converters/vrs_p_r/vrs_p_r.py +0 -0
  114. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/exceptions.py +0 -0
  115. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/__init__.py +0 -0
  116. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/enums.py +0 -0
  117. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/source_info.py +0 -0
  118. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/models/xlsx_inputs.py +0 -0
  119. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/scripts/parkapi.py +0 -0
  120. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/__init__.py +0 -0
  121. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/config_helper.py +0 -0
  122. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/encoding.py +0 -0
  123. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/util/xml_helper.py +0 -0
  124. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/boolean_validators.py +0 -0
  125. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/date_validator.py +0 -0
  126. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/datetime_validator.py +0 -0
  127. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/decimal_validators.py +0 -0
  128. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/integer_validators.py +0 -0
  129. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/noneable.py +0 -0
  130. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/string_validators.py +0 -0
  131. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources/validators/time_validators.py +0 -0
  132. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/dependency_links.txt +0 -0
  133. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/requires.txt +0 -0
  134. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/src/parkapi_sources.egg-info/top_level.txt +0 -0
  135. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/conftest.py +0 -0
  136. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/bahn_v2_test.py +0 -0
  137. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/bfrk_bw_test.py +0 -0
  138. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/conftest.py +0 -0
  139. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/bahn_v2.json +0 -0
  140. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/bfrk_bw_bike.csv +0 -0
  141. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/bfrk_bw_car.csv +0 -0
  142. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/freiburg.json +0 -0
  143. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/heidelberg.json +0 -0
  144. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/karlsruhe.json +0 -0
  145. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/neckarsulm.csv +0 -0
  146. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/catalog-city.json +0 -0
  147. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-10.json +0 -0
  148. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-11.json +0 -0
  149. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-12.json +0 -0
  150. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-15.json +0 -0
  151. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-17.json +0 -0
  152. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-18.json +0 -0
  153. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-19.json +0 -0
  154. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-31.json +0 -0
  155. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-32.json +0 -0
  156. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-33.json +0 -0
  157. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-34.json +0 -0
  158. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-4.json +0 -0
  159. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-42.json +0 -0
  160. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-48.json +0 -0
  161. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-49.json +0 -0
  162. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-51.json +0 -0
  163. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-52.json +0 -0
  164. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-60.json +0 -0
  165. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-61.json +0 -0
  166. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-7.json +0 -0
  167. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-71.json +0 -0
  168. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-by-city-9.json +0 -0
  169. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pbw/object-dynamic-all.json +0 -0
  170. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/pforzheim.json +0 -0
  171. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/reutlingen.csv +0 -0
  172. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/stuttgart-realtime.xml +0 -0
  173. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/stuttgart-static.xml +0 -0
  174. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/ulm.html +0 -0
  175. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/data/vrs_p_r.xlsx +0 -0
  176. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/freiburg_test.py +0 -0
  177. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/heidelberg_test.py +0 -0
  178. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/helper.py +0 -0
  179. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/karlsruhe_test.py +0 -0
  180. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/neckarsulm_test.py +0 -0
  181. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/pbw_test.py +0 -0
  182. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/pforzheim_test.py +0 -0
  183. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/stuttgart_test.py +0 -0
  184. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/ulm_test.py +0 -0
  185. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/converters/vrs_p_r_test.py +0 -0
  186. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/models/xlsx_inputs_test.py +0 -0
  187. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/util/data_xml_helper.py +0 -0
  188. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/util/encoding_test.py +0 -0
  189. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/util/xml_helper_test.py +0 -0
  190. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/boolean_validators_test.py +0 -0
  191. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/date_validators_test.py +0 -0
  192. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/datetime_validators_test.py +0 -0
  193. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/integer_validators_test.py +0 -0
  194. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/noneable_tests.py +0 -0
  195. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/string_validators_test.py +0 -0
  196. {parkapi_sources-0.2.0 → parkapi_sources-0.3.0}/tests/validators/time_validators_test.py +0 -0
  197. {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
- strategy:
17
- matrix:
18
- python-version:
19
- - '3'
13
+
20
14
  steps:
21
15
  - name: checkout
22
16
  uses: actions/checkout@v4
23
- - name: setup Python v${{ matrix.python-version }}
17
+
18
+ - name: setup Python v3.10
24
19
  uses: actions/setup-python@v5
25
20
  with:
26
- python-version: ${{ matrix.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.2.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
- | Stadt Mannheim | car | pull | `mannheim` | yes |
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 | car | push (csv) | `reutlingen` | no |
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
- | Stadt Mannheim | car | pull | `mannheim` | yes |
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 | car | push (csv) | `reutlingen` | no |
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.
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.2.0'
16
- __version_tuple__ = version_tuple = (0, 2, 0)
15
+ __version__ = version = '0.3.0'
16
+ __version_tuple__ = version_tuple = (0, 3, 0)
@@ -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 .mannheim import MannheimPullConverter
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
@@ -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(
@@ -3,4 +3,4 @@ Copyright 2024 binary butterfly GmbH
3
3
  Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt.
4
4
  """
5
5
 
6
- from .converter import MannheimPullConverter
6
+ from .converter import KienzlerPullConverter
@@ -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,6 @@
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 .converter import BuchenPushConverter, MannheimPushConverter
@@ -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
+ )