@things-factory/kpi 9.2.5 → 10.0.0-beta.10

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.
Files changed (139) hide show
  1. package/client/pages/kpi/kpi-list-page.ts +339 -525
  2. package/client/pages/kpi/kpi-tree-page.ts +135 -207
  3. package/client/pages/kpi-metric/kpi-metric-list-page.ts +146 -226
  4. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +187 -295
  5. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +123 -194
  6. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +57 -91
  7. package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +180 -278
  8. package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +186 -286
  9. package/client/pages/kpi-value/kpi-value-editor-page.ts +189 -292
  10. package/client/pages/kpi-value/kpi-value-list-page.ts +170 -264
  11. package/dist-client/pages/kpi/kpi-list-page.d.ts +0 -6
  12. package/dist-client/pages/kpi/kpi-list-page.js +150 -282
  13. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  14. package/dist-client/pages/kpi/kpi-tree-page.d.ts +1 -7
  15. package/dist-client/pages/kpi/kpi-tree-page.js +76 -127
  16. package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -1
  17. package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +0 -6
  18. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +62 -116
  19. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  20. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -7
  21. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +82 -140
  22. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  23. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -6
  24. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +54 -98
  25. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  26. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -7
  27. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +30 -57
  28. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
  29. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +1 -7
  30. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +91 -153
  31. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
  32. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +0 -6
  33. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +81 -155
  34. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
  35. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +1 -7
  36. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +80 -136
  37. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
  38. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -6
  39. package/dist-client/pages/kpi-value/kpi-value-list-page.js +73 -134
  40. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  41. package/dist-client/tsconfig.tsbuildinfo +1 -1
  42. package/dist-server/tsconfig.tsbuildinfo +1 -1
  43. package/package.json +18 -18
  44. package/client/tsconfig.json +0 -11
  45. package/dist-server/tsconfig.json +0 -10
  46. package/server/@types/index.d.ts +0 -11
  47. package/server/calculator/evaluator.ts +0 -45
  48. package/server/calculator/functions.ts +0 -67
  49. package/server/calculator/index.ts +0 -4
  50. package/server/calculator/parser.ts +0 -137
  51. package/server/calculator/provider.ts +0 -10
  52. package/server/controllers/index.ts +0 -2
  53. package/server/controllers/kpi-metric-value-provider.ts +0 -79
  54. package/server/controllers/kpi-value-provider.ts +0 -51
  55. package/server/index.ts +0 -6
  56. package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
  57. package/server/migrations/1752190849681-seed-kpi.ts +0 -356
  58. package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
  59. package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
  60. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
  61. package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
  62. package/server/migrations/grade-data/x11-performance-table.json +0 -962
  63. package/server/migrations/grade-data/x12-performance-table.json +0 -611
  64. package/server/migrations/grade-data/x14-performance-table.json +0 -42
  65. package/server/migrations/grade-data/x21-performance-table.json +0 -889
  66. package/server/migrations/grade-data/x22-performance-table.json +0 -1064
  67. package/server/migrations/grade-data/x23-performance-table.json +0 -42
  68. package/server/migrations/grade-data/x31-performance-table.json +0 -644
  69. package/server/migrations/grade-data/x32-performance-table.json +0 -993
  70. package/server/migrations/grade-data/x33-performance-table.json +0 -195
  71. package/server/migrations/grade-data/x34-performance-table.json +0 -12
  72. package/server/migrations/grade-data/x35-performance-table.json +0 -42
  73. package/server/migrations/grade-data/x41-performance-table.json +0 -825
  74. package/server/migrations/grade-data/x42-performance-table.json +0 -786
  75. package/server/migrations/grade-data/x43-performance-table.json +0 -12
  76. package/server/migrations/grade-data/x44-performance-table.json +0 -42
  77. package/server/migrations/grade-data/x51-performance-table.json +0 -924
  78. package/server/migrations/grade-data/x52-performance-table.json +0 -42
  79. package/server/migrations/grade-data/x61-performance-table.json +0 -261
  80. package/server/migrations/grade-data/x62-performance-table.json +0 -42
  81. package/server/migrations/index.ts +0 -9
  82. package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
  83. package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
  84. package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
  85. package/server/migrations/seed-data/kpi-values-seed.json +0 -402
  86. package/server/migrations/seed-data/kpis-seed.json +0 -488
  87. package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
  88. package/server/routes.ts +0 -81
  89. package/server/service/index.ts +0 -51
  90. package/server/service/kpi/aggregate-kpi.ts +0 -103
  91. package/server/service/kpi/event-subscriber.ts +0 -29
  92. package/server/service/kpi/index.ts +0 -9
  93. package/server/service/kpi/kpi-formula.service.ts +0 -164
  94. package/server/service/kpi/kpi-grade.types.ts +0 -28
  95. package/server/service/kpi/kpi-history.ts +0 -126
  96. package/server/service/kpi/kpi-mutation.ts +0 -553
  97. package/server/service/kpi/kpi-query.ts +0 -224
  98. package/server/service/kpi/kpi-type.ts +0 -151
  99. package/server/service/kpi/kpi.ts +0 -254
  100. package/server/service/kpi-alert/index.ts +0 -3
  101. package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
  102. package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
  103. package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
  104. package/server/service/kpi-metric/index.ts +0 -7
  105. package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
  106. package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
  107. package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
  108. package/server/service/kpi-metric/kpi-metric.ts +0 -134
  109. package/server/service/kpi-metric-value/index.ts +0 -7
  110. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
  111. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
  112. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
  113. package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
  114. package/server/service/kpi-org-scope/index.ts +0 -6
  115. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
  116. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
  117. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
  118. package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
  119. package/server/service/kpi-scope/index.ts +0 -11
  120. package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
  121. package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
  122. package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
  123. package/server/service/kpi-scope/kpi-scope.ts +0 -143
  124. package/server/service/kpi-statistic/index.ts +0 -7
  125. package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
  126. package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
  127. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
  128. package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
  129. package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
  130. package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
  131. package/server/service/kpi-value/index.ts +0 -7
  132. package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
  133. package/server/service/kpi-value/kpi-value-query.ts +0 -61
  134. package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
  135. package/server/service/kpi-value/kpi-value-type.ts +0 -122
  136. package/server/service/kpi-value/kpi-value.ts +0 -160
  137. package/server/service/utils/value-date-util.ts +0 -119
  138. package/server/tsconfig.json +0 -10
  139. package/server/types/global.d.ts +0 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/kpi",
