@dssp/dkpi 1.0.0-alpha.7 β†’ 1.0.0-alpha.70

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 (232) hide show
  1. package/KPI-STATISTICS-SERVICE.md +233 -0
  2. package/_index.html +0 -5
  3. package/assets/favicon.ico +0 -0
  4. package/assets/images/project-image.png +0 -0
  5. package/assets/manifest/apple-1024.png +0 -0
  6. package/assets/manifest/apple-120.png +0 -0
  7. package/assets/manifest/apple-152.png +0 -0
  8. package/assets/manifest/apple-167.png +0 -0
  9. package/assets/manifest/apple-180.png +0 -0
  10. package/assets/manifest/apple-touch-icon.png +0 -0
  11. package/assets/manifest/badge-128x128.png +0 -0
  12. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  13. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  14. package/assets/manifest/icon-128x128.png +0 -0
  15. package/assets/manifest/icon-192x192.png +0 -0
  16. package/assets/manifest/icon-512x512.png +0 -0
  17. package/assets/manifest/icon-72x72.png +0 -0
  18. package/assets/manifest/icon-96x96.png +0 -0
  19. package/assets/manifest/image-metaog.png +0 -0
  20. package/assets/manifest/maskable_icon.png +0 -0
  21. package/assets/manifest/ms-icon-144x144.png +0 -0
  22. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  23. package/assets/videos/intro.mp4 +0 -0
  24. package/dist-client/bootstrap.js +64 -4
  25. package/dist-client/bootstrap.js.map +1 -1
  26. package/dist-client/components/kpi-2d-lookup-chart.d.ts +43 -0
  27. package/dist-client/components/kpi-2d-lookup-chart.js +253 -0
  28. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  29. package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
  30. package/dist-client/components/kpi-boxplot-chart.js +291 -0
  31. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  32. package/dist-client/components/kpi-lookup-chart.d.ts +53 -0
  33. package/dist-client/components/kpi-lookup-chart.js +430 -0
  34. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  35. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  36. package/dist-client/components/kpi-mini-trend-chart.js +148 -0
  37. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  38. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  39. package/dist-client/components/kpi-radar-chart.js +259 -0
  40. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  41. package/dist-client/components/kpi-single-boxplot-chart.d.ts +24 -0
  42. package/dist-client/components/kpi-single-boxplot-chart.js +391 -0
  43. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  44. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  45. package/dist-client/components/kpi-trend-chart.js +220 -0
  46. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  47. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  48. package/dist-client/components/sv-pagenation-control.js +142 -0
  49. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  50. package/dist-client/google-map/common-google-map.d.ts +35 -0
  51. package/dist-client/google-map/common-google-map.js +345 -0
  52. package/dist-client/google-map/common-google-map.js.map +1 -0
  53. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  54. package/dist-client/google-map/google-map-loader.js +23 -0
  55. package/dist-client/google-map/google-map-loader.js.map +1 -0
  56. package/dist-client/icons/menu-icons.d.ts +6 -0
  57. package/dist-client/icons/menu-icons.js +42 -0
  58. package/dist-client/icons/menu-icons.js.map +1 -1
  59. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  60. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  61. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  62. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +46 -0
  63. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +378 -0
  64. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  65. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  66. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  67. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  68. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  69. package/dist-client/pages/kpi-admin/kpi-system-guide.js +535 -0
  70. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  71. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +18 -0
  72. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +259 -0
  73. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  74. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +22 -0
  75. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +346 -0
  76. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  77. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +26 -0
  78. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +433 -0
  79. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  80. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  81. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  82. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  83. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +38 -0
  84. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +851 -0
  85. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  86. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +42 -0
  87. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +316 -0
  88. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  89. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +28 -0
  90. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +497 -0
  91. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  92. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  93. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  94. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  95. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +52 -0
  96. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +798 -0
  97. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  98. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +63 -0
  99. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1089 -0
  100. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  101. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  102. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  103. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  104. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  105. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  106. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  107. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  108. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  109. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  110. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  111. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  112. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  113. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  114. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  115. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  116. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +57 -0
  117. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +719 -0
  118. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  119. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  120. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  121. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  122. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +68 -0
  123. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +380 -0
  124. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  125. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  126. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  127. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  128. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +40 -0
  129. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +190 -0
  130. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  131. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  132. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  133. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  134. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +71 -0
  135. package/dist-client/pages/kpi-value/kpi-value-list-page.js +464 -0
  136. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  137. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +14 -0
  138. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +339 -0
  139. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  140. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +14 -0
  141. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +276 -0
  142. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  143. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +18 -0
  144. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +307 -0
  145. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  146. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +18 -0
  147. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +433 -0
  148. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  149. package/dist-client/pages/sv-project-complete.d.ts +21 -0
  150. package/dist-client/pages/sv-project-complete.js +213 -0
  151. package/dist-client/pages/sv-project-complete.js.map +1 -0
  152. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  153. package/dist-client/pages/sv-project-completed-list.js +416 -0
  154. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  155. package/dist-client/pages/sv-project-detail.d.ts +46 -0
  156. package/dist-client/pages/sv-project-detail.js +1236 -0
  157. package/dist-client/pages/sv-project-detail.js.map +1 -0
  158. package/dist-client/pages/sv-project-list.d.ts +165 -0
  159. package/dist-client/pages/sv-project-list.js +488 -0
  160. package/dist-client/pages/sv-project-list.js.map +1 -0
  161. package/dist-client/route.d.ts +1 -1
  162. package/dist-client/route.js +35 -1
  163. package/dist-client/route.js.map +1 -1
  164. package/dist-client/shared/complete-api.d.ts +8 -0
  165. package/dist-client/shared/complete-api.js +177 -0
  166. package/dist-client/shared/complete-api.js.map +1 -0
  167. package/dist-client/shared/func.d.ts +2 -0
  168. package/dist-client/shared/func.js +22 -0
  169. package/dist-client/shared/func.js.map +1 -0
  170. package/dist-client/themes/dark.css +24 -24
  171. package/dist-client/themes/light.css +23 -23
  172. package/dist-client/tsconfig.tsbuildinfo +1 -1
  173. package/dist-client/viewparts/menu-tools.d.ts +40 -5
  174. package/dist-client/viewparts/menu-tools.js +289 -34
  175. package/dist-client/viewparts/menu-tools.js.map +1 -1
  176. package/dist-server/index.d.ts +2 -0
  177. package/dist-server/index.js +5 -0
  178. package/dist-server/index.js.map +1 -1
  179. package/dist-server/migrations/index.d.ts +1 -0
  180. package/dist-server/migrations/index.js +12 -0
  181. package/dist-server/migrations/index.js.map +1 -0
  182. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  183. package/dist-server/scripts/calculate-kpi-scores.js +333 -0
  184. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  185. package/dist-server/scripts/load-grade-data-migration.d.ts +14 -0
  186. package/dist-server/scripts/load-grade-data-migration.js +279 -0
  187. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  188. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +14 -0
  189. package/dist-server/scripts/propagate-parent-kpi-values.js +786 -0
  190. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  191. package/dist-server/scripts/recalculate-by-project-name.d.ts +2 -0
  192. package/dist-server/scripts/recalculate-by-project-name.js +72 -0
  193. package/dist-server/scripts/recalculate-by-project-name.js.map +1 -0
  194. package/dist-server/service/index.d.ts +4 -0
  195. package/dist-server/service/index.js +20 -0
  196. package/dist-server/service/index.js.map +1 -0
  197. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  198. package/dist-server/service/kpi-metric-value/index.js +8 -0
  199. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  200. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +74 -0
  201. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +687 -0
  202. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  203. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +7 -0
  204. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +52 -0
  205. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  206. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  207. package/dist-server/service/kpi-stat/index.js +8 -0
  208. package/dist-server/service/kpi-stat/index.js.map +1 -0
  209. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +12 -0
  210. package/dist-server/service/kpi-stat/kpi-stat-query.js +662 -0
  211. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  212. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  213. package/dist-server/service/kpi-stat/kpi-stat-types.js +180 -0
  214. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  215. package/dist-server/service/kpi-value/index.d.ts +3 -0
  216. package/dist-server/service/kpi-value/index.js +7 -0
  217. package/dist-server/service/kpi-value/index.js.map +1 -0
  218. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  219. package/dist-server/service/kpi-value/kpi-value-query.js +69 -0
  220. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  221. package/dist-server/tsconfig.tsbuildinfo +1 -1
  222. package/kpi-module-service-tests.md +1286 -0
  223. package/kpi-module-test-report.md +676 -0
  224. package/kpi-module-unit-test-detailed-report.md +925 -0
  225. package/kpi-module-unit-tests-detailed.md +1452 -0
  226. package/package.json +65 -55
  227. package/recalculate-batch.sh +64 -0
  228. package/recalculate-projects-range.sh +98 -0
  229. package/schema.graphql +2514 -455
  230. package/things-factory.config.js +11 -1
  231. package/views/auth-page.html +0 -1
  232. package/views/public/home.html +0 -1
