Skip to content

Commit

Permalink
wip: vincent
Browse files Browse the repository at this point in the history
  • Loading branch information
edelclaux committed Apr 26, 2024
1 parent a0a3300 commit 199b34a
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 35 deletions.
55 changes: 54 additions & 1 deletion backend/geonature/core/gn_synthese/imports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from apptax.taxonomie.models import Taxref
from flask import current_app
import sqlalchemy as sa
from sqlalchemy import func, distinct
from sqlalchemy import func, distinct, select

from geonature.utils.env import db
from geonature.utils.sentry import start_sentry_child
Expand Down Expand Up @@ -354,6 +354,59 @@ def remove_data_from_synthese(imprt):
db.session.delete(source)


def get_name_geom_4326_field():
"""Return the name of the field that contains the 4326 geometry.
For synthese, the name is actually the same for import transient table
`gn_imports.t_imports_synthese` and for the destination table `gn_synthese.synthese`.
Returns
-------
str
The name of the field
"""
return "the_geom_4326"


def get_where_clause_id_import(imprt):
"""Construct a WHERE clause to filter data for the import.
Data is in :
- import transient table for an 'in-progress' import
- destination table for a 'done' import
Used in function `get_valid_bbox`.
Parameters
----------
imprt : geonature.core.imports.models.TImport
The import object containing the import ID and destination.
Returns
-------
where_clause : sqlalchemy.BinaryExpression
A SQLAlchemy BinaryExpression that represents the WHERE clause.
"""
where_clause = None
id_import = imprt.id_import
destination_import = imprt.destination

# If import is still in-progress data is retrieved from the import transient table,
# otherwise the import is done and data is retrieved from the destination table
if imprt.loaded:
# Retrieve the import transient table ("t_imports_synthese")
transient_table = destination_import.get_transient_table()
# Set the WHERE clause
where_clause = transient_table.c["id_import"] == id_import
else:
# There is no 'id_import' field in the destination table 'synthese', must retrieve
# the corresponding `id_source` from the table "t_sources"
id_source = db.session.scalar(
select(TSources.id_source).where(TSources.name_source == f"Import(id={id_import})")
)
# Retrieve the destination table ("synthese")
entity = Entity.query.filter_by(destination=destination_import, code="observation").one()
destination_table = entity.get_destination_table()
# Set the WHERE clause
where_clause = destination_table.c["id_source"] == id_source

return where_clause


