Commit 69921c70 authored by Bertrand Gauthier's avatar Bertrand Gauthier
Browse files

WIP API mécanisme de substitutions

parent c463daae
Loading
Loading
Loading
Loading
+12 −12
Original line number Diff line number Diff line
@@ -673,13 +673,13 @@ const CONFIG_SYNCHROS = [
        'source' => [
            'name' => 'SyGAL',
            'code' => 'app',
            'table' => 'src_pre_structure',
            'table' => 'SRC_PRE_STRUCTURE',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
        ],
        'destination' => [
            'name' => 'Application',
            'table' => 'pre_structure',
            'table' => 'PRE_STRUCTURE',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
            'id_strategy' => null,
@@ -711,13 +711,13 @@ const CONFIG_SYNCHROS = [
        'source' => [
            'name' => 'SyGAL',
            'code' => 'app',
            'table' => 'src_pre_etablissement',
            'table' => 'SRC_PRE_ETABLISSEMENT',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
        ],
        'destination' => [
            'name' => 'Application',
            'table' => 'pre_etablissement',
            'table' => 'PRE_ETABLISSEMENT',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
            'id_strategy' => null,
@@ -749,13 +749,13 @@ const CONFIG_SYNCHROS = [
        'source' => [
            'name' => 'SyGAL',
            'code' => 'app',
            'table' => 'src_pre_ecole_doct',
            'table' => 'SRC_PRE_ECOLE_DOCT',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
        ],
        'destination' => [
            'name' => 'Application',
            'table' => 'pre_ecole_doct',
            'table' => 'PRE_ECOLE_DOCT',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
            'id_strategy' => null,
@@ -787,13 +787,13 @@ const CONFIG_SYNCHROS = [
        'source' => [
            'name' => 'SyGAL',
            'code' => 'app',
            'table' => 'src_pre_unite_rech',
            'table' => 'SRC_PRE_UNITE_RECH',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
        ],
        'destination' => [
            'name' => 'Application',
            'table' => 'pre_unite_rech',
            'table' => 'PRE_UNITE_RECH',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
            'id_strategy' => null,
@@ -825,13 +825,13 @@ const CONFIG_SYNCHROS = [
        'source' => [
            'name' => 'SyGAL',
            'code' => 'app',
            'table' => 'src_pre_individu',
            'table' => 'SRC_PRE_INDIVIDU',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
        ],
        'destination' => [
            'name' => 'Application',
            'table' => 'pre_individu',
            'table' => 'PRE_INDIVIDU',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
            'id_strategy' => null,
@@ -863,13 +863,13 @@ const CONFIG_SYNCHROS = [
        'source' => [
            'name' => 'SyGAL',
            'code' => 'app',
            'table' => 'src_pre_doctorant',
            'table' => 'SRC_PRE_DOCTORANT',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
        ],
        'destination' => [
            'name' => 'Application',
            'table' => 'pre_doctorant',
            'table' => 'PRE_DOCTORANT',
            'connection' => 'default',
            'source_code_column' => 'SOURCE_CODE',
            'id_strategy' => null,
+23 −29
Original line number Diff line number Diff line
@@ -13,12 +13,12 @@ Structures
| 12 | INSA::550   | ED550   | **550**  |
| 17 | UCN::550    | ED550   | **550**  |

Colonne pour détection de doublon : `code`.
Colonne pour détection de doublon (NPD) : `code`.


- Leur `code` étant identique, les structures `12` et `17` peuvent faire l'objet d'une substitution :

`structure`
`pre_structure`

| id  | source_code | libelle                    | **code** |
|-----|-------------|----------------------------|----------|
@@ -29,12 +29,13 @@ Colonne pour détection de doublon : `code`.

`structure_substit`

| from_id | to_id   |
|---------|---------|
| 12      | 32      |
| 17      | 32      |
| from_id | to_id | from_npd |
|---------|-------|----------|
| 12      | 32    | 550      |
| 17      | 32    | 550      |
| ...     | ...   | ...      |

Les enregistrements substitués ne doivent plus être modifiables.
NB : `from_id` pointe vers une `pre_structure`, `to_id` pointe vers une `structure`.

Dans toutes les tables où il y a une clé étrangère `structure_id`, il faut faire la jointure avec `structure_substit` 
pour tenir compte de la substitution.
@@ -42,7 +43,7 @@ pour tenir compte de la substitution.

- Si un nouveau doublon apparaît

`structure`
`pre_structure`

| id  | source_code | libelle                    | **code** |
|-----|-------------|----------------------------|----------|
@@ -53,15 +54,16 @@ pour tenir compte de la substitution.
| ... | ...         | ...                        | ...      |
| 61  | ULHN::550   | Ecole Doctrale du Bonheur  | **550**  |

Il faut modifier la substitution pour ajouter la structure `61`.
Il faut ajouter à la substitution la structure `61`.

`structure_substit`

| from_id | to_id |
|---------|-------|
| 12      | 32    |
| 17      | 32    |
| 61      | 32    |
| from_id | to_id | from_npd |
|---------|-------|----------|
| 12      | 32    | 550      |
| 17      | 32    | 550      |
| 61      | 32    | 550      |
| ...     | ...   | ...      |



@@ -77,18 +79,10 @@ Individus
| 12 | INSA::12345 | Hochon | Paul   | null           |
| 17 | UCN::ABCD   | Hochon | Paul   | 2000-01-01     |

Colonnes utilisées pour la détection de doublon : `nom`,  `prenom`,  `date_naissance`.
Colonnes utilisées pour la détection de doublon (NPD) : `nom`,  `prenom`,  `date_naissance`.


- Leur `nom`, `prenom`, `date_naissance` étant identiques, les individus `12` et `17` peuvent faire l'objet d'une substitution :

`pre_individu`

| id  | source_code | nom    | prenom | date_naissance |
|-----|-------------|--------|--------|----------------|
| 12  | INSA::12345 | Hochon | Paul   | null           |
| 17  | UCN::ABCD   | Hochon | Paul   | 2000-01-01     |

- Leurs `nom`, `prenom`, `date_naissance` étant identiques, les individus `12` et `17` peuvent faire l'objet d'une substitution :

`individu`

@@ -96,7 +90,6 @@ Colonnes utilisées pour la détection de doublon : `nom`, `prenom`, `date_nai
|-----|-------------|--------|--------|----------------|
| 32  | SYGAL::8889 | Hochon | Paul   | 2000-01-01     |


`individu_substit`

| from_id | to_id | from_npd               |
@@ -104,6 +97,7 @@ Colonnes utilisées pour la détection de doublon : `nom`, `prenom`, `date_nai
| 12      | 32    | hochon_paul_           |
| 17      | 32    | hochon_paul_2000-01-01 |

NB : `from_id` pointe vers un `pre_individu`, `to_id` pointe vers un `individu`.

Dans toutes les tables où il y a une clé étrangère `individu_id`, il faut faire la jointure avec `individu_substit`
pour tenir compte de la substitution.
@@ -112,7 +106,7 @@ Une évolution amélioration (?) ultérieure serait, plutôt que d'utiliser la s
directement dans les tables concernées les fk 'individu_id' par l'id de l'individu substituant.


### Un pre_individu est modifié.
### Si un `pre_individu` est modifié.

  - Calculer son NPD.

@@ -152,15 +146,15 @@ directement dans les tables concernées les fk 'individu_id' par l'id de l'indiv
### Un individu est historisé.

- Rechercher dans `individu_substit` s'il est substitué.
    - S'il n'est pas substitué, stop.
    - S'il n'est pas substitué, STOP.
    - S'il est substitué, le retirer de la substitution.
    - S'il ne reste dans la substitution qu'un seul individu substitué, historiser le substituant et la substitution.


### Un individu est supprimé.

Remarque : Ne pas gérer la suppression avec un "delete cascade" sur la fk 'individu_substit.individu_substitue_id'
car cela retire la possibilité d'amélioration évoquée plus haut (substitution réelle des fk).
Remarque : Ne pas gérer la suppression avec un "delete cascade" sur la fk 'individu_substit.from_id'
car cela retire la possibilité d'amélioration évoquée plus haut (remplacement des fk).

- Idem historisation.

+91 −42
Original line number Diff line number Diff line
@@ -2,10 +2,19 @@
-- Substitutions
--

-- --drop table substit_config;
-- create table substit_config (
--     param_name varchar(128) primary key,
--     param_value text,
--     param_value_default text
-- );


--drop table substit_log;
create table substit_log (
    id bigserial primary key,
    type varchar(128) not null,
    operation varchar(64) not null,
    substitue_id bigint,
    substituant_id bigint not null,
    npd varchar(256),
@@ -27,14 +36,11 @@ select kcu.table_name as source_table,
       kcu.constraint_name
from information_schema.table_constraints tco
    join information_schema.key_column_usage kcu
              on tco.constraint_schema = kcu.constraint_schema
                  and tco.constraint_name = kcu.constraint_name
        on tco.constraint_schema = kcu.constraint_schema and tco.constraint_name = kcu.constraint_name
    join information_schema.referential_constraints rco
              on tco.constraint_schema = rco.constraint_schema
                  and tco.constraint_name = rco.constraint_name
        on tco.constraint_schema = rco.constraint_schema and tco.constraint_name = rco.constraint_name
    join information_schema.table_constraints rel_tco
              on rco.unique_constraint_schema = rel_tco.constraint_schema
                  and rco.unique_constraint_name = rel_tco.constraint_name
        on rco.unique_constraint_schema = rel_tco.constraint_schema and rco.unique_constraint_name = rel_tco.constraint_name
where tco.constraint_type = 'FOREIGN KEY';


@@ -104,6 +110,7 @@ $$;


create or replace function substit_insert_log(type varchar,
                                              operation varchar,
                                              substitue_id bigint,
                                              substituant_id bigint,
                                              npd varchar,
@@ -111,8 +118,8 @@ create or replace function substit_insert_log(type varchar,
    language plpgsql
as
$$begin
    insert into substit_log(type, substitue_id, substituant_id, npd, log)
    values (type, substitue_id, substituant_id, npd, log);
    insert into substit_log(type, operation, substitue_id, substituant_id, npd, log)
    values (type, operation, substitue_id, substituant_id, npd, log);
end
$$;

@@ -138,7 +145,7 @@ begin
    if found then
        execute format('select * from substit_fetch_data_for_substituant_%s(%L) limit 1', type, p_npd) into data;
        execute format('select substit_create_substituant_%s($1)', type) using data into substituant_record_id;
        perform substit_insert_log(type, null, substituant_record_id, p_npd,
        perform substit_insert_log(type, 'SUBSTITUANT_CREATE', null, substituant_record_id, p_npd,
                                   format('Nouvel enregistrement substituant : %s', substituant_record_id));

        while found loop
@@ -166,7 +173,7 @@ $$begin

    execute format('select substit_update_substituant_%s(%s, $1)', type, p_substituant_id) using data;

    perform substit_insert_log(type, null, p_substituant_id, null,
    perform substit_insert_log(type, 'SUBSTITUANT_UPDATE', null, p_substituant_id, null,
                               format('Mise à jour du substituant %s', p_substituant_id));
end
$$;
@@ -193,7 +200,7 @@ begin
    while found loop
        execute format('update %s_substit set histo_destruction = current_timestamp, histo_destructeur_id = 1
                       where id = %s', type, v_substit_record.id);
        perform substit_insert_log(type, v_substit_record.from_id, p_substituant_id, v_substit_record.npd,
        perform substit_insert_log(type, 'SUBSTITUTION_HISTO', v_substit_record.from_id, p_substituant_id, v_substit_record.npd,
                                   format('Historisation de la substitution de %s par %s', v_substit_record.from_id, v_substit_record.to_id));
        fetch next from v_cursor_substit into v_substit_record;
    end loop;
@@ -201,7 +208,7 @@ begin

    execute format('update %I set histo_destruction = current_timestamp, histo_destructeur_id = 1
                   where id = %s', type, p_substituant_id);
    perform substit_insert_log(type, null, substituant_record_id, null,
    perform substit_insert_log(type, 'SUBSTITUANT_HISTO', null, substituant_record_id, null,
                               format('Historisation du substituant %s', p_substituant_id));

    raise notice '=> Fait.';
@@ -227,7 +234,7 @@ begin
    execute format('insert into %s_substit (from_id, to_id, npd, histo_createur_id)
                    values (%s, %s, %L, 1)', type, p_substitue_id, p_substituant_id, p_substitue_npd);

    perform substit_insert_log(type, p_substituant_id, p_substitue_id, p_substitue_npd,
    perform substit_insert_log(type, 'SUBSTITUE_ADD', p_substituant_id, p_substitue_id, p_substitue_npd,
                               format('Ajout de %s à la substitution par %s', p_substitue_id, p_substituant_id));
end
$$;
@@ -255,14 +262,13 @@ begin

    raise notice '=> %', substit_record;

    perform substit_insert_log(type, p_substituant_id, p_substitue_id, null,
    perform substit_insert_log(type, 'SUBSTITUTION_HISTO', p_substituant_id, p_substitue_id, null,
                               format('Historisation de la substitution de %s par %s', p_substitue_id, p_substituant_id));

    -- mise à jour de la substitution
    -- mise à jour du substituant
    execute format('select * from substit_fetch_data_for_substituant_%s(%L) limit 1', type, substit_record.npd) into data;
    -- NB : si aucune donnée n'est retournée, c'est que la substitution n'a plus de raison d'être (0 doublon).
    if data is null then
        raise notice 'Aucune donnée trouvée donc suppression de la substitution...';
        raise notice 'Aucune donnée trouvée donc la substitution n''a plus de raison d''être : suppression de la substitution...';
        perform substit_delete_substitution(type, substit_record.to_id);
    else
        perform substit_update_substituant(type, substit_record.to_id, data);
@@ -534,14 +540,54 @@ $$;


-- drop function substit_replace_foreign_keys_values;
create or replace function substit_replace_foreign_keys_values(p_table varchar, p_from_id bigint, p_to_id bigint) returns void
create or replace function substit_replace_foreign_keys_values(type varchar) returns void
    language plpgsql
as
$$declare
    v_cursor refcursor;
    v_substit_fk record;
    v_col_name varchar(100);
    v_tab_name varchar(100);
    v_message text;
begin
    --
    -- Parcours des tables ayant une clé étrangère vers la table spécifiée, pour remplacer les valeurs de clés étrangères.
    -- NB : Le moindre problème d'unicité stoppe le processus.
    --

    open v_cursor for execute
        format('select * from v_substit_foreign_keys_%s where target_table = %L and source_table <> %L order by source_table', type, type, type||'_substit');
    fetch next from v_cursor into v_substit_fk;
    while found loop
        v_tab_name = v_substit_fk.source_table;
        v_col_name = v_substit_fk.fk_column;

        v_message = format('Substitution ''%s'', remplacement des FK dans %s : %s',
            upper(type), upper(v_tab_name), v_col_name);

        execute format('update %I t set %I = ss.to_id from %I ss where ss.histo_destruction is null and t.%I = ss.from_id',
            v_tab_name, v_col_name, type||'_substit', v_col_name);

        raise notice '%', v_message;

        fetch next from v_cursor into v_substit_fk;
    end loop;
    close v_cursor;
end
$$;


-- drop function substit_replace_foreign_keys_values;
create or replace function substit_replace_foreign_keys_values(type varchar, p_from_id bigint, p_to_id bigint) returns void
    language plpgsql
as
$$declare
    v_substit_fk v_substit_foreign_keys;
    v_cursor refcursor;
    v_substit_fk record;
    v_col_name varchar(100);
    v_tab_name varchar(100);
    v_id bigint;
    v_count int;
    v_message text;
begin
    --
@@ -549,40 +595,43 @@ begin
    -- Les remplacements qui déclenchent une erreur d'unicité ne sont pas gérés : il ne sont tout simplement pas faits.
    --

    for v_substit_fk in
        select * from v_substit_foreign_keys
                 where target_table = p_table
                   and source_table not in (p_table, 'substit_'||p_table,
                                           'etablissement', 'ecole_doct', 'unite_rech') -- todo : à virer, ce n'est pas la solution !
                 order by source_table
    loop
    open v_cursor for execute
        format('select * from v_substit_foreign_keys_%s where target_table = %L and source_table <> %L order by source_table', type, type, type||'_substit');
    fetch next from v_cursor into v_substit_fk;
    while found loop
        v_tab_name = v_substit_fk.source_table;
        v_col_name = v_substit_fk.fk_column;

        ---------> todo : >>>>>>>>>>> empecher le remplacement de certaines FK précises <<<<<<<<<<<<

        v_message = format('Correction de la FK %L dans %L : ', v_col_name, v_tab_name);
        v_message = format('Substitution ''%s %s => %s'', remplacement de FK dans %s : %s = %s',
            upper(type), p_from_id, p_to_id, upper(v_tab_name), v_col_name, p_from_id);

        -- Remplacement éventuelle de la valeur de la clé étrangère.
        -- NB : pas possible d'écarter les historisés et de màj histo_modification car toutes les tables n'ont pas les colonnes histo_* !
        begin
            for v_id in
                execute format('update %I set %I = %s where %I = %s returning id', v_tab_name, v_col_name, p_to_id, v_col_name, p_from_id, v_tab_name) loop
                    perform substit_insert_log(p_table, p_from_id, p_to_id, null, v_message || format('id %s : %s => %s', v_id, p_from_id, p_to_id));
                    v_message = v_message || chr(10) || format('- id %s : %s => %s', v_id, p_from_id, p_to_id);
            v_count = 0;
            for v_id in execute format('update %I set %I = %s where %I = %s returning id', v_tab_name, v_col_name, p_to_id, v_col_name, p_from_id) loop
                v_count = v_count + 1;
                v_message = v_message || format(' remplacée par %s (id %s)', p_to_id, v_id);
                perform substit_insert_log(type, 'FK_REPLACE', p_from_id, p_to_id, null, v_message);
                raise notice '%', v_message;
            end loop;
            if v_id is not null then
            if v_count = 0 then
                v_message = v_message || ' non trouvée';
                raise notice '%', v_message;
            else
                raise notice '% remplacements effectués', v_count;
            end if;

            exception WHEN unique_violation THEN
                -- échec du remplacement à cause d'une erreur d'unicité
                perform substit_insert_log(p_table, p_from_id, p_to_id, null, v_message || format('%s => %s impossible (problème d''unicité)', p_from_id, p_to_id));
                v_message = v_message || chr(10) || format('- %s => %s impossible (problème d''unicité)', p_from_id, p_to_id);
                raise notice '%', v_message;
                perform substit_insert_log(type, 'FK_REPLACE_PROBLEM', p_from_id, p_to_id, null, v_message || format(': %s => %s impossible (problème d''unicité)', p_from_id, p_to_id));
                raise notice '%', v_message || format('%s => %s impossible (problème d''unicité)', p_from_id, p_to_id);
                -- todo : historiser la ligne puisqu'il y aen a forcément une autre ? (mais la table n'a pas forcément de colonne d'histo)
        end;

        fetch next from v_cursor into v_substit_fk;
    end loop;
    close v_cursor;
end
$$;

@@ -600,7 +649,7 @@ begin
        'update %I set histo_destruction = current_timestamp, histo_destructeur_id = %s where id = %s',
        type, APP_SOURCE_ID, p_substitue_id);

    perform substit_insert_log(type, p_substitue_id, p_substituant_id, null, format('Historisation du substitué %s', p_substitue_id));
    perform substit_insert_log(type, 'SUBSTITUE_HISTO', p_substitue_id, p_substituant_id, null, format('Historisation du substitué %s', p_substitue_id));
end
$$;

@@ -631,6 +680,6 @@ begin

    raise notice '%', v_message;

    perform substit_insert_log(type, p_substitue_id, p_substituant_id, null, v_message);
    perform substit_insert_log(type, 'SUBSTITE_RESTORE', p_substitue_id, p_substituant_id, null, v_message);
end
$$;
 No newline at end of file
+42 −13
Original line number Diff line number Diff line
@@ -4,6 +4,22 @@

--=============================== INDIVIDU ================================-

--
-- Vue listant les clés étrangères (FK) pointant vers 'individu'
-- dont la valeur doit être remplacée par l'id substituant éventuel.
--
-- drop view v_substit_foreign_keys_individu;
create or replace view v_substit_foreign_keys_individu as
    select * from v_substit_foreign_keys
    where target_table = 'individu'
      and source_table <> 'individu'
      and source_table <> 'individu_substit'
      and not (
          source_table = 'doctorant' and fk_column = 'individu_id'
      )
;


--
-- Mise à jour table INDIVIDU
--
@@ -73,13 +89,26 @@ create or replace view src_pre_individu as
        left join pays p on p.code_pays_apogee::text = tmp.cod_pay_nat::text;


drop view if exists v_diff_individu;
drop view if exists src_individu;
--drop view if exists v_diff_individu; drop view if exists src_individu;
create or replace view src_individu as
    select pre.*
    select id,
           source_id,
           source_code,
           type,
           civilite,
           nom_usuel,
           nom_patronymique,
           prenom1,
           prenom2,
           prenom3,
           email,
           date_naissance,
           nationalite,
           supann_id,
           pays_id_nationalite
    from pre_individu pre
    where pre.histo_destruction is null and not exists (
        select * from individu_substit where histo_destruction is null and from_id = pre.id
        select id from individu_substit where histo_destruction is null and from_id = pre.id
    );


@@ -130,15 +159,15 @@ $$begin
    -- définies dans la table 'xxxx_substit'.
    -- > Dans le cas où cela ne change rien du tout aux substitutions existantes, il faudra tout de même :
    --   - mettre à jour les valeurs dans la colonne 'npd' de la table 'xxxx_substit' en faisant appel
    --     à la fonction 'substit_npd_xxxx()';
    --     à la présente fonction;
    --   - mettre à jour manuellement les valeurs dans la colonne 'npd_force" de la table 'pre_xxxx'.
    -- > Dans le cas où cela invalide des substitutions existantes, il faudra :
    --   - historiser les substitutions concernées dans la table 'xxxx_substit' ;
    --   - mettre à jour manuellement les valeurs dans la colonne 'npd_force" de la table 'pre_xxxx'.
    --

    return normalized_string(individu.nom_patronymique) || '_' ||
           normalized_string(individu.prenom1) || '_' ||
    return normalized_string(trim(individu.nom_patronymique)) || '_' ||
           normalized_string(trim(individu.prenom1)) || '_' ||
           normalized_string(coalesce(date(individu.date_naissance)::varchar, ''));
end;
$$;
@@ -205,12 +234,12 @@ $$begin
        select
                    mode() within group (order by i.type) as type,
                    mode() within group (order by i.civilite) as civilite,
                    mode() within group (order by i.nom_patronymique) as nom_patronymique,
                    mode() within group (order by i.nom_usuel) as nom_usuel,
                    mode() within group (order by i.prenom1) as prenom1,
                    mode() within group (order by i.prenom2) as prenom2,
                    mode() within group (order by i.prenom3) as prenom3,
                    mode() within group (order by i.email) as email,
                    mode() within group (order by trim(i.nom_patronymique)) as nom_patronymique,
                    mode() within group (order by trim(i.nom_usuel)) as nom_usuel,
                    mode() within group (order by trim(i.prenom1)) as prenom1,
                    mode() within group (order by trim(i.prenom2)) as prenom2,
                    mode() within group (order by trim(i.prenom3)) as prenom3,
                    mode() within group (order by trim(i.email)) as email,
                    mode() within group (order by i.date_naissance) as date_naissance,
                    mode() within group (order by i.nationalite) as nationalite,
                    mode() within group (order by i.supann_id) as supann_id,
+30 −7

File changed.

Preview size limit exceeded, changes collapsed.

Loading