@bimatrix-aud-platform/aud_mcp_server 1.1.16 → 1.1.17

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.
@@ -1,4 +1,5 @@
1
1
  import { readFileSync, writeFileSync } from "fs";
2
+ import { createDefaultOlapField, isNumericColumnType } from "./olap-field.js";
2
3
  /**
3
4
  * MTSD 파일을 읽고, 자동 보정 규칙을 적용한 뒤, 파일을 덮어씁니다.
4
5
  */
@@ -92,73 +93,7 @@ function fixDataSourceReferences(doc, dsNameToId, dsIdSet, fixes) {
92
93
  }
93
94
  }
94
95
  // ---- Rule 2: OlapGrid DataSource 기반 Fields 자동 생성 ----
95
- // DataSource Column.Type이 "Numeric"이면 숫자형으로 판단
96
- const NUMERIC_COLUMN_TYPES = new Set([
97
- "Numeric",
98
- // SQL 타입 호환
99
- "INT", "INTEGER", "BIGINT", "SMALLINT", "TINYINT",
100
- "NUMERIC", "DECIMAL", "FLOAT", "DOUBLE", "REAL", "NUMBER", "MONEY",
101
- ]);
102
- function isNumericColumnType(type) {
103
- return NUMERIC_COLUMN_TYPES.has(type) || NUMERIC_COLUMN_TYPES.has(type.toUpperCase());
104
- }
105
- /** OlapField 기본값 생성 (OlapField constructor + Serialize 기준) */
106
- function createDefaultOlapField(key, caption) {
107
- return {
108
- Key: key,
109
- Caption: caption,
110
- ToolTipField: "",
111
- ToolTipText: "",
112
- Category: 1, // enCategory.Dimension
113
- Area: 0, // enArea.Hidden
114
- SummaryType: 0, // enSummaryType.None
115
- TotalSummaryType: 0,
116
- SummaryVariation: 0,
117
- GroupByType: 0, // enGroupByType.Auto
118
- Format: "",
119
- Formula: "",
120
- Formula2: "",
121
- RefFormula: "",
122
- Width: 100,
123
- Unit: 1,
124
- CreateType: 0, // enOlapFieldCreateType.Default
125
- SortType: 0, // enSortType.None
126
- MeasureSortField: "",
127
- SortBaseField: "",
128
- MoveAble: true,
129
- SortAble: true,
130
- FilterAble: true,
131
- AllowFilter: true,
132
- AllowRow: true,
133
- AllowColumn: true,
134
- AllowData: true,
135
- KeyType: 0, // enKeyType.None
136
- DataType: 0, // enDataType.Numeric
137
- SaveMode: 0, // enSaveMode.All
138
- LanguageCode: "",
139
- UseChartSource: true,
140
- VisibleSubTotal: true,
141
- SummaryBaseFieldKey: "",
142
- TextAlignment: 1, // enHorizonAlign.Right
143
- HeaderAlignment: 0, // enHorizonAlign.Left
144
- InDimensions: "",
145
- Visible: true,
146
- Expanded: true,
147
- MetaItemCode: "",
148
- MetaItemName: "",
149
- MetaRollupType: 0,
150
- MetaCalculatorField: false,
151
- MetaSummaryTypeIsDistinct: false,
152
- FilterInfo: {
153
- FilterType: 0, // enOlapFilterType.In
154
- FilterKind: 0, // enOlapFilterKind.Dimension
155
- HasMeasureFilter: false,
156
- MeasureFilterTypeA: 0, // enOlapMeasureFilterType.Equals
157
- MeasureFilterTypeB: 0,
158
- MeasureAndOrOperator: 2, // enAndOrOperator.And
159
- },
160
- };
161
- }
96
+ // createDefaultOlapField, isNumericColumnType는 olap-field.ts에서 import
162
97
  function fixOlapGridFields(doc, datas, fixes) {
163
98
  // DataSource Id → DataSource 객체 매핑
164
99
  const dsById = new Map();
@@ -0,0 +1,45 @@
1
+ /**
2
+ * OlapGrid iOLAPView.Fields 생성기
3
+ *
4
+ * 컬럼 정의를 입력받아 OlapField 배열을 생성합니다.
5
+ * iOLAP.OlapGrid.ts의 createDataSourceFields (UseMeta==false) 로직을 기반으로 합니다.
6
+ *
7
+ * @see 2.Sources/src/control/olapgrid/iOLAP.OlapGrid.ts:4592-4657 — 필드 생성
8
+ * @see 2.Sources/src/control/olapgrid/iOLAP.OlapGrid.ts:1806-1837 — Area 자동 배치
9
+ * @see 2.Sources/src/control/olapgrid/iOLAP.Enums.ts — enCategory, enArea 등
10
+ */
11
+ export declare function isNumericColumnType(type: string): boolean;
12
+ export declare function createDefaultOlapField(key: string, caption: string): any;
13
+ export interface OlapFieldColumn {
14
+ name: string;
15
+ caption?: string;
16
+ type?: string;
17
+ area?: string;
18
+ summaryType?: string;
19
+ format?: string;
20
+ sortType?: string;
21
+ width?: number;
22
+ }
23
+ export interface GenerateOlapFieldsInput {
24
+ columns: OlapFieldColumn[];
25
+ autoPlace?: boolean;
26
+ includeOptions?: boolean;
27
+ }
28
+ export interface GenerateOlapFieldsResult {
29
+ fields: any[];
30
+ iOLAPView?: any;
31
+ warnings: string[];
32
+ summary: {
33
+ total: number;
34
+ dimensions: number;
35
+ measures: number;
36
+ areaMap: {
37
+ row: string[];
38
+ column: string[];
39
+ data: string[];
40
+ filter: string[];
41
+ hidden: string[];
42
+ };
43
+ };
44
+ }
45
+ export declare function generateOlapFields(input: GenerateOlapFieldsInput): GenerateOlapFieldsResult;
@@ -0,0 +1,316 @@
1
+ /**
2
+ * OlapGrid iOLAPView.Fields 생성기
3
+ *
4
+ * 컬럼 정의를 입력받아 OlapField 배열을 생성합니다.
5
+ * iOLAP.OlapGrid.ts의 createDataSourceFields (UseMeta==false) 로직을 기반으로 합니다.
6
+ *
7
+ * @see 2.Sources/src/control/olapgrid/iOLAP.OlapGrid.ts:4592-4657 — 필드 생성
8
+ * @see 2.Sources/src/control/olapgrid/iOLAP.OlapGrid.ts:1806-1837 — Area 자동 배치
9
+ * @see 2.Sources/src/control/olapgrid/iOLAP.Enums.ts — enCategory, enArea 등
10
+ */
11
+ // ─── enums (iOLAP.Enums.ts 기준) ───
12
+ const enArea = { Hidden: 0, Row: 1, Column: 2, Filter: 3, Data: 4 };
13
+ const enCategory = { Default: 0, Dimension: 1, Measure: 2, Attribute: 3, Period: 4 };
14
+ const enSummaryType = { None: 0, Sum: 1, Min: 2, Max: 3, Average: 4, Count: 5, Calculate: 9, DistinctCount: 13, Text: 14 };
15
+ const enSortType = { None: 0, Asc: 1, Desc: 2, Custom: 3 };
16
+ const enDataType = { Numeric: 0, String: 1, DateTime8: 2, DateTimeNow: 3, UserCode: 4, CLOB: 5 };
17
+ // ─── 숫자형 컬럼 타입 판별 ───
18
+ const NUMERIC_COLUMN_TYPES = new Set([
19
+ "Numeric",
20
+ "INT", "INTEGER", "BIGINT", "SMALLINT", "TINYINT",
21
+ "NUMERIC", "DECIMAL", "FLOAT", "DOUBLE", "REAL", "NUMBER", "MONEY",
22
+ ]);
23
+ export function isNumericColumnType(type) {
24
+ return NUMERIC_COLUMN_TYPES.has(type) || NUMERIC_COLUMN_TYPES.has(type.toUpperCase());
25
+ }
26
+ // ─── OlapField 기본값 생성 (OlapField constructor + Serialize 기준) ───
27
+ export function createDefaultOlapField(key, caption) {
28
+ return {
29
+ Key: key,
30
+ Caption: caption,
31
+ ToolTipField: "",
32
+ ToolTipText: "",
33
+ Category: enCategory.Dimension,
34
+ Area: enArea.Hidden,
35
+ SummaryType: enSummaryType.None,
36
+ TotalSummaryType: 0,
37
+ SummaryVariation: 0,
38
+ GroupByType: 0,
39
+ Format: "",
40
+ Formula: "",
41
+ Formula2: "",
42
+ RefFormula: "",
43
+ Width: 100,
44
+ Unit: 1,
45
+ CreateType: 0,
46
+ SortType: enSortType.None,
47
+ MeasureSortField: "",
48
+ SortBaseField: "",
49
+ MoveAble: true,
50
+ SortAble: true,
51
+ FilterAble: true,
52
+ AllowFilter: true,
53
+ AllowRow: true,
54
+ AllowColumn: true,
55
+ AllowData: true,
56
+ KeyType: 0,
57
+ DataType: enDataType.Numeric,
58
+ SaveMode: 0,
59
+ LanguageCode: "",
60
+ UseChartSource: true,
61
+ VisibleSubTotal: true,
62
+ SummaryBaseFieldKey: "",
63
+ TextAlignment: 1, // Right
64
+ HeaderAlignment: 0, // Left
65
+ InDimensions: "",
66
+ Visible: true,
67
+ Expanded: true,
68
+ MetaItemCode: "",
69
+ MetaItemName: "",
70
+ MetaRollupType: 0,
71
+ MetaCalculatorField: false,
72
+ MetaSummaryTypeIsDistinct: false,
73
+ FilterInfo: {
74
+ FilterType: 0,
75
+ FilterKind: 0,
76
+ HasMeasureFilter: false,
77
+ MeasureFilterTypeA: 0,
78
+ MeasureFilterTypeB: 0,
79
+ MeasureAndOrOperator: 2,
80
+ },
81
+ };
82
+ }
83
+ // ─── summaryType 문자열 → enum 변환 ───
84
+ function parseSummaryType(s) {
85
+ switch (s.toLowerCase()) {
86
+ case "sum": return enSummaryType.Sum;
87
+ case "count": return enSummaryType.Count;
88
+ case "average":
89
+ case "avg": return enSummaryType.Average;
90
+ case "min": return enSummaryType.Min;
91
+ case "max": return enSummaryType.Max;
92
+ case "none": return enSummaryType.None;
93
+ case "distinctcount": return enSummaryType.DistinctCount;
94
+ case "text": return enSummaryType.Text;
95
+ default: return enSummaryType.None;
96
+ }
97
+ }
98
+ // ─── sortType 문자열 → enum 변환 ───
99
+ function parseSortType(s) {
100
+ switch (s.toLowerCase()) {
101
+ case "asc": return enSortType.Asc;
102
+ case "desc": return enSortType.Desc;
103
+ case "none": return enSortType.None;
104
+ default: return enSortType.None;
105
+ }
106
+ }
107
+ // ─── area 문자열 → enum 변환 ───
108
+ function parseArea(s) {
109
+ switch (s.toLowerCase()) {
110
+ case "row": return enArea.Row;
111
+ case "column":
112
+ case "col": return enArea.Column;
113
+ case "data": return enArea.Data;
114
+ case "filter": return enArea.Filter;
115
+ case "hidden": return enArea.Hidden;
116
+ default: return enArea.Hidden;
117
+ }
118
+ }
119
+ // ─── iOLAPView.Options 기본값 ───
120
+ function createDefaultOptions() {
121
+ return {
122
+ ViewType: 0,
123
+ IsExpandAll: true,
124
+ ShowExpandButtons: true,
125
+ EmptyCellText: "",
126
+ NotAvaliableCellText: "",
127
+ ZeroDivisioinCellText: "",
128
+ ErrorCellText: "",
129
+ RowGrandTotalText: "총계",
130
+ ColumnGrandTotalText: "총계",
131
+ RowTotalText: "소계",
132
+ ColumnTotalText: "소계",
133
+ RowTotalLocation: 1,
134
+ ColumnTotalLocation: 1,
135
+ RowGrandTotalLocation: 1,
136
+ ColumnGrandTotalLocation: 1,
137
+ DisplayColumnSubTotal: true,
138
+ DisplayRowSubTotal: true,
139
+ DisplayColumnGrandTotal: true,
140
+ DisplayRowGrandTotal: true,
141
+ ShowFilterArea: true,
142
+ ShowColumnrArea: true,
143
+ ShowRowArea: true,
144
+ ShowDataArea: true,
145
+ UseMultiHeader: false,
146
+ AutoSelection: false,
147
+ HideHorizontalScrollBar: false,
148
+ HideVerticalScrollBar: false,
149
+ DisableClipBoard: false,
150
+ CellHeight: 24,
151
+ HeaderCellHeight: 24,
152
+ CanResizeCellWidth: true,
153
+ RowHeaderUnFix: false,
154
+ MeasuresCreateArea: 1,
155
+ TreeHeaderWidth: 200,
156
+ TreeIndentWidth: 16,
157
+ DisableColumnSort: false,
158
+ EnableWriteBack: false,
159
+ MergeColumnHeaders: false,
160
+ MergeRowHeaders: false,
161
+ CacheOption: {
162
+ UseHybrid: true,
163
+ },
164
+ };
165
+ }
166
+ export function generateOlapFields(input) {
167
+ const warnings = [];
168
+ const columns = input.columns || [];
169
+ const autoPlace = input.autoPlace !== false; // 기본 true
170
+ const includeOptions = input.includeOptions === true;
171
+ if (columns.length === 0) {
172
+ warnings.push("columns 배열이 비어 있습니다.");
173
+ return {
174
+ fields: [],
175
+ warnings,
176
+ summary: { total: 0, dimensions: 0, measures: 0, areaMap: { row: [], column: [], data: [], filter: [], hidden: [] } },
177
+ };
178
+ }
179
+ // 1. 필드 생성
180
+ const fields = [];
181
+ const nameSet = new Set();
182
+ for (const col of columns) {
183
+ if (!col.name) {
184
+ warnings.push("name이 비어있는 컬럼이 있어 건너뜁니다.");
185
+ continue;
186
+ }
187
+ if (nameSet.has(col.name)) {
188
+ warnings.push(`중복 컬럼명 "${col.name}" 발견, 건너뜁니다.`);
189
+ continue;
190
+ }
191
+ nameSet.add(col.name);
192
+ const caption = col.caption || col.name;
193
+ const field = createDefaultOlapField(col.name, caption);
194
+ const isNumeric = isNumericColumnType(col.type || "string");
195
+ // 타입 기반 기본값 설정 (createDataSourceFields 로직)
196
+ if (isNumeric) {
197
+ field.Category = enCategory.Measure;
198
+ field.Format = "{0:N0}";
199
+ field.DataType = enDataType.Numeric;
200
+ field.SummaryType = enSummaryType.Sum;
201
+ field.TextAlignment = 1; // Right
202
+ }
203
+ else {
204
+ field.Category = enCategory.Dimension;
205
+ field.DataType = enDataType.String;
206
+ field.SortType = enSortType.Asc;
207
+ field.SummaryType = enSummaryType.Count;
208
+ field.TextAlignment = 1; // Right
209
+ }
210
+ // 커스텀 오버라이드
211
+ if (col.summaryType) {
212
+ field.SummaryType = parseSummaryType(col.summaryType);
213
+ }
214
+ if (col.format) {
215
+ field.Format = col.format;
216
+ }
217
+ if (col.sortType) {
218
+ field.SortType = parseSortType(col.sortType);
219
+ }
220
+ if (col.width != null) {
221
+ field.Width = col.width;
222
+ }
223
+ // 수동 Area 지정
224
+ if (col.area) {
225
+ field.Area = parseArea(col.area);
226
+ }
227
+ fields.push(field);
228
+ }
229
+ // 2. Area 자동 배치 (setDataSource 로직 준용)
230
+ if (autoPlace) {
231
+ let rowCnt = 0;
232
+ let colCnt = 0;
233
+ let dataCnt = 0;
234
+ for (const field of fields) {
235
+ // 수동 area가 이미 지정된 경우 스킵 (Hidden=0이 아닌 경우)
236
+ if (field.Area !== enArea.Hidden)
237
+ continue;
238
+ if (field.DataType === enDataType.Numeric) {
239
+ // Numeric → Data(최대 3개) → Filter
240
+ if (dataCnt < 3) {
241
+ field.Area = enArea.Data;
242
+ dataCnt++;
243
+ }
244
+ else {
245
+ field.Area = enArea.Filter;
246
+ }
247
+ }
248
+ else {
249
+ // String → Row(최대 2개) → Column(최대 2개) → Filter
250
+ if (rowCnt < 2) {
251
+ field.Area = enArea.Row;
252
+ rowCnt++;
253
+ }
254
+ else if (colCnt < 2) {
255
+ field.Area = enArea.Column;
256
+ colCnt++;
257
+ }
258
+ else {
259
+ field.Area = enArea.Filter;
260
+ }
261
+ }
262
+ }
263
+ }
264
+ // 3. 요약 정보 생성
265
+ const areaMap = { row: [], column: [], data: [], filter: [], hidden: [] };
266
+ let dimensions = 0;
267
+ let measures = 0;
268
+ for (const field of fields) {
269
+ if (field.Category === enCategory.Measure)
270
+ measures++;
271
+ else
272
+ dimensions++;
273
+ switch (field.Area) {
274
+ case enArea.Row:
275
+ areaMap.row.push(field.Key);
276
+ break;
277
+ case enArea.Column:
278
+ areaMap.column.push(field.Key);
279
+ break;
280
+ case enArea.Data:
281
+ areaMap.data.push(field.Key);
282
+ break;
283
+ case enArea.Filter:
284
+ areaMap.filter.push(field.Key);
285
+ break;
286
+ default:
287
+ areaMap.hidden.push(field.Key);
288
+ break;
289
+ }
290
+ }
291
+ // 4. 결과 조립
292
+ const result = {
293
+ fields,
294
+ warnings,
295
+ summary: {
296
+ total: fields.length,
297
+ dimensions,
298
+ measures,
299
+ areaMap,
300
+ },
301
+ };
302
+ // includeOptions: iOLAPView 전체 구조 포함
303
+ if (includeOptions) {
304
+ result.iOLAPView = {
305
+ Options: createDefaultOptions(),
306
+ TopFilter: {
307
+ Dimension: "",
308
+ Measure: "",
309
+ Rank: 0,
310
+ IsTop: true,
311
+ },
312
+ Fields: fields,
313
+ };
314
+ }
315
+ return result;
316
+ }
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import { generateGridColumns } from "./generators/grid-column.js";
12
12
  import { generateDataSource } from "./generators/datasource.js";
13
13
  import { fixMtsd } from "./generators/fixer.js";
14
14
  import { getControlInfo } from "./generators/control-info.js";
15
+ import { generateOlapFields } from "./generators/olap-field.js";
15
16
  import { callSchemaService, callDbmsList, isAudConfigured, setWorkspaceRoots } from "./utils/aud-api-client.js";
16
17
  const __filename = fileURLToPath(import.meta.url);
17
18
  const __dirname = dirname(__filename);
@@ -494,6 +495,54 @@ const tools = [
494
495
  additionalProperties: true,
495
496
  },
496
497
  },
