diff --git a/public/index.html b/public/index.html index 1de6152..f8884a7 100644 --- a/public/index.html +++ b/public/index.html @@ -10,6 +10,8 @@

freeCSV2HTML

+

+

Si tout se passe bien, ce texte sera remplacé par un tableau de données extraites du fichier csv.

diff --git a/src/errors.js b/src/errors.js index 50d8fb8..41d51c3 100644 --- a/src/errors.js +++ b/src/errors.js @@ -6,4 +6,5 @@ module.exports = needNaturalNumber: "Merci de fournir un nombre entier supérieur ou égal à zéro pour désigner chaque colonne.", needUrl: "Merci de fournir l'url du fichier CSV à parser.", parserFail: "La lecture des données du fichier a échoué.", + selectorFieldNotFound: "Au moins une des colonnes devant servir à filtrer les données n'existe pas dans le fichier.", }; \ No newline at end of file diff --git a/src/firstExample.ts b/src/firstExample.ts index 20a6892..c6f3928 100644 --- a/src/firstExample.ts +++ b/src/firstExample.ts @@ -6,6 +6,7 @@ const initialise = async () => { let converter=new freeCSV2HTML(); converter.datasViewElt={ id:"datas" }; + converter.datasSelectors=[{ colCSV:3, id:"filtre1"},{ colCSV:4, id:"filtre2"}]; converter.datasSourceUrl="http://localhost:8080/datas/elements-chimiques.csv"; await converter.run(); } @@ -14,77 +15,4 @@ const initialise = async () => console.error(e); } } -initialise(); - - - /* - public async setDatasUrl(url: string) : Promise // pas de setter classique à cause asynchrone - { - const converter=this; // nécessaire pour éviter confusion avec this à l'intérieur de XMLHttpRequest - await fetch(url).then(function(response) - { - if(response.ok) - { - converter._datasUrl=url; - } - }) - if(converter._datasUrl!=="") - return true; - else - return false; - } - - /* - let req = new XMLHttpRequest(); - req.open("GET", url); - req.addEventListener("load", function () - { - if (req.status >= 200 && req.status < 400) - { - converter._datasUrl=url; - return true; - } - else - { - console.error("Fichier non trouvé"); - return false; - } - }); - }*/ - - -/* Exemple d'appelle complet à papaparse : - Papa.parse(url, { - delimiter :"", - newline: "", - quoteChar:'"',// à proposer en option - escapeChar:"",// idem - header:true, - transformHeader:"", - dynamicTyping:false, - preview:0, - encoding:"", - worker:true, - comments:"", - step:"",// prévoir en option pour très gros fichiers ? - complete: function(results :any) - { - console.log(results.meta); - console.log(results.data[35]); - console.log(results.errors); - }, - error:function(error :any) - { - console.log(error); - }, - download: true,// nécessaire pour un accès via "http..." - downloadRequestBody:false, - skipEmptyLines: true, - chunk:"", - fastMode:"", - beforeFirstChunk:"", - withCredentials:false, - transform: "", // à revoir - delimitersToGuess:"" - }); - * // https://www.papaparse.com/docs#config*/ \ No newline at end of file +initialise(); \ No newline at end of file diff --git a/src/freeCSV2HTML.ts b/src/freeCSV2HTML.ts index aa9216b..7400bdf 100644 --- a/src/freeCSV2HTML.ts +++ b/src/freeCSV2HTML.ts @@ -11,6 +11,8 @@ interface domElement interface selectors extends domElement { colCSV: number; + name?: string; + values? : string[]; } export class freeCSV2HTML @@ -20,7 +22,6 @@ export class freeCSV2HTML private _datasSourceUrl: string = ""; private _datasSelectors: selectors[] = []; - // Données reçues ou générées suite parsage du fichier CSV : public parseMeta: papaParseMeta|undefined = undefined; public parseDatas: papaParseDatas[] = []; public parseErrors: papaParseErrors[] = []; @@ -38,7 +39,7 @@ export class freeCSV2HTML } } - // Pas plus de tests car nombreux protocoles possibles pour l'url du fichier, pas uniquement http(s). À revoir. + // Pas plus de tests + poussés, car nombreux protocoles possibles pour l'url du fichier. À revoir. set datasSourceUrl(url: string) { if(url.trim().length === 0) @@ -47,35 +48,34 @@ export class freeCSV2HTML this._datasSourceUrl=url.trim(); } - // La validité du numéro de colonne ne peut être vérifée qu'après le parsage. - set datasSelectors(selectors: selectors[]) + set datasSelectors(selectionElts: selectors[]) { let checkContainerExist: HTMLElement|null; - for(let i = 0; i < selectors.length; i++) + for(let i = 0; i < selectionElts.length; i++) { - checkContainerExist=document.getElementById(selectors[i].id); + checkContainerExist=document.getElementById(selectionElts[i].id); if(checkContainerExist === null) - throw new Error(errors.elementNotFound+selectors[i].id); - else if(Number.isInteger(selectors[i].colCSV) === false || selectors[i].colCSV < 0) - throw new Error(errors.needNaturalNumber); + throw new Error(errors.elementNotFound+selectionElts[i].id); + else if(Number.isInteger( selectionElts[i].colCSV) === false || selectionElts[i].colCSV < 0) + throw new Error(errors.needNaturalNumber); // La validité réelle du numéro de colonne ne peut être vérifée qu'après le parsage. else - selectors[i].eltDOM=checkContainerExist; + selectionElts[i].eltDOM=checkContainerExist; } - this._datasSelectors=selectors; + this._datasSelectors= selectionElts; } public async parse(): Promise { - const converter=this; // nécessaire pour éviter confusion avec le "this" de la promise + const converter=this; return new Promise((resolve,reject) => { - if(this._datasSourceUrl !== "" ) + if(converter._datasSourceUrl !== "" ) { - Papa.parse(this._datasSourceUrl, + Papa.parse(converter._datasSourceUrl, { - quoteChar:'"', // à proposer en option ? - header:true, - complete: function(results :any) + quoteChar: '"', + header: true, + complete: function(results :any) { converter.parseMeta=results.meta; converter.parseErrors=results.errors; // prévoir option bloquant la suite à la moindre erreur trouvée ? @@ -87,11 +87,11 @@ export class freeCSV2HTML reject(new Error(errors.parserFail)); }, download: true, - skipEmptyLines: true, // à proposer en option ? + skipEmptyLines: true, }); } else - resolve(new Error(errors.needUrl)); + reject(new Error(errors.needUrl)); }); } @@ -99,37 +99,106 @@ export class freeCSV2HTML { if (this._datasViewElt.eltDOM === undefined) throw new Error(errors.needDatasElt); - - if(this._datasSourceUrl === "" ) + else if(this._datasSourceUrl === "" ) throw new Error(errors.needUrl); - if(await this.parse() === true) + await this.parse(); + + if(this.parseDatas.length === 0 || this.parseMeta === undefined || this.parseMeta.fields === undefined) { - if(this.parseDatas.length === 0 || this.parseMeta === undefined || this.parseMeta.fields === undefined) - { - this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound; - return false; - } - else - { - this.datasHTML=""; - for (let i in this.parseMeta.fields) - this.datasHTML+=""; - this.datasHTML+=""; - for (let row in this.parseDatas) - { - this.datasHTML+=""; - for(let field in this.parseDatas[row]) - { - if( this.parseMeta.fields.indexOf(field) !== -1) // si les erreurs papaParse sont acceptées, il peut y avoir des données en trop : "__parsed_extra" - this.datasHTML+=""; - } - this.datasHTML+=""; - } - this.datasHTML+="
"+this.parseMeta.fields[i]+"
"+this.parseDatas[row][field]+"
"; - this._datasViewElt.eltDOM.innerHTML=this.datasHTML; - return true; - } + this._datasViewElt.eltDOM.innerHTML=errors.datasNotFound; + return false; } + else + { + // Affichage initial des données du fichier + this.datasHTML=""; + for (let i in this.parseMeta.fields) + this.datasHTML+=""; + this.datasHTML+=""; + for (let row in this.parseDatas) + { + this.datasHTML+=""; + for(let field in this.parseDatas[row]) + { + if( this.parseMeta.fields.indexOf(field) !== -1) // si les erreurs papaParse sont acceptées, il peut y avoir des données en trop : "__parsed_extra" + this.datasHTML+=""; + } + this.datasHTML+=""; + } + this.datasHTML+="
"+this.parseMeta.fields[i]+"
"+this.parseDatas[row][field]+"
"; + this._datasViewElt.eltDOM.innerHTML=this.datasHTML; + + // Si demandé, création des listes permettant de filter les données + if(this._datasSelectors.length > 0) + { + // Les colonnes devant servir de filtre existent-elles dans le fichier ? + let selectorsHTML : string [] = []; + for(let i in this._datasSelectors) + { + if(this._datasSelectors[i].colCSV > (this.parseMeta.fields.length-1)) + throw new Error(errors.selectorFieldNotFound); + else + { + let values=[], colName=this.parseMeta.fields[this._datasSelectors[i].colCSV]; + for (let row in this.parseDatas) + { + if(values.indexOf(this.parseDatas[row][colName]) === -1) + values.push(this.parseDatas[row][colName]); + } + if(values.length > 0) + { + values.sort(); // revoir le problème des chiffres, dates, etc. https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/sort + this._datasSelectors[i].name = colName; + this._datasSelectors[i].values=values; + selectorsHTML[i]=""; + this._datasSelectors[i].eltDOM!.innerHTML=selectorsHTML[i];// je force avec "!", car l'existence de eltDOM a été testée par le setter. + } + + // suite : https://stackoverflow.com/questions/19329978/change-selects-option-and-trigger-events-with-javascript + } + } + } + return true; + } + } -} \ No newline at end of file +} +/* Exemple d'appelle complet à papaparse : + Papa.parse(url, { + delimiter :"", + newline: "", + quoteChar:'"',// à proposer en option + escapeChar:"",// idem + header:true, + transformHeader:"", + dynamicTyping:false, + preview:0, + encoding:"", + worker:true, + comments:"", + step:"",// prévoir en option pour très gros fichiers ? + complete: function(results :any) + { + console.log(results.meta); + console.log(results.data[35]); + console.log(results.errors); + }, + error:function(error :any) + { + console.log(error); + }, + download: true,// nécessaire pour un accès via "http..." + downloadRequestBody:false, + skipEmptyLines: true, + chunk:"", + fastMode:"", + beforeFirstChunk:"", + withCredentials:false, + transform: "", // à revoir + delimitersToGuess:"" + }); + * // https://www.papaparse.com/docs#config*/ \ No newline at end of file diff --git a/tests/fixtures.js b/tests/fixtures.js index 4c11cd7..be7ab3a 100644 --- a/tests/fixtures.js +++ b/tests/fixtures.js @@ -2,4 +2,8 @@ module.exports = { datasViewEltHTML: '
', datasHTML : '
Z (numéro atomique)ÉlémentSymboleFamilleAbondance des éléments dans la croûte terrestre (μg/k)
1HydrogèneHNon-métal> 100000
2HéliumHeGaz noble> 1 et < 100 000
3LithiumLiMétal alcalin> 1 et < 100 000
4BérylliumBeMétal alcalino-terreux> 1 et < 100 000
5BoreBMétalloïde> 1 et < 100 000
6CarboneCNon-métal> 100000
7AzoteNNon-métal> 1 et < 100 000
8OxygèneONon-métal> 100000
9FluorFHalogène> 100000
10NéonNeGaz noble> 1 et < 100 000
11SodiumNaMétal alcalin> 100000
12MagnésiumMgMétal alcalino-terreux> 100000
13AluminiumAlMétal pauvre> 100000
14SiliciumSiMétalloïde> 100000
15PhosphorePNon-métal> 100000
16SoufreSNon-métal> 100000
17ChloreClHalogène> 100000
18ArgonArGaz noble> 1 et < 100 000
19PotassiumKMétal alcalin> 100000
20CalciumCaMétal alcalino-terreux> 100000
21ScandiumScMétal de transition> 1 et < 100 000
22TitaneTiMétal de transition> 100000
23VanadiumVMétal de transition> 100000
24ChromeCrMétal de transition> 100000
25ManganèseMnMétal de transition> 100000
26FerFeMétal de transition> 100000
27CobaltCoMétal de transition> 1 et < 100 000
28NickelNiMétal de transition> 1 et < 100 000
29CuivreCuMétal de transition> 1 et < 100 000
30ZincZnMétal pauvre> 1 et < 100 000
31GalliumGaMétal pauvre> 1 et < 100 000
32GermaniumGeMétalloïde> 1 et < 100 000
33ArsenicAsMétalloïde> 1 et < 100 000
34SéléniumSeNon-métal> 1 et < 100 000
35BromeBrHalogène> 1 et < 100 000
36KryptonKrgaz rare≤ 1
37RubidiumRbMétal alcalin> 1 et < 100 000
38StrontiumSrMétal alcalino-terreux> 100000
39YttriumYMétal de transition> 1 et < 100 000
40ZirconiumZrMétal de transition> 100000
41NiobiumNbMétal de transition> 1 et < 100 000
42MolybdèneMoMétal de transition> 1 et < 100 000
43TechnétiumTcMétal de transitionTraces
44RuthéniumRuMétal de transition≤ 1
45RhodiumRhMétal de transition≤ 1
46PalladiumPdMétal de transition> 1 et < 100 000
47ArgentAgMétal de transition> 1 et < 100 000
48CadmiumCdMétal pauvre> 1 et < 100 000
49IndiumInMétal pauvre> 1 et < 100 000
50ÉtainSnMétal pauvre> 1 et < 100 000
51AntimoineSbMétalloïde> 1 et < 100 000
52TellureTeMétalloïde≤ 1
53IodeIHalogène> 1 et < 100 000
54XénonXegaz rare≤ 1
55CésiumCsMétal alcalin> 1 et < 100 000
56BaryumBaMétal alcalino-terreux> 100000
57LanthaneLaLanthanide> 1 et < 100 000
58CériumCeLanthanide> 1 et < 100 000
59PraséodymePrLanthanide> 1 et < 100 000
60NéodymeNdLanthanide> 1 et < 100 000
61ProméthiumPmLanthanideTraces
62SamariumSmLanthanide> 1 et < 100 000
63EuropiumEuLanthanide> 1 et < 100 000
64GadoliniumGdLanthanide> 1 et < 100 000
65TerbiumTbLanthanide> 1 et < 100 000
66DysprosiumDyLanthanide> 1 et < 100 000
67HolmiumHoLanthanide> 1 et < 100 000
68ErbiumErLanthanide> 1 et < 100 000
69ThuliumTmLanthanide> 1 et < 100 000
70YtterbiumYbLanthanide> 1 et < 100 000
71LutéciumLuLanthanide> 1 et < 100 000
72HafniumHfMétal de transition> 1 et < 100 000
73TantaleTaMétal de transition> 1 et < 100 000
74TungstèneWMétal de transition> 1 et < 100 000
75RhéniumReMétal de transition≤ 1
76OsmiumOsMétal de transition> 1 et < 100 000
77IridiumIrMétal de transition≤ 1
78PlatinePtMétal de transition> 1 et < 100 000
79OrAuMétal de transition> 1 et < 100 000
80MercureHgMétal pauvre> 1 et < 100 000
81ThalliumTlMétal pauvre> 1 et < 100 000
82PlombPbMétal pauvre> 1 et < 100 000
83BismuthBiMétal pauvre> 1 et < 100 000
84PoloniumPoMétal pauvre≤ 1
85AstateAtMétalloïdeTraces
86RadonRnGaz noble≤ 1
87FranciumFrMétal alcalinTraces
88RadiumRaMétal alcalino-terreux≤ 1
89ActiniumAcActinide≤ 1
90ThoriumThActinide> 1 et < 100 000
91ProtactiniumPaActinide≤ 1
92UraniumUActinide> 1 et < 100 000
93NeptuniumNpActinideTraces
94PlutoniumPuActinideTraces
95AmériciumAmActinideInexistant
96CuriumCmActinideInexistant
97BerkéliumBkActinideInexistant
98CaliforniumCfActinideInexistant
99EinsteiniumEsActinideInexistant
100FermiumFmActinideInexistant
101MendéléviumMdActinideInexistant
102NobéliumNoActinideInexistant
103LawrenciumLrActinideInexistant
104RutherfordiumRfMétal de transitionInexistant
105DubniumDbMétal de transitionInexistant
106SeaborgiumSgMétal de transitionInexistant
107BohriumBhMétal de transitionInexistant
108HassiumHsMétal de transitionInexistant
109MeitnériumMtIndéfinieInexistant
110DarmstadtiumDsIndéfinieInexistant
111RoentgeniumRgIndéfinieInexistant
112CoperniciumCnMétal de transitionInexistant
113NihoniumNhIndéfinieInexistant
114FléroviumFlIndéfinieInexistant
115MoscoviumMcIndéfinieInexistant
116LivermoriumLvIndéfinieInexistant
117TennesseTsIndéfinieInexistant
118OganessonOgIndéfinieInexistant
', -} \ No newline at end of file + selector1HTML: '', + selector2HTML: '', + datasHTMLForSelect1: '
', + datasHTMLForSelect2:'
', +} \ No newline at end of file diff --git a/tests/freeCSV2HTMLSpec.ts b/tests/freeCSV2HTMLSpec.ts index 00fe89b..3737123 100644 --- a/tests/freeCSV2HTMLSpec.ts +++ b/tests/freeCSV2HTMLSpec.ts @@ -112,5 +112,63 @@ describe("freeCSV2HTML", () => let txtDatasViewsElt=document.getElementById("datas").innerHTML; expect(txtDatasViewsElt).toEqual(fixtures.datasHTML); }); + + it("Doit générer une erreur si au moins un des numéros de colonne des sélecteurs ne correspond pas à une des colonne du fichier.", async () => + { + converter.datasViewElt={ id:"datas" }; + converter.datasSourceUrl="http://localhost:9876/datas/elements-chimiques.csv"; + converter.datasSelectors=[{ colCSV:0, id:"selector1"},{ colCSV:5, id:"selector2"}]; + await expectAsync(converter.run()).toBeRejectedWith(new Error(errors.selectorFieldNotFound)); + }); + + it("Ne doit pas générer d'erreur si tous les numéros de colonne des sélecteurs correspondent pas à une des colonne du fichier.", async () => + { + converter.datasViewElt={ id:"datas" }; + converter.datasSourceUrl="http://localhost:9876/datas/elements-chimiques.csv"; + converter.datasSelectors=[{ colCSV:3, id:"selector1"},{ colCSV:4, id:"selector2"}]; + await expectAsync(converter.run()).not.toBeRejected(); + }); + + it("Pour chaque sélecteur demandé doit générer un élement