Skip to content

Commit

Permalink
Merge pull request #46 from mobidata-bw/push-realtime-data
Browse files Browse the repository at this point in the history
Push realtime data
  • Loading branch information
the-infinity authored Dec 13, 2023
2 parents 0d0d72d + b1832a0 commit 7503977
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 21 deletions.
32 changes: 32 additions & 0 deletions migrations/versions/2023-12-11-09-26-47_ccd3da2086d3_max_height.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""max height
Revision ID: ccd3da2086d3
Revises: a951c44669b8
Create Date: 2023-12-11 09:26:47.894699
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = 'ccd3da2086d3'
down_revision = 'a951c44669b8'
branch_labels = None
depends_on = None


def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('parking_site', schema=None) as batch_op:
batch_op.add_column(sa.Column('max_height', sa.Integer(), nullable=True))

# ### end Alembic commands ###


def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('parking_site', schema=None) as batch_op:
batch_op.drop_column('max_height')

# ### end Alembic commands ###
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ beautifulsoup4~=4.12.2
jsonschema~=4.20.0
python-dateutil~=2.8.2
python-decouple~=3.8.0
feedparser~=6.0.10
feedparser~=6.0.11
utm~=0.7.0

pyproj~=3.6.1

# binary butterfly shared libraries (https://git.sectio-aurea.org/public_libraries)
--extra-index-url https://git.sectio-aurea.org/api/v4/groups/262/-/packages/pypi/simple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
from webapp.common.logging.models import LogTag
from webapp.common.rest.exceptions import InvalidInputException, RestApiNotImplementedException
from webapp.models import ParkingSite, Source
from webapp.models.parking_site import ParkingSiteType
from webapp.models.parking_site import ParkingSiteType, OpeningStatus
from webapp.repositories import ParkingSiteRepository, SourceRepository
from webapp.repositories.exceptions import ObjectNotFoundException
from webapp.services.import_service import ParkingSiteGenericImportService

if TYPE_CHECKING:

Check failure on line 24 in webapp/admin_rest_api/generic_parking_sites/generic_parking_sites_handler.py

View workflow job for this annotation

GitHub Actions / lint (3)

Ruff (I001)

webapp/admin_rest_api/generic_parking_sites/generic_parking_sites_handler.py:6:1: I001 Import block is un-sorted or un-formatted
from webapp.converter.common.models import ImportSourceResult, StaticParkingSiteInput
from webapp.converter.common.models import ImportSourceResult
from webapp.converter.common.validators import StaticParkingSiteInput, RealtimeParkingSiteInput

Check failure on line 27 in webapp/admin_rest_api/generic_parking_sites/generic_parking_sites_handler.py

View workflow job for this annotation

GitHub Actions / lint (3)

Ruff (I001)

webapp/admin_rest_api/generic_parking_sites/generic_parking_sites_handler.py:25:1: I001 Import block is un-sorted or un-formatted

class GenericParkingSitesHandler(AdminApiBaseHandler):
Expand All @@ -48,26 +49,23 @@ def handle_json_data(self, source_uid: str, data: dict | list) -> 'ImportSourceR
import_service = self.parking_site_generic_import_service.push_converters[source_uid]

import_results = import_service.handle_json(data)
static_parking_site_inputs = import_results.static_parking_site_inputs

for static_parking_site_input in static_parking_site_inputs:
self._save_parking_site_input(source, static_parking_site_input)
self._handle_import_results(source, import_results)

return import_results

def handle_xml_data(self, source_uid: str, data: str) -> 'ImportSourceResult':
def handle_xml_data(self, source_uid: str, data: bytes) -> 'ImportSourceResult':
source = self._get_source(source_uid)
import_service = self.parking_site_generic_import_service.push_converters[source_uid]

try:
root_element = etree.fromstring(data, parser=etree.XMLParser(resolve_entities=False)) # noqa: S320
except ParseError as e:
raise InvalidInputException(message='Invalid XML file') from e

import_results = import_service.handle_xml(root_element)
static_parking_site_inputs = import_results.static_parking_site_inputs

for static_parking_site_input in static_parking_site_inputs:
self._save_parking_site_input(source, static_parking_site_input)
self._handle_import_results(source, import_results)

return import_results

