@things-factory/kpi 9.1.0 → 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- export * from './parser';
2
- export * from './evaluator';
3
- export * from './functions';
4
- export * from './provider';
1
+ export * from './parser.js';
2
+ export * from './evaluator.js';
3
+ export * from './functions.js';
4
+ export * from './provider.js';
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
- tslib_1.__exportStar(require("./parser"), exports);
5
- tslib_1.__exportStar(require("./evaluator"), exports);
6
- tslib_1.__exportStar(require("./functions"), exports);
7
- tslib_1.__exportStar(require("./provider"), exports);
4
+ tslib_1.__exportStar(require("./parser.js"), exports);
5
+ tslib_1.__exportStar(require("./evaluator.js"), exports);
6
+ tslib_1.__exportStar(require("./functions.js"), exports);
7
+ tslib_1.__exportStar(require("./provider.js"), exports);
8
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/calculator/index.ts"],"names":[],"mappings":";;;AAAA,mDAAwB;AACxB,sDAA2B;AAC3B,sDAA2B;AAC3B,qDAA0B","sourcesContent":["export * from './parser'\nexport * from './evaluator'\nexport * from './functions'\nexport * from './provider'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/calculator/index.ts"],"names":[],"mappings":";;;AAAA,sDAA2B;AAC3B,yDAA8B;AAC9B,yDAA8B;AAC9B,wDAA6B","sourcesContent":["export * from './parser.js'\nexport * from './evaluator.js'\nexport * from './functions.js'\nexport * from './provider.js'\n"]}
@@ -0,0 +1,2 @@
1
+ export * from './kpi-metric-value-provider.js';
2
+ export * from './kpi-value-provider.js';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./kpi-metric-value-provider.js"), exports);
5
+ tslib_1.__exportStar(require("./kpi-value-provider.js"), exports);
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/controllers/index.ts"],"names":[],"mappings":";;;AAAA,yEAA8C;AAC9C,kEAAuC","sourcesContent":["export * from './kpi-metric-value-provider.js'\nexport * from './kpi-value-provider.js'\n"]}
@@ -45,7 +45,7 @@ class KpiMetricValueProvider {
45
45
  // 기타 periodType별 보정 추가 가능
46
46
  }
47
47
  const valueRepo = (0, shell_1.getRepository)(kpi_metric_value_1.KpiMetricValue, this.options.tx);
