const { sanitizeEntity } = require("strapi-utils");
const { respond, respondWithError, translation } = require("@macromo/tools");
const {
TRANSLATION_INSIGHTS_SYSTEMS_CATEGORY_ANCESTRY,
TRANSLATION_INSIGHTS_SYSTEMS_CATEGORY_DISEASES,
TRANSLATION_INSIGHTS_SYSTEMS_CATEGORY_TRAITS,
TRANSLATION_INSIGHTS_SYSTEMS_DESCRIPTION_ANCESTRY,
} = require("@macromo/enums");
const {
responses: { FORBIDDEN, INTERNAL_SERVER_ERROR },
} = strapi.config.get("server");
const {
responses: {
INSIGHTS_DISEASE_NOT_FOUND,
INSIGHTS_MACROMO_ID_NOT_DEFINED,
INSIGHTS_MEDICATION_NOT_FOUND,
INSIGHTS_SYSTEM_ID_NOT_DEFINED,
INSIGHTS_SYSTEM_NOT_FOUND,
INSIGHTS_TRAIT_NOT_FOUND,
},
} = strapi.config.get("insights");
module.exports = {
async getAncestry(ctx) {
const {
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const { ancestry: ancestryGroups = {} } = medicalResultsState.toJSON();
const ancestryIds = Object.keys(ancestryGroups);
const ancestryState = await strapi
.query("ancestry")
.model.fetchAll(strapi.services["insights-schema"].ancestryBasic);
const ancestry = ancestryState.toJSON();
const payload = {
description: {
key: TRANSLATION_INSIGHTS_SYSTEMS_DESCRIPTION_ANCESTRY,
},
origins: ancestry.reduce((acc, { displayName, macromoId }) => {
if (ancestryIds.includes(macromoId)) {
acc.push(
sanitizeEntity(
{ displayName, value: ancestryGroups[macromoId] },
{ model: strapi.models.ancestry }
)
);
}
return acc;
}, []),
};
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getDiseases(ctx) {
const {
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const medicalResults = medicalResultsState.toJSON();
const diseasesState = await strapi
.query("disease")
.model.fetchAll(strapi.services["insights-schema"].diseasesBasic);
const diseases = diseasesState.toJSON();
const medicalResultsIds =
medicalResults.results?.insights?.map(({ macromoId }) => macromoId) ||
[];
const payload = diseases.reduce((acc, cur) => {
if (medicalResultsIds.includes(cur.macromoId)) {
const level = medicalResults.results?.insights?.find(
({ macromoId }) => macromoId === cur.macromoId
)?.riskScore;
return [
...acc,
sanitizeEntity({ ...cur, level }, { model: strapi.models.disease }),
];
}
return acc;
}, []);
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getDiseaseById(ctx) {
const {
params: { macromoId },
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
if (!macromoId) {
return respondWithError({ ctx, data: INSIGHTS_MACROMO_ID_NOT_DEFINED });
}
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const medicalResults = medicalResultsState.toJSON();
const diseaseState = await strapi
.query("disease")
.model.query((q) => {
q.where("macromoId", macromoId);
})
.fetch(strapi.services["insights-schema"].diseaseDetails);
if (!diseaseState) {
return respondWithError({ ctx, data: INSIGHTS_DISEASE_NOT_FOUND });
}
const { ...disease } = diseaseState.toJSON();
const level = medicalResults.results?.insights?.find(
(insight) => insight.macromoId === macromoId
)?.riskScore;
const payload = sanitizeEntity(
{
...disease,
level,
},
{ model: strapi.models.disease }
);
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getInsightsOverview(ctx) {
const {
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
try {
const questionnairesState = await strapi
.query("questionnaire")
.model.query((q) =>
q.where("userId", userId).whereNotNull("questionnaireId")
)
.fetchAll();
const questionnaires = questionnairesState.toJSON();
const questionnaireTemplateState = await strapi
.query("questionnaire_template")
.model.fetchAll();
const questionnaireTemplate = questionnaireTemplateState.toJSON();
const kitsState = await strapi
.query("kit")
.model.query((q) => q.where("userId", userId))
.fetchAll();
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const onboarding = {
"health-profile": questionnaireTemplate.length
? Math.round(
(questionnaires.length / questionnaireTemplate.length +
Number.EPSILON) *
100
) / 100
: 0,
insights: 0,
kit: +!!kits.length,
};
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({
ctx,
data: {
payload: {
categories: [],
diseases: [],
onboarding,
},
},
});
}
const medicalResults = medicalResultsState.toJSON();
const medicalResultsIds =
medicalResults.results?.insights?.map(({ macromoId }) => macromoId) ||
[];
const ancestryIds = Object.keys(medicalResults.ancestry || {});
const ancestryState = await strapi
.query("ancestry")
.model.fetchAll(strapi.services["insights-schema"].ancestryBasic);
const diseasesState = await strapi
.query("disease")
.model.fetchAll(strapi.services["insights-schema"].diseasesBasic);
const diseases = diseasesState.toJSON();
const traitsState = await strapi
.query("trait")
.model.fetchAll(strapi.services["insights-schema"].traitsBasic);
const traits = traitsState.toJSON();
const ancestry = ancestryState.toJSON();
const payload = {
categories: [
{
count: ancestry.filter(({ macromoId }) =>
ancestryIds.includes(macromoId)
).length,
displayName: {
key: TRANSLATION_INSIGHTS_SYSTEMS_CATEGORY_ANCESTRY,
},
},
{
count: traits.filter(({ macromoId }) =>
medicalResultsIds.includes(macromoId)
).length,
displayName: {
key: TRANSLATION_INSIGHTS_SYSTEMS_CATEGORY_TRAITS,
},
},
{
count: diseases.filter(({ macromoId }) =>
medicalResultsIds.includes(macromoId)
).length,
displayName: {
key: TRANSLATION_INSIGHTS_SYSTEMS_CATEGORY_DISEASES,
},
},
],
diseases: diseases.reduce((acc, cur) => {
if (medicalResultsIds.includes(cur.macromoId)) {
const level = medicalResults.results?.insights?.find(
(insight) => insight.macromoId === cur.macromoId
)?.riskScore;
return [
...acc,
sanitizeEntity(
{ ...cur, level },
{ model: strapi.models.disease }
),
];
}
return acc;
}, []),
onboarding: {
...onboarding,
insights: +!!medicalResults,
},
};
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getMedications(ctx) {
const {
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const medicalResults = medicalResultsState.toJSON();
const medicationsState = await strapi
.query("medication")
.model.fetchAll(strapi.services["insights-schema"].medicationsBasic);
const medications = medicationsState.toJSON();
const medicalResultsIds =
medicalResults?.results?.insights?.map(({ macromoId }) => macromoId) ||
[];
const payload = medications.reduce((acc, cur) => {
if (medicalResultsIds.includes(cur.macromoId)) {
const level = medicalResults?.results?.insights?.find(
(insight) => insight.macromoId === cur.macromoId
)?.riskScore;
return [
...acc,
sanitizeEntity(
{ ...cur, level },
{ model: strapi.models.medication }
),
];
}
return acc;
}, []);
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getMedicationById(ctx) {
const {
params: { macromoId },
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
if (!macromoId) {
return respondWithError({ ctx, data: INSIGHTS_MACROMO_ID_NOT_DEFINED });
}
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const medicalResults = medicalResultsState.toJSON();
const medicationState = await strapi
.query("medication")
.model.query((q) => {
q.where("macromoId", macromoId);
})
.fetch(strapi.services["insights-schema"].medicationDetails);
if (!medicationState) {
return respondWithError({ ctx, data: INSIGHTS_MEDICATION_NOT_FOUND });
}
const medication = medicationState.toJSON();
const level = medicalResults?.results?.insights?.find(
(insight) => insight.macromoId === macromoId
)?.riskScore;
const payload = sanitizeEntity(
{
...medication,
level,
},
{ model: strapi.models.medication }
);
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getTraits(ctx) {
const {
state: { user },
} = ctx;
const { id: userId } = user;
if (!userId) return respondWithError({ ctx, data: FORBIDDEN });
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const medicalResults = medicalResultsState.toJSON();
const traitsState = await strapi
.query("trait")
.model.fetchAll(strapi.services["insights-schema"].traitsBasic);
const traits = traitsState.toJSON();
const medicalResultsIds =
medicalResults.results?.insights?.map(({ macromoId }) => macromoId) ||
[];
const payload = traits.reduce((acc, cur) => {
if (medicalResultsIds.includes(cur.macromoId)) {
const level = medicalResults.results?.insights?.find(
(insight) => insight.macromoId === cur.macromoId
)?.riskScore;
return [
...acc,
sanitizeEntity({ ...cur, level }, { model: strapi.models.trait }),
];
}
return acc;
}, []);
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
async getTraitById(ctx) {
const {
params: { macromoId },
state: { user },
} = ctx;
const { id: userId } = user;
if (!macromoId) {
return respondWithError({ ctx, data: INSIGHTS_MACROMO_ID_NOT_DEFINED });
}
try {
const kitsState = await strapi
.query("kit")
.model.query((q) => {
q.where("userId", userId);
})
.fetchAll(strapi.services["insights-schema"].kits);
const kits = kitsState.toJSON();
const kitId = kits[0]?.id;
const medicalResultsState = kitId
? await strapi
.query("medical_result")
.model.query((q) => {
q.where("kit", kitId).andWhere("completed", true);
})
.fetch(strapi.services["insights-schema"].medicalResults)
: undefined;
if (!medicalResultsState) {
return respond({ ctx });
}
const medicalResults = medicalResultsState.toJSON();
const traitState = await strapi
.query("trait")
.model.query((q) => {
q.where("macromoId", macromoId);
})
.fetch(strapi.services["insights-schema"].traitDetails);
if (!traitState) {
return respondWithError({ ctx, data: INSIGHTS_TRAIT_NOT_FOUND });
}
const trait = traitState.toJSON();
const level = medicalResults?.results?.insights?.find(
(insight) => insight.macromoId === macromoId
)?.riskScore;
const payload = sanitizeEntity(
{
...trait,
level,
},
{ model: strapi.models.trait }
);
respond({ ctx, data: { payload } });
} catch (error) {
respondWithError({ ctx, data: INTERNAL_SERVER_ERROR, error });
}
},
// TODO - check, update errors
// CAUTION - doesn't work now!!!
async isUserInsightReady(ctx) {
const userId = ctx.state.user.id;
const user = await strapi
.query("user", "users-permissions")
.findOne({ id: userId });
if (!user) {
return ctx.badRequest("User not found");
}
try {
const insight = await strapi
.query("medical_result")
.findOne({ userId, completed: true });
if (!insight) {
return (ctx.body = {
statusCode: 200,
message: "Insights are not ready.",
});
} else {
// TODO: lang is missing, AWS is calling this route, wrong email template.
if (user.email) {
await strapi.services.user.sendInsightReadyEmail(user);
} else {
await strapi.services.sms.send(
"Your insights are ready! Please open Macromo" +
" application. https://www.macromo.org",
user.phone
);
}
ctx.body = {
statusCode: 200,
insightId: insight.id,
isReady: insight.completed,
};
}
} catch (e) {
console.error(e);
ctx.status = 500;
ctx.body = {
statusCode: 500,
error: "Internal server error",
};
}
},
};