@things-factory/kpi 9.1.19 → 10.0.0-beta.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.
Files changed (140) 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/service/index.d.ts +1 -1
  43. package/dist-server/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +18 -18
  45. package/client/tsconfig.json +0 -11
  46. package/dist-server/tsconfig.json +0 -10
  47. package/server/@types/index.d.ts +0 -11
  48. package/server/calculator/evaluator.ts +0 -45
  49. package/server/calculator/functions.ts +0 -67
  50. package/server/calculator/index.ts +0 -4
  51. package/server/calculator/parser.ts +0 -137
  52. package/server/calculator/provider.ts +0 -10
  53. package/server/controllers/index.ts +0 -2
  54. package/server/controllers/kpi-metric-value-provider.ts +0 -79
  55. package/server/controllers/kpi-value-provider.ts +0 -51
  56. package/server/index.ts +0 -6
  57. package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
  58. package/server/migrations/1752190849681-seed-kpi.ts +0 -356
  59. package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
  60. package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
  61. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
  62. package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
  63. package/server/migrations/grade-data/x11-performance-table.json +0 -962
  64. package/server/migrations/grade-data/x12-performance-table.json +0 -611
  65. package/server/migrations/grade-data/x14-performance-table.json +0 -42
  66. package/server/migrations/grade-data/x21-performance-table.json +0 -889
  67. package/server/migrations/grade-data/x22-performance-table.json +0 -1064
  68. package/server/migrations/grade-data/x23-performance-table.json +0 -42
  69. package/server/migrations/grade-data/x31-performance-table.json +0 -644
  70. package/server/migrations/grade-data/x32-performance-table.json +0 -993
  71. package/server/migrations/grade-data/x33-performance-table.json +0 -195
  72. package/server/migrations/grade-data/x34-performance-table.json +0 -12
  73. package/server/migrations/grade-data/x35-performance-table.json +0 -42
  74. package/server/migrations/grade-data/x41-performance-table.json +0 -825
  75. package/server/migrations/grade-data/x42-performance-table.json +0 -786
  76. package/server/migrations/grade-data/x43-performance-table.json +0 -12
  77. package/server/migrations/grade-data/x44-performance-table.json +0 -42
  78. package/server/migrations/grade-data/x51-performance-table.json +0 -924
  79. package/server/migrations/grade-data/x52-performance-table.json +0 -42
  80. package/server/migrations/grade-data/x61-performance-table.json +0 -261
  81. package/server/migrations/grade-data/x62-performance-table.json +0 -42
  82. package/server/migrations/index.ts +0 -9
  83. package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
  84. package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
  85. package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
  86. package/server/migrations/seed-data/kpi-values-seed.json +0 -402
  87. package/server/migrations/seed-data/kpis-seed.json +0 -488
  88. package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
  89. package/server/routes.ts +0 -81
  90. package/server/service/index.ts +0 -51
  91. package/server/service/kpi/aggregate-kpi.ts +0 -103
  92. package/server/service/kpi/event-subscriber.ts +0 -29
  93. package/server/service/kpi/index.ts +0 -9
  94. package/server/service/kpi/kpi-formula.service.ts +0 -164
  95. package/server/service/kpi/kpi-grade.types.ts +0 -28
  96. package/server/service/kpi/kpi-history.ts +0 -126
  97. package/server/service/kpi/kpi-mutation.ts +0 -553
  98. package/server/service/kpi/kpi-query.ts +0 -224
  99. package/server/service/kpi/kpi-type.ts +0 -151
  100. package/server/service/kpi/kpi.ts +0 -254
  101. package/server/service/kpi-alert/index.ts +0 -3
  102. package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
  103. package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
  104. package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
  105. package/server/service/kpi-metric/index.ts +0 -7
  106. package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
  107. package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
  108. package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
  109. package/server/service/kpi-metric/kpi-metric.ts +0 -134
  110. package/server/service/kpi-metric-value/index.ts +0 -7
  111. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
  112. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
  113. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
  114. package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
  115. package/server/service/kpi-org-scope/index.ts +0 -6
  116. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
  117. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
  118. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
  119. package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
  120. package/server/service/kpi-scope/index.ts +0 -11
  121. package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
  122. package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
  123. package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
  124. package/server/service/kpi-scope/kpi-scope.ts +0 -143
  125. package/server/service/kpi-statistic/index.ts +0 -7
  126. package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
  127. package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
  128. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
  129. package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
  130. package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
  131. package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
  132. package/server/service/kpi-value/index.ts +0 -7
  133. package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
  134. package/server/service/kpi-value/kpi-value-query.ts +0 -61
  135. package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
  136. package/server/service/kpi-value/kpi-value-type.ts +0 -122
  137. package/server/service/kpi-value/kpi-value.ts +0 -160
  138. package/server/service/utils/value-date-util.ts +0 -119
  139. package/server/tsconfig.json +0 -10
  140. 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.1.19",
3
+ "version": "10.0.0-beta.2",
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.1.19",
45
- "@things-factory/dataset": "^9.1.19",
46
- "@things-factory/shell": "^9.1.19"
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.2",
45
+ "@things-factory/dataset": "^10.0.0-beta.2",
46
+ "@things-factory/shell": "^10.0.0-beta.2"
47
47
  },
48
- "gitHead": "078438034dbe19915108e89ff24024f7044a85a9"
48
+ "gitHead": "ea8a08ff802ae28a37fdda605b6356482e7a6faf"
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
- }