Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RW-13823][risk=no] Add AIAN research purpose questions #9037

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions api/db/changelog/db.changelog-250-add-aian-research-columns.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="eric" id="db.changelog-250-add-aian-research-plan-column">
<addColumn tableName="workspace">
<column name="rp_aian_research_type" type="ENUM('EXCLUSIVE_AI_AN_POPULATION','CASE_CONTROL_AI_AN','FINDINGS_BY_AI_AN', 'NO_AI_AN_ANALYSIS')"/>
<column name="rp_aian_research_details" type="varchar(1000)"/>
</addColumn>
</changeSet>
</databaseChangeLog>
1 change: 1 addition & 0 deletions api/db/changelog/db.changelog-master.xml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@
<include file="changelog/db.changelog-247-drop-nonnull-sumologic-column-egress-event.xml"/>
<include file="changelog/db.changelog-248-add-public-release-number-column.xml"/>
<include file="changelog/db.changelog-249-add-time-window-start-egress-event.xml"/>
<include file="changelog/db.changelog-250-add-aian-research-columns.xml"/>
<!--
Note: to update the DB locally, do the following:
- Migrate schema changes: `./project.rb run-local-all-migrations`
Expand Down
30 changes: 30 additions & 0 deletions api/src/main/java/org/pmiops/workbench/db/model/DbWorkspace.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@
pkJoinColumns = @PrimaryKeyJoinColumn(name = "workspace_id"))
@Table(name = "workspace")
public class DbWorkspace {

public enum AIANResearchType {
EXCLUSIVE_AI_AN_POPULATION,
CASE_CONTROL_AI_AN,
FINDINGS_BY_AI_AN,
NO_AI_AN_ANALYSIS
}

private String firecloudUuid;

private long workspaceId;
Expand Down Expand Up @@ -99,6 +107,9 @@ public class DbWorkspace {

private Boolean isVwbWorkspace;

private AIANResearchType aianResearchType;
private String aianResearchDetails;

public DbWorkspace() {
setWorkspaceActiveStatusEnum(WorkspaceActiveStatus.ACTIVE);
}
Expand Down Expand Up @@ -797,6 +808,25 @@ public DbWorkspace setUsesTanagra(boolean usesTanagra) {
return this;
}

@Enumerated(EnumType.STRING)
@Column(name = "rp_aian_research_type")
public AIANResearchType getAianResearchType() {
return aianResearchType;
}

public void setAianResearchType(AIANResearchType aianResearchType) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getters/setters seem a little strange here, because it uses Aian instead of AIAN.

If I rename it to the later, the mapping breaks. Happy to adjust if you have a preference.

this.aianResearchType = aianResearchType;
}

@Column(name = "rp_aian_research_details")
public String getAianResearchDetails() {
return aianResearchDetails;
}

public void setAianResearchDetails(String aianResearchDetails) {
this.aianResearchDetails = aianResearchDetails;
}

@Transient
public boolean isCDRAndWorkspaceTanagraEnabled() {
return usesTanagra && cdrVersion.getTanagraEnabled();
Expand Down
12 changes: 12 additions & 0 deletions api/src/main/resources/workbench-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7587,6 +7587,10 @@ components:
- socialBehavioral
type: object
properties:
aianResearchDetails:
type: string
aianResearchType:
$ref: '#/components/schemas/AIANResearchType'
additionalNotes:
type: string
approved:
Expand Down Expand Up @@ -13254,6 +13258,14 @@ components:
userEmail:
type: string
description: The user that causes egress alert
AIANResearchType:
type: string
description: Research plan for American Indian or Alaska Native participants
enum:
- EXCLUSIVE_AI_AN_POPULATION
- CASE_CONTROL_AI_AN
- FINDINGS_BY_AI_AN
- NO_AI_AN_ANALYSIS
parameters:
userId:
name: userId
Expand Down
62 changes: 62 additions & 0 deletions ui/src/app/pages/workspace/workspace-edit-text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import * as React from 'react';
import * as fp from 'lodash/fp';

import {
AIANResearchType,
DisseminateResearchEnum,
ResearchOutcomeEnum,
SpecificPopulationEnum,
} from 'generated/fetch';

import { StyledExternalLink } from 'app/components/buttons';
import { FlexColumn } from 'app/components/flex';
import { TooltipTrigger } from 'app/components/popups';
import { SupportMailto } from 'app/components/support';
Expand Down Expand Up @@ -414,6 +416,37 @@ export const researchPurposeQuestions: Array<ResearchPurposeQuestion> = [
</span>
),
},
{
header: (
<div>
<AoU /> has a{' '}
<StyledExternalLink
href='http://researchallofus.org/PolicyRespectfulAIANResearch'
target='_blank'
>
{' '}
&nbsp;policy on respectful research involving American Indian and
Alaska Native (AI/AN) populations
</StyledExternalLink>
. The following questions are intended to assess the relevance of the
policy to your research.
</div>
),
description: '',
},
{
header:
'Does your research plan require any of the following with respect to individuals who ' +
'self-identify as American Indian or Alaska Native (AI/AN) or who are genetically similar to ' +
'populations with inferred Indigenous American genetic ancestry? Select the option that ' +
'best describes your plans.',
description: '',
},
{
header:
'Please explain your response by sharing specific details about your study design.',
description: <div>(Free text; 1000 character limit)</div>,
},
];

