Revert pydantic models to v1 version (#1584)

* Revert pydantic models to v1 version

Revert pydantic models to v1 version

Revert pydantic models to v1 version

Revert pydantic models to v1 version

* Add initial tests for models

Add initial tests for models

* Revert pydantic models to v1 version

Revert pydantic models to v1 version

Revert pydantic models to v1 version

Revert pydantic models to v1 version

* Add initial tests for models

Add initial tests for models

* Fix and replace methods from pydantic v2

* Add more tests for cql models
This commit is contained in:
Francesco Bartoli
2024-03-10 13:58:21 +01:00
committed by GitHub
parent 54688969b0
commit e69a9744a2
11 changed files with 282 additions and 173 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
# Francesco Bartoli <xbartolone@gmail.com> # Francesco Bartoli <xbartolone@gmail.com>
# #
# Copyright (c) 2022 Tom Kralidis # Copyright (c) 2022 Tom Kralidis
# Copyright (c) 2023 Francesco Bartoli # Copyright (c) 2024 Francesco Bartoli
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
+5 -3
View File
@@ -3,8 +3,10 @@
# ================================================================= # =================================================================
# #
# Authors: Sander Schaminee <sander.schaminee@geocat.net> # Authors: Sander Schaminee <sander.schaminee@geocat.net>
# Francesco Bartoli <xbartolone@gmail.com>
# #
# Copyright (c) 2023 Sander Schaminee # Copyright (c) 2023 Sander Schaminee
# Copyright (c) 2024 Francesco Bartoli
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -34,7 +36,7 @@ from pydantic import BaseModel, Field
class APIRules(BaseModel): class APIRules(BaseModel):
""" Pydantic model for API design rules that must be adhered to. """ """ Pydantic model for API design rules that must be adhered to. """
api_version: str = Field(pattern=r'^\d+\.\d+\..+$', api_version: str = Field(regex=r'^\d+\.\d+\..+$',
description="Semantic API version number.") description="Semantic API version number.")
url_prefix: str = Field( url_prefix: str = Field(
"", "",
@@ -60,11 +62,11 @@ class APIRules(BaseModel):
""" Returns a new APIRules instance for the current API version """ Returns a new APIRules instance for the current API version
and configured rules. """ and configured rules. """
obj = { obj = {
k: v for k, v in rules_config.items() if k in APIRules.model_fields k: v for k, v in rules_config.items() if k in APIRules.__fields__
} }
# Validation will fail if required `api_version` is missing # Validation will fail if required `api_version` is missing
# or if `api_version` is not a semantic version number # or if `api_version` is not a semantic version number
return APIRules.model_validate(obj) return APIRules.parse_obj(obj)
@property @property
def response_headers(self) -> dict: def response_headers(self) -> dict:
+128 -128
View File
@@ -7,7 +7,7 @@
# #
# Authors: Francesco Bartoli <xbartolone@gmail.com> # Authors: Francesco Bartoli <xbartolone@gmail.com>
# #
# Copyright (c) 2021 Francesco Bartoli # Copyright (c) 2024 Francesco Bartoli
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -35,11 +35,11 @@
from datetime import date, datetime from datetime import date, datetime
from typing import Any, List, Literal, Optional, Union from typing import Any, List, Literal, Optional, Union
from pydantic import BaseModel, Field, RootModel from pydantic import BaseModel, Field
class CQLModel(RootModel): class CQLModel(BaseModel):
root: 'Union[\n ComparisonPredicate,\n SpatialPredicate,\n TemporalPredicate,\n AndExpression\n ]' __root__: 'Union[\n ComparisonPredicate,\n SpatialPredicate,\n TemporalPredicate,\n AndExpression\n ]'
class AndExpression(BaseModel): class AndExpression(BaseModel):
@@ -58,16 +58,16 @@ class PropertyRef(BaseModel):
property: 'Optional[str]' = None property: 'Optional[str]' = None
class ScalarLiteral(RootModel): class ScalarLiteral(BaseModel):
root: 'Union[str, float, bool]' __root__: 'Union[str, float, bool]'
class Bbox(RootModel): class Bbox(BaseModel):
root: 'List[float]' __root__: 'List[float]'
class LinestringCoordinate(RootModel): class LinestringCoordinate(BaseModel):
root: 'List[Any]' __root__: 'List[Any]'
class Linestring(BaseModel): class Linestring(BaseModel):
@@ -76,8 +76,8 @@ class Linestring(BaseModel):
bbox: 'Optional[List[float]]' = Field(None) bbox: 'Optional[List[float]]' = Field(None)
class MultilineStringCoordinate(RootModel): class MultilineStringCoordinate(BaseModel):
root: 'List[Any]' __root__: 'List[Any]'
class Multilinestring(BaseModel): class Multilinestring(BaseModel):
@@ -92,8 +92,8 @@ class Multipoint(BaseModel):
bbox: 'Optional[List[float]]' = Field(None) bbox: 'Optional[List[float]]' = Field(None)
class MultipolygonCoordinateItem(RootModel): class MultipolygonCoordinateItem(BaseModel):
root: 'List[Any]' __root__: 'List[Any]'
class Multipolygon(BaseModel): class Multipolygon(BaseModel):
@@ -108,8 +108,8 @@ class Point(BaseModel):
bbox: 'Optional[List[float]]' = Field(None) bbox: 'Optional[List[float]]' = Field(None)
class PolygonCoordinatesItem(RootModel): class PolygonCoordinatesItem(BaseModel):
root: 'List[Any]' __root__: 'List[Any]'
class Polygon(BaseModel): class Polygon(BaseModel):
@@ -118,56 +118,56 @@ class Polygon(BaseModel):
bbox: 'Optional[List[float]]' = Field(None) bbox: 'Optional[List[float]]' = Field(None)
class TimeString(RootModel): class TimeString(BaseModel):
root: 'Union[date, datetime]' __root__: 'Union[date, datetime]'
class EnvelopeLiteral(BaseModel): class EnvelopeLiteral(BaseModel):
bbox: 'Bbox' bbox: 'Bbox'
class GeometryLiteral(RootModel): class GeometryLiteral(BaseModel):
root: 'Union[\n Point, Linestring, Polygon, Multipoint, Multilinestring, Multipolygon\n ]' __root__: 'Union[\n Point,\n Linestring,\n Polygon,\n Multipoint,\n Multilinestring,\n Multipolygon\n ]'
class TypedTimeString(BaseModel): class TypedTimeString(BaseModel):
datetime: 'TimeString' datetime: 'TimeString'
class PeriodString(RootModel): class PeriodString(BaseModel):
root: 'List[Union[TimeString, str]]' = Field(...) __root__: 'List[Union[TimeString, str]]' = Field(...)
class SpatialLiteral(RootModel): class SpatialLiteral(BaseModel):
root: 'Union[GeometryLiteral, EnvelopeLiteral]' __root__: 'Union[GeometryLiteral, EnvelopeLiteral]'
class TemporalLiteral(RootModel): class TemporalLiteral(BaseModel):
root: 'Union[TimeString, PeriodString]' __root__: 'Union[TimeString, PeriodString]'
class TypedPeriodString(BaseModel): class TypedPeriodString(BaseModel):
datetime: 'PeriodString' datetime: 'PeriodString'
class TypedTemporalLiteral(RootModel): class TypedTemporalLiteral(BaseModel):
root: 'Union[TypedTimeString, TypedPeriodString]' __root__: 'Union[TypedTimeString, TypedPeriodString]'
class ArrayPredicate(RootModel): class ArrayPredicate(BaseModel):
root: 'Union[\n AequalsExpression,\n AcontainsExpression,\n AcontainedByExpression,\n AoverlapsExpression,\n ]' __root__: 'Union[\n AequalsExpression,\n AcontainsExpression,\n AcontainedByExpression,\n AoverlapsExpression,\n ]'
class ComparisonPredicate(RootModel): class ComparisonPredicate(BaseModel):
root: 'Union[\n BinaryComparisonPredicate,\n IsLikePredicate,\n IsBetweenPredicate,\n IsInListPredicate,\n IsNullPredicate,\n ]' __root__: 'Union[\n BinaryComparisonPredicate,\n IsLikePredicate,\n IsBetweenPredicate,\n IsInListPredicate,\n IsNullPredicate,\n ]'
class SpatialPredicate(RootModel): class SpatialPredicate(BaseModel):
root: 'Union[\n IntersectsExpression,\n EqualsExpression,\n DisjointExpression,\n TouchesExpression,\n WithinExpression,\n OverlapsExpression,\n CrossesExpression,\n ContainsExpression,\n ]' __root__: 'Union[\n IntersectsExpression,\n EqualsExpression,\n DisjointExpression,\n TouchesExpression,\n WithinExpression,\n OverlapsExpression,\n CrossesExpression,\n ContainsExpression,\n ]'
class TemporalPredicate(RootModel): class TemporalPredicate(BaseModel):
root: 'Union[\n BeforeExpression,\n AfterExpression,\n MeetsExpression,\n MetbyExpression,\n ToverlapsExpression,\n OverlappedbyExpression,\n BeginsExpression,\n BegunbyExpression,\n DuringExpression,\n TcontainsExpression,\n EndsExpression,\n EndedbyExpression,\n TequalsExpression,\n AnyinteractsExpression,\n ]' __root__: 'Union[\n BeforeExpression,\n AfterExpression,\n MeetsExpression,\n MetbyExpression,\n ToverlapsExpression,\n OverlappedbyExpression,\n BeginsExpression,\n BegunbyExpression,\n DuringExpression,\n TcontainsExpression,\n EndsExpression,\n EndedbyExpression,\n TequalsExpression,\n AnyinteractsExpression,\n ]'
class AcontainedByExpression(BaseModel): class AcontainedByExpression(BaseModel):
@@ -206,8 +206,8 @@ class BegunbyExpression(BaseModel):
begunby: 'TemporalOperands' begunby: 'TemporalOperands'
class BinaryComparisonPredicate(RootModel): class BinaryComparisonPredicate(BaseModel):
root: 'Union[\n EqExpression, LtExpression, GtExpression, LteExpression, GteExpression\n ]' __root__: 'Union[\n EqExpression, LtExpression, GtExpression, LteExpression, GteExpression\n ]'
class ContainsExpression(BaseModel): class ContainsExpression(BaseModel):
@@ -244,8 +244,8 @@ class IntersectsExpression(BaseModel):
class Between(BaseModel): class Between(BaseModel):
value: 'ValueExpression' value: 'ValueExpression'
lower: 'Optional[ScalarExpression]' = Field(None) lower: 'ScalarExpression' = Field(None)
upper: 'Optional[ScalarExpression]' = Field(None) upper: 'ScalarExpression' = Field(None)
class IsBetweenPredicate(BaseModel): class IsBetweenPredicate(BaseModel):
@@ -310,8 +310,8 @@ class WithinExpression(BaseModel):
within: 'SpatialOperands' within: 'SpatialOperands'
class ArrayExpression(RootModel): class ArrayExpression(BaseModel):
root: 'List[Union[PropertyRef, FunctionRef, ArrayLiteral]]' = Field( __root__: 'List[Union[PropertyRef, FunctionRef, ArrayLiteral]]' = Field(
... # , max_items=2, min_items=2 ... # , max_items=2, min_items=2
) )
@@ -336,45 +336,45 @@ class LteExpression(BaseModel):
lte: 'ScalarOperands' lte: 'ScalarOperands'
class ScalarExpression(RootModel): class ScalarExpression(BaseModel):
root: 'Union[ScalarLiteral, PropertyRef,\n FunctionRef, ArithmeticExpression]' __root__: 'Union[ScalarLiteral,\n PropertyRef,\n FunctionRef,\n ArithmeticExpression]'
class ScalarOperands(RootModel): class ScalarOperands(BaseModel):
root: 'List[ScalarExpression]' = Field(...) __root__: 'List[ScalarExpression]' = Field(...)
class SpatialOperands(RootModel): class SpatialOperands(BaseModel):
root: 'List[GeomExpression]' = Field(...) __root__: 'List[GeomExpression]' = Field(...)
class TemporalOperands(RootModel): class TemporalOperands(BaseModel):
root: 'List[TemporalExpression]' = Field(...) __root__: 'List[TemporalExpression]' = Field(...)
# , max_items=2, min_items=2) # , max_items=2, min_items=2)
class ValueExpression(RootModel): class ValueExpression(BaseModel):
root: 'Union[ScalarExpression, SpatialLiteral, TypedTemporalLiteral]' __root__: 'Union[ScalarExpression, SpatialLiteral, TypedTemporalLiteral]'
class ArithmeticExpression(RootModel): class ArithmeticExpression(BaseModel):
root: 'Union[AddExpression, SubExpression, MulExpression, DivExpression]' __root__: 'Union[AddExpression, SubExpression, MulExpression, DivExpression]'
class ArrayLiteral(RootModel): class ArrayLiteral(BaseModel):
root: 'List[\n Union[\n ScalarLiteral,\n SpatialLiteral,\n TypedTemporalLiteral,\n PropertyRef,\n FunctionRef,\n ArithmeticExpression,\n ArrayLiteral,\n ]\n ]' __root__: 'List[\n Union[\n ScalarLiteral,\n SpatialLiteral,\n TypedTemporalLiteral,\n PropertyRef,\n FunctionRef,\n ArithmeticExpression,\n ArrayLiteral,\n ]\n ]'
class FunctionRef(BaseModel): class FunctionRef(BaseModel):
function: 'Function' function: 'Function'
class GeomExpression(RootModel): class GeomExpression(BaseModel):
root: 'Union[SpatialLiteral, PropertyRef, FunctionRef]' __root__: 'Union[SpatialLiteral, PropertyRef, FunctionRef]'
class TemporalExpression(RootModel): class TemporalExpression(BaseModel):
root: 'Union[TemporalLiteral, PropertyRef, FunctionRef]' __root__: 'Union[TemporalLiteral, PropertyRef, FunctionRef]'
# here # here
@@ -384,7 +384,7 @@ class AddExpression(BaseModel):
# here # here
class DivExpression(BaseModel): class DivExpression(BaseModel):
div_: 'Optional[ArithmeticOperands]' = Field(None, alias='/') div_: 'ArithmeticOperands' = Field(None, alias='/')
class Function(BaseModel): class Function(BaseModel):
@@ -402,68 +402,68 @@ class SubExpression(BaseModel):
sub_: 'ArithmeticOperands' = Field(..., alias='-') sub_: 'ArithmeticOperands' = Field(..., alias='-')
class ArithmeticOperands(RootModel): class ArithmeticOperands(BaseModel):
root: 'List[\n Union[ArithmeticExpression, PropertyRef, FunctionRef, float]\n ]' = Field(...) __root__: 'List[\n Union[ArithmeticExpression, PropertyRef, FunctionRef, float]\n ]' = Field(...)
CQLModel.model_rebuild() CQLModel.update_forward_refs()
AndExpression.model_rebuild() AndExpression.update_forward_refs()
ArrayPredicate.model_rebuild() ArrayPredicate.update_forward_refs()
ComparisonPredicate.model_rebuild() ComparisonPredicate.update_forward_refs()
SpatialPredicate.model_rebuild() SpatialPredicate.update_forward_refs()
TemporalPredicate.model_rebuild() TemporalPredicate.update_forward_refs()
AcontainedByExpression.model_rebuild() AcontainedByExpression.update_forward_refs()
AcontainsExpression.model_rebuild() AcontainsExpression.update_forward_refs()
AequalsExpression.model_rebuild() AequalsExpression.update_forward_refs()
AfterExpression.model_rebuild() AfterExpression.update_forward_refs()
AnyinteractsExpression.model_rebuild() AnyinteractsExpression.update_forward_refs()
AoverlapsExpression.model_rebuild() AoverlapsExpression.update_forward_refs()
BeforeExpression.model_rebuild() BeforeExpression.update_forward_refs()
BeginsExpression.model_rebuild() BeginsExpression.update_forward_refs()
BegunbyExpression.model_rebuild() BegunbyExpression.update_forward_refs()
BinaryComparisonPredicate.model_rebuild() BinaryComparisonPredicate.update_forward_refs()
ContainsExpression.model_rebuild() ContainsExpression.update_forward_refs()
CrossesExpression.model_rebuild() CrossesExpression.update_forward_refs()
DisjointExpression.model_rebuild() DisjointExpression.update_forward_refs()
DuringExpression.model_rebuild() DuringExpression.update_forward_refs()
EndedbyExpression.model_rebuild() EndedbyExpression.update_forward_refs()
EndsExpression.model_rebuild() EndsExpression.update_forward_refs()
EqualsExpression.model_rebuild() EqualsExpression.update_forward_refs()
IntersectsExpression.model_rebuild() IntersectsExpression.update_forward_refs()
Between.model_rebuild() Between.update_forward_refs()
In.model_rebuild() In.update_forward_refs()
IsBetweenPredicate.model_rebuild() IsBetweenPredicate.update_forward_refs()
IsLikePredicate.model_rebuild() IsLikePredicate.update_forward_refs()
IsNullPredicate.model_rebuild() IsNullPredicate.update_forward_refs()
ValueExpression.model_rebuild() ValueExpression.update_forward_refs()
MeetsExpression.model_rebuild() MeetsExpression.update_forward_refs()
MetbyExpression.model_rebuild() MetbyExpression.update_forward_refs()
OverlappedbyExpression.model_rebuild() OverlappedbyExpression.update_forward_refs()
OverlapsExpression.model_rebuild() OverlapsExpression.update_forward_refs()
TcontainsExpression.model_rebuild() TcontainsExpression.update_forward_refs()
TequalsExpression.model_rebuild() TequalsExpression.update_forward_refs()
TouchesExpression.model_rebuild() TouchesExpression.update_forward_refs()
ToverlapsExpression.model_rebuild() ToverlapsExpression.update_forward_refs()
WithinExpression.model_rebuild() WithinExpression.update_forward_refs()
ArrayExpression.model_rebuild() ArrayExpression.update_forward_refs()
EqExpression.model_rebuild() EqExpression.update_forward_refs()
GtExpression.model_rebuild() GtExpression.update_forward_refs()
GteExpression.model_rebuild() GteExpression.update_forward_refs()
LtExpression.model_rebuild() LtExpression.update_forward_refs()
LteExpression.model_rebuild() LteExpression.update_forward_refs()
ScalarExpression.model_rebuild() ScalarExpression.update_forward_refs()
ScalarOperands.model_rebuild() ScalarOperands.update_forward_refs()
SpatialOperands.model_rebuild() SpatialOperands.update_forward_refs()
TemporalOperands.model_rebuild() TemporalOperands.update_forward_refs()
ArithmeticExpression.model_rebuild() ArithmeticExpression.update_forward_refs()
ArrayLiteral.model_rebuild() ArrayLiteral.update_forward_refs()
ScalarLiteral.model_rebuild() ScalarLiteral.update_forward_refs()
PropertyRef.model_rebuild() PropertyRef.update_forward_refs()
FunctionRef.model_rebuild() FunctionRef.update_forward_refs()
AddExpression.model_rebuild() AddExpression.update_forward_refs()
DivExpression.model_rebuild() DivExpression.update_forward_refs()
MulExpression.model_rebuild() MulExpression.update_forward_refs()
SubExpression.model_rebuild() SubExpression.update_forward_refs()
def get_next_node(obj): def get_next_node(obj):
@@ -478,25 +478,25 @@ def get_next_node(obj):
next_node = obj.not_ next_node = obj.not_
logical_op = 'not' logical_op = 'not'
elif obj.__repr_name__() == 'ComparisonPredicate': elif obj.__repr_name__() == 'ComparisonPredicate':
next_node = obj.root next_node = obj.__root__
elif obj.__repr_name__() == 'SpatialPredicate': elif obj.__repr_name__() == 'SpatialPredicate':
next_node = obj.root next_node = obj.__root__
elif obj.__repr_name__() == 'TemporalPredicate': elif obj.__repr_name__() == 'TemporalPredicate':
next_node = obj.root next_node = obj.__root__
elif obj.__repr_name__() == 'IsBetweenPredicate': elif obj.__repr_name__() == 'IsBetweenPredicate':
next_node = obj.between next_node = obj.between
elif obj.__repr_name__() == 'Between': elif obj.__repr_name__() == 'Between':
next_node = obj.value next_node = obj.value
elif obj.__repr_name__() == 'ValueExpression': elif obj.__repr_name__() == 'ValueExpression':
next_node = obj.root or obj.lower or obj.upper next_node = obj.__root__ or obj.lower or obj.upper
elif obj.__repr_name__() == 'ScalarExpression': elif obj.__repr_name__() == 'ScalarExpression':
next_node = obj.root next_node = obj.__root__
elif obj.__repr_name__() == 'ScalarLiteral': elif obj.__repr_name__() == 'ScalarLiteral':
next_node = obj.root next_node = obj.__root__
elif obj.__repr_name__() == 'PropertyRef': elif obj.__repr_name__() == 'PropertyRef':
next_node = obj.property next_node = obj.property
elif obj.__repr_name__() == 'BinaryComparisonPredicate': elif obj.__repr_name__() == 'BinaryComparisonPredicate':
next_node = obj.root next_node = obj.__root__
elif obj.__repr_name__() == 'EqExpression': elif obj.__repr_name__() == 'EqExpression':
next_node = obj.eq next_node = obj.eq
logical_op = 'eq' logical_op = 'eq'
+4 -4
View File
@@ -4,7 +4,7 @@
# #
# Authors: Francesco Bartoli <xbartolone@gmail.com> # Authors: Francesco Bartoli <xbartolone@gmail.com>
# #
# Copyright (c) 2022 Francesco Bartoli # Copyright (c) 2024 Francesco Bartoli
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -31,7 +31,7 @@
from enum import Enum from enum import Enum
from pydantic import RootModel from pydantic import BaseModel
class SupportedFormats(Enum): class SupportedFormats(Enum):
@@ -39,5 +39,5 @@ class SupportedFormats(Enum):
YAML = "yaml" YAML = "yaml"
class OAPIFormat(RootModel): class OAPIFormat(BaseModel):
root: SupportedFormats = SupportedFormats.YAML __root__: SupportedFormats = SupportedFormats.YAML
+26 -25
View File
@@ -1,9 +1,10 @@
# ================================================================= # =================================================================
# #
# Authors: Tom Kralidis <tomkralidis@gmail.com> # Authors: Tom Kralidis <tomkralidis@gmail.com>
# Francesco Bartoli <xbartolone@gmail.com>
# #
# Copyright (c) 2023 Tom Kralidis # Copyright (c) 2023 Tom Kralidis
# Copyright (c) 2021 Francesco Bartoli # Copyright (c) 2024 Francesco Bartoli
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -294,7 +295,7 @@ class ElasticsearchProvider(BaseProvider):
try: try:
LOGGER.debug('querying Elasticsearch') LOGGER.debug('querying Elasticsearch')
if filterq: if filterq:
LOGGER.debug(f'adding cql object: {filterq.model_dump_json()}') LOGGER.debug(f'adding cql object: {filterq.json()}')
query = update_query(input_query=query, cql=filterq) query = update_query(input_query=query, cql=filterq)
LOGGER.debug(json.dumps(query, indent=4)) LOGGER.debug(json.dumps(query, indent=4))
@@ -646,16 +647,16 @@ class ESQueryBuilder:
def _build_query(q, cql): def _build_query(q, cql):
# this would be handled by the AST with the traverse of CQL model # this would be handled by the AST with the traverse of CQL model
op, node = get_next_node(cql.root) op, node = get_next_node(cql.__root__)
q.operation = op q.operation = op
if isinstance(node, list): if isinstance(node, list):
query_list = [] query_list = []
for elem in node: for elem in node:
op, next_node = get_next_node(elem) op, next_node = get_next_node(elem)
if not getattr(next_node, 'between', 0) == 0: if not getattr(next_node, 'between', 0) == 0:
property = next_node.between.value.root.root.property property = next_node.between.value.__root__.__root__.property
lower = next_node.between.lower.root.root lower = next_node.between.lower.__root__.__root__
upper = next_node.between.upper.root.root upper = next_node.between.upper.__root__.__root__
query_list.append(Q( query_list.append(Q(
{ {
'range': 'range':
@@ -666,24 +667,24 @@ def _build_query(q, cql):
} }
} }
)) ))
if not getattr(next_node, 'root', 0) == 0: if not getattr(next_node, '__root__', 0) == 0:
scalars = tuple(next_node.root.eq.root) scalars = tuple(next_node.__root__.eq.__root__)
property = scalars[0].root.property property = scalars[0].__root__.property
value = scalars[1].root.root value = scalars[1].__root__.__root__
query_list.append(Q( query_list.append(Q(
{'match': {f'{property}': f'{value}'}} {'match': {f'{property}': f'{value}'}}
)) ))
q.must(query_list) q.must(query_list)
elif not getattr(node, 'between', 0) == 0: elif not getattr(node, 'between', 0) == 0:
property = node.between.value.root.root.property property = node.between.value.__root__.__root__.property
lower = None lower = None
if not getattr(node.between.lower, if not getattr(node.between.lower,
'root', 0) == 0: '__root__', 0) == 0:
lower = node.between.lower.root.root lower = node.between.lower.__root__.__root__
upper = None upper = None
if not getattr(node.between.upper, if not getattr(node.between.upper,
'root', 0) == 0: '__root__', 0) == 0:
upper = node.between.upper.root.root upper = node.between.upper.__root__.__root__
query = Q( query = Q(
{ {
'range': 'range':
@@ -695,26 +696,26 @@ def _build_query(q, cql):
} }
) )
q.must(query) q.must(query)
elif not getattr(node, 'root', 0) == 0: elif not getattr(node, '__root__', 0) == 0:
next_op, next_node = get_next_node(node) next_op, next_node = get_next_node(node)
if not getattr(next_node, 'eq', 0) == 0: if not getattr(next_node, 'eq', 0) == 0:
scalars = tuple(next_node.eq.root) scalars = tuple(next_node.eq.__root__)
property = scalars[0].root.property property = scalars[0].__root__.property
value = scalars[1].root.root value = scalars[1].__root__.__root__
query = Q( query = Q(
{'match': {f'{property}': f'{value}'}} {'match': {f'{property}': f'{value}'}}
) )
q.must(query) q.must(query)
elif not getattr(node, 'intersects', 0) == 0: elif not getattr(node, 'intersects', 0) == 0:
property = node.intersects.root[0].root.property property = node.intersects.__root__[0].__root__.property
if property == 'geometry': if property == 'geometry':
geom_type = node.intersects.root[ geom_type = node.intersects.__root__[
1].root.root.root.type 1].__root__.__root__.__root__.type
if geom_type == 'Polygon': if geom_type == 'Polygon':
coordinates = node.intersects.root[ coordinates = node.intersects.__root__[
1].root.root.root.coordinates 1].__root__.__root__.__root__.coordinates
coords_list = [ coords_list = [
poly_coords.root for poly_coords in coordinates[0] poly_coords.__root__ for poly_coords in coordinates[0]
] ]
filter_ = Q( filter_ = Q(
{ {
+3 -3
View File
@@ -295,7 +295,7 @@ class MVTTippecanoeProvider(BaseMVTProvider):
content.tiles = service_url content.tiles = service_url
content.vector_layers = json.loads( content.vector_layers = json.loads(
metadata_json_content["json"])["vector_layers"] metadata_json_content["json"])["vector_layers"]
metadata['metadata'] = content.model_dump() metadata['metadata'] = content.dict()
# Some providers may not implement tilejson metadata # Some providers may not implement tilejson metadata
metadata['tilejson_url'] = f'{metadata_url}?f=tilejson' metadata['tilejson_url'] = f'{metadata_url}?f=tilejson'
except ProviderConnectionError: except ProviderConnectionError:
@@ -357,7 +357,7 @@ class MVTTippecanoeProvider(BaseMVTProvider):
content.links = links content.links = links
return content.model_dump(exclude_none=True) return content.dict(exclude_none=True)
def get_vendor_metadata(self, dataset, server_url, layer, tileset, def get_vendor_metadata(self, dataset, server_url, layer, tileset,
title, description, keywords, **kwargs): title, description, keywords, **kwargs):
@@ -376,7 +376,7 @@ class MVTTippecanoeProvider(BaseMVTProvider):
content.tiles = service_url content.tiles = service_url
content.vector_layers = json.loads( content.vector_layers = json.loads(
metadata_json_content["json"])["vector_layers"] metadata_json_content["json"])["vector_layers"]
return content.model_dump() return content.dict()
except ProviderConnectionError: except ProviderConnectionError:
msg = f'No tiles metadata json available: {self.service_metadata_url}' # noqa msg = f'No tiles metadata json available: {self.service_metadata_url}' # noqa
LOGGER.error(msg) LOGGER.error(msg)
+3
View File
@@ -13,6 +13,9 @@ starlette
uvicorn[standard] uvicorn[standard]
httpx httpx
# Pydantic/Dataclasses models
polyfactory
# PEP8 # PEP8
flake8 flake8
+1 -1
View File
@@ -1,2 +1,2 @@
Django Django
pydantic pydantic<2.0
+1 -1
View File
@@ -4,7 +4,7 @@ filelock
Flask Flask
jinja2 jinja2
jsonschema jsonschema
pydantic pydantic<2.0
pygeofilter pygeofilter
pygeoif pygeoif
pyproj pyproj
+7 -7
View File
@@ -3,7 +3,7 @@
# Authors: Tom Kralidis <tomkralidis@gmail.com> # Authors: Tom Kralidis <tomkralidis@gmail.com>
# #
# Copyright (c) 2020 Tom Kralidis # Copyright (c) 2020 Tom Kralidis
# Copyright (c) 2021 Francesco Bartoli # Copyright (c) 2024 Francesco Bartoli
# #
# Permission is hereby granted, free of charge, to any person # Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation # obtaining a copy of this software and associated documentation
@@ -78,7 +78,7 @@ def between():
"upper": 100000 "upper": 100000
} }
} }
return CQLModel.model_validate(between_) return CQLModel.parse_obj(between_)
@pytest.fixture() @pytest.fixture()
@@ -89,7 +89,7 @@ def between_upper():
"upper": 100000 "upper": 100000
} }
} }
return CQLModel.model_validate(between_) return CQLModel.parse_obj(between_)
@pytest.fixture() @pytest.fixture()
@@ -100,7 +100,7 @@ def between_lower():
"lower": 10000 "lower": 10000
} }
} }
return CQLModel.model_validate(between_) return CQLModel.parse_obj(between_)
@pytest.fixture() @pytest.fixture()
@@ -111,7 +111,7 @@ def eq():
"Admin-0 capital" "Admin-0 capital"
] ]
} }
return CQLModel.model_validate(eq_) return CQLModel.parse_obj(eq_)
@pytest.fixture() @pytest.fixture()
@@ -135,7 +135,7 @@ def _and(eq, between):
} }
] ]
} }
return CQLModel.model_validate(and_) return CQLModel.parse_obj(and_)
@pytest.fixture() @pytest.fixture()
@@ -155,7 +155,7 @@ def intersects():
] ]
} }
]} ]}
return CQLModel.model_validate(intersects) return CQLModel.parse_obj(intersects)
def test_query(config): def test_query(config):
+103
View File
@@ -0,0 +1,103 @@
# =================================================================
#
# Authors: Francesco Bartoli <xbartolone@gmail.com>
#
# Copyright (c) 2024 Francesco Bartoli
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use,
# copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following
# conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
# OTHER DEALINGS IN THE SOFTWARE.
#
# =================================================================
from polyfactory.factories.pydantic_factory import ModelFactory
from polyfactory.pytest_plugin import register_fixture
from pygeoapi.models.provider.base import GeospatialDataType
from pygeoapi.models.cql import (CQLModel, ComparisonPredicate,
ScalarExpression, SpatialPredicate,
TemporalPredicate, AndExpression,
Between, EqExpression, ScalarOperands,
IntersectsExpression, SpatialOperands)
@register_fixture
class CQLModelFactory(ModelFactory[CQLModel]):
...
@register_fixture
class GeospatialDataTypeFactory(ModelFactory[GeospatialDataType]):
...
@register_fixture
class BetweenModelFactory(ModelFactory[Between]):
...
@register_fixture
class EqExpressionModelFactory(ModelFactory[EqExpression]):
...
@register_fixture
class IntersectsExpressionModelFactory(ModelFactory[IntersectsExpression]):
...
def test_cql_model(cql_model_factory: CQLModelFactory) -> None:
cql_model_instance = cql_model_factory.build()
assert isinstance(cql_model_instance, CQLModel)
assert cql_model_instance.dict()
assert type(cql_model_instance.__root__) in [
ComparisonPredicate, SpatialPredicate, TemporalPredicate, AndExpression
]
def test_provider_base_geospatial_data_type(
geospatial_data_type_factory: GeospatialDataTypeFactory) -> None:
gdt_instance = geospatial_data_type_factory.build()
assert gdt_instance.dict()
assert isinstance(gdt_instance, GeospatialDataType)
def test_between_model(between_model_factory: BetweenModelFactory) -> None:
between_model_instance = between_model_factory.build()
assert isinstance(between_model_instance, Between)
assert between_model_instance.dict()
assert type(between_model_instance.lower) is ScalarExpression
assert type(between_model_instance.upper) is ScalarExpression
def test_eq_expression_model(
eq_expression_model_factory: EqExpressionModelFactory) -> None:
eqexpr_model_instance = eq_expression_model_factory.build()
assert isinstance(eqexpr_model_instance, EqExpression)
assert eqexpr_model_instance.dict()
assert type(eqexpr_model_instance.eq) is ScalarOperands
def test_intersects_expression_model(
intersects_expression_model_factory: IntersectsExpressionModelFactory) -> None: # noqa
intersectsexpr_model_instance = intersects_expression_model_factory.build()
assert isinstance(intersectsexpr_model_instance, IntersectsExpression)
assert intersectsexpr_model_instance.dict()
assert type(intersectsexpr_model_instance.intersects) is SpatialOperands