From 7423cca322b59175220e4f738e13b0676b415467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20P=C3=A9rez=20Sampayo?= Date: Thu, 4 Apr 2024 09:17:18 +0200 Subject: [PATCH] New Context Layer type based on related table (#257) * Context Layer based on related table Related Field on general context layer form WIP Filter related table data Clean django code Clean django code Clean django code Clean django code Clean django code * Fix autoformat changes * Rename fields --- .../ContextLayer/Form/RelatedTableFields.jsx | 114 ++++++++++++++++++ .../pages/Admin/ContextLayer/Form/index.jsx | 49 ++++++-- .../Admin/ContextLayer/StyleConfig/Map.jsx | 2 +- .../Admin/ContextLayer/StyleConfig/index.jsx | 63 ++++------ .../ContextLayer/StyleConfig/layerStyles.js | 57 +++++++++ .../LeftPanel/ContextLayers/Layer.jsx | 16 +++ .../MapLibre/LayerType/RelatedTable.js | 74 ++++++++++++ .../MapLibre/Layers/ContextLayers/index.jsx | 17 +++ .../frontend/src/utils/relatedTable.js | 29 +++++ .../geosight/data/forms/context_layer.py | 20 +++ .../migrations/0104_auto_20240401_1048.py | 69 +++++++++++ .../geosight/data/models/context_layer.py | 56 ++++++++- .../geosight/data/serializer/context_layer.py | 15 +-- 13 files changed, 521 insertions(+), 60 deletions(-) create mode 100644 django_project/frontend/src/pages/Admin/ContextLayer/Form/RelatedTableFields.jsx create mode 100644 django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/layerStyles.js create mode 100644 django_project/frontend/src/pages/Dashboard/MapLibre/LayerType/RelatedTable.js create mode 100644 django_project/geosight/data/migrations/0104_auto_20240401_1048.py diff --git a/django_project/frontend/src/pages/Admin/ContextLayer/Form/RelatedTableFields.jsx b/django_project/frontend/src/pages/Admin/ContextLayer/Form/RelatedTableFields.jsx new file mode 100644 index 000000000..adb709b0c --- /dev/null +++ b/django_project/frontend/src/pages/Admin/ContextLayer/Form/RelatedTableFields.jsx @@ -0,0 +1,114 @@ +/** + * GeoSight is UNICEF's geospatial web-based business intelligence platform. + * + * Contact : geosight-no-reply@unicef.org + * + * .. note:: This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * __author__ = 'francisco.perez@geomati.co' + * __date__ = '20/03/2024' + * __copyright__ = ('Copyright 2024, Unicef') + */ + +import React, { Fragment, useEffect, useState } from 'react'; + + +import { SelectWithSearch } from "../../../../components/Input/SelectWithSearch"; +import WhereInputModal from "../../../../components/SqlQueryGenerator/WhereInputModal"; +import { getRelatedTableFields } from "../../../../utils/relatedTable"; +import { fetchingData } from "../../../../Requests"; +import { dictDeepCopy } from "../../../../utils/main"; + + +/** + * Indicator Form App + * @param {dict} data Data of context layer. + * @param {boolean} checkConfig Checking config. + */ +export default function RelatedTableFields( + { + data, + onSetData + } +) { + const [relatedTableInfo, setRelatedTableInfo] = useState(null) + const [relatedTableData, setRelatedTableData] = useState(null) + + // Loading data + useEffect(() => { + if (!open) { + return + } + if (data.related_table) { + const params = {} + const url_info = `/api/related-table/${data.related_table}` + const url_data = `/api/related-table/${data.related_table}/data` + setRelatedTableInfo(null) + setRelatedTableData(null) + fetchingData( + url_data, params, {}, function (response, error) { + setRelatedTableData(dictDeepCopy(response)) + } + ) + fetchingData( + url_info, params, {}, function (response, error) { + setRelatedTableInfo(dictDeepCopy(response)) + } + ) + } + }, [data.related_table]) + + const relatedFields = relatedTableInfo && relatedTableData ? getRelatedTableFields(relatedTableInfo, relatedTableData) : [] + return ( +
+
+
Latitude Field
+
+ { + onSetData({ ...data, latitude_field: evt }) + }} + options={relatedFields.filter(rf => rf.type === 'number').map(rf => rf.name)} + className='FilterInput' /> +
+
+
+
Longitude Field
+
+ { + onSetData({ ...data, longitude_field: evt }) + }} + options={relatedFields.filter(rf => rf.type === 'number').map(rf => rf.name)} + className='FilterInput' /> +
+
+
+
Datetime field
+
+ { + onSetData({ ...data, datetime_field: evt }) + }} + options={relatedFields.filter(rf => rf.type === 'date').map(rf => rf.name)} + className='FilterInput' /> +
+
+ + { + onSetData({ ...data, query: evt }) + }} + title={"Filter the Data"} + /> +
+ ) +} \ No newline at end of file diff --git a/django_project/frontend/src/pages/Admin/ContextLayer/Form/index.jsx b/django_project/frontend/src/pages/Admin/ContextLayer/Form/index.jsx index 489a0d4b0..d1bb580b5 100644 --- a/django_project/frontend/src/pages/Admin/ContextLayer/Form/index.jsx +++ b/django_project/frontend/src/pages/Admin/ContextLayer/Form/index.jsx @@ -21,8 +21,9 @@ import { render } from '../../../../app'; import { store } from '../../../../store/admin'; import { SaveButton } from "../../../../components/Elements/Button"; import Admin, { pageNames } from '../../index'; -import { AdminForm } from '../../Components/AdminForm' -import StyleConfig from '../StyleConfig' +import { AdminForm } from '../../Components/AdminForm'; +import StyleConfig from '../StyleConfig'; +import RelatedTableFields from './RelatedTableFields'; import DjangoTemplateForm from "../../Components/AdminForm/DjangoTemplateForm"; import { resourceActions } from "../List"; import { dictDeepCopy } from "../../../../utils/main"; @@ -30,6 +31,7 @@ import { dictDeepCopy } from "../../../../utils/main"; import './style.scss'; let currentArcGis = null +let currentRelatedTable = null let init = false /** * Context Layer Form App @@ -87,21 +89,36 @@ export default function ContextLayerForm() { $('*[name="label_styles"]').val(JSON.stringify(newData['label_styles'])) $('*[name="data_fields"]').val(JSON.stringify(newData['data_fields'])) $('*[name="styles"]').val(JSON.stringify(newData['styles'])) + $('*[name="latitude_field"]').val(newData['latitude_field']) + $('*[name="longitude_field"]').val(newData['longitude_field']) + $('*[name="query"]').val(newData['query']) + $('*[name="datetime_field"]').val(newData['datetime_field']) } } const typeChange = (value) => { if (value === 'ARCGIS') { $('div[data-wrapper-name="arcgis_config"]').show() - } else { + $('div[data-wrapper-name="related_table"]').hide() + } else if (value === 'Related Table') { $('div[data-wrapper-name="arcgis_config"]').hide() + $('div[data-wrapper-name="token"]').hide() + $('div[data-wrapper-name="username"]').hide() + $('div[data-wrapper-name="password"]').hide() + $('div[data-wrapper-name="url"]').hide() + $('div[data-wrapper-name="related_table"]').show() + } + else { + $('div[data-wrapper-name="arcgis_config"]').hide() + $('div[data-wrapper-name="related_table"]').hide() + $('div[data-wrapper-name="url"]').show() } setData({ ...data, layer_type: value }) } const arcGisConfigChange = (value) => { currentArcGis = value - if (!value) { + if (!value && data.layer_type === 'ARCGIS') { $('div[data-wrapper-name="token"]').show() $('div[data-wrapper-name="username"]').show() $('div[data-wrapper-name="password"]').show() @@ -116,6 +133,16 @@ export default function ContextLayerForm() { }) } } + + const relatedTableConfigChange = (value) => { + currentRelatedTable = value + if (value) { + setData({ + ...data, + related_table: value + }) + } + } return ( @@ -181,6 +208,12 @@ export default function ContextLayerForm() { onChange={evt => { }} /> + {data.layer_type === 'Related Table' ? + : undefined + } ), 'Preview': ( @@ -192,8 +225,8 @@ export default function ContextLayerForm() { useOverrideLabel={false} /> ), - 'Fields':
, - 'Label':
, + 'Fields':
, + 'Label':
, }} /> diff --git a/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/Map.jsx b/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/Map.jsx index cc7371d61..6694e5589 100644 --- a/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/Map.jsx +++ b/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/Map.jsx @@ -87,7 +87,7 @@ export default function MapConfig({ data, layerInput }) { // When layer input changed, remove from map useEffect(() => { if (map) { - const id = 'Context-Layer' + const id = data.id ? `context-layer-${data.id}` : 'context-layer' contextLayerRendering(id, data, layerInput, map) } }, [map, layerInput]); diff --git a/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/index.jsx b/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/index.jsx index 2e06c711e..3fae605b5 100644 --- a/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/index.jsx +++ b/django_project/frontend/src/pages/Admin/ContextLayer/StyleConfig/index.jsx @@ -18,6 +18,7 @@ import MapConfig from './Map' import ArcgisConfig from './Arcgis' import { useDispatch } from "react-redux"; import { getLayer } from '../../../Dashboard/LeftPanel/ContextLayers/Layer' +import { defaultPointStyle, defaultVectorTyleStyle } from './layerStyles'; import './style.scss'; @@ -58,6 +59,16 @@ export default function StyleConfig( getLayer(data, setLayer, setLegend, setError, dispatch, setLayerDataClass); }, [data, tab]); + useEffect(() => { + if (!data.styles && data.layer_type === 'Related Table') { + setData({ + ...data, + styles: JSON.stringify(defaultPointStyle, null, 4), + override_style: true + }) + } + }, [data]); + useEffect(() => { if (layerDataClass) { setLayerData(layerDataClass) @@ -125,15 +136,18 @@ export default function StyleConfig( }
- +
: "" } { - data.layer_type === 'Vector Tile' ? <> + data.layer_type === 'Vector Tile' || data.layer_type === 'Related Table' ? <>
Layers
@@ -147,37 +161,10 @@ export default function StyleConfig(