From ac5019e19075320c0d56cc62504337801fad2c8a Mon Sep 17 00:00:00 2001 From: Devon Bush Date: Tue, 18 Jul 2023 16:03:30 -0400 Subject: [PATCH 1/3] enabling excel export --- .../export/ExportDataControl.test.tsx | 15 ++++++++ .../participants/export/ExportDataControl.tsx | 38 ++++++++++--------- 2 files changed, 35 insertions(+), 18 deletions(-) create mode 100644 ui-admin/src/study/participants/export/ExportDataControl.test.tsx diff --git a/ui-admin/src/study/participants/export/ExportDataControl.test.tsx b/ui-admin/src/study/participants/export/ExportDataControl.test.tsx new file mode 100644 index 0000000000..ec245cbe7e --- /dev/null +++ b/ui-admin/src/study/participants/export/ExportDataControl.test.tsx @@ -0,0 +1,15 @@ +import React from 'react' + +import { setupRouterTest } from 'test-utils/router-testing-utils' +import { mockStudyEnvContext } from 'test-utils/mocking-utils' +import { render, screen } from '@testing-library/react' +import ExportDataControl from './ExportDataControl' + +test('renders the file types', async () => { + const { RoutedComponent } = setupRouterTest( + // eslint-disable-next-line @typescript-eslint/no-empty-function + {}}/>) + render(RoutedComponent) + expect(screen.getByText('Tab-delimted (.tsv)')).toBeInTheDocument() + expect(screen.getByText('Excel (.xlsx)')).toBeInTheDocument() +}) diff --git a/ui-admin/src/study/participants/export/ExportDataControl.tsx b/ui-admin/src/study/participants/export/ExportDataControl.tsx index 3ef4c5c930..23beedc1f8 100644 --- a/ui-admin/src/study/participants/export/ExportDataControl.tsx +++ b/ui-admin/src/study/participants/export/ExportDataControl.tsx @@ -7,13 +7,22 @@ import { currentIsoDate } from 'util/timeUtils' import { failureNotification } from 'util/notifications' import { Store } from 'react-notifications-component' -// TODO: Add JSDoc -// eslint-disable-next-line jsdoc/require-jsdoc +const FILE_FORMATS = [{ + label: 'Tab-delimted (.tsv)', + value: 'TSV', + fileSuffix: 'tsv' +}, { + label: 'Excel (.xlsx)', + value: 'EXCEL', + fileSuffix: 'xlsx' +}] + +/** form for configuring and downloading enrollee data */ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext: StudyEnvContextT, show: boolean, setShow: React.Dispatch>}) => { const [humanReadable, setHumanReadable] = useState(true) const [onlyIncludeMostRecent, setOnlyIncludeMostRecent] = useState(true) - const [fileFormat, setFileFormat] = useState('TSV') + const [fileFormat, setFileFormat] = useState(FILE_FORMATS[0]) const [isLoading, setIsLoading] = useState(false) @@ -22,7 +31,7 @@ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext: onlyIncludeMostRecent, splitOptionsIntoColumns: !humanReadable, stableIdsForOptions: !humanReadable, - fileFormat + fileFormat: fileFormat.value } } @@ -45,7 +54,7 @@ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext: setIsLoading(true) Api.exportEnrollees(studyEnvContext.portal.shortcode, studyEnvContext.study.shortcode, studyEnvContext.currentEnv.environmentName, optionsFromState()).then(response => { - saveLoadedData(response, `${currentIsoDate() }-enrollees.${fileFormat.toLowerCase()}`) + saveLoadedData(response, `${currentIsoDate() }-enrollees.${fileFormat.fileSuffix}`) }) } @@ -62,9 +71,6 @@ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext: const includeRecentChanged = (e: React.ChangeEvent) => { setOnlyIncludeMostRecent(e.target.value === 'true') } - const fileFormatChanged = (e: React.ChangeEvent) => { - setFileFormat(e.target.value) - } return setShow(false)}> @@ -101,16 +107,12 @@ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext:
File format
- - + {FILE_FORMATS.map(format => )}
From c1c1517916769be5f815e6c3526c481085ae1c59 Mon Sep 17 00:00:00 2001 From: Devon Bush Date: Tue, 18 Jul 2023 20:50:52 -0400 Subject: [PATCH 2/3] adding basic help routes --- ui-admin/src/App.tsx | 5 +- ui-admin/src/help/ExportHelp.tsx | 115 ++++++++++++++++++ ui-admin/src/help/HelpPage.tsx | 12 ++ ui-admin/src/help/HelpRouter.tsx | 15 +++ .../export/ExportDataControl.test.tsx | 12 +- .../participants/export/ExportDataControl.tsx | 13 +- 6 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 ui-admin/src/help/ExportHelp.tsx create mode 100644 ui-admin/src/help/HelpPage.tsx create mode 100644 ui-admin/src/help/HelpRouter.tsx diff --git a/ui-admin/src/App.tsx b/ui-admin/src/App.tsx index 095dd9d144..ea319ac693 100644 --- a/ui-admin/src/App.tsx +++ b/ui-admin/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react' +import React, { lazy, useContext } from 'react' import 'react-notifications-component/dist/theme.css' import 'styles/notifications.css' import 'survey-core/defaultV2.min.css' @@ -22,6 +22,7 @@ import UserList from './user/UserList' import InvestigatorTermsOfUsePage from './terms/InvestigatorTermsOfUsePage' import PrivacyPolicyPage from 'terms/PrivacyPolicyPage' import { IdleStatusMonitor } from 'login/IdleStatusMonitor' +const HelpRouter = lazy(() => import('./help/HelpRouter')) /** container for the app including the router */ @@ -39,11 +40,13 @@ function App() { }> + } /> }> }/> }/> }/> + } /> } /> Unknown page}/> diff --git a/ui-admin/src/help/ExportHelp.tsx b/ui-admin/src/help/ExportHelp.tsx new file mode 100644 index 0000000000..d63ff7795b --- /dev/null +++ b/ui-admin/src/help/ExportHelp.tsx @@ -0,0 +1,115 @@ +import React from 'react' + +/** guide to using the participant export/download function */ +export default function ExportHelp() { + return
+

Participant List Export Info

+

Participant export enables download of tabular files (.tsv or .xlsx) containing the data currently visible in + the Participant List. + One row will be generated per participant.

+ +

File format

+
+
    +
  • + .xlsx will create an Excel spreadsheet of the participant data. Empty cells will represent + null values. +
  • +
  • + .tsv (tab-delimited values). Will export a tab-delimited file. This may be useful in + environments where Excel is unavailable, or if the + number of columns to be exported exceeds 16K. In order to have data be compliant, double-quotes will + be replaced by single quotes, and any values including + tabs or line breaks will be surrounded in double-quotes. +
  • +
+ + +
+ +

Human readable / Analysis friendly

+
    +
  • + Analysis friendly Each picklist answers will be displayed as a stable id, rather than the + displayed text. For multiselects, each answer option will appear in a separate column. For example, the + question "Which symptoms have you had?" + with options "fever", "nausea", and "persisent cough", + will be exported into 3 columns. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    MEDICAL_HISTORY.SYMTPOMS.FEVERMEDICAL_HISTORY.SYMPTOMS.NAUSEAMEDICAL_HISTORY.SYMPTOMS.COUGH
    fevernauseapersistent cough
    010
    110
    000
    + +
  • +
  • + Human readable will use display text where possible, and will show multi-select questions as a + single column, with a comma-delimited string of the answers given. For example, the question "Which + symptoms have you had?" + with options "fever", "nausea", and "persisent cough", + will be exported into 1 column. + + + + + + + + + + + + + + + + + + + + +
    MEDICAL_HISTORY.SYMTPOMS
    symptoms
    nausea
    fever, nausea
    +
  • +
+

Include all completions of an activity

+ This option controls how the export will behave if a participant has completed an activity multiple times. +
    +
  • + Yes A new set of columns will be added to the export for each time the activity was completed. + These will be + denoted by _2, _3, etc... Columns will appear in order of *recency*. So e.g. MEDICAL_HISTORY.SYMPTOMS + represents the + most recent completion, while MEDICAL_HISTORY_2.SYMPTOMS represents the next-most recent, and so on. +
  • +
  • + No Only the most recent completion for each activity will be included in the export. +
  • +
+
+} diff --git a/ui-admin/src/help/HelpPage.tsx b/ui-admin/src/help/HelpPage.tsx new file mode 100644 index 0000000000..31f39f528b --- /dev/null +++ b/ui-admin/src/help/HelpPage.tsx @@ -0,0 +1,12 @@ +import React from 'react' +import { Link } from 'react-router-dom' + +/** shows the root help page. No structure yet */ +export default function HelpPage() { + return
+

Juniper help topics

+
+ Participant export +
+
+} diff --git a/ui-admin/src/help/HelpRouter.tsx b/ui-admin/src/help/HelpRouter.tsx new file mode 100644 index 0000000000..caaaab66ab --- /dev/null +++ b/ui-admin/src/help/HelpRouter.tsx @@ -0,0 +1,15 @@ +import React from 'react' +import { Route, Routes } from 'react-router-dom' +import ExportHelp from './ExportHelp' +import HelpPage from './HelpPage' + +/** routes across individual help pages -- catches any unmatched routes to the main index */ +export default function HelpRouter() { + return
+ + }/> + }/> + }/> + +
+} diff --git a/ui-admin/src/study/participants/export/ExportDataControl.test.tsx b/ui-admin/src/study/participants/export/ExportDataControl.test.tsx index ec245cbe7e..f177289d58 100644 --- a/ui-admin/src/study/participants/export/ExportDataControl.test.tsx +++ b/ui-admin/src/study/participants/export/ExportDataControl.test.tsx @@ -2,8 +2,9 @@ import React from 'react' import { setupRouterTest } from 'test-utils/router-testing-utils' import { mockStudyEnvContext } from 'test-utils/mocking-utils' -import { render, screen } from '@testing-library/react' +import { render, screen, waitFor } from '@testing-library/react' import ExportDataControl from './ExportDataControl' +import userEvent from '@testing-library/user-event' test('renders the file types', async () => { const { RoutedComponent } = setupRouterTest( @@ -13,3 +14,12 @@ test('renders the file types', async () => { expect(screen.getByText('Tab-delimted (.tsv)')).toBeInTheDocument() expect(screen.getByText('Excel (.xlsx)')).toBeInTheDocument() }) + +test('help page loads', async () => { + const { RoutedComponent } = setupRouterTest( + // eslint-disable-next-line @typescript-eslint/no-empty-function + {}}/>) + render(RoutedComponent) + userEvent.click(screen.getByText('help page')) + waitFor(() => expect(screen.getByText('Participant List Export Info')).toBeInTheDocument()) +}) diff --git a/ui-admin/src/study/participants/export/ExportDataControl.tsx b/ui-admin/src/study/participants/export/ExportDataControl.tsx index 23beedc1f8..fabbd25517 100644 --- a/ui-admin/src/study/participants/export/ExportDataControl.tsx +++ b/ui-admin/src/study/participants/export/ExportDataControl.tsx @@ -6,6 +6,7 @@ import Api from 'api/api' import { currentIsoDate } from 'util/timeUtils' import { failureNotification } from 'util/notifications' import { Store } from 'react-notifications-component' +import { Link } from 'react-router-dom' const FILE_FORMATS = [{ label: 'Tab-delimted (.tsv)', @@ -74,10 +75,9 @@ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext: return setShow(false)}> - Download -
- {studyEnvContext.study.name}: {studyEnvContext.currentEnv.environmentName} -
+ + Download +
e.preventDefault()}> @@ -114,6 +114,11 @@ const ExportDataControl = ({ studyEnvContext, show, setShow }: {studyEnvContext: {format.label} )} +
+
+ For more information about download formats, + see the help page. +
From 13ebe6e6ab9a18bc66918ce6b8746dbcc5e9d6e0 Mon Sep 17 00:00:00 2001 From: Devon Bush Date: Tue, 18 Jul 2023 20:57:24 -0400 Subject: [PATCH 3/3] updating doc text --- ui-admin/src/help/ExportHelp.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui-admin/src/help/ExportHelp.tsx b/ui-admin/src/help/ExportHelp.tsx index d63ff7795b..30802f5db8 100644 --- a/ui-admin/src/help/ExportHelp.tsx +++ b/ui-admin/src/help/ExportHelp.tsx @@ -4,8 +4,8 @@ import React from 'react' export default function ExportHelp() { return

Participant List Export Info

-

Participant export enables download of tabular files (.tsv or .xlsx) containing the data currently visible in - the Participant List. +

Participant export enables download of tabular files (.tsv or .xlsx) containing all participants + except those who have withdrawn from the study. One row will be generated per participant.

File format