#!/usr/bin/env python3 # -*- coding: UTF-8 -*-. from sqlalchemy import create_engine, text from sqlalchemy.engine import URL from datetime import datetime as dt import pandas as pd import geopandas as gpd nom_numerateur = 'GEIER' pnom_numerator = 'Colas' today = dt.now().date().isoformat() # Parametres bdd user = 'cen_admin' pwd = '#CEN38@venir' adr = '91.134.194.221' port = '5432' base = 'sicen2' url = URL.create('postgresql+psycopg2', username=user, password=pwd, host=adr, database=base, ) con = create_engine(url) nom_numerateur = nom_numerateur.upper() pnom_numerator = pnom_numerator[0].upper() + pnom_numerator[1:] query_get_numerator = """ SELECT * FROM md.personne WHERE nom = '%s' AND prenom = '%s' AND id_structure = 1 """ % (nom_numerateur,pnom_numerator) numerateur = pd.read_sql_query( sql = query_get_numerator, con = con ) select_import = """ SELECT * FROM import.import_data_cen38; """ data_import = pd.read_sql_query( sql = select_import, con = con) check_datageom = """ DROP VIEW IF EXISTS import.test_geom_prestation; CREATE OR REPLACE VIEW import.test_geom_prestation as ( SELECT ROW_NUMBER() OVER (ORDER BY date_obs ASC) AS gid, localisation, CASE WHEN latitude::numeric > 6000000 THEN ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',2154) -- coordonnées en lambert93 WHEN latitude::numeric > 50 AND latitude::numeric < 3000000 THEN ST_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',27572),2154) -- coordonnées en Lambert II étendu --WHEN latitude is null THEN st_pointonsurface(b.geom) -- pas de coordonnées XY on récupère la geom du centroide du site CenRA si elle existe et si une partie du nom du site est renseignée dans la colonne LOCALISATION ELSE ST_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',4326),2154) -- coordonnées en WGS84 END as geometrie FROM import.import_data_cen38 --LEFT JOIN import.sites_serena_cren_mu_2015 b ON lower(b.nom_site) like '%'||lower(trim(localisation,'s'))||'%' -- on joint la table des sites CenRA pour récupérer éventuellement le centroide d'un site si pas de XY WHERE cd_nom is not null and (date_obs is not null or date_debut_obs is not null)) ; -- pour ignorer les lignes vides qui traineraient dans le csv SELECT * FROM import.test_geom_prestation; """ df_check_datageom = gpd.read_postgis( sql = text(check_datageom), con = con, geom_col='geometrie') if df_check_datageom.geometry.is_valid.all() \ and df_check_datageom.crs.srs == 'epsg:2154': drop_datageom = """DROP VIEW IF EXISTS import.test_geom_prestation;""" with con.begin() as cnx: cnx.execute(drop_datageom) create_table_pers = """ DROP TABLE IF EXISTS md.temp_import_personne; CREATE TABLE md.temp_import_personne AS WITH personnel AS ( SELECT a.id_personne,a.nom,a.prenom,b.id_structure,b.nom_structure FROM md.personne a JOIN md.structure b USING (id_structure) ) SELECT DISTINCT -- personne CASE WHEN observateur2 is null THEN (c.nom||' '||c.prenom)::text WHEN observateur2 is not null AND observateur3 is null THEN (c.nom||' '||c.prenom)||'&'||(d.nom||' '||d.prenom) WHEN observateur3 is not null AND observateur4 is null THEN (c.nom||' '||c.prenom)||'&'||(d.nom||' '||d.prenom)||'&'||(e.nom||' '||e.prenom) WHEN observateur4 is not null AND observateur5 is null THEN (c.nom||' '||c.prenom)||'&'||(d.nom||' '||d.prenom)||'&'||(e.nom||' '||e.prenom)||'&'||(f.nom||' '||f.prenom) ELSE c.nom_structure||'&'||d.nom_structure||'&'||(e.nom||' '||e.prenom)||'&'||(f.nom||' '||f.prenom)||'&'||(g.nom||' '||g.prenom) END AS personne, -- nom_structure CASE WHEN observateur2 is null THEN c.nom_structure::text WHEN observateur2 is not null AND observateur3 is null AND c.nom_structure <> d.nom_structure THEN c.nom_structure||'&'||d.nom_structure WHEN observateur2 is not null AND observateur3 is null AND c.nom_structure = d.nom_structure THEN c.nom_structure WHEN observateur3 is not null AND observateur4 is null AND c.nom_structure <> d.nom_structure AND d.nom_structure <> e.nom_structure AND c.nom_structure <> e.nom_structure THEN c.nom_structure||'&'||d.nom_structure||'&'||e.nom_structure WHEN observateur3 is not null AND observateur4 is null AND c.nom_structure = d.nom_structure AND d.nom_structure <> e.nom_structure THEN c.nom_structure||'&'||e.nom_structure WHEN observateur3 is not null AND observateur4 is null AND c.nom_structure <> d.nom_structure AND d.nom_structure = e.nom_structure THEN c.nom_structure||'&'||d.nom_structure WHEN observateur4 is not null AND observateur5 is null AND c.nom_structure <> d.nom_structure THEN c.nom_structure||'&'||d.nom_structure||'&'||e.nom_structure||'&'||f.nom_structure ELSE c.nom_structure||'&'||d.nom_structure||'&'||e.nom_structure||'&'||f.nom_structure||'&'||g.nom_structure END AS nom_structure, -- observateur CASE WHEN observateur2 is null THEN c.id_personne::text WHEN observateur2 is not null AND observateur3 is null THEN c.id_personne||'&'||d.id_personne WHEN observateur3 is not null AND observateur4 is null THEN c.id_personne||'&'||d.id_personne||'&'||e.id_personne WHEN observateur4 is not null AND observateur5 is null THEN c.id_personne||'&'||d.id_personne||'&'||e.id_personne||'&'||f.id_personne ELSE c.id_personne||'&'||d.id_personne||'&'||e.id_personne||'&'||f.id_personne||'&'||g.id_personne END AS observateur, structure FROM import.import_data_cen38 a LEFT JOIN personnel c ON (c.nom||' '||c.prenom) = trim(a.observateur1::text) AND lower(c.nom_structure) = lower(a.structure) LEFT JOIN personnel d ON (d.nom||' '||d.prenom) = trim(a.observateur2::text) AND lower(d.nom_structure) = lower(a.structure) LEFT JOIN personnel e ON (e.nom||' '||e.prenom) = trim(a.observateur3::text) AND lower(e.nom_structure) = lower(a.structure) LEFT JOIN personnel f ON (f.nom||' '||f.prenom) = trim(a.observateur4::text) AND lower(f.nom_structure) = lower(a.structure) LEFT JOIN personnel g ON (g.nom||' '||g.prenom) = trim(a.observateur5::text) AND lower(g.nom_structure) = lower(a.structure) ; """.format(id_creator = numerateur.id_personne.item()) with con.begin() as cnx: cnx.execute(create_table_pers) select_pers = 'SELECT * FROM md.temp_import_personne' df_pers = pd.read_sql_query( sql = select_pers, con = con) with_dataTOinsert = """ WITH personnel AS ( SELECT a.id_personne,a.nom,a.prenom,b.id_structure,b.nom_structure FROM md.personne a JOIN md.structure b USING (id_structure) ), t as ( SELECT CASE WHEN a.heure IS NULL THEN '00:00:00' ELSE a.heure END heure, a.date_obs, a.date_debut_obs, a.date_fin_obs, a.duree as date_textuelle, CASE WHEN b.regne IS NULL THEN bb.regne ELSE b.regne END regne, CASE WHEN b.phylum IS NULL THEN bb.phylum ELSE b.phylum END phylum, CASE WHEN b.classe IS NULL THEN bb.classe ELSE b.classe END classe, CASE WHEN b.ordre IS NULL THEN bb.ordre ELSE b.ordre END ordre, CASE WHEN b.famille IS NULL THEN bb.famille ELSE b.famille END famille, CASE WHEN b.nom_valide IS NULL THEN bb.nom_valide ELSE b.nom_valide END nom_valide, CASE WHEN b.nom_vern IS NULL THEN bb.nom_vern ELSE b.nom_vern END nom_vern, CASE WHEN b.nom_complet IS NULL THEN bb.nom_complet ELSE b.nom_complet END nom_complet, CASE WHEN b.cd_nom IS NULL THEN bb.cd_nom::text ELSE b.cd_nom END cd_nom, a.abondance_dominance as effectif_textuel, a.effectif_min, a.effectif_max, CASE WHEN strate::text is not null THEN strate::text ELSE age::text END AS type_effectif, CASE WHEN phenologie is not null THEN phenologie ELSE sexe END AS phenologie, CASE WHEN latitude::numeric > 6000000 THEN ST_X(st_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',2154),4326)) -- coordonnée X en lambert93 WHEN latitude::numeric > 50 AND latitude::numeric < 3000000 THEN ST_X(st_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',27572),4326)) -- coordonnées en Lambert II étendu -- WHEN latitude is null -- THEN ST_X(st_transform(st_pointonsurface(i.geom),4326)) -- pas de coordonnées XY on récupère la geom du centroide du site CenRA si elle existe et si une partie du nom du site est renseignée dans la colonne LOCALISATION ELSE longitude::double precision -- coordonnées en WGS84 END as longitude, CASE WHEN latitude::numeric > 6000000 THEN ST_Y(st_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',2154),4326)) -- coordonnée X en lambert93 WHEN latitude::numeric > 50 AND latitude::numeric < 3000000 THEN ST_Y(st_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',27572),4326)) -- coordonnées en Lambert II étendu -- WHEN latitude is null -- THEN ST_Y(st_transform(st_pointonsurface(i.geom),4326)) -- pas de coordonnées XY on récupère la geom du centroide du site CenRA si elle existe et si une partie du nom du site est renseignée dans la colonne LOCALISATION ELSE latitude::double precision END as latitude, localisation, CASE WHEN observateur2 is null THEN c.id_personne::text WHEN observateur2 is not null AND observateur3 is null THEN c.id_personne||'&'||d.id_personne WHEN observateur3 is not null AND observateur4 is null THEN c.id_personne||'&'||d.id_personne||'&'||e.id_personne WHEN observateur4 is not null AND observateur5 is null THEN c.id_personne||'&'||d.id_personne||'&'||e.id_personne||'&'||f.id_personne ELSE c.id_personne||'&'||d.id_personne||'&'||e.id_personne||'&'||f.id_personne||'&'||g.id_personne END AS observateur, {id_numerateur}::integer as numerisateur, -- adapter l'identifiant du numérisateur en fonction de la personne qui réalise l'import en masse -- cas d'une prestation on ajoute le CenRA à la structure de l'observateur '&1' sinon il faut supprimer CASE WHEN h.id_structure <> 1 THEN (h.id_structure||'&1')::text ELSE h.id_structure::text END as structure, remarque_obs, CASE WHEN lower(a.structure) like '%CEN Isère%' THEN true::boolean ELSE false::boolean END as diffusable, precision::saisie.enum_precision as precision, -- CASE -- WHEN latitude is null AND round(st_maxdistance(st_PointOnSurface(i.geom), st_convexhull(i.geom))::numeric,-2) < 101 THEN '10 à 100m'::saisie.enum_precision -- WHEN latitude is null AND round(st_maxdistance(st_PointOnSurface(i.geom), st_convexhull(i.geom))::numeric,-2) < 501 AND round(st_maxdistance(ST_PointOnSurface(i.geom), st_convexhull(i.geom))::numeric,-2) > 100 THEN '100 à 500m'::saisie.enum_precision -- WHEN latitude is null AND round(st_maxdistance(st_PointOnSurface(i.geom), st_convexhull(i.geom))::numeric,-2) < 1001 AND round(st_maxdistance(ST_PointOnSurface(i.geom), st_convexhull(i.geom))::numeric,-2) > 500 THEN '500 à 1000m'::saisie.enum_precision -- WHEN latitude is null AND round(st_maxdistance(st_PointOnSurface(i.geom), st_convexhull(i.geom))::numeric,-2) > 1001 THEN '> 1000m'::saisie.enum_precision -- ELSE precision::saisie.enum_precision -- END as precision, 'validée'::saisie.enum_statut_validation as statut_validation, j.id_etude, k.id_protocole, effectif::integer, CASE WHEN statut_repro::text IS NOT NULL THEN (statut_repro::text)::saisie.enum_reprostatut ELSE NULL END reprostatut, CASE WHEN latitude::numeric > 6000000 THEN ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',2154) -- coordonnées en lambert93 WHEN latitude::numeric > 50 AND latitude::numeric < 3000000 THEN ST_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',27572),2154) -- coordonnées en Lambert II étendu -- WHEN latitude is null -- THEN st_pointonsurface(i.geom) -- pas de coordonnées XY on récupère la geom du centroide du site CenRA si elle existe et si une partie du nom du site est renseignée dans la colonne LOCALISATION ELSE ST_transform(ST_GeomFromText('POINT('||longitude::double precision||' '||latitude::double precision||')',4326),2154) -- coordonnées en WGS84 END as geometrie, code_releve, CASE WHEN obs_null::text ilike 'oui' THEN true::boolean ELSE false::boolean END as obs_null FROM import.import_data_cen38 a LEFT JOIN inpn.taxref b ON b.cd_nom::text = a.cd_nom::text LEFT JOIN inpn.taxref bb ON bb.nom_complet::text = a.nom_complet::text LEFT JOIN personnel c ON (c.nom||' '||c.prenom) = trim(a.observateur1::text) AND lower(c.nom_structure) = lower(a.structure) LEFT JOIN personnel d ON (d.nom||' '||d.prenom) = trim(a.observateur2::text) AND lower(d.nom_structure) = lower(a.structure) LEFT JOIN personnel e ON (e.nom||' '||e.prenom) = trim(a.observateur3::text) AND lower(e.nom_structure) = lower(a.structure) LEFT JOIN personnel f ON (f.nom||' '||f.prenom) = trim(a.observateur4::text) AND lower(f.nom_structure) = lower(a.structure) LEFT JOIN personnel g ON (g.nom||' '||g.prenom) = trim(a.observateur5::text) AND lower(g.nom_structure) = lower(a.structure) JOIN md.structure h ON lower(h.nom_structure) = lower(replace(replace(a.structure,' & ','&'),'CEN38&','')) LEFT JOIN md.etude j on a.id_etude = j.nom_etude LEFT JOIN md.protocole k USING (id_protocole) --LEFT JOIN import.sites_serena_cren_mu_2015 i ON lower(i.nom_site) like '%'||lower(trim(localisation,'s'))||'%' -- on joint la table des sites CenRA pour récupérer éventuellement le centroide d'un site si pas de XY --JOIN md.etude i ON i.nom_etude = a.id_etude -- à utiliser si les champs id_etude et id_protocole contiennent le libelle plutôt que l'identifiant, modifier aussi au-dessus id_etude en i.id_etude, id_protocole en j.id_protocole --JOIN md.protocole j ON j.libelle = a.id_protocole ) """.format(id_numerateur=numerateur.id_personne.item()) select_dataTOinsert = """ SELECT DISTINCT heure::TIME WITHOUT TIME ZONE as heure_obs, date_obs, date_debut_obs, date_fin_obs, date_textuelle, regne, nom_vern, nom_complet, cd_nom, effectif_textuel, effectif_min, effectif_max, type_effectif, phenologie, longitude, latitude, localisation, observateur, numerisateur, structure, remarque_obs, c.code_insee, diffusable, "precision", statut_validation, id_etude, id_protocole, effectif, reprostatut, t.geometrie, code_releve id_waypoint, obs_null FROM t, ign_bd_topo.commune c WHERE ST_intersects(t.geometrie ,c.geometrie) --ORDER BY nom_complet, date_obs, longitude, latitude ; """ df_data_insert = gpd.read_postgis( sql = text(with_dataTOinsert+select_dataTOinsert), con = con, geom_col='geometrie') # pd.read_sql_query( # sql = text(with_dataTOinsert+select_dataTOinsert), # con = con,) if df_data_insert.shape[0] == data_import.shape[0]: insert_data = """ INSERT INTO saisie.saisie_observation( heure_obs, date_obs, date_debut_obs, date_fin_obs, date_textuelle, regne, nom_vern, nom_complet, cd_nom, effectif_textuel, effectif_min, effectif_max, type_effectif, phenologie, longitude, latitude, localisation, observateur, numerisateur, structure, remarque_obs, code_insee, diffusable, "precision", statut_validation, id_etude, id_protocole, effectif, reprostatut, geometrie, id_waypoint, obs_null) """ with con.begin() as cnx: cnx.execute( text(with_dataTOinsert+ insert_data+ select_dataTOinsert) ) update_structure_name = """ UPDATE saisie.suivi_saisie_observation a SET utilisateur = REPLACE(utilisateur, 'inconnu', '{email_numerator}') WHERE date_operation > '{today}' ; """.format( email_numerator=numerateur.email.item(), today = today ) drop_temp_table = """ DROP TABLE IF EXISTS md.temp_import_personne; """ with con.begin() as cnx: cnx.execute(update_structure_name) cnx.execute(drop_temp_table) else : print(data_import[~data_import.index.isin(df_data_insert.index)]) data_import[~data_import.index.isin(data_import.drop_duplicates().index)] check_datageom = """ DROP VIEW IF EXISTS import.test_geom_prestation; """ with con.begin() as cnx: cnx.execute(check_datageom)