Skip to content

Commit

Permalink
fix(adminPanel): RN-1289: update the entity associated with a survey …
Browse files Browse the repository at this point in the history
…resubmission (#5817)

* Initial update

* test updates

* Update importSurveyResponses.js

* Update importSurveyResponses.js

* Update importSurveyResponses.js

* Update SurveyResponseUpdatePersistor.js

* Delete ~$nonPeriodicUpdates.xlsx

* test updates

* review comments

* review updates

* addition of tests

---------

Co-authored-by: Andrew <vanbeekandrew@gmail.com>
  • Loading branch information
hrazasalman and avaek authored Aug 13, 2024
1 parent fedc8ea commit f7c9fec
Show file tree
Hide file tree
Showing 13 changed files with 95 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,15 @@ export class SurveyResponseUpdatePersistor {
return Object.keys(this.updatesByResponseId).length;
}

setupColumnsForSheet(sheetName, surveyResponseIds) {
surveyResponseIds.forEach((surveyResponseId, columnIndex) => {
if (!surveyResponseId) return; // array contains some empty slots representing info columns
this.updatesByResponseId[surveyResponseId] = {
setupColumnsForSheet(sheetName, surveyResponses) {
surveyResponses.forEach((surveyResponse, columnIndex) => {
if (!surveyResponse) return; // array contains some empty slots representing info columns
this.updatesByResponseId[surveyResponse.surveyResponseId] = {
type: UPDATE,
sheetName,
columnIndex,
surveyResponseId,
surveyResponseId: surveyResponse.surveyResponseId,
entityId: surveyResponse.entityId,
newSurveyResponse: null, // only populated if a new survey response is to be created
newDataTime: null, // only populated if submission time is to be updated
answers: {
Expand Down Expand Up @@ -149,12 +150,12 @@ export class SurveyResponseUpdatePersistor {
return { failures };
}

async processUpdate(transactingModels, { surveyResponseId, newDataTime, answers }) {
async processUpdate(transactingModels, { surveyResponseId, entityId, newDataTime, answers }) {
const newData = { entity_id: entityId };
if (newDataTime) {
await transactingModels.surveyResponse.updateById(surveyResponseId, {
data_time: newDataTime,
});
newData.data_time = newDataTime;
}
await transactingModels.surveyResponse.updateById(surveyResponseId, newData);
await this.processUpsertAnswers(transactingModels, surveyResponseId, answers.upserts);
await this.processDeleteAnswers(transactingModels, surveyResponseId, answers.deletes);
}
Expand Down Expand Up @@ -204,6 +205,7 @@ export class SurveyResponseUpdatePersistor {

async process() {
const allUpdates = Object.values(this.updatesByResponseId);

const creates = allUpdates.filter(({ type }) => type === CREATE);
const { failures: createFailures } = await this.processCreates(creates);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export async function importSurveyResponses(req, res) {
// extract response ids and set up update batcher
const { maxColumnIndex, maxRowIndex } = getMaxRowColumnIndex(sheet);
const minSurveyResponseIndex = INFO_COLUMN_HEADERS.length;
const surveyResponseIds = [];
const surveyResponses = [];
const isGeneratedIdByColumnIndex = [];
const existingResponseDataByColumnIndex = [];

Expand All @@ -168,32 +168,54 @@ export async function importSurveyResponses(req, res) {
for (let columnIndex = minSurveyResponseIndex; columnIndex <= maxColumnIndex; columnIndex++) {
const columnHeader = getColumnHeader(sheet, columnIndex);
const importMode = getImportMode(columnHeader);
const entityCode = getInfoForColumn(sheet, columnIndex, 'Entity Code');
const entityName = getInfoForColumn(sheet, columnIndex, 'Entity Name');
const entity = await models.entity.findOne({ code: entityCode });

if (entityCode && entityName) {
if (!entity) {
throw new ImportValidationError(
`Entity code does match any existing entity: ${entityCode}`,
);
}

if (entity.name !== entityName) {
throw new ImportValidationError(
`Entity code and name don't match: ${entity?.name} and ${entityName}`,
);
}
}

let surveyResponseIdValue = null;
if (IMPORT_BEHAVIOURS[importMode].shouldGenerateIds) {
surveyResponseIds[columnIndex] = generateId();
surveyResponseIdValue = generateId();
isGeneratedIdByColumnIndex[columnIndex] = true;
} else if (IMPORT_BEHAVIOURS[importMode].shouldUpdateExistingResponses) {
const { surveyResponseId } = await getExistingResponseData(columnIndex);

if (surveyResponseId) {
surveyResponseIds[columnIndex] = surveyResponseId;
surveyResponseIdValue = surveyResponseId;
} else {
// A matching existing response was not found, generate a new id
surveyResponseIds[columnIndex] = generateId();
surveyResponseIdValue = generateId();
isGeneratedIdByColumnIndex[columnIndex] = true;
}
} else {
surveyResponseIds[columnIndex] = columnHeader;
surveyResponseIdValue = columnHeader;
}
surveyResponses[columnIndex] = {
surveyResponseId: surveyResponseIdValue,
entityId: entity?.id,
};
}
updatePersistor.setupColumnsForSheet(tabName, surveyResponseIds);
updatePersistor.setupColumnsForSheet(tabName, surveyResponses);

for (let columnIndex = minSurveyResponseIndex; columnIndex <= maxColumnIndex; columnIndex++) {
const columnHeader = getColumnHeader(sheet, columnIndex);
validateColumnHeader(columnHeader, columnIndex, tabName);

if (isGeneratedIdByColumnIndex[columnIndex]) {
const surveyResponseId = surveyResponseIds[columnIndex];
const { surveyResponseId } = surveyResponses[columnIndex];
const surveyResponseDetails = await constructNewSurveyResponseDetails(
models,
sheet,
Expand Down Expand Up @@ -257,9 +279,9 @@ export async function importSurveyResponses(req, res) {
columnIndex <= maxColumnIndex;
columnIndex++
) {
const { surveyResponseId } = surveyResponses[columnIndex];
const columnHeader = getColumnHeader(sheet, columnIndex);
const importMode = getImportMode(columnHeader);
const surveyResponseId = surveyResponseIds[columnIndex];
const answerValue = getCellContents(sheet, columnIndex, rowIndex);
const transformedAnswerValue = answerTransformer
? await answerTransformer(models, answerValue)
Expand Down Expand Up @@ -331,7 +353,7 @@ export async function importSurveyResponses(req, res) {
const message =
failures.length > 0
? `Not all responses were successfully processed:
${failures.map(getFailureMessage).join('\n')}`
${failures.map(getFailureMessage).join('\n')}`
: null;
respond(res, { message, failures });
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,13 @@ export const testFunctionality = async () => {
await app.grantFullAccess();

await findOrCreateDummyCountryEntity(models, { code: 'DL', name: 'Demo Land' });
const entities = ['DL_1', 'DL_5', 'DL_7', 'DL_9'].map(code => ({
code,
const entities = [
{ code: 'DL_1', name: 'Port Douglas' },
{ code: 'DL_5', name: 'Hawthorn East' },
{ code: 'DL_7', name: 'Lake Charm' },
{ code: 'DL_9', name: 'Thornbury' },
].map(entity => ({
...entity,
country_code: 'DL',
}));
await findOrCreateRecords(models, { entity: entities });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ export const testGeneral = async () => {
await app.grantFullAccess();

await findOrCreateDummyCountryEntity(models, { code: 'DL', name: 'Demo Land' });
const entities = ['DL_1', 'DL_5', 'DL_7', 'DL_9'].map(code => ({
code,
const entities = [
{ code: 'DL_1', name: 'Port Douglas' },
{ code: 'DL_5', name: 'Hawthorn East' },
{ code: 'DL_7', name: 'Lake Charm' },
{ code: 'DL_9', name: 'Thornbury' },
].map(entity => ({
...entity,
country_code: 'DL',
}));
await findOrCreateRecords(models, { entity: entities });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ export const testOutdatedStatusUpdate = () => {
before(async () => {
await app.grantFullAccess();
await buildAndInsertSurveys(models, Object.values(SURVEYS));
await findOrCreateDummyCountryEntity(models, { code: 'TO' });
await findOrCreateDummyCountryEntity(models, { code: 'VU' });
await findOrCreateDummyCountryEntity(models, { code: 'TO', name: 'Tonga' });
await findOrCreateDummyCountryEntity(models, { code: 'VU', name: 'Vanuatu' });
});

beforeEach(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,25 +96,42 @@ export const testPermissions = async () => {
const entity = await findOrCreateDummyRecord(models.entity, {
code: 'DL_7',
country_code: demoLand.code,
name: 'Lake Charm',
});
await findOrCreateDummyRecord(models.entity, {
code: 'DL_9',
country_code: demoLand.code,
name: 'Thornbury',
});
await findOrCreateDummyRecord(models.entity, {
code: 'DL_10',
country_code: demoLand.code,
name: 'Traralgon',
});
await findOrCreateDummyRecord(models.entity, {
code: 'DL_11',
country_code: demoLand.code,
name: 'National Medical Warehouse',
});
await findOrCreateDummyRecord(models.entity, { code: 'DL_9', country_code: demoLand.code });
await findOrCreateDummyRecord(models.entity, { code: 'DL_10', country_code: demoLand.code });
await findOrCreateDummyRecord(models.entity, { code: 'DL_11', country_code: demoLand.code });
await findOrCreateDummyRecord(models.entity, {
code: 'KI_111_test',
country_code: kiribatiCountry.code,
name: 'Test 1',
});
await findOrCreateDummyRecord(models.entity, {
code: 'KI_222_test',
country_code: kiribatiCountry.code,
name: 'Test 2',
});
await findOrCreateDummyRecord(models.entity, {
code: 'KI_333_test',
country_code: kiribatiCountry.code,
name: 'Test 3',
});
await findOrCreateDummyRecord(models.entity, {
code: 'KI_444_test',
country_code: kiribatiCountry.code,
name: 'Test 4',
});
const userId = 'user_00000000000000_test';
await models.user.updateOrCreate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ export const testValidation = async () => {
await buildAndInsertSurveys(models, [VALIDATION_SURVEY]);
await findOrCreateDummyCountryEntity(models, { code: 'DL', name: 'Demo Land' });
await findOrCreateRecords(models, {
entity: ['DL_7', 'DL_9', 'DL_10', 'DL_11'].map(code => ({ code, country_code: 'DL' })),
entity: [
{ code: 'DL_1', name: 'Port Douglas' },
{ code: 'DL_7', name: 'Lake Charm' },
{ code: 'DL_9', name: 'Thornbury' },
{ code: 'DL_10', name: 'Traralgon' },
{ code: 'DL_11', name: 'National Medical Warehouse' },
].map(entity => ({ ...entity, country_code: 'DL' })),
});
});

Expand Down Expand Up @@ -52,6 +58,16 @@ export const testValidation = async () => {
'nonExistentQuestionId.xlsx',
/No question with id/,
],
[
'entity code and name mismatch',
'mismatchEntityNameAndCode.xlsx',
/Entity code and name don\'t match: Thornbury and Lake Charm/,
],
[
'invalid entity code',
'invalidEntity.xlsx',
/Entity code does match any existing entity: DL_15/,
],
];

testData.forEach(([description, file, expectedError]) => {
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit f7c9fec

Please sign in to comment.