export interface SpecificPopulationItem {
Expand Down Expand Up @@ -671,3 +704,32 @@ export const RequestForReviewFooter =
'address questions about potentially stigmatizing research. As your research progresses, ' +
'should any new concerns arise based on study outcomes, please contact the RAB for ' +
'assistance through the User Support Hub menu in your workspace.';

export const aianResearchTypeMap: Map<AIANResearchType, string> = new Map([
[
AIANResearchType.EXCLUSIVE_AI_AN_POPULATION,
'I am planning to conduct my study using a study population exclusively including individuals ' +
'who self-identify as AI/AN and/or who exhibit genetic similarity to populations with inferred ' +
'Indigenous American genetic ancestry.',
],
[
AIANResearchType.CASE_CONTROL_AI_AN,
'I am planning to conduct a case/control study where either the “case” or the “control” ' +
'population consists exclusively of individuals who self-identify as AI/AN and/or who exhibit ' +
'genetic similarity to populations with inferred Indigenous American genetic ancestry.',
],
[
AIANResearchType.FINDINGS_BY_AI_AN,
'I am planning to break down my findings by race/ethnicity and/or ancestral genetic similarity ' +
'in such a way that may raise findings that are specific to individuals who self-identify as ' +
'AI/AN and/or who exhibit genetic similarity to populations with inferred Indigenous American ' +
'genetic ancestry.',
],
[
AIANResearchType.NO_AI_AN_ANALYSIS,
'I will not conduct any analyses that would yield findings specific to AI/AN populations and/or ' +
'populations who exhibit genetic similarity to populations with inferred Indigenous American ' +
'genetic ancestry. If that changes, I will immediately update my responses on this form to ' +
'reflect that change. ',
],
]);
174 changes: 174 additions & 0 deletions ui/src/app/pages/workspace/workspace-edit.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as fp from 'lodash/fp';
import { mockNavigate } from 'setupTests';

import {
AIANResearchType,
DisseminateResearchEnum,
ProfileApi,
ResearchOutcomeEnum,
Expand Down Expand Up @@ -58,6 +59,8 @@ import { UserApiStub } from 'testing/stubs/user-api-stub';
import { workspaceStubs } from 'testing/stubs/workspaces';
import { WorkspacesApiStub } from 'testing/stubs/workspaces-api-stub';

import { aianResearchTypeMap } from './workspace-edit-text';

jest.mock('app/utils/project-billing-info', () => ({
getBillingAccountInfo: () =>
new Promise((resolve) =>
Expand Down Expand Up @@ -125,6 +128,8 @@ describe(WorkspaceEdit.name, () => {
// fields in the workspace form.
researchPurpose: {
...workspaceStubs[0].researchPurpose,
aianResearchType: AIANResearchType.FINDINGS_BY_AI_AN,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added so that the edit test cases do not fail.

aianResearchDetails: 'Plan to study AI/AN populations',
intendedStudy: 'intendedStudy ',
anticipatedFindings: 'anticipatedFindings ',
scientificApproach: 'scientificApproach ',
Expand Down Expand Up @@ -328,6 +333,9 @@ describe(WorkspaceEdit.name, () => {

const saveButton = screen.getByRole('button', { name: 'Update Workspace' });

await user.hover(saveButton);
screen.logTestingPlaygroundURL();

await waitFor(() => {
expectButtonElementEnabled(saveButton);
});
Expand Down Expand Up @@ -1129,4 +1137,170 @@ describe(WorkspaceEdit.name, () => {
`background-color: ${colors.highlight}`
);
});

it('should only display AIAN research questions when CDR >= v8', async () => {
workspaceEditMode = WorkspaceEditMode.Create;
renderComponent();

/* Select AIAN Research Plan */
const selectedAIANResearchType = aianResearchTypeMap.get(
AIANResearchType.CASE_CONTROL_AI_AN
);
const selectedAIANResearchTypeRadioElement = screen.getByRole('radio', {
name: selectedAIANResearchType,
});
expect(selectedAIANResearchTypeRadioElement).toBeInTheDocument();
await user.click(selectedAIANResearchTypeRadioElement);

/* Describe AIAN Research Plan */
const aianResearchDescription = screen.getByRole('textbox', {
name: /text area describing the aian research description text field/i,
});
expect(aianResearchDescription).toBeInTheDocument();
await user.clear(aianResearchDescription);
await user.paste('Example AIAN Research Description');

/* Switch to CDR Version < 8 */
const cdrVersionSelect = screen.getByRole('combobox', {
name: /cdr version dropdown/i,
}) as HTMLSelectElement;
await userEvent.selectOptions(cdrVersionSelect, [
CdrVersionsStubVariables.ALT_WORKSPACE_CDR_VERSION,
]);

/* Dismiss old study warning */
await user.click(
screen.getByRole('checkbox', {
name: /i will use this workspace to complete an existing study or replicate a previous study\./i,
})
);
await user.click(
screen.getByRole('checkbox', {
name: /in the workspace description below, i will identify which study i am continuing or replicating\./i,
})
);
await user.click(
screen.getByRole('button', {
name: /continue/i,
})
);

/* Confirm AIAN Research Plan is not visible */
expect(
screen.queryByRole('radio', {
name: selectedAIANResearchType,
})
).not.toBeInTheDocument();
expect(
screen.queryByRole('textbox', {
name: /text area describing the aian research description text field/i,
})
).not.toBeInTheDocument();

/* Switch back to CDR >= 8 */
await userEvent.selectOptions(cdrVersionSelect, [
CdrVersionsStubVariables.DEFAULT_WORKSPACE_CDR_VERSION,
]);

/* Confirm AIAN Research Plan fields persisted */
const selectedAIANResearchTypeRadioElementAfterReturn = screen.queryByRole(
'radio',
{
name: selectedAIANResearchType,
}
);
expect(selectedAIANResearchTypeRadioElementAfterReturn).toBeInTheDocument();
expect(selectedAIANResearchTypeRadioElementAfterReturn).toBeChecked();
const aianResearchDescriptionElementAfterReturn = screen.getByRole(
'textbox',
{
name: /text area describing the aian research description text field/i,
}
);
expect(aianResearchDescriptionElementAfterReturn).toBeInTheDocument();
const aianResearchDescriptionTextAfterReturn = within(
aianResearchDescriptionElementAfterReturn
).getByText('Example AIAN Research Description');
expect(aianResearchDescriptionTextAfterReturn).toBeInTheDocument();
});

it('should show AIAN validation when CDR >=8', async () => {
workspaceEditMode = WorkspaceEditMode.Create;
renderComponent();
const saveButton = screen.getByRole('button', {
name: /create workspace/i,
});
await user.hover(saveButton);
// Testing to see if validation appears
expect(
screen.getByText(
/you must select the type of AIAN research you are conducting \(question 6\.1\)/i
)
).toBeInTheDocument();

expect(
screen.getByText(
/you must provide details about your study design \(question 6\.2\)/i
)
).toBeInTheDocument();

// Select appropriate AIAN Research Values
const selectedAIANResearchTypeRadioElement = screen.getByRole('radio', {
name: aianResearchTypeMap.get(AIANResearchType.CASE_CONTROL_AI_AN),
});
expect(selectedAIANResearchTypeRadioElement).toBeInTheDocument();
await user.click(selectedAIANResearchTypeRadioElement);
const aianResearchDescription = screen.getByRole('textbox', {
name: /text area describing the aian research description text field/i,
});
expect(aianResearchDescription).toBeInTheDocument();
await user.clear(aianResearchDescription);
await user.paste('Example AIAN Research Description');

// Ensuring validation disappears when values are valid
await user.hover(saveButton);
expect(
screen.queryByText(
/you must select the type of AIAN research you are conducting \(question 6\.1\)/i
)
).not.toBeInTheDocument();

expect(
screen.queryByText(
/you must provide details about your study design \(question 6\.2\)/i
)
).not.toBeInTheDocument();
});

it('should not show AIAN validation when CDR <8', async () => {
workspaceEditMode = WorkspaceEditMode.Create;
renderComponent();

/* Switch to CDR Version < 8 */
const cdrVersionSelect = screen.getByRole('combobox', {
name: /cdr version dropdown/i,
}) as HTMLSelectElement;
await userEvent.selectOptions(cdrVersionSelect, [
CdrVersionsStubVariables.ALT_WORKSPACE_CDR_VERSION,
]);

// Hover over save button to trigger validation
const saveButton = screen.getByRole('button', {
name: /create workspace/i,
});
await user.hover(saveButton);

// Ensuring that inappropriate validation message doe snot appear
expect(
screen.queryByText(
/you must select the type of AIAN research you are conducting \(question 6\.1\)/i
)
).not.toBeInTheDocument();

expect(
screen.queryByText(
/you must provide details about your study design \(question 6\.2\)/i
)
).not.toBeInTheDocument();
});
});
Loading