-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Reutlingen bike converter, enable CI tests
- Loading branch information
1 parent
4543257
commit 4bc9816
Showing
13 changed files
with
567 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,17 +13,15 @@ on: | |
jobs: | ||
lint: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
python-version: | ||
- '3' | ||
|
||
steps: | ||
- name: checkout | ||
uses: actions/checkout@v4 | ||
- name: setup Python v${{ matrix.python-version }} | ||
|
||
- name: setup Python v3.10 | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: ${{ matrix.python-version }} | ||
python-version: '3.10' | ||
cache: 'pip' | ||
|
||
- name: pip install | ||
|
@@ -41,3 +39,28 @@ jobs: | |
# uses: uses: psf/black@stable | ||
run: | | ||
black -S --check --diff ./src ./tests | ||
test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
|
||
- name: set timezone | ||
uses: szenius/[email protected] | ||
with: | ||
timezoneLinux: "Europe/Berlin" | ||
|
||
- name: checkout | ||
uses: actions/checkout@v4 | ||
|
||
- name: setup Python v3.10 | ||
uses: actions/setup-python@v5 | ||
with: | ||
python-version: '3.10' | ||
cache: 'pip' | ||
|
||
- name: pip install | ||
run: pip install -r requirements.txt -r requirements-dev.txt | ||
|
||
- name: run pytest | ||
run: python -m pytest tests |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
""" | ||
Copyright 2024 binary butterfly GmbH | ||
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt. | ||
""" | ||
|
||
from .converter import ReutlingenBikePushConverter |
69 changes: 69 additions & 0 deletions
69
src/parkapi_sources/converters/reutlingen_bike/converter.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
""" | ||
Copyright 2024 binary butterfly GmbH | ||
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt. | ||
""" | ||
|
||
import csv | ||
from io import StringIO | ||
|
||
import pyproj | ||
from validataclass.exceptions import ValidationError | ||
from validataclass.validators import DataclassValidator | ||
|
||
from parkapi_sources.converters.base_converter.push import CsvConverter | ||
from parkapi_sources.converters.reutlingen_bike.validation import ReutlingenBikeRowInput | ||
from parkapi_sources.exceptions import ImportParkingSiteException | ||
from parkapi_sources.models import RealtimeParkingSiteInput, SourceInfo, StaticParkingSiteInput | ||
|
||
|
||
class ReutlingenBikePushConverter(CsvConverter): | ||
proj: pyproj.Proj = pyproj.Proj(proj='utm', zone=32, ellps='WGS84', preserve_units=True) | ||
reutlingen_bike_row_validator = DataclassValidator(ReutlingenBikeRowInput) | ||
|
||
source_info = SourceInfo( | ||
uid='reutlingen', | ||
name='Stadt Reutlingen: Fahrrad-Abstellanlagen', | ||
public_url='https://www.reutlingen.de', | ||
has_realtime_data=False, | ||
) | ||
|
||
header_mapping: dict[str, str] = { | ||
'\ufeffSTANDORT': 'name', | ||
'ANZAHL': 'capacity', | ||
'ANLAGE': 'additional_name', | ||
'GEOM': 'coordinates', | ||
} | ||
|
||
def handle_csv_string( | ||
self, | ||
data: StringIO, | ||
) -> tuple[list[StaticParkingSiteInput | RealtimeParkingSiteInput], list[ImportParkingSiteException]]: | ||
return self.handle_csv(list(csv.reader(data, dialect='unix', delimiter=','))) | ||
|
||
def handle_csv(self, data: list[list]) -> tuple[list[StaticParkingSiteInput], list[ImportParkingSiteException]]: | ||
static_parking_site_inputs: list[StaticParkingSiteInput] = [] | ||
static_parking_site_errors: list[ImportParkingSiteException] = [] | ||
|
||
mapping: dict[str, int] = self.get_mapping_by_header(self.header_mapping, data[0]) | ||
|
||
# We start at row 2, as the first one is our header | ||
for row in data[1:]: | ||
input_dict: dict[str, str] = {} | ||
for field in self.header_mapping.values(): | ||
input_dict[field] = row[mapping[field]] | ||
|
||
try: | ||
reutlingen_bike_row_input: ReutlingenBikeRowInput = self.reutlingen_bike_row_validator.validate(input_dict) | ||
except ValidationError as e: | ||
static_parking_site_errors.append( | ||
ImportParkingSiteException( | ||
source_uid=self.source_info.uid, | ||
parking_site_uid=input_dict.get('name'), | ||
message=f'validation error for {input_dict}: {e.to_dict()}', | ||
), | ||
) | ||
continue | ||
|
||
static_parking_site_inputs.append(reutlingen_bike_row_input.to_parking_site_input(self.proj)) | ||
|
||
return static_parking_site_inputs, static_parking_site_errors |
43 changes: 43 additions & 0 deletions
43
src/parkapi_sources/converters/reutlingen_bike/validation.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
""" | ||
Copyright 2024 binary butterfly GmbH | ||
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt. | ||
""" | ||
|
||
from datetime import datetime, timezone | ||
|
||
import pyproj | ||
from validataclass.dataclasses import validataclass | ||
from validataclass.validators import DecimalValidator, IntegerValidator, StringValidator | ||
|
||
from parkapi_sources.models import StaticParkingSiteInput | ||
from parkapi_sources.models.enums import PurposeType | ||
from parkapi_sources.validators import PointCoordinateTupleValidator | ||
|
||
|
||
@validataclass | ||
class ReutlingenBikeRowInput: | ||
coordinates: list = PointCoordinateTupleValidator(DecimalValidator()) | ||
capacity: int = IntegerValidator(allow_strings=True) | ||
name: str = StringValidator(max_length=255) | ||
additional_name: str = StringValidator(max_length=255) | ||
|
||
def to_parking_site_input(self, proj: pyproj.Proj) -> StaticParkingSiteInput: | ||
coordinates = proj(float(self.coordinates[0]), float(self.coordinates[1]), inverse=True) | ||
lat = coordinates[1] | ||
lon = coordinates[0] | ||
|
||
if self.name and self.additional_name: | ||
name = f'{self.name}, {self.additional_name}' | ||
elif self.additional_name: | ||
name = self.additional_name | ||
else: | ||
name = self.name | ||
return StaticParkingSiteInput( | ||
uid=f'{name}: {lat}-{lon}', | ||
lat=lat, | ||
lon=lon, | ||
name=name, | ||
static_data_updated_at=datetime.now(tz=timezone.utc), | ||
capacity=self.capacity, | ||
purpose=PurposeType.BIKE, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
""" | ||
Copyright 2024 binary butterfly GmbH | ||
Use of this source code is governed by an MIT-style license that can be found in the LICENSE.txt. | ||
""" | ||
|
||
import re | ||
from typing import Any | ||
|
||
from validataclass.exceptions import ValidationError | ||
from validataclass.validators import ListValidator | ||
|
||
|
||
class PointCoordinateTupleValidator(ListValidator): | ||
PATTERN = re.compile(r'POINT \(([-+]?\d+\.\d+) ([-+]?\d+\.\d+)\)') | ||
|
||
def validate(self, input_data: Any, **kwargs) -> list: | ||
self._ensure_type(input_data, str) | ||
input_match = re.match(self.PATTERN, input_data) | ||
|
||
if input_match is None: | ||
raise ValidationError(code='invalid_tuple_input', reason='invalid point coordinate tuple input') | ||
|
||
input_data = [input_match.group(1), input_match.group(2)] | ||
|
||
return super().validate(input_data, **kwargs) |
Oops, something went wrong.