48
- const value = await valueRepo.findOne({
48
+ var value = await valueRepo.findOne({
49
49
  where: {
50
50
  metric: { id: metric.id },
51
51
  valueDate,
@@ -54,7 +54,18 @@ class KpiMetricValueProvider {
54
54
  }
55
55
  });
56
56
  if (!value) {
57
- throw new Error(`Metric value not found: metric='${name}', org='${this.options.org ?? ''}', valueDate='${valueDate}', periodType='${metric.periodType}'`);
57
+ // 임시로 최신의 데이타 하나를 찾아오는 것으로 하자.
58
+ value = await valueRepo.findOne({
59
+ where: {
60
+ metric: { id: metric.id },
61
+ org: this.options.org ?? '',
62
+ domain: { id: this.options.domainId }
63
+ },
64
+ order: { valueDate: 'DESC' }
65
+ });
66
+ if (!value) {
67
+ throw new Error(`Metric value not found: metric='${name}', org='${this.options.org ?? ''}', valueDate='${valueDate}', periodType='${metric.periodType}'`);
68
+ }
58
69
  }
59
70
  return value.value;
60
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-metric-value-provider.js","sourceRoot":"","sources":["../../server/controllers/kpi-metric-value-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { KpiMetric } from '../service/kpi-metric/kpi-metric'\nimport { KpiMetricValue } from '../service/kpi-metric-value/kpi-metric-value'\nimport { ValueProvider } from '../calculator/provider'\nimport { getDefaultValueDate } from '../service/utils/value-date-util'\n\nexport class KpiMetricValueProvider implements ValueProvider {\n constructor(\n private options: {\n valueDate: string\n org?: string\n domainId: string\n tx?: any\n }\n ) {}\n\n async get(name: string) {\n const metricRepo = getRepository(KpiMetric, this.options.tx)\n const metric = await metricRepo.findOne({ where: { name, domain: { id: this.options.domainId } } })\n if (!metric) throw new Error(`Metric not found: ${name}`)\n\n // ALLTIME 타입인 경우 valueDate 무시하고 최신 값 조회\n if (metric.periodType === 'ALLTIME') {\n const valueRepo = getRepository(KpiMetricValue, this.options.tx)\n const value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n org: this.options.org ?? '',\n domain: { id: this.options.domainId }\n },\n order: { createdAt: 'DESC' }\n })\n if (!value) {\n throw new Error(`Metric value not found: metric='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`)\n }\n return value.value\n }\n\n // metric의 periodType에 맞게 valueDate 보정\n let valueDate = this.options.valueDate\n if (!valueDate) {\n valueDate = getDefaultValueDate(metric.periodType, 'current')\n } else {\n // valueDate가 metric.periodType에 맞는 포맷인지 검사, 아니면 보정\n // (간단히: 길이로 구분, 실제는 정규식 등으로 더 정교하게 가능)\n if (metric.periodType === 'MONTH' && valueDate.length > 7) valueDate = valueDate.slice(0, 7)\n if (metric.periodType === 'DAY' && valueDate.length > 10) valueDate = valueDate.slice(0, 10)\n // 기타 periodType별 보정 추가 가능\n }\n const valueRepo = getRepository(KpiMetricValue, this.options.tx)\n const value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n valueDate,\n org: this.options.org ?? '',\n domain: { id: this.options.domainId }\n }\n })\n if (!value) {\n throw new Error(\n `Metric value not found: metric='${name}', org='${this.options.org ?? ''}', valueDate='${valueDate}', periodType='${metric.periodType}'`\n )\n }\n return value.value\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-metric-value-provider.js","sourceRoot":"","sources":["../../server/controllers/kpi-metric-value-provider.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { KpiMetric } from '../service/kpi-metric/kpi-metric'\nimport { KpiMetricValue } from '../service/kpi-metric-value/kpi-metric-value'\nimport { ValueProvider } from '../calculator/provider'\nimport { getDefaultValueDate } from '../service/utils/value-date-util'\n\nexport class KpiMetricValueProvider implements ValueProvider {\n constructor(\n private options: {\n valueDate: string\n org?: string\n domainId: string\n tx?: any\n }\n ) {}\n\n async get(name: string) {\n const metricRepo = getRepository(KpiMetric, this.options.tx)\n const metric = await metricRepo.findOne({ where: { name, domain: { id: this.options.domainId } } })\n if (!metric) throw new Error(`Metric not found: ${name}`)\n\n // ALLTIME 타입인 경우 valueDate 무시하고 최신 값 조회\n if (metric.periodType === 'ALLTIME') {\n const valueRepo = getRepository(KpiMetricValue, this.options.tx)\n const value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n org: this.options.org ?? '',\n domain: { id: this.options.domainId }\n },\n order: { createdAt: 'DESC' }\n })\n if (!value) {\n throw new Error(`Metric value not found: metric='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`)\n }\n return value.value\n }\n\n // metric의 periodType에 맞게 valueDate 보정\n let valueDate = this.options.valueDate\n if (!valueDate) {\n valueDate = getDefaultValueDate(metric.periodType, 'current')\n } else {\n // valueDate가 metric.periodType에 맞는 포맷인지 검사, 아니면 보정\n // (간단히: 길이로 구분, 실제는 정규식 등으로 더 정교하게 가능)\n if (metric.periodType === 'MONTH' && valueDate.length > 7) valueDate = valueDate.slice(0, 7)\n if (metric.periodType === 'DAY' && valueDate.length > 10) valueDate = valueDate.slice(0, 10)\n // 기타 periodType별 보정 추가 가능\n }\n const valueRepo = getRepository(KpiMetricValue, this.options.tx)\n var value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n valueDate,\n org: this.options.org ?? '',\n domain: { id: this.options.domainId }\n }\n })\n if (!value) {\n // 임시로 최신의 데이타 하나를 찾아오는 것으로 하자.\n value = await valueRepo.findOne({\n where: {\n metric: { id: metric.id },\n org: this.options.org ?? '',\n domain: { id: this.options.domainId }\n },\n order: { valueDate: 'DESC' }\n })\n\n if (!value) {\n throw new Error(\n `Metric value not found: metric='${name}', org='${this.options.org ?? ''}', valueDate='${valueDate}', periodType='${metric.periodType}'`\n )\n }\n }\n\n return value.value\n }\n}\n"]}
@@ -1,3 +1,5 @@
1
1
  export * from './migrations/index.js';
2
2
  export * from './service/index.js';
3
+ export * from './calculator/index.js';
4
+ export * from './controllers/index.js';
3
5
  import './routes';
@@ -3,5 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./migrations/index.js"), exports);
5
5
  tslib_1.__exportStar(require("./service/index.js"), exports);
6
+ tslib_1.__exportStar(require("./calculator/index.js"), exports);
7
+ tslib_1.__exportStar(require("./controllers/index.js"), exports);
6
8
  require("./routes");
7
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,gEAAqC;AACrC,6DAAkC;AAElC,oBAAiB","sourcesContent":["export * from './migrations/index.js'\nexport * from './service/index.js'\n\nimport './routes'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../server/index.ts"],"names":[],"mappings":";;;AAAA,gEAAqC;AACrC,6DAAkC;AAClC,gEAAqC;AACrC,iEAAsC;AAEtC,oBAAiB","sourcesContent":["export * from './migrations/index.js'\nexport * from './service/index.js'\nexport * from './calculator/index.js'\nexport * from './controllers/index.js'\n\nimport './routes'\n"]}
@@ -37,7 +37,7 @@ class KpiValueScoreService {
37
37
  }
38
38
  }
39
39
  catch (error) {
40
- console.error('Score formula evaluation error:', error);
40
+ console.error(`Score formula evaluation for '${kpi.name}: ${kpi.scoreFormula}' error:`, error);
41
41
  }
42
42
  return null;
43
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-value-score.service.js","sourceRoot":"","sources":["../../../server/service/kpi-value/kpi-value-score.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Kpi } from '../kpi/kpi'\nimport { KpiValue } from './kpi-value'\nimport { parseFormula } from '../../calculator/parser'\nimport { evaluateFormula } from '../../calculator/evaluator'\nimport { builtinFunctions } from '../../calculator/functions'\n\nexport interface ScoreResult {\n score: number\n calculationMethod?: 'formula' | 'lookup'\n}\n\nexport class KpiValueScoreService {\n /**\n * scoreFormula를 사용한 성과 점수 계산\n */\n async calculateScoreFromFormula(kpi: Kpi, value: number): Promise<ScoreResult | null> {\n if (!kpi.scoreFormula) {\n return null\n }\n\n try {\n const ast = parseFormula(kpi.scoreFormula)\n const provider = {\n get: async (name: string) => {\n if (name === 'value') return value\n return null\n }\n }\n const evalContext = { functions: builtinFunctions, provider }\n const result = await evaluateFormula(ast, evalContext)\n\n if (result !== null && result !== undefined && !isNaN(result)) {\n const performanceScore = Number(result)\n\n // score를 0~1 범위로 제한\n const clampedScore = Math.max(0, Math.min(1, performanceScore))\n\n return {\n score: clampedScore,\n calculationMethod: 'formula'\n }\n }\n } catch (error) {\n console.error('Score formula evaluation error:', error)\n }\n\n return null\n }\n\n /**\n * KPI의 grades lookup table을 이용한 성과 점수 변환\n * 복잡한 성과 점수 변환을 수식으로 표현하기 어려운 경우 사용\n * @deprecated 향후 grades 필드 제거 예정. scoreFormula로 대체 권장\n */\n calculateScoreFromLookup(kpi: Kpi, value: number): ScoreResult | null {\n if (!kpi.grades || kpi.grades.length === 0) {\n return null\n }\n\n // grades lookup table에서 해당 값의 범위에 맞는 성과 점수 찾기\n const grade = kpi.grades.find(g => value >= g.minValue && value <= g.maxValue)\n\n if (!grade) {\n return null\n }\n\n // score를 0~1 범위로 제한\n const clampedScore = Math.max(0, Math.min(1, grade.score))\n\n return {\n score: clampedScore,\n calculationMethod: 'lookup'\n }\n }\n\n /**\n * KpiValue 생성/수정 시 성과 점수 자동 계산 및 저장\n */\n async calculateAndSaveScore(kpiValue: KpiValue, kpi: Kpi): Promise<void> {\n // 1. scoreFormula 우선 시도\n let scoreResult = await this.calculateScoreFromFormula(kpi, kpiValue.value)\n\n // 2. scoreFormula가 없거나 실패하면 grades lookup table 사용 (deprecated)\n if (!scoreResult) {\n scoreResult = this.calculateScoreFromLookup(kpi, kpiValue.value)\n }\n\n if (scoreResult) {\n // KpiValue의 score 필드에 성과 점수 저장\n kpiValue.score = scoreResult.score\n }\n }\n\n /**\n * 기존 KpiValue들의 성과 점수 재계산 (배치 처리)\n */\n async recalculateScoresForKpi(kpiId: string, context: ResolverContext): Promise<void> {\n const kpi = await getRepository(Kpi).findOne({ where: { id: kpiId } })\n if (!kpi) return\n\n const kpiValues = await getRepository(KpiValue).find({\n where: { kpiId, version: kpi.version }\n })\n\n for (const kpiValue of kpiValues) {\n await this.calculateAndSaveScore(kpiValue, kpi)\n }\n\n await getRepository(KpiValue).save(kpiValues)\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-value-score.service.js","sourceRoot":"","sources":["../../../server/service/kpi-value/kpi-value-score.service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getRepository } from '@things-factory/shell'\nimport { Kpi } from '../kpi/kpi'\nimport { KpiValue } from './kpi-value'\nimport { parseFormula } from '../../calculator/parser'\nimport { evaluateFormula } from '../../calculator/evaluator'\nimport { builtinFunctions } from '../../calculator/functions'\n\nexport interface ScoreResult {\n score: number\n calculationMethod?: 'formula' | 'lookup'\n}\n\nexport class KpiValueScoreService {\n /**\n * scoreFormula를 사용한 성과 점수 계산\n */\n async calculateScoreFromFormula(kpi: Kpi, value: number): Promise<ScoreResult | null> {\n if (!kpi.scoreFormula) {\n return null\n }\n\n try {\n const ast = parseFormula(kpi.scoreFormula)\n const provider = {\n get: async (name: string) => {\n if (name === 'value') return value\n return null\n }\n }\n const evalContext = { functions: builtinFunctions, provider }\n const result = await evaluateFormula(ast, evalContext)\n\n if (result !== null && result !== undefined && !isNaN(result)) {\n const performanceScore = Number(result)\n\n // score를 0~1 범위로 제한\n const clampedScore = Math.max(0, Math.min(1, performanceScore))\n\n return {\n score: clampedScore,\n calculationMethod: 'formula'\n }\n }\n } catch (error) {\n console.error(`Score formula evaluation for '${kpi.name}: ${kpi.scoreFormula}' error:`, error)\n }\n\n return null\n }\n\n /**\n * KPI의 grades lookup table을 이용한 성과 점수 변환\n * 복잡한 성과 점수 변환을 수식으로 표현하기 어려운 경우 사용\n * @deprecated 향후 grades 필드 제거 예정. scoreFormula로 대체 권장\n */\n calculateScoreFromLookup(kpi: Kpi, value: number): ScoreResult | null {\n if (!kpi.grades || kpi.grades.length === 0) {\n return null\n }\n\n // grades lookup table에서 해당 값의 범위에 맞는 성과 점수 찾기\n const grade = kpi.grades.find(g => value >= g.minValue && value <= g.maxValue)\n\n if (!grade) {\n return null\n }\n\n // score를 0~1 범위로 제한\n const clampedScore = Math.max(0, Math.min(1, grade.score))\n\n return {\n score: clampedScore,\n calculationMethod: 'lookup'\n }\n }\n\n /**\n * KpiValue 생성/수정 시 성과 점수 자동 계산 및 저장\n */\n async calculateAndSaveScore(kpiValue: KpiValue, kpi: Kpi): Promise<void> {\n // 1. scoreFormula 우선 시도\n let scoreResult = await this.calculateScoreFromFormula(kpi, kpiValue.value)\n\n // 2. scoreFormula가 없거나 실패하면 grades lookup table 사용 (deprecated)\n if (!scoreResult) {\n scoreResult = this.calculateScoreFromLookup(kpi, kpiValue.value)\n }\n\n if (scoreResult) {\n // KpiValue의 score 필드에 성과 점수 저장\n kpiValue.score = scoreResult.score\n }\n }\n\n /**\n * 기존 KpiValue들의 성과 점수 재계산 (배치 처리)\n */\n async recalculateScoresForKpi(kpiId: string, context: ResolverContext): Promise<void> {\n const kpi = await getRepository(Kpi).findOne({ where: { id: kpiId } })\n if (!kpi) return\n\n const kpiValues = await getRepository(KpiValue).find({\n where: { kpiId, version: kpi.version }\n })\n\n for (const kpiValue of kpiValues) {\n await this.calculateAndSaveScore(kpiValue, kpi)\n }\n\n await getRepository(KpiValue).save(kpiValues)\n }\n}\n"]}