498
+ {
499
+ name: "generate_olap_fields",
500
+ description: "OlapGrid의 iOLAPView.Fields 배열을 생성합니다. 컬럼 정의를 입력하면 Dimension/Measure 분류, Area 자동 배치, SummaryType 설정이 완료된 OlapField 배열을 반환합니다.",
501
+ inputSchema: {
502
+ type: "object",
503
+ properties: {
504
+ columns: {
505
+ type: "array",
506
+ items: {
507
+ type: "object",
508
+ properties: {
509
+ name: { type: "string", description: "컬럼명 (OlapField Key)" },
510
+ caption: { type: "string", description: "표시명 (생략 시 name)" },
511
+ type: {
512
+ type: "string",
513
+ enum: ["string", "number", "numeric", "date", "datetime", "int", "float", "decimal", "varchar", "nvarchar", "char"],
514
+ description: "컬럼 데이터 타입 (number/numeric/int/float/decimal → Measure, 그 외 → Dimension)",
515
+ },
516
+ area: {
517
+ type: "string",
518
+ enum: ["row", "column", "data", "filter", "hidden"],
519
+ description: "수동 Area 배치 (생략 시 자동 배치)",
520
+ },
521
+ summaryType: {
522
+ type: "string",
523
+ enum: ["sum", "count", "average", "min", "max", "none", "distinctcount", "text"],
524
+ description: "집계 함수 (Measure 기본: sum, Dimension 기본: count)",
525
+ },
526
+ format: { type: "string", description: "표시 포맷 (예: {0:N0})" },
527
+ sortType: { type: "string", enum: ["asc", "desc", "none"], description: "정렬" },
528
+ width: { type: "number", description: "너비 (기본 100)" },
529
+ },
530
+ required: ["name"],
531
+ },
532
+ description: "컬럼 정의 배열",
533
+ },
534
+ autoPlace: {
535
+ type: "boolean",
536
+ description: "Area 자동 배치 (기본: true). false면 수동 area 지정 필드만 배치, 나머지 Hidden",
537
+ },
538
+ includeOptions: {
539
+ type: "boolean",
540
+ description: "iOLAPView 전체 구조(Options + Fields) 포함 여부 (기본: false)",
541
+ },
542
+ },
543
+ required: ["columns"],
544
+ },
545
+ },
497
546
  {
498
547
  name: "fix_mtsd",
499
548
  description: "MTSD 파일을 읽어 자동 보정 규칙을 적용하고 파일을 덮어씁니다. DataSource Name→Id 참조 보정, Params Value 누락 보정, Columns Type 누락 보정, Docking/Border 필수 속성 보정을 수행합니다.",
@@ -942,6 +991,25 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
942
991
  ],
943
992
  };
944
993
  }
994
+ case "generate_olap_fields": {
995
+ const result = generateOlapFields(args);
996
+ return {
997
+ content: [
998
+ {
999
+ type: "text",
1000
+ text: JSON.stringify({
1001
+ success: true,
1002
+ fields: result.fields,
1003
+ iOLAPView: result.iOLAPView,
1004
+ summary: result.summary,
1005
+ warnings: result.warnings.length > 0
1006
+ ? result.warnings
1007
+ : undefined,
1008
+ }, null, 2),
1009
+ },
1010
+ ],
1011
+ };
1012
+ }
945
1013
  case "fix_mtsd": {
946
1014
  const filePath = args?.path;
947
1015
  if (!filePath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bimatrix-aud-platform/aud_mcp_server",
3
- "version": "1.1.16",
3
+ "version": "1.1.17",
4
4
  "description": "MCP Server for i-AUD MTSD document validation and generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",