From 6312ecc0e863c0455986ecf6853b63e05e334661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr> Date: Thu, 2 Sep 2021 17:00:38 +0200 Subject: [PATCH] implement post for feature_type and features from geojson --- src/store/modules/feature_type.js | 155 +++++++++------ src/views/feature/Feature_detail.vue | 2 +- .../feature_type/Feature_type_detail.vue | 43 +++-- src/views/feature_type/Feature_type_edit.vue | 179 ++++++++++++++++-- src/views/project/Project_detail.vue | 122 ++---------- 5 files changed, 310 insertions(+), 191 deletions(-) diff --git a/src/store/modules/feature_type.js b/src/store/modules/feature_type.js index 03b43c26..e4fdf2cb 100644 --- a/src/store/modules/feature_type.js +++ b/src/store/modules/feature_type.js @@ -1,63 +1,36 @@ import axios from "axios" -import store from '@/store'; +//import store from '@/store'; -export const RESET = 'RESET'; +// export const RESET = 'RESET'; -const initialState = () => ({ - form: { - colors_style: { - value: null, - options: [], - fields: [], - }, - color: { - id_for_label: "couleur", - label: "Couleur", - field: { - max_length: 128, // ! Vérifier la valeur dans django - }, - html_name: "couleur", - value: "#000000", - }, - title: { - errors: [], - id_for_label: "title", - label: "Titre", - field: { - max_length: 128, // ! Vérifier la valeur dans django - }, - html_name: "title", - value: null, - }, - geom_type: { - id_for_label: "geom_type", - label: "Type de géométrie", - field: { - choices: ["Ligne", "Point", "Polygone"], - max_length: 128, // ! Vérifier la valeur dans django - }, - html_name: "geom_type", - value: "Point", - }, - }, +/* const initialState = () => ({ + form: null, colorsStyleList: [], customForms: [], current_feature_type_slug: null, feature_types: [], -}); +}); */ const feature_type = { namespaced: true, - state: initialState(), - + //state: initialState(), + state: { + form: null, + colorsStyleList: [], + customForms: [], + current_feature_type_slug: null, + feature_types: [], + importFeatureTypeData: [], + }, + mutations: { - [RESET]: (state) => { + /* [RESET]: (state) => { const newState = initialState(); Object.keys(newState).forEach((key) => { state[key] = newState[key]; }); - }, + }, */ SET_FEATURE_TYPES(state, feature_types) { state.feature_types = feature_types; }, @@ -68,7 +41,7 @@ const feature_type = { state.form = payload; }, ADD_CUSTOM_FORM(state, customForm) { - state.customForms = [...state.customForms, customForm ]; + state.customForms = [...state.customForms, customForm]; }, UPDATE_CUSTOM_FORM(state, payload) { const index = state.customForms.findIndex((el) => el.dataKey === payload.dataKey); @@ -83,6 +56,9 @@ const feature_type = { if (form.label) res.push(form); } state.colorsStyleList = res; + }, + SET_IMPORT_FEATURE_TYPES_DATA(state, payload) { + state.importFeatureTypeData = payload; } }, getters: { @@ -91,27 +67,92 @@ const feature_type = { ) }, actions: { - [RESET]: ({ commit }) => { - commit("RESET_STATE"); - }, - POST_FEATURE_TYPE({ state }) { - const data = { form: state.form, formset: state.customForms } + /* [RESET]: ({ commit }) => { + commit("RESET_STATE"); + }, */ + POST_FEATURE_TYPE({ state, rootGetters }) { + const data = { + 'title': state.form.title.value, + 'slug': rootGetters.project.slug, + 'geom_type': state.form.geom_type.value, + 'color': state.form.color.value, + 'colors_style': state.form.colors_style.value, + 'project': rootGetters.project.slug, + //'project': state.form.project.value, + 'customfield_set': state.customForms.map(el => { + return { + 'position': el.position.value, + 'label': el.label.value, + 'name': el.name.value, + 'field_type': el.field_type.value, + 'options': el.options.value, + } + }), + //'is_editable': true, + } console.log("data", data) axios .post(`${process.env.VUE_APP_DJANGO_API_BASE}feature-types/`, data) .then((response) => { - const routerHistory = this.$router.options.routerHistory - store.commit("SET_USER", response.data.user); - this.$router.push(routerHistory[routerHistory.length - 1] || "/") - store.dispatch("GET_USER_LEVEL_PROJECTS"); + console.log(response) + // const routerHistory = this.$router.options.routerHistory + //store.commit("SET_USER", response.data.user); + // this.$router.push(routerHistory[routerHistory.length - 1] || "/") + //store.dispatch("GET_USER_LEVEL_PROJECTS"); }) - .catch(() => { - store.commit("SET_USER", false) + .catch((error) => { + console.error(error); + // store.commit("SET_USER", false) }); }, - }, + POST_FEATURES_FROM_GEOJSON({ rootGetters, dispatch }, payload) { + const { feature_type_slug, filenameToImport } = payload + + if (filenameToImport.size > 0) { + var formData = new FormData(); + formData.append("json_file", filenameToImport); + let url = + process.env.VUE_APP_URL_BASE + + "projet/" + rootGetters.project.slug + + "/type-signalement/" + feature_type_slug + + "/importer-geojson/"; + axios + .post(url, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }) + .then((response) => { + if (response.status == 200) { + dispatch("GET_IMPORTS", feature_type_slug); + // TODO : RELOAD DERNIER SIGNALEMENTS + } + }) + .catch((err) => { + // TODO : HANDLER ERROR + console.log(err); + }); + } + }, + + GET_IMPORTS({ commit }, feature_type) { + let url = + process.env.VUE_APP_DJANGO_API_BASE + + "import-tasks?feature_type_id=" + + feature_type; + axios + .get(url) + .then((response) => { + commit("SET_IMPORT_FEATURE_TYPES_DATA", response.data); + //commit("SET_IMPORT_TASK_DATA", response.data); + }) + .catch((err) => { + console.log(err); + }); + } + } } export default feature_type \ No newline at end of file diff --git a/src/views/feature/Feature_detail.vue b/src/views/feature/Feature_detail.vue index 95baee15..0310176f 100644 --- a/src/views/feature/Feature_detail.vue +++ b/src/views/feature/Feature_detail.vue @@ -266,11 +266,11 @@ <div class="ui segment"> <form id="form-comment" - action="{% url 'geocontrib:add_comment' slug=feature.project.slug feature_type_slug=feature.feature_type.slug feature_id=feature.feature_id%}" class="ui form" method="POST" enctype="multipart/form-data" > + <!-- action="{% url 'geocontrib:add_comment' slug=feature.project.slug feature_type_slug=feature.feature_type.slug feature_id=feature.feature_id%}" --> <!-- {% for hidden in comment_form.hidden_fields %} {{ hidden }} {% endfor %} --> diff --git a/src/views/feature_type/Feature_type_detail.vue b/src/views/feature_type/Feature_type_detail.vue index 7204cc59..4f251e2b 100644 --- a/src/views/feature_type/Feature_type_detail.vue +++ b/src/views/feature_type/Feature_type_detail.vue @@ -86,7 +86,10 @@ > <i class="upload icon"></i> Lancer l'import </button> - <ImportTask v-if="dataImport.length" :data="dataImport" /> + <ImportTask + v-if="importFeatureTypeData && importFeatureTypeData.length" + :data="importFeatureTypeData" + /> </div> </div> <div @@ -101,7 +104,11 @@ Vous pouvez télécharger l'ensemble des signalements ayant le statut publiés pour ce type. </p> - <button type="button" class="ui fluid teal icon button" @click="exportFeatures"> + <button + type="button" + class="ui fluid teal icon button" + @click="exportFeatures" + > <i class="download icon"></i> Exporter </button> // todo gérer export @@ -179,7 +186,6 @@ </template> <script> -import axios from "axios"; import { mapGetters, mapState } from "vuex"; import ImportTask from "@/components/ImportTask"; @@ -191,7 +197,7 @@ export default { computed: { ...mapGetters(["project"]), ...mapState("feature", ["features"]), - ...mapState("feature_type", ["feature_types"]), + ...mapState("feature_type", ["feature_types", "importFeatureTypeData"]), structure: function () { // * je ne sais pas pourquoi ça s'appelle structure return this.feature_types.find( @@ -203,7 +209,11 @@ export default { watch: { structure(newVal, oldVal) { if (newVal !== oldVal) { - this.getImports(); + //this.getImports(); + this.$store.dispatch( + "feature_type/GET_IMPORTS", + this.structure.slug + ); } }, }, @@ -217,7 +227,7 @@ export default { fileToImport: {}, showImport: false, showExport: true, - dataImport: [], + //dataImport: [], }; }, @@ -227,11 +237,14 @@ export default { if (!files.length) return; this.filenameToImport = files[0]; }, - toggleImport() { - console.log("toggleImport"); - }, + importGeoJson() { - if (this.filenameToImport.size > 0) { + this.$store.dispatch("feature_type/POST_FEATURES_FROM_GEOJSON", { + //slug: this.$route.params.slug, + feature_type_slug: this.$route.params.feature_type_slug, + filenameToImport: this.filenameToImport, + }); + /* if (this.filenameToImport.size > 0) { var formData = new FormData(); formData.append("json_file", this.filenameToImport); let url = @@ -257,9 +270,10 @@ export default { // TODO : HANDLER ERROR console.log(err); }); - } + } */ }, - getImports() { + + /* getImports() { let url = process.env.VUE_APP_DJANGO_API_BASE + "import-tasks?feature_type_id=" + @@ -272,9 +286,10 @@ export default { .catch((err) => { console.log(err); }); - }, + }, */ + exportFeatures() { - console.log("TEST", this.$store) + console.log("TEST", this.$store); this.$store.dispatch( "feature/EXPORT_FEATURES", this.$route.params.slug_type_signal diff --git a/src/views/feature_type/Feature_type_edit.vue b/src/views/feature_type/Feature_type_edit.vue index 7d2b0bf2..c40c7c87 100644 --- a/src/views/feature_type/Feature_type_edit.vue +++ b/src/views/feature_type/Feature_type_edit.vue @@ -109,16 +109,25 @@ </button> <div class="ui divider"></div> - <button class="ui teal icon button" type="button" @click="postForm"> + <button + class="ui teal icon button" + type="button" + @click="postFeatureType" + > <i class="white save icon"></i> {{ action === "create" ? "Créer" : "Sauvegarder" }} le type de signalement </button> - <button v-if="geojson" class="ui teal icon button" type="button" @click="postFormAndGeojson"> + <button + v-if="geojson" + class="ui teal icon button" + type="button" + @click="postFeatureTypeNfeatures" + > <i class="white save icon"></i> Créer et importer le(s) signalement(s) du geojson </button> - + // TODO: Add check script for form & other scripts // </form> </div> @@ -147,13 +156,70 @@ export default { return { action: "create", dataKey: 0, + form: { + colors_style: { + value: null, + options: [], + fields: [], + }, + color: { + id_for_label: "couleur", + label: "Couleur", + field: { + max_length: 128, // ! Vérifier la valeur dans django + }, + html_name: "couleur", + value: "#000000", + }, + title: { + errors: [], + id_for_label: "title", + label: "Titre", + field: { + max_length: 128, // ! Vérifier la valeur dans django + }, + html_name: "title", + value: null, + }, + geom_type: { + id_for_label: "geom_type", + label: "Type de géométrie", + field: { + choices: ["Ligne", "Point", "Polygone"], + max_length: 128, // ! Vérifier la valeur dans django + }, + html_name: "geom_type", + value: "Point", + }, + }, + reservedKeywords: [ + // todo : add keywords for mapstyle (strokewidth...) + "title", + "description", + "status", + "created_on", + "updated_on", + "archived_on", + "deletion_on", + "feature_type", + ], + + typeDict: { + boolean: "Booléen", + string: "Chaîne de caractères", + date: "Date", + list: "Liste de valeurs", + integer: "Nombre entier", + decimal: "Nombre décimal", + text: "Texte multiligne", + }, }; }, - props: ["geojson", ], + props: ["geojson"], computed: { ...mapGetters(["project"]), - ...mapState("feature_type", ["form", "customForms", "colorsStyleList"]), + ...mapState("feature_type", ["customForms", "colorsStyleList"]), ...mapGetters("feature_type", ["feature_type"]), selected_geom_type: { get() { @@ -189,7 +255,7 @@ export default { }, }, methods: { - ...mapMutations('feature_type', ['RESET']), + ...mapMutations("feature_type", ["RESET"]), definePageType() { if (this.$router.history.current.name === "ajouter-type-signalement") { this.action = "create"; @@ -203,9 +269,15 @@ export default { this.action = "duplicate"; } }, - addCustomForm() { + addCustomForm(customForm) { this.dataKey += 1; // * increment counter for key in v-for - this.$store.commit("feature_type/ADD_CUSTOM_FORM", {dataKey: this.dataKey}); // * create an object with the counter in store + let newCustomForm = { + dataKey: this.dataKey, + }; + if (customForm) { + newCustomForm = { newCustomForm, ...customForm }; + } + this.$store.commit("feature_type/ADD_CUSTOM_FORM", newCustomForm); // * create an object with the counter in store }, iniateColorsStyleFields() { const selected = this.colorsStyleList.find( @@ -222,7 +294,7 @@ export default { } }, - postForm() { + postFeatureType() { if (this.form.title.value) { this.form.title.errors = []; this.$store.dispatch("feature_type/POST_FEATURE_TYPE"); @@ -232,8 +304,10 @@ export default { this.form.title.errors.push("Veuillez compléter ce champ."); } }, - postFormAndGeojson(){ - console.log('TODO : DO FUNCTION') + postFeatureTypeNfeatures() { + // todo : post feature_type and create & post features + console.log("TODO : DO FUNCTION"); + this.postFeatureType(); }, updateStore() { this.$store.commit("feature_type/UPDATE_FORM", { @@ -243,6 +317,82 @@ export default { colors_style: this.form.colors_style, }); }, + + // * Methodes for geojson import * // + toNewFeatureType() { + this.$router.push({ + name: "ajouter-type-signalement", + params: { geojson: this.jsonDict }, + }); + }, + translateLabel(value) { + if (value == "LineString") { + return "Ligne"; + } else if (value == "Polygon") { + return "Polygone"; + } + return "point"; + + //("linestring", "Ligne"), + //("point", "Point"), + //("polygon", "Polygone"), + }, + + transformProperties(prop) { + const type = typeof prop; + if (type === "boolean") { + return "Booléen"; + } else if (type === "number") { + return "Nombre entier"; + } else if (type === "string") { + //* check if string is not convertible to a decimal + if (isNaN(parseFloat(prop))) { + if (new Date(prop) !== "Invalid Date") { + return "Date"; + } /* else if (prop.includes(",")) { + return "Liste de valeurs"; + } */ + else { + return "Chaîne de caractères"; + } + } else { + return "Nombre décimal"; + } + } + return null; + }, + + getFieldType(val) { + if (val in this.typeDict) return this.typeDict[val]; + }, + + importGeoJson() { + this.updateStore() + // TODO : VALIDATION IF A JSONDICT HAS NOT FEATURES + if (this.geojson.features) { + //* in order to get feature_type properties, the first feature is enough + const { properties, geometry } = this.geojson.features[0]; + this.form.title.value = properties.feature_type; + this.form.geom_type.value = this.translateLabel(geometry.type); + + //* loop properties to create a customForm for each of them + for (const [key, val] of Object.entries(properties)) { + //* check that the property is not a keyword from the backend or map style (to add) + if (!this.reservedKeywords.includes(key)) { + const customForm = { + label: { value: key || "" }, + name: { value: key || "" }, + // todo : increment position + position: { value: 0 }, // * not available in export + field_type: { value: this.transformProperties(val) }, // * guessed from the type + options: { value: null }, // * not available in export + }; + console.log("customForm", customForm, this.transformProperties(val), val); + this.addCustomForm(customForm); + } + } + } + }, }, created() { @@ -271,12 +421,13 @@ export default { .replace(",", "")} )`; this.updateStore(); // * initialize form in store in case this.form would not be modified } + } else if (this.geojson) { + this.importGeoJson(); } }, - beforeDestroy(){ + /* beforeDestroy() { this.RESET(); - - }, + }, */ /* checkform() { let form_idx = $('#id_form-TOTAL_FORMS').val(); for (var i=0; i <= form_idx;i++ ){ diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue index 71a37e9e..d1c9a206 100644 --- a/src/views/project/Project_detail.vue +++ b/src/views/project/Project_detail.vue @@ -193,7 +193,7 @@ <label class="ui" for="json_file"> <i class="ui plus icon"></i> <span class="label" - >Créer un nouveau type de signalement avec un geojson</span + >Créer un nouveau type de signalement à partir d'un GeoJSON</span > </label> <input @@ -210,7 +210,7 @@ <div id="button-import" v-if="filenameToImport.size > 0"> <button :disabled="filenameToImport.size == 0" - @click="importGeoJson" + @click="toNewFeatureType" class="ui fluid teal icon button" > <i class="upload icon"></i> Lancer l'import avec le fichier @@ -469,28 +469,8 @@ export default { data() { return { - choices: { - boolean: "Booléen", - string: "Chaîne de caractères", - date: "Date", - list: "Liste de valeurs", - integer: "Nombre entier", - decimal: "Nombre décimal", - text: "Texte multiligne", - }, - labelCustomForm: [ - 'title', - 'description', - 'status', - 'created_on', - 'updated_on', - 'archived_on', - 'deletion_on', - 'feature_type', - ], - jsonStr: '', - jsonDict: [], - filenameToImport: {name: '', size: 0}, + geojsonImport: [], + filenameToImport: { name: "", size: 0 }, slug: this.$route.params.slug, isModalOpen: false, is_suscriber: false, @@ -502,10 +482,11 @@ export default { }, computed: { ...mapGetters(["project"]), - ...mapState("feature_type", ["form", "customForms", "feature_types"]), + ...mapState("feature_type", ["feature_types"]), ...mapState(["last_comments", "user"]), BASE_URL: () => process.env.VUE_APP_BASE_URL, - last_features: function () { // TODO : Filter les dernières + last_features: function () { + // TODO : Filter les dernières return this.$store.state.feature.features; }, }, @@ -520,98 +501,29 @@ export default { }, methods: { - updateStore() { - // FIRST : TEST AVEC FORM - this.$store.commit("feature_type/UPDATE_FORM", { - title: this.form.title.value, - geom_type: this.form.geom_type.value, + toNewFeatureType() { + this.$router.push({ + name: "ajouter-type-signalement", + params: { geojson: this.geojsonImport }, }); - // AFTER : AND CUSTOM FORM - }, - toNewFeatureType(){ - this.$router.push({ - name: 'ajouter-type-signalement', - params: { geojson: this.jsonDict } - }); - }, - toFormatGeom(value){ - if (value == 'LineString'){ - console.log('LineString', value) - return 'Ligne'; - } - else if(value == 'Polygon'){ - return 'Polygone' - } - return value; - }, - isLabelProperties(value){ - return this.labelCustomForm.includes(value) - }, - searchValue(key){ - if (key in this.choices) - return this.choices[key] - }, - capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1); - }, - importGeoJson() { - if (Object.entries(this.jsonDict).length){ - // TODO : VALIDATION IF A JSONDICT HAS NOT FEATURES - for (const [, value] of Object.entries(this.jsonDict['features'])) { - - this.form.title.value = value['properties']['feature_type'] - this.form.geom_type.value = this.toFormatGeom(value['geometry']['type']) - - // TODO : FILL A ARRAY CUSTOMFORM -> VOIR AVEC TIM - let customForm = { - label : { value : ''}, - name : { value : ''}, - field_type : { value : '', field : {choices : ''}}, - } - // FOR - for (const [k, val] of Object.entries(value['properties'])) { - if (k=='title'){ - customForm['label']['value'] = val - customForm['name']['value'] = val - } - if (! this.isLabelProperties(k)){ - let typeof_val = this.searchValue(typeof val) - customForm['field_type']['value'] = typeof_val - } - //ENDFOR - } - this.addCustomForm(customForm); - } - // GO TO NEW FORM - this.toNewFeatureType() - } - }, - addCustomForm(customForm) { - this.$store.commit("feature_type/ADD_CUSTOM_FORM", customForm); // * create an object with the counter in store }, onFileChange(e) { var files = e.target.files || e.dataTransfer.files; - if (!files.length) - return; - this.filenameToImport = files[0] + if (!files.length) return; + this.filenameToImport = files[0]; // TODO : VALIDATION IF FILE IS JSON - if (this.filenameToImport.size > 0){ - + if (this.filenameToImport.size > 0) { const fr = new FileReader(); - fr.onload = e => { - this.jsonDict = JSON.parse(e.target.result); - this.jsonStr = JSON.stringify(this.jsonDict, null, 2); - } + fr.onload = (e) => { + this.geojsonImport = JSON.parse(e.target.result); + }; fr.readAsText(this.filenameToImport); } }, subsribeProject() { console.log("Subsribe to project"); }, - getFeatureSlug(url) { - return url.split("type-signalement")[1].split("signalement"); - }, }, }; </script> -- GitLab