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

A_VALID_1 - [HOME][VALIDATION] Ajout d'une section "Dernières validations" #3226

Open
edelclaux opened this issue Oct 11, 2024 · 0 comments

Comments

@edelclaux
Copy link
Contributor

Epic: "Enrichir page d'accueil" #2983

Feature: Mettre ancre qui renvoie vers le paragraphe de l'issue

Description:

On souhaite ajouter une section "Dernière Validations" sur la page d'accueil. Voir l'epic #2983.

Config

Cette section sera optionnelle et désactivée par défault. Sa configuration sera gestion basée sur une nouvelle entrée booléenne (optionnelle, à false par défaut) dans la config:

[HOME]
   DISPLAY_LAST_VALIDATIONS  = true

Cette section sera également fonction des permissions associées à l'accès au module validation.

Elements technique concernés :

  • Backend:
    • route: /validation
      @blueprint.route("", methods=["GET", "POST"])
      @permissions.check_cruved_scope("C", get_scope=True, module_code="VALIDATION")
      def get_synthese_data(scope):
      """
      Return synthese and t_validations data filtered by form params
      Params must have same synthese fields names
      .. :quickref: Validation;
      Parameters:
      ------------
      Returns
      -------
      FeatureCollection
      """
      enable_profile = current_app.config["FRONTEND"]["ENABLE_PROFILES"]
      fields = {
      "id_synthese",
      "unique_id_sinp",
      "entity_source_pk_value",
      "meta_update_date",
      "id_nomenclature_valid_status",
      "nomenclature_valid_status.cd_nomenclature",
      "nomenclature_valid_status.mnemonique",
      "nomenclature_valid_status.label_default",
      "last_validation.validation_date",
      "last_validation.validation_auto",
      "taxref.cd_nom",
      "taxref.nom_vern",
      "taxref.lb_nom",
      "taxref.nom_vern_or_lb_nom",
      "dataset.validable",
      }
      if enable_profile:
      fields |= {
      "profile.score",
      "profile.valid_phenology",
      "profile.valid_altitude",
      "profile.valid_distribution",
      }
      fields |= {col["column_name"] for col in blueprint.config["COLUMN_LIST"]}
      filters = (request.json if request.is_json else None) or {}
      result_limit = filters.pop("limit", blueprint.config["NB_MAX_OBS_MAP"])
      lateral_join = {}
      """
      1) We start creating the query with SQLAlchemy ORM.
      2) We convert this query to SQLAlchemy Core in order to use
      SyntheseQuery utility class to apply user filters.
      3) We get back the results in the ORM through from_statement.
      We populate relationships with contains_eager.
      We create a lot of aliases, that are selected at step 1,
      and given to contains_eager at step 3 to correctly identify columns
      to use to populate relationships models.
      """
      last_validation_subquery = (
      sa.select(TValidations)
      .where(TValidations.uuid_attached_row == Synthese.unique_id_sinp)
      .order_by(TValidations.validation_date.desc())
      .limit(1)
      .subquery()
      .lateral("last_validation")
      )
      last_validation = aliased(TValidations, last_validation_subquery)
      lateral_join = {last_validation: Synthese.last_validation}
      if enable_profile:
      profile_subquery = (
      sa.select(VConsistancyData)
      .where(VConsistancyData.id_synthese == Synthese.id_synthese)
      .limit(result_limit)
      .subquery()
      .lateral("profile")
      )
      profile = aliased(VConsistancyData, profile_subquery)
      lateral_join[profile] = Synthese.profile
      relationships = list(
      {
      field.split(".", 1)[0]
      for field in fields
      if "." in field
      and not (field.startswith("last_validation.") or field.startswith("profile."))
      }
      )
      # Get dataset relationship : filter only validable dataset
      dataset_index = relationships.index("dataset")
      relationships = [getattr(Synthese, rel) for rel in relationships]
      aliases = [aliased(rel.property.mapper.class_) for rel in relationships]
      dataset_alias = aliases[dataset_index]
      query = db.session.query(Synthese, *aliases, *lateral_join.keys())
      for rel, alias in zip(relationships, aliases):
      query = query.outerjoin(rel.of_type(alias))
      for alias in lateral_join.keys():
      query = query.outerjoin(alias, sa.true())
      query = query.where(Synthese.the_geom_4326.isnot(None)).order_by(Synthese.date_min.desc())
      # filter with profile
      if enable_profile:
      score = filters.pop("score", None)
      if score is not None:
      query = query.where(profile.score == score)
      valid_distribution = filters.pop("valid_distribution", None)
      if valid_distribution is not None:
      query = query.where(profile.valid_distribution.is_(valid_distribution))
      valid_altitude = filters.pop("valid_altitude", None)
      if valid_altitude is not None:
      query = query.where(profile.valid_altitude.is_(valid_altitude))
      valid_phenology = filters.pop("valid_phenology", None)
      if valid_phenology is not None:
      query = query.where(profile.valid_phenology.is_(valid_phenology))
      if filters.pop("modif_since_validation", None):
      query = query.where(Synthese.meta_update_date > last_validation.validation_date)
      # Filter only validable dataset
      query = query.where(dataset_alias.validable == True)
      # Step 2: give SyntheseQuery the Core selectable from ORM query
      assert len(query.selectable.get_final_froms()) == 1
      query = (
      SyntheseQuery(
      Synthese,
      query.selectable,
      filters, # , query_joins=query.selectable.get_final_froms()[0] # DUPLICATION of OUTER JOIN
      )
      .filter_query_all_filters(g.current_user, scope)
      .limit(result_limit)
      )
      # Step 3: Construct Synthese model from query result
      syntheseModelQuery = Synthese.query.options(
      *[contains_eager(rel, alias=alias) for rel, alias in zip(relationships, aliases)]
      ).options(*[contains_eager(rel, alias=alias) for alias, rel in lateral_join.items()])
      # to pass alert reports infos with synthese to validation list
      # only if tools are activate for validation
      alertActivate = (
      len(current_app.config["SYNTHESE"]["ALERT_MODULES"])
      and "VALIDATION" in current_app.config["SYNTHESE"]["ALERT_MODULES"]
      )
      pinActivate = (
      len(current_app.config["SYNTHESE"]["PIN_MODULES"])
      and "VALIDATION" in current_app.config["SYNTHESE"]["PIN_MODULES"]
      )
      if alertActivate or pinActivate:
      fields |= {"reports.report_type.type"}
      syntheseModelQuery = syntheseModelQuery.options(
      selectinload(Synthese.reports).joinedload(TReport.report_type)
      )
      query = syntheseModelQuery.from_statement(query)
      # The raise option ensure that we have correctly retrived relationships data at step 3
      return jsonify(query.as_geofeaturecollection(fields=fields))
