Skip to content
Open
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
40 changes: 34 additions & 6 deletions src/utils_flask_sqla_geo/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import fiona
from fiona.crs import from_epsg

from sqlalchemy.sql.selectable import Select
from utils_flask_sqla_geo.schema import GeoAlchemyAutoSchema
from utils_flask_sqla_geo.utilsgeometry import FIONA_MAPPING
from utils_flask_sqla.env import db


def export_csv(
Expand Down Expand Up @@ -51,8 +53,13 @@ def export_csv(

writer.writeheader() # ligne d'entête

# legacy sqlalchemy 1.x Query object
if type(query) is Select:
data = db.session.execute(query.execution_options(yield_per=chunk_size))
else:
data = query.yield_per(chunk_size)
# écriture des lignes dans le fichier csv
for line in schema.dump(query.yield_per(chunk_size), many=True):
for line in schema.dump(data, many=True):
writer.writerow(line)


Expand Down Expand Up @@ -80,11 +87,19 @@ def export_geojson(

# instantiation du schema
schema = schema_class(
only=columns or None, as_geojson=True, feature_geometry=geometry_field_name
only=columns or None,
as_geojson=True,
feature_geometry=geometry_field_name,
)

# legacy sqlalchemy 1.x Query object
if type(query) is Select:
data = db.session.execute(query.execution_options(yield_per=chunk_size))
else:
data = query.yield_per(chunk_size)

# serialisation
feature_collection = schema.dump(query.yield_per(chunk_size), many=True)
feature_collection = schema.dump(data, many=True)

# écriture du ficher geojson
for chunk in json.JSONEncoder().iterencode(feature_collection):
Expand Down Expand Up @@ -123,8 +138,14 @@ def export_json(
# instantiation du schema avec only
schema = schema_class(only=only or None)

# legacy sqlalchemy 1.x Query object
if type(query) is Select:
data = db.session.execute(query.execution_options(yield_per=chunk_size))
else:
data = query.yield_per(chunk_size)

# serialisation
iterable_data = schema.dump(query.yield_per(chunk_size), many=True)
iterable_data = schema.dump(data, many=True)

# écriture du fichier json
for chunk in json.JSONEncoder().iterencode(iterable_data):
Expand All @@ -141,11 +162,18 @@ def export_geopackage(
chunk_size: int = 1000,
):
schema = schema_class(
only=columns or None, as_geojson=True, feature_geometry=geometry_field_name
only=columns or None,
as_geojson=True,
feature_geometry=geometry_field_name,
)

feature_collection = schema.dump(query.yield_per(chunk_size), many=True)
# legacy sqlalchemy 1.x Query object
if type(query) is Select:
data = db.session.execute(query.execution_options(yield_per=chunk_size))
else:
data = query.yield_per(chunk_size)

feature_collection = schema.dump(data, many=True)
# FIXME: filter tableDef columns with columns
properties = {
db_col.key: FIONA_MAPPING.get(db_col.type.__class__.__name__.lower(), "str")
Expand Down
15 changes: 13 additions & 2 deletions src/utils_flask_sqla_geo/schema.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from enum import Enum

import json
from marshmallow import Schema, fields, RAISE, EXCLUDE
from marshmallow.decorators import pre_load, post_dump
from marshmallow.validate import OneOf, Range
Expand Down Expand Up @@ -166,8 +166,18 @@ def __init__(
if as_geojson:
self.feature_id = feature_id or self.opts.feature_id
self.feature_geometry = feature_geometry or self.opts.feature_geometry

if not self.feature_geometry:
raise TypeError("Missing 'feature_geometry'")

# Test type du champ feature_geometry
# si de type text on considère qu'il correspond au retour de la fonction st_asgeojson de postgis
# dans ce cas la valeur doit être transformée en json
if type(self._declared_fields[self.feature_geometry]) is fields.String:
self.to_geometry = lambda val: json.loads(val)
else:
self.to_geometry = lambda val: val

# Add feature geometry to serialized fields
exclude.discard(self.feature_geometry)
if only is not None:
Expand All @@ -177,8 +187,9 @@ def __init__(
def to_feature(self, properties):
feature = {
"properties": properties,
"geometry": properties.pop(self.feature_geometry),
"geometry": self.to_geometry(properties.pop(self.feature_geometry)),
}

if self.feature_id and self.feature_id in properties:
feature.update(
{
Expand Down
67 changes: 67 additions & 0 deletions src/utils_flask_sqla_geo/tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import sqlalchemy as sa
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.postgresql import JSONB
from geoalchemy2 import Geometry
from geoalchemy2.shape import from_shape, to_shape
from shapely.geometry import Point
Expand Down Expand Up @@ -445,6 +446,72 @@ class Meta:
assert schema1.dump(b) == expected
assert schema2.dump(b) == expected

def test_st_asgeojson_geometries(self):
class ModelB(Base):
__tablename__ = "table_st_asgeojson"
pk = Column(Integer, primary_key=True)
name = Column(String)
st_asgeojson = Column(String)

class ModelBSchema1(GeoAlchemyAutoSchema):
class Meta:
model = ModelB
include_fk = True
feature_geometry = "st_asgeojson"

schema1 = ModelBSchema1(as_geojson=True, feature_geometry="st_asgeojson")

b = ModelB(
pk=1,
name="b1",
st_asgeojson=json.dumps({"type": "Point", "coordinates": [3.7, 44.4]}),
)
expected = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [3.7, 44.4],
},
"properties": {
"pk": 1,
"name": "b1",
},
}
assert schema1.dump(b) == expected

def test_geojson_geometries(self):
class ModelB(Base):
__tablename__ = "table_geojson"
pk = Column(Integer, primary_key=True)
name = Column(String)
geojson = Column(JSONB)

class ModelBSchema1(GeoAlchemyAutoSchema):
class Meta:
model = ModelB
include_fk = True
feature_geometry = "geojson"

schema1 = ModelBSchema1(as_geojson=True, feature_geometry="geojson")

b = ModelB(
pk=1,
name="b1",
geojson={"type": "Point", "coordinates": [3.7, 44.4]},
)
expected = {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [3.7, 44.4],
},
"properties": {
"pk": 1,
"name": "b1",
},
}
assert schema1.dump(b) == expected

def test_generator_json(self):
def generate_objects():
for i in range(3):
Expand Down