diff --git a/src/assets/js/utils.js b/src/assets/js/utils.js index 284cbdf1d18adb5a327589c0e13ac9a9429dd510..05a20057e776787f9d9ff3e9afc70a0013698c2b 100644 --- a/src/assets/js/utils.js +++ b/src/assets/js/utils.js @@ -14,16 +14,16 @@ export function fileConvertSizeToMo(aSize){ return (aSize/def[0]).toFixed(def[2]); } -export function csvToJson(csv) { +export function csvToJson(csv, delimiter) { const result = []; const allLines = csv.split('\n'); - const headers = allLines[0].split(','); + const headers = allLines[0].split(delimiter); const [, ...lines] = allLines; for (const line of lines) { const obj = {}; - const currentLine = line.split(','); + const currentLine = line.split(delimiter); for (let i = 0; i < headers.length; i++) { obj[headers[i]] = currentLine[i]; diff --git a/src/components/Project/Detail/ProjectFeatureTypes.vue b/src/components/Project/Detail/ProjectFeatureTypes.vue index 0ec336a9bc2f693bd67d92a5abb40df7874703f9..7233ab3fb43727395096056596a5faae3140fda9 100644 --- a/src/components/Project/Detail/ProjectFeatureTypes.vue +++ b/src/components/Project/Detail/ProjectFeatureTypes.vue @@ -614,11 +614,22 @@ export default { fr.readAsText(this.csvFileToImport); fr.onloadend = () => { + // Find csv delimiter + const commaDelimited = fr.result.split('\n')[0].includes(','); + const semicolonDelimited = fr.result.split('\n')[0].includes(';'); + const delimiter = commaDelimited && !semicolonDelimited ? ',' : semicolonDelimited ? ';' : false; + + if ((commaDelimited && semicolonDelimited) || !delimiter) { + this.csvError = `Le fichier ${this.csvFileToImport.name} n'est pas formaté correctement`; + this.featureTypeImporting = false; + return; + } + // Check if file contains 'lat' and 'long' fields const headersLine = fr.result .split('\n')[0] - .split(',') + .split(delimiter) .filter(el => { return el === 'lat' || el === 'lon'; }); @@ -628,7 +639,7 @@ export default { const sampleLine = fr.result .split('\n')[1] - .split(',') + .split(delimiter) .map(el => { return !isNaN(el) && el.indexOf('.') != -1; }) @@ -636,7 +647,7 @@ export default { if (sampleLine.length > 1 && headersLine.length === 2) { this.csvError = null; - this.csvImport = csvToJson(fr.result); + this.csvImport = csvToJson(fr.result, delimiter); this.featureTypeImporting = false; //* stock filename to import features afterward this.SET_FILE_TO_IMPORT(this.csvFileToImport); diff --git a/src/views/FeatureType/FeatureTypeDetail.vue b/src/views/FeatureType/FeatureTypeDetail.vue index 6d65a3bce7bb383ccfc6f3c156f1dd0b57aed8d7..57bf96f0c94a11114e949ae1a76b929aa5fcc1f6 100644 --- a/src/views/FeatureType/FeatureTypeDetail.vue +++ b/src/views/FeatureType/FeatureTypeDetail.vue @@ -195,9 +195,9 @@ <option value="GeoJSON"> GeoJSON </option> - <option value="CSV"> + <!-- <option value="CSV"> CSV - </option> + </option> --> </select> <button type="button" @@ -513,7 +513,7 @@ export default { methods: { ...mapMutations('feature-type', [ 'SET_CURRENT_FEATURE_TYPE_SLUG', - 'SET_FILE_TO_IMPORT' + 'SET_FILE_TO_IMPORT', ]), ...mapActions('feature-type', [ 'SEND_FEATURES_FROM_CSV', @@ -588,20 +588,35 @@ export default { checkCsvValidity(csv) { this.importError = ''; + + // Find csv delimiter + const commaDelimited = csv.split('\n')[0].includes(','); + const semicolonDelimited = csv.split('\n')[0].includes(';'); + const delimiter = commaDelimited && !semicolonDelimited ? ',' : semicolonDelimited ? ';' : false; + + if ((commaDelimited && semicolonDelimited) || !delimiter) { + this.importError = `Le fichier ${this.csvFileToImport.name} n'est pas formaté correctement`; + return false; + } + // Check if file contains 'lat' and 'long' fields const headersLine = csv .split('\n')[0] - .split(',') + .split(delimiter) .filter(el => { return el === 'lat' || el === 'lon'; }); // Look for 2 decimal fields in first line of csv // corresponding to lon and lat + if (headersLine.length !== 2) { + this.importError = 'Le fichier ne semble pas contenir de champs de coordonnées.'; + return false; + } const sampleLine = csv .split('\n')[1] - .split(',') + .split(delimiter) .map(el => { return !isNaN(el) && el.indexOf('.') != -1; }) @@ -614,11 +629,27 @@ export default { options: el.options, }; }); - const csvFeatures = csvToJson(csv); + const csvFeatures = csvToJson(csv, delimiter); for (const feature of csvFeatures) { - for (const { name, field_type, options } of fields) { + for (let { name, field_type, options } of fields) { if (name in feature) { const fieldInFeature = feature[name]; + + // overide some specific cases on date type data + if ( + typeof fieldInFeature === 'string' && + ['/', ':', '-'].some((el) => fieldInFeature.includes(el)) && + (new Date(fieldInFeature)) instanceof Date && + !isNaN((new Date(fieldInFeature)).valueOf()) + ) { + field_type = 'char'; + } else if ( + field_type === 'date' && + ((new Date(fieldInFeature)) instanceof Date) + ) { + field_type = 'char'; + } + const customType = this.transformProperties(fieldInFeature); //* if custom field value is not null, then check validity of field if (fieldInFeature !== null) { @@ -712,7 +743,7 @@ export default { } else if (this.geojsonFileToImport.size > 0) { //* import directly from geojson payload['fileToImport'] = this.geojsonFileToImport; } else { - this.importError = "La ressource n'a pas pu être récupéré."; + this.importError = 'La ressource n\'a pas pu être récupéré.'; return; } this.$store.dispatch('feature-type/SEND_FEATURES_FROM_GEOJSON', payload) @@ -747,7 +778,7 @@ export default { if (blob) { const link = document.createElement('a'); link.href = URL.createObjectURL(blob); - link.download = `${this.project.title}-${this.structure.title}.json`; + link.download = `${this.project.title}-${this.structure.title}.${this.exportFormat === 'GeoJSON' ? 'json' : 'csv'}`; link.click(); URL.revokeObjectURL(link.href); }