diff --git a/outputs/.DS_Store b/outputs/.DS_Store deleted file mode 100644 index a3ee175..0000000 Binary files a/outputs/.DS_Store and /dev/null differ diff --git a/package-lock.json b/package-lock.json index 7c67217..07c23fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindlogger-pdf-server", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindlogger-pdf-server", - "version": "1.0.0", + "version": "1.0.1", "license": "ISC", "dependencies": { "body-parser": "^1.20.0", diff --git a/src/core/interfaces/index.ts b/src/core/interfaces/index.ts index 3d6fe37..4749321 100644 --- a/src/core/interfaces/index.ts +++ b/src/core/interfaces/index.ts @@ -39,6 +39,7 @@ export interface IActivity { reportIncludedItemName: string responseIsEditable: boolean scoresAndReports: IActivityScoresAndReports + subscaleSetting: ActivitySubscalesSetting showAllAtOnce: boolean splashScreen: string items: IActivityItem[] @@ -130,6 +131,11 @@ export interface IActivityScoresAndReports { reports: IActivityScoresAndReportsScores[] | IActivityScoresAndReportsSections[] } +export enum ScoringType { + score = 'score', + raw_score = 'raw_score', +} + export interface IActivityScoresAndReportsScores { type: 'score' id: string @@ -139,6 +145,8 @@ export interface IActivityScoresAndReportsScores { itemsPrint: string[] itemsScore: string[] message: string | null + scoringType: ScoringType | null + subscaleName: string | null } export interface IActivityScoresAndReportsSections { @@ -225,3 +233,26 @@ export interface SendPdfReportResponse { pdf: string email: Email } + +export const TScoreSeverity = ['Minimal', 'Mild', 'Moderate', 'Severe'] as const + +export type TScoreSeverity = (typeof TScoreSeverity)[number] + +export type LookupTableDataItem = { + score?: string + rawScore: string + optionalText: string + severity: TScoreSeverity | null + age?: string | number | null + sex?: string | null + id: string +} + +export type Subscale = { + name: string + subscaleTableData?: LookupTableDataItem[] | null +} + +export type ActivitySubscalesSetting = { + subscales: Subscale[] +} diff --git a/src/models/activity.ts b/src/models/activity.ts index e2164d5..14092b8 100644 --- a/src/models/activity.ts +++ b/src/models/activity.ts @@ -9,12 +9,28 @@ import { User, Map, ScoreForSummary, + ActivitySubscalesSetting, + ScoringType, } from '../core/interfaces' import { Calculator, convertMarkdownToHtml, getScoresSummary, isFloat, toFixed } from '../core/helpers' import { replaceVariablesInMarkdown } from '../core/helpers/markdownVariableReplacer/' import { ScoresCalculator } from '../core/helpers/ScoresCalculator' import { ConditionalLogicService } from '../modules/report/helpers/conditionalLogic' +const INTERVAL_SYMBOL = '~' + +const enum LookupTableItems { + Age_screen = 'age_screen', + Gender_screen = 'gender_screen', +} + +const enum Sex { + M = 'M', + F = 'F', +} + +const parseSex = (sex: string | null | undefined) => (sex === Sex.M ? '0' : '1') + export class ActivityEntity { public json: IActivity public schemaId: string @@ -26,6 +42,7 @@ export class ActivityEntity { public reportIncludeItem: string public allowSummary: boolean public reports: IActivityScoresAndReportsSections[] | IActivityScoresAndReportsScores[] + public subscaleSetting: ActivitySubscalesSetting constructor(data: IActivity, items: IActivityItem[] = []) { this.json = data @@ -44,6 +61,7 @@ export class ActivityEntity { this.allowSummary = data.scoresAndReports?.showScoreSummary || false this.reports = data.scoresAndReports?.reports || [] + this.subscaleSetting = data.subscaleSetting || null } getVisibleItems(): ItemEntity[] { @@ -93,6 +111,42 @@ export class ActivityEntity { break } + const subscaleItem = this.subscaleSetting.subscales.find(({ name }) => name === report.subscaleName) + + if (subscaleItem?.subscaleTableData && subscaleItem.subscaleTableData.length > 0) { + const calculatedScore = scores[report.id] + const genderItemIndex = this.items.findIndex((item) => item.name == LookupTableItems.Gender_screen) + const genderAnswer = responses[genderItemIndex] + const ageItemIndex = this.items.findIndex((item) => item.name == LookupTableItems.Age_screen) + const ageAnswer = responses[ageItemIndex] + const subscaleTableData = subscaleItem.subscaleTableData + + const subscaleTableDataItem = subscaleTableData.find(({ sex, age, rawScore }) => { + let reportedAge: number | null = null + if (typeof ageAnswer === 'string') { + reportedAge = Number(ageAnswer) + } else if ('value' in ageAnswer && typeof ageAnswer.value === 'number') { + reportedAge = ageAnswer.value + } + + const withAge = age === reportedAge + const withSex = parseSex(sex) === String(genderAnswer?.value) + + if (!withSex || !withAge) return false + + const hasInterval = rawScore.includes(INTERVAL_SYMBOL) + if (!hasInterval) return rawScore === String(calculatedScore) + + const [minScore, maxScore] = rawScore.replace(/\s/g, '').split(INTERVAL_SYMBOL) + + return Number(minScore) <= calculatedScore && calculatedScore <= Number(maxScore) + }) + + if (report.scoringType == ScoringType.score && subscaleTableDataItem) { + scores[report.id] = Number(subscaleTableDataItem.score) || calculatedScore + } + } + for (const conditional of report.conditionalLogic) { const isReportVisible = this.testVisibility(conditional, scores)