Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

demonstrator for source/record refactoring #1928

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions dev/config/pyramid_oereb.yml.mako

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ def __init__(self, **kwargs):
"""
config_parser = StandardThemeConfigParser(**kwargs)
self.models = config_parser.get_models()
BaseDatabaseSource.__init__(self, **{**kwargs['source']['params'],
'model': self.models.PublicLawRestriction})
bds_kwargs = {
'model': self.models.Geometry,
'db_connection': kwargs.get('source').get('params').get('db_connection')
'db_connection': kwargs.get('source').get('params').get('db_connection'),
'record_class': self._geometry_record_class,
}

BaseDatabaseSource.__init__(self, **bds_kwargs)
Expand Down
48 changes: 13 additions & 35 deletions pyramid_oereb/contrib/data_sources/standard/sources/address.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,26 @@
# -*- coding: utf-8 -*-
from geoalchemy2.elements import _SpatialElement
from geoalchemy2.shape import to_shape
from sqlalchemy.orm.exc import NoResultFound

from pyramid_oereb.core.sources import BaseDatabaseSource
from pyramid_oereb.core.sources.address import AddressBaseSource


class DatabaseSource(BaseDatabaseSource, AddressBaseSource):
class DatabaseSource(BaseDatabaseSource):

def read(self, params, street_name, zip_code, street_number):
def filter(self, query, params, street_name, zip_code, street_number):
"""
The read method to access the standard database structure. It uses SQL-Alchemy for querying. It tries
to find the items via passed arguments. If there was no result found it goes on with assigning an
empty list as records instance attribute.
The filter method only selects some specific results from the standard database structure.
It uses SQL-Alchemy for querying via passed arguments.

Args:
params (pyramid_oereb.views.webservice.Parameter): The parameters of the extract request.
street_name (unicode): The name of the street for the desired address.
zip_code (int): The postal zipcode for the desired address.
street_number (str): The house or so called street number of the desired address.
"""
session = self._adapter_.get_session(self._key_)
try:
query = session.query(self._model_)
results = [query.filter(
self._model_.street_name == street_name
).filter(
self._model_.zip_code == zip_code
).filter(
self._model_.street_number == street_number
).one()]

self.records = []
for result in results:
self.records.append(self._record_class_(
result.street_name,
result.zip_code,
result.street_number,
to_shape(result.geom) if isinstance(result.geom, _SpatialElement) else None
))

except NoResultFound:
self.records = []

finally:
session.close()
filtered_query = query.filter(
self._model_.street_name == street_name
).filter(
self._model_.zip_code == zip_code
).filter(
self._model_.street_number == street_number
).limit(1)

return filtered_query
5 changes: 4 additions & 1 deletion pyramid_oereb/contrib/data_sources/standard/sources/plr.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,12 @@ def __init__(self, **kwargs):
"""
config_parser = StandardThemeConfigParser(**kwargs)
self.models = config_parser.get_models()
BaseDatabaseSource.__init__(self, **{**kwargs['source']['params'],
'model': self.models.PublicLawRestriction})
bds_kwargs = {
'model': self.models.Geometry,
'db_connection': kwargs.get('source').get('params').get('db_connection')
'db_connection': kwargs.get('source').get('params').get('db_connection'),
'record_class': self._geometry_record_class,
}

BaseDatabaseSource.__init__(self, **bds_kwargs)
Expand Down
3 changes: 1 addition & 2 deletions pyramid_oereb/contrib/data_sources/swisstopo/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@

from pyramid_oereb import Config
from pyramid_oereb.core.records.address import AddressRecord
from pyramid_oereb.core.sources.address import AddressBaseSource


class AddressGeoAdminSource(AddressBaseSource):
class AddressGeoAdminSource():

Check warning on line 10 in pyramid_oereb/contrib/data_sources/swisstopo/address.py

View check run for this annotation

Codecov / codecov/patch

pyramid_oereb/contrib/data_sources/swisstopo/address.py#L10

Added line #L10 was not covered by tests
"""
An address source that uses the federal GeoAdmin API to return the geo location of a postal address.
"""
Expand Down
9 changes: 8 additions & 1 deletion pyramid_oereb/core/records/address.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
from geoalchemy2.elements import _SpatialElement
from geoalchemy2.shape import to_shape
from shapely import is_geometry


class AddressRecord(object):
Expand All @@ -24,4 +27,8 @@ def __init__(self, street_name, zip_code, street_number, geom):
self.street_name = street_name
self.zip_code = zip_code
self.street_number = street_number
self.geom = geom
self.geom = None
if is_geometry(geom) or isinstance(geom, str):
self.geom = geom
elif isinstance(geom, _SpatialElement):
self.geom = to_shape(geom)
21 changes: 8 additions & 13 deletions pyramid_oereb/core/records/municipality.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
# -*- coding: utf-8 -*-
from dataclasses import dataclass
from typing import Any