@@ -0,0 +1,333 @@
1
+ #!/usr/bin/env ts-node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.calculateKpiScores = calculateKpiScores;
5
+ exports.listKpisWithLookupTables = listKpisWithLookupTables;
6
+ const typeorm_1 = require("typeorm");
7
+ function isGrade2DLookup(grades) {
8
+ return grades && typeof grades === 'object' && !Array.isArray(grades) && grades.type === 'PROGRESS_DEVIATION_LOOKUP';
9
+ }
10
+ /**
11
+ * 2D lookup tableμ—μ„œ 점수 계산 (X13: 곡정λ₯  Γ— 편차 β†’ 점수)
12
+ * @param deviationValue 편차율 (formula 결과)
13
+ * @param grades 2D lookup grades 객체
14
+ * @param progressRate 곡정λ₯  (0~100)
15
+ */
16
+ function calculate2DScore(deviationValue, grades, progressRate) {
17
+ if (!grades.rows || grades.rows.length === 0)
18
+ return null;
19
+ const progressIndex = Math.min(99, Math.max(0, Math.floor(progressRate)));
20
+ const row = grades.rows.find(r => r.progressRate === progressIndex);
21
+ if (!row)
22
+ return null;
23
+ if (deviationValue < row.boundary5to4)
24
+ return 5;
25
+ if (deviationValue < row.boundary4to3)
26
+ return 4;
27
+ if (deviationValue < row.boundary3to2)
28
+ return 3;
29
+ if (deviationValue < row.boundary2to1)
30
+ return 2;
31
+ return 1;
32
+ }
33
+ /**
34
+ * Calculate KPI scores based on lookup tables (grades)
35
+ * Processes kpi-values and updates scores based on KPI grades
36
+ */
37
+ async function calculateKpiScores(specificKpiName, forceRecalculate = false) {
38
+ var _a;
39
+ // Set NODE_ENV if not set
40
+ if (!process.env.NODE_ENV) {
41
+ process.env.NODE_ENV = 'development';
42
+ }
43
+ // Initialize Things-Factory environment
44
+ const { config } = require('@things-factory/env');
45
+ const ormconfig = require('@things-factory/shell/dist-server/initializers/ormconfig.js');
46
+ const connectionConfig = config.get('ormconfig');
47
+ let connection;
48
+ console.log('πŸš€ Starting KPI Score Calculation...');
49
+ try {
50
+ // Create database connection using Things-Factory pattern
51
+ console.log('πŸ”Œ Connecting to database...');
52
+ connection = await (0, typeorm_1.createConnection)(Object.assign(Object.assign(Object.assign({}, ormconfig), connectionConfig), { logging: false }));
53
+ // Register the connection with Things-Factory shell
54
+ const { addDataSource } = require('@things-factory/shell');
55
+ addDataSource('default', connection);
56
+ console.log('βœ… Database connected');
57
+ // Now we can use getRepository with the registered connection
58
+ const { getRepository } = require('@things-factory/shell');
59
+ const { Kpi } = require('@things-factory/kpi');
60
+ const kpiRepository = getRepository(Kpi);
61
+ // Get KPI-Value repository (assuming it exists)
62
+ let kpiValueRepository;
63
+ try {
64
+ const { KpiValue } = require('@things-factory/kpi');
65
+ kpiValueRepository = getRepository(KpiValue);
66
+ }
67
+ catch (error) {
68
+ console.error('❌ KpiValue entity not found. Please ensure it exists in @things-factory/kpi');
69
+ return;
70
+ }
71
+ // Get KPIs with grades (lookup tables)
72
+ let kpisQuery = kpiRepository
73
+ .createQueryBuilder('kpi')
74
+ .leftJoinAndSelect('kpi.domain', 'domain')
75
+ .where('kpi.grades IS NOT NULL')
76
+ .andWhere('domain.name = :domainName', { domainName: 'SYSTEM' });
77
+ if (specificKpiName) {
78
+ kpisQuery = kpisQuery.andWhere('kpi.name = :name', { name: specificKpiName });
79
+ console.log(`🎯 Processing specific KPI: ${specificKpiName}`);
80
+ }
81
+ else {
82
+ console.log('🎯 Processing all KPIs with lookup tables');
83
+ }
84
+ const kpis = await kpisQuery.getMany();
85
+ if (kpis.length === 0) {
86
+ console.log('⚠️ No KPIs found with lookup tables (grades)');
87
+ return;
88
+ }
89
+ console.log(`πŸ“Š Found ${kpis.length} KPIs with lookup tables`);
90
+ let totalProcessed = 0;
91
+ let totalUpdated = 0;
92
+ let errorCount = 0;
93
+ // 2D lookup KPI (X13)에 ν•„μš”ν•œ Project entity μ€€λΉ„
94
+ let projectRepository = null;
95
+ const progressRateCache = new Map();
96
+ try {
97
+ const { Project } = require('@dssp/project');
98
+ projectRepository = getRepository(Project);
99
+ }
100
+ catch (_b) {
101
+ console.log('⚠️ @dssp/project not found β€” 2D lookup KPIs will use default progressRate');
102
+ }
103
+ for (const kpi of kpis) {
104
+ try {
105
+ console.log(`\nπŸ“„ Processing KPI: ${kpi.name}`);
106
+ const is2D = isGrade2DLookup(kpi.grades);
107
+ if (!is2D && (!kpi.grades || !Array.isArray(kpi.grades) || kpi.grades.length === 0)) {
108
+ console.log(` ⚠️ No grades found, skipping`);
109
+ continue;
110
+ }
111
+ if (is2D) {
112
+ console.log(` πŸ“ˆ 2D Lookup table: ${kpi.grades.rows.length} rows`);
113
+ }
114
+ else {
115
+ console.log(` πŸ“ˆ Lookup table: ${kpi.grades.length} grades`);
116
+ }
117
+ // Get KPI values for this KPI
118
+ let kpiValuesQuery = kpiValueRepository
119
+ .createQueryBuilder('kpiValue')
120
+ .leftJoinAndSelect('kpiValue.kpi', 'kpi')
121
+ .where('kpi.id = :kpiId', { kpiId: kpi.id })
122
+ .andWhere('kpiValue.value IS NOT NULL');
123
+ if (!forceRecalculate) {
124
+ // Only process null or 0 scores by default
125
+ kpiValuesQuery = kpiValuesQuery.andWhere('(kpiValue.score IS NULL OR kpiValue.score = 0)');
126
+ }
127
+ const kpiValues = await kpiValuesQuery.getMany();
128
+ if (kpiValues.length === 0) {
129
+ console.log(` βœ… No null/zero scores found (all values already calculated)`);
130
+ continue;
131
+ }
132
+ console.log(` πŸ”’ Processing ${kpiValues.length} null/zero scores`);
133
+ let updatedCount = 0;
134
+ for (const kpiValue of kpiValues) {
135
+ const value = kpiValue.value;
136
+ let calculatedScore;
137
+ if (is2D) {
138
+ // 2D lookup: project의 totalProgress 쑰회
139
+ let progressRate = 50; // default
140
+ const projectId = kpiValue.group;
141
+ if (projectId && projectRepository) {
142
+ if (progressRateCache.has(projectId)) {
143
+ progressRate = progressRateCache.get(projectId);
144
+ }
145
+ else {
146
+ const project = await projectRepository.findOne({ where: { id: projectId } });
147
+ progressRate = (_a = project === null || project === void 0 ? void 0 : project.totalProgress) !== null && _a !== void 0 ? _a : 50;
148
+ progressRateCache.set(projectId, progressRate);
149
+ }
150
+ }
151
+ calculatedScore = calculate2DScore(value, kpi.grades, progressRate);
152
+ }
153
+ else {
154
+ calculatedScore = calculateScoreFromGrades(value, kpi.grades);
155
+ }
156
+ if (calculatedScore !== null) {
157
+ // Only update if the score is actually different
158
+ if (kpiValue.score !== calculatedScore) {
159
+ const oldScore = kpiValue.score;
160
+ kpiValue.score = calculatedScore;
161
+ await kpiValueRepository.save(kpiValue);
162
+ updatedCount++;
163
+ console.log(` πŸ“Š Value ${value} β†’ Score ${calculatedScore} (was ${oldScore || 'null'})`);
164
+ }
165
+ }
166
+ else {
167
+ console.log(` ⚠️ Value ${value} is below all ranges in lookup table`);
168
+ }
169
+ totalProcessed++;
170
+ }
171
+ console.log(` βœ… Updated ${updatedCount}/${kpiValues.length} values`);
172
+ totalUpdated += updatedCount;
173
+ }
174
+ catch (kpiError) {
175
+ console.log(` ❌ Error processing KPI ${kpi.name}: ${kpiError.message}`);
176
+ errorCount++;
177
+ }
178
+ }
179
+ console.log(`\nπŸŽ‰ KPI Score Calculation Complete!`);
180
+ console.log(` πŸ“Š KPIs processed: ${kpis.length}`);
181
+ console.log(` πŸ”’ Total values processed: ${totalProcessed}`);
182
+ console.log(` βœ… Scores updated: ${totalUpdated}`);
183
+ console.log(` ❌ Errors: ${errorCount}`);
184
+ if (errorCount > 0) {
185
+ console.log(`\n⚠️ Some KPIs had errors. Check the logs above for details.`);
186
+ }
187
+ }
188
+ catch (error) {
189
+ console.error(`❌ Fatal error during score calculation:`, error);
190
+ throw error;
191
+ }
192
+ finally {
193
+ if (connection) {
194
+ await connection.close();
195
+ console.log('πŸ”Œ Database connection closed');
196
+ }
197
+ }
198
+ }
199
+ /**
200
+ * Calculate score from grades lookup table
201
+ * If exact range not found, use the score of the largest minValue that is <= value
202
+ */
203
+ function calculateScoreFromGrades(value, grades) {
204
+ // First try to find exact range match
205
+ for (const grade of grades) {
206
+ if (value >= grade.minValue && value <= grade.maxValue) {
207
+ return grade.score || 0;
208
+ }
209
+ }
210
+ // If no exact match, find the grade with largest minValue that is <= value
211
+ let bestGrade = null;
212
+ let bestMinValue = -Infinity;
213
+ for (const grade of grades) {
214
+ if (grade.minValue <= value && grade.minValue > bestMinValue) {
215
+ bestGrade = grade;
216
+ bestMinValue = grade.minValue;
217
+ }
218
+ }
219
+ if (bestGrade) {
220
+ return bestGrade.score || 0;
221
+ }
222
+ return null; // Value is smaller than all minValues in lookup table
223
+ }
224
+ /**
225
+ * List KPIs that have lookup tables (grades)
226
+ */
227
+ async function listKpisWithLookupTables() {
228
+ // Set NODE_ENV if not set
229
+ if (!process.env.NODE_ENV) {
230
+ process.env.NODE_ENV = 'development';
231
+ }
232
+ // Initialize Things-Factory environment
233
+ const { config } = require('@things-factory/env');
234
+ const ormconfig = require('@things-factory/shell/dist-server/initializers/ormconfig.js');
235
+ const connectionConfig = config.get('ormconfig');
236
+ let connection;
237
+ try {
238
+ console.log('πŸ”Œ Connecting to database...');
239
+ connection = await (0, typeorm_1.createConnection)(Object.assign(Object.assign(Object.assign({}, ormconfig), connectionConfig), { logging: false }));
240
+ const { addDataSource } = require('@things-factory/shell');
241
+ addDataSource('default', connection);
242
+ const { getRepository } = require('@things-factory/shell');
243
+ const { Kpi } = require('@things-factory/kpi');
244
+ const kpiRepository = getRepository(Kpi);
245
+ // Get KPIs with grades
246
+ const kpis = await kpiRepository
247
+ .createQueryBuilder('kpi')
248
+ .leftJoinAndSelect('kpi.domain', 'domain')
249
+ .where('kpi.grades IS NOT NULL')
250
+ .andWhere('domain.name = :domainName', { domainName: 'SYSTEM' })
251
+ .getMany();
252
+ console.log(`πŸ“ KPIs with lookup tables (${kpis.length}):\n`);
253
+ if (kpis.length === 0) {
254
+ console.log(' No KPIs found with lookup tables.');
255
+ return;
256
+ }
257
+ kpis.forEach((kpi, index) => {
258
+ const gradeCount = kpi.grades ? kpi.grades.length : 0;
259
+ console.log(` ${(index + 1).toString().padStart(2)}: ${kpi.name} (${gradeCount} grades)`);
260
+ });
261
+ console.log(`\nπŸ“ˆ Total: ${kpis.length} KPIs with lookup tables`);
262
+ }
263
+ catch (error) {
264
+ console.error('❌ Error listing KPIs:', error);
265
+ }
266
+ finally {
267
+ if (connection) {
268
+ await connection.close();
269
+ }
270
+ }
271
+ }
272
+ /**
273
+ * CLI execution when called directly
274
+ */
275
+ async function main() {
276
+ const args = process.argv.slice(2);
277
+ try {
278
+ if (args.includes('--help') || args.includes('-h')) {
279
+ console.log(`
280
+ 🎯 KPI Score Calculator
281
+
282
+ Usage:
283
+ ts-node server/scripts/calculate-kpi-scores.ts [options] [kpiName]
284
+
285
+ Options:
286
+ --list, -l List KPIs that have lookup tables
287
+ --force, -f Force recalculate all scores (not just null/zero ones)
288
+ --help, -h Show this help
289
+
290
+ Examples:
291
+ ts-node server/scripts/calculate-kpi-scores.ts # Calculate scores for null/zero values only
292
+ ts-node server/scripts/calculate-kpi-scores.ts --force # Recalculate ALL scores
293
+ ts-node server/scripts/calculate-kpi-scores.ts "X11. 연면적 λŒ€λΉ„ 곡사기간" # Calculate for specific KPI
294
+ ts-node server/scripts/calculate-kpi-scores.ts --list # List KPIs with lookup tables
295
+
296
+ Note: By default, only processes kpi-values with null or zero scores.
297
+ Use --force to recalculate all scores.
298
+ `);
299
+ return;
300
+ }
301
+ if (args.includes('--list') || args.includes('-l')) {
302
+ await listKpisWithLookupTables();
303
+ return;
304
+ }
305
+ const forceRecalculate = args.includes('--force') || args.includes('-f');
306
+ const specificKpiName = args.find(arg => !arg.startsWith('--'));
307
+ if (specificKpiName) {
308
+ console.log(`🎯 Calculating scores for specific KPI: ${specificKpiName}`);
309
+ console.log(` Mode: ${forceRecalculate ? 'Force recalculate ALL' : 'Only null/zero scores'}`);
310
+ await calculateKpiScores(specificKpiName, forceRecalculate);
311
+ }
312
+ else {
313
+ console.log(`🎯 Calculating scores for all KPIs with lookup tables`);
314
+ console.log(` Mode: ${forceRecalculate ? 'Force recalculate ALL' : 'Only null/zero scores'}`);
315
+ await calculateKpiScores(undefined, forceRecalculate);
316
+ }
317
+ console.log('\n✨ Done!');
318
+ }
319
+ catch (error) {
320
+ console.error('\n❌ Script failed:', error.message);
321
+ process.exit(1);
322
+ }
323
+ }
324
+ // Handle uncaught errors
325
+ process.on('unhandledRejection', (error) => {
326
+ console.error('❌ Unhandled error:', error.message || error);
327
+ process.exit(1);
328
+ });
329
+ // Run if called directly
330
+ if (require.main === module) {
331
+ main();
332
+ }
333
+ //# sourceMappingURL=calculate-kpi-scores.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calculate-kpi-scores.js","sourceRoot":"","sources":["../../server/scripts/calculate-kpi-scores.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env ts-node\n\nimport { createConnection } from 'typeorm'\nimport * as fs from 'fs'\nimport * as path from 'path'\n\ninterface KpiGrade {\n name: string\n minValue: number\n maxValue: number\n score?: number\n color?: string\n description?: string\n}\n\ninterface Grade2DRow {\n progressRate: number\n boundary5to4: number\n boundary4to3: number\n boundary3to2: number\n boundary2to1: number\n}\n\ninterface Grade2DLookup {\n type: 'PROGRESS_DEVIATION_LOOKUP'\n rows: Grade2DRow[]\n}\n\ninterface KpiRecord {\n id: string\n name: string\n grades: KpiGrade[] | Grade2DLookup | any\n}\n\ninterface KpiValueRecord {\n id: string\n value: number\n score?: number\n group?: string\n kpi: KpiRecord\n}\n\nfunction isGrade2DLookup(grades: any): grades is Grade2DLookup {\n return grades && typeof grades === 'object' && !Array.isArray(grades) && grades.type === 'PROGRESS_DEVIATION_LOOKUP'\n}\n\n/**\n * 2D lookup tableμ—μ„œ 점수 계산 (X13: 곡정λ₯  Γ— 편차 β†’ 점수)\n * @param deviationValue 편차율 (formula κ²°κ³Ό)\n * @param grades 2D lookup grades 객체\n * @param progressRate 곡정λ₯  (0~100)\n */\nfunction calculate2DScore(deviationValue: number, grades: Grade2DLookup, progressRate: number): number | null {\n if (!grades.rows || grades.rows.length === 0) return null\n\n const progressIndex = Math.min(99, Math.max(0, Math.floor(progressRate)))\n const row = grades.rows.find(r => r.progressRate === progressIndex)\n if (!row) return null\n\n if (deviationValue < row.boundary5to4) return 5\n if (deviationValue < row.boundary4to3) return 4\n if (deviationValue < row.boundary3to2) return 3\n if (deviationValue < row.boundary2to1) return 2\n return 1\n}\n\n/**\n * Calculate KPI scores based on lookup tables (grades)\n * Processes kpi-values and updates scores based on KPI grades\n */\nexport async function calculateKpiScores(specificKpiName?: string, forceRecalculate = false): Promise<void> {\n // Set NODE_ENV if not set\n if (!process.env.NODE_ENV) {\n process.env.NODE_ENV = 'development'\n }\n\n // Initialize Things-Factory environment\n const { config } = require('@things-factory/env')\n const ormconfig = require('@things-factory/shell/dist-server/initializers/ormconfig.js')\n const connectionConfig = config.get('ormconfig')\n\n let connection\n\n console.log('πŸš€ Starting KPI Score Calculation...')\n\n try {\n // Create database connection using Things-Factory pattern\n console.log('πŸ”Œ Connecting to database...')\n connection = await createConnection({\n ...ormconfig,\n ...connectionConfig,\n logging: false\n })\n\n // Register the connection with Things-Factory shell\n const { addDataSource } = require('@things-factory/shell')\n addDataSource('default', connection)\n\n console.log('βœ… Database connected')\n\n // Now we can use getRepository with the registered connection\n const { getRepository } = require('@things-factory/shell')\n const { Kpi } = require('@things-factory/kpi')\n const kpiRepository = getRepository(Kpi)\n\n // Get KPI-Value repository (assuming it exists)\n let kpiValueRepository\n try {\n const { KpiValue } = require('@things-factory/kpi')\n kpiValueRepository = getRepository(KpiValue)\n } catch (error) {\n console.error('❌ KpiValue entity not found. Please ensure it exists in @things-factory/kpi')\n return\n }\n\n // Get KPIs with grades (lookup tables)\n let kpisQuery = kpiRepository\n .createQueryBuilder('kpi')\n .leftJoinAndSelect('kpi.domain', 'domain')\n .where('kpi.grades IS NOT NULL')\n .andWhere('domain.name = :domainName', { domainName: 'SYSTEM' })\n\n if (specificKpiName) {\n kpisQuery = kpisQuery.andWhere('kpi.name = :name', { name: specificKpiName })\n console.log(`🎯 Processing specific KPI: ${specificKpiName}`)\n } else {\n console.log('🎯 Processing all KPIs with lookup tables')\n }\n\n const kpis = await kpisQuery.getMany()\n\n if (kpis.length === 0) {\n console.log('⚠️ No KPIs found with lookup tables (grades)')\n return\n }\n\n console.log(`πŸ“Š Found ${kpis.length} KPIs with lookup tables`)\n\n let totalProcessed = 0\n let totalUpdated = 0\n let errorCount = 0\n\n // 2D lookup KPI (X13)에 ν•„μš”ν•œ Project entity μ€€λΉ„\n let projectRepository: any = null\n const progressRateCache = new Map<string, number>()\n\n try {\n const { Project } = require('@dssp/project')\n projectRepository = getRepository(Project)\n } catch {\n console.log('⚠️ @dssp/project not found β€” 2D lookup KPIs will use default progressRate')\n }\n\n for (const kpi of kpis) {\n try {\n console.log(`\\nπŸ“„ Processing KPI: ${kpi.name}`)\n\n const is2D = isGrade2DLookup(kpi.grades)\n\n if (!is2D && (!kpi.grades || !Array.isArray(kpi.grades) || kpi.grades.length === 0)) {\n console.log(` ⚠️ No grades found, skipping`)\n continue\n }\n\n if (is2D) {\n console.log(` πŸ“ˆ 2D Lookup table: ${kpi.grades.rows.length} rows`)\n } else {\n console.log(` πŸ“ˆ Lookup table: ${kpi.grades.length} grades`)\n }\n\n // Get KPI values for this KPI\n let kpiValuesQuery = kpiValueRepository\n .createQueryBuilder('kpiValue')\n .leftJoinAndSelect('kpiValue.kpi', 'kpi')\n .where('kpi.id = :kpiId', { kpiId: kpi.id })\n .andWhere('kpiValue.value IS NOT NULL')\n\n if (!forceRecalculate) {\n // Only process null or 0 scores by default\n kpiValuesQuery = kpiValuesQuery.andWhere('(kpiValue.score IS NULL OR kpiValue.score = 0)')\n }\n\n const kpiValues = await kpiValuesQuery.getMany()\n\n if (kpiValues.length === 0) {\n console.log(` βœ… No null/zero scores found (all values already calculated)`)\n continue\n }\n\n console.log(` πŸ”’ Processing ${kpiValues.length} null/zero scores`)\n\n let updatedCount = 0\n\n for (const kpiValue of kpiValues) {\n const value = kpiValue.value\n let calculatedScore: number | null\n\n if (is2D) {\n // 2D lookup: project의 totalProgress 쑰회\n let progressRate = 50 // default\n const projectId = kpiValue.group\n if (projectId && projectRepository) {\n if (progressRateCache.has(projectId)) {\n progressRate = progressRateCache.get(projectId)!\n } else {\n const project = await projectRepository.findOne({ where: { id: projectId } })\n progressRate = project?.totalProgress ?? 50\n progressRateCache.set(projectId, progressRate)\n }\n }\n calculatedScore = calculate2DScore(value, kpi.grades as Grade2DLookup, progressRate)\n } else {\n calculatedScore = calculateScoreFromGrades(value, kpi.grades as KpiGrade[])\n }\n\n if (calculatedScore !== null) {\n // Only update if the score is actually different\n if (kpiValue.score !== calculatedScore) {\n const oldScore = kpiValue.score\n kpiValue.score = calculatedScore\n await kpiValueRepository.save(kpiValue)\n updatedCount++\n\n console.log(` πŸ“Š Value ${value} β†’ Score ${calculatedScore} (was ${oldScore || 'null'})`)\n }\n } else {\n console.log(` ⚠️ Value ${value} is below all ranges in lookup table`)\n }\n\n totalProcessed++\n }\n\n console.log(` βœ… Updated ${updatedCount}/${kpiValues.length} values`)\n totalUpdated += updatedCount\n\n } catch (kpiError) {\n console.log(` ❌ Error processing KPI ${kpi.name}: ${kpiError.message}`)\n errorCount++\n }\n }\n\n console.log(`\\nπŸŽ‰ KPI Score Calculation Complete!`)\n console.log(` πŸ“Š KPIs processed: ${kpis.length}`)\n console.log(` πŸ”’ Total values processed: ${totalProcessed}`)\n console.log(` βœ… Scores updated: ${totalUpdated}`)\n console.log(` ❌ Errors: ${errorCount}`)\n\n if (errorCount > 0) {\n console.log(`\\n⚠️ Some KPIs had errors. Check the logs above for details.`)\n }\n\n } catch (error) {\n console.error(`❌ Fatal error during score calculation:`, error)\n throw error\n } finally {\n if (connection) {\n await connection.close()\n console.log('πŸ”Œ Database connection closed')\n }\n }\n}\n\n/**\n * Calculate score from grades lookup table\n * If exact range not found, use the score of the largest minValue that is <= value\n */\nfunction calculateScoreFromGrades(value: number, grades: KpiGrade[]): number | null {\n // First try to find exact range match\n for (const grade of grades) {\n if (value >= grade.minValue && value <= grade.maxValue) {\n return grade.score || 0\n }\n }\n \n // If no exact match, find the grade with largest minValue that is <= value\n let bestGrade: KpiGrade | null = null\n let bestMinValue = -Infinity\n \n for (const grade of grades) {\n if (grade.minValue <= value && grade.minValue > bestMinValue) {\n bestGrade = grade\n bestMinValue = grade.minValue\n }\n }\n \n if (bestGrade) {\n return bestGrade.score || 0\n }\n \n return null // Value is smaller than all minValues in lookup table\n}\n\n/**\n * List KPIs that have lookup tables (grades)\n */\nexport async function listKpisWithLookupTables(): Promise<void> {\n // Set NODE_ENV if not set\n if (!process.env.NODE_ENV) {\n process.env.NODE_ENV = 'development'\n }\n\n // Initialize Things-Factory environment\n const { config } = require('@things-factory/env')\n const ormconfig = require('@things-factory/shell/dist-server/initializers/ormconfig.js')\n const connectionConfig = config.get('ormconfig')\n\n let connection\n\n try {\n console.log('πŸ”Œ Connecting to database...')\n connection = await createConnection({\n ...ormconfig,\n ...connectionConfig,\n logging: false\n })\n\n const { addDataSource } = require('@things-factory/shell')\n addDataSource('default', connection)\n\n const { getRepository } = require('@things-factory/shell')\n const { Kpi } = require('@things-factory/kpi')\n const kpiRepository = getRepository(Kpi)\n\n // Get KPIs with grades\n const kpis = await kpiRepository\n .createQueryBuilder('kpi')\n .leftJoinAndSelect('kpi.domain', 'domain')\n .where('kpi.grades IS NOT NULL')\n .andWhere('domain.name = :domainName', { domainName: 'SYSTEM' })\n .getMany()\n\n console.log(`πŸ“ KPIs with lookup tables (${kpis.length}):\\n`)\n\n if (kpis.length === 0) {\n console.log(' No KPIs found with lookup tables.')\n return\n }\n\n kpis.forEach((kpi, index) => {\n const gradeCount = kpi.grades ? kpi.grades.length : 0\n console.log(` ${(index + 1).toString().padStart(2)}: ${kpi.name} (${gradeCount} grades)`)\n })\n\n console.log(`\\nπŸ“ˆ Total: ${kpis.length} KPIs with lookup tables`)\n\n } catch (error) {\n console.error('❌ Error listing KPIs:', error)\n } finally {\n if (connection) {\n await connection.close()\n }\n }\n}\n\n/**\n * CLI execution when called directly\n */\nasync function main() {\n const args = process.argv.slice(2)\n\n try {\n if (args.includes('--help') || args.includes('-h')) {\n console.log(`\n🎯 KPI Score Calculator\n\nUsage:\n ts-node server/scripts/calculate-kpi-scores.ts [options] [kpiName]\n\nOptions:\n --list, -l List KPIs that have lookup tables\n --force, -f Force recalculate all scores (not just null/zero ones)\n --help, -h Show this help\n\nExamples:\n ts-node server/scripts/calculate-kpi-scores.ts # Calculate scores for null/zero values only\n ts-node server/scripts/calculate-kpi-scores.ts --force # Recalculate ALL scores\n ts-node server/scripts/calculate-kpi-scores.ts \"X11. 연면적 λŒ€λΉ„ 곡사기간\" # Calculate for specific KPI\n ts-node server/scripts/calculate-kpi-scores.ts --list # List KPIs with lookup tables\n\nNote: By default, only processes kpi-values with null or zero scores.\n Use --force to recalculate all scores.\n `)\n return\n }\n\n if (args.includes('--list') || args.includes('-l')) {\n await listKpisWithLookupTables()\n return\n }\n\n const forceRecalculate = args.includes('--force') || args.includes('-f')\n const specificKpiName = args.find(arg => !arg.startsWith('--'))\n \n if (specificKpiName) {\n console.log(`🎯 Calculating scores for specific KPI: ${specificKpiName}`)\n console.log(` Mode: ${forceRecalculate ? 'Force recalculate ALL' : 'Only null/zero scores'}`)\n await calculateKpiScores(specificKpiName, forceRecalculate)\n } else {\n console.log(`🎯 Calculating scores for all KPIs with lookup tables`)\n console.log(` Mode: ${forceRecalculate ? 'Force recalculate ALL' : 'Only null/zero scores'}`)\n await calculateKpiScores(undefined, forceRecalculate)\n }\n\n console.log('\\n✨ Done!')\n \n } catch (error) {\n console.error('\\n❌ Script failed:', error.message)\n process.exit(1)\n }\n}\n\n// Handle uncaught errors\nprocess.on('unhandledRejection', (error: any) => {\n console.error('❌ Unhandled error:', error.message || error)\n process.exit(1)\n})\n\n// Run if called directly\nif (require.main === module) {\n main()\n}"]}
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ts-node
2
+ /**
3
+ * Load KPI grade data from JSON files - Migration Style
4
+ * This can be called directly from migrations or other server-side scripts
5
+ */
6
+ export declare function loadKpiGradeData(specificFile?: string): Promise<void>;
7
+ /**
8
+ * X31/X32 KPIλ₯Ό λΉ„ν™œμ„±ν™” (active = false, grades = null)
9
+ */
10
+ export declare function deactivateDeletedKpis(): Promise<void>;
11
+ /**
12
+ * List available grade data files
13
+ */
14
+ export declare function listGradeDataFiles(): string[];