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

feat: allow arbitrary response for suggester #900

Open
wants to merge 5 commits into
base: next
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
4 changes: 4 additions & 0 deletions src/constants/dictionary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,10 @@ const dictionary: Dictionary = {
fr: 'Retrouver dans le questionnaire',
en: 'Retrieve in the questionnaire',
},
allowArbitraryResponse: {
fr: 'Autoriser une réponse libre',
en: 'Allow an arbitrary response',
},
list: {
fr: 'Liste',
en: 'List',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export function formToState(form) {
y,
z,
mesureLevel,
arbitraryVariableOfVariableId,
codeListReference,
codeListReferenceLabel,
isCollected,
Expand All @@ -61,6 +62,7 @@ export function formToState(form) {
x,
y,
z,
arbitraryVariableOfVariableId,
isCollected,
alternativeLabel,
mesureLevel,
Expand Down Expand Up @@ -94,6 +96,7 @@ export function storeToForm(currentStore) {
x,
y,
z,
arbitraryVariableOfVariableId,
isCollected = '1',
alternativeLabel,
mesureLevel,
Expand All @@ -107,6 +110,7 @@ export function storeToForm(currentStore) {
x,
y,
z,
arbitraryVariableOfVariableId,
isCollected,
alternativeLabel,
mesureLevel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ import {
const { RADIO } = DATATYPE_VIS_HINT;

export const defaultState = {
allowArbitraryResponse: false,
mandatory: false,
visHint: RADIO,
// [DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultState),
};

export const defaultForm = {
allowArbitraryResponse: false,
mandatory: false,
visHint: RADIO,
// [DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultForm),
Expand All @@ -24,13 +26,15 @@ export const defaultForm = {
export function formToState(form, transformers) {
const {
id,
allowArbitraryResponse,
mandatory,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: codesListForm,
} = form;

return {
id,
allowArbitraryResponse,
mandatory,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
Expand All @@ -39,10 +43,11 @@ export function formToState(form, transformers) {
}

export function stateToForm(currentState, transformers) {
const { id, visHint, mandatory } = currentState;
const { id, allowArbitraryResponse, visHint, mandatory } = currentState;

return {
id,
allowArbitraryResponse,
mandatory,
visHint,
[DEFAULT_CODES_LIST_SELECTOR_PATH]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export const defaultMeasureForm = {
type: SIMPLE,
[SIMPLE]: defaultMeasureSimpleState,
[SINGLE_CHOICE]: {
allowArbitraryResponse: false,
[DEFAULT_CODES_LIST_SELECTOR_PATH]: cloneDeep(CodesListDefaultForm),
visHint: RADIO,
},
Expand Down
7 changes: 7 additions & 0 deletions src/model/remote-to-stores.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export function questionnaireRemoteToStores(remote, currentStores = {}) {
remote.Child,
collectedVariables,
);

const arbitraryVariables = Component.getArbitraryVariablesFromRemote(
remote.Child,
collectedVariables,
);

const codesListsStore = CodesList.remoteToStore(
codesLists,
variableclarification,
Expand All @@ -56,6 +62,7 @@ export function questionnaireRemoteToStores(remote, currentStores = {}) {
responsesByVariable,
codesListsStore,
variableclarification,
arbitraryVariables,
),
};
// Components store
Expand Down
21 changes: 20 additions & 1 deletion src/model/transformations/collected-variable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function remoteToStore(
responsesByVariable,
codesListsStore,
variableclarification,
arbitraryVariables,
) {
remote.forEach((variable) => {
if (variableclarification) {
Expand All @@ -45,9 +46,26 @@ export function remoteToStore(
}
}
}
if (arbitraryVariables) {
const extendedArbitraryVariable = arbitraryVariables.find(
(arbitraryVariable) =>
arbitraryVariable.CollectedVariableReference === variable.id,
);
if (extendedArbitraryVariable) {
variable.arbitraryVariableOfVariableId =
extendedArbitraryVariable.arbitraryVariableOfVariableId;
}
}
});
return remote.reduce((acc, ev) => {
const { Name: name, Label: label, CodeListReference, z, mesureLevel } = ev;
const {
Name: name,
Label: label,
CodeListReference,
z,
mesureLevel,
arbitraryVariableOfVariableId,
} = ev;
const id = ev.id || uuid();

const formatSingleRemote = remoteToStateFormatSimple({
Expand All @@ -69,6 +87,7 @@ export function remoteToStore(
...responsesByVariable[id],
z,
mesureLevel,
arbitraryVariableOfVariableId,
},
};
}, {});
Expand Down
80 changes: 80 additions & 0 deletions src/model/transformations/collected-variable.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,86 @@ describe('collected variable tranformations', () => {
output,
);
});

test('should add the arbitrary variables to the collected', () => {
const input = [
{
id: 'm6aty8by',
Name: 'SUGGESTER',
type: 'CollectedVariableType',
Label: 'SUGGESTER label',
Datatype: {
type: 'TextDatatypeType',
Pattern: '',
typeName: 'TEXT',
MaxLength: 1,
},
CodeListReference: 'id',
},
{
id: 'm6atzjnb',
Name: 'SUGGESTER_ARBITRARY',
type: 'CollectedVariableType',
Label: 'SUGGESTER_ARBITRARY label',
Datatype: {
type: 'TextDatatypeType',
typeName: 'TEXT',
MaxLength: 249,
},
arbitraryVariableOfVariableId: 'm6aty8by',
},
];
const arbitraryVariables = [
{
Datatype: {
type: 'TextDatatypeType',
typeName: 'TEXT',
MaxLength: 249,
},
mandatory: false,
CollectedVariableReference: 'm6atzjnb',
arbitraryVariableOfVariableId: 'm6aty8by',
},
];
const responsesByVariable = { m6aty8by: {} };
const codesListStore = { id: { label: 'label' } };
const variableclarification = [];
const output = {
m6aty8by: {
id: 'm6aty8by',
name: 'SUGGESTER',
label: 'SUGGESTER label',
type: TEXT,
codeListReference: 'id',
codeListReferenceLabel: 'label',
TEXT: {
maxLength: 1,
pattern: '',
},
},
m6atzjnb: {
id: 'm6atzjnb',
name: 'SUGGESTER_ARBITRARY',
label: 'SUGGESTER_ARBITRARY label',
type: TEXT,
codeListReferenceLabel: '',
TEXT: {
maxLength: 249,
},
arbitraryVariableOfVariableId: 'm6aty8by',
},
};

expect(
remoteToStore(
input,
responsesByVariable,
codesListStore,
variableclarification,
arbitraryVariables,
),
).toEqual(output);
});
});
describe('remoteToComponentState', () => {
test('should return the state representation of a collected variable', () => {
Expand Down
98 changes: 98 additions & 0 deletions src/model/transformations/component.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,61 @@ export const getClarificarionfromremote = (Children, collectedVariables) => {
return variableClarification;
};

/*
* Get the list of questions containing an attribute "ArbitraryResponse".
* Currently we only look at single response questions.
*/
const getArbitraryQuestions = (Children) => {
const arbitraryQuestions = [];
const childr = Children.filter((children) => children.Child?.length !== 0);
childr.forEach((item) => {
item.Child?.forEach((component) => {
// component is a subsequence
if (component.type === 'SequenceType') {
component.Child.forEach((question) => {
if (
// single choice question
question.questionType === SINGLE_CHOICE &&
question.ArbitraryResponse !== undefined
) {
arbitraryQuestions.push(question);
}
});
} else if (
// component is a single choice question
component.questionType === SINGLE_CHOICE &&
component.ArbitraryResponse !== undefined
) {
arbitraryQuestions.push(component);
}
});
});
return arbitraryQuestions;
};

/*
* Get the list of arbitrary variables.
* An arbitrary variable is the "ArbitraryResponse" object of a question,
* extented with "arbitraryVariableOfVariableId" which is the corresponding Response variable id.
*/
export const getArbitraryVariablesFromRemote = (Children) => {
const arbitraryVariables = [];
const arbitraryQuestions = getArbitraryQuestions(Children);

arbitraryQuestions.forEach((question) => {
// a question with arbitrary is a singleReponse question so it has only one Response
const responseVariableId = question.Response[0].CollectedVariableReference;

const arbitraryVariable = {
...question.ArbitraryResponse,
arbitraryVariableOfVariableId: responseVariableId,
};
arbitraryVariables.push(arbitraryVariable);
});

return arbitraryVariables;
};

function remoteToVariableResponseNested(children = [], acc = {}) {
children.forEach((child) => {
const {
Expand Down Expand Up @@ -226,6 +281,7 @@ function remoteToState(remote, componentGroup, codesListsStore) {
Control: controls,
Response: responses,
ClarificationQuestion: responsesClarification,
ArbitraryResponse: arbitraryResponse,
ResponseStructure: responseStructure,
Child: children,
parent,
Expand All @@ -250,6 +306,10 @@ function remoteToState(remote, componentGroup, codesListsStore) {
responseFinal = responseFinal.concat(clar.Response);
});
}
if (arbitraryResponse !== undefined) {
responseFinal.push(arbitraryResponse);
}

const state = {
id,
name,
Expand Down Expand Up @@ -629,6 +689,37 @@ function getClarificationResponseTableQuestion(
};
}

function getArbitraryResponse(collectedVariablesStore, collectedVariables) {
const collectedVariableQuestions = [];
Object.values(collectedVariablesStore).forEach((collec) => {
if (collectedVariables !== undefined) {
collectedVariables.forEach((variables) => {
if (collec.id === variables) {
collectedVariableQuestions.push(collec);
}
});
}
});

for (const collected of collectedVariableQuestions) {
const isArbitraryVariable =
collected.arbitraryVariableOfVariableId !== undefined;
// we can have only one collected variable that is an arbitrary variable
if (isArbitraryVariable) {
const responseModel = {
mandatory: false,
typeName: collected.type,
maxLength: collected.TEXT.maxLength,
collectedVariable: collected.id,
};
const arbitraryResponse = Response.stateToRemote(responseModel);
return arbitraryResponse;
}
}

return undefined;
}

function storeToRemoteNested(
state,
store,
Expand Down Expand Up @@ -686,8 +777,15 @@ function storeToRemoteNested(
responsesClarification,
flowControl,
);

remote.FlowControl = remoteclarification.flowcontrolefinal;
remote.ClarificationQuestion = remoteclarification.ClarificationQuestion;

const remoteArbitrary = getArbitraryResponse(
collectedVariablesStore,
collectedVariables,
);
remote.ArbitraryResponse = remoteArbitrary;
}
if (
responseFormat.type === MULTIPLE_CHOICE &&
Expand Down
Loading
Loading