class MunicipalityRecord(object):
@dataclass
class MunicipalityRecord:
"""
The base document class.

Expand All @@ -11,15 +14,7 @@ class MunicipalityRecord(object):
published (bool): Is this municipality ready for publishing via server.
geom (str or None): The geometry which is representing this municipality as a WKT.
"""
def __init__(self, fosnr, name, published, geom=None):
"""
Args:
fosnr (int): The unique id bfs of the municipality.
name (unicode): The zipcode for this address.
published (bool): Is this municipality ready for publishing via server.
geom (str or None): The geometry which is representing this municipality as a WKT.
"""
self.fosnr = fosnr
self.name = name
self.published = published
self.geom = geom
fosnr: int
name: str
published: bool
geom: Any = None
2 changes: 1 addition & 1 deletion pyramid_oereb/core/renderer/extract/json_.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from pyramid_oereb.core import get_multilingual_element
from pyramid_oereb.core.records.documents import DocumentRecord
from pyramid_oereb.core.records.theme import ThemeRecord
from pyramid_oereb.core.sources.plr import PlrRecord
from pyramid_oereb.core.records.plr import PlrRecord

from pyramid_oereb.core.renderer import Base
from pyramid_oereb.core.views.webservice import Parameter
Expand Down
34 changes: 34 additions & 0 deletions pyramid_oereb/core/sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from pyramid.config import ConfigurationError
from pyramid.path import DottedNameResolver
from sqlalchemy import text
from sqlalchemy.orm.exc import NoResultFound

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -55,6 +56,15 @@
self._model_ = DottedNameResolver().maybe_resolve(kwargs.get('model'))
else:
raise ConfigurationError('"model" for source has to be defined in used yaml configuration file')
record_class = kwargs.get('record_class')
if record_class:
if isinstance(record_class, dict):
for key, current_class in record_class.items():
self.__setattr__(f"_{key}_record_class", DottedNameResolver().maybe_resolve(current_class))
else:
self._record_class_ = DottedNameResolver().maybe_resolve(kwargs.get('record_class'))
else:
raise ConfigurationError('"record_class" for source has to be defined in used yaml configuration file')

Check warning on line 67 in pyramid_oereb/core/sources/__init__.py

View check run for this annotation

Codecov / codecov/patch

pyramid_oereb/core/sources/__init__.py#L67

Added line #L67 was not covered by tests
# check if database is available and wait until it might be or raise error
timeout_target = time.time() + self.TIMEOUT
db_healthy = False
Expand Down Expand Up @@ -88,3 +98,27 @@
return True
except Exception:
return False

@staticmethod
def filter(self, query, *args, **kwargs):
"""
abstract method to be overridden
"""
return query

Check warning on line 107 in pyramid_oereb/core/sources/__init__.py

View check run for this annotation

Codecov / codecov/patch

pyramid_oereb/core/sources/__init__.py#L107

Added line #L107 was not covered by tests

def read(self, *args, **kwargs):
session = self._adapter_.get_session(self._key_)
try:
query = session.query(self._model_)
results = self.filter(query, *args, **kwargs).all()

self.records = []
for result in results:
res_dict = {c.name: result.__getattribute__(c.name) for c in result.__table__.columns}
self.records.append(self._record_class_(**res_dict))

except NoResultFound:
self.records = []

Check warning on line 121 in pyramid_oereb/core/sources/__init__.py

View check run for this annotation

Codecov / codecov/patch

pyramid_oereb/core/sources/__init__.py#L121

Added line #L121 was not covered by tests

finally:
session.close()
27 changes: 0 additions & 27 deletions pyramid_oereb/core/sources/address.py

This file was deleted.

37 changes: 0 additions & 37 deletions pyramid_oereb/core/sources/plr.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
# -*- coding: utf-8 -*-
import logging

from pyramid_oereb.core.records.documents import DocumentRecord
from pyramid_oereb.core.records.embeddable import DatasourceRecord
from pyramid_oereb.core.records.disclaimer import DisclaimerRecord
from pyramid_oereb.core.records.geometry import GeometryRecord
from pyramid_oereb.core.records.glossary import GlossaryRecord
from pyramid_oereb.core.records.law_status import LawStatusRecord
from pyramid_oereb.core.records.office import OfficeRecord
from pyramid_oereb.core.records.plr import PlrRecord
from pyramid_oereb.core.records.view_service import ViewServiceRecord, LegendEntryRecord
from pyramid_oereb.core.sources import Base