3
- "version": "9.2.5",
3
+ "version": "10.0.0-beta.10",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -28,22 +28,22 @@
28
28
  "migration:create": "node ../../node_modules/typeorm/cli.js migration:create ./server/migrations/migration"
29
29
  },
30
30
  "dependencies": {
31
- "@operato/app": "^9.0.0",
32
- "@operato/data-grist": "^9.0.0",
33
- "@operato/dataset": "^9.0.0",
34
- "@operato/ghost-print": "^9.0.0",
35
- "@operato/graphql": "^9.0.0",
36
- "@operato/grist-editor": "^9.0.0",
37
- "@operato/i18n": "^9.0.0",
38
- "@operato/layout": "^9.0.0",
39
- "@operato/moment-timezone-es": "^9.0.0",
40
- "@operato/p13n": "^9.0.0",
41
- "@operato/shell": "^9.0.0",
42
- "@operato/styles": "^9.0.0",
43
- "@operato/utils": "^9.0.0",
44
- "@things-factory/auth-base": "^9.2.5",
45
- "@things-factory/dataset": "^9.2.5",
46
- "@things-factory/shell": "^9.2.5"
31
+ "@operato/app": "^10.0.0-beta.1",
32
+ "@operato/data-grist": "^10.0.0-beta.1",
33
+ "@operato/dataset": "^10.0.0-beta.1",
34
+ "@operato/ghost-print": "^10.0.0-beta.1",
35
+ "@operato/graphql": "^10.0.0-beta.1",
36
+ "@operato/grist-editor": "^10.0.0-beta.1",
37
+ "@operato/i18n": "^10.0.0-beta.1",
38
+ "@operato/layout": "^10.0.0-beta.1",
39
+ "@operato/moment-timezone-es": "^10.0.0-beta.1",
40
+ "@operato/p13n": "^10.0.0-beta.1",
41
+ "@operato/shell": "^10.0.0-beta.1",
42
+ "@operato/styles": "^10.0.0-beta.1",
43
+ "@operato/utils": "^10.0.0-beta.1",
44
+ "@things-factory/auth-base": "^10.0.0-beta.10",
45
+ "@things-factory/dataset": "^10.0.0-beta.10",
46
+ "@things-factory/shell": "^10.0.0-beta.10"
47
47
  },
