Skip to content

Commit

Permalink
4033 - NationalData-DataSources Cycle 2029 (#4035)
Browse files Browse the repository at this point in the history
* 4033 - NationalData-DataSources Cycle 2029: data/metadata clone (#4034)

* 4033 - Do not import dataSources.text prop / Fix clone cycle props

* 4033 - improve Objects.isEmpty + unit test

* 4033 - fix DataSources grid

* 4033 - Do not import dataSources.text value

* 4033 - Dashboard cycle clone (#4039)

* fix dashboard clone

* Disable region level dashboard on all but fra 2020

* 4033 - NationalData->DataSources Cycle 2029 - Labels + clone props fix (#4048)

* 4033: Fix DataSources labels

* 4033: Use Objects.isNil to check props to clone

---------

Co-authored-by: Miró Sorja <[email protected]>
  • Loading branch information
minotogna and sorja authored Oct 21, 2024
1 parent 9731795 commit 89c7f81
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 79 deletions.
4 changes: 2 additions & 2 deletions src/client/pages/CountryHome/FraHome/hooks/useSections.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export const useSections = (): Array<Section> => {

if (!cycle) return null

const isFra2025 = assessment.props.name === AssessmentNames.fra && cycle.name === '2025'
const showOverview = !isFra2025 || Areas.isISOCountry(countryIso)
const isFra2020 = assessment.props.name === AssessmentNames.fra && cycle.name === '2020'
const showOverview = isFra2020 || Areas.isISOCountry(countryIso)

if (showOverview) {
sections.push({ name: SectionNames.Country.Home.overview, component: Overview })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import './DataSources.scss'
import React from 'react'
import { useTranslation } from 'react-i18next'

import { Objects } from 'utils/objects'

import { CommentableDescriptionName } from 'meta/assessment'
import { NationalDataDescription } from 'meta/assessment/description'

Expand Down Expand Up @@ -30,7 +32,7 @@ export const DataSources: React.FC<Props> = (props: Props) => {
const { nationalData } = props

const { t } = useTranslation()
const { assessmentName, cycleName } = useCycleRouteParams()
const { assessmentName } = useCycleRouteParams()
const { sectionName } = useSectionContext()
const { dataSources, text } = useDataSourcesData({ sectionName })
const { dataSourcesLinked } = useGetDataSourcesLinked({ nationalData, sectionName })
Expand All @@ -39,8 +41,8 @@ export const DataSources: React.FC<Props> = (props: Props) => {
const editable = useIsDescriptionEditable({ sectionName, name })
const { empty } = useDescriptionErrorState({ name, sectionName })

const renderGrid = Boolean(dataSources?.length || dataSourcesLinked?.length || editable)
const keyPrefix = `${assessmentName}.${cycleName}.description.dataSource`
const renderGrid = Boolean(!Objects.isEmpty(dataSources) || !Objects.isEmpty(dataSourcesLinked) || editable)
const keyPrefix = `${assessmentName}.description.dataSource`

return (
<DataGrid className="description" withActions={canEdit}>
Expand Down
1 change: 0 additions & 1 deletion src/i18n/resources/en/2020.json

This file was deleted.

11 changes: 0 additions & 11 deletions src/i18n/resources/en/2025.json

This file was deleted.

21 changes: 9 additions & 12 deletions src/i18n/resources/en/fra.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,4 @@
{
"2020": {},
"2025": {
"description": {
"dataSource": {
"comments": "$t(dataSource.comments)",
"referenceToTataSource": "$t(dataSource.referenceToTataSource)",
"typeOfDataSource": "$t(dataSource.typeOfDataSource)",
"variable": "$t(dataSource.fraVariable)",
"yearForDataSource": "$t(dataSource.yearForDataSource)"
}
}
},
"area100Ha": "Area (1000 ha)",
"area100HaYear": "Area (1000 ha/year)",
"areaAffectedByFire": {
Expand Down Expand Up @@ -94,6 +82,15 @@
"whatIsDefinition": "What is the national definition of \"Degraded forest\"?",
"yearOfLatestAssessment": "Year of latest assessment"
},
"description": {
"dataSource": {
"comments": "$t(dataSource.comments)",
"referenceToTataSource": "$t(dataSource.referenceToTataSource)",
"typeOfDataSource": "$t(dataSource.typeOfDataSource)",
"variable": "$t(dataSource.fraVariable)",
"yearForDataSource": "$t(dataSource.yearForDataSource)"
}
},
"designatedManagementObjective": {
"designatedManagementObjective2025": "$t(designatedManagementObjective.designatedManagementObjective)",
"noDesignation": "No designation",
Expand Down
24 changes: 16 additions & 8 deletions src/meta/assessment/cols.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TFunction } from 'i18next'
import { Objects } from 'utils/objects'

import { Cycle } from 'meta/assessment/cycle'
import { Labels } from 'meta/assessment/labels'
Expand All @@ -12,16 +13,23 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; col: Col })
const { uuid: cycleSourceUuid } = cycleSource
const { uuid: cycleTargetUuid } = cycleTarget

const _props: Col['props'] = { ...col.props }
const _props: Col['props'] = Objects.cloneDeep(col.props)
_props.cycles.push(cycleTargetUuid)

if (_props.calculateFn?.[cycleSourceUuid]) _props.calculateFn[cycleTargetUuid] = _props.calculateFn[cycleSourceUuid]
if (_props.classNames?.[cycleSourceUuid]) _props.classNames[cycleTargetUuid] = _props.classNames[cycleSourceUuid]
if (_props.labels?.[cycleSourceUuid]) _props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid]
if (_props.linkedNodes?.[cycleSourceUuid]) _props.linkedNodes[cycleTargetUuid] = _props.linkedNodes[cycleSourceUuid]
if (_props.style?.[cycleSourceUuid]) _props.style[cycleTargetUuid] = _props.style[cycleSourceUuid]
if (_props.validateFns?.[cycleSourceUuid]) _props.validateFns[cycleTargetUuid] = _props.validateFns[cycleSourceUuid]
if (_props.variableNo?.[cycleSourceUuid]) _props.variableNo[cycleTargetUuid] = _props.variableNo[cycleSourceUuid]
if (!Objects.isNil(_props.calculateFn?.[cycleSourceUuid]))
_props.calculateFn[cycleTargetUuid] = Objects.cloneDeep(_props.calculateFn[cycleSourceUuid])
if (!Objects.isNil(_props.classNames?.[cycleSourceUuid]))
_props.classNames[cycleTargetUuid] = Objects.cloneDeep(_props.classNames[cycleSourceUuid])
if (!Objects.isNil(_props.labels?.[cycleSourceUuid]))
_props.labels[cycleTargetUuid] = Objects.cloneDeep(_props.labels[cycleSourceUuid])
if (!Objects.isNil(_props.linkedNodes?.[cycleSourceUuid]))
_props.linkedNodes[cycleTargetUuid] = Objects.cloneDeep(_props.linkedNodes[cycleSourceUuid])
if (!Objects.isNil(_props.style?.[cycleSourceUuid]))
_props.style[cycleTargetUuid] = Objects.cloneDeep(_props.style[cycleSourceUuid])
if (!Objects.isNil(_props.validateFns?.[cycleSourceUuid]))
_props.validateFns[cycleTargetUuid] = Objects.cloneDeep(_props.validateFns[cycleSourceUuid])
if (!Objects.isNil(_props.variableNo?.[cycleSourceUuid]))
_props.variableNo[cycleTargetUuid] = Objects.cloneDeep(_props.variableNo[cycleSourceUuid])

return _props
}
Expand Down
27 changes: 17 additions & 10 deletions src/meta/assessment/rows.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Objects } from 'utils/objects'

import { Cycle } from 'meta/assessment/cycle'
import { Row } from 'meta/assessment/row'

Expand All @@ -7,18 +9,23 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; row: Row })
const { uuid: cycleSourceUuid } = cycleSource
const { uuid: cycleTargetUuid } = cycleTarget

const _props: Row['props'] = { ...row.props }
const _props: Row['props'] = Objects.cloneDeep(row.props)
_props.cycles.push(cycleTargetUuid)

if (_props.calculateFn?.[cycleSourceUuid]) _props.calculateFn[cycleTargetUuid] = _props.calculateFn[cycleSourceUuid]
if (_props.calculateIf?.[cycleSourceUuid]) _props.calculateIf[cycleTargetUuid] = _props.calculateIf[cycleSourceUuid]
if (_props.chart?.[cycleSourceUuid]) _props.chart[cycleTargetUuid] = _props.chart[cycleSourceUuid]
if (_props.excludeFromDataExport?.[cycleSourceUuid])
_props.excludeFromDataExport[cycleTargetUuid] = _props.excludeFromDataExport[cycleSourceUuid]
if (_props.linkToSection?.[cycleSourceUuid])
_props.linkToSection[cycleTargetUuid] = _props.linkToSection[cycleSourceUuid]
if (_props.validateFns?.[cycleSourceUuid]) _props.validateFns[cycleTargetUuid] = _props.validateFns[cycleSourceUuid]
if (_props.withReview?.[cycleSourceUuid]) _props.withReview[cycleTargetUuid] = _props.withReview[cycleSourceUuid]
if (!Objects.isNil(_props.calculateFn?.[cycleSourceUuid]))
_props.calculateFn[cycleTargetUuid] = Objects.cloneDeep(_props.calculateFn[cycleSourceUuid])
if (!Objects.isNil(_props.calculateIf?.[cycleSourceUuid]))
_props.calculateIf[cycleTargetUuid] = Objects.cloneDeep(_props.calculateIf[cycleSourceUuid])
if (!Objects.isNil(_props.chart?.[cycleSourceUuid]))
_props.chart[cycleTargetUuid] = Objects.cloneDeep(_props.chart[cycleSourceUuid])
if (!Objects.isNil(_props.excludeFromDataExport?.[cycleSourceUuid]))
_props.excludeFromDataExport[cycleTargetUuid] = Objects.cloneDeep(_props.excludeFromDataExport[cycleSourceUuid])
if (!Objects.isNil(_props.linkToSection?.[cycleSourceUuid]))
_props.linkToSection[cycleTargetUuid] = Objects.cloneDeep(_props.linkToSection[cycleSourceUuid])
if (!Objects.isNil(_props.validateFns?.[cycleSourceUuid]))
_props.validateFns[cycleTargetUuid] = Objects.cloneDeep(_props.validateFns[cycleSourceUuid])
if (!Objects.isNil(_props.withReview?.[cycleSourceUuid]))
_props.withReview[cycleTargetUuid] = Objects.cloneDeep(_props.withReview[cycleSourceUuid])

return _props
}
Expand Down
10 changes: 7 additions & 3 deletions src/meta/assessment/sections.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Objects } from 'utils/objects'

import { Cycle } from 'meta/assessment/cycle'
import { Section } from 'meta/assessment/section'

Expand All @@ -7,11 +9,13 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; section: Se
const { uuid: cycleSourceUuid } = cycleSource
const { uuid: cycleTargetUuid } = cycleTarget

const _props: Section['props'] = { ...section.props }
const _props: Section['props'] = Objects.cloneDeep(section.props)
_props.cycles.push(cycleTargetUuid)

if (_props.anchors?.[cycleSourceUuid]) _props.anchors[cycleTargetUuid] = _props.anchors[cycleSourceUuid]
if (_props.labels?.[cycleSourceUuid]) _props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid]
if (!Objects.isNil(_props.anchors?.[cycleSourceUuid]))
_props.anchors[cycleTargetUuid] = Objects.cloneDeep(_props.anchors[cycleSourceUuid])
if (!Objects.isNil(_props.labels?.[cycleSourceUuid]))
_props.labels[cycleTargetUuid] = Objects.cloneDeep(_props.labels[cycleSourceUuid])

return _props
}
Expand Down
17 changes: 12 additions & 5 deletions src/meta/assessment/subSections.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Objects } from 'utils/objects'

import { Cycle } from 'meta/assessment/cycle'
import { Section, SubSection } from 'meta/assessment/section'
import { Sections } from 'meta/assessment/sections'
Expand All @@ -12,12 +14,17 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; subSection:
const section = subSection as Section

const _props: SubSection['props'] = Sections.cloneProps({ cycleSource, cycleTarget, section }) as SubSection['props']
_props.cycles.push(cycleTargetUuid)

if (_props.descriptions?.[cycleSourceUuid])
_props.descriptions[cycleTargetUuid] = _props.descriptions[cycleSourceUuid]
if (_props.hidden?.[cycleSourceUuid]) _props.hidden[cycleTargetUuid] = _props.hidden[cycleSourceUuid]
if (_props.hints?.[cycleSourceUuid]) _props.hints[cycleTargetUuid] = _props.hints[cycleSourceUuid]
const descriptionsSource = Objects.cloneDeep(_props.descriptions?.[cycleSourceUuid])
if (!Objects.isEmpty(descriptionsSource)) {
// delete nationalData->dataSources->text needed only in FRA 2025
Objects.unset(descriptionsSource, ['nationalData', 'dataSources', 'text'])
_props.descriptions[cycleTargetUuid] = descriptionsSource
}
if (!Objects.isNil(_props.hidden?.[cycleSourceUuid]))
_props.hidden[cycleTargetUuid] = Objects.cloneDeep(_props.hidden[cycleSourceUuid])
if (!Objects.isNil(_props.hints?.[cycleSourceUuid]))
_props.hints[cycleTargetUuid] = Objects.cloneDeep(_props.hints[cycleSourceUuid])

return _props
}
Expand Down
11 changes: 7 additions & 4 deletions src/meta/assessment/tableSections.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Objects } from 'utils/objects'

import { Cycle } from 'meta/assessment/cycle'
import { TableSection } from 'meta/assessment/tableSection'

Expand All @@ -11,12 +13,13 @@ const cloneProps = (props: {
const { uuid: cycleSourceUuid } = cycleSource
const { uuid: cycleTargetUuid } = cycleTarget

const _props: TableSection['props'] = { ...tableSection.props }
const _props: TableSection['props'] = Objects.cloneDeep(tableSection.props)
_props.cycles.push(cycleTargetUuid)

if (_props.descriptions?.[cycleSourceUuid])
_props.descriptions[cycleTargetUuid] = _props.descriptions[cycleSourceUuid]
if (_props.labels?.[cycleSourceUuid]) _props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid]
if (!Objects.isNil(_props.descriptions?.[cycleSourceUuid]))
_props.descriptions[cycleTargetUuid] = Objects.cloneDeep(_props.descriptions[cycleSourceUuid])
if (!Objects.isNil(_props.labels?.[cycleSourceUuid]))
Objects.cloneDeep((_props.labels[cycleTargetUuid] = _props.labels[cycleSourceUuid]))

return _props
}
Expand Down
23 changes: 13 additions & 10 deletions src/meta/assessment/tables.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Objects } from 'utils/objects'

import { Cycle } from 'meta/assessment/cycle'
import { Table } from 'meta/assessment/table'

Expand All @@ -7,18 +9,19 @@ const cloneProps = (props: { cycleSource: Cycle; cycleTarget: Cycle; table: Tabl
const { uuid: cycleSourceUuid } = cycleSource
const { uuid: cycleTargetUuid } = cycleTarget

const _props: Table['props'] = { ...table.props }
const _props: Table['props'] = Objects.cloneDeep(table.props)
_props.cycles.push(cycleTargetUuid)

if (_props.cellsExportAlways?.[cycleSourceUuid])
_props.cellsExportAlways[cycleTargetUuid] = _props.cellsExportAlways[cycleSourceUuid]
if (_props.columnNames?.[cycleSourceUuid]) _props.columnNames[cycleTargetUuid] = _props.columnNames[cycleSourceUuid]
if (_props.columnsExport?.[cycleSourceUuid])
_props.columnsExport[cycleTargetUuid] = _props.columnsExport[cycleSourceUuid]
if (_props.columnsExportAlways?.[cycleSourceUuid])
_props.columnsExportAlways[cycleTargetUuid] = _props.columnsExportAlways[cycleSourceUuid]
if (_props.disableErrorMessage?.[cycleSourceUuid])
_props.disableErrorMessage[cycleTargetUuid] = _props.disableErrorMessage[cycleSourceUuid]
if (!Objects.isNil(_props.cellsExportAlways?.[cycleSourceUuid]))
_props.cellsExportAlways[cycleTargetUuid] = Objects.cloneDeep(_props.cellsExportAlways[cycleSourceUuid])
if (!Objects.isNil(_props.columnNames?.[cycleSourceUuid]))
_props.columnNames[cycleTargetUuid] = Objects.cloneDeep(_props.columnNames[cycleSourceUuid])
if (!Objects.isNil(_props.columnsExport?.[cycleSourceUuid]))
_props.columnsExport[cycleTargetUuid] = Objects.cloneDeep(_props.columnsExport[cycleSourceUuid])
if (!Objects.isNil(_props.columnsExportAlways?.[cycleSourceUuid]))
_props.columnsExportAlways[cycleTargetUuid] = Objects.cloneDeep(_props.columnsExportAlways[cycleSourceUuid])
if (!Objects.isNil(_props.disableErrorMessage?.[cycleSourceUuid]))
_props.disableErrorMessage[cycleTargetUuid] = Objects.cloneDeep(_props.disableErrorMessage[cycleSourceUuid])

return _props
}
Expand Down
23 changes: 20 additions & 3 deletions src/server/controller/assessment/cloneCycle/_cloneData.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import { CommentableDescriptionName } from 'meta/assessment'
import { NodeExtType } from 'meta/nodeExt'

import { CloneProps } from 'server/controller/assessment/cloneCycle/types'
import { BaseProtocol, Schemas } from 'server/db'

Expand All @@ -16,8 +19,16 @@ export const cloneData = async (props: CloneProps, client: BaseProtocol): Promis
select uuid, country_iso, row_uuid, col_uuid, value
from ${schemaCycleSource}.node;
insert into ${schemaCycleTarget}.node_ext (country_iso, parent_uuid, props, type, uuid, value)
select country_iso, parent_uuid, props, type, uuid, value
insert into ${schemaCycleTarget}.node_ext (
country_iso, parent_uuid, props, type, uuid, value
)
select
country_iso, parent_uuid, props, type, uuid,
case
when type = '${NodeExtType.dashboard}' then
replace(value::text, '${cycleSource.uuid}', '${cycleTarget.uuid}')::jsonb
else value
end as value
from ${schemaCycleSource}.node_ext;
insert into ${schemaCycleTarget}.node_values_estimation (uuid, country_iso, table_uuid, created_at, method, variables)
Expand All @@ -39,7 +50,13 @@ export const cloneData = async (props: CloneProps, client: BaseProtocol): Promis
from ${schemaCycleSource}.original_data_point;
insert into ${schemaCycleTarget}.descriptions (country_iso, section_name, name, value)
select country_iso, section_name, name, value
select country_iso,
section_name,
name,
case
when name = '${CommentableDescriptionName.dataSources}' then jsonb_delete(value, 'text')
else value
end -- // delete nationalData->dataSources->text needed only in FRA 2025
from ${schemaCycleSource}.descriptions;
insert into ${schemaCycleTarget}.repository (uuid, country_iso, file_uuid, link, props)
Expand Down
3 changes: 1 addition & 2 deletions src/utils/objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import * as isEqual from 'lodash.isequal'
// @ts-ignore
import * as isFunction from 'lodash.isfunction'
// @ts-ignore
import * as isNil from 'lodash.isnil'
// @ts-ignore
import * as merge from 'lodash.merge'
// @ts-ignore
import * as pick from 'lodash.pick'
Expand All @@ -17,6 +15,7 @@ import { camelize } from './camelize'
import { getDiff } from './getDiff'
import { getInPath } from './getInPath'
import { isEmpty } from './isEmpty'
import { isNil } from './isNil'
import { propertyOf } from './propertyOf'
import { setInPath } from './setInPath'

Expand Down
30 changes: 30 additions & 0 deletions src/utils/objects/isEmpty.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { isEmpty } from 'utils/objects/isEmpty'

const tests: Array<{ empty: boolean; value: any }> = [
{ empty: true, value: undefined },
{ empty: true, value: null },
{ empty: true, value: '' },
{ empty: true, value: ' ' },
{ empty: false, value: ' fdsafd a' },
{ empty: false, value: 'fdsafd ' },
{ empty: false, value: 'fdsafd' },
{ empty: false, value: { a: '' } },
{ empty: false, value: { a: '', b: '' } },
{ empty: false, value: { a: undefined } },
{ empty: true, value: {} },
{ empty: false, value: [1] },
{ empty: false, value: [1, 2, 3] },
{ empty: true, value: [] },
{ empty: false, value: [undefined] },
{ empty: false, value: [null] },
{ empty: false, value: [undefined, null] },
]

describe('isEmpty', () => {
tests.forEach(({ empty, value }) => {
test(`${JSON.stringify(value)}-${empty}`, () => {
const result = isEmpty(value)
expect(result).toEqual(empty)
})
})
})
10 changes: 5 additions & 5 deletions src/utils/objects/isEmpty.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { isNil } from 'utils/objects/isNil'

/**
* Determines if the specified value is null, empty string, NaN, empty object or empty array.
*
* @param {any} value - Value to
* @returns {boolean} True if the specified value is empty, false otherwise.
*/
export const isEmpty = (value: any): boolean =>
value === undefined ||
value === null ||
value === '' ||
isNil(value) ||
Number.isNaN(value) ||
(value instanceof Object && Object.entries(value).length === 0) ||
(Array.isArray(value) && value.length === 0)
(typeof value === 'object' && Object.keys(value).length === 0) ||
(typeof value === 'string' && value.trim().length === 0)
4 changes: 4 additions & 0 deletions src/utils/objects/isNil.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// @ts-expect-error
import * as isNil from 'lodash.isnil'

export { isNil }

0 comments on commit 89c7f81

Please sign in to comment.