From c6ea5d5f2bb47e7a86d8e6a9330645dc63b0a2e0 Mon Sep 17 00:00:00 2001 From: Gavriil Date: Sat, 2 Nov 2024 21:07:04 +0100 Subject: [PATCH] [WIP] Trying things --- shared/locales/en/website-responses.json | 22 ++++++ .../src/utils/stats/SurveyStatsCalculator.ts | 72 +++++++++++++++++++ .../(website)/survey/responses/page.tsx | 63 ++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 shared/locales/en/website-responses.json create mode 100644 shared/src/utils/stats/SurveyStatsCalculator.ts create mode 100644 website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx diff --git a/shared/locales/en/website-responses.json b/shared/locales/en/website-responses.json new file mode 100644 index 000000000..99394c5e2 --- /dev/null +++ b/shared/locales/en/website-responses.json @@ -0,0 +1,22 @@ +{ + "title": "Survey Responses", + "onboarding": { + "title": "Onboarding Survey", + "description": "Filled out once before recipient is joining the program" + }, + "checkin": { + "title": "Check-in Survey", + "description": "Filled out every 6 months while recipient is in the program" + }, + "offboarding": { + "title": "Offboarding Survey", + "description": "Filled out once when recipient is finishing the program" + }, + "offboarded-checkin": { + "title": "Follow-up Survey", + "description": "Filled out every 6 months after recipient left the program" + } + + + +} diff --git a/shared/src/utils/stats/SurveyStatsCalculator.ts b/shared/src/utils/stats/SurveyStatsCalculator.ts new file mode 100644 index 000000000..831b61674 --- /dev/null +++ b/shared/src/utils/stats/SurveyStatsCalculator.ts @@ -0,0 +1,72 @@ +import _ from 'lodash'; +import { FirestoreAdmin } from '../../firebase/admin/FirestoreAdmin'; +import { Recipient, RECIPIENT_FIRESTORE_PATH } from '../../types/recipient'; +import { Survey, SURVEY_FIRETORE_PATH, SurveyQuestionnaire, SurveyStatus } from '../../types/survey'; + +export interface SurveyStats { + total: number; + type: SurveyQuestionnaire; +} + +export interface SurveyAnswersByType { + answers: any[]; +} + +export class SurveyStatsCalculator { + private readonly _data: SurveyStats[]; + private readonly _aggregatedData: { [key: string]: { [key: string]: SurveyAnswersByType } }; + + private constructor( + data: SurveyStats[], + aggregatedData: { + [key: string]: { [key: string]: SurveyAnswersByType }; + }, + ) { + this._data = data; + this._aggregatedData = aggregatedData; + } + + /** + * @param firestoreAdmin + */ + static async build(firestoreAdmin: FirestoreAdmin): Promise { + const recipients = await firestoreAdmin.collection(RECIPIENT_FIRESTORE_PATH).get(); + let documents = await Promise.all( + recipients.docs + .filter((recipient) => !recipient.get('test_recipient')) + .map(async (recipient) => { + return await firestoreAdmin + .collection(`${RECIPIENT_FIRESTORE_PATH}/${recipient.id}/${SURVEY_FIRETORE_PATH}`) + .get(); + }), + ); + let ignored = ['pageNo']; + let surveysData = documents.flatMap((snapshot) => snapshot.docs).map((survey) => survey.data()); + + let aggregatedData: { [key: string]: { [key: string]: SurveyAnswersByType } } = {}; + const typeCounts: { [type: string]: number } = {}; + surveysData.forEach((item) => { + if (item.status === SurveyStatus.Completed) { + typeCounts[item.questionnaire] = (typeCounts[item.questionnaire] || 0) + 1; + for (const [key, value] of Object.entries(item.data)) { + if (!ignored.includes(key)) { + aggregatedData[item.questionnaire] = aggregatedData[item.questionnaire] || {}; + aggregatedData[item.questionnaire][key] = aggregatedData[item.questionnaire][key] || { answers: [] }; + aggregatedData[item.questionnaire][key].answers.push(value); + } + } + } + }); + const data = Object.entries(typeCounts).map(([type, total]) => ({ type, total }) as SurveyStats); + + return new SurveyStatsCalculator(data, aggregatedData); + } + + get data(): SurveyStats[] { + return this._data; + } + + get aggregatedData(): { [key: string]: { [key: string]: SurveyAnswersByType } } { + return this._aggregatedData; + } +} diff --git a/website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx b/website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx new file mode 100644 index 000000000..08cae99c7 --- /dev/null +++ b/website/src/app/[lang]/[region]/(website)/survey/responses/page.tsx @@ -0,0 +1,63 @@ +import { firestoreAdmin } from '@/firebase-admin'; +import { SurveyStatsCalculator } from '@socialincome/shared/src/utils/stats/SurveyStatsCalculator'; +import { Badge, BaseContainer, Card, CardTitle, Typography } from '@socialincome/ui'; +import { Translator } from '@socialincome/shared/src/utils/i18n'; +import { DefaultPageProps } from '@/app/[lang]/[region]'; +import { SurveyQuestionnaire } from '@socialincome/shared/src/types/survey'; + +export const revalidate = 3600; // update once an hour +export default async function Page({ params: { lang } }: DefaultPageProps) { + const surveyStatsCalculator = await SurveyStatsCalculator.build(firestoreAdmin); + const temp = surveyStatsCalculator.data; + const allSurveyData = Object.values(SurveyQuestionnaire).map((it) => temp.find((survey) => survey.type == it)); + const data = surveyStatsCalculator.aggregatedData; + const translator = await Translator.getInstance({ + language: lang, + namespaces: ['website-responses','website-survey'], + }); + let selectedSurvey = SurveyQuestionnaire.Onboarding; + + return ( + + + {translator.t('title')} + +
+
+ {allSurveyData.map( + (surveyData) => + surveyData && ( + + {translator.t(surveyData.type + '.title')} + + {translator.t(surveyData.type + '.description')} + {surveyData.total} data points + + ), + )} +
+
+
+
+ {Object.keys(data[selectedSurvey]).map((key) => ( + + +
+ {translator.t('survey.questions.'+key.replace("V1","TitleV1"))} + + {data[selectedSurvey][key].answers.length} answers + +
+
+
+ {JSON.stringify(data[selectedSurvey][key].answers)} +
+
+ + + ))} +
+
+
+ ); +}