Skip to content
GitLab
Explore
Sign in
Register
Primary navigation
Search or go to…
Project
G
Géocontrib Frontend
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Redmine
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Terraform modules
Analyze
Contributor analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
GéoContrib
Géocontrib Frontend
Commits
6f3d5fdc
Commit
6f3d5fdc
authored
1 year ago
by
Timothee P
Browse files
Options
Downloads
Patches
Plain Diff
enforce CSV checks
parent
e01cf97a
No related branches found
No related tags found
1 merge request
!724
REDMINE_ISSUE-19251 | Listes de valeurs pré-enr. - Vérification que l'option existe bien dans la liste ne fonctionne pas dans le cas où j'ai un string
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/components/Project/Detail/ProjectFeatureTypes.vue
+2
-1
2 additions, 1 deletion
src/components/Project/Detail/ProjectFeatureTypes.vue
src/views/FeatureType/FeatureTypeDetail.vue
+131
-52
131 additions, 52 deletions
src/views/FeatureType/FeatureTypeDetail.vue
with
133 additions
and
53 deletions
src/components/Project/Detail/ProjectFeatureTypes.vue
+
2
−
1
View file @
6f3d5fdc
...
...
@@ -515,7 +515,8 @@ export default {
});
// Look for 2 decimal fields in first line of csv
// corresponding to lon and lat
if
(
checkLonLatValues
(
headers
,
rows
)
&&
headersCoord
.
length
===
2
)
{
const
hasCoordValues
=
checkLonLatValues
(
headers
,
rows
);
if
(
headersCoord
.
length
===
2
&&
hasCoordValues
)
{
this
.
csvError
=
null
;
csv
()
.
fromString
(
fr
.
result
)
...
...
This diff is collapsed.
Click to expand it.
src/views/FeatureType/FeatureTypeDetail.vue
+
131
−
52
View file @
6f3d5fdc
...
...
@@ -388,11 +388,11 @@ import { escape as _escape } from 'lodash';
import
{
mapActions
,
mapMutations
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
{
formatStringDate
,
transformProperties
}
from
'
@/utils
'
;
import
FeatureFetchOffsetRoute
from
'
@/components/Feature/FeatureFetchOffsetRoute
'
;
import
ImportTask
from
'
@/components/ImportTask
'
;
import
featureAPI
from
'
@/services/feature-api
'
;
import
{
fileConvertSizeToMo
}
from
'
@/assets/js/utils
'
;
// TODO: refactor with above utils, those files are similar
import
{
fileConvertSizeToMo
,
determineDelimiter
,
parseCSV
,
checkLonLatValues
}
from
'
@/assets/js/utils
'
;
const
geojsonFileToImport
=
{
name
:
'
Sélectionner un fichier GeoJSON ...
'
,
...
...
@@ -648,107 +648,186 @@ export default {
return
true
;
},
/**
* Checks the validity of a CSV string. It ensures the CSV uses a recognized delimiter,
* contains 'lat' and 'lon' headers, and that these columns contain decimal values within valid ranges.
* Additionally, it verifies the consistency and presence of data in the CSV, and that the types of values are valid.
*
* @param {string} csvString - The CSV content in string format.
* @returns {boolean|Promise<boolean>} Returns a boolean or a Promise resolving to a boolean,
* indicating the validity of the CSV.
*/
async
checkCsvValidity
(
csvString
)
{
this
.
importError
=
''
;
// Find csvString delimiter
const
commaDelimited
=
csvString
.
split
(
'
\n
'
)[
0
].
includes
(
'
,
'
);
const
semicolonDelimited
=
csvString
.
split
(
'
\n
'
)[
0
].
includes
(
'
;
'
);
const
delimiter
=
commaDelimited
&&
!
semicolonDelimited
?
'
,
'
:
semicolonDelimited
?
'
;
'
:
false
;
if
((
commaDelimited
&&
semicolonDelimited
)
||
!
delimiter
)
{
// Determine the delimiter of the CSV
const
delimiter
=
determineDelimiter
(
csvString
);
if
(
!
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
=
csvString
.
split
(
'
\n
'
)[
0
]
.
replace
(
/
(\r\n
|
\n
|
\r)
/gm
,
''
)
.
split
(
delimiter
)
.
filter
(
el
=>
{
return
el
===
'
lat
'
||
el
===
'
lon
'
;
});
if
(
headersLine
.
length
!==
2
)
{
this
.
importError
=
'
Le fichier ne semble pas contenir de champs de coordonnées.
'
;
// Parse the CSV string into rows
const
rows
=
parseCSV
(
csvString
,
delimiter
);
// Extract headers and check for required fields 'lat' and 'lon'
const
headers
=
rows
.
shift
();
if
(
!
headers
.
includes
(
'
lat
'
)
||
!
headers
.
includes
(
'
lon
'
))
{
this
.
importError
=
'
Les champs obligatoires "lat" et "lon" sont absents.
'
;
return
false
;
}
// Ensure there are data rows after the headers
if
(
rows
.
length
===
0
)
{
this
.
importError
=
'
Aucune donnée trouvée après les en-têtes.
'
;
return
false
;
}
const
sampleLine
=
csvString
.
split
(
'
\n
'
)[
1
]
.
split
(
delimiter
)
.
map
(
el
=>
{
return
!
isNaN
(
el
)
&&
el
.
indexOf
(
'
.
'
)
!=
-
1
;
})
.
filter
(
Boolean
);
if
(
sampleLine
.
length
>
1
&&
headersLine
.
length
===
2
)
{
const
features
=
await
csv
().
fromString
(
csvString
);
return
this
.
isValidTypes
({
features
});
}
else
{
// Ensure that each row has the same number of columns as the headers
if
(
rows
.
some
(
row
=>
row
.
length
!==
headers
.
length
))
{
this
.
importError
=
'
Incohérence dans le nombre de colonnes par ligne.
'
;
return
false
;
}
// Verify the presence and validity of coordinate values
const
hasCoordValues
=
checkLonLatValues
(
headers
,
rows
);
if
(
!
hasCoordValues
)
{
this
.
importError
=
'
Les valeurs de "lon" et "lat" ne sont pas valides.
'
;
return
false
;
}
// Convert the CSV string to a JSON object for further processing
const
jsonFromCsv
=
await
csv
().
fromString
(
csvString
);
// Validate the types of values in the JSON object
const
validity
=
await
this
.
isValidTypes
({
features
:
jsonFromCsv
});
return
validity
;
},
onGeojsonFileChange
(
e
)
{
/**
* Handles the change event for GeoJSON file input. This function is triggered when a user selects a file.
* It reads the file, checks its validity if it's not too large, and updates the component state accordingly.
*
* @param {Event} e - The event triggered by file input change.
*/
async
onGeojsonFileChange
(
e
)
{
// Start loading process
this
.
loadingImportFile
=
true
;
this
.
csvFileToImport
=
csvFileToImport
;
// empty csv file to avoid sending it instead of geojson file
// Clear any previously selected CSV file to avoid confusion
this
.
csvFileToImport
=
csvFileToImport
;
// Retrieve the files from the event
const
files
=
e
.
target
.
files
||
e
.
dataTransfer
.
files
;
// If no file is selected, stop the loading process and return
if
(
!
files
.
length
)
{
this
.
loadingImportFile
=
false
;
return
;
}
const
reader
=
new
FileReader
();
reader
.
addEventListener
(
'
load
'
,
(
e
)
=>
{
// bypass json check for files larger then 10 Mo
/**
* Asynchronously processes the content of the file.
* Checks the validity of the GeoJSON file if it's smaller than a certain size.
* Updates the state with the GeoJSON file if it's valid.
*
* @param {string} fileContent - The content of the file read by FileReader.
*/
const
processFile
=
async
(
fileContent
)
=>
{
let
jsonValidity
;
// Check the file size and determine the GeoJSON validity
if
(
parseFloat
(
fileConvertSizeToMo
(
files
[
0
].
size
))
<=
10
)
{
// If the file is smaller than 10 Mo, check its validity
try
{
jsonValidity
=
this
.
isValidTypes
(
JSON
.
parse
(
e
.
target
.
result
));
}
catch
(
e
)
{
this
.
DISPLAY_MESSAGE
({
comment
:
e
,
level
:
'
negative
'
});
const
json
=
JSON
.
parse
(
fileContent
);
jsonValidity
=
await
this
.
isValidTypes
(
json
);
}
catch
(
error
)
{
this
.
DISPLAY_MESSAGE
({
comment
:
error
,
level
:
'
negative
'
});
jsonValidity
=
false
;
}
}
else
{
// Assume validity for larger files
jsonValidity
=
true
;
}
// If the GeoJSON is valid, update the component state with the file
if
(
jsonValidity
)
{
this
.
geojsonFileToImport
=
files
[
0
];
// todo : remove this value from state as it stored (first attempt didn't work)
this
.
SET_FILE_TO_IMPORT
(
this
.
geojsonFileToImport
);
this
.
geojsonFileToImport
=
files
[
0
];
// TODO: Remove this value from state as it is stored (first attempt didn't work)
this
.
SET_FILE_TO_IMPORT
(
this
.
geojsonFileToImport
);
}
// Stop the loading process
this
.
loadingImportFile
=
false
;
});
};
// Setup the load event listener for FileReader
reader
.
addEventListener
(
'
load
'
,
(
e
)
=>
processFile
(
e
.
target
.
result
));
// Read the text from the selected file
reader
.
readAsText
(
files
[
0
]);
},
onCsvFileChange
(
e
)
{
/**
* Handles the change event for CSV file input. This function is triggered when a user selects a file.
* It reads the file, checks its validity if it's not too large, and updates the component state accordingly.
*
* @param {Event} e - The event triggered by file input change.
*/
async
onCsvFileChange
(
e
)
{
// Start loading process
this
.
loadingImportFile
=
true
;
this
.
geojsonFileToImport
=
geojsonFileToImport
;
// empty geojson file to avoid sending it instead of csv file
// Clear any previously selected geojson file to avoid confusion
this
.
geojsonFileToImport
=
geojsonFileToImport
;
// Retrieve the files from the event
const
files
=
e
.
target
.
files
||
e
.
dataTransfer
.
files
;
// If no file is selected, stop the loading process and return
if
(
!
files
.
length
)
{
this
.
loadingImportFile
=
false
;
return
;
}
// Create a new FileReader to read the selected file
const
reader
=
new
FileReader
();
reader
.
addEventListener
(
'
load
'
,
(
e
)
=>
{
// bypass csv check for files larger then 10 Mo
/**
* Asynchronously processes the content of the file.
* Checks the validity of the CSV file if it's smaller than a certain size.
* Updates the state with the CSV file if it's valid.
*
* @param {string} fileContent - The content of the file read by FileReader.
*/
const
processFile
=
async
(
fileContent
)
=>
{
let
csvValidity
;
// Check the file size and determine the CSV validity
if
(
parseFloat
(
fileConvertSizeToMo
(
files
[
0
].
size
))
<=
10
)
{
csvValidity
=
this
.
checkCsvValidity
(
e
.
target
.
result
);
// If the file is smaller than 10 Mo, check its validity
csvValidity
=
await
this
.
checkCsvValidity
(
fileContent
);
}
else
{
// Assume validity for larger files
csvValidity
=
true
;
}
// If the CSV is valid, update the component state with the file
if
(
csvValidity
)
{
this
.
csvFileToImport
=
files
[
0
];
// todo : remove this value from state as it stored (first attempt didn't work)
this
.
SET_FILE_TO_IMPORT
(
this
.
csvFileToImport
);
this
.
csvFileToImport
=
files
[
0
];
// TODO: Remove this value from state as it is stored (first attempt didn't work)
this
.
SET_FILE_TO_IMPORT
(
this
.
csvFileToImport
);
}
// Stop the loading process
this
.
loadingImportFile
=
false
;
});
};
// Setup the load event listener for FileReader
reader
.
addEventListener
(
'
load
'
,
(
e
)
=>
processFile
(
e
.
target
.
result
));
// Read the text from the selected file
reader
.
readAsText
(
files
[
0
]);
},
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment