diff --git a/src/components/Inputs/AtomicMode.js b/src/components/Inputs/AtomicMode.js index e0165a2f2..468061c8b 100644 --- a/src/components/Inputs/AtomicMode.js +++ b/src/components/Inputs/AtomicMode.js @@ -4,7 +4,7 @@ import { RadioGroupField } from '../index.js' const atomicModeOptions = [ { value: 'ALL', label: i18n.t('Do not import') }, - { value: 'NONE', label: i18n.t('Import') }, + { value: 'OBJECT', label: i18n.t('Import') }, ] const defaultAtomicModeOption = atomicModeOptions[0].value diff --git a/src/components/Inputs/EndDate.js b/src/components/Inputs/EndDate.js index 164f3b097..0ddd4656c 100644 --- a/src/components/Inputs/EndDate.js +++ b/src/components/Inputs/EndDate.js @@ -1,5 +1,6 @@ import i18n from '@dhis2/d2-i18n' import { hasValue, composeValidators } from '@dhis2/ui' +import PropTypes from 'prop-types' import React from 'react' import { DATE_VALIDATOR } from '../DatePicker/DatePickerField.js' import { DatePickerField } from '../index.js' @@ -9,13 +10,18 @@ const DATATEST = 'input-end-date' const LABEL = i18n.t('End date') const VALIDATOR = composeValidators(hasValue, DATE_VALIDATOR) -const EndDate = () => ( +const EndDate = ({ name, label }) => ( ) +EndDate.propTypes = { + label: PropTypes.string, + name: PropTypes.string, +} + export { EndDate } diff --git a/src/components/Inputs/FollowUpStatus.js b/src/components/Inputs/FollowUpStatus.js index d09234ab0..1fc2383d0 100644 --- a/src/components/Inputs/FollowUpStatus.js +++ b/src/components/Inputs/FollowUpStatus.js @@ -5,12 +5,12 @@ import { RadioGroupField } from '../index.js' const followUpStatusOptions = [ { value: 'ALL', label: i18n.t('All') }, - { value: 'TRUE', label: i18n.t('Marked for follow-up') }, - { value: 'FALSE', label: i18n.t('Not marked for follow-up') }, + { value: 'true', label: i18n.t('Marked for follow-up') }, + { value: 'false', label: i18n.t('Not marked for follow-up') }, ] const defaultFollowUpStatusOption = followUpStatusOptions[0].value -const NAME = 'followUpStatus' +const NAME = 'followup' const DATATEST = 'input-follow-up-status' const LABEL = i18n.t('Include only entities with follow-up status') diff --git a/src/components/Inputs/LastUpdatedDuration.js b/src/components/Inputs/LastUpdatedDuration.js index 08b24dd51..21fc074c3 100644 --- a/src/components/Inputs/LastUpdatedDuration.js +++ b/src/components/Inputs/LastUpdatedDuration.js @@ -5,7 +5,7 @@ import React from 'react' import { DURATION_VALIDATOR } from '../Duration/DurationField.js' import { DurationField } from '../index.js' -const NAME = 'lastUpdatedDuration' +const NAME = 'updatedWithin' const DATATEST = 'input-last-updated-duration' const LABEL = i18n.t('Last updated duration') const VALIDATOR = composeValidators(hasValue, DURATION_VALIDATOR) diff --git a/src/components/Inputs/LastUpdatedEndDate.js b/src/components/Inputs/LastUpdatedEndDate.js index f765fc732..0bd558b66 100644 --- a/src/components/Inputs/LastUpdatedEndDate.js +++ b/src/components/Inputs/LastUpdatedEndDate.js @@ -5,7 +5,7 @@ import React from 'react' import { OPTIONAL_DATE_VALIDATOR } from '../DatePicker/DatePickerField.js' import { DatePickerField } from '../index.js' -const NAME = 'lastUpdatedEndDate' +const NAME = 'updatedBefore' const DATATEST = 'input-last-updated-end-date' const LABEL = i18n.t('Last updated end date') const VALIDATOR = composeValidators(OPTIONAL_DATE_VALIDATOR) diff --git a/src/components/Inputs/LastUpdatedStartDate.js b/src/components/Inputs/LastUpdatedStartDate.js index 9a99ae208..d38735480 100644 --- a/src/components/Inputs/LastUpdatedStartDate.js +++ b/src/components/Inputs/LastUpdatedStartDate.js @@ -5,7 +5,7 @@ import React from 'react' import { OPTIONAL_DATE_VALIDATOR } from '../DatePicker/DatePickerField.js' import { DatePickerField } from '../index.js' -const NAME = 'lastUpdatedStartDate' +const NAME = 'updatedAfter' const DATATEST = 'input-last-updated-start-date' const LABEL = i18n.t('Last updated start date') const VALIDATOR = composeValidators(OPTIONAL_DATE_VALIDATOR) diff --git a/src/components/Inputs/ProgramEndDate.js b/src/components/Inputs/ProgramEndDate.js index e2f65738d..f1dcd137a 100644 --- a/src/components/Inputs/ProgramEndDate.js +++ b/src/components/Inputs/ProgramEndDate.js @@ -5,7 +5,7 @@ import React from 'react' import { OPTIONAL_DATE_VALIDATOR } from '../DatePicker/DatePickerField.js' import { DatePickerField } from '../index.js' -const NAME = 'programEndDate' +const NAME = 'enrollmentEnrolledBefore' const DATATEST = 'input-program-end-date' const LABEL = i18n.t('End date') const VALIDATOR = composeValidators(OPTIONAL_DATE_VALIDATOR) diff --git a/src/components/Inputs/ProgramStartDate.js b/src/components/Inputs/ProgramStartDate.js index 35e035fb8..7757048c4 100644 --- a/src/components/Inputs/ProgramStartDate.js +++ b/src/components/Inputs/ProgramStartDate.js @@ -5,7 +5,7 @@ import React from 'react' import { OPTIONAL_DATE_VALIDATOR } from '../DatePicker/DatePickerField.js' import { DatePickerField } from '../index.js' -const NAME = 'programStartDate' +const NAME = 'enrollmentEnrolledAfter' const DATATEST = 'input-program-start-date' const LABEL = i18n.t('Start date') const VALIDATOR = composeValidators(OPTIONAL_DATE_VALIDATOR) diff --git a/src/components/Inputs/StartDate.js b/src/components/Inputs/StartDate.js index 8b77968f8..23c41904e 100644 --- a/src/components/Inputs/StartDate.js +++ b/src/components/Inputs/StartDate.js @@ -1,5 +1,6 @@ import i18n from '@dhis2/d2-i18n' import { hasValue, composeValidators } from '@dhis2/ui' +import PropTypes from 'prop-types' import React from 'react' import { DATE_VALIDATOR } from '../DatePicker/DatePickerField.js' import { DatePickerField } from '../index.js' @@ -9,13 +10,17 @@ const DATATEST = 'input-start-date' const LABEL = i18n.t('Start date') const VALIDATOR = composeValidators(hasValue, DATE_VALIDATOR) -const StartDate = () => ( +const StartDate = ({ name, label }) => ( ) +StartDate.propTypes = { + label: PropTypes.string, + name: PropTypes.string, +} export { StartDate } diff --git a/src/components/Inputs/TEITypeFilter.js b/src/components/Inputs/TEITypeFilter.js index f440f0478..822e9956f 100644 --- a/src/components/Inputs/TEITypeFilter.js +++ b/src/components/Inputs/TEITypeFilter.js @@ -3,7 +3,6 @@ import React from 'react' import { RadioGroupField } from '../index.js' const teiTypeFilterOptions = [ - { value: 'NONE', label: i18n.t('None') }, { value: 'PROGRAM', label: i18n.t('Program') }, { value: 'TE', label: i18n.t('Tracked entity type') }, ] diff --git a/src/components/JobSummary/SingleSummary/SingleSummary.js b/src/components/JobSummary/SingleSummary/SingleSummary.js index f54ae56cd..001bf54d1 100644 --- a/src/components/JobSummary/SingleSummary/SingleSummary.js +++ b/src/components/JobSummary/SingleSummary/SingleSummary.js @@ -20,6 +20,7 @@ const SingleSummary = ({ status, description, conflicts, + validationReport, id, }) => (
@@ -29,7 +30,7 @@ const SingleSummary = ({ name="summary" > <> - {status && ( + {status && description && ( - {importCount.imported} - {importCount.deleted} - {importCount.ignored} - {importCount.updated} - {importCount.total} + + {importCount?.imported ?? '0'} + + {importCount?.deleted} + {importCount?.ignored} + {importCount?.updated} + {importCount?.total} + {!!validationReport?.errorReports?.length && ( + + + + + {i18n.t('UID')} + + {i18n.t('Error Code')} + + {i18n.t('Message')} + + {i18n.t('Tracker Type')} + + {/* {i18n.t('')} */} + + + + {validationReport.errorReports.map((c, i) => ( + + {c.uid} + + + {c.warningCode ?? c.errorCode} + + + {c.message} + {c.trackerType} + + ))} + +
+
+ )} {conflicts && ( { importCount={importCount} status={summary.status} description={summary.description} + validationReport={summary.validationReport} conflicts={ summary.conflicts && (summary.conflicts.length || null) && diff --git a/src/components/JobSummary/__test__/__snapshots__/Summary.test.js.snap b/src/components/JobSummary/__test__/__snapshots__/Summary.test.js.snap index 08e40e253..92bad399b 100644 --- a/src/components/JobSummary/__test__/__snapshots__/Summary.test.js.snap +++ b/src/components/JobSummary/__test__/__snapshots__/Summary.test.js.snap @@ -172,51 +172,6 @@ exports[`different job type summaries matches snapshot - EVENT_IMPORT 1`] = ` > Summary - - - - - - - - - - - - -
- Status - - Description -
- ERROR - -
Summary -
- - - - - - - - - - - -
- Status - - Description -
- OK - -
+ > + 0 +
, } const teiImportPage = { - name: i18n.t('TEI import'), + name: i18n.t('Tracked entity import'), code: 'tei-import', path: '/import/tei', icon: , @@ -88,7 +88,7 @@ const metadataExportPage = { } const teiExportPage = { - name: i18n.t('TEI export'), + name: i18n.t('Tracked entity export'), code: 'tei-export', path: '/export/tei', icon: , diff --git a/src/hooks/useTasks.js b/src/hooks/useTasks.js index 7dfcc606c..7b52c2794 100644 --- a/src/hooks/useTasks.js +++ b/src/hooks/useTasks.js @@ -16,6 +16,20 @@ const jobSummaryQuery = { }, } +const trackerEventQuery = { + events: { + resource: 'tracker/jobs/', + id: ({ taskId }) => `${taskId}`, + }, +} + +const trackerSummaryQuery = { + summary: { + resource: 'tracker/jobs/', + id: ({ taskId }) => `${taskId}/report`, + }, +} + const defaultTasks = { data: {}, event: {}, @@ -39,13 +53,20 @@ const createFetchEvents = } const newTask = { ...task } - const { events, error } = await engine.query(jobEventQuery, { + const query = + task.importType === 'TRACKER_IMPORT_JOB' + ? trackerEventQuery + : jobEventQuery + + const response = await engine.query(query, { variables: { type: task.importType, taskId: task.id, }, }) + const { events, error } = response + if (error) { console.error('fetchEvents error: ', error) return @@ -90,13 +111,24 @@ const createFetchEvents = const createFetchSummary = (engine, setTasks) => async (type, id, task) => { const newTask = { ...task } - const { summary, error } = await engine.query(jobSummaryQuery, { + // we could still keep one query here (the jobs query), but tracker provides a facade to these + // and even though this branches the logic unnecessarily, we should stick to + // trackers' endpoint for tracker imports and they could abstract some job-related details + // more details here: https://docs.dhis2.org/en/develop/using-the-api/dhis-core-version-master/tracker.html#webapi_nti_import_summary + const query = + task.importType === 'TRACKER_IMPORT_JOB' + ? trackerSummaryQuery + : jobSummaryQuery + + const response = await engine.query(query, { variables: { type: task.importType, taskId: task.id, }, }) + const { summary, error } = response + if (error) { console.error('fetchSummary error: ', error) return diff --git a/src/pages/DataExport/form-helper.js b/src/pages/DataExport/form-helper.js index 03c350735..216b7793d 100644 --- a/src/pages/DataExport/form-helper.js +++ b/src/pages/DataExport/form-helper.js @@ -56,7 +56,8 @@ const onExport = (baseUrl, setExportEnabled) => async (values) => { const filename = `${endpoint}.${fileExtension}` const downloadUrlParams = valuesToParams(values, filename) const url = `${apiBaseUrl}${endpoint}?${downloadUrlParams}` - locationAssign(url, setExportEnabled) + locationAssign(url) + setExportEnabled(true) // log for debugging purposes console.log('data-export:', { url, params: downloadUrlParams }) diff --git a/src/pages/EventExport/EventExport.js b/src/pages/EventExport/EventExport.js index e88f1fef5..eb1d8e860 100644 --- a/src/pages/EventExport/EventExport.js +++ b/src/pages/EventExport/EventExport.js @@ -14,7 +14,6 @@ import { OrgUnitTree, ProgramPicker, Format, - formatOptions, defaultFormatOption, Compression, defaultCompressionOption, @@ -32,6 +31,7 @@ import { defaultOrgUnitIdSchemeOption, IdScheme, defaultIdSchemeOption, + formatNoXmlOptions, } from '../../components/Inputs/index.js' import { jsDateToISO8601 } from '../../utils/helper.js' import { onExport, validate } from './form-helper.js' @@ -41,7 +41,7 @@ const { Form } = ReactFinalForm // PAGE INFO export const PAGE_NAME = i18n.t('Event export') export const PAGE_DESCRIPTION = i18n.t( - 'Export event data for programs, stages and tracked entities to JSON, CSV, or DXF2 format.' + 'Export event data for programs, stages and tracked entities to JSON or CSV format.' ) const PAGE_ICON = @@ -58,13 +58,14 @@ const initialValues = { programStage: undefined, format: defaultFormatOption, compression: defaultCompressionOption, - startDate: jsDateToISO8601(threeMonthsBeforeToday), - endDate: jsDateToISO8601(today), + occurredAfter: jsDateToISO8601(threeMonthsBeforeToday), + occurredBefore: jsDateToISO8601(today), includeDeleted: false, dataElementIdScheme: defaultDataElementIdSchemeOption, orgUnitIdScheme: defaultOrgUnitIdSchemeOption, idScheme: defaultIdSchemeOption, inclusion: defaultInclusionOption, + skipPaging: true, } const EventExport = () => { @@ -100,10 +101,10 @@ const EventExport = () => { - - + + - + diff --git a/src/pages/EventExport/form-helper.js b/src/pages/EventExport/form-helper.js index bdecb8ff4..898d104c4 100644 --- a/src/pages/EventExport/form-helper.js +++ b/src/pages/EventExport/form-helper.js @@ -14,8 +14,8 @@ const onExport = (baseUrl, setExportEnabled) => (values) => { programStage, format, compression, - startDate, - endDate, + occurredAfter, + occurredBefore, includeDeleted, dataElementIdScheme, orgUnitIdScheme, @@ -24,7 +24,7 @@ const onExport = (baseUrl, setExportEnabled) => (values) => { } = values // generate URL and redirect - const apiBaseUrl = `${baseUrl}/api/` + const apiBaseUrl = `${baseUrl}/api/tracker/` const endpoint = `events` const endpointExtension = compression ? `${format}.${compression}` : format const filename = `${endpoint}.${endpointExtension}` @@ -38,8 +38,8 @@ const onExport = (baseUrl, setExportEnabled) => (values) => { `orgUnitIdScheme=${orgUnitIdScheme}`, `idScheme=${idScheme}`, `attachment=${filename}`, - `startDate=${startDate}`, - `endDate=${endDate}`, + `occurredAfter=${occurredAfter}`, + `occurredBefore=${occurredBefore}`, `ouMode=${inclusion}`, `format=${format}`, programStage != ALL_VALUE ? `programStage=${programStage}` : '', @@ -47,7 +47,8 @@ const onExport = (baseUrl, setExportEnabled) => (values) => { .filter((s) => s != '') .join('&') const url = `${apiBaseUrl}${endpoint}.${endpointExtension}?${downloadUrlParams}` - locationAssign(url, setExportEnabled) + locationAssign(url) + setExportEnabled(true) // log for debugging purposes console.log('event-export:', { url, params: downloadUrlParams }) diff --git a/src/pages/EventImport/EventImport.js b/src/pages/EventImport/EventImport.js index 4f9e867f9..ef0c66139 100644 --- a/src/pages/EventImport/EventImport.js +++ b/src/pages/EventImport/EventImport.js @@ -14,7 +14,6 @@ import { import { FileUpload, Format, - formatOptions, defaultFormatOption, DataElementIdScheme, defaultDataElementIdSchemeOption, @@ -26,6 +25,7 @@ import { defaultOrgUnitIdSchemeOption, ImportButtonStrip, FormAlerts, + formatNoXmlOptions, } from '../../components/Inputs/index.js' import { TaskContext, getNewestTask } from '../../contexts/index.js' import { getPrevJobDetails } from '../../utils/helper.js' @@ -34,7 +34,7 @@ import { onImport } from './form-helper.js' // PAGE INFO export const PAGE_NAME = i18n.t('Event import') export const PAGE_DESCRIPTION = i18n.t( - 'Import event data for programs, stages and tracked entities to JSON, CSV, or DXF2 format.' + 'Import event data for programs, stages and tracked entities from JSON or CSV format.' ) const PAGE_ICON = @@ -95,14 +95,14 @@ const EventImport = () => { ', } )} /> diff --git a/src/pages/EventImport/form-helper.js b/src/pages/EventImport/form-helper.js index af0ce5664..5dd6c3a1f 100644 --- a/src/pages/EventImport/form-helper.js +++ b/src/pages/EventImport/form-helper.js @@ -12,32 +12,27 @@ const onImport = format, dataElementIdScheme, orgUnitIdScheme, - eventIdScheme, idScheme, } = values // send xhr - const apiBaseUrl = `${baseUrl}/api/` - const endpoint = 'events.json' + const apiBaseUrl = `${baseUrl}/api/tracker` const params = [ - 'skipFirst=true', `async=${isAsync}`, - `dryRun=${dryRun}`, + `importMode=${dryRun ? 'validate' : 'commit'}`, `dataElementIdScheme=${dataElementIdScheme}`, `orgUnitIdScheme=${orgUnitIdScheme}`, - `eventIdScheme=${eventIdScheme}`, `idScheme=${idScheme}`, - `payloadFormat=${format}`, ].join('&') - const url = `${apiBaseUrl}${endpoint}?${params}` + const url = `${apiBaseUrl}?${params}` try { await uploadFile({ url, file: files[0], format: format, - type: 'EVENT_IMPORT', - isAsync: isAsync, + type: 'TRACKER_IMPORT_JOB', + isAsync, setProgress, addEntry: (id, entry) => addTask('event', id, { ...entry, jobDetails: values }), diff --git a/src/pages/Home/pages.js b/src/pages/Home/pages.js index fbacd067a..dd60f360d 100644 --- a/src/pages/Home/pages.js +++ b/src/pages/Home/pages.js @@ -81,7 +81,7 @@ const exportPages = capitalizePages([ { name: TEI_EXPORT_PAGE_NAME, description: TEI_EXPORT_DESCRIPTION, - linkText: i18n.t('Export tracked entity instances'), + linkText: i18n.t('Export tracked entities'), to: '/export/tei', }, ]) @@ -120,7 +120,7 @@ const importPages = capitalizePages([ { name: TEI_IMPORT_PAGE_NAME, description: TEI_IMPORT_DESCRIPTION, - linkText: i18n.t('Import tracked entity instances'), + linkText: i18n.t('Import tracked entities'), to: '/import/tei', }, ]) diff --git a/src/pages/MetadataDependencyExport/form-helper.js b/src/pages/MetadataDependencyExport/form-helper.js index ee5e77dca..ca7dc2526 100644 --- a/src/pages/MetadataDependencyExport/form-helper.js +++ b/src/pages/MetadataDependencyExport/form-helper.js @@ -11,7 +11,8 @@ const onExport = (baseUrl, setExportEnabled) => (values) => { const endpointExtension = compression ? `${format}.${compression}` : format const downloadUrlParams = `skipSharing=${skipSharing}&download=true` const url = `${apiBaseUrl}${endpoint}.${endpointExtension}?${downloadUrlParams}` - locationAssign(url, setExportEnabled) + locationAssign(url) + setExportEnabled(true) // log for debugging purposes console.log('metadata-dependency-export:', { diff --git a/src/pages/MetadataExport/form-helper.js b/src/pages/MetadataExport/form-helper.js index 879f3889a..63a1cbe0b 100644 --- a/src/pages/MetadataExport/form-helper.js +++ b/src/pages/MetadataExport/form-helper.js @@ -12,7 +12,8 @@ const onExport = (baseUrl, setExportEnabled) => (values) => { const schemaParams = checkedSchemas.map((name) => `${name}=true`).join('&') const downloadUrlParams = `skipSharing=${skipSharing}&download=true&${schemaParams}` const url = `${apiBaseUrl}${endpoint}.${endpointExtension}?${downloadUrlParams}` - locationAssign(url, setExportEnabled) + locationAssign(url) + setExportEnabled(true) // log for debugging purposes console.log('metadata-export:', { url, params: downloadUrlParams }) diff --git a/src/pages/TEIExport/TEIExport.js b/src/pages/TEIExport/TEIExport.js index c870c13b9..2acefc9df 100644 --- a/src/pages/TEIExport/TEIExport.js +++ b/src/pages/TEIExport/TEIExport.js @@ -12,7 +12,6 @@ import { } from '../../components/index.js' import { Format, - formatOptions, defaultFormatOption, OrgUnitMode, defaultOrgUnitSelectionModeOption, @@ -36,7 +35,6 @@ import { AssignedUserMode, defaultAssignedUserModeOption, IncludeDeleted, - IncludeAllAttributes, DataElementIdScheme, defaultDataElementIdSchemeOption, EventIdScheme, @@ -47,15 +45,16 @@ import { defaultOrgUnitIdSchemeOption, ExportButton, FormAlerts, + formatNoXmlOptions, } from '../../components/Inputs/index.js' import { onExport, validate } from './form-helper.js' const { Form } = ReactFinalForm // PAGE INFO -export const PAGE_NAME = i18n.t('Tracked entity instances export') +export const PAGE_NAME = i18n.t('Tracked entities export') export const PAGE_DESCRIPTION = i18n.t( - 'Export tracked entity instances in JSON, CSV, or DXF2 format.' + 'Export tracked entities in JSON or CSV format.' ) const PAGE_ICON = @@ -69,18 +68,17 @@ const initialValues = { inclusion: defaultInclusionOption, teiTypeFilter: defaultTEITypeFilterOption, programStatus: defaultProgramStatusOption, - followUpStatus: defaultFollowUpStatusOption, - programStartDate: '', - programEndDate: '', + followup: defaultFollowUpStatusOption, + enrollmentEnrolledAfter: '', + enrollmentEnrolledBefore: '', compression: '', // disable compression until it is properly implemented in the backend lastUpdatedFilter: defaultLastUpdatedFilterOption, - lastUpdatedStartDate: '', - lastUpdatedEndDate: '', - lastUpdatedDuration: '', + updatedAfter: '', + updatedBefore: '', + updatedWithin: '', assignedUserModeFilter: false, assignedUserMode: defaultAssignedUserModeOption, includeDeleted: false, - includeAllAttributes: false, dataElementIdScheme: defaultDataElementIdSchemeOption, eventIdScheme: defaultEventIdSchemeOption, orgUnitIdScheme: defaultOrgUnitIdSchemeOption, @@ -136,7 +134,7 @@ const TEIExport = () => { - + @@ -147,7 +145,6 @@ const TEIExport = () => { - @@ -157,9 +154,7 @@ const TEIExport = () => { diff --git a/src/pages/TEIExport/form-helper.js b/src/pages/TEIExport/form-helper.js index 5f19cb647..8c9ba7e21 100644 --- a/src/pages/TEIExport/form-helper.js +++ b/src/pages/TEIExport/form-helper.js @@ -17,7 +17,6 @@ const valuesToParams = ( inclusion, format, includeDeleted, - includeAllAttributes, dataElementIdScheme, eventIdScheme, orgUnitIdScheme, @@ -26,13 +25,13 @@ const valuesToParams = ( assignedUserMode, teiTypeFilter, programStatus, - followUpStatus, - programStartDate, - programEndDate, + followup, + enrollmentEnrolledAfter, + enrollmentEnrolledBefore, lastUpdatedFilter, - lastUpdatedStartDate, - lastUpdatedEndDate, - lastUpdatedDuration, + updatedAfter, + updatedBefore, + updatedWithin, }, filename ) => { @@ -40,18 +39,18 @@ const valuesToParams = ( ouMode: ouMode, format: format, includeDeleted: includeDeleted.toString(), - includeAllAttributes: includeAllAttributes.toString(), dataElementIdScheme: dataElementIdScheme, eventIdScheme: eventIdScheme, orgUnitIdScheme: orgUnitIdScheme, idScheme: idScheme, attachment: filename, + skipPaging: true, } // include selected org.units only when manual selection is selected // ouMode is then stored in the `inclusion` field if (ouMode === OU_MODE_MANUAL_VALUE) { - minParams.ou = selectedOrgUnits.map((o) => pathToId(o)).join(';') + minParams.orgUnit = selectedOrgUnits.map((o) => pathToId(o)).join(';') minParams.ouMode = inclusion } @@ -71,14 +70,16 @@ const valuesToParams = ( minParams.programStatus = programStatus } - minParams.followUpStatus = followUpStatus + if (followup !== 'ALL') { + minParams.followup = followup + } - if (programStartDate) { - minParams.programStartDate = programStartDate + if (enrollmentEnrolledAfter) { + minParams.enrollmentEnrolledAfter = enrollmentEnrolledAfter } - if (programEndDate) { - minParams.programEndDate = programEndDate + if (enrollmentEnrolledBefore) { + minParams.enrollmentEnrolledBefore = enrollmentEnrolledBefore } } @@ -87,17 +88,17 @@ const valuesToParams = ( } if (lastUpdatedFilter == 'DATE') { - if (lastUpdatedStartDate) { - minParams.lastUpdatedStartDate = lastUpdatedStartDate + if (updatedAfter) { + minParams.updatedAfter = updatedAfter } - if (lastUpdatedEndDate) { - minParams.lastUpdatedEndDate = lastUpdatedEndDate + if (updatedBefore) { + minParams.updatedBefore = updatedBefore } } if (lastUpdatedFilter == 'DURATION') { - minParams.lastUpdatedDuration = lastUpdatedDuration + minParams.updatedWithin = updatedWithin } return Object.keys(minParams) @@ -111,12 +112,13 @@ const onExport = (baseUrl, setExportEnabled) => async (values) => { const { format } = values // generate URL and redirect - const apiBaseUrl = `${baseUrl}/api/` - const endpoint = `trackedEntityInstances` + const apiBaseUrl = `${baseUrl}/api/tracker/` + const endpoint = `trackedEntities` const filename = `${endpoint}.${format}` const downloadUrlParams = valuesToParams(values, filename) const url = `${apiBaseUrl}${endpoint}.${format}?${downloadUrlParams}` - locationAssign(url, setExportEnabled) + locationAssign(url) + setExportEnabled(true) // log for debugging purposes console.log('tei-export:', { url, params: downloadUrlParams }) @@ -127,40 +129,40 @@ const validate = (values) => { if ( values.teiTypeFilter == 'PROGRAM' && - values.programStartDate && - values.programEndDate + values.enrollmentEnrolledAfter && + values.enrollmentEnrolledBefore ) { - errors.programStartDate = DATE_BEFORE_VALIDATOR( - values.programStartDate, - values.programEndDate + errors.enrollmentEnrolledAfter = DATE_BEFORE_VALIDATOR( + values.enrollmentEnrolledAfter, + values.enrollmentEnrolledBefore ) - errors.programEndDate = DATE_AFTER_VALIDATOR( - values.programEndDate, - values.programStartDate + errors.enrollmentEnrolledBefore = DATE_AFTER_VALIDATOR( + values.enrollmentEnrolledBefore, + values.enrollmentEnrolledAfter ) } if ( values.lastUpdatedFilter == 'DATE' && - values.lastUpdatedStartDate && - values.lastUpdatedEndDate + values.updatedAfter && + values.updatedBefore ) { - errors.lastUpdatedStartDate = DATE_BEFORE_VALIDATOR( - values.lastUpdatedStartDate, - values.lastUpdatedEndDate + errors.updatedAfter = DATE_BEFORE_VALIDATOR( + values.updatedAfter, + values.updatedBefore ) - errors.lastUpdatedEndDate = DATE_AFTER_VALIDATOR( - values.lastUpdatedEndDate, - values.lastUpdatedStartDate + errors.updatedBefore = DATE_AFTER_VALIDATOR( + values.updatedBefore, + values.updatedAfter ) } if ( values.lastUpdatedFilter == 'DATE' && - !values.lastUpdatedStartDate && - !values.lastUpdatedEndDate + !values.updatedAfter && + !values.updatedBefore ) { - errors.lastUpdatedEndDate = i18n.t( + errors.updatedBefore = i18n.t( "At least one of the 'last updated' date fields must be specified" ) } diff --git a/src/pages/TEIImport/TEIImport.js b/src/pages/TEIImport/TEIImport.js index 3daed9cf9..aeb5c11d5 100644 --- a/src/pages/TEIImport/TEIImport.js +++ b/src/pages/TEIImport/TEIImport.js @@ -14,7 +14,6 @@ import { import { FileUpload, Format, - formatNoCsvOptions, defaultFormatOption, Identifier, defaultIdentifierOption, @@ -46,6 +45,7 @@ import { defaultIdSchemeOption, OrgUnitIdScheme, defaultOrgUnitIdSchemeOption, + formatNoXmlNoCsvOptions, } from '../../components/Inputs/index.js' import { TaskContext, getNewestTask } from '../../contexts/index.js' import { getPrevJobDetails, getInitialBoolValue } from '../../utils/helper.js' @@ -54,9 +54,9 @@ import { onImport } from './form-helper.js' const { Form } = ReactFinalForm // PAGE INFO -export const PAGE_NAME = i18n.t('Tracked entity instances import') +export const PAGE_NAME = i18n.t('Tracked entities import') export const PAGE_DESCRIPTION = i18n.t( - 'Import tracked entity instances using JSON or DXF2 format.' + 'Import tracked entities using JSON format.' ) const PAGE_ICON = @@ -81,9 +81,7 @@ const createInitialValues = (prevJobDetails) => ({ prevJobDetails.skipValidation, defaultSkipValidationOption ), - // disable async until it is fully implemented for this resource - // (expected 2.36) - isAsync: false, + isAsync: true, dataElementIdScheme: prevJobDetails.dataElementIdScheme || defaultDataElementIdSchemeOption, orgUnitIdScheme: @@ -136,14 +134,14 @@ const TEIImport = () => { ', } )} /> diff --git a/src/pages/TEIImport/form-helper.js b/src/pages/TEIImport/form-helper.js index d7386cbb0..ed3b60cba 100644 --- a/src/pages/TEIImport/form-helper.js +++ b/src/pages/TEIImport/form-helper.js @@ -26,8 +26,7 @@ const onImport = } = values // send xhr - const apiBaseUrl = `${baseUrl}/api/` - const endpoint = 'trackedEntityInstances.json' + const apiBaseUrl = `${baseUrl}/api/tracker/` const params = [ `importMode=${dryRun ? 'VALIDATE' : 'COMMIT'}`, `identifier=${identifier}`, @@ -49,18 +48,19 @@ const onImport = ] .filter((s) => s != '') .join('&') - const url = `${apiBaseUrl}${endpoint}?${params}` + const url = `${apiBaseUrl}?${params}` try { await uploadFile({ url, file: files[0], format: format, - type: 'TEI_IMPORT', + type: 'TRACKER_IMPORT_JOB', isAsync: isAsync, setProgress, - addEntry: (id, entry) => - addTask('tei', id, { ...entry, jobDetails: values }), + addEntry: (id, entry) => { + addTask('tei', id, { ...entry, jobDetails: values }) + }, }) return jobStartedMessage } catch (e) { diff --git a/src/utils/helper.js b/src/utils/helper.js index 5db84c3f2..853274c24 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -101,7 +101,8 @@ const uploadFile = ({ url, upload: file, type, - onResponse: ({ error, id, msg, typeReports }) => { + onResponse: (response) => { + const { error, id, msg, typeReports } = response let entry if (!isAsync) { // we are done @@ -149,12 +150,14 @@ const uploadFile = ({ created: new Date(), lastUpdated: new Date(), completed: false, - events: [msg], + events: [{ ...msg, date: new Date() }], // this is a workaround for the initial message date coming as invalid + summary: undefined, error: false, importType: type, } } + addEntry(entry.id, entry) if (error) { @@ -169,7 +172,7 @@ const uploadFile = ({ const response = JSON.parse(ev.target.response) message = response.message } catch (e2) { - message = genericErrorMessage + message = ev } console.error('sendFile error', message) reject(errF(message)) @@ -193,7 +196,7 @@ const downloadWindowHtml = ` ` // call stub function if available -const locationAssign = (url, setExportEnabled) => { +const locationAssign = (url) => { if (window.locationAssign) { window.locationAssign(url) } else { @@ -201,11 +204,6 @@ const locationAssign = (url, setExportEnabled) => { downloadWindow.document.title = downloadWindowTitle downloadWindow.document.body.innerHTML = downloadWindowHtml // does not work in Chrome - - const enableExport = () => setExportEnabled(true) - downloadWindow.onbeforeunload = enableExport - downloadWindow.onabort = enableExport - downloadWindow.onerror = enableExport } } diff --git a/src/utils/tasks.js b/src/utils/tasks.js index b68ca447f..1179012a4 100644 --- a/src/utils/tasks.js +++ b/src/utils/tasks.js @@ -43,7 +43,7 @@ const categoryTypes = [ key: 'tei', importType: 'TEI_IMPORT', icon: , - label: i18n.t('TEI'), + label: i18n.t('Tracked entity'), }, ]