@things-factory/kpi 10.1.248 → 10.1.249
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/dist-client/bootstrap.d.ts +2 -0
- package/dist-client/bootstrap.js +7 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/charts/kpi-boxplot-chart.d.ts +24 -0
- package/dist-client/charts/kpi-boxplot-chart.js +324 -0
- package/dist-client/charts/kpi-boxplot-chart.js.map +1 -0
- package/dist-client/charts/kpi-mini-trend-chart.d.ts +14 -0
- package/dist-client/charts/kpi-mini-trend-chart.js +148 -0
- package/dist-client/charts/kpi-mini-trend-chart.js.map +1 -0
- package/dist-client/charts/kpi-radar-chart.d.ts +16 -0
- package/dist-client/charts/kpi-radar-chart.js +138 -0
- package/dist-client/charts/kpi-radar-chart.js.map +1 -0
- package/dist-client/charts/kpi-trend-chart.d.ts +25 -0
- package/dist-client/charts/kpi-trend-chart.js +186 -0
- package/dist-client/charts/kpi-trend-chart.js.map +1 -0
- package/dist-client/google-map/common-google-map.d.ts +34 -0
- package/dist-client/google-map/common-google-map.js +333 -0
- package/dist-client/google-map/common-google-map.js.map +1 -0
- package/dist-client/google-map/google-map-loader.d.ts +8 -0
- package/dist-client/google-map/google-map-loader.js +22 -0
- package/dist-client/google-map/google-map-loader.js.map +1 -0
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/kpi/kpi-grade-editor.d.ts +35 -0
- package/dist-client/pages/kpi/kpi-grade-editor.js +165 -0
- package/dist-client/pages/kpi/kpi-grade-editor.js.map +1 -0
- package/dist-client/pages/kpi/kpi-importer.d.ts +23 -0
- package/dist-client/pages/kpi/kpi-importer.js +92 -0
- package/dist-client/pages/kpi/kpi-importer.js.map +1 -0
- package/dist-client/pages/kpi/kpi-list-page.d.ts +136 -0
- package/dist-client/pages/kpi/kpi-list-page.js +948 -0
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -0
- package/dist-client/pages/kpi/kpi-overview-sample.d.ts +36 -0
- package/dist-client/pages/kpi/kpi-overview-sample.js +264 -0
- package/dist-client/pages/kpi/kpi-overview-sample.js.map +1 -0
- package/dist-client/pages/kpi/kpi-overview.d.ts +14 -0
- package/dist-client/pages/kpi/kpi-overview.js +361 -0
- package/dist-client/pages/kpi/kpi-overview.js.map +1 -0
- package/dist-client/pages/kpi/kpi-tree-page.d.ts +51 -0
- package/dist-client/pages/kpi/kpi-tree-page.js +355 -0
- package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -0
- package/dist-client/pages/kpi/kpi-view.d.ts +12 -0
- package/dist-client/pages/kpi/kpi-view.js +193 -0
- package/dist-client/pages/kpi/kpi-view.js.map +1 -0
- package/dist-client/pages/kpi/kpi-viz-editor.d.ts +19 -0
- package/dist-client/pages/kpi/kpi-viz-editor.js +562 -0
- package/dist-client/pages/kpi/kpi-viz-editor.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +17 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +279 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +19 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +385 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +465 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +24 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +440 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +33 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +444 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +369 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +128 -0
- package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +31 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +493 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +50 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +884 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
- package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
- package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
- package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +146 -0
- package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
- package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +21 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.js +160 -0
- package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.d.ts +23 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.js +92 -0
- package/dist-client/pages/kpi-metric/kpi-metric-importer.js.map +1 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +65 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +415 -0
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +52 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +678 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +75 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +64 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +311 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +175 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +35 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +161 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.d.ts +47 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +655 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-importer.d.ts +23 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-importer.js +92 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-importer.js.map +1 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +65 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +416 -0
- package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +50 -0
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js +700 -0
- package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
- package/dist-client/pages/kpi-value/kpi-value-importer.js +92 -0
- package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +73 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +403 -0
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +47 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/viewparts/kpi-metric-input.d.ts +23 -0
- package/dist-client/viewparts/kpi-metric-input.js +140 -0
- package/dist-client/viewparts/kpi-metric-input.js.map +1 -0
- package/dist-client/viewparts/kpi-metric-meta-editor.d.ts +36 -0
- package/dist-client/viewparts/kpi-metric-meta-editor.js +321 -0
- package/dist-client/viewparts/kpi-metric-meta-editor.js.map +1 -0
- package/dist-client/viewparts/metric-input-hint.d.ts +26 -0
- package/dist-client/viewparts/metric-input-hint.js +19 -0
- package/dist-client/viewparts/metric-input-hint.js.map +1 -0
- package/dist-client/viewparts/metric-input.test.d.ts +1 -0
- package/dist-client/viewparts/metric-input.test.js +43 -0
- package/dist-client/viewparts/metric-input.test.js.map +1 -0
- package/dist-client/viewparts/metric-renderer-registry.d.ts +18 -0
- package/dist-client/viewparts/metric-renderer-registry.js +29 -0
- package/dist-client/viewparts/metric-renderer-registry.js.map +1 -0
- package/dist-client/viewparts/ox-grist-editor-metric-input-hint.d.ts +19 -0
- package/dist-client/viewparts/ox-grist-editor-metric-input-hint.js +76 -0
- package/dist-client/viewparts/ox-grist-editor-metric-input-hint.js.map +1 -0
- package/dist-server/calculator/calculator.test.d.ts +1 -0
- package/dist-server/calculator/calculator.test.js +126 -0
- package/dist-server/calculator/calculator.test.js.map +1 -0
- package/dist-server/calculator/evaluator.d.ts +8 -0
- package/dist-server/calculator/evaluator.js +42 -0
- package/dist-server/calculator/evaluator.js.map +1 -0
- package/dist-server/calculator/functions.d.ts +3 -0
- package/dist-server/calculator/functions.js +62 -0
- package/dist-server/calculator/functions.js.map +1 -0
- package/dist-server/calculator/index.d.ts +6 -0
- package/dist-server/calculator/index.js +10 -0
- package/dist-server/calculator/index.js.map +1 -0
- package/dist-server/calculator/parser.d.ts +34 -0
- package/dist-server/calculator/parser.js +185 -0
- package/dist-server/calculator/parser.js.map +1 -0
- package/dist-server/calculator/provider.d.ts +8 -0
- package/dist-server/calculator/provider.js +13 -0
- package/dist-server/calculator/provider.js.map +1 -0
- package/dist-server/calculator/score.d.ts +21 -0
- package/dist-server/calculator/score.js +36 -0
- package/dist-server/calculator/score.js.map +1 -0
- package/dist-server/calculator/score.test.d.ts +1 -0
- package/dist-server/calculator/score.test.js +47 -0
- package/dist-server/calculator/score.test.js.map +1 -0
- package/dist-server/calculator/validate.d.ts +15 -0
- package/dist-server/calculator/validate.js +30 -0
- package/dist-server/calculator/validate.js.map +1 -0
- package/dist-server/calculator/validate.test.d.ts +1 -0
- package/dist-server/calculator/validate.test.js +45 -0
- package/dist-server/calculator/validate.test.js.map +1 -0
- package/dist-server/controllers/index.d.ts +2 -0
- package/dist-server/controllers/index.js +6 -0
- package/dist-server/controllers/index.js.map +1 -0
- package/dist-server/controllers/kpi-metric-value-provider.d.ts +11 -0
- package/dist-server/controllers/kpi-metric-value-provider.js +97 -0
- package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -0
- package/dist-server/controllers/kpi-value-provider.d.ts +11 -0
- package/dist-server/controllers/kpi-value-provider.js +50 -0
- package/dist-server/controllers/kpi-value-provider.js.map +1 -0
- package/dist-server/index.d.ts +5 -0
- package/dist-server/index.js +9 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +102 -0
- package/dist-server/migrations/1752190849680-seed-kpi-metrics.js.map +1 -0
- package/dist-server/migrations/1752190849681-seed-kpi.d.ts +5 -0
- package/dist-server/migrations/1752190849681-seed-kpi.js +315 -0
- package/dist-server/migrations/1752190849681-seed-kpi.js.map +1 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.d.ts +7 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.js +51 -0
- package/dist-server/migrations/1752192090123-add-grades-to-kpi.js.map +1 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.d.ts +5 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.js +710 -0
- package/dist-server/migrations/1752192090124-add-kpi-statistics.js.map +1 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.d.ts +6 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js +111 -0
- package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js.map +1 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.d.ts +6 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.js +187 -0
- package/dist-server/migrations/1752192090129-seed-kpi-values.js.map +1 -0
- package/dist-server/migrations/1752192090200-add-meta-to-kpi-metric.d.ts +10 -0
- package/dist-server/migrations/1752192090200-add-meta-to-kpi-metric.js +26 -0
- package/dist-server/migrations/1752192090200-add-meta-to-kpi-metric.js.map +1 -0
- package/dist-server/migrations/1752192090201-backfill-kpi-metric-meta.d.ts +11 -0
- package/dist-server/migrations/1752192090201-backfill-kpi-metric-meta.js +54 -0
- package/dist-server/migrations/1752192090201-backfill-kpi-metric-meta.js.map +1 -0
- package/dist-server/migrations/grade-data/x11-performance-table.json +962 -0
- package/dist-server/migrations/grade-data/x12-performance-table.json +611 -0
- package/dist-server/migrations/grade-data/x14-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x21-performance-table.json +889 -0
- package/dist-server/migrations/grade-data/x22-performance-table.json +1064 -0
- package/dist-server/migrations/grade-data/x23-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x31-performance-table.json +644 -0
- package/dist-server/migrations/grade-data/x32-performance-table.json +993 -0
- package/dist-server/migrations/grade-data/x33-performance-table.json +195 -0
- package/dist-server/migrations/grade-data/x34-performance-table.json +12 -0
- package/dist-server/migrations/grade-data/x35-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x41-performance-table.json +825 -0
- package/dist-server/migrations/grade-data/x42-performance-table.json +786 -0
- package/dist-server/migrations/grade-data/x43-performance-table.json +12 -0
- package/dist-server/migrations/grade-data/x44-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x51-performance-table.json +924 -0
- package/dist-server/migrations/grade-data/x52-performance-table.json +42 -0
- package/dist-server/migrations/grade-data/x61-performance-table.json +261 -0
- package/dist-server/migrations/grade-data/x62-performance-table.json +42 -0
- package/dist-server/migrations/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/migrations/seed-data/kpi-metrics-seed.json +517 -0
- package/dist-server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
- package/dist-server/migrations/seed-data/kpi-scopes-seed.json +121 -0
- package/dist-server/migrations/seed-data/kpi-values-seed.json +402 -0
- package/dist-server/migrations/seed-data/kpis-seed.json +488 -0
- package/dist-server/migrations/seed-data/scope-definitions-seed.json +90 -0
- package/dist-server/routes.d.ts +1 -0
- package/dist-server/routes.js +72 -0
- package/dist-server/routes.js.map +1 -0
- package/dist-server/service/index.d.ts +15 -0
- package/dist-server/service/index.js +49 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/kpi/aggregate-kpi.d.ts +8 -0
- package/dist-server/service/kpi/aggregate-kpi.js +123 -0
- package/dist-server/service/kpi/aggregate-kpi.js.map +1 -0
- package/dist-server/service/kpi/event-subscriber.d.ts +9 -0
- package/dist-server/service/kpi/event-subscriber.js +31 -0
- package/dist-server/service/kpi/event-subscriber.js.map +1 -0
- package/dist-server/service/kpi/index.d.ts +8 -0
- package/dist-server/service/kpi/index.js +12 -0
- package/dist-server/service/kpi/index.js.map +1 -0
- package/dist-server/service/kpi/kpi-formula.service.d.ts +49 -0
- package/dist-server/service/kpi/kpi-formula.service.js +165 -0
- package/dist-server/service/kpi/kpi-formula.service.js.map +1 -0
- package/dist-server/service/kpi/kpi-grade.types.d.ts +22 -0
- package/dist-server/service/kpi/kpi-grade.types.js +3 -0
- package/dist-server/service/kpi/kpi-grade.types.js.map +1 -0
- package/dist-server/service/kpi/kpi-history.d.ts +29 -0
- package/dist-server/service/kpi/kpi-history.js +148 -0
- package/dist-server/service/kpi/kpi-history.js.map +1 -0
- package/dist-server/service/kpi/kpi-mutation.d.ts +14 -0
- package/dist-server/service/kpi/kpi-mutation.js +553 -0
- package/dist-server/service/kpi/kpi-mutation.js.map +1 -0
- package/dist-server/service/kpi/kpi-query.d.ts +24 -0
- package/dist-server/service/kpi/kpi-query.js +309 -0
- package/dist-server/service/kpi/kpi-query.js.map +1 -0
- package/dist-server/service/kpi/kpi-type.d.ts +48 -0
- package/dist-server/service/kpi/kpi-type.js +221 -0
- package/dist-server/service/kpi/kpi-type.js.map +1 -0
- package/dist-server/service/kpi/kpi.d.ts +130 -0
- package/dist-server/service/kpi/kpi.js +356 -0
- package/dist-server/service/kpi/kpi.js.map +1 -0
- package/dist-server/service/kpi-alert/index.d.ts +2 -0
- package/dist-server/service/kpi-alert/index.js +6 -0
- package/dist-server/service/kpi-alert/index.js.map +1 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.d.ts +4 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.js +72 -0
- package/dist-server/service/kpi-alert/kpi-alert-query.js.map +1 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.d.ts +8 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.js +33 -0
- package/dist-server/service/kpi-alert/kpi-alert-type.js.map +1 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.d.ts +8 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +126 -0
- package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -0
- package/dist-server/service/kpi-metric/index.d.ts +6 -0
- package/dist-server/service/kpi-metric/index.js +10 -0
- package/dist-server/service/kpi-metric/index.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.d.ts +46 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.js +27 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.test.d.ts +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.test.js +43 -0
- package/dist-server/service/kpi-metric/kpi-metric-meta.test.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-mutation.d.ts +10 -0
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js +296 -0
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.d.ts +14 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.js +126 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +45 -0
- package/dist-server/service/kpi-metric/kpi-metric-type.js +177 -0
- package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -0
- package/dist-server/service/kpi-metric/kpi-metric.d.ts +39 -0
- package/dist-server/service/kpi-metric/kpi-metric.js +174 -0
- package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -0
- package/dist-server/service/kpi-metric-value/index.d.ts +6 -0
- package/dist-server/service/kpi-metric-value/index.js +10 -0
- package/dist-server/service/kpi-metric-value/index.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +11 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +283 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +13 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +106 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +26 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +112 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +23 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js +107 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -0
- package/dist-server/service/kpi-org-scope/index.d.ts +5 -0
- package/dist-server/service/kpi-org-scope/index.js +9 -0
- package/dist-server/service/kpi-org-scope/index.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.d.ts +8 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js +170 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.d.ts +14 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js +152 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.d.ts +26 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js +101 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js.map +1 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.d.ts +26 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.js +135 -0
- package/dist-server/service/kpi-org-scope/kpi-org-scope.js.map +1 -0
- package/dist-server/service/kpi-scope/index.d.ts +9 -0
- package/dist-server/service/kpi-scope/index.js +14 -0
- package/dist-server/service/kpi-scope/index.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope-mutation.d.ts +9 -0
- package/dist-server/service/kpi-scope/kpi-scope-mutation.js +140 -0
- package/dist-server/service/kpi-scope/kpi-scope-mutation.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope-query.d.ts +11 -0
- package/dist-server/service/kpi-scope/kpi-scope-query.js +89 -0
- package/dist-server/service/kpi-scope/kpi-scope-query.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope-type.d.ts +35 -0
- package/dist-server/service/kpi-scope/kpi-scope-type.js +138 -0
- package/dist-server/service/kpi-scope/kpi-scope-type.js.map +1 -0
- package/dist-server/service/kpi-scope/kpi-scope.d.ts +38 -0
- package/dist-server/service/kpi-scope/kpi-scope.js +144 -0
- package/dist-server/service/kpi-scope/kpi-scope.js.map +1 -0
- package/dist-server/service/kpi-statistic/index.d.ts +6 -0
- package/dist-server/service/kpi-statistic/index.js +10 -0
- package/dist-server/service/kpi-statistic/index.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.d.ts +43 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.js +181 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.d.ts +50 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.js +324 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.d.ts +14 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js +279 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-query.d.ts +17 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-query.js +183 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-query.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-type.d.ts +59 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-type.js +200 -0
- package/dist-server/service/kpi-statistic/kpi-statistic-type.js.map +1 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +42 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js +210 -0
- package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -0
- package/dist-server/service/kpi-value/index.d.ts +6 -0
- package/dist-server/service/kpi-value/index.js +10 -0
- package/dist-server/service/kpi-value/index.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-mutation.d.ts +13 -0
- package/dist-server/service/kpi-value/kpi-value-mutation.js +447 -0
- package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-query.d.ts +22 -0
- package/dist-server/service/kpi-value/kpi-value-query.js +149 -0
- package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-score.service.d.ts +24 -0
- package/dist-server/service/kpi-value/kpi-value-score.service.js +61 -0
- package/dist-server/service/kpi-value/kpi-value-score.service.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value-type.d.ts +33 -0
- package/dist-server/service/kpi-value/kpi-value-type.js +156 -0
- package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -0
- package/dist-server/service/kpi-value/kpi-value.d.ts +32 -0
- package/dist-server/service/kpi-value/kpi-value.js +175 -0
- package/dist-server/service/kpi-value/kpi-value.js.map +1 -0
- package/dist-server/service/utils/value-date-util.d.ts +4 -0
- package/dist-server/service/utils/value-date-util.js +117 -0
- package/dist-server/service/utils/value-date-util.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-input.test.js","sourceRoot":"","sources":["../../client/viewparts/metric-input.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveInputHint } from './metric-input-hint'\nimport { MetricRendererRegistry } from './metric-renderer-registry'\n\ndescribe('resolveInputHint (client copy)', () => {\n it('meta 없음 → number 폴백', () => {\n expect(resolveInputHint(null)).toEqual({ widget: 'number', readonly: false })\n expect(resolveInputHint({ collectType: 'AUTO' })).toEqual({ widget: 'number', readonly: true })\n })\n it('rating 기본값(max 5, step 1)', () => {\n expect(resolveInputHint({ meta: { input: { widget: 'rating' } } })).toEqual({\n widget: 'rating',\n max: 5,\n step: 1,\n readonly: false\n })\n })\n it('readonly 명시 false 는 AUTO 라도 존중', () => {\n expect(resolveInputHint({ collectType: 'AUTO', meta: { input: { widget: 'number', readonly: false } } }).readonly).toBe(\n false\n )\n })\n})\n\ndescribe('MetricRendererRegistry', () => {\n const dummy: any = () => null\n beforeEach(() => MetricRendererRegistry.clear())\n\n it('매칭 없으면 undefined (→ 제네릭)', () => {\n expect(MetricRendererRegistry.resolve({ meta: {} } as any)).toBeUndefined()\n })\n\n it('predicate 매칭 시 렌더러 반환, 먼저 등록된 것 우선', () => {\n const r1: any = () => 'r1'\n const r2: any = () => 'r2'\n MetricRendererRegistry.register((m: any) => m.name === 'X', r1)\n MetricRendererRegistry.register((m: any) => m.name === 'X', r2)\n expect(MetricRendererRegistry.resolve({ name: 'X' } as any)).toBe(r1)\n expect(MetricRendererRegistry.resolve({ name: 'Y' } as any)).toBeUndefined()\n })\n\n it('predicate 예외는 매칭 실패로 처리(다음으로 진행)', () => {\n MetricRendererRegistry.register(() => {\n throw new Error('boom')\n }, dummy)\n const ok: any = () => 'ok'\n MetricRendererRegistry.register((m: any) => m.name === 'Z', ok)\n expect(MetricRendererRegistry.resolve({ name: 'Z' } as any)).toBe(ok)\n })\n})\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TemplateResult } from 'lit';
|
|
2
|
+
import type { KpiMetricLike } from './metric-input-hint';
|
|
3
|
+
/** 특정 metric 에 대한 커스텀 입력 렌더러. 제네릭 위젯으로 표현 못 하는 예외용. */
|
|
4
|
+
export type MetricInputRenderer = (metric: KpiMetricLike, value: any, onChange: (value: any) => void) => TemplateResult;
|
|
5
|
+
/**
|
|
6
|
+
* 커스텀 렌더러 레지스트리 (80/20 탈출구).
|
|
7
|
+
* 제네릭 렌더러가 대부분을 처리하고, 특수 UI 만 predicate 로 등록해 우선 적용한다.
|
|
8
|
+
*/
|
|
9
|
+
declare class Registry {
|
|
10
|
+
private entries;
|
|
11
|
+
register(predicate: (metric: KpiMetricLike) => boolean, renderer: MetricInputRenderer): void;
|
|
12
|
+
/** 가장 먼저 매칭되는 커스텀 렌더러(없으면 undefined → 제네릭 사용). */
|
|
13
|
+
resolve(metric: KpiMetricLike): MetricInputRenderer | undefined;
|
|
14
|
+
/** 테스트/리셋용. */
|
|
15
|
+
clear(): void;
|
|
16
|
+
}
|
|
17
|
+
export declare const MetricRendererRegistry: Registry;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 커스텀 렌더러 레지스트리 (80/20 탈출구).
|
|
3
|
+
* 제네릭 렌더러가 대부분을 처리하고, 특수 UI 만 predicate 로 등록해 우선 적용한다.
|
|
4
|
+
*/
|
|
5
|
+
class Registry {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.entries = [];
|
|
8
|
+
}
|
|
9
|
+
register(predicate, renderer) {
|
|
10
|
+
this.entries.push({ predicate, renderer });
|
|
11
|
+
}
|
|
12
|
+
/** 가장 먼저 매칭되는 커스텀 렌더러(없으면 undefined → 제네릭 사용). */
|
|
13
|
+
resolve(metric) {
|
|
14
|
+
return this.entries.find(e => {
|
|
15
|
+
try {
|
|
16
|
+
return e.predicate(metric);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
})?.renderer;
|
|
22
|
+
}
|
|
23
|
+
/** 테스트/리셋용. */
|
|
24
|
+
clear() {
|
|
25
|
+
this.entries = [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const MetricRendererRegistry = new Registry();
|
|
29
|
+
//# sourceMappingURL=metric-renderer-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metric-renderer-registry.js","sourceRoot":"","sources":["../../client/viewparts/metric-renderer-registry.ts"],"names":[],"mappings":"AAYA;;;GAGG;AACH,MAAM,QAAQ;IAAd;QACU,YAAO,GAAY,EAAE,CAAA;IAqB/B,CAAC;IAnBC,QAAQ,CAAC,SAA6C,EAAE,QAA6B;QACnF,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC5C,CAAC;IAED,kDAAkD;IAClD,OAAO,CAAC,MAAqB;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;YAC3B,IAAI,CAAC;gBACH,OAAO,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC,CAAC,EAAE,QAAQ,CAAA;IACd,CAAC;IAED,eAAe;IACf,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;IACnB,CAAC;CACF;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,QAAQ,EAAE,CAAA","sourcesContent":["import type { TemplateResult } from 'lit'\nimport type { KpiMetricLike } from './metric-input-hint'\n\n/** 특정 metric 에 대한 커스텀 입력 렌더러. 제네릭 위젯으로 표현 못 하는 예외용. */\nexport type MetricInputRenderer = (\n metric: KpiMetricLike,\n value: any,\n onChange: (value: any) => void\n) => TemplateResult\n\ntype Entry = { predicate: (metric: KpiMetricLike) => boolean; renderer: MetricInputRenderer }\n\n/**\n * 커스텀 렌더러 레지스트리 (80/20 탈출구).\n * 제네릭 렌더러가 대부분을 처리하고, 특수 UI 만 predicate 로 등록해 우선 적용한다.\n */\nclass Registry {\n private entries: Entry[] = []\n\n register(predicate: (metric: KpiMetricLike) => boolean, renderer: MetricInputRenderer): void {\n this.entries.push({ predicate, renderer })\n }\n\n /** 가장 먼저 매칭되는 커스텀 렌더러(없으면 undefined → 제네릭 사용). */\n resolve(metric: KpiMetricLike): MetricInputRenderer | undefined {\n return this.entries.find(e => {\n try {\n return e.predicate(metric)\n } catch {\n return false\n }\n })?.renderer\n }\n\n /** 테스트/리셋용. */\n clear(): void {\n this.entries = []\n }\n}\n\nexport const MetricRendererRegistry = new Registry()\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { OxGristEditor } from '@operato/data-grist';
|
|
2
|
+
import './kpi-metric-meta-editor';
|
|
3
|
+
/**
|
|
4
|
+
* KpiMetric.meta 컬럼용 grist 커스텀 에디터.
|
|
5
|
+
*
|
|
6
|
+
* 셀을 클릭/엔터하면 일반 텍스트 에디터 대신 구조화된 <kpi-metric-meta-editor> 팝업을 연다.
|
|
7
|
+
* (json5 에디터가 ox-popup-code-input 팝업을 여는 것과 동일한 패턴)
|
|
8
|
+
* 저장 시 field-change 로 새 meta 객체를 grist 에 되돌려 dirty 처리 → 목록의 저장 버튼으로 반영.
|
|
9
|
+
*
|
|
10
|
+
* 등록: registerEditor('metric-input-hint', OxGristEditorMetricInputHint)
|
|
11
|
+
* 컬럼: { type: 'metric-input-hint', name: 'meta', record: { editable: true } }
|
|
12
|
+
*/
|
|
13
|
+
export declare class OxGristEditorMetricInputHint extends OxGristEditor {
|
|
14
|
+
get editorTemplate(): import("lit-html").TemplateResult<1>;
|
|
15
|
+
private _summary;
|
|
16
|
+
_onclick(e: Event): void;
|
|
17
|
+
_onkeydown(e: Event): void;
|
|
18
|
+
private _openEditor;
|
|
19
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import { customElement } from 'lit/decorators.js';
|
|
4
|
+
import { openPopup } from '@operato/layout';
|
|
5
|
+
import { OxGristEditor } from '@operato/data-grist';
|
|
6
|
+
import './kpi-metric-meta-editor';
|
|
7
|
+
/**
|
|
8
|
+
* KpiMetric.meta 컬럼용 grist 커스텀 에디터.
|
|
9
|
+
*
|
|
10
|
+
* 셀을 클릭/엔터하면 일반 텍스트 에디터 대신 구조화된 <kpi-metric-meta-editor> 팝업을 연다.
|
|
11
|
+
* (json5 에디터가 ox-popup-code-input 팝업을 여는 것과 동일한 패턴)
|
|
12
|
+
* 저장 시 field-change 로 새 meta 객체를 grist 에 되돌려 dirty 처리 → 목록의 저장 버튼으로 반영.
|
|
13
|
+
*
|
|
14
|
+
* 등록: registerEditor('metric-input-hint', OxGristEditorMetricInputHint)
|
|
15
|
+
* 컬럼: { type: 'metric-input-hint', name: 'meta', record: { editable: true } }
|
|
16
|
+
*/
|
|
17
|
+
let OxGristEditorMetricInputHint = class OxGristEditorMetricInputHint extends OxGristEditor {
|
|
18
|
+
get editorTemplate() {
|
|
19
|
+
return html `<div tabindex="0">${this._summary(this.value)}</div>`;
|
|
20
|
+
}
|
|
21
|
+
_summary(meta) {
|
|
22
|
+
const input = meta?.input;
|
|
23
|
+
if (!input?.widget)
|
|
24
|
+
return '';
|
|
25
|
+
const extra = [
|
|
26
|
+
input.max !== undefined ? `max ${input.max}` : '',
|
|
27
|
+
input.step !== undefined ? `step ${input.step}` : '',
|
|
28
|
+
input.readonly ? 'RO' : ''
|
|
29
|
+
]
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.join('·');
|
|
32
|
+
return extra ? `${input.widget} (${extra})` : input.widget;
|
|
33
|
+
}
|
|
34
|
+
_onclick(e) {
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
this._openEditor();
|
|
37
|
+
}
|
|
38
|
+
_onkeydown(e) {
|
|
39
|
+
if (e.key === 'Enter') {
|
|
40
|
+
e.stopPropagation();
|
|
41
|
+
this._openEditor();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
_openEditor() {
|
|
45
|
+
// 이 컬럼의 value = record.meta. 에디터는 metric.meta 를 읽으므로 record 에 현재 value 를 얹어 전달.
|
|
46
|
+
const metric = { ...(this.record || {}), meta: this.value };
|
|
47
|
+
const change = (meta) => {
|
|
48
|
+
this.dispatchEvent(new CustomEvent('field-change', {
|
|
49
|
+
bubbles: true,
|
|
50
|
+
composed: true,
|
|
51
|
+
detail: {
|
|
52
|
+
before: this.value,
|
|
53
|
+
after: meta,
|
|
54
|
+
column: this.column,
|
|
55
|
+
record: this.record,
|
|
56
|
+
row: this.row
|
|
57
|
+
}
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
const popup = openPopup(html `
|
|
61
|
+
<kpi-metric-meta-editor
|
|
62
|
+
.metric=${metric}
|
|
63
|
+
@metric-meta-save=${(ev) => {
|
|
64
|
+
change(ev.detail.meta);
|
|
65
|
+
popup.close();
|
|
66
|
+
}}
|
|
67
|
+
@metric-meta-cancel=${() => popup.close()}
|
|
68
|
+
></kpi-metric-meta-editor>
|
|
69
|
+
`, { backdrop: true, title: `입력 힌트 · ${this.record?.name || ''}`, size: 'medium' });
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
OxGristEditorMetricInputHint = __decorate([
|
|
73
|
+
customElement('ox-grist-editor-metric-input-hint')
|
|
74
|
+
], OxGristEditorMetricInputHint);
|
|
75
|
+
export { OxGristEditorMetricInputHint };
|
|
76
|
+
//# sourceMappingURL=ox-grist-editor-metric-input-hint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ox-grist-editor-metric-input-hint.js","sourceRoot":"","sources":["../../client/viewparts/ox-grist-editor-metric-input-hint.ts"],"names":[],"mappings":"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","sourcesContent":["import { html } from 'lit'\nimport { customElement } from 'lit/decorators.js'\nimport { openPopup } from '@operato/layout'\nimport { OxGristEditor } from '@operato/data-grist'\n\nimport './kpi-metric-meta-editor'\n\n/**\n * KpiMetric.meta 컬럼용 grist 커스텀 에디터.\n *\n * 셀을 클릭/엔터하면 일반 텍스트 에디터 대신 구조화된 <kpi-metric-meta-editor> 팝업을 연다.\n * (json5 에디터가 ox-popup-code-input 팝업을 여는 것과 동일한 패턴)\n * 저장 시 field-change 로 새 meta 객체를 grist 에 되돌려 dirty 처리 → 목록의 저장 버튼으로 반영.\n *\n * 등록: registerEditor('metric-input-hint', OxGristEditorMetricInputHint)\n * 컬럼: { type: 'metric-input-hint', name: 'meta', record: { editable: true } }\n */\n@customElement('ox-grist-editor-metric-input-hint')\nexport class OxGristEditorMetricInputHint extends OxGristEditor {\n get editorTemplate() {\n return html`<div tabindex=\"0\">${this._summary(this.value)}</div>`\n }\n\n private _summary(meta: any): string {\n const input = meta?.input\n if (!input?.widget) return ''\n const extra = [\n input.max !== undefined ? `max ${input.max}` : '',\n input.step !== undefined ? `step ${input.step}` : '',\n input.readonly ? 'RO' : ''\n ]\n .filter(Boolean)\n .join('·')\n return extra ? `${input.widget} (${extra})` : input.widget\n }\n\n _onclick(e: Event) {\n e.stopPropagation()\n this._openEditor()\n }\n\n _onkeydown(e: Event) {\n if ((e as KeyboardEvent).key === 'Enter') {\n e.stopPropagation()\n this._openEditor()\n }\n }\n\n private _openEditor() {\n // 이 컬럼의 value = record.meta. 에디터는 metric.meta 를 읽으므로 record 에 현재 value 를 얹어 전달.\n const metric = { ...((this.record as any) || {}), meta: this.value }\n\n const change = (meta: any) => {\n this.dispatchEvent(\n new CustomEvent('field-change', {\n bubbles: true,\n composed: true,\n detail: {\n before: this.value,\n after: meta,\n column: this.column,\n record: this.record,\n row: this.row\n }\n })\n )\n }\n\n const popup = openPopup(\n html`\n <kpi-metric-meta-editor\n .metric=${metric}\n @metric-meta-save=${(ev: CustomEvent) => {\n change(ev.detail.meta)\n popup.close()\n }}\n @metric-meta-cancel=${() => popup.close()}\n ></kpi-metric-meta-editor>\n `,\n { backdrop: true, title: `입력 힌트 · ${(this.record as any)?.name || ''}`, size: 'medium' }\n )\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const parser_1 = require("./parser");
|
|
4
|
+
const evaluator_1 = require("./evaluator");
|
|
5
|
+
const functions_1 = require("./functions");
|
|
6
|
+
const provider_1 = require("./provider");
|
|
7
|
+
/** 테스트 헬퍼: 수식 + 변수값 맵 → 평가 결과 */
|
|
8
|
+
async function evalWith(formula, values = {}) {
|
|
9
|
+
const ast = (0, parser_1.parseFormula)(formula);
|
|
10
|
+
return (0, evaluator_1.evaluateFormula)(ast, { functions: functions_1.builtinFunctions, provider: new provider_1.ObjectValueProvider(values) });
|
|
11
|
+
}
|
|
12
|
+
describe('parser (parseFormula)', () => {
|
|
13
|
+
it('숫자/소수를 파싱한다', () => {
|
|
14
|
+
expect((0, parser_1.parseFormula)('42')).toEqual({ type: 'number', value: 42 });
|
|
15
|
+
expect((0, parser_1.parseFormula)('3.14')).toEqual({ type: 'number', value: 3.14 });
|
|
16
|
+
});
|
|
17
|
+
it('대괄호 변수(한글·공백 포함)를 파싱하고 대괄호를 벗긴다', () => {
|
|
18
|
+
expect((0, parser_1.parseFormula)('[재해 건수]')).toEqual({ type: 'variable', name: '재해 건수' });
|
|
19
|
+
});
|
|
20
|
+
it('bare 식별자를 변수로 파싱한다', () => {
|
|
21
|
+
expect((0, parser_1.parseFormula)('x11')).toEqual({ type: 'variable', name: 'x11' });
|
|
22
|
+
});
|
|
23
|
+
it('사칙연산 우선순위(*,/ 가 +,- 보다 높음)를 지킨다', () => {
|
|
24
|
+
const ast = (0, parser_1.parseFormula)('1 + 2 * 3');
|
|
25
|
+
// + 가 최상위, 오른쪽이 (2*3)
|
|
26
|
+
expect(ast).toMatchObject({ type: 'binary', op: '+', right: { type: 'binary', op: '*' } });
|
|
27
|
+
});
|
|
28
|
+
it('괄호로 우선순위를 바꾼다', () => {
|
|
29
|
+
const ast = (0, parser_1.parseFormula)('(1 + 2) * 3');
|
|
30
|
+
expect(ast).toMatchObject({ type: 'binary', op: '*', left: { type: 'binary', op: '+' } });
|
|
31
|
+
});
|
|
32
|
+
it('단항 마이너스를 파싱한다', () => {
|
|
33
|
+
expect((0, parser_1.parseFormula)('-5')).toEqual({ type: 'unary', op: '-', arg: { type: 'number', value: 5 } });
|
|
34
|
+
});
|
|
35
|
+
it('함수 호출을 파싱한다', () => {
|
|
36
|
+
const ast = (0, parser_1.parseFormula)('sum(1, 2, 3)');
|
|
37
|
+
expect(ast).toMatchObject({ type: 'function', name: 'sum' });
|
|
38
|
+
expect(ast.args).toHaveLength(3);
|
|
39
|
+
});
|
|
40
|
+
it('잘못된 토큰/불완전 수식은 throw 한다', () => {
|
|
41
|
+
expect(() => (0, parser_1.parseFormula)('1 @ 2')).toThrow();
|
|
42
|
+
expect(() => (0, parser_1.parseFormula)('[a] +')).toThrow();
|
|
43
|
+
expect(() => (0, parser_1.parseFormula)('(1 + 2')).toThrow();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe('collectVariableNames', () => {
|
|
47
|
+
it('대괄호 한글 변수명을 정확히 수집한다 (정규식이 놓치는 케이스)', () => {
|
|
48
|
+
const ast = (0, parser_1.parseFormula)('[실제공사기간] / [연면적]');
|
|
49
|
+
expect((0, parser_1.collectVariableNames)(ast).sort()).toEqual(['실제공사기간', '연면적'].sort());
|
|
50
|
+
});
|
|
51
|
+
it('중복을 제거한다', () => {
|
|
52
|
+
const ast = (0, parser_1.parseFormula)('([계획공사비] - [계획공사비]) / [계획공사비]');
|
|
53
|
+
expect((0, parser_1.collectVariableNames)(ast)).toEqual(['계획공사비']);
|
|
54
|
+
});
|
|
55
|
+
it('함수 인자 안의 변수도 수집한다', () => {
|
|
56
|
+
const ast = (0, parser_1.parseFormula)('round([a] / [b], 2)');
|
|
57
|
+
expect((0, parser_1.collectVariableNames)(ast).sort()).toEqual(['a', 'b']);
|
|
58
|
+
});
|
|
59
|
+
it('변수가 없으면 빈 배열', () => {
|
|
60
|
+
expect((0, parser_1.collectVariableNames)((0, parser_1.parseFormula)('1 + 2'))).toEqual([]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe('evaluator — 연산', () => {
|
|
64
|
+
it('사칙연산과 우선순위', async () => {
|
|
65
|
+
expect(await evalWith('1 + 2 * 3')).toBe(7);
|
|
66
|
+
expect(await evalWith('(1 + 2) * 3')).toBe(9);
|
|
67
|
+
expect(await evalWith('10 / 4')).toBe(2.5);
|
|
68
|
+
expect(await evalWith('-5 + 3')).toBe(-2);
|
|
69
|
+
});
|
|
70
|
+
it('변수를 provider 로 해석한다 (대괄호 한글 포함)', async () => {
|
|
71
|
+
expect(await evalWith('[a] / [b]', { a: 10, b: 2 })).toBe(5);
|
|
72
|
+
expect(await evalWith('[재해 건수] / [연간 근로자 수] * 100', { '재해 건수': 5, '연간 근로자 수': 1000 })).toBeCloseTo(0.5);
|
|
73
|
+
});
|
|
74
|
+
it('알 수 없는 함수는 throw', async () => {
|
|
75
|
+
await expect(evalWith('nope(1)')).rejects.toThrow(/Unknown function/);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('builtinFunctions', () => {
|
|
79
|
+
it('sum/avg/min/max/round/if', async () => {
|
|
80
|
+
expect(await evalWith('sum(1, 2, 3)')).toBe(6);
|
|
81
|
+
expect(await evalWith('avg(2, 4, 6)')).toBe(4);
|
|
82
|
+
expect(await evalWith('min(3, 1, 2)')).toBe(1);
|
|
83
|
+
expect(await evalWith('max(3, 1, 2)')).toBe(3);
|
|
84
|
+
expect(await evalWith('round(3.14159, 2)')).toBe(3.14);
|
|
85
|
+
expect(await evalWith('if(1, 10, 20)')).toBe(10);
|
|
86
|
+
expect(await evalWith('if(0, 10, 20)')).toBe(20);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* 회귀 방어: kpi 모듈 seed(kpis-seed.json)의 실제 formula 코퍼스.
|
|
91
|
+
* 이 formula들은 dkpi 런타임에서 AST 엔진으로 평가되므로, 파싱·변수추출·평가가
|
|
92
|
+
* 깨지면 dkpi 가 깨진다. (P0-1: aggregate 경로도 이제 이 엔진을 씀)
|
|
93
|
+
*/
|
|
94
|
+
describe('seed formula 코퍼스 (회귀 방어)', () => {
|
|
95
|
+
const SEED_FORMULAS = [
|
|
96
|
+
'[실제공사기간] / [연면적]',
|
|
97
|
+
'([실제공사기간] - [계획공사기간]) / [계획공사기간]',
|
|
98
|
+
'[일정 이탈 수준]',
|
|
99
|
+
'[실제공사비] / [연면적]',
|
|
100
|
+
'([실제공사비]-[계획공사비]) / [계획공사비]',
|
|
101
|
+
'[품질시험 불합격 건수] / [연면적]',
|
|
102
|
+
'[재해 건수] / [연간 근로자 수] * 100',
|
|
103
|
+
'[노동손실일수] / ( [총 근로자수] * [1인당 연간 근로 시간] ) * 1000',
|
|
104
|
+
'[총 건설 폐기물 발생량] / [연면적]',
|
|
105
|
+
'[투입인력] / [연면적]'
|
|
106
|
+
];
|
|
107
|
+
it.each(SEED_FORMULAS)('파싱되고 변수추출된다: %s', formula => {
|
|
108
|
+
const ast = (0, parser_1.parseFormula)(formula);
|
|
109
|
+
const vars = (0, parser_1.collectVariableNames)(ast);
|
|
110
|
+
expect(Array.isArray(vars)).toBe(true);
|
|
111
|
+
// 단일 변수 formula 도 최소 1개 이상
|
|
112
|
+
expect(vars.length).toBeGreaterThanOrEqual(1);
|
|
113
|
+
});
|
|
114
|
+
it('대표 formula 들이 올바른 수치를 낸다', async () => {
|
|
115
|
+
expect(await evalWith('[실제공사기간] / [연면적]', { 실제공사기간: 300, 연면적: 1500 })).toBeCloseTo(0.2);
|
|
116
|
+
expect(await evalWith('([실제공사비]-[계획공사비]) / [계획공사비]', { 실제공사비: 1200, 계획공사비: 1000 })).toBeCloseTo(0.2);
|
|
117
|
+
expect(await evalWith('[노동손실일수] / ( [총 근로자수] * [1인당 연간 근로 시간] ) * 1000', {
|
|
118
|
+
노동손실일수: 10,
|
|
119
|
+
'총 근로자수': 100,
|
|
120
|
+
'1인당 연간 근로 시간': 2000
|
|
121
|
+
})).toBeCloseTo(0.05);
|
|
122
|
+
// 단일 변수 pass-through
|
|
123
|
+
expect(await evalWith('[일정 이탈 수준]', { '일정 이탈 수준': 3 })).toBe(3);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
//# sourceMappingURL=calculator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calculator.test.js","sourceRoot":"","sources":["../../server/calculator/calculator.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { parseFormula, collectVariableNames } from './parser'\nimport { evaluateFormula } from './evaluator'\nimport { builtinFunctions } from './functions'\nimport { ObjectValueProvider } from './provider'\n\n/** 테스트 헬퍼: 수식 + 변수값 맵 → 평가 결과 */\nasync function evalWith(formula: string, values: Record<string, any> = {}): Promise<any> {\n const ast = parseFormula(formula)\n return evaluateFormula(ast, { functions: builtinFunctions, provider: new ObjectValueProvider(values) })\n}\n\ndescribe('parser (parseFormula)', () => {\n it('숫자/소수를 파싱한다', () => {\n expect(parseFormula('42')).toEqual({ type: 'number', value: 42 })\n expect(parseFormula('3.14')).toEqual({ type: 'number', value: 3.14 })\n })\n\n it('대괄호 변수(한글·공백 포함)를 파싱하고 대괄호를 벗긴다', () => {\n expect(parseFormula('[재해 건수]')).toEqual({ type: 'variable', name: '재해 건수' })\n })\n\n it('bare 식별자를 변수로 파싱한다', () => {\n expect(parseFormula('x11')).toEqual({ type: 'variable', name: 'x11' })\n })\n\n it('사칙연산 우선순위(*,/ 가 +,- 보다 높음)를 지킨다', () => {\n const ast = parseFormula('1 + 2 * 3')\n // + 가 최상위, 오른쪽이 (2*3)\n expect(ast).toMatchObject({ type: 'binary', op: '+', right: { type: 'binary', op: '*' } })\n })\n\n it('괄호로 우선순위를 바꾼다', () => {\n const ast = parseFormula('(1 + 2) * 3')\n expect(ast).toMatchObject({ type: 'binary', op: '*', left: { type: 'binary', op: '+' } })\n })\n\n it('단항 마이너스를 파싱한다', () => {\n expect(parseFormula('-5')).toEqual({ type: 'unary', op: '-', arg: { type: 'number', value: 5 } })\n })\n\n it('함수 호출을 파싱한다', () => {\n const ast = parseFormula('sum(1, 2, 3)')\n expect(ast).toMatchObject({ type: 'function', name: 'sum' })\n expect((ast as any).args).toHaveLength(3)\n })\n\n it('잘못된 토큰/불완전 수식은 throw 한다', () => {\n expect(() => parseFormula('1 @ 2')).toThrow()\n expect(() => parseFormula('[a] +')).toThrow()\n expect(() => parseFormula('(1 + 2')).toThrow()\n })\n})\n\ndescribe('collectVariableNames', () => {\n it('대괄호 한글 변수명을 정확히 수집한다 (정규식이 놓치는 케이스)', () => {\n const ast = parseFormula('[실제공사기간] / [연면적]')\n expect(collectVariableNames(ast).sort()).toEqual(['실제공사기간', '연면적'].sort())\n })\n\n it('중복을 제거한다', () => {\n const ast = parseFormula('([계획공사비] - [계획공사비]) / [계획공사비]')\n expect(collectVariableNames(ast)).toEqual(['계획공사비'])\n })\n\n it('함수 인자 안의 변수도 수집한다', () => {\n const ast = parseFormula('round([a] / [b], 2)')\n expect(collectVariableNames(ast).sort()).toEqual(['a', 'b'])\n })\n\n it('변수가 없으면 빈 배열', () => {\n expect(collectVariableNames(parseFormula('1 + 2'))).toEqual([])\n })\n})\n\ndescribe('evaluator — 연산', () => {\n it('사칙연산과 우선순위', async () => {\n expect(await evalWith('1 + 2 * 3')).toBe(7)\n expect(await evalWith('(1 + 2) * 3')).toBe(9)\n expect(await evalWith('10 / 4')).toBe(2.5)\n expect(await evalWith('-5 + 3')).toBe(-2)\n })\n\n it('변수를 provider 로 해석한다 (대괄호 한글 포함)', async () => {\n expect(await evalWith('[a] / [b]', { a: 10, b: 2 })).toBe(5)\n expect(await evalWith('[재해 건수] / [연간 근로자 수] * 100', { '재해 건수': 5, '연간 근로자 수': 1000 })).toBeCloseTo(0.5)\n })\n\n it('알 수 없는 함수는 throw', async () => {\n await expect(evalWith('nope(1)')).rejects.toThrow(/Unknown function/)\n })\n})\n\ndescribe('builtinFunctions', () => {\n it('sum/avg/min/max/round/if', async () => {\n expect(await evalWith('sum(1, 2, 3)')).toBe(6)\n expect(await evalWith('avg(2, 4, 6)')).toBe(4)\n expect(await evalWith('min(3, 1, 2)')).toBe(1)\n expect(await evalWith('max(3, 1, 2)')).toBe(3)\n expect(await evalWith('round(3.14159, 2)')).toBe(3.14)\n expect(await evalWith('if(1, 10, 20)')).toBe(10)\n expect(await evalWith('if(0, 10, 20)')).toBe(20)\n })\n})\n\n/**\n * 회귀 방어: kpi 모듈 seed(kpis-seed.json)의 실제 formula 코퍼스.\n * 이 formula들은 dkpi 런타임에서 AST 엔진으로 평가되므로, 파싱·변수추출·평가가\n * 깨지면 dkpi 가 깨진다. (P0-1: aggregate 경로도 이제 이 엔진을 씀)\n */\ndescribe('seed formula 코퍼스 (회귀 방어)', () => {\n const SEED_FORMULAS = [\n '[실제공사기간] / [연면적]',\n '([실제공사기간] - [계획공사기간]) / [계획공사기간]',\n '[일정 이탈 수준]',\n '[실제공사비] / [연면적]',\n '([실제공사비]-[계획공사비]) / [계획공사비]',\n '[품질시험 불합격 건수] / [연면적]',\n '[재해 건수] / [연간 근로자 수] * 100',\n '[노동손실일수] / ( [총 근로자수] * [1인당 연간 근로 시간] ) * 1000',\n '[총 건설 폐기물 발생량] / [연면적]',\n '[투입인력] / [연면적]'\n ]\n\n it.each(SEED_FORMULAS)('파싱되고 변수추출된다: %s', formula => {\n const ast = parseFormula(formula)\n const vars = collectVariableNames(ast)\n expect(Array.isArray(vars)).toBe(true)\n // 단일 변수 formula 도 최소 1개 이상\n expect(vars.length).toBeGreaterThanOrEqual(1)\n })\n\n it('대표 formula 들이 올바른 수치를 낸다', async () => {\n expect(await evalWith('[실제공사기간] / [연면적]', { 실제공사기간: 300, 연면적: 1500 })).toBeCloseTo(0.2)\n expect(\n await evalWith('([실제공사비]-[계획공사비]) / [계획공사비]', { 실제공사비: 1200, 계획공사비: 1000 })\n ).toBeCloseTo(0.2)\n expect(\n await evalWith('[노동손실일수] / ( [총 근로자수] * [1인당 연간 근로 시간] ) * 1000', {\n 노동손실일수: 10,\n '총 근로자수': 100,\n '1인당 연간 근로 시간': 2000\n })\n ).toBeCloseTo(0.05)\n // 단일 변수 pass-through\n expect(await evalWith('[일정 이탈 수준]', { '일정 이탈 수준': 3 })).toBe(3)\n })\n})\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FormulaNode } from './parser';
|
|
2
|
+
import type { FormulaFunctionMap } from './functions';
|
|
3
|
+
import type { ValueProvider } from './provider';
|
|
4
|
+
export interface EvalContext {
|
|
5
|
+
functions: FormulaFunctionMap;
|
|
6
|
+
provider: ValueProvider;
|
|
7
|
+
}
|
|
8
|
+
export declare function evaluateFormula(node: FormulaNode, ctx: EvalContext): Promise<any>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.evaluateFormula = evaluateFormula;
|
|
4
|
+
// 비동기 evaluator만 유지, 함수명은 evaluateFormula로 단순화
|
|
5
|
+
async function evaluateFormula(node, ctx) {
|
|
6
|
+
switch (node.type) {
|
|
7
|
+
case 'number':
|
|
8
|
+
return node.value;
|
|
9
|
+
case 'variable':
|
|
10
|
+
return await ctx.provider.get(node.name);
|
|
11
|
+
case 'function': {
|
|
12
|
+
const fn = ctx.functions[node.name];
|
|
13
|
+
if (!fn)
|
|
14
|
+
throw new Error('Unknown function: ' + node.name);
|
|
15
|
+
const args = await Promise.all(node.args.map(arg => evaluateFormula(arg, ctx)));
|
|
16
|
+
return fn(...args);
|
|
17
|
+
}
|
|
18
|
+
case 'binary': {
|
|
19
|
+
const l = await evaluateFormula(node.left, ctx);
|
|
20
|
+
const r = await evaluateFormula(node.right, ctx);
|
|
21
|
+
switch (node.op) {
|
|
22
|
+
case '+':
|
|
23
|
+
return l + r;
|
|
24
|
+
case '-':
|
|
25
|
+
return l - r;
|
|
26
|
+
case '*':
|
|
27
|
+
return l * r;
|
|
28
|
+
case '/':
|
|
29
|
+
return l / r;
|
|
30
|
+
default:
|
|
31
|
+
throw new Error('Unknown operator: ' + node.op);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
case 'unary':
|
|
35
|
+
if (node.op === '-')
|
|
36
|
+
return -(await evaluateFormula(node.arg, ctx));
|
|
37
|
+
throw new Error('Unknown unary operator: ' + node.op);
|
|
38
|
+
default:
|
|
39
|
+
throw new Error('Unknown node type: ' + node.type);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../server/calculator/evaluator.ts"],"names":[],"mappings":";;AAUA,0CAkCC;AAnCD,+CAA+C;AACxC,KAAK,UAAU,eAAe,CAAC,IAAiB,EAAE,GAAgB;IACvE,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,KAAK,CAAA;QACnB,KAAK,UAAU;YACb,OAAO,MAAM,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1C,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,EAAE,GAAG,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACnC,IAAI,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,CAAA;YAC1D,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAA;YAC/E,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAA;QACpB,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,CAAC,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;YAC/C,MAAM,CAAC,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;YAChD,QAAQ,IAAI,CAAC,EAAE,EAAE,CAAC;gBAChB,KAAK,GAAG;oBACN,OAAO,CAAC,GAAG,CAAC,CAAA;gBACd,KAAK,GAAG;oBACN,OAAO,CAAC,GAAG,CAAC,CAAA;gBACd,KAAK,GAAG;oBACN,OAAO,CAAC,GAAG,CAAC,CAAA;gBACd,KAAK,GAAG;oBACN,OAAO,CAAC,GAAG,CAAC,CAAA;gBACd;oBACE,MAAM,IAAI,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;YACnD,CAAC;QACH,CAAC;QACD,KAAK,OAAO;YACV,IAAI,IAAI,CAAC,EAAE,KAAK,GAAG;gBAAE,OAAO,CAAC,CAAC,MAAM,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;YACnE,MAAM,IAAI,KAAK,CAAC,0BAA0B,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;QACvD;YACE,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAI,IAAY,CAAC,IAAI,CAAC,CAAA;IAC/D,CAAC;AACH,CAAC","sourcesContent":["import type { FormulaNode } from './parser'\nimport type { FormulaFunctionMap } from './functions'\nimport type { ValueProvider } from './provider'\n\nexport interface EvalContext {\n functions: FormulaFunctionMap\n provider: ValueProvider\n}\n\n// 비동기 evaluator만 유지, 함수명은 evaluateFormula로 단순화\nexport async function evaluateFormula(node: FormulaNode, ctx: EvalContext): Promise<any> {\n switch (node.type) {\n case 'number':\n return node.value\n case 'variable':\n return await ctx.provider.get(node.name)\n case 'function': {\n const fn = ctx.functions[node.name]\n if (!fn) throw new Error('Unknown function: ' + node.name)\n const args = await Promise.all(node.args.map(arg => evaluateFormula(arg, ctx)))\n return fn(...args)\n }\n case 'binary': {\n const l = await evaluateFormula(node.left, ctx)\n const r = await evaluateFormula(node.right, ctx)\n switch (node.op) {\n case '+':\n return l + r\n case '-':\n return l - r\n case '*':\n return l * r\n case '/':\n return l / r\n default:\n throw new Error('Unknown operator: ' + node.op)\n }\n }\n case 'unary':\n if (node.op === '-') return -(await evaluateFormula(node.arg, ctx))\n throw new Error('Unknown unary operator: ' + node.op)\n default:\n throw new Error('Unknown node type: ' + (node as any).type)\n }\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.builtinFunctions = void 0;
|
|
4
|
+
exports.builtinFunctions = {
|
|
5
|
+
sum: (...args) => args.reduce((a, b) => a + b, 0),
|
|
6
|
+
avg: (...args) => (args.length ? args.reduce((a, b) => a + b, 0) / args.length : 0),
|
|
7
|
+
min: (...args) => Math.min(...args),
|
|
8
|
+
max: (...args) => Math.max(...args),
|
|
9
|
+
round: (v, d = 0) => Number(v.toFixed(d)),
|
|
10
|
+
if: (cond, t, f) => (cond ? t : f),
|
|
11
|
+
// 성과 지수 계산을 위한 수치 적분 함수들
|
|
12
|
+
integrate: (func, a, b, n = 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
|
+
beta_integrand: (t, alpha, beta) => {
|
|
22
|
+
// 베타 분포 피적분함수: t^(α-1) × (1-t)^(β-1)
|
|
23
|
+
return Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1);
|
|
24
|
+
},
|
|
25
|
+
incomplete_beta: (x, alpha, beta) => {
|
|
26
|
+
// 불완전 베타 함수 계산
|
|
27
|
+
const func = (t) => Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1);
|
|
28
|
+
const h = x / 1000;
|
|
29
|
+
let sum = (func(0) + func(x)) / 2;
|
|
30
|
+
for (let i = 1; i < 1000; i++) {
|
|
31
|
+
sum += func(i * h);
|
|
32
|
+
}
|
|
33
|
+
return sum * h;
|
|
34
|
+
},
|
|
35
|
+
complete_beta: (alpha, beta) => {
|
|
36
|
+
// 완전 베타 함수 계산
|
|
37
|
+
const func = (t) => Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1);
|
|
38
|
+
const h = 1 / 1000;
|
|
39
|
+
let sum = (func(0) + func(1)) / 2;
|
|
40
|
+
for (let i = 1; i < 1000; i++) {
|
|
41
|
+
sum += func(i * h);
|
|
42
|
+
}
|
|
43
|
+
return sum * h;
|
|
44
|
+
},
|
|
45
|
+
performance_index: (x, alpha1, beta1, alpha2, beta2) => {
|
|
46
|
+
// 성과 지수 계산: 1 - (불완전 베타 / 완전 베타)
|
|
47
|
+
const numerator = exports.builtinFunctions.incomplete_beta(x, alpha1, beta1);
|
|
48
|
+
const denominator = exports.builtinFunctions.complete_beta(alpha2, beta2);
|
|
49
|
+
return 1 - numerator / denominator;
|
|
50
|
+
},
|
|
51
|
+
// 지수 함수들
|
|
52
|
+
exp: (x) => Math.exp(x),
|
|
53
|
+
log: (x) => Math.log(x),
|
|
54
|
+
pow: (x, y) => Math.pow(x, y),
|
|
55
|
+
// 지수 감쇠 성과 지수
|
|
56
|
+
exponential_decay: (value, scale, power) => {
|
|
57
|
+
// exp(-(value / scale)^power)
|
|
58
|
+
return Math.exp(-Math.pow(value / scale, power));
|
|
59
|
+
}
|
|
60
|
+
// 필요시 확장
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=functions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functions.js","sourceRoot":"","sources":["../../server/calculator/functions.ts"],"names":[],"mappings":"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","sourcesContent":["export type FormulaFunction = (...args: any[]) => any\nexport type FormulaFunctionMap = Record<string, FormulaFunction>\n\nexport const builtinFunctions: FormulaFunctionMap = {\n sum: (...args: any[]) => args.reduce((a, b) => a + b, 0),\n avg: (...args: any[]) => (args.length ? args.reduce((a, b) => a + b, 0) / args.length : 0),\n min: (...args: any[]) => Math.min(...args),\n max: (...args: any[]) => Math.max(...args),\n round: (v: number, d: number = 0) => Number(v.toFixed(d)),\n if: (cond: any, t: any, f: any) => (cond ? t : f),\n // 성과 지수 계산을 위한 수치 적분 함수들\n integrate: (func: Function, a: number, b: number, n: number = 1000) => {\n // 사다리꼴 적분법 (Trapezoidal Rule)\n const h = (b - a) / n\n let sum = (func(a) + func(b)) / 2\n for (let i = 1; i < n; i++) {\n sum += func(a + i * h)\n }\n return sum * h\n },\n\n beta_integrand: (t: number, alpha: number, beta: number) => {\n // 베타 분포 피적분함수: t^(α-1) × (1-t)^(β-1)\n return Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1)\n },\n\n incomplete_beta: (x: number, alpha: number, beta: number) => {\n // 불완전 베타 함수 계산\n const func = (t: number) => Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1)\n const h = x / 1000\n let sum = (func(0) + func(x)) / 2\n for (let i = 1; i < 1000; i++) {\n sum += func(i * h)\n }\n return sum * h\n },\n\n complete_beta: (alpha: number, beta: number) => {\n // 완전 베타 함수 계산\n const func = (t: number) => Math.pow(t, alpha - 1) * Math.pow(1 - t, beta - 1)\n const h = 1 / 1000\n let sum = (func(0) + func(1)) / 2\n for (let i = 1; i < 1000; i++) {\n sum += func(i * h)\n }\n return sum * h\n },\n\n performance_index: (x: number, alpha1: number, beta1: number, alpha2: number, beta2: number) => {\n // 성과 지수 계산: 1 - (불완전 베타 / 완전 베타)\n const numerator = builtinFunctions.incomplete_beta(x, alpha1, beta1)\n const denominator = builtinFunctions.complete_beta(alpha2, beta2)\n return 1 - numerator / denominator\n },\n\n // 지수 함수들\n exp: (x: number) => Math.exp(x),\n log: (x: number) => Math.log(x),\n pow: (x: number, y: number) => Math.pow(x, y),\n\n // 지수 감쇠 성과 지수\n exponential_decay: (value: number, scale: number, power: number) => {\n // exp(-(value / scale)^power)\n return Math.exp(-Math.pow(value / scale, power))\n }\n // 필요시 확장\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./parser.js"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./evaluator.js"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./functions.js"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./provider.js"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./validate.js"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./score.js"), exports);
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/calculator/index.ts"],"names":[],"mappings":";;;AAAA,sDAA2B;AAC3B,yDAA8B;AAC9B,yDAA8B;AAC9B,wDAA6B;AAC7B,wDAA6B;AAC7B,qDAA0B","sourcesContent":["export * from './parser.js'\nexport * from './evaluator.js'\nexport * from './functions.js'\nexport * from './provider.js'\nexport * from './validate.js'\nexport * from './score.js'\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type FormulaNode = {
|
|
2
|
+
type: 'number';
|
|
3
|
+
value: number;
|
|
4
|
+
} | {
|
|
5
|
+
type: 'variable';
|
|
6
|
+
name: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: 'function';
|
|
9
|
+
name: string;
|
|
10
|
+
args: FormulaNode[];
|
|
11
|
+
} | {
|
|
12
|
+
type: 'binary';
|
|
13
|
+
op: string;
|
|
14
|
+
left: FormulaNode;
|
|
15
|
+
right: FormulaNode;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'unary';
|
|
18
|
+
op: string;
|
|
19
|
+
arg: FormulaNode;
|
|
20
|
+
};
|
|
21
|
+
export declare function parseFormula(formula: string): FormulaNode;
|
|
22
|
+
/**
|
|
23
|
+
* AST 안의 모든 변수명을 (중복 제거하여) 수집한다.
|
|
24
|
+
*
|
|
25
|
+
* 정규식 기반 추출(예: `[a-zA-Z_]\w*`)은 `[한글 변수명]`·공백 포함 이름을 놓치지만,
|
|
26
|
+
* 파서가 만든 AST 의 variable 노드는 대괄호가 벗겨진 실제 이름을 담으므로 정확하다.
|
|
27
|
+
* aggregate 등에서 "이 수식이 참조하는 metric 이름들"을 얻는 신뢰 가능한 경로.
|
|
28
|
+
*/
|
|
29
|
+
export declare function collectVariableNames(node: FormulaNode): string[];
|
|
30
|
+
/**
|
|
31
|
+
* AST 안의 모든 함수명을 (중복 제거하여) 수집한다.
|
|
32
|
+
* formula 검증에서 "알 수 없는 함수" 를 잡는 데 쓴다.
|
|
33
|
+
*/
|
|
34
|
+
export declare function collectFunctionNames(node: FormulaNode): string[];
|