def report_plot(imprt: TImports) -> Row:
"""
Generate a plot of the taxonomic distribution (for each rank) based on the import.
Expand Down
4 changes: 4 additions & 0 deletions backend/geonature/core/gn_synthese/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
check_transient_data,
import_data_to_synthese,
remove_data_from_synthese,
get_name_geom_4326_field,
get_where_clause_id_import,
report_plot,
)

Expand All @@ -19,6 +21,8 @@ def generate_input_url_for_dataset(self, dataset):
"check_transient_data": check_transient_data,
"import_data_to_destination": import_data_to_synthese,
"remove_data_from_destination": remove_data_from_synthese,
"get_name_geom_4326_field": get_name_geom_4326_field,
"get_where_clause_id_import": get_where_clause_id_import,
"statistics_labels": [
{"key": "taxa_count", "value": "Nombre de taxons importés"},
],
Expand Down
63 changes: 29 additions & 34 deletions backend/geonature/core/imports/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,64 +102,59 @@ def detect_separator(f, encoding):
return dialect.delimiter


def get_valid_bbox(imprt, entity, geom_4326_field):
def get_valid_bbox(imprt, entity):
"""Get the valid bounding box for a given import.
Parameters
----------
imprt : geonature.core.imports.models.TImports
The import object.
entity : geonature.core.imports.models.Entity
The entity object (e.g.: observation, station...).
geom_4326_field : geonature.core.imports.models.BibFields
The field containing the geometry of the entity in the transient table.
Returns
-------
dict or None
The valid bounding box as a JSON object, or None if no valid bounding box.
Raises
------
NotImplementedError
If the destination of the import is not implemented yet (e.g.: 'metadata'...)
If the function is not implemented for the destination of the import.
"""
# TODO: verify how to assert that an import has data in transient table or not and whether it is related to t_imports.date_end_import or t_imports.loaded fields
# Retrieve the name of the geom field to retrieve geometries of data from
if "get_name_geom_4326_field" not in imprt.destination.module._imports_:
raise NotImplementedError(
f"function get_valid_bbox not implemented for an import with destination '{imprt.destination.code}, needs `get_name_geom_4326_field` function"
)
name_geom_4326_field = imprt.destination.module._imports_["get_name_geom_4326_field"]()

# Retrieve the where clause to filter data for the given import
if "get_where_clause_id_import" not in imprt.destination.module._imports_:
raise NotImplementedError(
f"function get_valid_bbox not implemented for an import with destination '{imprt.destination.code}, needs `get_where_clause_id_import` function"
)
where_clause_id_import = imprt.destination.module._imports_["get_where_clause_id_import"](
imprt
)

# Build the statement to retrieve the valid bounding box
statement = None
if imprt.loaded == True:
# Compute from entries in the transient table and related to the import
transient_table = imprt.destination.get_transient_table()
stmt = (
select(func.ST_AsGeojson(func.ST_Extent(transient_table.c[geom_4326_field.dest_field])))
.where(transient_table.c.id_import == imprt.id_import)
statement = (
select(func.ST_AsGeojson(func.ST_Extent(transient_table.c[name_geom_4326_field])))
.where(where_clause_id_import)
.where(transient_table.c[entity.validity_column] == True)
)
else:
# Compute from entries in the destination table and related to the import
id_module_import = db.session.execute(
select(TModules.id_module).where(TModules.module_code == "IMPORT")
).scalar()
# TODO: build a destination-generic query using geom_4326_field.dest_field or another method to retrieve geom field from entity or destination
# Need to handle the filtering by id_import in a generic way, but the logic is different between Synthese (no id_import field, and join needed) and occhab (with id_import field)
destination_table = entity.get_destination_table()
stmt = None
if imprt.destination.code == 'synthese':
stmt = (
select(
func.ST_AsGeojson(
func.ST_Extent(destination_table.c[geom_4326_field.dest_field])
)
)
.join(TSources)
.where(TSources.id_module == id_module_import)
.where(TSources.name_source == f"Import(id={imprt.id_import})")
)
elif imprt.destination.code == 'occhab':
stmt = select(
func.ST_AsGeojson(func.ST_Extent(destination_table.c[geom_4326_field.dest_field]))
).where(destination_table.c["id_import"] == imprt.id_import)
else:
raise NotImplementedError(f"function get_valid_bbox not implemented for an import with destination '{imprt.destination.code}'")

(valid_bbox,) = db.session.execute(stmt).fetchone()
statement = select(
func.ST_AsGeojson(func.ST_Extent(destination_table.c[name_geom_4326_field]))
).where(where_clause_id_import)

# Execute the statement to eventually retrieve the valid bounding box
(valid_bbox,) = db.session.execute(statement).fetchone()

# Return the valid bounding box or None
if valid_bbox:
return json.loads(valid_bbox)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,49 @@ def remove_data_from_occhab(imprt):
r = db.session.execute(
sa.delete(destination_table).where(destination_table.c.id_import == imprt.id_import)
)


def get_name_geom_4326_field():
"""Return the name of the field that contains the 4326 geometry.
For occhab, the name is actually the same for import transient table
`gn_imports.t_imports_occhab` and for the destination table `pr_occhab.t_stations`.
Returns
-------
str
The name of the field
"""
return "geom_4326"


def get_where_clause_id_import(imprt):
"""Construct a WHERE clause to filter data for the import.
Data is in :
- import transient table for an 'in-progress' import
- destination table for a 'done' import
Used in function `get_valid_bbox`.
Parameters
----------
imprt : geonature.core.imports.models.TImport
The import object containing the import ID and destination.
Returns
-------
where_clause : sqlalchemy.BinaryExpression
A SQLAlchemy BinaryExpression that represents the WHERE clause.
"""
where_clause = None
id_import = imprt.id_import
destination_import = imprt.destination

# If import is still in-progress data is retrieved from the import transient table,
# otherwise the import is done and data is retrieved from the destination table
if imprt.loaded:
# Retrieve the import transient table ("t_imports_occhab")
table_with_data = destination_import.get_transient_table()
else:
# Retrieve the destination table ("t_stations")
entity = Entity.query.filter_by(destination=destination_import, code="station").one()
table_with_data = entity.get_destination_table()
# Set the WHERE clause
where_clause = table_with_data.c["id_import"] == id_import

return where_clause
4 changes: 4 additions & 0 deletions contrib/gn_module_occhab/backend/gn_module_occhab/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
check_transient_data,
import_data_to_occhab,
remove_data_from_occhab,
get_name_geom_4326_field,
get_where_clause_id_import,
)


Expand All @@ -20,6 +22,8 @@ def generate_input_url_for_dataset(self, dataset):
"check_transient_data": check_transient_data,
"import_data_to_destination": import_data_to_occhab,
"remove_data_from_destination": remove_data_from_occhab,
"get_name_geom_4326_field": get_name_geom_4326_field,
"get_where_clause_id_import": get_where_clause_id_import,
"statistics_labels": [
{"key": "station_count", "value": "Nombre de stations importées"},
{"key": "habitat_count", "value": "Nombre d’habitats importés"},
Expand Down

0 comments on commit 199b34a

Please sign in to comment.