pyjallib 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,844 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ namingConfig 모듈 - Naming 클래스의 설정을 관리하는 기능 제공
6
+ NamePart 객체를 기반으로 네이밍 설정을 저장하고 불러오는 기능 구현
7
+ """
8
+
9
+ import json
10
+ import os
11
+ import copy
12
+ from typing import List, Dict, Any, Optional, Union
13
+ import csv # Import the csv module
14
+
15
+
16
+ # NamePart 클래스 임포트
17
+ from pyjallib.namePart import NamePart, NamePartType
18
+
19
+
20
+ class NamingConfig:
21
+ """
22
+ Naming 클래스의 설정을 관리하는 클래스.
23
+ NamePart 객체 리스트를 관리하고 JSON 파일로 저장/불러오기 기능 제공.
24
+ """
25
+
26
+ def __init__(self, padding_num: int = 2, name_parts: Optional[List[NamePart]] = None,
27
+ config_file_path: str = "", default_file_name: str = "namingConfig.json",
28
+ required_parts: Optional[List[str]] = None):
29
+ """
30
+ 클래스 초기화 및 기본 설정값 정의
31
+
32
+ Args:
33
+ padding_num: 인덱스 패딩 자릿수 (기본값: 2)
34
+ name_parts: 초기 NamePart 객체 리스트 (기본값: None, 기본 파트로 초기화)
35
+ config_file_path: 설정 파일 경로 (기본값: 빈 문자열)
36
+ default_file_name: 기본 파일명 (기본값: "namingConfig.json")
37
+ required_parts: 필수 namePart 목록 (기본값: ["RealName"])
38
+ """
39
+ # NamePart 객체 리스트
40
+ self.name_parts = name_parts or []
41
+
42
+ # 추가 설정
43
+ self.padding_num = padding_num
44
+
45
+ # NamePart 순서 정보 저장
46
+ self.part_order = []
47
+
48
+ # 필수 namePart 정의 (삭제 불가능)
49
+ self.required_parts = required_parts or ["RealName"]
50
+
51
+ # 설정 파일 경로 및 기본 파일명
52
+ self.config_file_path = config_file_path
53
+ self.default_file_name = default_file_name
54
+
55
+ # 스크립트 디렉토리 기준 기본 경로 설정
56
+ script_dir = os.path.dirname(os.path.abspath(__file__))
57
+ config_dir = os.path.join(script_dir, "ConfigFiles")
58
+ self.default_file_path = os.path.join(config_dir, self.default_file_name)
59
+
60
+ # name_parts가 제공되지 않은 경우에만 기본 NamePart 초기화
61
+ if not self.name_parts:
62
+ self._initialize_default_parts()
63
+ else:
64
+ # 제공된 name_parts가 있는 경우 순서 업데이트 및 타입 자동 업데이트
65
+ self._update_part_order()
66
+ self._update_part_types_based_on_order()
67
+
68
+ def _initialize_default_parts(self):
69
+ """기본 NamePart 객체들 초기화"""
70
+ # 기본 순서 정의 (명시적으로 순서를 저장)
71
+ self.part_order = []
72
+
73
+ # Prefix 부분 (PREFIX 타입)
74
+ prefixPart = NamePart("Prefix", NamePartType.PREFIX, ["Pr"], ["Prefix"], False, ["접두사"]) # Add korean descriptions
75
+
76
+ # RealName 부분 (REALNAME 타입)
77
+ realNamePart = NamePart("RealName", NamePartType.REALNAME, [], [], False, []) # Add korean descriptions
78
+
79
+ # Index 부분 (INDEX 타입)
80
+ indexPart = NamePart("Index", NamePartType.INDEX, [], [], False, []) # Add korean descriptions
81
+
82
+ # Suffix 부분 (SUFFIX 타입)
83
+ suffixPart = NamePart("Suffix", NamePartType.SUFFIX, ["Su"], ["Suffix"], False, ["접미사"]) # Add korean descriptions
84
+
85
+ # 기본 순서대로 설정
86
+ self.name_parts = [prefixPart, realNamePart, indexPart, suffixPart]
87
+
88
+ self._update_part_order() # 초기화 후 순서 업데이트
89
+
90
+ # 타입 자동 업데이트
91
+ self._update_part_types_based_on_order()
92
+
93
+ def _update_part_order(self):
94
+ """
95
+ NamePart 순서 업데이트 - 기본적으로 NamePart 객체의 순서에 따라 업데이트
96
+ """
97
+ self.part_order = [part.get_name() for part in self.name_parts]
98
+
99
+ def _get_real_name_index(self) -> int:
100
+ """
101
+ RealName 파트의 인덱스를 반환합니다.
102
+
103
+ Returns:
104
+ RealName 파트의 인덱스, 없으면 -1
105
+ """
106
+ for i, part in enumerate(self.name_parts):
107
+ if part.get_type() == NamePartType.REALNAME:
108
+ return i
109
+ return -1
110
+
111
+ def _update_part_types_based_on_order(self) -> bool:
112
+ """
113
+ NamePart 순서에 따라 파트의 타입을 자동으로 업데이트합니다.
114
+ RealName을 기준으로 앞에 있는 파트는 PREFIX, 뒤에 있는 파트는 SUFFIX로 설정합니다.
115
+ (RealName과 Index 파트는 예외)
116
+
117
+ Returns:
118
+ 업데이트 성공 여부 (True/False)
119
+ """
120
+ # RealName 파트 인덱스 찾기
121
+ real_name_index = self._get_real_name_index()
122
+ if real_name_index == -1:
123
+ print("경고: RealName 파트를 찾을 수 없어 타입 자동 업데이트를 수행할 수 없습니다.")
124
+ return False
125
+
126
+ # 각 파트의 타입을 순서에 따라 업데이트
127
+ for i, part in enumerate(self.name_parts):
128
+ partName = part.get_name()
129
+
130
+ # RealName은 항상 REALNAME 타입
131
+ if partName == "RealName":
132
+ part.set_type(NamePartType.REALNAME)
133
+ continue
134
+
135
+ # Index는 항상 INDEX 타입
136
+ if partName == "Index":
137
+ part.set_type(NamePartType.INDEX)
138
+ continue
139
+
140
+ # RealName 앞의 파트는 PREFIX, 뒤의 파트는 SUFFIX
141
+ if i < real_name_index:
142
+ part.set_type(NamePartType.PREFIX)
143
+ else:
144
+ part.set_type(NamePartType.SUFFIX)
145
+
146
+ return True
147
+
148
+ def get_part_names(self) -> List[str]:
149
+ """
150
+ 모든 NamePart 이름 목록 반환
151
+
152
+ Returns:
153
+ NamePart 이름 목록
154
+ """
155
+ return [part.get_name() for part in self.name_parts]
156
+
157
+ def get_part_order(self) -> List[str]:
158
+ """
159
+ NamePart 순서 목록 반환
160
+
161
+ Returns:
162
+ NamePart 이름 순서 목록
163
+ """
164
+ return self.part_order.copy()
165
+
166
+ def get_part(self, name: str) -> Optional[NamePart]:
167
+ """
168
+ 이름으로 NamePart 객체 가져오기
169
+
170
+ Args:
171
+ name: NamePart 이름
172
+
173
+ Returns:
174
+ NamePart 객체, 없으면 None
175
+ """
176
+ for part in self.name_parts:
177
+ if part.get_name() == name:
178
+ return part
179
+ return None
180
+
181
+ def add_part(self, name: str, part_type: NamePartType = NamePartType.UNDEFINED,
182
+ values: Optional[List[str]] = None, descriptions: Optional[List[str]] = None,
183
+ korean_descriptions: Optional[List[str]] = None) -> bool: # Add korean_descriptions parameter
184
+ """
185
+ 새 NamePart 객체 추가
186
+
187
+ Args:
188
+ name: 추가할 NamePart 이름
189
+ part_type: NamePart 타입 (기본값: UNDEFINED)
190
+ values: 사전 정의된 값 목록 (기본값: None)
191
+ descriptions: 값에 대한 설명 목록 (기본값: None, 값과 동일하게 설정됨)
192
+ korean_descriptions: 값에 대한 한국어 설명 목록 (기본값: None, 값과 동일하게 설정됨) # Add korean_descriptions doc
193
+
194
+ Returns:
195
+ 추가 성공 여부 (True/False)
196
+ """
197
+ if not name:
198
+ print("오류: 유효한 NamePart 이름을 입력하세요.")
199
+ return False
200
+
201
+ # 이미 존재하는지 확인
202
+ if self.get_part(name) is not None:
203
+ print(f"오류: '{name}' NamePart가 이미 존재합니다.")
204
+ return False
205
+
206
+ # 새 NamePart 객체 생성 - NamePart 클래스의 생성자 활용
207
+ new_part = NamePart(name, part_type, values or [], descriptions, False, korean_descriptions) # Pass korean_descriptions
208
+
209
+ # 리스트에 추가
210
+ self.name_parts.append(new_part)
211
+
212
+ # 순서 목록에 추가
213
+ if name not in self.part_order:
214
+ self.part_order.append(name)
215
+
216
+ # 순서에 따라 타입 업데이트
217
+ self._update_part_types_based_on_order()
218
+ return True
219
+
220
+ def remove_part(self, name: str) -> bool:
221
+ """
222
+ NamePart 객체 제거 (필수 부분은 제거 불가)
223
+
224
+ Args:
225
+ name: 제거할 NamePart 이름
226
+
227
+ Returns:
228
+ 제거 성공 여부 (True/False)
229
+ """
230
+ # 필수 부분은 제거 불가능
231
+ if name in self.required_parts:
232
+ print(f"오류: 필수 NamePart '{name}'는 제거할 수 없습니다.")
233
+ return False
234
+
235
+ # 찾아서 제거
236
+ for i, part in enumerate(self.name_parts):
237
+ if part.get_name() == name:
238
+ del self.name_parts[i]
239
+
240
+ # 순서 목록에서도 제거
241
+ if name in self.part_order:
242
+ self.part_order.remove(name)
243
+
244
+ # 순서에 따라 타입 업데이트
245
+ self._update_part_types_based_on_order()
246
+ return True
247
+
248
+ print(f"오류: '{name}' NamePart가 존재하지 않습니다.")
249
+ return False
250
+
251
+ def reorder_parts(self, new_order: List[str]) -> bool:
252
+ """
253
+ NamePart 순서 변경
254
+
255
+ Args:
256
+ new_order: 새로운 NamePart 이름 순서 배열
257
+
258
+ Returns:
259
+ 변경 성공 여부 (True/False)
260
+ """
261
+ # 배열 길이 확인
262
+ if len(new_order) != len(self.name_parts):
263
+ print("오류: 새 순서의 항목 수가 기존 NamePart와 일치하지 않습니다.")
264
+ return False
265
+
266
+ # 모든 필수 부분이 포함되어 있는지 확인
267
+ for part in self.required_parts:
268
+ if part not in new_order:
269
+ print(f"오류: 필수 NamePart '{part}'가 새 순서에 포함되어 있지 않습니다.")
270
+ return False
271
+
272
+ # 모든 이름이 현재 존재하는지 확인
273
+ current_names = self.get_part_names()
274
+ for name in new_order:
275
+ if name not in current_names:
276
+ print(f"오류: '{name}' NamePart가 존재하지 않습니다.")
277
+ return False
278
+
279
+ # 순서 변경을 위한 새 리스트 생성
280
+ reordered_parts = []
281
+ for name in new_order:
282
+ part = self.get_part(name)
283
+ if part:
284
+ reordered_parts.append(part)
285
+
286
+ # 새 순서로 업데이트
287
+ self.name_parts = reordered_parts
288
+ self.part_order = new_order.copy()
289
+
290
+ # 순서에 따라 타입 업데이트
291
+ self._update_part_types_based_on_order()
292
+ return True
293
+
294
+ def set_padding_num(self, padding_num: int) -> bool:
295
+ """
296
+ 인덱스 자릿수 설정
297
+
298
+ Args:
299
+ padding_num: 설정할 패딩 자릿수
300
+
301
+ Returns:
302
+ 설정 성공 여부 (True/False)
303
+ """
304
+ if not isinstance(padding_num, int) or padding_num < 1:
305
+ print("오류: 패딩 자릿수는 1 이상의 정수여야 합니다.")
306
+ return False
307
+
308
+ self.padding_num = padding_num
309
+ return True
310
+
311
+ def set_part_type(self, part_name: str, part_type: NamePartType) -> bool:
312
+ """
313
+ 특정 NamePart의 타입 설정
314
+
315
+ Args:
316
+ part_name: NamePart 이름
317
+ part_type: 설정할 타입 (NamePartType 열거형 값)
318
+
319
+ Returns:
320
+ 설정 성공 여부 (True/False)
321
+ """
322
+ part = self.get_part(part_name)
323
+ if not part:
324
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
325
+ return False
326
+
327
+ # 필수 RealName 부분은 항상 REALNAME 타입이어야 함
328
+ if part_name == "RealName" and part_type != NamePartType.REALNAME:
329
+ print("오류: RealName 부분은 반드시 REALNAME 타입이어야 합니다.")
330
+ return False
331
+
332
+ # Index 부분은 항상 INDEX 타입이어야 함
333
+ if part_name == "Index" and part_type != NamePartType.INDEX:
334
+ print("오류: Index 부분은 반드시 INDEX 타입이어야 합니다.")
335
+ return False
336
+
337
+ part.set_type(part_type)
338
+ return True
339
+
340
+ def get_part_type(self, part_name: str) -> Optional[NamePartType]:
341
+ """
342
+ 특정 NamePart의 타입 가져오기
343
+
344
+ Args:
345
+ part_name: NamePart 이름
346
+
347
+ Returns:
348
+ NamePart 타입, 없으면 None
349
+ """
350
+ part = self.get_part(part_name)
351
+ if not part:
352
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
353
+ return None
354
+
355
+ return part.get_type()
356
+
357
+ def set_part_values(self, part_name: str, values: List[str],
358
+ descriptions: Optional[List[str]] = None,
359
+ korean_descriptions: Optional[List[str]] = None) -> bool: # Add korean_descriptions parameter
360
+ """
361
+ 특정 NamePart의 사전 정의 값 설정
362
+
363
+ Args:
364
+ part_name: NamePart 이름
365
+ values: 설정할 사전 정의 값 리스트
366
+ descriptions: 설정할 설명 목록 (기본값: None, 값과 같은 설명 사용)
367
+ korean_descriptions: 설정할 한국어 설명 목록 (기본값: None, 값과 같은 설명 사용) # Add korean_descriptions doc
368
+
369
+ Returns:
370
+ 설정 성공 여부 (True/False)
371
+ """
372
+ part = self.get_part(part_name)
373
+ if not part:
374
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
375
+ return False
376
+
377
+ # REALNAME이나 INDEX 타입은 사전 정의 값 설정 불가
378
+ if part.is_realname() or part.is_index():
379
+ print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 설정할 수 없습니다.")
380
+ return False
381
+
382
+ if not values:
383
+ print(f"오류: {part_name} 부분의 사전 정의 값은 적어도 하나 이상 있어야 합니다.")
384
+ return False
385
+
386
+ # 값 설정
387
+ part.set_predefined_values(values, descriptions, korean_descriptions) # Pass korean_descriptions
388
+
389
+ return True
390
+
391
+ def set_part_value_by_csv(self, part_name: str, csv_file_path: str) -> bool:
392
+ """
393
+ 특정 NamePart의 사전 정의 값을 CSV 파일로 설정
394
+ CSV 파일 형식: value,description,koreanDescription (각 줄당)
395
+
396
+ Args:
397
+ part_name: NamePart 이름
398
+ csv_file_path: CSV 파일 경로
399
+
400
+ Returns:
401
+ 설정 성공 여부 (True/False)
402
+ """
403
+ part = self.get_part(part_name)
404
+ if not part:
405
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
406
+ return False
407
+
408
+ # REALNAME이나 INDEX 타입은 사전 정의 값 설정 불가
409
+ if part.is_realname() or part.is_index():
410
+ print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 설정할 수 없습니다.")
411
+ return False
412
+
413
+ # CSV 파일에서 값, 설명, 한국어 설명 읽기
414
+ values = []
415
+ descriptions = []
416
+ korean_descriptions = []
417
+ try:
418
+ with open(csv_file_path, 'r', encoding='utf-8', newline='') as f:
419
+ reader = csv.reader(f)
420
+ for row in reader:
421
+ if len(row) >= 3: # Ensure row has at least 3 columns
422
+ value = row[0].strip()
423
+ description = row[1].strip()
424
+ korean_description = row[2].strip()
425
+ if value: # Skip empty values
426
+ values.append(value)
427
+ descriptions.append(description if description else value) # Use value if description is empty
428
+ korean_descriptions.append(korean_description if korean_description else value) # Use value if korean_description is empty
429
+ elif len(row) == 2: # Handle case with value and description only
430
+ value = row[0].strip()
431
+ description = row[1].strip()
432
+ if value:
433
+ values.append(value)
434
+ descriptions.append(description if description else value)
435
+ korean_descriptions.append(value) # Use value as korean description
436
+ elif len(row) == 1: # Handle case with value only
437
+ value = row[0].strip()
438
+ if value:
439
+ values.append(value)
440
+ descriptions.append(value)
441
+ korean_descriptions.append(value)
442
+
443
+ if not values:
444
+ print(f"오류: CSV 파일 '{csv_file_path}'에서 유효한 값을 찾을 수 없습니다.")
445
+ return False
446
+
447
+ # 값, 설명, 한국어 설명 설정
448
+ return self.set_part_values(part_name, values, descriptions, korean_descriptions)
449
+ except FileNotFoundError:
450
+ print(f"오류: CSV 파일을 찾을 수 없습니다: {csv_file_path}")
451
+ return False
452
+ except Exception as e:
453
+ print(f"오류: CSV 파일을 읽는 중 오류 발생: {e}")
454
+ return False
455
+
456
+ def add_part_value(self, part_name: str, value: str,
457
+ description: Optional[str] = None,
458
+ korean_description: Optional[str] = None) -> bool: # Add korean_description parameter
459
+ """
460
+ 특정 NamePart에 사전 정의 값 추가
461
+
462
+ Args:
463
+ part_name: NamePart 이름
464
+ value: 추가할 사전 정의 값
465
+ description: 추가할 값의 설명 (기본값: None, 값과 같은 설명 사용)
466
+ korean_description: 추가할 값의 한국어 설명 (기본값: None, 값과 같은 설명 사용) # Add korean_description doc
467
+
468
+ Returns:
469
+ 추가 성공 여부 (True/False)
470
+ """
471
+ part = self.get_part(part_name)
472
+ if not part:
473
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
474
+ return False
475
+
476
+ # REALNAME이나 INDEX 타입은 사전 정의 값 추가 불가
477
+ if part.is_realname() or part.is_index():
478
+ print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 추가할 수 없습니다.")
479
+ return False
480
+
481
+ # 값이 이미 존재하는지 확인
482
+ if part.contains_value(value):
483
+ print(f"오류: '{value}'가 이미 {part_name} 부분의 사전 정의 값에 존재합니다.")
484
+ return False
485
+
486
+ # description이 없으면 값을 설명으로 사용
487
+ if description is None:
488
+ description = value
489
+
490
+ # korean_description이 없으면 값을 설명으로 사용
491
+ if korean_description is None:
492
+ korean_description = value
493
+
494
+ # NamePart 클래스의 add_predefined_value 메소드 직접 활용
495
+ return part.add_predefined_value(value, description, korean_description) # Pass korean_description
496
+
497
+ def remove_part_value(self, part_name: str, value: str) -> bool:
498
+ """
499
+ 특정 NamePart에서 사전 정의 값과 해당 설명 제거
500
+
501
+ Args:
502
+ part_name: NamePart 이름
503
+ value: 제거할 사전 정의 값
504
+
505
+ Returns:
506
+ 제거 성공 여부 (True/False)
507
+ """
508
+ part = self.get_part(part_name)
509
+ if not part:
510
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
511
+ return False
512
+
513
+ # REALNAME이나 INDEX 타입은 사전 정의 값 제거 불가
514
+ if part.is_realname() or part.is_index():
515
+ print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 제거할 수 없습니다.")
516
+ return False
517
+
518
+ # 값이 존재하는지 확인
519
+ if not part.contains_value(value):
520
+ print(f"오류: '{value}'가 {part_name} 부분의 사전 정의 값에 존재하지 않습니다.")
521
+ return False
522
+
523
+ # 마지막 값인지 확인
524
+ if part.get_value_count() <= 1:
525
+ print(f"오류: {part_name} 부분의 사전 정의 값은 적어도 하나 이상 있어야 합니다.")
526
+ return False
527
+
528
+ # NamePart 클래스의 remove_predefined_value 메소드 직접 활용
529
+ return part.remove_predefined_value(value)
530
+
531
+ def set_part_descriptions(self, part_name: str, descriptions: List[str]) -> bool:
532
+ """
533
+ 특정 NamePart의 설명 목록 설정
534
+
535
+ Args:
536
+ part_name: NamePart 이름
537
+ descriptions: 설정할 설명 목록
538
+
539
+ Returns:
540
+ 설정 성공 여부 (True/False)
541
+ """
542
+ part = self.get_part(part_name)
543
+ if not part:
544
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
545
+ return False
546
+
547
+ # REALNAME이나 INDEX 타입은 설명 설정 불가
548
+ if part.is_realname() or part.is_index():
549
+ print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 설명을 설정할 수 없습니다.")
550
+ return False
551
+
552
+ # NamePart 클래스 메소드 활용하여 설명 설정
553
+ values = part.get_predefined_values()
554
+
555
+ # 길이 맞추기
556
+ if len(descriptions) < len(values):
557
+ descriptions.extend([""] * (len(values) - len(descriptions)))
558
+ elif len(descriptions) > len(values):
559
+ descriptions = descriptions[:len(values)]
560
+
561
+ # 각 값에 대한 설명 설정 (NamePart.set_description 사용)
562
+ success = True
563
+ for i, value in enumerate(values):
564
+ if not part.set_description(value, descriptions[i]):
565
+ success = False # 실패 시 기록 (이론상 발생하지 않음)
566
+
567
+ return success
568
+
569
+ def get_part_descriptions(self, part_name: str) -> List[str]:
570
+ """
571
+ 특정 NamePart의 설명 목록 가져오기
572
+
573
+ Args:
574
+ part_name: NamePart 이름
575
+
576
+ Returns:
577
+ 설명 목록
578
+ """
579
+ part = self.get_part(part_name)
580
+ if not part:
581
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
582
+ return []
583
+
584
+ return part.get_descriptions()
585
+
586
+ def set_part_korean_descriptions(self, part_name: str, korean_descriptions: List[str]) -> bool:
587
+ """
588
+ 특정 NamePart의 한국어 설명 목록 설정
589
+
590
+ Args:
591
+ part_name: NamePart 이름
592
+ korean_descriptions: 설정할 한국어 설명 목록
593
+
594
+ Returns:
595
+ 설정 성공 여부 (True/False)
596
+ """
597
+ part = self.get_part(part_name)
598
+ if not part:
599
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
600
+ return False
601
+
602
+ # REALNAME이나 INDEX 타입은 설명 설정 불가
603
+ if part.is_realname() or part.is_index():
604
+ print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 한국어 설명을 설정할 수 없습니다.")
605
+ return False
606
+
607
+ # NamePart 클래스 메소드 활용하여 설명 설정
608
+ values = part.get_predefined_values()
609
+
610
+ # 길이 맞추기
611
+ if len(korean_descriptions) < len(values):
612
+ korean_descriptions.extend([""] * (len(values) - len(korean_descriptions)))
613
+ elif len(korean_descriptions) > len(values):
614
+ korean_descriptions = korean_descriptions[:len(values)]
615
+
616
+ # 각 값에 대한 한국어 설명 설정 (NamePart.set_korean_description 사용)
617
+ success = True
618
+ for i, value in enumerate(values):
619
+ if not part.set_korean_description(value, korean_descriptions[i]):
620
+ success = False # 실패 시 기록 (이론상 발생하지 않음)
621
+
622
+ return success
623
+
624
+ def get_part_korean_descriptions(self, part_name: str) -> List[str]:
625
+ """
626
+ 특정 NamePart의 한국어 설명 목록 가져오기
627
+
628
+ Args:
629
+ part_name: NamePart 이름
630
+
631
+ Returns:
632
+ 한국어 설명 목록
633
+ """
634
+ part = self.get_part(part_name)
635
+ if not part:
636
+ print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
637
+ return []
638
+
639
+ return part.get_korean_descriptions()
640
+
641
+ def get_prefix_parts(self) -> List[NamePart]:
642
+ """
643
+ 모든 PREFIX 타입 NamePart 가져오기
644
+
645
+ Returns:
646
+ PREFIX 타입의 NamePart 객체 리스트
647
+ """
648
+ return [part for part in self.name_parts if part.is_prefix()]
649
+
650
+ def get_suffix_parts(self) -> List[NamePart]:
651
+ """
652
+ 모든 SUFFIX 타입 NamePart 가져오기
653
+
654
+ Returns:
655
+ SUFFIX 타입의 NamePart 객체 리스트
656
+ """
657
+ return [part for part in self.name_parts if part.is_suffix()]
658
+
659
+ def get_realname_part(self) -> Optional[NamePart]:
660
+ """
661
+ REALNAME 타입 NamePart 가져오기
662
+
663
+ Returns:
664
+ REALNAME 타입의 NamePart 객체, 없으면 None
665
+ """
666
+ for part in self.name_parts:
667
+ if part.is_realname():
668
+ return part
669
+ return None
670
+
671
+ def get_index_part(self) -> Optional[NamePart]:
672
+ """
673
+ INDEX 타입 NamePart 가져오기
674
+
675
+ Returns:
676
+ INDEX 타입의 NamePart 객체, 없으면 None
677
+ """
678
+ for part in self.name_parts:
679
+ if part.is_index():
680
+ return part
681
+ return None
682
+
683
+ def save(self, file_path: Optional[str] = None) -> bool:
684
+ """
685
+ 현재 설정을 JSON 파일로 저장
686
+
687
+ Args:
688
+ file_path: 저장할 파일 경로 (기본값: self.default_file_path)
689
+
690
+ Returns:
691
+ 저장 성공 여부 (True/False)
692
+ """
693
+ save_path = file_path or self.default_file_path
694
+
695
+ try:
696
+ # 저장할 데이터 준비
697
+ save_data = {
698
+ "paddingNum": self.padding_num,
699
+ "partOrder": self.part_order, # 순서 정보 저장
700
+ "nameParts": []
701
+ }
702
+
703
+ # 각 NamePart 객체를 딕셔너리로 변환하여 추가
704
+ for part in self.name_parts:
705
+ save_data["nameParts"].append(part.to_dict())
706
+
707
+ # JSON 파일로 저장
708
+ with open(save_path, 'w', encoding='utf-8') as f:
709
+ json.dump(save_data, f, indent=4, ensure_ascii=False)
710
+
711
+ self.config_file_path = save_path
712
+ return True
713
+ except Exception as e:
714
+ print(f"설정 저장 중 오류 발생: {e}")
715
+ return False
716
+
717
+ def load(self, file_path: Optional[str] = None) -> bool:
718
+ """
719
+ JSON 파일에서 설정 불러오기
720
+
721
+ Args:
722
+ file_path: 불러올 파일 경로 (기본값: self.default_file_path)
723
+
724
+ Returns:
725
+ 로드 성공 여부 (True/False)
726
+ """
727
+ load_path = file_path or self.default_file_path
728
+
729
+ try:
730
+ if os.path.exists(load_path):
731
+ with open(load_path, 'r', encoding='utf-8') as f:
732
+ loaded_data = json.load(f)
733
+
734
+ # 필수 키가 있는지 확인
735
+ if "nameParts" not in loaded_data:
736
+ print("경고: 설정 파일에 필수 키 'nameParts'가 없습니다.")
737
+ return False
738
+
739
+ # paddingNum 불러오기
740
+ if "paddingNum" in loaded_data:
741
+ self.padding_num = loaded_data["paddingNum"]
742
+
743
+ # 파트 순서 불러오기
744
+ if "partOrder" in loaded_data:
745
+ self.part_order = loaded_data["partOrder"]
746
+ else:
747
+ # 없으면 기본 순서 생성
748
+ self.part_order = [part_data["name"] for part_data in loaded_data["nameParts"]]
749
+
750
+ # NamePart 객체 리스트 생성
751
+ new_parts = []
752
+ for part_data in loaded_data["nameParts"]:
753
+ part = NamePart.from_dict(part_data)
754
+ new_parts.append(part)
755
+
756
+ # 필수 NamePart가 포함되어 있는지 확인
757
+ part_names = [part.get_name() for part in new_parts]
758
+ for required_name in self.required_parts:
759
+ if required_name not in part_names:
760
+ print(f"경고: 필수 NamePart '{required_name}'가 설정에 포함되어 있지 않습니다.")
761
+ return False
762
+
763
+ # 모든 확인이 통과되면 데이터 업데이트
764
+ self.name_parts = new_parts
765
+ self.config_file_path = load_path
766
+
767
+ # 순서에 따라 타입 업데이트
768
+ self._update_part_types_based_on_order()
769
+ self._update_part_order() # 순서 업데이트
770
+ return True
771
+ else:
772
+ print(f"설정 파일을 찾을 수 없습니다: {load_path}")
773
+ return False
774
+ except Exception as e:
775
+ print(f"설정 로드 중 오류 발생: {e}")
776
+ return False
777
+
778
+ def apply_to_naming(self, naming_instance) -> bool:
779
+ """
780
+ 설정을 Naming 인스턴스에 적용
781
+
782
+ Args:
783
+ naming_instance: 설정을 적용할 Naming 클래스 인스턴스
784
+
785
+ Returns:
786
+ 적용 성공 여부 (True/False)
787
+ """
788
+ try:
789
+ # NamePart 객체 리스트 복사하여 적용
790
+ naming_instance._nameParts = copy.deepcopy(self.name_parts)
791
+
792
+ # paddingNum 설정
793
+ naming_instance._paddingNum = self.padding_num
794
+
795
+ return True
796
+ except Exception as e:
797
+ print(f"설정 적용 중 오류 발생: {e}")
798
+ return False
799
+
800
+ def insert_part(self, name: str, part_type: NamePartType, position: int,
801
+ values: Optional[List[str]] = None,
802
+ descriptions: Optional[List[str]] = None,
803
+ korean_descriptions: Optional[List[str]] = None) -> bool: # Add value/description parameters
804
+ """
805
+ 특정 위치에 새 NamePart 삽입
806
+
807
+ Args:
808
+ name: 삽입할 NamePart 이름
809
+ part_type: NamePart 타입
810
+ position: 삽입할 위치 (인덱스)
811
+ values: 사전 정의된 값 목록 (기본값: None) # Add doc
812
+ descriptions: 값에 대한 설명 목록 (기본값: None) # Add doc
813
+ korean_descriptions: 값에 대한 한국어 설명 목록 (기본값: None) # Add doc
814
+
815
+ Returns:
816
+ 삽입 성공 여부 (True/False)
817
+ """
818
+ if not name:
819
+ print("오류: 유효한 NamePart 이름을 입력하세요.")
820
+ return False
821
+
822
+ # 이미 존재하는지 확인
823
+ if self.get_part(name) is not None:
824
+ print(f"오류: '{name}' NamePart가 이미 존재합니다.")
825
+ return False
826
+
827
+ # 위치 범위 확인
828
+ if position < 0 or position > len(self.name_parts):
829
+ print(f"오류: 위치가 유효하지 않습니다. 0에서 {len(self.name_parts)} 사이의 값이어야 합니다.")
830
+ return False
831
+
832
+ # 새 NamePart 생성 (값과 설명 포함)
833
+ new_part = NamePart(name, part_type, values or [], descriptions, False, korean_descriptions) # Pass values/descriptions
834
+
835
+ # 지정된 위치에 삽입
836
+ self.name_parts.insert(position, new_part)
837
+
838
+ # 순서 목록 업데이트
839
+ if name not in self.part_order:
840
+ self.part_order.insert(position, name)
841
+
842
+ # 순서에 따라 타입 업데이트
843
+ self._update_part_types_based_on_order()
844
+ return True