48
- "gitHead": "d3622f475ae3bab84322d31b064cc4cda20f7062"
48
+ "gitHead": "95acadd39e9a0ff3b2f34d9f7082142395903179"
49
49
  }
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": true,
5
- "declaration": true,
6
- "module": "esnext",
7
- "outDir": "../dist-client",
8
- "baseUrl": "./"
9
- },
10
- "include": ["./**/*"]
11
- }
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig-base.json",
3
- "compilerOptions": {
4
- "strict": false,
5
- "module": "commonjs",
6
- "outDir": "../dist-server",
7
- "baseUrl": "./"
8
- },
9
- "include": ["./**/*"]
10
- }
@@ -1,11 +0,0 @@
1
- // Things Factory Global Types
2
- // auth-base의 글로벌 타입들을 자동으로 사용할 수 있게 함
3
-
4
- /// <reference types="@things-factory/auth-base/server/types" />
5
-
6
- declare global {
7
- // 추가적인 KPI 모듈만의 글로벌 타입이 필요하면 여기에 선언
8
- // 예: KpiContext, KpiFormula 등
9
- }
10
-
11
- export {}
@@ -1,45 +0,0 @@
1
- import type { FormulaNode } from './parser'
2
- import type { FormulaFunctionMap } from './functions'
3
- import type { ValueProvider } from './provider'
4
-
5
- export interface EvalContext {
6
- functions: FormulaFunctionMap
7
- provider: ValueProvider
8
- }
9
-
10
- // 비동기 evaluator만 유지, 함수명은 evaluateFormula로 단순화
11
- export async function evaluateFormula(node: FormulaNode, ctx: EvalContext): Promise<any> {
12
- switch (node.type) {
13
- case 'number':
14
- return node.value
15
- case 'variable':
16
- return await ctx.provider.get(node.name)
17
- case 'function': {
18
- const fn = ctx.functions[node.name]
19
- if (!fn) throw new Error('Unknown function: ' + node.name)
20
- const args = await Promise.all(node.args.map(arg => evaluateFormula(arg, ctx)))
21
- return fn(...args)
22
- }
23
- case 'binary': {
24
- const l = await evaluateFormula(node.left, ctx)
25
- const r = await evaluateFormula(node.right, ctx)
26
- switch (node.op) {
27
- case '+':
28
- return l + r
29
- case '-':
30
- return l - r
31
- case '*':
32
- return l * r
33
- case '/':
34
- return l / r
35
- default:
36
- throw new Error('Unknown operator: ' + node.op)
37
- }
38
- }
39
- case 'unary':
40
- if (node.op === '-') return -(await evaluateFormula(node.arg, ctx))
41
- throw new Error('Unknown unary operator: ' + node.op)
42
- default:
43
- throw new Error('Unknown node type: ' + (node as any).type)
44
- }
45
- }
@@ -1,67 +0,0 @@
1
- export type FormulaFunction = (...args: any[]) => any
2
- export type FormulaFunctionMap = Record<string, FormulaFunction>
3
-
4
- export const builtinFunctions: FormulaFunctionMap = {
5
- sum: (...args: any[]) => args.reduce((a, b) => a + b, 0),
6
- avg: (...args: any[]) => (args.length ? args.reduce((a, b) => a + b, 0) / args.length : 0),
7
- min: (...args: any[]) => Math.min(...args),
8
- max: (...args: any[]) => Math.max(...args),
9
- round: (v: number, d: number = 0) => Number(v.toFixed(d)),
10
- if: (cond: any, t: any, f: any) => (cond ? t : f),
11
- // 성과 지수 계산을 위한 수치 적분 함수들
12
- integrate: (func: Function, a: number, b: number, n: number = 1000) => {
13
- // 사다리꼴 적분법 (Trapezoidal Rule)
14
- const h = (b - a) / n
15
- let sum = (func(a) + func(b)) / 2
16
- for (let i = 1; i < n; i++) {
17
- sum += func(a + i * h)
18
- }
19
- return sum * h
20
- },
21
-
22
- beta_integrand: (t: number, alpha: number, beta: number) => {
23
- // 베타 분포 피적분함수: t^(α-1) × (1-t)^(β-1)
24
- return Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1)
25
- },
26
-
27
- incomplete_beta: (x: number, alpha: number, beta: number) => {
28
- // 불완전 베타 함수 계산
29
- const func = (t: number) => Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1)
30
- const h = x / 1000
31
- let sum = (func(0) + func(x)) / 2
32
- for (let i = 1; i < 1000; i++) {
33
- sum += func(i * h)
34
- }
35
- return sum * h
36
- },
37
-
38
- complete_beta: (alpha: number, beta: number) => {
39
- // 완전 베타 함수 계산
40
- const func = (t: number) => Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1)
41
- const h = 1 / 1000
42
- let sum = (func(0) + func(1)) / 2
43
- for (let i = 1; i < 1000; i++) {
44
- sum += func(i * h)
45
- }
46
- return sum * h
47
- },
48
-
49
- performance_index: (x: number, alpha1: number, beta1: number, alpha2: number, beta2: number) => {
50
- // 성과 지수 계산: 1 - (불완전 베타 / 완전 베타)
51
- const numerator = builtinFunctions.incomplete_beta(x, alpha1, beta1)
52
- const denominator = builtinFunctions.complete_beta(alpha2, beta2)
53
- return 1 - numerator / denominator
54
- },
55
-
56
- // 지수 함수들
57
- exp: (x: number) => Math.exp(x),
58
- log: (x: number) => Math.log(x),
59
- pow: (x: number, y: number) => Math.pow(x, y),
60
-
61
- // 지수 감쇠 성과 지수
62
- exponential_decay: (value: number, scale: number, power: number) => {
63
- // exp(-(value / scale)^power)
64
- return Math.exp(-Math.pow(value / scale, power))
65
- }
66
- // 필요시 확장
67
- }
@@ -1,4 +0,0 @@
1
- export * from './parser.js'
2
- export * from './evaluator.js'
3
- export * from './functions.js'
4
- export * from './provider.js'
@@ -1,137 +0,0 @@
1
- // KPI formula 파서: 수식 문자열을 AST로 변환
2
-
3
- export type FormulaNode =
4
- | { type: 'number'; value: number }
5
- | { type: 'variable'; name: string }
6
- | { type: 'function'; name: string; args: FormulaNode[] }
7
- | { type: 'binary'; op: string; left: FormulaNode; right: FormulaNode }
8
- | { type: 'unary'; op: string; arg: FormulaNode }
9
-
10
- // 토큰 타입 정의
11
- const TOKEN_REGEX = /\s*(\d+\.\d+|\d+|\[.*?\]|[A-Za-z_][A-Za-z0-9_]*|[+\-*/(),])/y
12
-
13
- interface Token {
14
- type: 'number' | 'variable' | 'function' | 'operator' | 'paren' | 'comma'
15
- value: string
16
- }
17
-
18
- function tokenize(formula: string): Token[] {
19
- const tokens: Token[] = []
20
- let m: RegExpExecArray | null
21
- let lastIndex = 0
22
- while ((m = TOKEN_REGEX.exec(formula))) {
23
- lastIndex = TOKEN_REGEX.lastIndex
24
- // m[0]은 공백 포함, m[1]은 capturing group (공백 제외)
25
- const raw = m[1]
26
- if (/^\d/.test(raw)) {
27
- tokens.push({ type: 'number', value: raw })
28
- } else if (/^\[.*\]$/.test(raw)) {
29
- tokens.push({ type: 'variable', value: raw.slice(1, -1) })
30
- } else if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(raw)) {
31
- tokens.push({ type: 'function', value: raw })
32
- } else if (/^[+\-*/]$/.test(raw)) {
33
- tokens.push({ type: 'operator', value: raw })
34
- } else if (/^[(),]$/.test(raw)) {
35
- if (raw === ',') tokens.push({ type: 'comma', value: raw })
36
- else tokens.push({ type: 'paren', value: raw })
37
- }
38
- }
39
- if (lastIndex < formula.length) {
40
- throw new Error('Unexpected token at: ' + formula.slice(lastIndex))
41
- }
42
- return tokens
43
- }
44
-
45
- // 파서 구현 (재귀 하향, 연산자 우선순위, 함수/변수/숫자/괄호 지원)
46
- export function parseFormula(formula: string): FormulaNode {
47
- const tokens = tokenize(formula)
48
- let pos = 0
49
-
50
- function peek() {
51
- return tokens[pos]
52
- }
53
- function next() {
54
- return tokens[pos++]
55
- }
56
- function expect(type: string, value?: string) {
57
- const t = next()
58
- if (!t || t.type !== type || (value && t.value !== value))
59
- throw new Error('Expected ' + type + (value ? ':' + value : ''))
60
- return t
61
- }
62
-
63
- function parsePrimary(): FormulaNode {
64
- const t = peek()
65
- if (!t) throw new Error('Unexpected end')
66
- if (t.type === 'number') {
67
- next()
68
- return { type: 'number', value: parseFloat(t.value) }
69
- }
70
- if (t.type === 'variable') {
71
- next()
72
- return { type: 'variable', name: t.value }
73
- }
74
- if (t.type === 'function') {
75
- // function 타입 토큰을 만났을 때, 다음 토큰이 '('인지 확인
76
- const name = t.value
77
- next()
78
-
79
- // 다음 토큰이 '('이면 함수 호출, 아니면 변수로 처리
80
- if (peek() && peek().type === 'paren' && peek().value === '(') {
81
- // 함수 호출: func(expr, ...)
82
- expect('paren', '(')
83
- const args: FormulaNode[] = []
84
- if ((peek() && peek().type !== 'paren') || (peek() && peek().value !== ')')) {
85
- while (true) {
86
- args.push(parseExpr())
87
- if (peek() && peek().type === 'comma') {
88
- next()
89
- } else break
90
- }
91
- }
92
- expect('paren', ')')
93
- return { type: 'function', name, args }
94
- } else {
95
- // 변수로 처리
96
- return { type: 'variable', name }
97
- }
98
- }
99
- if (t.type === 'operator' && t.value === '-') {
100
- next()
101
- return { type: 'unary', op: '-', arg: parsePrimary() }
102
- }
103
- if (t.type === 'paren' && t.value === '(') {
104
- next()
105
- const node = parseExpr()
106
- expect('paren', ')')
107
- return node
108
- }
109
- throw new Error('Unexpected token: ' + t.type + ':' + t.value)
110
- }
111
-
112
- function parseMulDiv(): FormulaNode {
113
- let node = parsePrimary()
114
- while (peek() && peek().type === 'operator' && (peek().value === '*' || peek().value === '/')) {
115
- const op = next().value
116
- node = { type: 'binary', op, left: node, right: parsePrimary() }
117
- }
118
- return node
119
- }
120
-
121
- function parseAddSub(): FormulaNode {
122
- let node = parseMulDiv()
123
- while (peek() && peek().type === 'operator' && (peek().value === '+' || peek().value === '-')) {
124
- const op = next().value
125
- node = { type: 'binary', op, left: node, right: parseMulDiv() }
126
- }
127
- return node
128
- }
129
-
130
- function parseExpr(): FormulaNode {
131
- return parseAddSub()
132
- }
133
-
134
- const ast = parseExpr()
135
- if (pos < tokens.length) throw new Error('Unexpected token at end: ' + JSON.stringify(tokens[pos]))
136
- return ast
137
- }
@@ -1,10 +0,0 @@
1
- export interface ValueProvider {
2
- get(name: string): any | Promise<any>
3
- }
4
-
5
- export class ObjectValueProvider implements ValueProvider {
6
- constructor(private values: Record<string, any>) {}
7
- get(name: string) {
8
- return this.values[name]
9
- }
10
- }
@@ -1,2 +0,0 @@
1
- export * from './kpi-metric-value-provider.js'
2
- export * from './kpi-value-provider.js'
@@ -1,79 +0,0 @@
1
- import { getRepository } from '@things-factory/shell'
2
- import { KpiMetric } from '../service/kpi-metric/kpi-metric'
3
- import { KpiMetricValue } from '../service/kpi-metric-value/kpi-metric-value'
4
- import { ValueProvider } from '../calculator/provider'
5
- import { getDefaultValueDate } from '../service/utils/value-date-util'
6
-
7
- export class KpiMetricValueProvider implements ValueProvider {
8
- constructor(
9
- private options: {
10
- valueDate: string
11
- org?: string
12
- domainId: string
13
- tx?: any
14
- }
15
- ) {}
16
-
17
- async get(name: string) {
18
- const metricRepo = getRepository(KpiMetric, this.options.tx)
19
- const metric = await metricRepo.findOne({ where: { name, domain: { id: this.options.domainId } } })
20
- if (!metric) throw new Error(`Metric not found: ${name}`)
21
-
22
- // ALLTIME 타입인 경우 valueDate 무시하고 최신 값 조회
23
- if (metric.periodType === 'ALLTIME') {
24
- const valueRepo = getRepository(KpiMetricValue, this.options.tx)
25
- const value = await valueRepo.findOne({
26
- where: {
27
- metric: { id: metric.id },
28
- org: this.options.org ?? '',
29
- domain: { id: this.options.domainId }
30
- },
31
- order: { createdAt: 'DESC' }
32
- })
33
- if (!value) {
34
- throw new Error(`Metric value not found: metric='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`)
35
- }
36
- return value.value
37
- }
38
-
39
- // metric의 periodType에 맞게 valueDate 보정
40
- let valueDate = this.options.valueDate
41
- if (!valueDate) {
42
- valueDate = getDefaultValueDate(metric.periodType, 'current')
43
- } else {
44
- // valueDate가 metric.periodType에 맞는 포맷인지 검사, 아니면 보정
45
- // (간단히: 길이로 구분, 실제는 정규식 등으로 더 정교하게 가능)
46
- if (metric.periodType === 'MONTH' && valueDate.length > 7) valueDate = valueDate.slice(0, 7)
47
- if (metric.periodType === 'DAY' && valueDate.length > 10) valueDate = valueDate.slice(0, 10)
48
- // 기타 periodType별 보정 추가 가능
49
- }
50
- const valueRepo = getRepository(KpiMetricValue, this.options.tx)
51
- var value = await valueRepo.findOne({
52
- where: {
53
- metric: { id: metric.id },
54
- valueDate,
55
- org: this.options.org ?? '',
56
- domain: { id: this.options.domainId }
57
- }
58
- })
59
- if (!value) {
60
- // 임시로 최신의 데이타 하나를 찾아오는 것으로 하자.
61
- value = await valueRepo.findOne({
62
- where: {
63
- metric: { id: metric.id },
64
- org: this.options.org ?? '',
65
- domain: { id: this.options.domainId }
66
- },
67
- order: { valueDate: 'DESC' }
68
- })
69
-
70
- if (!value) {
71
- throw new Error(
72
- `Metric value not found: metric='${name}', org='${this.options.org ?? ''}', valueDate='${valueDate}', periodType='${metric.periodType}'`
73
- )
74
- }
75
- }
76
-
77
- return value.value
78
- }
79
- }
@@ -1,51 +0,0 @@
1
- import { getRepository } from '@things-factory/shell'
2
- import { Kpi } from '../service/kpi/kpi'
3
- import { KpiValue } from '../service/kpi-value/kpi-value'
4
- import { ValueProvider } from '../calculator/provider'
5
-
6
- export class KpiValueProvider implements ValueProvider {
7
- constructor(
8
- private options: {
9
- valueDate: string
10
- org?: string
11
- domainId: string
12
- tx?: any
13
- }
14
- ) {}
15
-
16
- async get(name: string) {
17
- const kpiRepo = getRepository(Kpi, this.options.tx)
18
- const kpi = await kpiRepo.findOne({ where: { name, domain: { id: this.options.domainId } } })
19
- if (!kpi) throw new Error(`KPI not found: ${name}`)
20
- const valueRepo = getRepository(KpiValue, this.options.tx)
21
-
22
- // SINGLE 타입인 경우 valueDate 무시하고 최신 값 조회
23
- const whereCondition: any = {
24
- kpi: { id: kpi.id },
25
- org: this.options.org ?? '',
26
- domain: { id: this.options.domainId }
27
- }
28
-
29
- if (kpi.periodType === 'ALLTIME') {
30
- // ALLTIME 타입은 valueDate 무시하고 최신 값 사용
31
- const value = await valueRepo.findOne({
32
- where: whereCondition,
33
- order: { createdAt: 'DESC' }
34
- })
35
- if (!value) {
36
- throw new Error(`KPI value not found: kpi='${name}', org='${this.options.org ?? ''}' (ALLTIME type)`)
37
- }
38
- return value.value
39
- } else {
40
- // 기존 로직: 특정 날짜의 값 조회
41
- whereCondition.valueDate = this.options.valueDate
42
- const value = await valueRepo.findOne({ where: whereCondition })
43
- if (!value) {
44
- throw new Error(
45
- `KPI value not found: kpi='${name}', org='${this.options.org ?? ''}', valueDate='${this.options.valueDate}'`
46
- )
47
- }
48
- return value.value
49
- }
50
- }
51
- }
package/server/index.ts DELETED
@@ -1,6 +0,0 @@
1
- export * from './migrations/index.js'
2
- export * from './service/index.js'
3
- export * from './calculator/index.js'
4
- export * from './controllers/index.js'
5
-
6
- import './routes'
@@ -1,124 +0,0 @@
1
- import { MigrationInterface, QueryRunner } from 'typeorm'
2
- import { Domain, getRepository } from '@things-factory/shell'
3
- import { KpiPeriodType } from '../service/kpi/kpi'
4
- import { KpiMetric, KpiMetricCollectType } from '../service/kpi-metric/kpi-metric'
5
- import * as fs from 'fs'
6
- import * as path from 'path'
7
-
8
- interface KpiMetricSeedData {
9
- name: string
10
- description: string
11
- unit?: string | null
12
- source?: string | null
13
- field_name?: string | null
14
- active: boolean
15
- schedule_id?: string | null
16
- timezone: string
17
- data_set_id?: string | null
18
- period_type: string
19
- collect_type: string
20
- schedule?: string | null
21
- created_at?: string
22
- updated_at?: string
23
- }
24
-
25
- export class SeedKpiMetrics1752190849680 implements MigrationInterface {
26
- name = 'SeedKpiMetrics1752190849680'
27
-
28
- public async up(queryRunner: QueryRunner): Promise<void> {
29
- try {
30
- const repository = getRepository(KpiMetric)
31
- const domainRepository = getRepository(Domain)
32
-
33
- // SYSTEM 도메인 확인
34
- const domain: Domain = await domainRepository.findOneBy({ name: 'SYSTEM' })
35
- if (!domain) {
36
- console.log('❌ SYSTEM domain not found')
37
- return
38
- }
39
-
40
- // JSON 파일 읽기
41
- const seedFilePath = path.join(__dirname, 'seed-data', 'kpi-metrics-seed.json')
42
- const seedData: KpiMetricSeedData[] = JSON.parse(fs.readFileSync(seedFilePath, 'utf8'))
43
-
44
- console.log(`📊 Processing ${seedData.length} KPI metrics from JSON seed`)
45
-
46
- for (const metricData of seedData) {
47
- try {
48
- const existingMetric = await repository.findOne({
49
- where: { name: metricData.name, domain: { id: domain.id } }
50
- })
51
-
52
- if (existingMetric) {
53
- console.log(`⚠️ KPI Metric already exists: ${metricData.name}`)
54
- continue
55
- }
56
-
57
- const metric = await repository.save({
58
- name: metricData.name,
59
- description: metricData.description,
60
- unit: metricData.unit,
61
- source: metricData.source,
62
- fieldName: metricData.field_name,
63
- active: metricData.active,
64
- scheduleId: metricData.schedule_id,
65
- timezone: metricData.timezone,
66
- dataSetId: metricData.data_set_id,
67
- periodType: KpiPeriodType[metricData.period_type as keyof typeof KpiPeriodType],
68
- collectType: KpiMetricCollectType[metricData.collect_type as keyof typeof KpiMetricCollectType],
69
- schedule: metricData.schedule,
70
- domain
71
- })
72
-
73
- console.log(`✅ Created KPI Metric: ${metric.name}`)
74
- } catch (error) {
75
- console.error(`❌ Error creating KPI Metric "${metricData.name}":`, error)
76
- }
77
- }
78
-
79
- console.log(`🎉 Successfully processed KPI metrics from JSON seed`)
80
- } catch (error) {
81
- console.error('❌ Error seeding KPI metrics from JSON:', error)
82
- throw error
83
- }
84
- }
85
-
86
- public async down(queryRunner: QueryRunner): Promise<void> {
87
- try {
88
- const repository = getRepository(KpiMetric)
89
- const domainRepository = getRepository(Domain)
90
-
91
- const domain: Domain = await domainRepository.findOneBy({ name: 'SYSTEM' })
92
- if (!domain) {
93
- console.log('⚠️ SYSTEM domain not found. Nothing to delete.')
94
- return
95
- }
96
-
97
- // JSON 파일 읽기
98
- const seedFilePath = path.join(__dirname, 'seed-data', 'kpi-metrics-seed.json')
99
- const seedData: KpiMetricSeedData[] = JSON.parse(fs.readFileSync(seedFilePath, 'utf8'))
100
-
101
- console.log(`🗑️ Cleaning up ${seedData.length} KPI metrics`)
102
-
103
- for (const metricData of seedData.reverse()) {
104
- try {
105
- const metric = await repository.findOne({
106
- where: { name: metricData.name, domain: { id: domain.id } }
107
- })
108
-
109
- if (metric) {
110
- await repository.delete(metric.id)
111
- console.log(`✅ Deleted KPI Metric: ${metricData.name}`)
112
- }
113
- } catch (error) {
114
- console.error(`❌ Error deleting KPI Metric "${metricData.name}":`, error)
115
- }
116
- }
117
-
118
- console.log(`🎉 Successfully cleaned up KPI metrics`)
119
- } catch (error) {
120
- console.error('❌ Error cleaning up KPI metrics:', error)
121
- throw error
122
- }
123
- }
124
- }