UNPKG

@jadermme/orus-core

Version:

ORUS Core Framework - Universal framework for 6 Pillars assessment, domain-agnostic

268 lines 9.09 kB
/** * ORUS Core - Data Completeness Calculation * * Pure functions for calculating data completeness across pillars and assessments. * Helps track progress and guide users toward more reliable assessments. * * @remarks * Data completeness (0-1) indicates: * - 0.0 = No data collected * - 0.5 = Partial data (subjective or incomplete objective) * - 1.0 = Complete data (all required fields populated) * * Completeness directly impacts: * - Assessment confidence * - Mode transition eligibility * - Recommendation quality */ /** * Default field schema (universal for all verticals) */ export const DEFAULT_PILLAR_FIELD_SCHEMA = { core: { score: true, status: true, mode: true, confidence: true }, optional: { trend: true, insights: true, recommendations: true, metadata: false // Metadata is truly optional } }; /** * Calculates data completeness for a single pillar * * @param pillar - Pillar assessment to evaluate * @param schema - Field schema (optional, uses default if not provided) * @returns Completeness result with breakdown * * @remarks * - Pure function: deterministic calculation * - Core fields are weighted 70%, optional 30% * - Missing required fields = fails minimum requirements * - Completeness can still be high without optional fields * * @example * ```typescript * const pillar: PillarAssessment = { * pillarId: PillarId.PILLAR_1, * score: 5.0, * status: 'attention', * mode: 'objective', * confidence: 'high', * // trend, insights, recommendations missing * lastUpdated: new Date(), * dataCompleteness: 0.7 * }; * * const result = calculatePillarCompleteness(pillar); * // result.coreCompleteness = 1.0 (all core fields present) * // result.optionalCompleteness = 0.0 (no optional fields) * // result.completeness ≈ 0.7 (weighted average) * ``` */ export function calculatePillarCompleteness(pillar, schema = DEFAULT_PILLAR_FIELD_SCHEMA) { const missingFields = []; const missingOptionalFields = []; // Check core fields (required) let corePresent = 0; let coreRequired = 0; Object.entries(schema.core).forEach(([field, required]) => { if (required) { coreRequired++; const value = pillar[field]; if (value !== undefined && value !== null) { corePresent++; } else { missingFields.push(field); } } }); const coreCompleteness = coreRequired > 0 ? corePresent / coreRequired : 1.0; // Check optional fields let optionalPresent = 0; let optionalRequired = 0; Object.entries(schema.optional).forEach(([field, shouldCheck]) => { if (shouldCheck) { optionalRequired++; const value = pillar[field]; // For arrays, check if non-empty if (Array.isArray(value)) { if (value.length > 0) { optionalPresent++; } else { missingOptionalFields.push(field); } } else if (value !== undefined && value !== null) { optionalPresent++; } else { missingOptionalFields.push(field); } } }); const optionalCompleteness = optionalRequired > 0 ? optionalPresent / optionalRequired : 1.0; // Weighted average: core 70%, optional 30% const completeness = coreCompleteness * 0.7 + optionalCompleteness * 0.3; return { pillarId: pillar.pillarId, completeness: Math.round(completeness * 100) / 100, coreCompleteness: Math.round(coreCompleteness * 100) / 100, optionalCompleteness: Math.round(optionalCompleteness * 100) / 100, missingFields, missingOptionalFields, meetsMinimumRequirements: missingFields.length === 0 }; } /** * Calculates data completeness for entire assessment * * @param assessment - Six pillars assessment * @param schema - Vertical schema (optional) * @returns Assessment completeness result * * @remarks * - Pure function: deterministic calculation * - Overall = average of all pillar completeness * - Generates actionable suggestions for improvement * - Categorizes pillars by completeness level * * @example * ```typescript * const result = calculateAssessmentCompleteness(assessment); * * console.log(`Overall: ${result.overall * 100}%`); * console.log(`Complete pillars: ${result.completePillars}/6`); * * if (result.suggestions.length > 0) { * console.log('Next steps:', result.suggestions); * } * ``` */ export function calculateAssessmentCompleteness(assessment, schema) { const perPillar = {}; let totalCompleteness = 0; let completePillars = 0; let partialPillars = 0; let emptyPillars = 0; // Calculate per-pillar completeness Object.values(assessment.pillars).forEach((pillar) => { const pillarSchema = schema?.pillars[pillar.pillarId] || DEFAULT_PILLAR_FIELD_SCHEMA; const result = calculatePillarCompleteness(pillar, pillarSchema); perPillar[pillar.pillarId] = result; totalCompleteness += result.completeness; // Categorize pillar if (result.completeness === 0) { emptyPillars++; } else if (result.meetsMinimumRequirements && result.completeness >= 0.7) { completePillars++; } else { partialPillars++; } }); const overall = totalCompleteness / 6; // Generate suggestions const suggestions = []; if (emptyPillars > 0) { suggestions.push(`${emptyPillars} pillar${emptyPillars > 1 ? 's have' : ' has'} no data. Start with assessment express.`); } if (partialPillars > 0) { suggestions.push(`${partialPillars} pillar${partialPillars > 1 ? 's have' : ' has'} incomplete data. Review and complete missing fields.`); } // Check mode eligibility const minForObjective = schema?.minCompletenessForObjective || 0.7; const minForHybrid = schema?.minCompletenessForHybrid || 0.4; if (overall < minForHybrid) { suggestions.push('Collect more data to unlock hybrid mode (minimum 40% completeness).'); } else if (overall < minForObjective && overall >= minForHybrid) { suggestions.push('Continue collecting data to unlock objective mode (minimum 70% completeness).'); } // Specific pillar recommendations const lowCompletenessPillars = Object.values(perPillar) .filter((p) => p.completeness < 0.5) .map((p) => p.pillarId); if (lowCompletenessPillars.length > 0 && lowCompletenessPillars.length <= 3) { suggestions.push(`Focus on completing: ${lowCompletenessPillars.join(', ')}`); } return { overall: Math.round(overall * 100) / 100, perPillar, completePillars, partialPillars, emptyPillars, suggestions }; } /** * Gets pillars that need more data * * @param result - Assessment completeness result * @param threshold - Minimum acceptable completeness (default 0.7) * @returns Pillar IDs needing attention * * @remarks * - Pure function: simple filter * - Useful for UI highlighting and user guidance * * @example * ```typescript * const result = calculateAssessmentCompleteness(assessment); * const needsWork = getIncompletePillars(result, 0.7); * * needsWork.forEach(pillarId => { * console.log(`${pillarId}: ${result.perPillar[pillarId].completeness * 100}%`); * }); * ``` */ export function getIncompletePillars(result, threshold = 0.7) { return Object.values(result.perPillar) .filter((p) => p.completeness < threshold) .map((p) => p.pillarId); } /** * Checks if assessment is ready for a specific mode * * @param assessment - Six pillars assessment * @param targetMode - Desired mode * @param schema - Vertical schema (optional) * @returns Whether assessment has sufficient data for mode * * @remarks * - Pure function: deterministic check * - Uses schema thresholds if provided, otherwise defaults * - Complements canTransitionMode from modeLogic * * @example * ```typescript * if (isReadyForMode(assessment, 'objective')) { * console.log('Assessment has enough data for objective mode'); * } else { * console.log('Need more data collection'); * } * ``` */ export function isReadyForMode(assessment, targetMode, schema) { const result = calculateAssessmentCompleteness(assessment, schema); const minForObjective = schema?.minCompletenessForObjective || 0.7; const minForHybrid = schema?.minCompletenessForHybrid || 0.4; switch (targetMode) { case 'subjective': return true; // Always ready for subjective case 'hybrid': return result.overall >= minForHybrid; case 'objective': return result.overall >= minForObjective; default: return false; } } //# sourceMappingURL=dataCompleteness.js.map