Expand All @@ -83,10 +81,8 @@ def handle_csv_data(self, source_uid: str, data: str) -> 'ImportSourceResult':
import_results = import_service.handle_csv(rows)
except Exception as e:
raise InvalidInputException(message=f'Invalid input: {getattr(e, "message", "unknown reason")}') from e
static_parking_site_inputs = import_results.static_parking_site_inputs

for static_parking_site_input in static_parking_site_inputs:
self._save_parking_site_input(source, static_parking_site_input)
self._handle_import_results(source, import_results)

return import_results

Expand All @@ -104,10 +100,8 @@ def handle_xlsx_data(self, source_uid: str, data: bytes) -> 'ImportSourceResult'
import_results = import_service.handle_xlsx(workbook)
except Exception as e:
raise InvalidInputException(message=f'Invalid input: {getattr(e, "message", "unknown reason")}') from e
static_parking_site_inputs = import_results.static_parking_site_inputs

for static_parking_site_input in static_parking_site_inputs:
self._save_parking_site_input(source, static_parking_site_input)
self._handle_import_results(source, import_results)

return import_results

Expand All @@ -125,7 +119,16 @@ def _get_source(self, source_uid: str) -> Source:

return source

def _save_parking_site_input(self, source: Source, static_parking_site_input: 'StaticParkingSiteInput'):
def _handle_import_results(self, source: Source, import_results: 'ImportSourceResult'):
if import_results.static_parking_site_inputs:
for static_parking_site_input in import_results.static_parking_site_inputs:
self._save_static_parking_site_input(source, static_parking_site_input)

if import_results.realtime_parking_site_inputs:
for realtime_parking_site_inputs in import_results.realtime_parking_site_inputs:
self._save_realtime_parking_site_input(source, realtime_parking_site_inputs)

def _save_static_parking_site_input(self, source: Source, static_parking_site_input: 'StaticParkingSiteInput'):
try:
parking_site = self.parking_site_repository.fetch_parking_site_by_source_id_and_external_uid(
source_id=source.id,
Expand All @@ -144,3 +147,19 @@ def _save_parking_site_input(self, source: Source, static_parking_site_input: 'S
setattr(parking_site, key, value)

self.parking_site_repository.save_parking_site(parking_site)

def _save_realtime_parking_site_input(self, source: Source, realtime_parking_site_input: 'RealtimeParkingSiteInput'):
parking_site = self.parking_site_repository.fetch_parking_site_by_source_id_and_external_uid(
source_id=source.id,
original_uid=realtime_parking_site_input.uid,
)

for key, value in realtime_parking_site_input.to_dict().items():
if key in ['uid']:
continue
if key == 'realtime_opening_status' and value:
value = OpeningStatus[value.name]
setattr(parking_site, key, value)

self.parking_site_repository.save_parking_site(parking_site)

Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _generate_response(import_result: 'ImportSourceResult') -> dict[str, dict[st
if import_result.realtime_parking_site_errors is not None:
result['summary']['realtime_error_count'] = len(import_result.realtime_parking_site_errors)
result['errors']['realtime'] = [
{'message': error.message, 'uid': error.uid} for error in import_result.static_parking_site_errors
{'message': error.message, 'uid': error.uid} for error in import_result.realtime_parking_site_errors
]
return result

Expand Down Expand Up @@ -128,7 +128,7 @@ class GenericParkingSitesXmlMethodView(GenericParkingSitesMethodView):
def post(self):
result = self.generic_parking_sites_handler.handle_xml_data(
source_uid=self.request_helper.get_basicauth_username(),
data=self.request_helper.get_request_body_text(),
data=self.request_helper.get_request_body(),
)

return jsonify(self._generate_response(result))
Expand Down
1 change: 1 addition & 0 deletions webapp/models/parking_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class ParkingSite(BaseModel):
type: Mapped[Optional[ParkingSiteType]] = mapped_column(SqlalchemyEnum(ParkingSiteType), nullable=True)

max_stay: Mapped[Optional[int]] = mapped_column(Integer(), nullable=True)
max_height: Mapped[Optional[int]] = mapped_column(Integer(), nullable=True)
has_lighting: Mapped[Optional[bool]] = mapped_column(Boolean(), nullable=True)
fee_description: Mapped[Optional[str]] = mapped_column(String(256), nullable=True)
has_fee: Mapped[Optional[bool]] = mapped_column(Boolean(), nullable=True)
Expand Down

0 comments on commit 7503977

Please sign in to comment.