@jadermme/orus-core
Version:
ORUS Core Framework - Universal framework for 6 Pillars assessment, domain-agnostic
268 lines • 9.09 kB
JavaScript
/**
* 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