[ADD] demo: delivered event files, example CSVs, payload fingerprint
The four demonstrations ship as event files written once by a script, the source of truth of § 15.5: a test replays the generator and compares SHA-256 fingerprints of the payload alone, naming the paths that differ, so a new build version never forces a regeneration. The affiliation profile is read from the delivered file. The two demonstration CSVs are the export of those files; the edge-case CSV is written by hand, and its windows-1252 variant derived. Version and forged-name guards skip these generated files, as § 18.5 and § 15.6 allow. Checked: node and long series; the script writes only its seven files. --- FR --- [ADD] démo : fichiers d'événement livrés, CSV d'exemple, empreinte Les quatre démonstrations se livrent en fichiers d'événement qu'un script écrit une fois, source de vérité du § 15.5 : une épreuve rejoue le générateur et compare les empreintes SHA-256 de la seule charge, en nommant les chemins qui diffèrent ; une nouvelle version n'impose donc aucune régénération. Le profil d'appartenances se lit dans le fichier livré. Les deux CSV de démonstration sont l'export de ces fichiers ; celui des cas limites s'écrit à la main, sa variante windows-1252 en dérive. Les gardes de version et de noms forgés passent ces fichiers. Vérifié : séries node et longue ; le script n'écrit que ses sept fichiers. Assisted-by: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EUXSGcwCLSC69FWEdCtWSb
This commit is contained in:
parent
201259331d
commit
2bca543e7e
18 changed files with 2493 additions and 19 deletions
2
.gitattributes
vendored
2
.gitattributes
vendored
|
|
@ -1,3 +1,5 @@
|
|||
* text=auto eol=lf
|
||||
*.cmd text eol=crlf
|
||||
test/fixtures/csv/** -text
|
||||
src/demo/livrees/*.gtt.json -text
|
||||
exemples/*.csv -text
|
||||
|
|
|
|||
16
exemples/participants_cas_limites.csv
Normal file
16
exemples/participants_cas_limites.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Nom;Prénom;Organisation;Courriel;Rôle;Exclu;Notes;Téléphone
|
||||
Lanterneau;Basile;Chœur de l’Anse;basile.lanterneau@exemple.test;animation;;;555-0101
|
||||
Pommerel;Capucine;Société des Hérons;capucine.pommerel@exemple.test;;;;555-0102
|
||||
Marbrelle;Gaspard;Cercle des Saules;;animation;;;555-0103
|
||||
Sablonnet;Ysaline;Chœur de l’Anse;;;;;555-0104
|
||||
Givrecœur;Anselme;;anselme.givrecoeur@exemple.test;;;;555-0105
|
||||
Tourterel;Héloïse;Cercle des Saules;;;oui;S’est désistée la veille;555-0106
|
||||
Fenouillet;Côme;Société des Hérons;;;;"végétarien; sans noix, ni arachides";555-0107
|
||||
Coudraie;Apolline;Chœur de l’Anse;;;;"Arrive après le premier tour
|
||||
repart avant le dessert";555-0108
|
||||
Lanterneau;Basile;Chœur de l’Anse;;;;;555-0109
|
||||
Brunœil;;Cercle des Saules;brunoeil@exemple.test;;;;555-0110
|
||||
Ormelin;Faustine;Societe des herons;;;;;555-0111
|
||||
;Clotilde;Cercle des Saules;clotilde@exemple.test;;;;555-0112
|
||||
Ardoisier;Edgar;Société des Hérons;;;;;555-0113
|
||||
Cressonnier;Bertille;Cercle des Saules;;;;;555-0114
|
||||
|
16
exemples/participants_cas_limites_cp1252.csv
Normal file
16
exemples/participants_cas_limites_cp1252.csv
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
Nom,Prénom,Organisation,Courriel,Rôle,Exclu,Notes,Téléphone
|
||||
Lanterneau,Basile,Chœur de l’Anse,basile.lanterneau@exemple.test,animation,,,555-0101
|
||||
Pommerel,Capucine,Société des Hérons,capucine.pommerel@exemple.test,,,,555-0102
|
||||
Marbrelle,Gaspard,Cercle des Saules,,animation,,,555-0103
|
||||
Sablonnet,Ysaline,Chœur de l’Anse,,,,,555-0104
|
||||
Givrecœur,Anselme,,anselme.givrecoeur@exemple.test,,,,555-0105
|
||||
Tourterel,Héloïse,Cercle des Saules,,,oui,S’est désistée la veille,555-0106
|
||||
Fenouillet,Côme,Société des Hérons,,,,"végétarien; sans noix, ni arachides",555-0107
|
||||
Coudraie,Apolline,Chœur de l’Anse,,,,"Arrive après le premier tour
|
||||
repart avant le dessert",555-0108
|
||||
Lanterneau,Basile,Chœur de l’Anse,,,,,555-0109
|
||||
Brunœil,,Cercle des Saules,brunoeil@exemple.test,,,,555-0110
|
||||
Ormelin,Faustine,Societe des herons,,,,,555-0111
|
||||
,Clotilde,Cercle des Saules,clotilde@exemple.test,,,,555-0112
|
||||
Ardoisier,Edgar,Société des Hérons,,,,,555-0113
|
||||
Cressonnier,Bertille,Cercle des Saules,,,,,555-0114
|
||||
|
261
exemples/participants_demo_grande.csv
Normal file
261
exemples/participants_demo_grande.csv
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
nom;prenom;appartenance;courriel;titre_pressenti;exclu;notes
|
||||
Mirvault;Isabelle;Comptoir Gravelune;;;non;
|
||||
Neskière;Youssef;Verrerie Vaubrenne;;animation;non;
|
||||
Abrevande;Félix;Imprimerie Yzorne;;;non;
|
||||
Fralvançon;Hugo;Brasserie Wassorel;;;non;
|
||||
Fralveux;Manon;Menuiserie Estrolle;;;non;
|
||||
Luskotte;Catherine;Fonderie Estrolle;;;non;
|
||||
WAMMERWOLM-MAWWEMMEL;Zoé;Menuiserie Ulvardes;;;non;
|
||||
Neskichon;Louise;Ateliers Rossolde;;animation;non;
|
||||
Urvesson;Catherine;Fabrique Ulvardes;;;non;
|
||||
Hulvivet;Karim;Fonderie Xandrelle;;;non;
|
||||
Selveron;Caroline;Ateliers Cassorgue;;;non;
|
||||
Korvault;Catherine;Tannerie Nervanche;;;non;
|
||||
Fralvotte;Caroline;Fonderie Ivoranche;;;non;
|
||||
Jusvesson;Geneviève;Tannerie Belvarre;;;non;
|
||||
Abrevumet;Paul;Ateliers Aubrenc;;;non;
|
||||
Fralvault;Léa;Fonderie Sorbagne;;;non;
|
||||
Obrivet;Fatima;Fabrique Sorbagne;;;non;
|
||||
Voskeval;Philippe;Menuiserie Estrolle;;animation;non;
|
||||
Dulveval;Zoé;Menuiserie Quimvaux;;;non;
|
||||
Korveux;Paul;Filature Olbrecq;;;non;
|
||||
Luskière;Julie;Fabrique Sorbagne;;;non;
|
||||
Clovande;Émilie;Ateliers Falvigne;;;non;
|
||||
Hulvande;Paul;Verrerie Vaubrenne;;;non;
|
||||
Fralvesson;Hugo;Filature Wassorel;;;non;
|
||||
Urvière;Isabelle;Brasserie Belvarre;;;non;
|
||||
Rozvotte;Thomas;Verrerie Ivoranche;;;non;
|
||||
Bralmumet;Isabelle;Verrerie Aubrenc;;;non;
|
||||
Dulvouze;Catherine;Ateliers Rossolde;;;non;
|
||||
Voskichon;Marc;Menuiserie Estrolle;;;non;
|
||||
Mirvivet;Simon;Ateliers Ivoranche;;;non;
|
||||
Clovault;Louise;Imprimerie Yzorne;;;non;
|
||||
Voskeux;Lucie;Coopérative Morvalle;;;non;
|
||||
Eskignac;Amina;Brasserie Xandrelle;;;non;
|
||||
Korvouze;Fatima;Ateliers Tavrenne;;animation;non;
|
||||
Selvoubre;Luc;Brasserie Sorbagne;;;non;
|
||||
Fralvichon;Étienne;Papeterie Ulvardes;;;non;
|
||||
Voskivet;Philippe;Ateliers Tavrenne;;;non;
|
||||
Quilmançon;Louise;Menuiserie Estrolle;;;non;
|
||||
Luskande;Sophie;Fabrique Morvalle;;;non;
|
||||
Abreveron;Pierre;Ateliers Rossolde;;;non;
|
||||
Neskeux;Philippe;Comptoir Xandrelle;;;non;
|
||||
Neskançon;Amina;Fabrique Ulvardes;;;non;
|
||||
Tolvande;Léa;Ateliers Rossolde;;;non;
|
||||
Quilmeron;Antoine;Fabrique Kalvenne;;;non;
|
||||
Clovumet;Guillaume;Verrerie Pravette;;;non;
|
||||
Eskeux;Michel;Papeterie Wassorel;;;non;
|
||||
Hulvault;Louise;Coopérative Jolvane;;;non;
|
||||
Hulveron;Catherine;Coopérative Rossolde;;animation;non;
|
||||
Ivrivet;François;Menuiserie Sorbagne;;animation;non;
|
||||
Eskumet;Samuel;Verrerie Ivoranche;;;non;
|
||||
Fralvivet;Claire;Imprimerie Yzorne;;;non;
|
||||
Mirveux;Léa;Menuiserie Wassorel;;;non;
|
||||
Gorzesson;Karim;Papeterie Ulvardes;;;non;
|
||||
Fralvumet;Jean;Ateliers Falvigne;;animation;non;
|
||||
Urvançon;Manon;Ateliers Tavrenne;;animation;non;
|
||||
Quilmande;Sylvie;Imprimerie Yzorne;;;non;
|
||||
Gorzichon;François;Ateliers Estrolle;;;non;
|
||||
Pruvesson;Amina;Fonderie Sorbagne;;;non;
|
||||
Quilmière;Lucie;Fabrique Ivoranche;;;non;
|
||||
Korvesson;Fatima;Ateliers Rossolde;;;non;
|
||||
Selvesson;Nicolas;Imprimerie Yzorne;;;non;
|
||||
Selvivet;Louis;Menuiserie Hostrenne;;animation;non;
|
||||
Neskesson;Louise;Tannerie Belvarre;;;non;
|
||||
Tolvesson;Lucie;Filature Zelvaine;;;non;
|
||||
Quilmesson;Olivier;Comptoir Gravelune;;;non;
|
||||
Luskivet;Guillaume;Brasserie Lavorneau;;;non;
|
||||
Neskault;Gabriel;Menuiserie Tavrenne;;;non;
|
||||
Urvumet;Antoine;Menuiserie Hostrenne;;;non;
|
||||
Jusvançon;Olivier;Brasserie Morvalle;;;non;
|
||||
Dulvançon;Simon;Menuiserie Ivoranche;;;non;
|
||||
Gorzard;Chloé;Fabrique Sorbagne;;;non;
|
||||
Pruveux;Gabriel;Papeterie Estrolle;;;non;
|
||||
Clovière;Manon;Ateliers Ivoranche;;;non;
|
||||
Obrouze;François;Tannerie Olbrecq;;animation;non;
|
||||
Tolveval;Karim;Tannerie Belvarre;;;non;
|
||||
Dulvumet;Geneviève;Menuiserie Quimvaux;;animation;non;
|
||||
Bérêçàuvïlle-Dœûmôntëÿ;Hugo;Ateliers Falvigne;;;non;
|
||||
Gorzeron;Claire;Menuiserie Hostrenne;;;non;
|
||||
Luskumet;Hélène;Brasserie Xandrelle;;;non;
|
||||
Fralvande;Louis;Ateliers Rossolde;;;non;
|
||||
Selvault;Émilie;Fabrique Sorbagne;;;non;
|
||||
Neskumet;Léa;Verrerie Aubrenc;;;non;
|
||||
Korvichon;Isabelle;Menuiserie Ulvardes;;;non;
|
||||
Obrande;Michel;Menuiserie Estrolle;;;non;
|
||||
Luskeval;Isabelle;Brasserie Wassorel;;;non;
|
||||
Tolvoubre;Fatima;Papeterie Ulvardes;;;non;
|
||||
Gorzière;Zoé;Menuiserie Olbrecq;;;non;
|
||||
Korvignac;Sylvie;Fabrique Ulvardes;;;non;
|
||||
Obreval;Antoine;Ateliers Cassorgue;;;non;
|
||||
Korveron;Michel;Fonderie Sorbagne;;animation;non;
|
||||
Tolvumet;Camille;Fonderie Estrolle;;;non;
|
||||
Dulvesson;Marie;Ateliers Cassorgue;;;non;
|
||||
Jusvard;Youssef;Tannerie Aubrenc;;animation;non;
|
||||
Tolvignac;Guillaume;Menuiserie Sorbagne;;;non;
|
||||
Mirvotte;Gabriel;Tannerie Belvarre;;;non;
|
||||
Urvard;Marie;Filature Wassorel;;animation;non;
|
||||
Bralmesson;Marie;Coopérative Dravenne;;animation;non;
|
||||
Neskignac;Paul;Brasserie Sorbagne;;;non;
|
||||
Bralmeux;Samuel;Ateliers Rossolde;;animation;non;
|
||||
Ivrotte;Raphaël;Coopérative Rossolde;;;non;
|
||||
Luskouze;Marc;Ateliers Rossolde;;;non;
|
||||
Jusvotte;Sylvie;Brasserie Belvarre;;;non;
|
||||
Bralmoubre;Véronique;Fonderie Xandrelle;;;non;
|
||||
Selvard;Pierre;Verrerie Vaubrenne;;;non;
|
||||
Hulveux;Marie;Coopérative Rossolde;;;non;
|
||||
Pruvotte;Sophie;Imprimerie Yzorne;;animation;non;
|
||||
Fralveval;Karim;Coopérative Gravelune;;;non;
|
||||
Gorzignac;Philippe;Imprimerie Falvigne;;;non;
|
||||
Selvouze;Étienne;Ateliers Tavrenne;;;non;
|
||||
Hulvichon;Louis;Ateliers Falvigne;;;non;
|
||||
Urvoubre;Sylvie;Verrerie Vaubrenne;;;non;
|
||||
Pruveval;Pierre;Comptoir Xandrelle;;;non;
|
||||
Hulvotte;Guillaume;Verrerie Vaubrenne;;;non;
|
||||
Abrevignac;Geneviève;Brasserie Morvalle;;;non;
|
||||
Abrevault;Gabriel;Ateliers Rossolde;;animation;non;
|
||||
Bralmignac;Amina;Fonderie Estrolle;;;non;
|
||||
Bralmançon;Olivier;Comptoir Lavorneau;;;non;
|
||||
Luskoubre;Marc;Verrerie Vaubrenne;;;non;
|
||||
Abrevoubre;Geneviève;Menuiserie Gravelune;;;non;
|
||||
Korvoubre;Raphaël;Verrerie Vaubrenne;;;non;
|
||||
Mirvard;Pierre;Verrerie Ivoranche;;;non;
|
||||
Jusvumet;Camille;Fonderie Estrolle;;;non;
|
||||
Cloveval;Samuel;Menuiserie Ulvardes;;;non;
|
||||
Selvière;Antoine;Coopérative Dravenne;;;non;
|
||||
Jusvouze;Véronique;Verrerie Vaubrenne;;;non;
|
||||
Neskivet;Paul;Comptoir Jolvane;;;non;
|
||||
Dulvande;Samuel;Verrerie Pravette;;;non;
|
||||
Bralmault;Antoine;Fabrique Sorbagne;;animation;non;
|
||||
Abrevière;Émilie;Imprimerie Yzorne;;animation;non;
|
||||
Selveval;Lucie;Filature Zelvaine;;;non;
|
||||
Tolvichon;Olivier;Fonderie Xandrelle;;;non;
|
||||
Hulvard;Fatima;Ateliers Estrolle;;;non;
|
||||
Ivreux;Alexandre;Tannerie Aubrenc;;;non;
|
||||
Korveval;Félix;Tannerie Nervanche;;;non;
|
||||
Urvichon;Karim;Tannerie Nervanche;;;non;
|
||||
Dulvichon;Émilie;Menuiserie Ulvardes;;;non;
|
||||
Tolvouze;Luc;Fabrique Ivoranche;;;non;
|
||||
Jusvivet;Claire;Imprimerie Yzorne;;animation;non;
|
||||
Quilmault;Amina;Coopérative Jolvane;;;non;
|
||||
Ivrouze;François;Fonderie Aubrenc;;;non;
|
||||
Gorzumet;Hélène;Fonderie Aubrenc;;;non;
|
||||
Ivrault;Sophie;Coopérative Morvalle;;;non;
|
||||
Bralmière;Louise;Tannerie Quimvaux;;;non;
|
||||
Korvande;Nicolas;Comptoir Sorbagne;;;non;
|
||||
Ivrumet;Geneviève;Coopérative Jolvane;;;non;
|
||||
Obresson;Geneviève;Coopérative Gravelune;;;non;
|
||||
Obrançon;Guillaume;Papeterie Hostrenne;;;non;
|
||||
Urvignac;Fatima;Ateliers Aubrenc;;;non;
|
||||
Bralmotte;Claire;Ateliers Tavrenne;;;non;
|
||||
Hulvignac;Félix;Imprimerie Hostrenne;;;non;
|
||||
Clovotte;François;Coopérative Rossolde;;;non;
|
||||
Dulvoubre;Philippe;Brasserie Belvarre;;;non;
|
||||
Rozveron;Samuel;Fonderie Sorbagne;;;non;
|
||||
Hulvoubre;Pierre;Menuiserie Olbrecq;;;non;
|
||||
Urvouze;Émilie;Comptoir Gravelune;;;non;
|
||||
Korvançon;Félix;Fonderie Ivoranche;;;non;
|
||||
Neskande;Émilie;Coopérative Gravelune;;;non;
|
||||
Mirvumet;Chantal;Menuiserie Gravelune;;;non;
|
||||
Korvotte;Alexandre;Menuiserie Nervanche;;;non;
|
||||
Eskeval;Zoé;Tannerie Nervanche;;;non;
|
||||
Neskard;Camille;Menuiserie Gravelune;;animation;non;
|
||||
Tolveron;Manon;Brasserie Belvarre;;animation;non;
|
||||
Voskoubre;Nathalie;Fonderie Kalvenne;;;non;
|
||||
Pruvière;Sophie;Ateliers Rossolde;;;non;
|
||||
Obrichon;Paul;Ateliers Rossolde;;;non;
|
||||
Abrevançon;Fatima;Tannerie Nervanche;;animation;non;
|
||||
Eskière;Nathalie;Fonderie Xandrelle;;;non;
|
||||
Ivreron;Caroline;Imprimerie Falvigne;;;non;
|
||||
Neskoubre;Véronique;Fonderie Estrolle;;;non;
|
||||
Urvivet;Anne;Fonderie Kalvenne;;;non;
|
||||
Hulvançon;Olivier;Menuiserie Estrolle;;;non;
|
||||
Clovançon;Karim;Fabrique Sorbagne;;;non;
|
||||
Quilmotte;Zoé;Ateliers Aubrenc;;;non;
|
||||
Eskouze;Félix;Filature Zelvaine;;;non;
|
||||
Rozvichon;Amina;Verrerie Olbrecq;;;non;
|
||||
Fralvière;Simon;Comptoir Sorbagne;;;non;
|
||||
Vandergrolcquesmirtavelle-Ostrovaubelindigues;Marc;Tannerie Aubrenc;;animation;non;
|
||||
Hulvière;Pierre;Papeterie Hostrenne;;;non;
|
||||
Jusvault;Raphaël;Fabrique Sorbagne;;;non;
|
||||
Dulvivet;Guillaume;Filature Olbrecq;;animation;non;
|
||||
Fralvoubre;Pierre;Imprimerie Falvigne;;;non;
|
||||
Selveux;Hélène;Brasserie Lavorneau;;;non;
|
||||
Rozvançon;Caroline;Brasserie Belvarre;;;non;
|
||||
Korvière;Olivier;Ateliers Falvigne;;;non;
|
||||
Ivrichon;Nicolas;Fabrique Sorbagne;;;non;
|
||||
Korvumet;Félix;Imprimerie Yzorne;;;non;
|
||||
Voskande;Félix;Ateliers Falvigne;;animation;non;
|
||||
Selvumet;Guillaume;Papeterie Estrolle;;;non;
|
||||
Urvotte;Michel;Verrerie Ivoranche;;;non;
|
||||
Eskault;Émilie;Coopérative Jolvane;;;non;
|
||||
Bralmeval;Hélène;Brasserie Dravenne;;;non;
|
||||
Hulvesson;Guillaume;Comptoir Jolvane;;;non;
|
||||
Jusvoubre;Caroline;Fabrique Sorbagne;;;non;
|
||||
Luskeux;Félix;Tannerie Belvarre;;;non;
|
||||
Pruvouze;Antoine;Imprimerie Yzorne;;;non;
|
||||
Quilmignac;Julie;Menuiserie Jolvane;;;non;
|
||||
Tolvançon;Étienne;Menuiserie Estrolle;;animation;non;
|
||||
Voskumet;Hugo;Ateliers Tavrenne;;;non;
|
||||
Quilmumet;Manon;Fonderie Sorbagne;;;non;
|
||||
Voskouze;Marc;Fonderie Estrolle;;;non;
|
||||
Abrevard;Samuel;Filature Dravenne;;;non;
|
||||
Luskichon;Guillaume;Brasserie Belvarre;;animation;non;
|
||||
Clovesson;Manon;Fabrique Sorbagne;;;non;
|
||||
Hulvumet;Simon;Ateliers Ivoranche;;;non;
|
||||
Voskeron;Lucie;Verrerie Ivoranche;;;non;
|
||||
Selvignac;Thomas;Brasserie Sorbagne;;animation;non;
|
||||
Neskeron;Jean;Filature Zelvaine;;;non;
|
||||
Pruvivet;Manon;Menuiserie Aubrenc;;;non;
|
||||
Clovard;Sophie;Brasserie Belvarre;;;non;
|
||||
Gorzeval;Étienne;Papeterie Ulvardes;;;non;
|
||||
Ivroubre;Philippe;Ateliers Tavrenne;;animation;non;
|
||||
Luskault;Gabriel;Verrerie Gravelune;;;non;
|
||||
Korvard;Émilie;Verrerie Vaubrenne;;;non;
|
||||
Quilmichon;Thomas;Fonderie Estrolle;;;non;
|
||||
Eskançon;Alexandre;Menuiserie Hostrenne;;;non;
|
||||
Luskançon;Émilie;Ateliers Tavrenne;;;non;
|
||||
Dulvault;Marc;Verrerie Ivoranche;;;non;
|
||||
Rozveval;Hélène;Comptoir Ivoranche;;;non;
|
||||
Pruvançon;Julie;Imprimerie Yzorne;;;non;
|
||||
Mirveron;Louis;Filature Wassorel;;;non;
|
||||
Eskard;François;Fonderie Xandrelle;;;non;
|
||||
Bralmouze;Hugo;Tannerie Falvigne;;animation;non;
|
||||
Ivrançon;Louis;Coopérative Morvalle;;;non;
|
||||
Clovivet;Karim;Fonderie Kalvenne;;;non;
|
||||
Rozvande;Léa;Verrerie Olbrecq;;;non;
|
||||
Hulvouze;Amina;Coopérative Dravenne;;;non;
|
||||
Eskesson;Étienne;Menuiserie Ulvardes;;;non;
|
||||
Abreveval;Philippe;Ateliers Falvigne;;;non;
|
||||
Quilmeux;Émilie;Ateliers Falvigne;;;non;
|
||||
Ivreval;Gabriel;Verrerie Vaubrenne;;animation;non;
|
||||
Voskotte;Simon;Fabrique Ivoranche;;;non;
|
||||
Obrignac;Chantal;Imprimerie Yzorne;;;non;
|
||||
Eskotte;Guillaume;Verrerie Nervanche;;;non;
|
||||
Obrard;Léa;Comptoir Sorbagne;;;non;
|
||||
Gorzivet;Guillaume;Fonderie Estrolle;;;non;
|
||||
Mirvançon;Geneviève;Brasserie Belvarre;;;non;
|
||||
Selvotte;Julie;Comptoir Xandrelle;;;non;
|
||||
Mirvesson;Amina;Brasserie Sorbagne;;;non;
|
||||
Luskignac;Isabelle;Papeterie Estrolle;;;non;
|
||||
Obrumet;Chloé;Comptoir Yzorne;;;non;
|
||||
Abrevesson;Anne;Fonderie Estrolle;;;non;
|
||||
Rozvumet;Fatima;Menuiserie Hostrenne;;;non;
|
||||
Mirvoubre;Guillaume;Comptoir Sorbagne;;;non;
|
||||
Rozvoubre;Véronique;Fonderie Xandrelle;;;non;
|
||||
Gorzotte;Claire;Menuiserie Olbrecq;;;non;
|
||||
Ivrière;Antoine;Fabrique Sorbagne;;;non;
|
||||
Selvichon;Isabelle;Tannerie Nervanche;;;non;
|
||||
Urvault;Isabelle;Comptoir Yzorne;;;non;
|
||||
Voskesson;Julie;Brasserie Belvarre;;;non;
|
||||
Abrevotte;Chantal;Ateliers Ivoranche;;;non;
|
||||
Neskotte;Amina;Fabrique Kalvenne;;;non;
|
||||
Voskançon;Étienne;Menuiserie Olbrecq;;;non;
|
||||
Fralveron;Zoé;Menuiserie Sorbagne;;;non;
|
||||
Rozvouze;Marc;Ateliers Estrolle;;;non;
|
||||
Abreveux;Sylvie;Comptoir Jolvane;;;non;
|
||||
Pruvignac;Karim;Menuiserie Estrolle;;;non;
|
||||
Mirveval;Zoé;Fabrique Kalvenne;;;non;
|
||||
Fralvignac;Philippe;Menuiserie Estrolle;;;non;
|
||||
Gorzeux;Gabriel;Menuiserie Ulvardes;;;non;
|
||||
Urveval;Jean;Menuiserie Quimvaux;;;non;
|
||||
|
13
exemples/participants_demo_petite.csv
Normal file
13
exemples/participants_demo_petite.csv
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
nom;prenom;appartenance;courriel;titre_pressenti;exclu;notes
|
||||
Bralmumet;Caroline;Ateliers Jolvane;;;non;
|
||||
Neskault;Hugo;Tannerie Cassorgue;;;non;
|
||||
Ivrotte;Youssef;Verrerie Ulvardes;;;non;
|
||||
Selvière;Louis;Tannerie Cassorgue;;;non;
|
||||
Selvançon;Youssef;Ateliers Jolvane;;;non;
|
||||
Quilmoubre;Amina;Verrerie Ulvardes;;;non;
|
||||
Voskotte;Julie;Verrerie Ulvardes;;;non;
|
||||
Neskivet;Paul;Ateliers Jolvane;;;non;
|
||||
Bralmignac;Michel;Tannerie Cassorgue;;;non;
|
||||
Tolveval;Zoé;Verrerie Ulvardes;;;non;
|
||||
Urvard;Luc;Tannerie Cassorgue;;;non;
|
||||
Mirveval;Véronique;Ateliers Jolvane;;;non;
|
||||
|
351
scripts/engendrer_livrables.js
Normal file
351
scripts/engendrer_livrables.js
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Les fichiers livrés que le catalogue des démonstrations détermine (§ 10.3,
|
||||
// § 15.5) : le fichier d'état de chaque démonstration, sous
|
||||
// src/demo/livrees/ ; l'export CSV de la liste des participants de la petite
|
||||
// et de la grande, et la variante windows-1252 du CSV des cas limites, sous
|
||||
// exemples/. Le CSV des cas limites s'écrit à la main : le script le lit et
|
||||
// ne l'écrit jamais.
|
||||
//
|
||||
// node scripts/engendrer_livrables.js [destination]
|
||||
//
|
||||
// La destination, la racine du projet par défaut, reçoit les sept fichiers
|
||||
// sous leurs chemins relatifs, et rien n'est écrit ailleurs ; un chemin
|
||||
// relatif se lit depuis le répertoire courant. Tout se calcule avant la
|
||||
// première écriture. Le fichier livré est la source de vérité (§ 15.5,
|
||||
// point 5) : src/demo/livrees.long.test.js rejoue ce script et compare
|
||||
// l'empreinte de chaque charge à celle du fichier livré. Lancer le script
|
||||
// est donc le geste conscient qui change une démonstration : il imprime,
|
||||
// pour chaque fichier, s'il est créé, inchangé ou réécrit, et l'empreinte de
|
||||
// la charge de chaque fichier d'état. Ni horloge ni tirage n'y entrent.
|
||||
//
|
||||
// Une démonstration livrée est la configuration de son entrée du catalogue,
|
||||
// mise en charge (§ 4, § 8.8) :
|
||||
// - l'événement demo:<clé>, au nom du catalogue, sans date, en brouillon et
|
||||
// sans filiation ;
|
||||
// - les sièges par défaut : la capacité la plus fréquente des tables, la
|
||||
// plus grande à égalité ; une table d'une autre capacité porte la sienne
|
||||
// en surcharge (§ 6.1) ;
|
||||
// - les tables rondes, rangées en grille dans l'ordre de la configuration,
|
||||
// rangée par rangée, leurs centres espacés de 300 cm ; la grille a la plus
|
||||
// petite largeur dont le carré contient toutes les tables, et seule sa
|
||||
// dernière rangée est incomplète ;
|
||||
// - les contraintes de la configuration, les sièges non attribués, la
|
||||
// génération par défaut de la recherche ;
|
||||
// - chaque réservation, celle d'un animateur, au siège 1 de sa table pour
|
||||
// tous les tours, ce siège titré « animation », et le titre pressenti
|
||||
// « animation » porté par l'animateur ;
|
||||
// - aucune proposition : le placement n'est pas figé (§ 15.5, point 6).
|
||||
// Son fichier d'état s'écrit à la révision 1, sans journal, sous la version
|
||||
// de la construction qui écrit (§ 18.6).
|
||||
import { createHash } from 'node:crypto';
|
||||
import { mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { decoder } from '../src/csv/encodage.js';
|
||||
import { SEPARATEUR, ecrireCsv, exporterParticipants } from '../src/csv/export.js';
|
||||
import { decouper } from '../src/csv/lecture.js';
|
||||
import { CATALOGUE } from '../src/demo/catalogue.js';
|
||||
import { serialiser, serialiserCharge } from '../src/stockage/canonique.js';
|
||||
import { analyser, creerCharge } from '../src/stockage/document.js';
|
||||
import { VERSION } from '../src/version.genere.js';
|
||||
|
||||
const RACINE = resolve(fileURLToPath(new URL('..', import.meta.url)));
|
||||
|
||||
// Chemins des livrables, relatifs à la racine, séparés par « / ».
|
||||
const DOSSIER_LIVREES = 'src/demo/livrees';
|
||||
const CAS_LIMITES = 'exemples/participants_cas_limites.csv';
|
||||
const CAS_LIMITES_CP1252 = 'exemples/participants_cas_limites_cp1252.csv';
|
||||
const csvDeDemonstration = (cle) => `exemples/participants_demo_${cle}.csv`;
|
||||
|
||||
// Démonstrations dont la liste des participants est livrée en CSV, dans
|
||||
// l'ordre du § 10.3.
|
||||
const DEMONSTRATIONS_EN_CSV = ['petite', 'grande'];
|
||||
|
||||
// Séparateur de la variante windows-1252 (§ 10.3).
|
||||
const VIRGULE = ',';
|
||||
|
||||
// Écart entre les centres de deux tables voisines de la grille, en cm.
|
||||
const ECART_CM = 300;
|
||||
|
||||
// Le siège de l'animateur, et le titre qu'il porte.
|
||||
const SIEGE_ANIMATEUR = 1;
|
||||
const ANIMATION = 'animation';
|
||||
|
||||
const USAGE = 'usage : node scripts/engendrer_livrables.js [destination]';
|
||||
|
||||
// Capacité la plus fréquente parmi les tables, la plus grande à égalité ; le
|
||||
// résultat ne dépend pas de l'ordre des tables.
|
||||
function capaciteParDefaut(tables) {
|
||||
const effectifs = new Map();
|
||||
for (const { capacite } of tables) effectifs.set(capacite, (effectifs.get(capacite) ?? 0) + 1);
|
||||
let defaut = null;
|
||||
for (const [capacite, effectif] of effectifs) {
|
||||
const effectifDuDefaut = effectifs.get(defaut) ?? 0;
|
||||
if (effectif > effectifDuDefaut || (effectif === effectifDuDefaut && capacite > defaut)) defaut = capacite;
|
||||
}
|
||||
return defaut;
|
||||
}
|
||||
|
||||
// Largeur de la grille de nombre tables : la plus petite largeur c telle
|
||||
// que c × c ≥ nombre.
|
||||
function colonnesDe(nombre) {
|
||||
let colonnes = 1;
|
||||
while (colonnes * colonnes < nombre) colonnes += 1;
|
||||
return colonnes;
|
||||
}
|
||||
|
||||
// Plus grand identifiant d'une liste d'enregistrements ; 0 pour une liste
|
||||
// vide.
|
||||
const plusGrandId = (enregistrements) => enregistrements.reduce((plus, { id }) => Math.max(plus, id), 0);
|
||||
|
||||
// Animateur de chaque table réservée, par identifiant de table, dans l'ordre
|
||||
// des réservations. Lève quand une réservation ne vaut pas pour tous les
|
||||
// tours, ou qu'une table en reçoit deux : le siège 1 ne porte qu'un
|
||||
// animateur, et le script n'écrit que ce que le catalogue produit.
|
||||
function animateursDe(reservations, cle) {
|
||||
const animateurs = new Map();
|
||||
for (const { participant, table, portee } of reservations) {
|
||||
if (portee !== 'tous' || animateurs.has(table)) {
|
||||
throw new Error(
|
||||
`démonstration ${cle} : la table ${table} reçoit au plus un animateur, réservé pour tous les tours`,
|
||||
);
|
||||
}
|
||||
animateurs.set(table, participant);
|
||||
}
|
||||
return animateurs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Charge de la démonstration d'une entrée du catalogue, telle que l'en-tête
|
||||
* du module la décrit. La configuration est construite une fois ; la charge
|
||||
* rendue est faite d'objets neufs.
|
||||
*
|
||||
* @param {{cle: string, nom: string, construire: () => import('../src/moteur/types.js').Configuration}} entree
|
||||
* @returns {import('../src/stockage/types.js').Charge}
|
||||
* @throws {Error} quand une réservation ne vaut pas pour tous les tours, ou
|
||||
* qu'une table en reçoit deux
|
||||
*/
|
||||
export function chargeDeDemonstration({ cle, nom, construire }) {
|
||||
const { participants, tables, tours, reservations, contraintes } = construire();
|
||||
const siegesParDefaut = capaciteParDefaut(tables);
|
||||
const neuve = creerCharge({ id: `demo:${cle}`, nom, date: null, siegesParDefaut, tours });
|
||||
const animateurs = animateursDe(reservations, cle);
|
||||
const reserves = new Set(animateurs.values());
|
||||
const colonnes = colonnesDe(tables.length);
|
||||
const { separerAppartenances, nouveauxVoisins, nouvelleTable, varierAppartenances } = contraintes;
|
||||
return {
|
||||
...neuve,
|
||||
reglages: { ...neuve.reglages, separerAppartenances, nouveauxVoisins, nouvelleTable, varierAppartenances },
|
||||
prochainsIds: { participant: plusGrandId(participants) + 1, table: plusGrandId(tables) + 1, proposition: 1 },
|
||||
participants: participants.map((participant) => ({
|
||||
id: participant.id,
|
||||
nom: participant.nom,
|
||||
prenom: participant.prenom ?? null,
|
||||
appartenance: participant.appartenance,
|
||||
courriel: null,
|
||||
titrePressenti: reserves.has(participant.id) ? ANIMATION : null,
|
||||
notes: null,
|
||||
exclu: participant.exclu === true,
|
||||
})),
|
||||
tables: tables.map(({ id, numero, capacite }, rang) => ({
|
||||
id,
|
||||
numero,
|
||||
sieges: capacite === siegesParDefaut ? null : capacite,
|
||||
forme: 'ronde',
|
||||
position: { x: (rang % colonnes) * ECART_CM, y: Math.floor(rang / colonnes) * ECART_CM },
|
||||
})),
|
||||
reservations: [...animateurs].map(([table, participant]) => ({
|
||||
participant,
|
||||
table,
|
||||
siege: SIEGE_ANIMATEUR,
|
||||
portee: 'tous',
|
||||
tour: null,
|
||||
})),
|
||||
titres: [...animateurs.keys()].map((table) => ({ table, siege: SIEGE_ANIMATEUR, libelle: ANIMATION })),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Texte du fichier d'état livré d'une entrée du catalogue : sa charge, en
|
||||
* forme canonique, à la révision 1, sous la version de la construction qui
|
||||
* écrit.
|
||||
*
|
||||
* @param {{cle: string, nom: string, construire: Function}} entree
|
||||
* @param {{produitVersion?: string}} [options] version affichée de la
|
||||
* construction, VERSION.affichee par défaut
|
||||
* @returns {string}
|
||||
*/
|
||||
export function texteDeDemonstration(entree, { produitVersion = VERSION.affichee } = {}) {
|
||||
return serialiser(chargeDeDemonstration(entree), { revision: 1, produitVersion });
|
||||
}
|
||||
|
||||
/**
|
||||
* Empreinte d'une charge (§ 15.5, point 5) : SHA-256 de son texte canonique
|
||||
* seul, en 64 chiffres hexadécimaux minuscules. L'en-tête n'y entre pas : la
|
||||
* version de la construction qui écrit ne la change pas.
|
||||
*
|
||||
* @param {import('../src/stockage/types.js').Charge} charge
|
||||
* @returns {string}
|
||||
*/
|
||||
export function empreinte(charge) {
|
||||
return createHash('sha256').update(serialiserCharge(charge)).digest('hex');
|
||||
}
|
||||
|
||||
// Octet windows-1252 de chaque caractère que ce codage représente, lu dans
|
||||
// le décodeur windows-1252 lui-même, octet par octet : l'encodage en est
|
||||
// l'inverse exact. 00 à 7F donnent l'ASCII, A0 à FF les mêmes points de
|
||||
// code, 80 à 9F les signes propres à windows-1252, dont « € », « œ » et
|
||||
// l'apostrophe typographique.
|
||||
const DECODEUR_1252 = new TextDecoder('windows-1252');
|
||||
const OCTET_1252 = new Map(
|
||||
Array.from({ length: 256 }, (_, octet) => [DECODEUR_1252.decode(Uint8Array.of(octet)), octet]),
|
||||
);
|
||||
|
||||
/**
|
||||
* Octets windows-1252 d'un texte, un par caractère, sans marque.
|
||||
*
|
||||
* @param {string} texte
|
||||
* @returns {Uint8Array}
|
||||
* @throws {RangeError} pour un caractère que windows-1252 ne représente pas,
|
||||
* nommé par son point de code et son rang dans le texte, en unités UTF-16
|
||||
*/
|
||||
export function enWindows1252(texte) {
|
||||
const octets = [];
|
||||
let rang = 0;
|
||||
for (const caractere of texte) {
|
||||
const octet = OCTET_1252.get(caractere);
|
||||
if (octet === undefined) {
|
||||
const point = caractere.codePointAt(0).toString(16).toUpperCase().padStart(4, '0');
|
||||
throw new RangeError(`U+${point}, au rang ${rang} : caractère absent de windows-1252`);
|
||||
}
|
||||
octets.push(octet);
|
||||
rang += caractere.length;
|
||||
}
|
||||
return Uint8Array.from(octets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variante windows-1252 du CSV des cas limites (§ 10.3) : ses
|
||||
* enregistrements, découpés sous « ; », réécrits sous « , » par l'écriture
|
||||
* unique des CSV, puis encodés en windows-1252, sans marque. Chaque champ
|
||||
* garde son texte ; un champ qui porte la virgule, un guillemet ou une fin
|
||||
* de ligne s'écrit cité.
|
||||
*
|
||||
* @param {Uint8Array} octets le fichier écrit à la main, que decoder lit
|
||||
* @returns {Uint8Array}
|
||||
* @throws {Error} quand un guillemet reste ouvert à la fin du fichier
|
||||
*/
|
||||
export function varianteWindows1252(octets) {
|
||||
const { enregistrements, guillemetOuvert } = decouper(decoder(octets).texte, SEPARATEUR);
|
||||
if (guillemetOuvert) throw new Error(`${CAS_LIMITES} : un guillemet reste ouvert à la fin du fichier`);
|
||||
// ecrireCsv écrit de l'UTF-8 avec marque, que le décodeur UTF-8 retire.
|
||||
return enWindows1252(new TextDecoder().decode(ecrireCsv(enregistrements, VIRGULE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Les sept livrables, calculés sans rien écrire : { chemin, octets,
|
||||
* empreinte } ; le chemin est relatif à la racine, séparé par « / » ;
|
||||
* l'empreinte, celle de la charge d'un fichier d'état, null pour un CSV. Dans
|
||||
* l'ordre : le fichier d'état de chaque démonstration, dans l'ordre du
|
||||
* catalogue ; l'export de la liste des participants de la petite, puis de la
|
||||
* grande, chacun lu dans le texte du fichier d'état de sa démonstration ; la
|
||||
* variante windows-1252 du CSV des cas limites.
|
||||
*
|
||||
* @param {{casLimites: Uint8Array, produitVersion?: string}} options
|
||||
* casLimites : les octets du CSV des cas limites, écrit à la main
|
||||
* @returns {Array<{chemin: string, octets: Uint8Array, empreinte: string|null}>}
|
||||
*/
|
||||
export function livrables({ casLimites, produitVersion = VERSION.affichee }) {
|
||||
const etats = CATALOGUE.map((entree) => {
|
||||
const texte = texteDeDemonstration(entree, { produitVersion });
|
||||
return { cle: entree.cle, texte, charge: analyser(texte).charge };
|
||||
});
|
||||
const fichiers = etats.map(({ cle, texte, charge }) => ({
|
||||
chemin: `${DOSSIER_LIVREES}/${cle}.gtt.json`,
|
||||
octets: new TextEncoder().encode(texte),
|
||||
empreinte: empreinte(charge),
|
||||
}));
|
||||
for (const cle of DEMONSTRATIONS_EN_CSV) {
|
||||
const etat = etats.find((candidat) => candidat.cle === cle);
|
||||
if (etat === undefined) throw new Error(`aucune démonstration ${cle} au catalogue`);
|
||||
fichiers.push({
|
||||
chemin: csvDeDemonstration(cle),
|
||||
octets: exporterParticipants(etat.charge.participants),
|
||||
empreinte: null,
|
||||
});
|
||||
}
|
||||
fichiers.push({ chemin: CAS_LIMITES_CP1252, octets: varianteWindows1252(casLimites), empreinte: null });
|
||||
return fichiers;
|
||||
}
|
||||
|
||||
// Octets d'un fichier, ou null quand il n'existe pas.
|
||||
function lireSiPresent(chemin) {
|
||||
try {
|
||||
return readFileSync(chemin);
|
||||
} catch (erreur) {
|
||||
if (erreur.code === 'ENOENT') return null;
|
||||
throw erreur;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Écrit les sept livrables sous destination, après les avoir tous calculés,
|
||||
* et rien ailleurs. Le CSV des cas limites se lit sous source. Rend chaque
|
||||
* livrable avec son état : « créé » quand le fichier n'existait pas,
|
||||
* « inchangé » quand il portait déjà ces octets, « réécrit » sinon.
|
||||
*
|
||||
* @param {string} destination racine qui reçoit les fichiers
|
||||
* @param {{source?: string, produitVersion?: string}} [options] source : la
|
||||
* racine du projet par défaut
|
||||
* @returns {Array<{chemin: string, octets: Uint8Array, empreinte: string|null, etat: string}>}
|
||||
*/
|
||||
export function ecrireLivrables(destination, { source = RACINE, produitVersion } = {}) {
|
||||
const casLimites = readFileSync(join(source, ...CAS_LIMITES.split('/')));
|
||||
return livrables({ casLimites, produitVersion }).map((fichier) => {
|
||||
const cible = join(destination, ...fichier.chemin.split('/'));
|
||||
const avant = lireSiPresent(cible);
|
||||
mkdirSync(dirname(cible), { recursive: true });
|
||||
writeFileSync(cible, fichier.octets);
|
||||
const etat = avant === null ? 'créé' : Buffer.compare(avant, fichier.octets) === 0 ? 'inchangé' : 'réécrit';
|
||||
return { ...fichier, etat };
|
||||
});
|
||||
}
|
||||
|
||||
// Ligne de commande : une destination au plus, la racine par défaut. Sort à
|
||||
// 0 après l'écriture, à 1 quand un livrable ne se calcule ou ne s'écrit pas,
|
||||
// à 2 sur un usage fautif.
|
||||
function executer(arguments_) {
|
||||
if (arguments_.length > 1 || arguments_.some((argument) => argument.startsWith('-'))) {
|
||||
console.error(USAGE);
|
||||
return 2;
|
||||
}
|
||||
const destination = arguments_.length === 1 ? resolve(arguments_[0]) : RACINE;
|
||||
let ecrits;
|
||||
try {
|
||||
ecrits = ecrireLivrables(destination);
|
||||
} catch (erreur) {
|
||||
console.error(`Livrables : ${erreur.message}`);
|
||||
return 1;
|
||||
}
|
||||
console.log(`Livrables écrits sous ${destination}, construction ${VERSION.affichee} :`);
|
||||
for (const { chemin, octets, empreinte: hachage, etat } of ecrits) {
|
||||
const suite = hachage === null ? '' : `, empreinte de la charge ${hachage}`;
|
||||
console.log(` ${etat.padEnd(9)}${chemin} (${octets.length} octets${suite})`);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Vrai quand ce fichier est le script que Node a lancé, et non un module
|
||||
// importé.
|
||||
function estLanceDirectement() {
|
||||
if (process.argv[1] === undefined) return false;
|
||||
try {
|
||||
return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (estLanceDirectement()) process.exitCode = executer(process.argv.slice(2));
|
||||
491
scripts/engendrer_livrables.test.js
Normal file
491
scripts/engendrer_livrables.test.js
Normal file
|
|
@ -0,0 +1,491 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Épreuves du script qui engendre les fichiers livrés (§ 10.3, § 15.5) : la
|
||||
// charge de chaque démonstration — la configuration du catalogue,
|
||||
// l'événement demo:<clé>, la salle en grille, les animateurs au siège 1 —,
|
||||
// le fichier d'état qu'il en écrit, la variante windows-1252 du CSV des cas
|
||||
// limites, et la ligne de commande, qui écrit dans le dossier donné et nulle
|
||||
// part ailleurs, et réécrit les mêmes octets quand on la relance. « Nulle
|
||||
// part ailleurs » s'éprouve sur une copie du projet, photographiée avant et
|
||||
// après chaque lancement : tout fichier écrit hors des sept livrables s'y
|
||||
// voit, le CSV des cas limites, que le script lit sans l'écrire, comme les
|
||||
// autres. Le contenu des fichiers livrés eux-mêmes s'éprouve à côté d'eux,
|
||||
// dans src/demo/livrees.test.js et src/demo/livrees.long.test.js.
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { cpSync, mkdtempSync, readdirSync, readFileSync, rmSync, statSync, utimesSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join, relative, sep } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { exporterParticipants } from '../src/csv/export.js';
|
||||
import { decouper } from '../src/csv/lecture.js';
|
||||
import { CATALOGUE } from '../src/demo/catalogue.js';
|
||||
import { serialiser } from '../src/stockage/canonique.js';
|
||||
import { GENERATION_PAR_DEFAUT, analyser, configurationDepuisCharge } from '../src/stockage/document.js';
|
||||
import { VERSION } from '../src/version.genere.js';
|
||||
import { describe, test } from '../test/lanceur.js';
|
||||
import { versionVoisine } from '../test/version_voisine.js';
|
||||
import {
|
||||
chargeDeDemonstration,
|
||||
enWindows1252,
|
||||
livrables,
|
||||
texteDeDemonstration,
|
||||
varianteWindows1252,
|
||||
} from './engendrer_livrables.js';
|
||||
|
||||
const RACINE = fileURLToPath(new URL('..', import.meta.url));
|
||||
const SCRIPT = fileURLToPath(new URL('./engendrer_livrables.js', import.meta.url));
|
||||
const CAS_LIMITES = join(RACINE, 'exemples', 'participants_cas_limites.csv');
|
||||
|
||||
// Les sept livrables que le script écrit, chemins relatifs à la racine, dans
|
||||
// l'ordre où il les écrit : un fichier d'état par démonstration du
|
||||
// catalogue, les deux CSV de démonstration, puis la variante windows-1252 du
|
||||
// CSV des cas limites (§ 10.3).
|
||||
const LIVRABLES = [
|
||||
...CATALOGUE.map(({ cle }) => `src/demo/livrees/${cle}.gtt.json`),
|
||||
'exemples/participants_demo_petite.csv',
|
||||
'exemples/participants_demo_grande.csv',
|
||||
'exemples/participants_cas_limites_cp1252.csv',
|
||||
];
|
||||
|
||||
// Ce que le script lit, chemins relatifs à la racine du projet : package.json,
|
||||
// qui déclare les modules ES, le script, le code de src/ qu'il importe et les
|
||||
// CSV de exemples/, celui des cas limites compris, qu'il lit sans l'écrire.
|
||||
const COPIES = ['package.json', 'scripts/engendrer_livrables.js', 'src', 'exemples'];
|
||||
|
||||
// Date de modification, en secondes, de chaque fichier copié : un instant
|
||||
// ancien. Toute écriture ultérieure la déplace, d'octets identiques comprise,
|
||||
// quelle que soit la finesse de l'horloge du système de fichiers.
|
||||
const DATE_ANCIENNE = 1_000_000_000;
|
||||
|
||||
const MARQUE = '\u{FEFF}';
|
||||
|
||||
// Entrée du catalogue de clé donnée.
|
||||
const entreeDe = (cle) => CATALOGUE.find((entree) => entree.cle === cle);
|
||||
|
||||
// Entrée d'essai : la petite démonstration, sa configuration retouchée par
|
||||
// retoucher, qui reçoit une copie et la rend.
|
||||
const entreeRetouchee = (retoucher) => ({
|
||||
cle: 'essai',
|
||||
nom: 'Essai',
|
||||
construire: () => retoucher(entreeDe('petite').construire()),
|
||||
});
|
||||
|
||||
const parTable = (a, b) => a.table - b.table;
|
||||
|
||||
// Dossier temporaire neuf, passé à faire, puis effacé ; rend ce que rend
|
||||
// faire.
|
||||
function avecDossier(faire) {
|
||||
const dossier = mkdtempSync(join(tmpdir(), 'livrables-'));
|
||||
try {
|
||||
return faire(dossier);
|
||||
} finally {
|
||||
rmSync(dossier, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
// Fichiers d'un dossier, à toute profondeur, en chemins relatifs séparés par
|
||||
// « / », triés.
|
||||
const fichiersSous = (dossier) =>
|
||||
readdirSync(dossier, { recursive: true, withFileTypes: true })
|
||||
.filter((entree) => entree.isFile())
|
||||
.map((entree) => relative(dossier, join(entree.parentPath, entree.name)).split(sep).join('/'))
|
||||
.sort();
|
||||
|
||||
// Instantané d'un dossier : la taille et la date de modification, en
|
||||
// nanosecondes, de chaque fichier à toute profondeur, sous son chemin relatif
|
||||
// séparé par « / ».
|
||||
const instantane = (dossier) =>
|
||||
new Map(
|
||||
fichiersSous(dossier).map((chemin) => {
|
||||
const { size, mtimeNs } = statSync(join(dossier, chemin), { bigint: true });
|
||||
return [chemin, `${size} octets, modifié à ${mtimeNs} ns`];
|
||||
}),
|
||||
);
|
||||
|
||||
// Chemins que deux instantanés distinguent, triés : un fichier créé, retiré,
|
||||
// ou dont la taille ou la date de modification change. Un fichier réécrit avec
|
||||
// les mêmes octets en fait partie, sa date de modification avance.
|
||||
const differences = (avant, apres) =>
|
||||
[...new Set([...avant.keys(), ...apres.keys()])].filter((chemin) => avant.get(chemin) !== apres.get(chemin)).sort();
|
||||
|
||||
// Lance le script de chemin donné depuis le répertoire cwd ; rend son statut
|
||||
// et ses sorties.
|
||||
const lancerScript = (script, cwd, ...arguments_) =>
|
||||
spawnSync(process.execPath, [script, ...arguments_], { cwd, encoding: 'utf8' });
|
||||
|
||||
// Lance le script du projet depuis le répertoire cwd.
|
||||
const lancer = (cwd, ...arguments_) => lancerScript(SCRIPT, cwd, ...arguments_);
|
||||
|
||||
// Dossier temporaire neuf qui porte, sous projet/, une copie de ce que le
|
||||
// script lit, chaque fichier daté de DATE_ANCIENNE. Passe à faire ce dossier
|
||||
// et lancerCopie, qui lance depuis lui le script copié ; efface le dossier ;
|
||||
// rend ce que rend faire. Le script y est à la racine de son arbre, comme
|
||||
// dans le projet : un fichier qu'il écrit hors de la destination tombe dans
|
||||
// ce dossier, copie du projet ou répertoire courant, jamais dans le projet.
|
||||
function avecProjetCopie(faire) {
|
||||
return avecDossier((dossier) => {
|
||||
for (const chemin of COPIES) {
|
||||
cpSync(join(RACINE, chemin), join(dossier, 'projet', chemin), { recursive: true });
|
||||
}
|
||||
for (const chemin of fichiersSous(dossier)) utimesSync(join(dossier, chemin), DATE_ANCIENNE, DATE_ANCIENNE);
|
||||
const copie = join(dossier, 'projet', 'scripts', 'engendrer_livrables.js');
|
||||
return faire(dossier, (...arguments_) => lancerScript(copie, dossier, ...arguments_));
|
||||
});
|
||||
}
|
||||
|
||||
const echapper = (texte) => texte.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
describe("engendrer_livrables : la charge d'une démonstration (§ 15)", () => {
|
||||
test('porte la configuration du catalogue : participants, capacités, tours, réservations, contraintes', () => {
|
||||
for (const entree of CATALOGUE) {
|
||||
const configuration = entree.construire();
|
||||
const charge = chargeDeDemonstration(entree);
|
||||
const lue = configurationDepuisCharge(charge);
|
||||
assert.deepEqual(
|
||||
{ ...lue, reservations: [...lue.reservations].sort(parTable) },
|
||||
{
|
||||
...configuration,
|
||||
participants: configuration.participants.map(({ id, nom, appartenance }) => ({
|
||||
id,
|
||||
nom,
|
||||
appartenance,
|
||||
exclu: false,
|
||||
})),
|
||||
reservations: [...configuration.reservations].sort(parTable),
|
||||
},
|
||||
entree.cle,
|
||||
);
|
||||
assert.deepEqual(
|
||||
charge.participants.map(({ prenom }) => prenom),
|
||||
configuration.participants.map(({ prenom }) => prenom),
|
||||
entree.cle,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("l'événement demo:<clé> : nom du catalogue, sans date, en brouillon, sans filiation, les tours et le défaut de sièges de la configuration", () => {
|
||||
// Sièges par défaut des quatre démonstrations (§ 15.1, § 15.3).
|
||||
const defauts = new Map([['grande', 8], ['grande-sans-exception', 8], ['petite', 3], ['petite-conflit', 3]]);
|
||||
assert.deepEqual(CATALOGUE.map(({ cle }) => cle).sort(), [...defauts.keys()].sort());
|
||||
for (const entree of CATALOGUE) {
|
||||
assert.deepEqual(
|
||||
chargeDeDemonstration(entree).evenement,
|
||||
{
|
||||
id: `demo:${entree.cle}`,
|
||||
nom: entree.nom,
|
||||
date: null,
|
||||
siegesParDefaut: defauts.get(entree.cle),
|
||||
tours: entree.construire().tours,
|
||||
unite: 'cm',
|
||||
etat: 'brouillon',
|
||||
filiation: null,
|
||||
},
|
||||
entree.cle,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('les réglages : les contraintes de la configuration, une à une, les sièges non attribués, la génération par défaut', () => {
|
||||
for (const entree of CATALOGUE) {
|
||||
assert.deepEqual(
|
||||
chargeDeDemonstration(entree).reglages,
|
||||
{ ...entree.construire().contraintes, attribuerSieges: false, generation: { ...GENERATION_PAR_DEFAUT } },
|
||||
entree.cle,
|
||||
);
|
||||
}
|
||||
const contraintes = {
|
||||
separerAppartenances: false,
|
||||
nouveauxVoisins: true,
|
||||
nouvelleTable: false,
|
||||
varierAppartenances: true,
|
||||
};
|
||||
const entree = entreeRetouchee((configuration) => ({ ...configuration, contraintes }));
|
||||
assert.deepEqual(chargeDeDemonstration(entree).reglages, {
|
||||
...contraintes,
|
||||
attribuerSieges: false,
|
||||
generation: { ...GENERATION_PAR_DEFAUT },
|
||||
});
|
||||
});
|
||||
|
||||
test('les tables : rondes, en grille rangée par rangée, centres à 300 cm, la dernière rangée seule incomplète', () => {
|
||||
assert.deepEqual(chargeDeDemonstration(entreeDe('petite')).tables, [
|
||||
{ id: 1, numero: 1, sieges: null, forme: 'ronde', position: { x: 0, y: 0 } },
|
||||
{ id: 2, numero: 2, sieges: null, forme: 'ronde', position: { x: 300, y: 0 } },
|
||||
{ id: 3, numero: 3, sieges: null, forme: 'ronde', position: { x: 0, y: 300 } },
|
||||
{ id: 4, numero: 4, sieges: null, forme: 'ronde', position: { x: 300, y: 300 } },
|
||||
]);
|
||||
// 33 tables : six colonnes, cinq rangées pleines, puis trois tables.
|
||||
const { tables } = chargeDeDemonstration(entreeDe('grande'));
|
||||
const positions = tables.map(({ position: { x, y } }) => [x, y]);
|
||||
assert.deepEqual(positions.slice(0, 7), [[0, 0], [300, 0], [600, 0], [900, 0], [1200, 0], [1500, 0], [0, 300]]);
|
||||
assert.deepEqual(positions.slice(-4), [[1500, 1200], [0, 1500], [300, 1500], [600, 1500]]);
|
||||
assert.equal(new Set(positions.map(String)).size, 33);
|
||||
assert.ok(tables.every(({ forme }) => forme === 'ronde'));
|
||||
assert.deepEqual(tables.map(({ id, numero }) => [id, numero]), positions.map((_, rang) => [rang + 1, rang + 1]));
|
||||
});
|
||||
|
||||
test("les sièges : le défaut est la capacité la plus fréquente, la plus grande à égalité ; toute autre capacité surcharge sa table (§ 6.1)", () => {
|
||||
const surcharges = (charge) =>
|
||||
charge.tables.filter(({ sieges }) => sieges !== null).map(({ numero, sieges }) => [numero, sieges]);
|
||||
assert.deepEqual(surcharges(chargeDeDemonstration(entreeDe('grande'))), [[30, 7], [31, 7], [32, 7], [33, 7]]);
|
||||
for (const cle of ['grande-sans-exception', 'petite', 'petite-conflit']) {
|
||||
assert.deepEqual(surcharges(chargeDeDemonstration(entreeDe(cle))), [], cle);
|
||||
}
|
||||
const capacites = [6, 9, 6, 9];
|
||||
const egalite = chargeDeDemonstration(
|
||||
entreeRetouchee((configuration) => ({
|
||||
...configuration,
|
||||
tables: configuration.tables.map((table, rang) => ({ ...table, capacite: capacites[rang] })),
|
||||
})),
|
||||
);
|
||||
assert.equal(egalite.evenement.siegesParDefaut, 9);
|
||||
assert.deepEqual(surcharges(egalite), [[1, 6], [3, 6]]);
|
||||
});
|
||||
|
||||
test("grandes : chaque animateur réservé au siège 1 de sa table pour tous les tours, ce siège titré « animation », le titre pressenti porté par lui seul ; petites : ni réservation ni titre", () => {
|
||||
for (const cle of ['grande', 'grande-sans-exception']) {
|
||||
const entree = entreeDe(cle);
|
||||
const animateurs = entree.construire().reservations;
|
||||
const charge = chargeDeDemonstration(entree);
|
||||
assert.equal(animateurs.length, 33, cle);
|
||||
assert.deepEqual(
|
||||
[...charge.reservations].sort(parTable),
|
||||
animateurs
|
||||
.map(({ participant, table }) => ({ participant, table, siege: 1, portee: 'tous', tour: null }))
|
||||
.sort(parTable),
|
||||
cle,
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...charge.titres].sort(parTable),
|
||||
charge.tables.map(({ id }) => ({ table: id, siege: 1, libelle: 'animation' })),
|
||||
cle,
|
||||
);
|
||||
assert.deepEqual(
|
||||
charge.participants
|
||||
.filter(({ titrePressenti }) => titrePressenti !== null)
|
||||
.map(({ id, titrePressenti }) => [id, titrePressenti]),
|
||||
animateurs.map(({ participant }) => [participant, 'animation']).sort((a, b) => a[0] - b[0]),
|
||||
cle,
|
||||
);
|
||||
}
|
||||
for (const cle of ['petite', 'petite-conflit']) {
|
||||
const charge = chargeDeDemonstration(entreeDe(cle));
|
||||
assert.deepEqual([charge.reservations, charge.titres], [[], []], cle);
|
||||
assert.ok(charge.participants.every(({ titrePressenti }) => titrePressenti === null), cle);
|
||||
}
|
||||
});
|
||||
|
||||
test("ni proposition ni retenu, personne d'exclu, ni courriel ni notes ; les compteurs dépassent chaque identifiant", () => {
|
||||
for (const entree of CATALOGUE) {
|
||||
const charge = chargeDeDemonstration(entree);
|
||||
assert.deepEqual([charge.propositions, charge.retenu], [[], null], entree.cle);
|
||||
assert.ok(
|
||||
charge.participants.every(({ exclu, courriel, notes }) => exclu === false && courriel === null && notes === null),
|
||||
entree.cle,
|
||||
);
|
||||
const plusGrand = (liste) => Math.max(...liste.map(({ id }) => id));
|
||||
assert.deepEqual(
|
||||
charge.prochainsIds,
|
||||
{ participant: plusGrand(charge.participants) + 1, table: plusGrand(charge.tables) + 1, proposition: 1 },
|
||||
entree.cle,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test("refuse une configuration qu'il ne sait pas écrire : deux réservations à une table, une réservation d'un seul tour", () => {
|
||||
const avecReservations = (reservations) =>
|
||||
entreeRetouchee((configuration) => ({ ...configuration, reservations }));
|
||||
assert.throws(
|
||||
() =>
|
||||
chargeDeDemonstration(
|
||||
avecReservations([
|
||||
{ participant: 1, table: 2, portee: 'tous' },
|
||||
{ participant: 5, table: 2, portee: 'tous' },
|
||||
]),
|
||||
),
|
||||
/essai.*table 2/,
|
||||
);
|
||||
assert.throws(
|
||||
() => chargeDeDemonstration(avecReservations([{ participant: 1, table: 3, portee: 'tour', tour: 2 }])),
|
||||
/essai.*table 3/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("engendrer_livrables : le fichier d'état écrit (§ 8.8, § 18.6)", () => {
|
||||
test('révision 1, la version de la construction qui écrit, la forme canonique ; UTF-8 sans marque, des LF seules, une fin de ligne finale', () => {
|
||||
for (const entree of CATALOGUE) {
|
||||
const texte = texteDeDemonstration(entree);
|
||||
const lu = analyser(texte);
|
||||
assert.deepEqual(
|
||||
[lu.entete.format, lu.entete.produitVersion, lu.entete.revision],
|
||||
[1, VERSION.affichee, 1],
|
||||
entree.cle,
|
||||
);
|
||||
assert.equal(serialiser(lu.charge, lu.entete), texte, entree.cle);
|
||||
assert.ok(!texte.startsWith(MARQUE) && !texte.includes('\r'), entree.cle);
|
||||
assert.ok(texte.endsWith('}\n') && !texte.endsWith('\n\n'), entree.cle);
|
||||
}
|
||||
});
|
||||
|
||||
test("une autre construction n'écrit qu'un autre en-tête : la charge est la même", () => {
|
||||
const voisine = versionVoisine(VERSION.affichee);
|
||||
for (const entree of CATALOGUE) {
|
||||
const ici = analyser(texteDeDemonstration(entree));
|
||||
const ailleurs = analyser(texteDeDemonstration(entree, { produitVersion: voisine }));
|
||||
assert.equal(ailleurs.entete.produitVersion, voisine, entree.cle);
|
||||
assert.deepEqual(ailleurs.charge, ici.charge, entree.cle);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('engendrer_livrables : la variante windows-1252 (§ 10.3)', () => {
|
||||
test('enWindows1252 : un octet par caractère, les signes propres à windows-1252 compris', () => {
|
||||
// Octets de la table publiée de windows-1252.
|
||||
assert.deepEqual(
|
||||
[...enWindows1252('aé€\u{2019}œŸ\u{A0}ÿ')],
|
||||
[0x61, 0xe9, 0x80, 0x92, 0x9c, 0x9f, 0xa0, 0xff],
|
||||
);
|
||||
const decodeur = new TextDecoder('windows-1252');
|
||||
for (let octet = 0; octet < 256; octet += 1) {
|
||||
assert.deepEqual([...enWindows1252(decodeur.decode(Uint8Array.of(octet)))], [octet], `octet ${octet}`);
|
||||
}
|
||||
});
|
||||
|
||||
test('enWindows1252 refuse un caractère que windows-1252 ne porte pas, et nomme son point de code et son rang', () => {
|
||||
assert.throws(
|
||||
() => enWindows1252('noté ✓'),
|
||||
(erreur) => erreur instanceof RangeError && /U\+2713/.test(erreur.message) && /rang 5/.test(erreur.message),
|
||||
);
|
||||
assert.throws(() => enWindows1252('\u{1D11E}'), /U\+1D11E/);
|
||||
});
|
||||
|
||||
test('varianteWindows1252 : les mêmes enregistrements, séparés par des virgules, en windows-1252 sans marque', () => {
|
||||
const source = [
|
||||
'Nom;Prénom;Notes',
|
||||
'Lanterneau;Basile;"chœur; l\u{2019}Anse, nord"',
|
||||
'Coudraie;Apolline;"deux\nlignes"',
|
||||
'Brunœil;;sans prénom',
|
||||
];
|
||||
const octets = new TextEncoder().encode(`${MARQUE}${source.join('\r\n')}\r\n`);
|
||||
const variante = varianteWindows1252(octets);
|
||||
assert.equal(
|
||||
new TextDecoder('windows-1252').decode(variante),
|
||||
[
|
||||
'Nom,Prénom,Notes',
|
||||
'Lanterneau,Basile,"chœur; l\u{2019}Anse, nord"',
|
||||
'Coudraie,Apolline,"deux\nlignes"',
|
||||
'Brunœil,,sans prénom',
|
||||
'',
|
||||
].join('\r\n'),
|
||||
);
|
||||
assert.deepEqual([...variante.subarray(0, 3)], [...enWindows1252('Nom')]);
|
||||
assert.deepEqual(
|
||||
decouper(new TextDecoder('windows-1252').decode(variante), ',').enregistrements,
|
||||
decouper(new TextDecoder().decode(octets), ';').enregistrements,
|
||||
);
|
||||
});
|
||||
|
||||
test('varianteWindows1252 refuse un fichier dont un guillemet reste ouvert', () => {
|
||||
const octets = new TextEncoder().encode(`${MARQUE}Nom;Notes\r\nLanterneau;"sans fin\r\n`);
|
||||
assert.throws(() => varianteWindows1252(octets), /guillemet/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('engendrer_livrables : les livrables (§ 10.3, § 15.5)', () => {
|
||||
test("les sept fichiers, dans l'ordre ; les CSV de démonstration exportent les fichiers d'état écrits, la variante dérive des cas limites", () => {
|
||||
const casLimites = readFileSync(CAS_LIMITES);
|
||||
const fichiers = livrables({ casLimites });
|
||||
assert.deepEqual(
|
||||
fichiers.map(({ chemin }) => chemin),
|
||||
LIVRABLES,
|
||||
);
|
||||
const octetsDe = new Map(fichiers.map(({ chemin, octets }) => [chemin, octets]));
|
||||
for (const entree of CATALOGUE) {
|
||||
assert.equal(
|
||||
new TextDecoder().decode(octetsDe.get(`src/demo/livrees/${entree.cle}.gtt.json`)),
|
||||
texteDeDemonstration(entree),
|
||||
entree.cle,
|
||||
);
|
||||
}
|
||||
for (const cle of ['petite', 'grande']) {
|
||||
const etat = new TextDecoder().decode(octetsDe.get(`src/demo/livrees/${cle}.gtt.json`));
|
||||
assert.deepEqual(
|
||||
octetsDe.get(`exemples/participants_demo_${cle}.csv`),
|
||||
exporterParticipants(analyser(etat).charge.participants),
|
||||
cle,
|
||||
);
|
||||
}
|
||||
assert.deepEqual(octetsDe.get('exemples/participants_cas_limites_cp1252.csv'), varianteWindows1252(casLimites));
|
||||
});
|
||||
});
|
||||
|
||||
describe('engendrer_livrables : la ligne de commande', () => {
|
||||
test('écrit les sept livrables sous le dossier donné, relatif au répertoire courant, et rien ailleurs, au premier lancement comme au suivant', () =>
|
||||
avecProjetCopie((dossier, lancerCopie) => {
|
||||
// Le dossier entier, copie du projet comprise, est photographié avant et
|
||||
// après chaque lancement : seuls les sept livrables, sous sortie/, y
|
||||
// apparaissent. Le CSV des cas limites écrit à la main, tout autre
|
||||
// fichier de la copie et le répertoire courant doivent y rester tels
|
||||
// quels.
|
||||
const sept = LIVRABLES.map((chemin) => `sortie/${chemin}`).sort();
|
||||
const avant = instantane(dossier);
|
||||
assert.ok(avant.has('projet/exemples/participants_cas_limites.csv'), 'la copie porte le CSV des cas limites');
|
||||
const premier = lancerCopie('sortie');
|
||||
assert.equal(premier.status, 0, premier.stderr);
|
||||
const apres = instantane(dossier);
|
||||
assert.deepEqual(differences(avant, apres), sept, "le premier lancement n'écrit pas exactement les sept livrables");
|
||||
const attendus = livrables({ casLimites: readFileSync(CAS_LIMITES) });
|
||||
for (const { chemin, octets } of attendus) {
|
||||
assert.deepEqual(new Uint8Array(readFileSync(join(dossier, 'sortie', chemin))), octets, chemin);
|
||||
assert.match(premier.stdout, new RegExp(`^ *créé +${echapper(chemin)} `, 'm'), chemin);
|
||||
}
|
||||
// Relancé, le script ne touche rien hors des sept livrables, qu'il
|
||||
// réécrit sans en changer un octet : leurs dates peuvent avancer, leurs
|
||||
// octets non.
|
||||
const second = lancerCopie('sortie');
|
||||
assert.equal(second.status, 0, second.stderr);
|
||||
assert.deepEqual(
|
||||
differences(apres, instantane(dossier)).filter((chemin) => !sept.includes(chemin)),
|
||||
[],
|
||||
'le lancement suivant écrit autre chose que les sept livrables',
|
||||
);
|
||||
for (const { chemin, octets } of attendus) {
|
||||
assert.deepEqual(new Uint8Array(readFileSync(join(dossier, 'sortie', chemin))), octets, chemin);
|
||||
}
|
||||
}));
|
||||
|
||||
test('relancé, il réécrit les mêmes octets, et le dit', () =>
|
||||
avecDossier((dossier) => {
|
||||
const premier = lancer(dossier, '.');
|
||||
assert.equal(premier.status, 0, premier.stderr);
|
||||
const octets = LIVRABLES.map((chemin) => readFileSync(join(dossier, chemin)));
|
||||
const second = lancer(dossier, dossier);
|
||||
assert.equal(second.status, 0, second.stderr);
|
||||
LIVRABLES.forEach((chemin, rang) => {
|
||||
assert.deepEqual(readFileSync(join(dossier, chemin)), octets[rang], chemin);
|
||||
assert.match(second.stdout, new RegExp(`^ *inchangé +${echapper(chemin)} `, 'm'), chemin);
|
||||
});
|
||||
}));
|
||||
|
||||
test("une destination qui ne s'écrit pas sort en 1 en nommant la cause", () =>
|
||||
avecDossier((dossier) => {
|
||||
writeFileSync(join(dossier, 'occupe'), 'un fichier, pas un dossier\n');
|
||||
const resultat = lancer(dossier, 'occupe');
|
||||
assert.equal(resultat.status, 1, resultat.stdout);
|
||||
assert.match(resultat.stderr, /^Livrables : .*occupe/m);
|
||||
assert.deepEqual(fichiersSous(dossier), ['occupe']);
|
||||
}));
|
||||
|
||||
test("un argument de trop, ou une option, sort en 2 en rappelant l'usage, sans rien écrire", () =>
|
||||
avecDossier((dossier) => {
|
||||
for (const arguments_ of [['a', 'b'], ['--aide']]) {
|
||||
const resultat = lancer(dossier, ...arguments_);
|
||||
assert.equal(resultat.status, 2, arguments_.join(' '));
|
||||
assert.match(resultat.stderr, /usage : node scripts\/engendrer_livrables\.js \[destination\]/);
|
||||
}
|
||||
assert.deepEqual(fichiersSous(dossier), []);
|
||||
}));
|
||||
});
|
||||
|
|
@ -299,7 +299,14 @@ const LIGNES_OBLIGATOIRES = [
|
|||
// contrôlent ;
|
||||
// - les documents (*.md), dont le point 7 contrôle les noms cités ;
|
||||
// - les épreuves de la dérivation, qui citent des versions par nécessité ;
|
||||
// - les données d'épreuve, sous test/fixtures/.
|
||||
// - les données d'épreuve, sous test/fixtures/ ;
|
||||
// - les démonstrations livrées, sous src/demo/livrees/, données engendrées
|
||||
// dont l'en-tête porte la version de la construction qui les a écrites
|
||||
// (§ 18.5, § 18.6) : une livraison suivante ne les réécrit pas (§ 15.5,
|
||||
// point 5).
|
||||
// Un répertoire s'exclut par son chemin exact, et rien n'est lu sous lui ;
|
||||
// un fichier dont le nom commence comme le sien, src/demo/livrees.js, reste
|
||||
// balayé.
|
||||
// package.json reste balayé : la forme technique y est à sa place, la forme
|
||||
// affichée non.
|
||||
const ARBRES_BALAYES = ['src', 'scripts', 'electron', 'test'];
|
||||
|
|
@ -310,7 +317,7 @@ const FICHIERS_EXCLUS = new Set([
|
|||
'CHANGELOG.md',
|
||||
'scripts/version.test.js',
|
||||
]);
|
||||
const REPERTOIRES_EXCLUS = new Set(['test/fixtures']);
|
||||
const REPERTOIRES_EXCLUS = new Set(['test/fixtures', 'src/demo/livrees']);
|
||||
const DOCUMENT = /\.md$/i;
|
||||
const ARBRE_DES_SOURCES = 'src/';
|
||||
|
||||
|
|
|
|||
|
|
@ -629,12 +629,21 @@ describe('version : contrôle du § 18.5, un défaut à la fois', () => {
|
|||
assert.deepEqual(controler(racine, { dateCivile: DATE }), []);
|
||||
}));
|
||||
|
||||
test('point 6 : seul le répertoire des démonstrations livrées est exclu, pas un fichier qui porte son nom', () =>
|
||||
surArbre((racine) => {
|
||||
ecrire(racine, 'src/demo/livrees.js', "export const v = '2026.10.05.01';\n");
|
||||
ecrire(racine, 'src/demo/livrees/sous/grande.gtt.json', '{"produitVersion":"2026.10.05.01"}\n');
|
||||
const echecs = exigerEchecs(racine, [6], [/^6\. src\/demo\/livrees\.js, ligne 1 : « 2026\.10\.05\.01 », forme affichée/]);
|
||||
assert.equal(echecs.length, 1, echecs.join('\n'));
|
||||
}));
|
||||
|
||||
test('point 6 : données, guides, dépendances et sorties restent hors du périmètre', () =>
|
||||
surArbre((racine) => {
|
||||
const versions = `${VERSION} ${TECHNIQUE} ${TECHNIQUE}.0\n`;
|
||||
for (const chemin of [
|
||||
'scripts/version.test.js',
|
||||
'test/fixtures/demonstration.json',
|
||||
'src/demo/livrees/grande.gtt.json',
|
||||
'GUIDE-USAGE.md',
|
||||
'docs/notes.md',
|
||||
'node_modules/paquet/index.js',
|
||||
|
|
|
|||
|
|
@ -3,8 +3,12 @@
|
|||
|
||||
// Garde des noms forgés (§ 14.9, § 15.6) : aucun patronyme forgé complet, nom
|
||||
// long compris, ni aucune racine d'organisation n'apparaît en mot entier dans
|
||||
// un fichier texte du projet hors de src/demo/noms.js, qui les définit. Les
|
||||
// données d'épreuve de test/fixtures/ sont balayées comme le reste.
|
||||
// un fichier texte du projet hors de src/demo/noms.js, qui les définit, et
|
||||
// des fichiers que scripts/engendrer_livrables.js engendre et qui les portent
|
||||
// par construction : les démonstrations livrées, sous src/demo/livrees/, et
|
||||
// les deux CSV de démonstration, sous exemples/. Le CSV des cas limites,
|
||||
// écrit à la main, sa variante windows-1252 et les données d'épreuve de
|
||||
// test/fixtures/ sont balayés comme le reste.
|
||||
//
|
||||
// Un mot entier est bordé, de chaque côté, par autre chose qu'une lettre, une
|
||||
// marque combinante ou un chiffre : le trait de soulignement et le trait
|
||||
|
|
@ -16,11 +20,13 @@
|
|||
// Chaque fichier de l'arbre exerce une règle : une sonde de test/fixtures/
|
||||
// porte chaque nom cherché ; d'autres placent un nom au-delà du premier Mio
|
||||
// du texte, en NFD, après un octet nul, dans une entrée cachée, dans un
|
||||
// noms.js qui n'est pas celui des listes, ou sous un répertoire exclu ; trois
|
||||
// derniers se lisent malgré un nom voisin d'une exclusion : src/distances/,
|
||||
// distinctes.js et un fichier .git. L'épreuve affirme les fichiers lus, le
|
||||
// compte exact des trouvailles, et que la garde de l'épreuve du projet
|
||||
// désigne exactement les fichiers placés sous un répertoire exclu.
|
||||
// noms.js qui n'est pas celui des listes, dans un fichier engendré, ou sous
|
||||
// un répertoire exclu ; les derniers se lisent malgré un nom voisin d'une
|
||||
// exclusion : src/distances/, distinctes.js, un fichier .git, un module
|
||||
// src/demo/livrees.js, le CSV des cas limites et sa variante. L'épreuve
|
||||
// affirme les fichiers lus, le compte exact des trouvailles, et que la garde
|
||||
// de l'épreuve du projet désigne exactement les fichiers placés sous un
|
||||
// répertoire exclu.
|
||||
//
|
||||
// L'épreuve établit l'absence dans ce projet, rien sur le monde réel : aucune
|
||||
// vérification locale ne prouve qu'un nom forgé ne désigne nulle part une
|
||||
|
|
@ -28,7 +34,7 @@
|
|||
import assert from 'node:assert/strict';
|
||||
import { mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import { dirname, join, relative, sep } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import { NOMS_LONGS, RACINES, patronymes } from './noms.js';
|
||||
|
|
@ -36,6 +42,20 @@ import { NOMS_LONGS, RACINES, patronymes } from './noms.js';
|
|||
const RACINE = fileURLToPath(new URL('../..', import.meta.url));
|
||||
const LISTES = join('src', 'demo', 'noms.js');
|
||||
|
||||
// Fichiers qui portent les noms forgés par construction, que
|
||||
// scripts/engendrer_livrables.js écrit (§ 10.3, § 15.6) : tout fichier sous
|
||||
// src/demo/livrees/, et les deux CSV de démonstration.
|
||||
const DEMONSTRATIONS_LIVREES = join('src', 'demo', 'livrees') + sep;
|
||||
const CSV_DE_DEMONSTRATION = new Set([
|
||||
join('exemples', 'participants_demo_petite.csv'),
|
||||
join('exemples', 'participants_demo_grande.csv'),
|
||||
]);
|
||||
|
||||
// Vrai pour les listes du générateur et pour un fichier que
|
||||
// scripts/engendrer_livrables.js en engendre.
|
||||
const porteLesNomsParConstruction = (chemin) =>
|
||||
chemin === LISTES || chemin.startsWith(DEMONSTRATIONS_LIVREES) || CSV_DE_DEMONSTRATION.has(chemin);
|
||||
|
||||
// Répertoires hors balayage, à toute profondeur : .git, les dépendances, les
|
||||
// caches des outils, et les sorties de construction, qui portent une copie
|
||||
// des listes mises en paquet (www, dist et chaque dist-…, plateformes de
|
||||
|
|
@ -87,17 +107,18 @@ function motifDes(noms) {
|
|||
// Occurrences du motif dans un texte, normalisé en NFC.
|
||||
const occurrences = (motif, texte) => [...texte.normalize('NFC').matchAll(motif)].map(([mot]) => mot);
|
||||
|
||||
// Balaie l'arbre de racine : chaque fichier texte hors des répertoires exclus
|
||||
// et des listes du générateur se lit en entier, décodé en UTF-8, et chaque
|
||||
// nom cherché s'y relève. Rend les chemins examinés, relatifs à racine et
|
||||
// triés, et les trouvailles { chemin, mot }, fichier après fichier, dans
|
||||
// l'ordre du texte.
|
||||
// Balaie l'arbre de racine : chaque fichier texte hors des répertoires
|
||||
// exclus, des listes du générateur et des fichiers que
|
||||
// scripts/engendrer_livrables.js en engendre se lit en entier, décodé en
|
||||
// UTF-8, et chaque nom cherché s'y relève. Rend les chemins examinés,
|
||||
// relatifs à racine et triés, et les trouvailles { chemin, mot }, fichier
|
||||
// après fichier, dans l'ordre du texte.
|
||||
function balayer(racine) {
|
||||
const motif = motifDes(nomsCherches());
|
||||
const examines = [];
|
||||
const trouvailles = [];
|
||||
for (const chemin of fichiers(racine)) {
|
||||
if (chemin === LISTES) continue;
|
||||
if (porteLesNomsParConstruction(chemin)) continue;
|
||||
const octets = readFileSync(join(racine, chemin));
|
||||
if (estBinaire(octets)) continue;
|
||||
examines.push(chemin);
|
||||
|
|
@ -178,12 +199,22 @@ describe('noms forgés : balayage du projet (§ 15.6)', () => {
|
|||
[join('src', 'distances', 'a.js'), patronyme, [patronyme]],
|
||||
[join('src', 'distinctes.js'), 'paires distinctes\n', []],
|
||||
[join('sous', '.git'), 'gitdir: ../.git/worktrees/sous\n', []],
|
||||
// Les voisins des fichiers engendrés se lisent : un module dont le nom
|
||||
// commence comme leur répertoire, le CSV des cas limites, écrit à la
|
||||
// main, et sa variante windows-1252, que le script dérive de lui.
|
||||
[join('src', 'demo', 'livrees.js'), patronyme, [patronyme]],
|
||||
[join('exemples', 'participants_cas_limites.csv'), patronyme, [patronyme]],
|
||||
[join('exemples', 'participants_cas_limites_cp1252.csv'), patronyme, [patronyme]],
|
||||
];
|
||||
// [chemin, contenu] : les fichiers que le balayage saute — un octet nul
|
||||
// dans les 8000 premiers, les listes du générateur.
|
||||
// dans les 8000 premiers, les listes du générateur, les fichiers que le
|
||||
// script en engendre.
|
||||
const sautes = [
|
||||
['nul-dedans.dat', nulA(7999, patronyme)],
|
||||
[LISTES, patronyme],
|
||||
[join('src', 'demo', 'livrees', 'grande.gtt.json'), patronyme],
|
||||
[join('exemples', 'participants_demo_petite.csv'), patronyme],
|
||||
[join('exemples', 'participants_demo_grande.csv'), patronyme],
|
||||
];
|
||||
// Un fichier sous chaque répertoire exclu, à la racine et plus profond ;
|
||||
// chacun porte un nom forgé.
|
||||
|
|
@ -224,7 +255,7 @@ describe('noms forgés : balayage du projet (§ 15.6)', () => {
|
|||
assert.deepEqual(arbre.map(([chemin]) => chemin).filter(sousUnRepertoireExclu), exclus);
|
||||
});
|
||||
|
||||
test('aucun patronyme forgé ni aucune racine hors de src/demo/noms.js', () => {
|
||||
test('aucun patronyme forgé ni aucune racine hors de src/demo/noms.js et des fichiers engendrés', () => {
|
||||
assert.ok(nomsCherches().length > 0, 'aucun nom forgé à chercher');
|
||||
const { examines, trouvailles } = balayer(RACINE);
|
||||
assert.ok(examines.length > 0, 'balayage vide');
|
||||
|
|
|
|||
57
src/demo/livrees.js
Normal file
57
src/demo/livrees.js
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Les démonstrations livrées (§ 15, § 15.5) : le texte du fichier d'état de
|
||||
// chaque démonstration du catalogue, tel que scripts/engendrer_livrables.js
|
||||
// l'a écrit sous livrees/. Le fichier livré est la source de vérité (§ 15.5,
|
||||
// point 5) : montrer une démonstration lit ce fichier, sans rejouer le
|
||||
// générateur. Charger une démonstration crée un événement neuf depuis ce
|
||||
// texte (§ 15) ; le texte, une chaîne, ne se modifie pas, et la
|
||||
// démonstration d'origine non plus.
|
||||
//
|
||||
// Chaque fichier s'importe en module JSON, syntaxe du langage que Node et
|
||||
// Vite lisent l'un comme l'autre : le module se charge sous Node seul, ce
|
||||
// qu'exige le repli « node --test » du § 14.8, et un fichier absent casse
|
||||
// la construction. Un module JSON rend le document, non ses octets : le
|
||||
// sérialiseur canonique le réécrit sous son propre en-tête. Chaque fichier
|
||||
// livré est en forme canonique, UTF-8 sans marque, fin de ligne LF ; le
|
||||
// texte rendu est donc le sien, octet pour octet, ce que livrees.test.js
|
||||
// exige sous Vitest comme sous Node seul.
|
||||
//
|
||||
// Chaque texte décrit l'événement demo:<clé>, à la révision 1, en brouillon,
|
||||
// sans proposition.
|
||||
import { serialiser } from '../stockage/canonique.js';
|
||||
import grande from './livrees/grande.gtt.json' with { type: 'json' };
|
||||
import grandeSansException from './livrees/grande-sans-exception.gtt.json' with { type: 'json' };
|
||||
import petite from './livrees/petite.gtt.json' with { type: 'json' };
|
||||
import petiteConflit from './livrees/petite-conflit.gtt.json' with { type: 'json' };
|
||||
|
||||
// Texte canonique d'un fichier d'état lu en module JSON : sa charge, sous la
|
||||
// révision et la version de son en-tête ; format et comptes se recalculent.
|
||||
const texteDe = ({ entete, charge }) => serialiser(charge, entete);
|
||||
|
||||
/**
|
||||
* Les quatre démonstrations livrées, dans l'ordre du catalogue
|
||||
* (catalogue.js) : { cle, texte }, figées ; cle est celle de l'entrée du
|
||||
* catalogue, texte le fichier d'état livré.
|
||||
* @type {ReadonlyArray<Readonly<{cle: string, texte: string}>>}
|
||||
*/
|
||||
export const LIVREES = Object.freeze([
|
||||
Object.freeze({ cle: 'grande', texte: texteDe(grande) }),
|
||||
Object.freeze({ cle: 'grande-sans-exception', texte: texteDe(grandeSansException) }),
|
||||
Object.freeze({ cle: 'petite', texte: texteDe(petite) }),
|
||||
Object.freeze({ cle: 'petite-conflit', texte: texteDe(petiteConflit) }),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Texte livré de la démonstration de clé donnée.
|
||||
*
|
||||
* @param {string} cle la clé d'une entrée du catalogue
|
||||
* @returns {string}
|
||||
* @throws {RangeError} pour une clé qu'aucune démonstration livrée ne porte
|
||||
*/
|
||||
export function texteLivre(cle) {
|
||||
const livree = LIVREES.find((candidate) => candidate.cle === cle);
|
||||
if (livree === undefined) throw new RangeError(`aucune démonstration livrée de clé ${JSON.stringify(cle)}`);
|
||||
return livree.texte;
|
||||
}
|
||||
166
src/demo/livrees.long.test.js
Normal file
166
src/demo/livrees.long.test.js
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Épreuves longues des démonstrations livrées (§ 14.4) : l'empreinte de la
|
||||
// charge de chaque fichier livré contre celle que rejoue le script (§ 15.5,
|
||||
// point 5), le profil d'appartenances lu dans le fichier (§ 15.2), et
|
||||
// l'aller-retour sur chacun, grande démonstration comprise (§ 8.9). Le
|
||||
// fichier livré est la source de vérité : un écart d'empreinte se nomme par
|
||||
// les chemins de la charge qui diffèrent, et se résout en engendrant de
|
||||
// nouveau, geste conscient, jamais en corrigeant l'épreuve.
|
||||
import assert from 'node:assert/strict';
|
||||
import { empreinte, texteDeDemonstration } from '../../scripts/engendrer_livrables.js';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import { versionVoisine } from '../../test/version_voisine.js';
|
||||
import { serialiser } from '../stockage/canonique.js';
|
||||
import { difference } from '../stockage/correctifs.js';
|
||||
import { analyser } from '../stockage/document.js';
|
||||
import { examiner } from '../stockage/placements.js';
|
||||
import { VERSION } from '../version.genere.js';
|
||||
import { CATALOGUE } from './catalogue.js';
|
||||
import { LIVREES, texteLivre } from './livrees.js';
|
||||
|
||||
const EMPREINTE = /^[0-9a-f]{64}$/;
|
||||
|
||||
// Chemin d'une opération de correctif, lisible : les clés jointes par des
|
||||
// points, un rang entre crochets, un enregistrement par [id=n].
|
||||
const cheminLisible = (chemin) =>
|
||||
chemin
|
||||
.map((etape) => {
|
||||
if (typeof etape === 'string') return `.${etape}`;
|
||||
return typeof etape === 'number' ? `[${etape}]` : `[id=${etape.id}]`;
|
||||
})
|
||||
.join('')
|
||||
.replace(/^\./, '');
|
||||
|
||||
// Affirme que la charge rejouée a l'empreinte de la charge livrée ; à
|
||||
// l'échec, nomme chaque chemin où elles diffèrent, tel que le correctif de
|
||||
// l'une à l'autre le pose ou le retire (§ 15.5, point 5).
|
||||
function memeEmpreinte(livree, rejouee, cle) {
|
||||
if (empreinte(rejouee) === empreinte(livree)) return;
|
||||
const chemins = difference(livree, rejouee).map(({ op, chemin }) => `${op} ${cheminLisible(chemin)}`);
|
||||
assert.fail(
|
||||
`${cle} : la charge que rejoue le script diffère du fichier livré : ${chemins.join(' ; ')}. ` +
|
||||
'Engendrer de nouveau est un geste conscient : node scripts/engendrer_livrables.js',
|
||||
);
|
||||
}
|
||||
|
||||
// Profil d'appartenances d'une liste de participants (§ 15.2) : pour chaque
|
||||
// taille de groupe, le nombre de groupes de cette taille, tailles
|
||||
// croissantes ; les personnes sans appartenance ne forment pas de groupe.
|
||||
function profil(participants) {
|
||||
const tailles = new Map();
|
||||
for (const { appartenance } of participants) {
|
||||
if (appartenance !== null) tailles.set(appartenance, (tailles.get(appartenance) ?? 0) + 1);
|
||||
}
|
||||
const groupesParTaille = new Map();
|
||||
for (const taille of tailles.values()) groupesParTaille.set(taille, (groupesParTaille.get(taille) ?? 0) + 1);
|
||||
return [...groupesParTaille].sort((a, b) => a[0] - b[0]);
|
||||
}
|
||||
|
||||
// Les fichiers livrés, tous ; lève quand il n'y en a aucun (§ 14.2).
|
||||
function balayer() {
|
||||
assert.ok(LIVREES.length > 0, 'aucun fichier livré examiné');
|
||||
return LIVREES;
|
||||
}
|
||||
|
||||
// Aller-retour d'un fichier livré (§ 8.9) : charger — analyser, puis
|
||||
// contrôler les placements, qui n'écartent ni ne signalent rien —,
|
||||
// enregistrer sous une autre construction et à la révision suivante,
|
||||
// recharger. Rend la charge chargée, la charge rechargée, et le texte que
|
||||
// l'enregistrement écrirait sous l'en-tête lu.
|
||||
function allerRetour(texte) {
|
||||
const lu = analyser(texte);
|
||||
const { charge, fautives, derives } = examiner(lu.charge);
|
||||
assert.deepEqual([fautives, derives], [[], []]);
|
||||
const enregistre = serialiser(charge, {
|
||||
revision: lu.entete.revision + 1,
|
||||
produitVersion: versionVoisine(VERSION.affichee),
|
||||
});
|
||||
const relu = analyser(enregistre);
|
||||
assert.notEqual(relu.entete.revision, lu.entete.revision);
|
||||
return { charge, relue: relu.charge, sousLEnteteLu: serialiser(charge, lu.entete) };
|
||||
}
|
||||
|
||||
describe('démonstrations livrées : empreinte (§ 15.5, point 5)', () => {
|
||||
test("pour chaque entrée du catalogue, la charge que rejoue le script a l'empreinte du fichier livré", () => {
|
||||
assert.ok(CATALOGUE.length > 0, 'catalogue vide');
|
||||
for (const entree of CATALOGUE) {
|
||||
const livree = analyser(texteLivre(entree.cle)).charge;
|
||||
const rejouee = analyser(texteDeDemonstration(entree)).charge;
|
||||
memeEmpreinte(livree, rejouee, entree.cle);
|
||||
}
|
||||
});
|
||||
|
||||
test("un changement de produitVersion seul ne change pas l'empreinte : elle porte sur la charge, l'en-tête exclu", () => {
|
||||
const voisine = versionVoisine(VERSION.affichee);
|
||||
for (const entree of CATALOGUE) {
|
||||
const texteIci = texteDeDemonstration(entree);
|
||||
const texteAilleurs = texteDeDemonstration(entree, { produitVersion: voisine });
|
||||
assert.notEqual(texteAilleurs, texteIci, entree.cle);
|
||||
const ailleurs = analyser(texteAilleurs);
|
||||
assert.equal(ailleurs.entete.produitVersion, voisine, entree.cle);
|
||||
assert.equal(empreinte(ailleurs.charge), empreinte(analyser(texteIci).charge), entree.cle);
|
||||
memeEmpreinte(analyser(texteLivre(entree.cle)).charge, ailleurs.charge, entree.cle);
|
||||
}
|
||||
});
|
||||
|
||||
test("l'empreinte : 64 chiffres hexadécimaux, une par charge — les quatre démonstrations en ont quatre", () => {
|
||||
const empreintes = balayer().map(({ texte }) => empreinte(analyser(texte).charge));
|
||||
assert.ok(empreintes.every((valeur) => EMPREINTE.test(valeur)), empreintes.join(' '));
|
||||
assert.equal(new Set(empreintes).size, empreintes.length);
|
||||
});
|
||||
|
||||
test("l'échec nomme les chemins qui diffèrent, jamais seulement deux empreintes", () => {
|
||||
const livree = analyser(texteLivre('petite')).charge;
|
||||
const rejouee = structuredClone(livree);
|
||||
rejouee.participants.find(({ id }) => id === 7).nom = 'Lanterneau';
|
||||
rejouee.tables.find(({ id }) => id === 3).position.x += 1;
|
||||
assert.notEqual(empreinte(rejouee), empreinte(livree));
|
||||
assert.throws(
|
||||
() => memeEmpreinte(livree, rejouee, 'petite'),
|
||||
(erreur) => {
|
||||
assert.match(erreur.message, /^petite : /);
|
||||
assert.match(erreur.message, /poser participants\[id=7\]\.nom ; poser tables\[id=3\]\.position\.x\./);
|
||||
assert.doesNotMatch(erreur.message, /[0-9a-f]{64}/);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("démonstrations livrées : profil d'appartenances (§ 15.2)", () => {
|
||||
test("les tailles d'appartenance lues dans chaque fichier livré égalent celles du générateur", () => {
|
||||
for (const entree of CATALOGUE) {
|
||||
const attendu = profil(analyser(texteLivre(entree.cle)).charge.participants);
|
||||
assert.ok(attendu.length > 0, entree.cle);
|
||||
assert.deepEqual(profil(entree.construire().participants), attendu, entree.cle);
|
||||
}
|
||||
});
|
||||
|
||||
test('le profil compte les groupes par taille, sans les personnes sans appartenance', () => {
|
||||
const personne = (id, appartenance) => ({ id, appartenance });
|
||||
assert.deepEqual(
|
||||
profil([
|
||||
personne(1, 'Chœur de l\u{2019}Anse'),
|
||||
personne(2, 'Cercle des Saules'),
|
||||
personne(3, 'Chœur de l\u{2019}Anse'),
|
||||
personne(4, null),
|
||||
personne(5, 'Société des Hérons'),
|
||||
]),
|
||||
[[1, 2], [2, 1]],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('démonstrations livrées : aller-retour sur chacune, grande comprise (§ 8.9)', () => {
|
||||
test("charger, enregistrer, recharger rend la même charge ; le texte livré est celui qu'écrit le sérialiseur", () => {
|
||||
const examines = balayer();
|
||||
for (const { cle, texte } of examines) {
|
||||
const { charge, relue, sousLEnteteLu } = allerRetour(texte);
|
||||
assert.deepEqual(relue, charge, cle);
|
||||
assert.equal(sousLEnteteLu, texte, cle);
|
||||
}
|
||||
assert.ok(examines.some(({ cle }) => cle === 'grande'), 'la grande démonstration non examinée');
|
||||
});
|
||||
});
|
||||
233
src/demo/livrees.test.js
Normal file
233
src/demo/livrees.test.js
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||||
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
||||
|
||||
// Épreuves des démonstrations livrées et des CSV d'exemple, série surveillée
|
||||
// (§ 8.9, § 10.3, § 14.4, § 14.8, § 15) : un fichier par entrée du
|
||||
// catalogue, que LIVREES porte octet pour octet, sous Vitest comme sous Node
|
||||
// seul ; chacun une démonstration neuve, en forme canonique ;
|
||||
// l'aller-retour sur la petite démonstration ; les deux CSV de
|
||||
// démonstration, l'export exact des fichiers livrés ; les deux CSV des cas
|
||||
// limites, la même lecture en deux encodages, où chaque cas du § 10.3 se
|
||||
// voit, et des noms inventés hors des listes du générateur. L'empreinte, le
|
||||
// profil et l'aller-retour sur la grande sont dans livrees.long.test.js.
|
||||
import assert from 'node:assert/strict';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { varianteWindows1252 } from '../../scripts/engendrer_livrables.js';
|
||||
import { describe, test } from '../../test/lanceur.js';
|
||||
import { versionVoisine } from '../../test/version_voisine.js';
|
||||
import { apercevoir } from '../csv/apercu.js';
|
||||
import { decoder } from '../csv/encodage.js';
|
||||
import { exporterParticipants } from '../csv/export.js';
|
||||
import { serialiser } from '../stockage/canonique.js';
|
||||
import { analyser } from '../stockage/document.js';
|
||||
import { examiner } from '../stockage/placements.js';
|
||||
import { VERSION } from '../version.genere.js';
|
||||
import { CATALOGUE } from './catalogue.js';
|
||||
import { LIVREES, texteLivre } from './livrees.js';
|
||||
import { NOMS_LONGS, PRENOMS, RACINES, TETES, patronymes } from './noms.js';
|
||||
|
||||
const DOSSIER_LIVREES = new URL('./livrees/', import.meta.url);
|
||||
const EXEMPLES = new URL('../../exemples/', import.meta.url);
|
||||
|
||||
// Les démonstrations dont la série surveillée fait l'aller-retour ; la série
|
||||
// longue le fait sur toutes (§ 14.4).
|
||||
const PETITES = new Set(['petite', 'petite-conflit']);
|
||||
|
||||
const MARQUE_UTF8 = [0xef, 0xbb, 0xbf];
|
||||
|
||||
const lireLivre = (cle) => readFileSync(new URL(`${cle}.gtt.json`, DOSSIER_LIVREES));
|
||||
const lireExemple = (nom) => readFileSync(new URL(nom, EXEMPLES));
|
||||
|
||||
// Texte d'un fichier livré, décodé en UTF-8 strict : un octet invalide lève.
|
||||
const texteDuFichier = (cle) => new TextDecoder('utf-8', { fatal: true }).decode(lireLivre(cle));
|
||||
|
||||
// Programme que Node exécute seul, hors de Vite et de Vitest : il importe
|
||||
// livrees.js et écrit LIVREES en JSON sur sa sortie standard.
|
||||
const SOUS_NODE_SEUL = [
|
||||
`const { LIVREES } = await import(${JSON.stringify(new URL('./livrees.js', import.meta.url).href)});`,
|
||||
'process.stdout.write(JSON.stringify(LIVREES));',
|
||||
].join('\n');
|
||||
|
||||
// Les fichiers livrés que retient garder ; lève quand il n'en reste aucun :
|
||||
// un balayage qui n'examine rien ne prouve rien (§ 14.2).
|
||||
function balayer(garder) {
|
||||
const examines = LIVREES.filter(({ cle }) => garder(cle));
|
||||
assert.ok(examines.length > 0, 'aucun fichier livré examiné');
|
||||
return examines;
|
||||
}
|
||||
|
||||
// Aller-retour d'un fichier livré (§ 8.9) : charger — analyser, puis
|
||||
// contrôler les placements, qui n'écartent ni ne signalent rien —,
|
||||
// enregistrer sous une autre construction et à la révision suivante,
|
||||
// recharger. Rend la charge chargée, la charge rechargée, et le texte que
|
||||
// l'enregistrement écrirait sous l'en-tête lu.
|
||||
function allerRetour(texte) {
|
||||
const lu = analyser(texte);
|
||||
const { charge, fautives, derives } = examiner(lu.charge);
|
||||
assert.deepEqual([fautives, derives], [[], []]);
|
||||
const enregistre = serialiser(charge, {
|
||||
revision: lu.entete.revision + 1,
|
||||
produitVersion: versionVoisine(VERSION.affichee),
|
||||
});
|
||||
const relu = analyser(enregistre);
|
||||
assert.notEqual(relu.entete.revision, lu.entete.revision);
|
||||
return { charge, relue: relu.charge, sousLEnteteLu: serialiser(charge, lu.entete) };
|
||||
}
|
||||
|
||||
// Rang du premier octet où deux suites diffèrent, longueur comprise, ou -1.
|
||||
function premierEcart(a, b) {
|
||||
const longueur = Math.min(a.length, b.length);
|
||||
for (let rang = 0; rang < longueur; rang += 1) if (a[rang] !== b[rang]) return rang;
|
||||
return a.length === b.length ? -1 : longueur;
|
||||
}
|
||||
|
||||
// Les deux lectures du CSV des cas limites : celle du fichier écrit à la
|
||||
// main et celle de sa variante windows-1252.
|
||||
function lecturesDesCasLimites() {
|
||||
return [
|
||||
['participants_cas_limites.csv', decoder(lireExemple('participants_cas_limites.csv'))],
|
||||
['participants_cas_limites_cp1252.csv', decoder(lireExemple('participants_cas_limites_cp1252.csv'))],
|
||||
].map(([nom, { texte, encodage }]) => ({ nom, encodage, apercu: apercevoir(texte) }));
|
||||
}
|
||||
|
||||
describe('démonstrations livrées : les fichiers (§ 15.5, point 5)', () => {
|
||||
test("un fichier par entrée du catalogue, nommé <clé>.gtt.json, et rien d'autre : ni journal ni proposition", () => {
|
||||
const fichiers = readdirSync(DOSSIER_LIVREES).sort();
|
||||
assert.ok(fichiers.length > 0, 'aucun fichier livré');
|
||||
assert.deepEqual(fichiers, CATALOGUE.map(({ cle }) => `${cle}.gtt.json`).sort());
|
||||
});
|
||||
|
||||
test("LIVREES porte le texte de chaque fichier, octet pour octet, dans l'ordre du catalogue, figé", () => {
|
||||
assert.deepEqual(
|
||||
LIVREES.map(({ cle }) => cle),
|
||||
CATALOGUE.map(({ cle }) => cle),
|
||||
);
|
||||
for (const livree of LIVREES) {
|
||||
assert.equal(
|
||||
livree.texte,
|
||||
texteDuFichier(livree.cle),
|
||||
`${livree.cle} : LIVREES ne porte pas le texte du fichier ; un fichier hors de la forme canonique s'y réécrit autrement`,
|
||||
);
|
||||
assert.ok(Object.isFrozen(livree), livree.cle);
|
||||
}
|
||||
assert.ok(Object.isFrozen(LIVREES));
|
||||
});
|
||||
|
||||
test('Node seul, sans Vite ni Vitest, importe livrees.js et y lit le texte de chaque fichier : le repli « node --test » le charge (§ 14.8)', () => {
|
||||
const resultat = spawnSync(process.execPath, ['--input-type=module', '--eval', SOUS_NODE_SEUL], {
|
||||
encoding: 'utf8',
|
||||
});
|
||||
assert.equal(resultat.status, 0, resultat.stderr);
|
||||
const lues = JSON.parse(resultat.stdout);
|
||||
assert.deepEqual(
|
||||
lues.map(({ cle }) => cle),
|
||||
CATALOGUE.map(({ cle }) => cle),
|
||||
);
|
||||
for (const { cle, texte } of lues) {
|
||||
assert.equal(texte, texteDuFichier(cle), `${cle} : le texte lu sous Node seul diffère du fichier`);
|
||||
}
|
||||
});
|
||||
|
||||
test("texteLivre rend le texte d'une clé, et refuse une clé qu'aucune démonstration ne porte", () => {
|
||||
for (const { cle, texte } of LIVREES) assert.equal(texteLivre(cle), texte, cle);
|
||||
assert.throws(() => texteLivre('moyenne'), (erreur) => erreur instanceof RangeError && /moyenne/.test(erreur.message));
|
||||
});
|
||||
|
||||
test('chaque fichier : UTF-8 sans marque, des LF seules, une seule fin de ligne finale', () => {
|
||||
for (const { cle } of LIVREES) {
|
||||
const octets = lireLivre(cle);
|
||||
assert.notDeepEqual([...octets.subarray(0, 3)], MARQUE_UTF8, cle);
|
||||
const texte = new TextDecoder('utf-8', { fatal: true }).decode(octets);
|
||||
assert.ok(!texte.includes('\r'), cle);
|
||||
assert.ok(texte.endsWith('}\n') && !texte.endsWith('\n\n'), cle);
|
||||
}
|
||||
});
|
||||
|
||||
test("chaque fichier est une démonstration neuve : identifiant demo:<clé>, nom du catalogue, révision 1, brouillon, sans filiation, ni proposition ni retenu", () => {
|
||||
for (const { cle, texte } of LIVREES) {
|
||||
const { entete, charge } = analyser(texte);
|
||||
assert.deepEqual(
|
||||
[entete.revision, charge.evenement.id, charge.evenement.nom, charge.evenement.etat, charge.evenement.filiation],
|
||||
[1, `demo:${cle}`, CATALOGUE.find((entree) => entree.cle === cle).nom, 'brouillon', null],
|
||||
cle,
|
||||
);
|
||||
assert.deepEqual([charge.propositions, charge.retenu], [[], null], cle);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('démonstrations livrées : aller-retour sur la petite (§ 8.9, § 14.4)', () => {
|
||||
test("charger, enregistrer, recharger rend la même charge ; le texte livré est celui qu'écrit le sérialiseur", () => {
|
||||
for (const { cle, texte } of balayer((cle) => PETITES.has(cle))) {
|
||||
const { charge, relue, sousLEnteteLu } = allerRetour(texte);
|
||||
assert.deepEqual(relue, charge, cle);
|
||||
assert.equal(sousLEnteteLu, texte, cle);
|
||||
}
|
||||
});
|
||||
|
||||
test("le balayage refuse de passer quand il n'examine aucun fichier (§ 14.2)", () => {
|
||||
assert.throws(() => balayer(() => false), /aucun fichier livré examiné/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("CSV d'exemple : les deux de démonstration (§ 10.3)", () => {
|
||||
test("participants_demo_petite.csv et participants_demo_grande.csv égalent, octet pour octet, l'export des fichiers livrés", () => {
|
||||
for (const cle of ['petite', 'grande']) {
|
||||
const lus = lireExemple(`participants_demo_${cle}.csv`);
|
||||
const attendus = exporterParticipants(analyser(texteLivre(cle)).charge.participants);
|
||||
assert.equal(premierEcart(lus, attendus), -1, `participants_demo_${cle}.csv : premier octet qui diffère`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("CSV d'exemple : les cas limites (§ 10.3)", () => {
|
||||
test('UTF-8 avec marque et « ; », puis windows-1252 et « , » : la même lecture, séparateur à part', () => {
|
||||
assert.deepEqual([...lireExemple('participants_cas_limites.csv').subarray(0, 3)], MARQUE_UTF8);
|
||||
const [utf8, windows1252] = lecturesDesCasLimites();
|
||||
assert.deepEqual(
|
||||
[utf8.encodage, utf8.apercu.separateur, windows1252.encodage, windows1252.apercu.separateur],
|
||||
['utf-8-bom', ';', 'windows-1252', ','],
|
||||
);
|
||||
assert.equal(utf8.apercu.refusGlobal, null);
|
||||
assert.deepEqual({ ...windows1252.apercu, separateur: ';' }, utf8.apercu);
|
||||
});
|
||||
|
||||
test('la variante windows-1252 est celle que le script dérive du fichier écrit à la main', () => {
|
||||
const lus = lireExemple('participants_cas_limites_cp1252.csv');
|
||||
const attendus = varianteWindows1252(lireExemple('participants_cas_limites.csv'));
|
||||
assert.equal(premierEcart(lus, attendus), -1, 'participants_cas_limites_cp1252.csv : premier octet qui diffère');
|
||||
});
|
||||
|
||||
test('chaque cas du § 10.3 se voit dans les deux fichiers', () => {
|
||||
for (const { nom, apercu } of lecturesDesCasLimites()) {
|
||||
const champs = apercu.lignes.map((ligne) => ligne.champs);
|
||||
const bruts = apercu.lignes.flatMap(({ brut }) => brut);
|
||||
const cas = [
|
||||
['une colonne non reconnue', apercu.association.nonReconnues.length > 0],
|
||||
['une personne sans appartenance', champs.some(({ appartenance }) => appartenance === null)],
|
||||
['une exclusion', champs.some(({ exclu }) => exclu === true)],
|
||||
// Un champ qui porte le séparateur n'a pu se lire entier que cité.
|
||||
['un champ cité contenant le séparateur', bruts.some((champ) => champ.includes(apercu.separateur))],
|
||||
['un champ cité sur deux lignes', bruts.some((champ) => champ.includes('\n'))],
|
||||
['un doublon', apercu.doublons.length > 0],
|
||||
['une personne sans prénom', champs.some(({ prenom }) => prenom === null)],
|
||||
['deux orthographes fondues', apercu.fusions.some(({ orthographes }) => orthographes.length >= 2)],
|
||||
['une ligne refusée faute de nom', apercu.refusees.some(({ code }) => code === 'NOM_ABSENT')],
|
||||
];
|
||||
assert.deepEqual(cas.filter(([, vu]) => !vu).map(([libelle]) => libelle), [], nom);
|
||||
}
|
||||
});
|
||||
|
||||
test('noms, organisations et courriels inventés : aucun nom des listes du générateur, chaque courriel sous .test', () => {
|
||||
const { texte } = decoder(lireExemple('participants_cas_limites.csv'));
|
||||
// Chaque nom des listes, en mot entier, sans égard à la casse, en NFC.
|
||||
const noms = [...PRENOMS, ...patronymes(), ...NOMS_LONGS, ...TETES, ...RACINES]
|
||||
.map((nom) => nom.normalize('NFC').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'));
|
||||
const motif = new RegExp(`(?<![\\p{L}\\p{M}\\p{N}])(?:${noms.join('|')})(?![\\p{L}\\p{M}\\p{N}])`, 'giu');
|
||||
assert.deepEqual([...texte.normalize('NFC').matchAll(motif)].map(([mot]) => mot), []);
|
||||
const courriels = texte.match(/[^\s;,"]+@[^\s;,"]+/g) ?? [];
|
||||
assert.ok(courriels.length > 0, 'aucun courriel');
|
||||
assert.deepEqual(courriels.filter((courriel) => !courriel.endsWith('.test')), []);
|
||||
});
|
||||
});
|
||||
377
src/demo/livrees/grande-sans-exception.gtt.json
Normal file
377
src/demo/livrees/grande-sans-exception.gtt.json
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
{
|
||||
"entete": {"format":1,"produitVersion":"2026.10.05.01","revision":1,"comptes":{"participants":260,"tables":33,"reservations":33,"titres":33,"propositions":0,"retenu":0}},
|
||||
"charge": {
|
||||
"evenement": {"id":"demo:grande-sans-exception","nom":"Grande démonstration sans exception","date":null,"siegesParDefaut":8,"tours":4,"unite":"cm","etat":"brouillon","filiation":null},
|
||||
"reglages": {"separerAppartenances":true,"nouveauxVoisins":true,"nouvelleTable":true,"varierAppartenances":true,"attribuerSieges":false,"generation":{"nombre":5,"arret":200000,"historique":1000}},
|
||||
"prochainsIds": {"participant":261,"table":34,"proposition":1},
|
||||
"participants": [
|
||||
{"id":1,"nom":"Mirvault","prenom":"Isabelle","appartenance":"Comptoir Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":2,"nom":"Neskière","prenom":"Youssef","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":3,"nom":"Abrevande","prenom":"Félix","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":4,"nom":"Fralvançon","prenom":"Hugo","appartenance":"Brasserie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":5,"nom":"Fralveux","prenom":"Manon","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":6,"nom":"Luskotte","prenom":"Catherine","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":7,"nom":"WAMMERWOLM-MAWWEMMEL","prenom":"Zoé","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":8,"nom":"Neskichon","prenom":"Louise","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":9,"nom":"Urvesson","prenom":"Catherine","appartenance":"Fabrique Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":10,"nom":"Hulvivet","prenom":"Karim","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":11,"nom":"Selveron","prenom":"Caroline","appartenance":"Ateliers Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":12,"nom":"Korvault","prenom":"Catherine","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":13,"nom":"Fralvotte","prenom":"Caroline","appartenance":"Fonderie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":14,"nom":"Jusvesson","prenom":"Geneviève","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":15,"nom":"Abrevumet","prenom":"Paul","appartenance":"Ateliers Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":16,"nom":"Fralvault","prenom":"Léa","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":17,"nom":"Obrivet","prenom":"Fatima","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":18,"nom":"Voskeval","prenom":"Philippe","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":19,"nom":"Dulveval","prenom":"Zoé","appartenance":"Menuiserie Quimvaux","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":20,"nom":"Korveux","prenom":"Paul","appartenance":"Filature Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":21,"nom":"Luskière","prenom":"Julie","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":22,"nom":"Clovande","prenom":"Émilie","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":23,"nom":"Hulvande","prenom":"Paul","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":24,"nom":"Fralvesson","prenom":"Hugo","appartenance":"Filature Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":25,"nom":"Urvière","prenom":"Isabelle","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":26,"nom":"Rozvotte","prenom":"Thomas","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":27,"nom":"Bralmumet","prenom":"Isabelle","appartenance":"Verrerie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":28,"nom":"Dulvouze","prenom":"Catherine","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":29,"nom":"Voskichon","prenom":"Marc","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":30,"nom":"Mirvivet","prenom":"Simon","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":31,"nom":"Clovault","prenom":"Louise","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":32,"nom":"Voskeux","prenom":"Lucie","appartenance":"Coopérative Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":33,"nom":"Eskignac","prenom":"Amina","appartenance":"Brasserie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":34,"nom":"Korvouze","prenom":"Fatima","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":35,"nom":"Selvoubre","prenom":"Luc","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":36,"nom":"Fralvichon","prenom":"Étienne","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":37,"nom":"Voskivet","prenom":"Philippe","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":38,"nom":"Quilmançon","prenom":"Louise","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":39,"nom":"Luskande","prenom":"Sophie","appartenance":"Fabrique Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":40,"nom":"Abreveron","prenom":"Pierre","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":41,"nom":"Neskeux","prenom":"Philippe","appartenance":"Comptoir Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":42,"nom":"Neskançon","prenom":"Amina","appartenance":"Fabrique Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":43,"nom":"Tolvande","prenom":"Léa","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":44,"nom":"Quilmeron","prenom":"Antoine","appartenance":"Fabrique Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":45,"nom":"Clovumet","prenom":"Guillaume","appartenance":"Verrerie Pravette","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":46,"nom":"Eskeux","prenom":"Michel","appartenance":"Papeterie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":47,"nom":"Hulvault","prenom":"Louise","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":48,"nom":"Hulveron","prenom":"Catherine","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":49,"nom":"Ivrivet","prenom":"François","appartenance":"Menuiserie Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":50,"nom":"Eskumet","prenom":"Samuel","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":51,"nom":"Fralvivet","prenom":"Claire","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":52,"nom":"Mirveux","prenom":"Léa","appartenance":"Menuiserie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":53,"nom":"Gorzesson","prenom":"Karim","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":54,"nom":"Fralvumet","prenom":"Jean","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":55,"nom":"Urvançon","prenom":"Manon","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":56,"nom":"Quilmande","prenom":"Sylvie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":57,"nom":"Gorzichon","prenom":"François","appartenance":"Ateliers Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":58,"nom":"Pruvesson","prenom":"Amina","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":59,"nom":"Quilmière","prenom":"Lucie","appartenance":"Fabrique Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":60,"nom":"Korvesson","prenom":"Fatima","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":61,"nom":"Selvesson","prenom":"Nicolas","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":62,"nom":"Selvivet","prenom":"Louis","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":63,"nom":"Neskesson","prenom":"Louise","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":64,"nom":"Tolvesson","prenom":"Lucie","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":65,"nom":"Quilmesson","prenom":"Olivier","appartenance":"Comptoir Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":66,"nom":"Luskivet","prenom":"Guillaume","appartenance":"Brasserie Lavorneau","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":67,"nom":"Neskault","prenom":"Gabriel","appartenance":"Menuiserie Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":68,"nom":"Urvumet","prenom":"Antoine","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":69,"nom":"Jusvançon","prenom":"Olivier","appartenance":"Brasserie Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":70,"nom":"Dulvançon","prenom":"Simon","appartenance":"Menuiserie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":71,"nom":"Gorzard","prenom":"Chloé","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":72,"nom":"Pruveux","prenom":"Gabriel","appartenance":"Papeterie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":73,"nom":"Clovière","prenom":"Manon","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":74,"nom":"Obrouze","prenom":"François","appartenance":"Tannerie Olbrecq","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":75,"nom":"Tolveval","prenom":"Karim","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":76,"nom":"Dulvumet","prenom":"Geneviève","appartenance":"Menuiserie Quimvaux","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":77,"nom":"Bérêçàuvïlle-Dœûmôntëÿ","prenom":"Hugo","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":78,"nom":"Gorzeron","prenom":"Claire","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":79,"nom":"Luskumet","prenom":"Hélène","appartenance":"Brasserie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":80,"nom":"Fralvande","prenom":"Louis","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":81,"nom":"Selvault","prenom":"Émilie","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":82,"nom":"Neskumet","prenom":"Léa","appartenance":"Verrerie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":83,"nom":"Korvichon","prenom":"Isabelle","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":84,"nom":"Obrande","prenom":"Michel","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":85,"nom":"Luskeval","prenom":"Isabelle","appartenance":"Brasserie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":86,"nom":"Tolvoubre","prenom":"Fatima","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":87,"nom":"Gorzière","prenom":"Zoé","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":88,"nom":"Korvignac","prenom":"Sylvie","appartenance":"Fabrique Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":89,"nom":"Obreval","prenom":"Antoine","appartenance":"Ateliers Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":90,"nom":"Korveron","prenom":"Michel","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":91,"nom":"Tolvumet","prenom":"Camille","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":92,"nom":"Dulvesson","prenom":"Marie","appartenance":"Ateliers Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":93,"nom":"Jusvard","prenom":"Youssef","appartenance":"Tannerie Aubrenc","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":94,"nom":"Tolvignac","prenom":"Guillaume","appartenance":"Menuiserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":95,"nom":"Mirvotte","prenom":"Gabriel","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":96,"nom":"Urvard","prenom":"Marie","appartenance":"Filature Wassorel","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":97,"nom":"Bralmesson","prenom":"Marie","appartenance":"Coopérative Dravenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":98,"nom":"Neskignac","prenom":"Paul","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":99,"nom":"Bralmeux","prenom":"Samuel","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":100,"nom":"Ivrotte","prenom":"Raphaël","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":101,"nom":"Luskouze","prenom":"Marc","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":102,"nom":"Jusvotte","prenom":"Sylvie","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":103,"nom":"Bralmoubre","prenom":"Véronique","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":104,"nom":"Selvard","prenom":"Pierre","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":105,"nom":"Hulveux","prenom":"Marie","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":106,"nom":"Pruvotte","prenom":"Sophie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":107,"nom":"Fralveval","prenom":"Karim","appartenance":"Coopérative Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":108,"nom":"Gorzignac","prenom":"Philippe","appartenance":"Imprimerie Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":109,"nom":"Selvouze","prenom":"Étienne","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":110,"nom":"Hulvichon","prenom":"Louis","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":111,"nom":"Urvoubre","prenom":"Sylvie","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":112,"nom":"Pruveval","prenom":"Pierre","appartenance":"Comptoir Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":113,"nom":"Hulvotte","prenom":"Guillaume","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":114,"nom":"Abrevignac","prenom":"Geneviève","appartenance":"Brasserie Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":115,"nom":"Abrevault","prenom":"Gabriel","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":116,"nom":"Bralmignac","prenom":"Amina","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":117,"nom":"Bralmançon","prenom":"Olivier","appartenance":"Comptoir Lavorneau","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":118,"nom":"Luskoubre","prenom":"Marc","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":119,"nom":"Abrevoubre","prenom":"Geneviève","appartenance":"Menuiserie Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":120,"nom":"Korvoubre","prenom":"Raphaël","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":121,"nom":"Mirvard","prenom":"Pierre","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":122,"nom":"Jusvumet","prenom":"Camille","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":123,"nom":"Cloveval","prenom":"Samuel","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":124,"nom":"Selvière","prenom":"Antoine","appartenance":"Coopérative Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":125,"nom":"Jusvouze","prenom":"Véronique","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":126,"nom":"Neskivet","prenom":"Paul","appartenance":"Comptoir Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":127,"nom":"Dulvande","prenom":"Samuel","appartenance":"Verrerie Pravette","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":128,"nom":"Bralmault","prenom":"Antoine","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":129,"nom":"Abrevière","prenom":"Émilie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":130,"nom":"Selveval","prenom":"Lucie","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":131,"nom":"Tolvichon","prenom":"Olivier","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":132,"nom":"Hulvard","prenom":"Fatima","appartenance":"Ateliers Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":133,"nom":"Ivreux","prenom":"Alexandre","appartenance":"Tannerie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":134,"nom":"Korveval","prenom":"Félix","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":135,"nom":"Urvichon","prenom":"Karim","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":136,"nom":"Dulvichon","prenom":"Émilie","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":137,"nom":"Tolvouze","prenom":"Luc","appartenance":"Fabrique Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":138,"nom":"Jusvivet","prenom":"Claire","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":139,"nom":"Quilmault","prenom":"Amina","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":140,"nom":"Ivrouze","prenom":"François","appartenance":"Fonderie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":141,"nom":"Gorzumet","prenom":"Hélène","appartenance":"Fonderie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":142,"nom":"Ivrault","prenom":"Sophie","appartenance":"Coopérative Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":143,"nom":"Bralmière","prenom":"Louise","appartenance":"Tannerie Quimvaux","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":144,"nom":"Korvande","prenom":"Nicolas","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":145,"nom":"Ivrumet","prenom":"Geneviève","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":146,"nom":"Obresson","prenom":"Geneviève","appartenance":"Coopérative Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":147,"nom":"Obrançon","prenom":"Guillaume","appartenance":"Papeterie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":148,"nom":"Urvignac","prenom":"Fatima","appartenance":"Ateliers Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":149,"nom":"Bralmotte","prenom":"Claire","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":150,"nom":"Hulvignac","prenom":"Félix","appartenance":"Imprimerie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":151,"nom":"Clovotte","prenom":"François","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":152,"nom":"Dulvoubre","prenom":"Philippe","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":153,"nom":"Rozveron","prenom":"Samuel","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":154,"nom":"Hulvoubre","prenom":"Pierre","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":155,"nom":"Urvouze","prenom":"Émilie","appartenance":"Comptoir Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":156,"nom":"Korvançon","prenom":"Félix","appartenance":"Fonderie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":157,"nom":"Neskande","prenom":"Émilie","appartenance":"Coopérative Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":158,"nom":"Mirvumet","prenom":"Chantal","appartenance":"Menuiserie Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":159,"nom":"Korvotte","prenom":"Alexandre","appartenance":"Menuiserie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":160,"nom":"Eskeval","prenom":"Zoé","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":161,"nom":"Neskard","prenom":"Camille","appartenance":"Menuiserie Gravelune","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":162,"nom":"Tolveron","prenom":"Manon","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":163,"nom":"Voskoubre","prenom":"Nathalie","appartenance":"Fonderie Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":164,"nom":"Pruvière","prenom":"Sophie","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":165,"nom":"Obrichon","prenom":"Paul","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":166,"nom":"Abrevançon","prenom":"Fatima","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":167,"nom":"Eskière","prenom":"Nathalie","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":168,"nom":"Ivreron","prenom":"Caroline","appartenance":"Imprimerie Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":169,"nom":"Neskoubre","prenom":"Véronique","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":170,"nom":"Urvivet","prenom":"Anne","appartenance":"Fonderie Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":171,"nom":"Hulvançon","prenom":"Olivier","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":172,"nom":"Clovançon","prenom":"Karim","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":173,"nom":"Quilmotte","prenom":"Zoé","appartenance":"Ateliers Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":174,"nom":"Eskouze","prenom":"Félix","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":175,"nom":"Rozvichon","prenom":"Amina","appartenance":"Verrerie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":176,"nom":"Fralvière","prenom":"Simon","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":177,"nom":"Vandergrolcquesmirtavelle-Ostrovaubelindigues","prenom":"Marc","appartenance":"Tannerie Aubrenc","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":178,"nom":"Hulvière","prenom":"Pierre","appartenance":"Papeterie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":179,"nom":"Jusvault","prenom":"Raphaël","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":180,"nom":"Dulvivet","prenom":"Guillaume","appartenance":"Filature Olbrecq","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":181,"nom":"Fralvoubre","prenom":"Pierre","appartenance":"Imprimerie Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":182,"nom":"Selveux","prenom":"Hélène","appartenance":"Brasserie Lavorneau","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":183,"nom":"Rozvançon","prenom":"Caroline","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":184,"nom":"Korvière","prenom":"Olivier","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":185,"nom":"Ivrichon","prenom":"Nicolas","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":186,"nom":"Korvumet","prenom":"Félix","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":187,"nom":"Voskande","prenom":"Félix","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":188,"nom":"Selvumet","prenom":"Guillaume","appartenance":"Papeterie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":189,"nom":"Urvotte","prenom":"Michel","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":190,"nom":"Eskault","prenom":"Émilie","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":191,"nom":"Bralmeval","prenom":"Hélène","appartenance":"Brasserie Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":192,"nom":"Hulvesson","prenom":"Guillaume","appartenance":"Comptoir Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":193,"nom":"Jusvoubre","prenom":"Caroline","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":194,"nom":"Luskeux","prenom":"Félix","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":195,"nom":"Pruvouze","prenom":"Antoine","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":196,"nom":"Quilmignac","prenom":"Julie","appartenance":"Menuiserie Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":197,"nom":"Tolvançon","prenom":"Étienne","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":198,"nom":"Voskumet","prenom":"Hugo","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":199,"nom":"Quilmumet","prenom":"Manon","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":200,"nom":"Voskouze","prenom":"Marc","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":201,"nom":"Abrevard","prenom":"Samuel","appartenance":"Filature Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":202,"nom":"Luskichon","prenom":"Guillaume","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":203,"nom":"Clovesson","prenom":"Manon","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":204,"nom":"Hulvumet","prenom":"Simon","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":205,"nom":"Voskeron","prenom":"Lucie","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":206,"nom":"Selvignac","prenom":"Thomas","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":207,"nom":"Neskeron","prenom":"Jean","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":208,"nom":"Pruvivet","prenom":"Manon","appartenance":"Menuiserie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":209,"nom":"Clovard","prenom":"Sophie","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":210,"nom":"Gorzeval","prenom":"Étienne","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":211,"nom":"Ivroubre","prenom":"Philippe","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":212,"nom":"Luskault","prenom":"Gabriel","appartenance":"Verrerie Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":213,"nom":"Korvard","prenom":"Émilie","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":214,"nom":"Quilmichon","prenom":"Thomas","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":215,"nom":"Eskançon","prenom":"Alexandre","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":216,"nom":"Luskançon","prenom":"Émilie","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":217,"nom":"Dulvault","prenom":"Marc","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":218,"nom":"Rozveval","prenom":"Hélène","appartenance":"Comptoir Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":219,"nom":"Pruvançon","prenom":"Julie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":220,"nom":"Mirveron","prenom":"Louis","appartenance":"Filature Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":221,"nom":"Eskard","prenom":"François","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":222,"nom":"Bralmouze","prenom":"Hugo","appartenance":"Tannerie Falvigne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":223,"nom":"Ivrançon","prenom":"Louis","appartenance":"Coopérative Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":224,"nom":"Clovivet","prenom":"Karim","appartenance":"Fonderie Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":225,"nom":"Rozvande","prenom":"Léa","appartenance":"Verrerie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":226,"nom":"Hulvouze","prenom":"Amina","appartenance":"Coopérative Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":227,"nom":"Eskesson","prenom":"Étienne","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":228,"nom":"Abreveval","prenom":"Philippe","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":229,"nom":"Quilmeux","prenom":"Émilie","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":230,"nom":"Ivreval","prenom":"Gabriel","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":231,"nom":"Voskotte","prenom":"Simon","appartenance":"Fabrique Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":232,"nom":"Obrignac","prenom":"Chantal","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":233,"nom":"Eskotte","prenom":"Guillaume","appartenance":"Verrerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":234,"nom":"Obrard","prenom":"Léa","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":235,"nom":"Gorzivet","prenom":"Guillaume","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":236,"nom":"Mirvançon","prenom":"Geneviève","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":237,"nom":"Selvotte","prenom":"Julie","appartenance":"Comptoir Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":238,"nom":"Mirvesson","prenom":"Amina","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":239,"nom":"Luskignac","prenom":"Isabelle","appartenance":"Papeterie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":240,"nom":"Obrumet","prenom":"Chloé","appartenance":"Comptoir Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":241,"nom":"Abrevesson","prenom":"Anne","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":242,"nom":"Rozvumet","prenom":"Fatima","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":243,"nom":"Mirvoubre","prenom":"Guillaume","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":244,"nom":"Rozvoubre","prenom":"Véronique","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":245,"nom":"Gorzotte","prenom":"Claire","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":246,"nom":"Ivrière","prenom":"Antoine","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":247,"nom":"Selvichon","prenom":"Isabelle","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":248,"nom":"Urvault","prenom":"Isabelle","appartenance":"Comptoir Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":249,"nom":"Voskesson","prenom":"Julie","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":250,"nom":"Abrevotte","prenom":"Chantal","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":251,"nom":"Neskotte","prenom":"Amina","appartenance":"Fabrique Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":252,"nom":"Voskançon","prenom":"Étienne","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":253,"nom":"Fralveron","prenom":"Zoé","appartenance":"Menuiserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":254,"nom":"Rozvouze","prenom":"Marc","appartenance":"Ateliers Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":255,"nom":"Abreveux","prenom":"Sylvie","appartenance":"Comptoir Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":256,"nom":"Pruvignac","prenom":"Karim","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":257,"nom":"Mirveval","prenom":"Zoé","appartenance":"Fabrique Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":258,"nom":"Fralvignac","prenom":"Philippe","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":259,"nom":"Gorzeux","prenom":"Gabriel","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":260,"nom":"Urveval","prenom":"Jean","appartenance":"Menuiserie Quimvaux","courriel":null,"titrePressenti":null,"notes":null,"exclu":false}
|
||||
],
|
||||
"tables": [
|
||||
{"id":1,"numero":1,"sieges":null,"forme":"ronde","position":{"x":0,"y":0}},
|
||||
{"id":2,"numero":2,"sieges":null,"forme":"ronde","position":{"x":300,"y":0}},
|
||||
{"id":3,"numero":3,"sieges":null,"forme":"ronde","position":{"x":600,"y":0}},
|
||||
{"id":4,"numero":4,"sieges":null,"forme":"ronde","position":{"x":900,"y":0}},
|
||||
{"id":5,"numero":5,"sieges":null,"forme":"ronde","position":{"x":1200,"y":0}},
|
||||
{"id":6,"numero":6,"sieges":null,"forme":"ronde","position":{"x":1500,"y":0}},
|
||||
{"id":7,"numero":7,"sieges":null,"forme":"ronde","position":{"x":0,"y":300}},
|
||||
{"id":8,"numero":8,"sieges":null,"forme":"ronde","position":{"x":300,"y":300}},
|
||||
{"id":9,"numero":9,"sieges":null,"forme":"ronde","position":{"x":600,"y":300}},
|
||||
{"id":10,"numero":10,"sieges":null,"forme":"ronde","position":{"x":900,"y":300}},
|
||||
{"id":11,"numero":11,"sieges":null,"forme":"ronde","position":{"x":1200,"y":300}},
|
||||
{"id":12,"numero":12,"sieges":null,"forme":"ronde","position":{"x":1500,"y":300}},
|
||||
{"id":13,"numero":13,"sieges":null,"forme":"ronde","position":{"x":0,"y":600}},
|
||||
{"id":14,"numero":14,"sieges":null,"forme":"ronde","position":{"x":300,"y":600}},
|
||||
{"id":15,"numero":15,"sieges":null,"forme":"ronde","position":{"x":600,"y":600}},
|
||||
{"id":16,"numero":16,"sieges":null,"forme":"ronde","position":{"x":900,"y":600}},
|
||||
{"id":17,"numero":17,"sieges":null,"forme":"ronde","position":{"x":1200,"y":600}},
|
||||
{"id":18,"numero":18,"sieges":null,"forme":"ronde","position":{"x":1500,"y":600}},
|
||||
{"id":19,"numero":19,"sieges":null,"forme":"ronde","position":{"x":0,"y":900}},
|
||||
{"id":20,"numero":20,"sieges":null,"forme":"ronde","position":{"x":300,"y":900}},
|
||||
{"id":21,"numero":21,"sieges":null,"forme":"ronde","position":{"x":600,"y":900}},
|
||||
{"id":22,"numero":22,"sieges":null,"forme":"ronde","position":{"x":900,"y":900}},
|
||||
{"id":23,"numero":23,"sieges":null,"forme":"ronde","position":{"x":1200,"y":900}},
|
||||
{"id":24,"numero":24,"sieges":null,"forme":"ronde","position":{"x":1500,"y":900}},
|
||||
{"id":25,"numero":25,"sieges":null,"forme":"ronde","position":{"x":0,"y":1200}},
|
||||
{"id":26,"numero":26,"sieges":null,"forme":"ronde","position":{"x":300,"y":1200}},
|
||||
{"id":27,"numero":27,"sieges":null,"forme":"ronde","position":{"x":600,"y":1200}},
|
||||
{"id":28,"numero":28,"sieges":null,"forme":"ronde","position":{"x":900,"y":1200}},
|
||||
{"id":29,"numero":29,"sieges":null,"forme":"ronde","position":{"x":1200,"y":1200}},
|
||||
{"id":30,"numero":30,"sieges":null,"forme":"ronde","position":{"x":1500,"y":1200}},
|
||||
{"id":31,"numero":31,"sieges":null,"forme":"ronde","position":{"x":0,"y":1500}},
|
||||
{"id":32,"numero":32,"sieges":null,"forme":"ronde","position":{"x":300,"y":1500}},
|
||||
{"id":33,"numero":33,"sieges":null,"forme":"ronde","position":{"x":600,"y":1500}}
|
||||
],
|
||||
"reservations": [
|
||||
{"participant":2,"table":17,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":8,"table":22,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":18,"table":6,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":34,"table":3,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":48,"table":15,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":49,"table":32,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":54,"table":26,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":55,"table":1,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":62,"table":25,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":74,"table":31,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":76,"table":30,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":90,"table":8,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":93,"table":21,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":96,"table":14,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":97,"table":27,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":99,"table":33,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":106,"table":12,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":115,"table":24,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":128,"table":4,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":129,"table":29,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":138,"table":20,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":161,"table":16,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":162,"table":5,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":166,"table":13,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":177,"table":10,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":180,"table":18,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":187,"table":7,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":197,"table":11,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":202,"table":2,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":206,"table":28,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":211,"table":19,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":222,"table":23,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":230,"table":9,"siege":1,"portee":"tous","tour":null}
|
||||
],
|
||||
"titres": [
|
||||
{"table":1,"siege":1,"libelle":"animation"},
|
||||
{"table":2,"siege":1,"libelle":"animation"},
|
||||
{"table":3,"siege":1,"libelle":"animation"},
|
||||
{"table":4,"siege":1,"libelle":"animation"},
|
||||
{"table":5,"siege":1,"libelle":"animation"},
|
||||
{"table":6,"siege":1,"libelle":"animation"},
|
||||
{"table":7,"siege":1,"libelle":"animation"},
|
||||
{"table":8,"siege":1,"libelle":"animation"},
|
||||
{"table":9,"siege":1,"libelle":"animation"},
|
||||
{"table":10,"siege":1,"libelle":"animation"},
|
||||
{"table":11,"siege":1,"libelle":"animation"},
|
||||
{"table":12,"siege":1,"libelle":"animation"},
|
||||
{"table":13,"siege":1,"libelle":"animation"},
|
||||
{"table":14,"siege":1,"libelle":"animation"},
|
||||
{"table":15,"siege":1,"libelle":"animation"},
|
||||
{"table":16,"siege":1,"libelle":"animation"},
|
||||
{"table":17,"siege":1,"libelle":"animation"},
|
||||
{"table":18,"siege":1,"libelle":"animation"},
|
||||
{"table":19,"siege":1,"libelle":"animation"},
|
||||
{"table":20,"siege":1,"libelle":"animation"},
|
||||
{"table":21,"siege":1,"libelle":"animation"},
|
||||
{"table":22,"siege":1,"libelle":"animation"},
|
||||
{"table":23,"siege":1,"libelle":"animation"},
|
||||
{"table":24,"siege":1,"libelle":"animation"},
|
||||
{"table":25,"siege":1,"libelle":"animation"},
|
||||
{"table":26,"siege":1,"libelle":"animation"},
|
||||
{"table":27,"siege":1,"libelle":"animation"},
|
||||
{"table":28,"siege":1,"libelle":"animation"},
|
||||
{"table":29,"siege":1,"libelle":"animation"},
|
||||
{"table":30,"siege":1,"libelle":"animation"},
|
||||
{"table":31,"siege":1,"libelle":"animation"},
|
||||
{"table":32,"siege":1,"libelle":"animation"},
|
||||
{"table":33,"siege":1,"libelle":"animation"}
|
||||
],
|
||||
"propositions": [],
|
||||
"retenu": null
|
||||
}
|
||||
}
|
||||
377
src/demo/livrees/grande.gtt.json
Normal file
377
src/demo/livrees/grande.gtt.json
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
{
|
||||
"entete": {"format":1,"produitVersion":"2026.10.05.01","revision":1,"comptes":{"participants":260,"tables":33,"reservations":33,"titres":33,"propositions":0,"retenu":0}},
|
||||
"charge": {
|
||||
"evenement": {"id":"demo:grande","nom":"Grande démonstration","date":null,"siegesParDefaut":8,"tours":4,"unite":"cm","etat":"brouillon","filiation":null},
|
||||
"reglages": {"separerAppartenances":true,"nouveauxVoisins":true,"nouvelleTable":true,"varierAppartenances":true,"attribuerSieges":false,"generation":{"nombre":5,"arret":200000,"historique":1000}},
|
||||
"prochainsIds": {"participant":261,"table":34,"proposition":1},
|
||||
"participants": [
|
||||
{"id":1,"nom":"Mirvault","prenom":"Isabelle","appartenance":"Comptoir Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":2,"nom":"Neskière","prenom":"Youssef","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":3,"nom":"Abrevande","prenom":"Félix","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":4,"nom":"Fralvançon","prenom":"Hugo","appartenance":"Brasserie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":5,"nom":"Fralveux","prenom":"Manon","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":6,"nom":"Luskotte","prenom":"Catherine","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":7,"nom":"WAMMERWOLM-MAWWEMMEL","prenom":"Zoé","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":8,"nom":"Neskichon","prenom":"Louise","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":9,"nom":"Urvesson","prenom":"Catherine","appartenance":"Fabrique Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":10,"nom":"Hulvivet","prenom":"Karim","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":11,"nom":"Selveron","prenom":"Caroline","appartenance":"Ateliers Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":12,"nom":"Korvault","prenom":"Catherine","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":13,"nom":"Fralvotte","prenom":"Caroline","appartenance":"Fonderie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":14,"nom":"Jusvesson","prenom":"Geneviève","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":15,"nom":"Abrevumet","prenom":"Paul","appartenance":"Ateliers Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":16,"nom":"Fralvault","prenom":"Léa","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":17,"nom":"Obrivet","prenom":"Fatima","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":18,"nom":"Voskeval","prenom":"Philippe","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":19,"nom":"Dulveval","prenom":"Zoé","appartenance":"Menuiserie Quimvaux","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":20,"nom":"Korveux","prenom":"Paul","appartenance":"Filature Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":21,"nom":"Luskière","prenom":"Julie","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":22,"nom":"Clovande","prenom":"Émilie","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":23,"nom":"Hulvande","prenom":"Paul","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":24,"nom":"Fralvesson","prenom":"Hugo","appartenance":"Filature Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":25,"nom":"Urvière","prenom":"Isabelle","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":26,"nom":"Rozvotte","prenom":"Thomas","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":27,"nom":"Bralmumet","prenom":"Isabelle","appartenance":"Verrerie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":28,"nom":"Dulvouze","prenom":"Catherine","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":29,"nom":"Voskichon","prenom":"Marc","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":30,"nom":"Mirvivet","prenom":"Simon","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":31,"nom":"Clovault","prenom":"Louise","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":32,"nom":"Voskeux","prenom":"Lucie","appartenance":"Coopérative Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":33,"nom":"Eskignac","prenom":"Amina","appartenance":"Brasserie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":34,"nom":"Korvouze","prenom":"Fatima","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":35,"nom":"Selvoubre","prenom":"Luc","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":36,"nom":"Fralvichon","prenom":"Étienne","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":37,"nom":"Voskivet","prenom":"Philippe","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":38,"nom":"Quilmançon","prenom":"Louise","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":39,"nom":"Luskande","prenom":"Sophie","appartenance":"Fabrique Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":40,"nom":"Abreveron","prenom":"Pierre","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":41,"nom":"Neskeux","prenom":"Philippe","appartenance":"Comptoir Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":42,"nom":"Neskançon","prenom":"Amina","appartenance":"Fabrique Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":43,"nom":"Tolvande","prenom":"Léa","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":44,"nom":"Quilmeron","prenom":"Antoine","appartenance":"Fabrique Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":45,"nom":"Clovumet","prenom":"Guillaume","appartenance":"Verrerie Pravette","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":46,"nom":"Eskeux","prenom":"Michel","appartenance":"Papeterie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":47,"nom":"Hulvault","prenom":"Louise","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":48,"nom":"Hulveron","prenom":"Catherine","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":49,"nom":"Ivrivet","prenom":"François","appartenance":"Menuiserie Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":50,"nom":"Eskumet","prenom":"Samuel","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":51,"nom":"Fralvivet","prenom":"Claire","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":52,"nom":"Mirveux","prenom":"Léa","appartenance":"Menuiserie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":53,"nom":"Gorzesson","prenom":"Karim","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":54,"nom":"Fralvumet","prenom":"Jean","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":55,"nom":"Urvançon","prenom":"Manon","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":56,"nom":"Quilmande","prenom":"Sylvie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":57,"nom":"Gorzichon","prenom":"François","appartenance":"Ateliers Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":58,"nom":"Pruvesson","prenom":"Amina","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":59,"nom":"Quilmière","prenom":"Lucie","appartenance":"Fabrique Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":60,"nom":"Korvesson","prenom":"Fatima","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":61,"nom":"Selvesson","prenom":"Nicolas","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":62,"nom":"Selvivet","prenom":"Louis","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":63,"nom":"Neskesson","prenom":"Louise","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":64,"nom":"Tolvesson","prenom":"Lucie","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":65,"nom":"Quilmesson","prenom":"Olivier","appartenance":"Comptoir Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":66,"nom":"Luskivet","prenom":"Guillaume","appartenance":"Brasserie Lavorneau","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":67,"nom":"Neskault","prenom":"Gabriel","appartenance":"Menuiserie Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":68,"nom":"Urvumet","prenom":"Antoine","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":69,"nom":"Jusvançon","prenom":"Olivier","appartenance":"Brasserie Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":70,"nom":"Dulvançon","prenom":"Simon","appartenance":"Menuiserie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":71,"nom":"Gorzard","prenom":"Chloé","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":72,"nom":"Pruveux","prenom":"Gabriel","appartenance":"Papeterie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":73,"nom":"Clovière","prenom":"Manon","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":74,"nom":"Obrouze","prenom":"François","appartenance":"Tannerie Olbrecq","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":75,"nom":"Tolveval","prenom":"Karim","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":76,"nom":"Dulvumet","prenom":"Geneviève","appartenance":"Menuiserie Quimvaux","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":77,"nom":"Bérêçàuvïlle-Dœûmôntëÿ","prenom":"Hugo","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":78,"nom":"Gorzeron","prenom":"Claire","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":79,"nom":"Luskumet","prenom":"Hélène","appartenance":"Brasserie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":80,"nom":"Fralvande","prenom":"Louis","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":81,"nom":"Selvault","prenom":"Émilie","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":82,"nom":"Neskumet","prenom":"Léa","appartenance":"Verrerie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":83,"nom":"Korvichon","prenom":"Isabelle","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":84,"nom":"Obrande","prenom":"Michel","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":85,"nom":"Luskeval","prenom":"Isabelle","appartenance":"Brasserie Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":86,"nom":"Tolvoubre","prenom":"Fatima","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":87,"nom":"Gorzière","prenom":"Zoé","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":88,"nom":"Korvignac","prenom":"Sylvie","appartenance":"Fabrique Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":89,"nom":"Obreval","prenom":"Antoine","appartenance":"Ateliers Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":90,"nom":"Korveron","prenom":"Michel","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":91,"nom":"Tolvumet","prenom":"Camille","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":92,"nom":"Dulvesson","prenom":"Marie","appartenance":"Ateliers Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":93,"nom":"Jusvard","prenom":"Youssef","appartenance":"Tannerie Aubrenc","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":94,"nom":"Tolvignac","prenom":"Guillaume","appartenance":"Menuiserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":95,"nom":"Mirvotte","prenom":"Gabriel","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":96,"nom":"Urvard","prenom":"Marie","appartenance":"Filature Wassorel","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":97,"nom":"Bralmesson","prenom":"Marie","appartenance":"Coopérative Dravenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":98,"nom":"Neskignac","prenom":"Paul","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":99,"nom":"Bralmeux","prenom":"Samuel","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":100,"nom":"Ivrotte","prenom":"Raphaël","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":101,"nom":"Luskouze","prenom":"Marc","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":102,"nom":"Jusvotte","prenom":"Sylvie","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":103,"nom":"Bralmoubre","prenom":"Véronique","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":104,"nom":"Selvard","prenom":"Pierre","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":105,"nom":"Hulveux","prenom":"Marie","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":106,"nom":"Pruvotte","prenom":"Sophie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":107,"nom":"Fralveval","prenom":"Karim","appartenance":"Coopérative Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":108,"nom":"Gorzignac","prenom":"Philippe","appartenance":"Imprimerie Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":109,"nom":"Selvouze","prenom":"Étienne","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":110,"nom":"Hulvichon","prenom":"Louis","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":111,"nom":"Urvoubre","prenom":"Sylvie","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":112,"nom":"Pruveval","prenom":"Pierre","appartenance":"Comptoir Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":113,"nom":"Hulvotte","prenom":"Guillaume","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":114,"nom":"Abrevignac","prenom":"Geneviève","appartenance":"Brasserie Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":115,"nom":"Abrevault","prenom":"Gabriel","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":116,"nom":"Bralmignac","prenom":"Amina","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":117,"nom":"Bralmançon","prenom":"Olivier","appartenance":"Comptoir Lavorneau","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":118,"nom":"Luskoubre","prenom":"Marc","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":119,"nom":"Abrevoubre","prenom":"Geneviève","appartenance":"Menuiserie Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":120,"nom":"Korvoubre","prenom":"Raphaël","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":121,"nom":"Mirvard","prenom":"Pierre","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":122,"nom":"Jusvumet","prenom":"Camille","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":123,"nom":"Cloveval","prenom":"Samuel","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":124,"nom":"Selvière","prenom":"Antoine","appartenance":"Coopérative Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":125,"nom":"Jusvouze","prenom":"Véronique","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":126,"nom":"Neskivet","prenom":"Paul","appartenance":"Comptoir Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":127,"nom":"Dulvande","prenom":"Samuel","appartenance":"Verrerie Pravette","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":128,"nom":"Bralmault","prenom":"Antoine","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":129,"nom":"Abrevière","prenom":"Émilie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":130,"nom":"Selveval","prenom":"Lucie","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":131,"nom":"Tolvichon","prenom":"Olivier","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":132,"nom":"Hulvard","prenom":"Fatima","appartenance":"Ateliers Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":133,"nom":"Ivreux","prenom":"Alexandre","appartenance":"Tannerie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":134,"nom":"Korveval","prenom":"Félix","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":135,"nom":"Urvichon","prenom":"Karim","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":136,"nom":"Dulvichon","prenom":"Émilie","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":137,"nom":"Tolvouze","prenom":"Luc","appartenance":"Fabrique Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":138,"nom":"Jusvivet","prenom":"Claire","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":139,"nom":"Quilmault","prenom":"Amina","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":140,"nom":"Ivrouze","prenom":"François","appartenance":"Fonderie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":141,"nom":"Gorzumet","prenom":"Hélène","appartenance":"Fonderie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":142,"nom":"Ivrault","prenom":"Sophie","appartenance":"Coopérative Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":143,"nom":"Bralmière","prenom":"Louise","appartenance":"Tannerie Quimvaux","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":144,"nom":"Korvande","prenom":"Nicolas","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":145,"nom":"Ivrumet","prenom":"Geneviève","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":146,"nom":"Obresson","prenom":"Geneviève","appartenance":"Coopérative Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":147,"nom":"Obrançon","prenom":"Guillaume","appartenance":"Papeterie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":148,"nom":"Urvignac","prenom":"Fatima","appartenance":"Ateliers Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":149,"nom":"Bralmotte","prenom":"Claire","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":150,"nom":"Hulvignac","prenom":"Félix","appartenance":"Imprimerie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":151,"nom":"Clovotte","prenom":"François","appartenance":"Coopérative Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":152,"nom":"Dulvoubre","prenom":"Philippe","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":153,"nom":"Rozveron","prenom":"Samuel","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":154,"nom":"Hulvoubre","prenom":"Pierre","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":155,"nom":"Urvouze","prenom":"Émilie","appartenance":"Comptoir Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":156,"nom":"Korvançon","prenom":"Félix","appartenance":"Fonderie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":157,"nom":"Neskande","prenom":"Émilie","appartenance":"Coopérative Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":158,"nom":"Mirvumet","prenom":"Chantal","appartenance":"Menuiserie Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":159,"nom":"Korvotte","prenom":"Alexandre","appartenance":"Menuiserie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":160,"nom":"Eskeval","prenom":"Zoé","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":161,"nom":"Neskard","prenom":"Camille","appartenance":"Menuiserie Gravelune","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":162,"nom":"Tolveron","prenom":"Manon","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":163,"nom":"Voskoubre","prenom":"Nathalie","appartenance":"Fonderie Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":164,"nom":"Pruvière","prenom":"Sophie","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":165,"nom":"Obrichon","prenom":"Paul","appartenance":"Ateliers Rossolde","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":166,"nom":"Abrevançon","prenom":"Fatima","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":167,"nom":"Eskière","prenom":"Nathalie","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":168,"nom":"Ivreron","prenom":"Caroline","appartenance":"Imprimerie Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":169,"nom":"Neskoubre","prenom":"Véronique","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":170,"nom":"Urvivet","prenom":"Anne","appartenance":"Fonderie Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":171,"nom":"Hulvançon","prenom":"Olivier","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":172,"nom":"Clovançon","prenom":"Karim","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":173,"nom":"Quilmotte","prenom":"Zoé","appartenance":"Ateliers Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":174,"nom":"Eskouze","prenom":"Félix","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":175,"nom":"Rozvichon","prenom":"Amina","appartenance":"Verrerie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":176,"nom":"Fralvière","prenom":"Simon","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":177,"nom":"Vandergrolcquesmirtavelle-Ostrovaubelindigues","prenom":"Marc","appartenance":"Tannerie Aubrenc","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":178,"nom":"Hulvière","prenom":"Pierre","appartenance":"Papeterie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":179,"nom":"Jusvault","prenom":"Raphaël","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":180,"nom":"Dulvivet","prenom":"Guillaume","appartenance":"Filature Olbrecq","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":181,"nom":"Fralvoubre","prenom":"Pierre","appartenance":"Imprimerie Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":182,"nom":"Selveux","prenom":"Hélène","appartenance":"Brasserie Lavorneau","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":183,"nom":"Rozvançon","prenom":"Caroline","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":184,"nom":"Korvière","prenom":"Olivier","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":185,"nom":"Ivrichon","prenom":"Nicolas","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":186,"nom":"Korvumet","prenom":"Félix","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":187,"nom":"Voskande","prenom":"Félix","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":188,"nom":"Selvumet","prenom":"Guillaume","appartenance":"Papeterie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":189,"nom":"Urvotte","prenom":"Michel","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":190,"nom":"Eskault","prenom":"Émilie","appartenance":"Coopérative Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":191,"nom":"Bralmeval","prenom":"Hélène","appartenance":"Brasserie Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":192,"nom":"Hulvesson","prenom":"Guillaume","appartenance":"Comptoir Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":193,"nom":"Jusvoubre","prenom":"Caroline","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":194,"nom":"Luskeux","prenom":"Félix","appartenance":"Tannerie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":195,"nom":"Pruvouze","prenom":"Antoine","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":196,"nom":"Quilmignac","prenom":"Julie","appartenance":"Menuiserie Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":197,"nom":"Tolvançon","prenom":"Étienne","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":198,"nom":"Voskumet","prenom":"Hugo","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":199,"nom":"Quilmumet","prenom":"Manon","appartenance":"Fonderie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":200,"nom":"Voskouze","prenom":"Marc","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":201,"nom":"Abrevard","prenom":"Samuel","appartenance":"Filature Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":202,"nom":"Luskichon","prenom":"Guillaume","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":203,"nom":"Clovesson","prenom":"Manon","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":204,"nom":"Hulvumet","prenom":"Simon","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":205,"nom":"Voskeron","prenom":"Lucie","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":206,"nom":"Selvignac","prenom":"Thomas","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":207,"nom":"Neskeron","prenom":"Jean","appartenance":"Filature Zelvaine","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":208,"nom":"Pruvivet","prenom":"Manon","appartenance":"Menuiserie Aubrenc","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":209,"nom":"Clovard","prenom":"Sophie","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":210,"nom":"Gorzeval","prenom":"Étienne","appartenance":"Papeterie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":211,"nom":"Ivroubre","prenom":"Philippe","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":212,"nom":"Luskault","prenom":"Gabriel","appartenance":"Verrerie Gravelune","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":213,"nom":"Korvard","prenom":"Émilie","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":214,"nom":"Quilmichon","prenom":"Thomas","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":215,"nom":"Eskançon","prenom":"Alexandre","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":216,"nom":"Luskançon","prenom":"Émilie","appartenance":"Ateliers Tavrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":217,"nom":"Dulvault","prenom":"Marc","appartenance":"Verrerie Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":218,"nom":"Rozveval","prenom":"Hélène","appartenance":"Comptoir Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":219,"nom":"Pruvançon","prenom":"Julie","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":220,"nom":"Mirveron","prenom":"Louis","appartenance":"Filature Wassorel","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":221,"nom":"Eskard","prenom":"François","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":222,"nom":"Bralmouze","prenom":"Hugo","appartenance":"Tannerie Falvigne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":223,"nom":"Ivrançon","prenom":"Louis","appartenance":"Coopérative Morvalle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":224,"nom":"Clovivet","prenom":"Karim","appartenance":"Fonderie Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":225,"nom":"Rozvande","prenom":"Léa","appartenance":"Verrerie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":226,"nom":"Hulvouze","prenom":"Amina","appartenance":"Coopérative Dravenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":227,"nom":"Eskesson","prenom":"Étienne","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":228,"nom":"Abreveval","prenom":"Philippe","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":229,"nom":"Quilmeux","prenom":"Émilie","appartenance":"Ateliers Falvigne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":230,"nom":"Ivreval","prenom":"Gabriel","appartenance":"Verrerie Vaubrenne","courriel":null,"titrePressenti":"animation","notes":null,"exclu":false},
|
||||
{"id":231,"nom":"Voskotte","prenom":"Simon","appartenance":"Fabrique Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":232,"nom":"Obrignac","prenom":"Chantal","appartenance":"Imprimerie Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":233,"nom":"Eskotte","prenom":"Guillaume","appartenance":"Verrerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":234,"nom":"Obrard","prenom":"Léa","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":235,"nom":"Gorzivet","prenom":"Guillaume","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":236,"nom":"Mirvançon","prenom":"Geneviève","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":237,"nom":"Selvotte","prenom":"Julie","appartenance":"Comptoir Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":238,"nom":"Mirvesson","prenom":"Amina","appartenance":"Brasserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":239,"nom":"Luskignac","prenom":"Isabelle","appartenance":"Papeterie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":240,"nom":"Obrumet","prenom":"Chloé","appartenance":"Comptoir Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":241,"nom":"Abrevesson","prenom":"Anne","appartenance":"Fonderie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":242,"nom":"Rozvumet","prenom":"Fatima","appartenance":"Menuiserie Hostrenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":243,"nom":"Mirvoubre","prenom":"Guillaume","appartenance":"Comptoir Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":244,"nom":"Rozvoubre","prenom":"Véronique","appartenance":"Fonderie Xandrelle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":245,"nom":"Gorzotte","prenom":"Claire","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":246,"nom":"Ivrière","prenom":"Antoine","appartenance":"Fabrique Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":247,"nom":"Selvichon","prenom":"Isabelle","appartenance":"Tannerie Nervanche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":248,"nom":"Urvault","prenom":"Isabelle","appartenance":"Comptoir Yzorne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":249,"nom":"Voskesson","prenom":"Julie","appartenance":"Brasserie Belvarre","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":250,"nom":"Abrevotte","prenom":"Chantal","appartenance":"Ateliers Ivoranche","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":251,"nom":"Neskotte","prenom":"Amina","appartenance":"Fabrique Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":252,"nom":"Voskançon","prenom":"Étienne","appartenance":"Menuiserie Olbrecq","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":253,"nom":"Fralveron","prenom":"Zoé","appartenance":"Menuiserie Sorbagne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":254,"nom":"Rozvouze","prenom":"Marc","appartenance":"Ateliers Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":255,"nom":"Abreveux","prenom":"Sylvie","appartenance":"Comptoir Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":256,"nom":"Pruvignac","prenom":"Karim","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":257,"nom":"Mirveval","prenom":"Zoé","appartenance":"Fabrique Kalvenne","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":258,"nom":"Fralvignac","prenom":"Philippe","appartenance":"Menuiserie Estrolle","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":259,"nom":"Gorzeux","prenom":"Gabriel","appartenance":"Menuiserie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":260,"nom":"Urveval","prenom":"Jean","appartenance":"Menuiserie Quimvaux","courriel":null,"titrePressenti":null,"notes":null,"exclu":false}
|
||||
],
|
||||
"tables": [
|
||||
{"id":1,"numero":1,"sieges":null,"forme":"ronde","position":{"x":0,"y":0}},
|
||||
{"id":2,"numero":2,"sieges":null,"forme":"ronde","position":{"x":300,"y":0}},
|
||||
{"id":3,"numero":3,"sieges":null,"forme":"ronde","position":{"x":600,"y":0}},
|
||||
{"id":4,"numero":4,"sieges":null,"forme":"ronde","position":{"x":900,"y":0}},
|
||||
{"id":5,"numero":5,"sieges":null,"forme":"ronde","position":{"x":1200,"y":0}},
|
||||
{"id":6,"numero":6,"sieges":null,"forme":"ronde","position":{"x":1500,"y":0}},
|
||||
{"id":7,"numero":7,"sieges":null,"forme":"ronde","position":{"x":0,"y":300}},
|
||||
{"id":8,"numero":8,"sieges":null,"forme":"ronde","position":{"x":300,"y":300}},
|
||||
{"id":9,"numero":9,"sieges":null,"forme":"ronde","position":{"x":600,"y":300}},
|
||||
{"id":10,"numero":10,"sieges":null,"forme":"ronde","position":{"x":900,"y":300}},
|
||||
{"id":11,"numero":11,"sieges":null,"forme":"ronde","position":{"x":1200,"y":300}},
|
||||
{"id":12,"numero":12,"sieges":null,"forme":"ronde","position":{"x":1500,"y":300}},
|
||||
{"id":13,"numero":13,"sieges":null,"forme":"ronde","position":{"x":0,"y":600}},
|
||||
{"id":14,"numero":14,"sieges":null,"forme":"ronde","position":{"x":300,"y":600}},
|
||||
{"id":15,"numero":15,"sieges":null,"forme":"ronde","position":{"x":600,"y":600}},
|
||||
{"id":16,"numero":16,"sieges":null,"forme":"ronde","position":{"x":900,"y":600}},
|
||||
{"id":17,"numero":17,"sieges":null,"forme":"ronde","position":{"x":1200,"y":600}},
|
||||
{"id":18,"numero":18,"sieges":null,"forme":"ronde","position":{"x":1500,"y":600}},
|
||||
{"id":19,"numero":19,"sieges":null,"forme":"ronde","position":{"x":0,"y":900}},
|
||||
{"id":20,"numero":20,"sieges":null,"forme":"ronde","position":{"x":300,"y":900}},
|
||||
{"id":21,"numero":21,"sieges":null,"forme":"ronde","position":{"x":600,"y":900}},
|
||||
{"id":22,"numero":22,"sieges":null,"forme":"ronde","position":{"x":900,"y":900}},
|
||||
{"id":23,"numero":23,"sieges":null,"forme":"ronde","position":{"x":1200,"y":900}},
|
||||
{"id":24,"numero":24,"sieges":null,"forme":"ronde","position":{"x":1500,"y":900}},
|
||||
{"id":25,"numero":25,"sieges":null,"forme":"ronde","position":{"x":0,"y":1200}},
|
||||
{"id":26,"numero":26,"sieges":null,"forme":"ronde","position":{"x":300,"y":1200}},
|
||||
{"id":27,"numero":27,"sieges":null,"forme":"ronde","position":{"x":600,"y":1200}},
|
||||
{"id":28,"numero":28,"sieges":null,"forme":"ronde","position":{"x":900,"y":1200}},
|
||||
{"id":29,"numero":29,"sieges":null,"forme":"ronde","position":{"x":1200,"y":1200}},
|
||||
{"id":30,"numero":30,"sieges":7,"forme":"ronde","position":{"x":1500,"y":1200}},
|
||||
{"id":31,"numero":31,"sieges":7,"forme":"ronde","position":{"x":0,"y":1500}},
|
||||
{"id":32,"numero":32,"sieges":7,"forme":"ronde","position":{"x":300,"y":1500}},
|
||||
{"id":33,"numero":33,"sieges":7,"forme":"ronde","position":{"x":600,"y":1500}}
|
||||
],
|
||||
"reservations": [
|
||||
{"participant":2,"table":17,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":8,"table":22,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":18,"table":6,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":34,"table":3,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":48,"table":15,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":49,"table":32,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":54,"table":26,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":55,"table":1,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":62,"table":25,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":74,"table":31,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":76,"table":30,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":90,"table":8,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":93,"table":21,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":96,"table":14,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":97,"table":27,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":99,"table":33,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":106,"table":12,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":115,"table":24,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":128,"table":4,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":129,"table":29,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":138,"table":20,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":161,"table":16,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":162,"table":5,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":166,"table":13,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":177,"table":10,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":180,"table":18,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":187,"table":7,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":197,"table":11,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":202,"table":2,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":206,"table":28,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":211,"table":19,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":222,"table":23,"siege":1,"portee":"tous","tour":null},
|
||||
{"participant":230,"table":9,"siege":1,"portee":"tous","tour":null}
|
||||
],
|
||||
"titres": [
|
||||
{"table":1,"siege":1,"libelle":"animation"},
|
||||
{"table":2,"siege":1,"libelle":"animation"},
|
||||
{"table":3,"siege":1,"libelle":"animation"},
|
||||
{"table":4,"siege":1,"libelle":"animation"},
|
||||
{"table":5,"siege":1,"libelle":"animation"},
|
||||
{"table":6,"siege":1,"libelle":"animation"},
|
||||
{"table":7,"siege":1,"libelle":"animation"},
|
||||
{"table":8,"siege":1,"libelle":"animation"},
|
||||
{"table":9,"siege":1,"libelle":"animation"},
|
||||
{"table":10,"siege":1,"libelle":"animation"},
|
||||
{"table":11,"siege":1,"libelle":"animation"},
|
||||
{"table":12,"siege":1,"libelle":"animation"},
|
||||
{"table":13,"siege":1,"libelle":"animation"},
|
||||
{"table":14,"siege":1,"libelle":"animation"},
|
||||
{"table":15,"siege":1,"libelle":"animation"},
|
||||
{"table":16,"siege":1,"libelle":"animation"},
|
||||
{"table":17,"siege":1,"libelle":"animation"},
|
||||
{"table":18,"siege":1,"libelle":"animation"},
|
||||
{"table":19,"siege":1,"libelle":"animation"},
|
||||
{"table":20,"siege":1,"libelle":"animation"},
|
||||
{"table":21,"siege":1,"libelle":"animation"},
|
||||
{"table":22,"siege":1,"libelle":"animation"},
|
||||
{"table":23,"siege":1,"libelle":"animation"},
|
||||
{"table":24,"siege":1,"libelle":"animation"},
|
||||
{"table":25,"siege":1,"libelle":"animation"},
|
||||
{"table":26,"siege":1,"libelle":"animation"},
|
||||
{"table":27,"siege":1,"libelle":"animation"},
|
||||
{"table":28,"siege":1,"libelle":"animation"},
|
||||
{"table":29,"siege":1,"libelle":"animation"},
|
||||
{"table":30,"siege":1,"libelle":"animation"},
|
||||
{"table":31,"siege":1,"libelle":"animation"},
|
||||
{"table":32,"siege":1,"libelle":"animation"},
|
||||
{"table":33,"siege":1,"libelle":"animation"}
|
||||
],
|
||||
"propositions": [],
|
||||
"retenu": null
|
||||
}
|
||||
}
|
||||
32
src/demo/livrees/petite-conflit.gtt.json
Normal file
32
src/demo/livrees/petite-conflit.gtt.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"entete": {"format":1,"produitVersion":"2026.10.05.01","revision":1,"comptes":{"participants":12,"tables":4,"reservations":0,"titres":0,"propositions":0,"retenu":0}},
|
||||
"charge": {
|
||||
"evenement": {"id":"demo:petite-conflit","nom":"Petite démonstration, conflit inévitable","date":null,"siegesParDefaut":3,"tours":4,"unite":"cm","etat":"brouillon","filiation":null},
|
||||
"reglages": {"separerAppartenances":true,"nouveauxVoisins":true,"nouvelleTable":true,"varierAppartenances":true,"attribuerSieges":false,"generation":{"nombre":5,"arret":200000,"historique":1000}},
|
||||
"prochainsIds": {"participant":13,"table":5,"proposition":1},
|
||||
"participants": [
|
||||
{"id":1,"nom":"Bralmumet","prenom":"Caroline","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":2,"nom":"Neskault","prenom":"Hugo","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":3,"nom":"Ivrotte","prenom":"Youssef","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":4,"nom":"Selvière","prenom":"Louis","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":5,"nom":"Selvançon","prenom":"Youssef","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":6,"nom":"Quilmoubre","prenom":"Amina","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":7,"nom":"Voskotte","prenom":"Julie","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":8,"nom":"Neskivet","prenom":"Paul","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":9,"nom":"Bralmignac","prenom":"Michel","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":10,"nom":"Tolveval","prenom":"Zoé","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":11,"nom":"Urvard","prenom":"Luc","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":12,"nom":"Mirveval","prenom":"Véronique","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false}
|
||||
],
|
||||
"tables": [
|
||||
{"id":1,"numero":1,"sieges":null,"forme":"ronde","position":{"x":0,"y":0}},
|
||||
{"id":2,"numero":2,"sieges":null,"forme":"ronde","position":{"x":300,"y":0}},
|
||||
{"id":3,"numero":3,"sieges":null,"forme":"ronde","position":{"x":0,"y":300}},
|
||||
{"id":4,"numero":4,"sieges":null,"forme":"ronde","position":{"x":300,"y":300}}
|
||||
],
|
||||
"reservations": [],
|
||||
"titres": [],
|
||||
"propositions": [],
|
||||
"retenu": null
|
||||
}
|
||||
}
|
||||
32
src/demo/livrees/petite.gtt.json
Normal file
32
src/demo/livrees/petite.gtt.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"entete": {"format":1,"produitVersion":"2026.10.05.01","revision":1,"comptes":{"participants":12,"tables":4,"reservations":0,"titres":0,"propositions":0,"retenu":0}},
|
||||
"charge": {
|
||||
"evenement": {"id":"demo:petite","nom":"Petite démonstration","date":null,"siegesParDefaut":3,"tours":4,"unite":"cm","etat":"brouillon","filiation":null},
|
||||
"reglages": {"separerAppartenances":true,"nouveauxVoisins":true,"nouvelleTable":true,"varierAppartenances":true,"attribuerSieges":false,"generation":{"nombre":5,"arret":200000,"historique":1000}},
|
||||
"prochainsIds": {"participant":13,"table":5,"proposition":1},
|
||||
"participants": [
|
||||
{"id":1,"nom":"Bralmumet","prenom":"Caroline","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":2,"nom":"Neskault","prenom":"Hugo","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":3,"nom":"Ivrotte","prenom":"Youssef","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":4,"nom":"Selvière","prenom":"Louis","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":5,"nom":"Selvançon","prenom":"Youssef","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":6,"nom":"Quilmoubre","prenom":"Amina","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":7,"nom":"Voskotte","prenom":"Julie","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":8,"nom":"Neskivet","prenom":"Paul","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":9,"nom":"Bralmignac","prenom":"Michel","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":10,"nom":"Tolveval","prenom":"Zoé","appartenance":"Verrerie Ulvardes","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":11,"nom":"Urvard","prenom":"Luc","appartenance":"Tannerie Cassorgue","courriel":null,"titrePressenti":null,"notes":null,"exclu":false},
|
||||
{"id":12,"nom":"Mirveval","prenom":"Véronique","appartenance":"Ateliers Jolvane","courriel":null,"titrePressenti":null,"notes":null,"exclu":false}
|
||||
],
|
||||
"tables": [
|
||||
{"id":1,"numero":1,"sieges":null,"forme":"ronde","position":{"x":0,"y":0}},
|
||||
{"id":2,"numero":2,"sieges":null,"forme":"ronde","position":{"x":300,"y":0}},
|
||||
{"id":3,"numero":3,"sieges":null,"forme":"ronde","position":{"x":0,"y":300}},
|
||||
{"id":4,"numero":4,"sieges":null,"forme":"ronde","position":{"x":300,"y":300}}
|
||||
],
|
||||
"reservations": [],
|
||||
"titres": [],
|
||||
"propositions": [],
|
||||
"retenu": null
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,10 @@
|
|||
// un patronyme distinct, chaque appartenance un libellé distinct, sans boucle
|
||||
// de rattrapage. Les fragments sont forgés pour ne désigner personne ;
|
||||
// catalogue.long.test.js refuse qu'un patronyme complet ou une racine
|
||||
// apparaisse ailleurs dans le projet.
|
||||
// apparaisse ailleurs dans le projet, hors des fichiers que
|
||||
// scripts/engendrer_livrables.js engendre de ces listes et qui les portent
|
||||
// par construction : les démonstrations livrées et les deux CSV de
|
||||
// démonstration (§ 15.6).
|
||||
//
|
||||
// L'ordre de chaque liste fait partie du contrat des tirages (§ 15.5,
|
||||
// point 3) : un tirage désigne un rang, et changer un ordre change les
|
||||
|
|
|
|||
Loading…
Reference in a new issue