@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.
- package/client/pages/kpi/kpi-list-page.ts +339 -525
- package/client/pages/kpi/kpi-tree-page.ts +135 -207
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +146 -226
- package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +187 -295
- package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +123 -194
- package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.ts +57 -91
- package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +180 -278
- package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +186 -286
- package/client/pages/kpi-value/kpi-value-editor-page.ts +189 -292
- package/client/pages/kpi-value/kpi-value-list-page.ts +170 -264
- package/dist-client/pages/kpi/kpi-list-page.d.ts +0 -6
- package/dist-client/pages/kpi/kpi-list-page.js +150 -282
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +1 -7
- package/dist-client/pages/kpi/kpi-tree-page.js +76 -127
- package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +62 -116
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +82 -140
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +54 -98
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -7
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +30 -57
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +91 -153
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +81 -155
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +1 -7
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +80 -136
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -6
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +73 -134
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -18
- package/client/tsconfig.json +0 -11
- package/dist-server/tsconfig.json +0 -10
- package/server/@types/index.d.ts +0 -11
- package/server/calculator/evaluator.ts +0 -45
- package/server/calculator/functions.ts +0 -67
- package/server/calculator/index.ts +0 -4
- package/server/calculator/parser.ts +0 -137
- package/server/calculator/provider.ts +0 -10
- package/server/controllers/index.ts +0 -2
- package/server/controllers/kpi-metric-value-provider.ts +0 -79
- package/server/controllers/kpi-value-provider.ts +0 -51
- package/server/index.ts +0 -6
- package/server/migrations/1752190849680-seed-kpi-metrics.ts +0 -124
- package/server/migrations/1752190849681-seed-kpi.ts +0 -356
- package/server/migrations/1752192090123-add-grades-to-kpi.ts +0 -67
- package/server/migrations/1752192090124-add-kpi-statistics.ts +0 -719
- package/server/migrations/1752192090128-seed-kpi-org-scope.ts +0 -132
- package/server/migrations/1752192090129-seed-kpi-values.ts +0 -207
- package/server/migrations/grade-data/x11-performance-table.json +0 -962
- package/server/migrations/grade-data/x12-performance-table.json +0 -611
- package/server/migrations/grade-data/x14-performance-table.json +0 -42
- package/server/migrations/grade-data/x21-performance-table.json +0 -889
- package/server/migrations/grade-data/x22-performance-table.json +0 -1064
- package/server/migrations/grade-data/x23-performance-table.json +0 -42
- package/server/migrations/grade-data/x31-performance-table.json +0 -644
- package/server/migrations/grade-data/x32-performance-table.json +0 -993
- package/server/migrations/grade-data/x33-performance-table.json +0 -195
- package/server/migrations/grade-data/x34-performance-table.json +0 -12
- package/server/migrations/grade-data/x35-performance-table.json +0 -42
- package/server/migrations/grade-data/x41-performance-table.json +0 -825
- package/server/migrations/grade-data/x42-performance-table.json +0 -786
- package/server/migrations/grade-data/x43-performance-table.json +0 -12
- package/server/migrations/grade-data/x44-performance-table.json +0 -42
- package/server/migrations/grade-data/x51-performance-table.json +0 -924
- package/server/migrations/grade-data/x52-performance-table.json +0 -42
- package/server/migrations/grade-data/x61-performance-table.json +0 -261
- package/server/migrations/grade-data/x62-performance-table.json +0 -42
- package/server/migrations/index.ts +0 -9
- package/server/migrations/seed-data/kpi-metrics-seed.json +0 -454
- package/server/migrations/seed-data/kpi-org-scope-seed.json +0 -1676
- package/server/migrations/seed-data/kpi-scopes-seed.json +0 -121
- package/server/migrations/seed-data/kpi-values-seed.json +0 -402
- package/server/migrations/seed-data/kpis-seed.json +0 -488
- package/server/migrations/seed-data/scope-definitions-seed.json +0 -90
- package/server/routes.ts +0 -81
- package/server/service/index.ts +0 -51
- package/server/service/kpi/aggregate-kpi.ts +0 -103
- package/server/service/kpi/event-subscriber.ts +0 -29
- package/server/service/kpi/index.ts +0 -9
- package/server/service/kpi/kpi-formula.service.ts +0 -164
- package/server/service/kpi/kpi-grade.types.ts +0 -28
- package/server/service/kpi/kpi-history.ts +0 -126
- package/server/service/kpi/kpi-mutation.ts +0 -553
- package/server/service/kpi/kpi-query.ts +0 -224
- package/server/service/kpi/kpi-type.ts +0 -151
- package/server/service/kpi/kpi.ts +0 -254
- package/server/service/kpi-alert/index.ts +0 -3
- package/server/service/kpi-alert/kpi-alert-query.ts +0 -59
- package/server/service/kpi-alert/kpi-alert-type.ts +0 -20
- package/server/service/kpi-metric/aggregate-kpi-metric.ts +0 -132
- package/server/service/kpi-metric/index.ts +0 -7
- package/server/service/kpi-metric/kpi-metric-mutation.ts +0 -309
- package/server/service/kpi-metric/kpi-metric-query.ts +0 -70
- package/server/service/kpi-metric/kpi-metric-type.ts +0 -111
- package/server/service/kpi-metric/kpi-metric.ts +0 -134
- package/server/service/kpi-metric-value/index.ts +0 -7
- package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +0 -270
- package/server/service/kpi-metric-value/kpi-metric-value-query.ts +0 -62
- package/server/service/kpi-metric-value/kpi-metric-value-type.ts +0 -82
- package/server/service/kpi-metric-value/kpi-metric-value.ts +0 -93
- package/server/service/kpi-org-scope/index.ts +0 -6
- package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +0 -173
- package/server/service/kpi-org-scope/kpi-org-scope-query.ts +0 -127
- package/server/service/kpi-org-scope/kpi-org-scope-type.ts +0 -68
- package/server/service/kpi-org-scope/kpi-org-scope.ts +0 -123
- package/server/service/kpi-scope/index.ts +0 -11
- package/server/service/kpi-scope/kpi-scope-mutation.ts +0 -129
- package/server/service/kpi-scope/kpi-scope-query.ts +0 -63
- package/server/service/kpi-scope/kpi-scope-type.ts +0 -96
- package/server/service/kpi-scope/kpi-scope.ts +0 -143
- package/server/service/kpi-statistic/index.ts +0 -7
- package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +0 -231
- package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +0 -410
- package/server/service/kpi-statistic/kpi-statistic-mutation.ts +0 -291
- package/server/service/kpi-statistic/kpi-statistic-query.ts +0 -146
- package/server/service/kpi-statistic/kpi-statistic-type.ts +0 -152
- package/server/service/kpi-statistic/kpi-statistic.ts +0 -199
- package/server/service/kpi-value/index.ts +0 -7
- package/server/service/kpi-value/kpi-value-mutation.ts +0 -432
- package/server/service/kpi-value/kpi-value-query.ts +0 -61
- package/server/service/kpi-value/kpi-value-score.service.ts +0 -106
- package/server/service/kpi-value/kpi-value-type.ts +0 -122
- package/server/service/kpi-value/kpi-value.ts +0 -160
- package/server/service/utils/value-date-util.ts +0 -119
- package/server/tsconfig.json +0 -10
- package/server/types/global.d.ts +0 -8
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { Resolver, Query, FieldResolver, Root, Args, Arg, Ctx, Directive } from 'type-graphql'
|
|
2
|
-
import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
|
|
3
|
-
import { User } from '@things-factory/auth-base'
|
|
4
|
-
import type ResolverContext from '@things-factory/auth-base'
|
|
5
|
-
import { KpiStatistic } from './kpi-statistic.js'
|
|
6
|
-
import { KpiStatisticList } from './kpi-statistic-type.js'
|
|
7
|
-
import { Kpi, KpiPeriodType } from '../kpi/kpi.js'
|
|
8
|
-
import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope.js'
|
|
9
|
-
|
|
10
|
-
@Resolver(KpiStatistic)
|
|
11
|
-
export class KpiStatisticQuery {
|
|
12
|
-
@Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
|
|
13
|
-
@Query(returns => KpiStatistic!, { nullable: true, description: 'To fetch a KpiStatistic' })
|
|
14
|
-
async kpiStatistic(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<KpiStatistic> {
|
|
15
|
-
const { domain } = context.state
|
|
16
|
-
|
|
17
|
-
return await getRepository(KpiStatistic).findOne({
|
|
18
|
-
where: { domain: { id: domain.id }, id }
|
|
19
|
-
})
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
@Query(returns => KpiStatisticList, { description: 'To fetch multiple KpiStatistics' })
|
|
23
|
-
@Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
|
|
24
|
-
async kpiStatistics(
|
|
25
|
-
@Args(type => ListParam) params: ListParam,
|
|
26
|
-
@Ctx() context: ResolverContext
|
|
27
|
-
): Promise<KpiStatisticList> {
|
|
28
|
-
const { domain } = context.state
|
|
29
|
-
|
|
30
|
-
const queryBuilder = getQueryBuilderFromListParams({
|
|
31
|
-
domain,
|
|
32
|
-
params,
|
|
33
|
-
repository: await getRepository(KpiStatistic),
|
|
34
|
-
searchables: ['kpi', 'valueDate'],
|
|
35
|
-
filtersMap: {
|
|
36
|
-
kpi: { columnName: 'id', relationColumn: 'kpi' },
|
|
37
|
-
kpiOrgScope: { columnName: 'id', relationColumn: 'kpiOrgScope' },
|
|
38
|
-
periodType: { columnName: 'periodType' },
|
|
39
|
-
valueDate: { columnName: 'valueDate' },
|
|
40
|
-
scope02: {
|
|
41
|
-
columnName: 'scope02',
|
|
42
|
-
relationColumn: 'kpiOrgScope'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
const [items, total] = await queryBuilder.getManyAndCount()
|
|
48
|
-
|
|
49
|
-
return { items, total }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
@FieldResolver(type => Kpi)
|
|
53
|
-
async kpi(@Root() kpiStatistic: KpiStatistic): Promise<Kpi> {
|
|
54
|
-
return kpiStatistic.kpiId && (await getRepository(Kpi).findOneBy({ id: kpiStatistic.kpiId }))
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@FieldResolver(type => Domain)
|
|
58
|
-
async domain(@Root() kpiStatistic: KpiStatistic): Promise<Domain> {
|
|
59
|
-
return kpiStatistic.domainId && (await getRepository(Domain).findOneBy({ id: kpiStatistic.domainId }))
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@FieldResolver(type => User)
|
|
63
|
-
async updater(@Root() kpiStatistic: KpiStatistic): Promise<User> {
|
|
64
|
-
return kpiStatistic.updaterId && (await getRepository(User).findOneBy({ id: kpiStatistic.updaterId }))
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
@FieldResolver(type => User)
|
|
68
|
-
async creator(@Root() kpiStatistic: KpiStatistic): Promise<User> {
|
|
69
|
-
return kpiStatistic.creatorId && (await getRepository(User).findOneBy({ id: kpiStatistic.creatorId }))
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
@FieldResolver(type => KpiOrgScope)
|
|
73
|
-
async kpiOrgScope(@Root() kpiStatistic: KpiStatistic): Promise<KpiOrgScope> {
|
|
74
|
-
return kpiStatistic.kpiOrgScopeId && (await getRepository(KpiOrgScope).findOneBy({ id: kpiStatistic.kpiOrgScopeId }))
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
|
|
78
|
-
@Query(returns => [KpiStatistic], {
|
|
79
|
-
description: 'Get regional statistics for dashboard map visualization'
|
|
80
|
-
})
|
|
81
|
-
async regionalKpiStatistics(
|
|
82
|
-
@Arg('valueDate') valueDate: string,
|
|
83
|
-
@Arg('periodType', type => KpiPeriodType, { defaultValue: KpiPeriodType.MONTH }) periodType: KpiPeriodType,
|
|
84
|
-
@Arg('regions', type => [String], { nullable: true }) regions: string[] | undefined,
|
|
85
|
-
@Ctx() context: ResolverContext
|
|
86
|
-
): Promise<KpiStatistic[]> {
|
|
87
|
-
const { domain } = context.state
|
|
88
|
-
|
|
89
|
-
const queryBuilder = getRepository(KpiStatistic)
|
|
90
|
-
.createQueryBuilder('stat')
|
|
91
|
-
.leftJoinAndSelect('stat.kpi', 'kpi')
|
|
92
|
-
.leftJoinAndSelect('stat.kpiOrgScope', 'orgScope')
|
|
93
|
-
.where('stat.domain = :domainId', { domainId: domain.id })
|
|
94
|
-
.andWhere('stat.periodType = :periodType', { periodType })
|
|
95
|
-
.andWhere('stat.valueDate = :valueDate', { valueDate })
|
|
96
|
-
.andWhere('orgScope.scope02 IS NOT NULL') // 지역 정보가 있는 것만
|
|
97
|
-
|
|
98
|
-
if (regions && regions.length > 0) {
|
|
99
|
-
queryBuilder.andWhere('orgScope.scope02 IN (:...regions)', { regions })
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
queryBuilder.orderBy('orgScope.scope02', 'ASC')
|
|
103
|
-
.addOrderBy('kpi.name', 'ASC')
|
|
104
|
-
|
|
105
|
-
return await queryBuilder.getMany()
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
@Directive('@privilege(category: "kpi", privilege: "query", domainOwnerGranted: true, superUserGranted: true)')
|
|
109
|
-
@Query(returns => [KpiStatistic], {
|
|
110
|
-
description: 'Get aggregated statistics by scope02 (regional level) for dashboard'
|
|
111
|
-
})
|
|
112
|
-
async dashboardRegionalStatistics(
|
|
113
|
-
@Arg('valueDate') valueDate: string,
|
|
114
|
-
@Arg('periodType', type => KpiPeriodType, { defaultValue: KpiPeriodType.MONTH }) periodType: KpiPeriodType,
|
|
115
|
-
@Ctx() context: ResolverContext
|
|
116
|
-
): Promise<KpiStatistic[]> {
|
|
117
|
-
const { domain } = context.state
|
|
118
|
-
|
|
119
|
-
// 지역별로 평균을 내서 하나의 통계로 합침
|
|
120
|
-
const query = `
|
|
121
|
-
SELECT
|
|
122
|
-
'regional-aggregate-' || orgScope.scope02 as id,
|
|
123
|
-
kpi.id as kpiId,
|
|
124
|
-
kpi.name as kpiName,
|
|
125
|
-
orgScope.scope02 as region,
|
|
126
|
-
AVG(stat.mean) as avgMean,
|
|
127
|
-
AVG(stat.median) as avgMedian,
|
|
128
|
-
MIN(stat.minimum) as overallMin,
|
|
129
|
-
MAX(stat.maximum) as overallMax,
|
|
130
|
-
AVG(stat.standardDeviation) as avgStdDev,
|
|
131
|
-
SUM(stat.count) as totalCount,
|
|
132
|
-
COUNT(*) as orgCount
|
|
133
|
-
FROM kpi_statistic stat
|
|
134
|
-
LEFT JOIN kpi ON stat.kpiId = kpi.id
|
|
135
|
-
LEFT JOIN kpi_org_scope orgScope ON stat.kpiOrgScopeId = orgScope.id
|
|
136
|
-
WHERE stat.domainId = ?
|
|
137
|
-
AND stat.periodType = ?
|
|
138
|
-
AND stat.valueDate = ?
|
|
139
|
-
AND orgScope.scope02 IS NOT NULL
|
|
140
|
-
GROUP BY kpi.id, orgScope.scope02
|
|
141
|
-
ORDER BY orgScope.scope02, kpi.name
|
|
142
|
-
`
|
|
143
|
-
|
|
144
|
-
return await getRepository(KpiStatistic).query(query, [domain.id, periodType, valueDate])
|
|
145
|
-
}
|
|
146
|
-
}
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { ObjectType, Field, InputType, Int, ID } from 'type-graphql'
|
|
2
|
-
|
|
3
|
-
import { ObjectRef, ScalarObject } from '@things-factory/shell'
|
|
4
|
-
import { KpiPeriodType } from '../kpi/kpi'
|
|
5
|
-
|
|
6
|
-
import { KpiStatistic } from './kpi-statistic.js'
|
|
7
|
-
|
|
8
|
-
@InputType()
|
|
9
|
-
export class NewKpiStatistic {
|
|
10
|
-
@Field({ nullable: true })
|
|
11
|
-
kpi?: ObjectRef
|
|
12
|
-
|
|
13
|
-
@Field({
|
|
14
|
-
description:
|
|
15
|
-
'Date or period for which this statistic is calculated (e.g., day: YYYY-MM-DD, month: YYYY-MM, quarter: YYYY-Qn, range: YYYY-MM-DD~YYYY-MM-DD).'
|
|
16
|
-
})
|
|
17
|
-
valueDate: string
|
|
18
|
-
|
|
19
|
-
@Field(type => KpiPeriodType)
|
|
20
|
-
periodType: KpiPeriodType
|
|
21
|
-
|
|
22
|
-
// 핵심 통계 필드들
|
|
23
|
-
@Field({ nullable: true })
|
|
24
|
-
count?: number
|
|
25
|
-
|
|
26
|
-
@Field({ nullable: true })
|
|
27
|
-
sum?: number
|
|
28
|
-
|
|
29
|
-
@Field({ nullable: true })
|
|
30
|
-
range?: number
|
|
31
|
-
|
|
32
|
-
@Field({ nullable: true })
|
|
33
|
-
mean?: number
|
|
34
|
-
|
|
35
|
-
@Field({ nullable: true })
|
|
36
|
-
median?: number
|
|
37
|
-
|
|
38
|
-
@Field({ nullable: true })
|
|
39
|
-
minimum?: number
|
|
40
|
-
|
|
41
|
-
@Field({ nullable: true })
|
|
42
|
-
maximum?: number
|
|
43
|
-
|
|
44
|
-
@Field({ nullable: true })
|
|
45
|
-
standardDeviation?: number
|
|
46
|
-
|
|
47
|
-
@Field({ nullable: true })
|
|
48
|
-
variance?: number
|
|
49
|
-
|
|
50
|
-
@Field({ nullable: true })
|
|
51
|
-
percentile25?: number
|
|
52
|
-
|
|
53
|
-
@Field({ nullable: true })
|
|
54
|
-
percentile75?: number
|
|
55
|
-
|
|
56
|
-
@Field({ nullable: true })
|
|
57
|
-
iqr?: number
|
|
58
|
-
|
|
59
|
-
@Field({ nullable: true })
|
|
60
|
-
lowerFence?: number
|
|
61
|
-
|
|
62
|
-
@Field({ nullable: true })
|
|
63
|
-
upperFence?: number
|
|
64
|
-
|
|
65
|
-
// 확장 필드들
|
|
66
|
-
@Field(type => ScalarObject, { nullable: true })
|
|
67
|
-
additionalStatistics?: { [metricName: string]: number }
|
|
68
|
-
|
|
69
|
-
@Field(type => ScalarObject, { nullable: true })
|
|
70
|
-
metadata?: { [key: string]: any }
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
@InputType()
|
|
74
|
-
export class KpiStatisticPatch {
|
|
75
|
-
@Field(type => ID, { nullable: true })
|
|
76
|
-
id?: string
|
|
77
|
-
|
|
78
|
-
@Field({ nullable: true })
|
|
79
|
-
kpi?: ObjectRef
|
|
80
|
-
|
|
81
|
-
@Field({
|
|
82
|
-
nullable: true,
|
|
83
|
-
description:
|
|
84
|
-
'Date or period for which this statistic is calculated (e.g., day: YYYY-MM-DD, month: YYYY-MM, quarter: YYYY-Qn, range: YYYY-MM-DD~YYYY-MM-DD).'
|
|
85
|
-
})
|
|
86
|
-
valueDate?: string
|
|
87
|
-
|
|
88
|
-
@Field(type => KpiPeriodType, { nullable: true })
|
|
89
|
-
periodType?: KpiPeriodType
|
|
90
|
-
|
|
91
|
-
// 핵심 통계 필드들
|
|
92
|
-
@Field({ nullable: true })
|
|
93
|
-
count?: number
|
|
94
|
-
|
|
95
|
-
@Field({ nullable: true })
|
|
96
|
-
sum?: number
|
|
97
|
-
|
|
98
|
-
@Field({ nullable: true })
|
|
99
|
-
range?: number
|
|
100
|
-
|
|
101
|
-
@Field({ nullable: true })
|
|
102
|
-
mean?: number
|
|
103
|
-
|
|
104
|
-
@Field({ nullable: true })
|
|
105
|
-
median?: number
|
|
106
|
-
|
|
107
|
-
@Field({ nullable: true })
|
|
108
|
-
minimum?: number
|
|
109
|
-
|
|
110
|
-
@Field({ nullable: true })
|
|
111
|
-
maximum?: number
|
|
112
|
-
|
|
113
|
-
@Field({ nullable: true })
|
|
114
|
-
standardDeviation?: number
|
|
115
|
-
|
|
116
|
-
@Field({ nullable: true })
|
|
117
|
-
variance?: number
|
|
118
|
-
|
|
119
|
-
@Field({ nullable: true })
|
|
120
|
-
percentile25?: number
|
|
121
|
-
|
|
122
|
-
@Field({ nullable: true })
|
|
123
|
-
percentile75?: number
|
|
124
|
-
|
|
125
|
-
@Field({ nullable: true })
|
|
126
|
-
iqr?: number
|
|
127
|
-
|
|
128
|
-
@Field({ nullable: true })
|
|
129
|
-
lowerFence?: number
|
|
130
|
-
|
|
131
|
-
@Field({ nullable: true })
|
|
132
|
-
upperFence?: number
|
|
133
|
-
|
|
134
|
-
// 확장 필드들
|
|
135
|
-
@Field(type => ScalarObject, { nullable: true })
|
|
136
|
-
additionalStatistics?: { [metricName: string]: number }
|
|
137
|
-
|
|
138
|
-
@Field(type => ScalarObject, { nullable: true })
|
|
139
|
-
metadata?: { [key: string]: any }
|
|
140
|
-
|
|
141
|
-
@Field({ nullable: true })
|
|
142
|
-
cuFlag?: string
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
@ObjectType()
|
|
146
|
-
export class KpiStatisticList {
|
|
147
|
-
@Field(type => [KpiStatistic])
|
|
148
|
-
items: KpiStatistic[]
|
|
149
|
-
|
|
150
|
-
@Field(type => Int)
|
|
151
|
-
total: number
|
|
152
|
-
}
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
CreateDateColumn,
|
|
3
|
-
UpdateDateColumn,
|
|
4
|
-
Entity,
|
|
5
|
-
Index,
|
|
6
|
-
Column,
|
|
7
|
-
RelationId,
|
|
8
|
-
ManyToOne,
|
|
9
|
-
PrimaryGeneratedColumn
|
|
10
|
-
} from 'typeorm'
|
|
11
|
-
import { ObjectType, Field, Int, ID } from 'type-graphql'
|
|
12
|
-
|
|
13
|
-
import { Domain, ScalarObject } from '@things-factory/shell'
|
|
14
|
-
import { User } from '@things-factory/auth-base'
|
|
15
|
-
import { Kpi, KpiPeriodType } from '../kpi/kpi'
|
|
16
|
-
import { KpiOrgScope } from '../kpi-org-scope/kpi-org-scope'
|
|
17
|
-
|
|
18
|
-
@Entity()
|
|
19
|
-
@Index('ix_kpi_statistic_target', (kpiStatistic: KpiStatistic) => [kpiStatistic.domain, kpiStatistic.kpi])
|
|
20
|
-
@Index('ix_kpi_statistic_period', (kpiStatistic: KpiStatistic) => [
|
|
21
|
-
kpiStatistic.domain,
|
|
22
|
-
kpiStatistic.valueDate,
|
|
23
|
-
kpiStatistic.periodType
|
|
24
|
-
])
|
|
25
|
-
@Index('ix_kpi_statistic_scope', (kpiStatistic: KpiStatistic) => [
|
|
26
|
-
kpiStatistic.domain,
|
|
27
|
-
kpiStatistic.kpi,
|
|
28
|
-
kpiStatistic.kpiOrgScope,
|
|
29
|
-
kpiStatistic.valueDate
|
|
30
|
-
])
|
|
31
|
-
@Index('ix_kpi_statistic_legacy_scope', (kpiStatistic: KpiStatistic) => [
|
|
32
|
-
kpiStatistic.domain,
|
|
33
|
-
kpiStatistic.kpi,
|
|
34
|
-
kpiStatistic.scope,
|
|
35
|
-
kpiStatistic.valueDate
|
|
36
|
-
])
|
|
37
|
-
@ObjectType({
|
|
38
|
-
description:
|
|
39
|
-
'KPI Statistics Entity - Stores comprehensive statistical information for KPIs and Categories including central tendency measures (mean, median), dispersion metrics (standard deviation, variance), range indicators (min, max), and percentile distributions (25th, 75th percentiles, IQR). Supports both KPI and Category targets with flexible period-based aggregation (daily, weekly, monthly, yearly). Includes extensible JSON fields for additional metrics and metadata for calculation tracking.'
|
|
40
|
-
})
|
|
41
|
-
export class KpiStatistic {
|
|
42
|
-
@PrimaryGeneratedColumn('uuid')
|
|
43
|
-
@Field(type => ID)
|
|
44
|
-
readonly id: string
|
|
45
|
-
|
|
46
|
-
// === 대상 정보 ===
|
|
47
|
-
|
|
48
|
-
@ManyToOne(() => Kpi)
|
|
49
|
-
@Field(type => Kpi, {
|
|
50
|
-
nullable: true,
|
|
51
|
-
description: 'Reference to the KPI definition for which this value is calculated.'
|
|
52
|
-
})
|
|
53
|
-
kpi: Kpi
|
|
54
|
-
|
|
55
|
-
@RelationId((kpiStatistic: KpiStatistic) => kpiStatistic.kpi)
|
|
56
|
-
kpiId: string
|
|
57
|
-
|
|
58
|
-
// === 통계 기간 ===
|
|
59
|
-
@Column()
|
|
60
|
-
@Field({
|
|
61
|
-
description:
|
|
62
|
-
'Date or period for which this statistic is calculated (e.g., day: YYYY-MM-DD, month: YYYY-MM, quarter: YYYY-Qn, range: YYYY-MM-DD~YYYY-MM-DD).'
|
|
63
|
-
})
|
|
64
|
-
valueDate: string
|
|
65
|
-
|
|
66
|
-
@Column({ default: 'DAY' })
|
|
67
|
-
@Field(type => KpiPeriodType, { description: 'Aggregation period type for this statistic.' })
|
|
68
|
-
periodType: KpiPeriodType
|
|
69
|
-
|
|
70
|
-
// === 스코프 정보 ===
|
|
71
|
-
|
|
72
|
-
@ManyToOne(() => KpiOrgScope, { nullable: true })
|
|
73
|
-
@Field(type => KpiOrgScope, {
|
|
74
|
-
nullable: true,
|
|
75
|
-
description: 'Organization scope for scoped statistics. Null for overall statistics.'
|
|
76
|
-
})
|
|
77
|
-
kpiOrgScope?: KpiOrgScope
|
|
78
|
-
|
|
79
|
-
@RelationId((kpiStatistic: KpiStatistic) => kpiStatistic.kpiOrgScope)
|
|
80
|
-
kpiOrgScopeId?: string
|
|
81
|
-
|
|
82
|
-
@Column({ nullable: true })
|
|
83
|
-
@Field({
|
|
84
|
-
nullable: true,
|
|
85
|
-
description: 'Legacy scope field - use kpiOrgScope instead. Statistical scope - null for overall statistics, category value for scoped statistics (e.g., "서울", "부장", "대규모")'
|
|
86
|
-
})
|
|
87
|
-
scope?: string
|
|
88
|
-
|
|
89
|
-
// === 핵심 통계 필드 (14개) ===
|
|
90
|
-
|
|
91
|
-
// 1. 기본 정보 (3개)
|
|
92
|
-
@Column({ nullable: true })
|
|
93
|
-
@Field({ nullable: true, description: 'Number of data points used in calculation' })
|
|
94
|
-
count?: number // 데이터 개수
|
|
95
|
-
|
|
96
|
-
@Column({ type: 'float', nullable: true })
|
|
97
|
-
@Field({ nullable: true, description: 'Sum of all values in the dataset' })
|
|
98
|
-
sum?: number // 합계
|
|
99
|
-
|
|
100
|
-
@Column({ type: 'float', nullable: true })
|
|
101
|
-
@Field({ nullable: true, description: 'Range of values (maximum - minimum)' })
|
|
102
|
-
range?: number // 범위
|
|
103
|
-
|
|
104
|
-
// 2. 중심 경향 (2개)
|
|
105
|
-
@Column({ type: 'float', nullable: true })
|
|
106
|
-
@Field({ nullable: true, description: 'Arithmetic mean of all values' })
|
|
107
|
-
mean?: number // 평균
|
|
108
|
-
|
|
109
|
-
@Column({ type: 'float', nullable: true })
|
|
110
|
-
@Field({ nullable: true, description: 'Middle value when data is sorted (50th percentile)' })
|
|
111
|
-
median?: number // 중앙값
|
|
112
|
-
|
|
113
|
-
// 3. 범위 (2개)
|
|
114
|
-
@Column({ type: 'float', nullable: true })
|
|
115
|
-
@Field({ nullable: true, description: 'Minimum value in the dataset' })
|
|
116
|
-
minimum?: number // 최소값
|
|
117
|
-
|
|
118
|
-
@Column({ type: 'float', nullable: true })
|
|
119
|
-
@Field({ nullable: true, description: 'Maximum value in the dataset' })
|
|
120
|
-
maximum?: number // 최대값
|
|
121
|
-
|
|
122
|
-
// 4. 분산 (2개)
|
|
123
|
-
@Column({ type: 'float', nullable: true })
|
|
124
|
-
@Field({ nullable: true, description: 'Standard deviation - measure of data dispersion' })
|
|
125
|
-
standardDeviation?: number // 표준편차
|
|
126
|
-
|
|
127
|
-
@Column({ type: 'float', nullable: true })
|
|
128
|
-
@Field({ nullable: true, description: 'Variance - square of standard deviation' })
|
|
129
|
-
variance?: number // 분산
|
|
130
|
-
|
|
131
|
-
// 5. 분위수 (5개)
|
|
132
|
-
@Column({ type: 'float', nullable: true })
|
|
133
|
-
@Field({ nullable: true, description: '25th percentile - 25% of data is below this value' })
|
|
134
|
-
percentile25?: number // 25분위수
|
|
135
|
-
|
|
136
|
-
@Column({ type: 'float', nullable: true })
|
|
137
|
-
@Field({ nullable: true, description: '75th percentile - 75% of data is below this value' })
|
|
138
|
-
percentile75?: number // 75분위수
|
|
139
|
-
|
|
140
|
-
@Column({ type: 'float', nullable: true })
|
|
141
|
-
@Field({ nullable: true, description: 'Interquartile range (75th percentile - 25th percentile)' })
|
|
142
|
-
iqr?: number // 사분위수 범위
|
|
143
|
-
|
|
144
|
-
@Column({ type: 'float', nullable: true })
|
|
145
|
-
@Field({ nullable: true, description: 'Lower fence for outlier detection (Q1 - 1.5 * IQR)' })
|
|
146
|
-
lowerFence?: number // 하위 울타리
|
|
147
|
-
|
|
148
|
-
@Column({ type: 'float', nullable: true })
|
|
149
|
-
@Field({ nullable: true, description: 'Upper fence for outlier detection (Q3 + 1.5 * IQR)' })
|
|
150
|
-
upperFence?: number // 상위 울타리
|
|
151
|
-
|
|
152
|
-
// === 확장 가능한 필드 (JSON) ===
|
|
153
|
-
@Column('simple-json', { nullable: true })
|
|
154
|
-
@Field(type => ScalarObject, {
|
|
155
|
-
nullable: true,
|
|
156
|
-
description: 'Additional statistical metrics stored as key-value pairs'
|
|
157
|
-
})
|
|
158
|
-
additionalStatistics: {
|
|
159
|
-
[metricName: string]: number
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// === 메타데이터 (JSON) ===
|
|
163
|
-
@Column('simple-json', { nullable: true })
|
|
164
|
-
@Field(type => ScalarObject, {
|
|
165
|
-
nullable: true,
|
|
166
|
-
description: 'Calculation metadata including method, timestamp, and data quality information'
|
|
167
|
-
})
|
|
168
|
-
metadata: { [key: string]: any }
|
|
169
|
-
|
|
170
|
-
// === 표준 필드들 ===
|
|
171
|
-
@ManyToOne(type => Domain)
|
|
172
|
-
@Field({ nullable: true, description: 'Domain this statistic belongs to' })
|
|
173
|
-
domain?: Domain
|
|
174
|
-
|
|
175
|
-
@RelationId((kpiStatistic: KpiStatistic) => kpiStatistic.domain)
|
|
176
|
-
domainId?: string
|
|
177
|
-
|
|
178
|
-
@CreateDateColumn()
|
|
179
|
-
@Field({ nullable: true, description: 'Timestamp when this statistic was created' })
|
|
180
|
-
createdAt?: Date
|
|
181
|
-
|
|
182
|
-
@UpdateDateColumn()
|
|
183
|
-
@Field({ nullable: true, description: 'Timestamp when this statistic was last updated' })
|
|
184
|
-
updatedAt?: Date
|
|
185
|
-
|
|
186
|
-
@ManyToOne(type => User, { nullable: true })
|
|
187
|
-
@Field(type => User, { nullable: true, description: 'User who created this statistic' })
|
|
188
|
-
creator?: User
|
|
189
|
-
|
|
190
|
-
@RelationId((kpiStatistic: KpiStatistic) => kpiStatistic.creator)
|
|
191
|
-
creatorId?: string
|
|
192
|
-
|
|
193
|
-
@ManyToOne(type => User, { nullable: true })
|
|
194
|
-
@Field(type => User, { nullable: true, description: 'User who last updated this statistic' })
|
|
195
|
-
updater?: User
|
|
196
|
-
|
|
197
|
-
@RelationId((kpiStatistic: KpiStatistic) => kpiStatistic.updater)
|
|
198
|
-
updaterId?: string
|
|
199
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { KpiValue } from './kpi-value'
|
|
2
|
-
import { KpiValueQuery } from './kpi-value-query'
|
|
3
|
-
import { KpiValueMutation } from './kpi-value-mutation'
|
|
4
|
-
|
|
5
|
-
export const entities = [KpiValue]
|
|
6
|
-
export const resolvers = [KpiValueQuery, KpiValueMutation]
|
|
7
|
-
export const subscribers = []
|