Exemple d'élement retourné par la route de type FeatureCollection
"features":
{
	"0": {
		"geometry": {
			"coordinates": [
				4.456124,
				44.80359
			],
			"type": "Point"
		},
		"id": "9932",
		"properties": {
			"dataset": {
				"dataset_name": "Toto",
				"validable": true
			},
			"entity_source_pk_value": "2090",
			"id_nomenclature_valid_status": 458,
			"id_synthese": 9932,
			"last_validation": {
				"validation_auto": true,
				"validation_date": "2024-06-25 11:06:56.145125"
			},
			"meta_update_date": "2024-06-25 11:06:56.145125",
			"nomenclature_valid_status": {
				"cd_nomenclature": "0",
				"label_default": "En attente de validation",
				"mnemonique": "En attente de validation"
			},
			"observers": "Administrateur test",
			"profile": null,
			"reports": [],
			"taxref": {
				"cd_nom": 355822,
				"lb_nom": "Guernea",
				"nom_vern": null,
				"nom_vern_or_lb_nom": "Guernea"
			},
			"unique_id_sinp": "16c9b19d-c10f-46d7-a539-18b2c70e5eee"
		},
		"type": "Feature"
	}
}

Développement technique envisagé

Modifier cette route pour :

  • choisir de renvoyer ou non les géométries associées aux validations.
  • paginer l'objet renvoyé
  • Enrichir de paramètres pour pouvoir obtenir les éléments demandés dans l'EPIC ( #2983)
    • Validateur (via id_validator)
    • Message de validation ( validation_comment)

une alternative serait de créer une route dédiée

@edelclaux edelclaux converted this from a draft issue Oct 11, 2024
@edelclaux edelclaux moved this to A discuter in ARB IDF - GeoNature-social Oct 17, 2024
@edelclaux edelclaux moved this from A discuter to En discussion in ARB IDF - GeoNature-social Oct 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: En discussion
Development

No branches or pull requests

1 participant