log = logging.getLogger(__name__)
Expand All @@ -25,17 +16,6 @@ class PlrBaseSource(Base):
datasource (list of pyramid_oereb.lib.records.embeddable.DatasourceRecord): List of data source
records used for the additional data in flavour `embeddable`.
"""
_documents_record_class = DocumentRecord
_disclaimer_record_class = DisclaimerRecord
_geometry_record_class = GeometryRecord
_glossary_record_class = GlossaryRecord
_legend_entry_record_class = LegendEntryRecord
_office_record_class = OfficeRecord
_plr_record_class = PlrRecord
_view_service_record_class = ViewServiceRecord
_law_status_record_class = LawStatusRecord
_datasource_record_class = DatasourceRecord

datasource = list()

def __init__(self, **kwargs):
Expand Down Expand Up @@ -69,20 +49,3 @@ def info(self):
dict: The info dictionary.
"""
return self._plr_info

def read(self, params, real_estate, bbox):
"""
Every public law restriction source has to implement a read method. This method must accept the two
key word parameters. If you want adapt to your own source for real estates, this is the point where
to hook in.

Args:
params (pyramid_oereb.views.webservice.Parameter): The parameters of the extract request.
real_estate (pyramid_oereb.lib.records.real_estate.RealEstateRecord): The real estate which is
used as filter to find all related public law restrictions.
bbox (shapely.geometry.base.BaseGeometry): The bounding box which is used as a pre-filter to find
all public law restrictions. This is related to the fact that we need to provide not only the
public law restrictions that are related to the real estate but also the ones which are in
the visible extent of the map.
"""
self.records = list()
14 changes: 13 additions & 1 deletion tests/contrib.data_sources.oereblex/sources/test_plr_oereblex.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,19 @@ def plr_source_params(db_connection):
"db_connection": db_connection,
"model_factory": "pyramid_oereb.contrib.data_sources.standard."
"models.theme.model_factory_string_pk",
"schema_name": "forest_perimeters"
"schema_name": "forest_perimeters",
"record_class": {
"documents": "pyramid_oereb.core.records.documents.DocumentRecord",
"disclaimer": "pyramid_oereb.core.records.disclaimer.DisclaimerRecord",
"geometry": "pyramid_oereb.core.records.geometry.GeometryRecord",
"glossary": "pyramid_oereb.core.records.glossary.GlossaryRecord",
"legend_entry": "pyramid_oereb.core.records.view_service.LegendEntryRecord",
"office": "pyramid_oereb.core.records.office.OfficeRecord",
"plr": "pyramid_oereb.core.records.plr.PlrRecord",
"view_service": "pyramid_oereb.core.records.view_service.ViewServiceRecord",
"law_status": "pyramid_oereb.core.records.law_status.LawStatusRecord",
"datasource": "pyramid_oereb.core.records.embeddable.DatasourceRecord",
}
}
},
"hooks": {
Expand Down
33 changes: 23 additions & 10 deletions tests/contrib.data_sources.standard/sources/test_adress.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
def address_source_params(db_connection):
yield {
"db_connection": db_connection,
"model": "pyramid_oereb.contrib.data_sources.standard.models.main.Address"
"model": "pyramid_oereb.contrib.data_sources.standard.models.main.Address",
"record_class": "pyramid_oereb.core.records.address.AddressRecord"
}


Expand All @@ -28,13 +29,18 @@ def one_address_result_session(session, query, wkb_point):

class Query(query):

def one(self):
return Address(**{
'street_name': 'teststreet',
'street_number': '99a',
'zip_code': 4050,
'geom': wkb_point
})
def limit(self, limit):
class LimitedQuery(Query):
def all(self):
return [
Address(**{
'street_name': 'teststreet',
'street_number': '99a',
'zip_code': 4050,
'geom': wkb_point
})
]
return LimitedQuery()

class Session(session):

Expand All @@ -49,8 +55,15 @@ def no_result_session(session, query, wkb_point):

class Query(query):

def one(self):
raise NoResultFound
class LimitedQuery(query):
def all(self):
raise NoResultFound

def limit(self, limit):
return self.LimitedQuery()

def filter(self, term):
return self

class Session(session):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
def disclaimer_source_params(db_connection):
yield {
"db_connection": db_connection,
"model": "pyramid_oereb.contrib.data_sources.standard.models.main.Disclaimer"
"model": "pyramid_oereb.contrib.data_sources.standard.models.main.Disclaimer",
"record_class": "pyramid_oereb.core.records.disclaimer.DisclaimerRecord"
}


Expand Down
3 changes: 2 additions & 1 deletion tests/contrib.data_sources.standard/sources/test_document.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
def document_source_params(db_connection):
yield {
"db_connection": db_connection,
"model": "pyramid_oereb.contrib.data_sources.standard.models.main.Document"
"model": "pyramid_oereb.contrib.data_sources.standard.models.main.Document",
"record_class": "pyramid_oereb.core.records.documents.DocumentRecord"
}


Expand Down
Loading
Loading