pyjallib.namingConfig

namingConfig 모듈 - Naming 클래스의 설정을 관리하는 기능 제공 NamePart 객체를 기반으로 네이밍 설정을 저장하고 불러오는 기능 구현

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

Naming 클래스의 설정을 관리하는 클래스. NamePart 객체 리스트를 관리하고 JSON 파일로 저장/불러오기 기능 제공.

NamingConfig( padding_num: int = 2, name_parts: typing.Optional[typing.List[pyjallib.namePart.NamePart]] = None, config_file_path: str = '', default_file_name: str = 'namingConfig.json', required_parts: typing.Optional[typing.List[str]] = None)
27    def __init__(self, padding_num: int = 2, name_parts: Optional[List[NamePart]] = None, 
28                 config_file_path: str = "", default_file_name: str = "namingConfig.json", 
29                 required_parts: Optional[List[str]] = None):
30        """
31        클래스 초기화 및 기본 설정값 정의
32        
33        Args:
34            padding_num: 인덱스 패딩 자릿수 (기본값: 2)
35            name_parts: 초기 NamePart 객체 리스트 (기본값: None, 기본 파트로 초기화)
36            config_file_path: 설정 파일 경로 (기본값: 빈 문자열)
37            default_file_name: 기본 파일명 (기본값: "namingConfig.json")
38            required_parts: 필수 namePart 목록 (기본값: ["RealName"])
39        """
40        # NamePart 객체 리스트
41        self.name_parts = name_parts or []
42        
43        # 추가 설정
44        self.padding_num = padding_num
45        
46        # NamePart 순서 정보 저장
47        self.part_order = []
48        
49        # 필수 namePart 정의 (삭제 불가능)
50        self.required_parts = required_parts or ["RealName"]
51        
52        # 설정 파일 경로 및 기본 파일명
53        self.config_file_path = config_file_path
54        self.default_file_name = default_file_name
55        
56        # 스크립트 디렉토리 기준 기본 경로 설정
57        script_dir = os.path.dirname(os.path.abspath(__file__))
58        config_dir = os.path.join(script_dir, "ConfigFiles")
59        self.default_file_path = os.path.join(config_dir, self.default_file_name)
60        
61        # name_parts가 제공되지 않은 경우에만 기본 NamePart 초기화
62        if not self.name_parts:
63            self._initialize_default_parts()
64        else:
65            # 제공된 name_parts가 있는 경우 순서 업데이트 및 타입 자동 업데이트
66            self._update_part_order()
67            self._update_part_types_based_on_order()

클래스 초기화 및 기본 설정값 정의

Args: padding_num: 인덱스 패딩 자릿수 (기본값: 2) name_parts: 초기 NamePart 객체 리스트 (기본값: None, 기본 파트로 초기화) config_file_path: 설정 파일 경로 (기본값: 빈 문자열) default_file_name: 기본 파일명 (기본값: "namingConfig.json") required_parts: 필수 namePart 목록 (기본값: ["RealName"])

name_parts
padding_num
part_order
required_parts
config_file_path
default_file_name
default_file_path
def get_part_names(self) -> List[str]:
149    def get_part_names(self) -> List[str]:
150        """
151        모든 NamePart 이름 목록 반환
152        
153        Returns:
154            NamePart 이름 목록
155        """
156        return [part.get_name() for part in self.name_parts]

모든 NamePart 이름 목록 반환

Returns: NamePart 이름 목록

def get_part_order(self) -> List[str]:
158    def get_part_order(self) -> List[str]:
159        """
160        NamePart 순서 목록 반환
161        
162        Returns:
163            NamePart 이름 순서 목록
164        """
165        return self.part_order.copy()

NamePart 순서 목록 반환

Returns: NamePart 이름 순서 목록

def get_part(self, name: str) -> Optional[pyjallib.namePart.NamePart]:
167    def get_part(self, name: str) -> Optional[NamePart]:
168        """
169        이름으로 NamePart 객체 가져오기
170        
171        Args:
172            name: NamePart 이름
173            
174        Returns:
175            NamePart 객체, 없으면 None
176        """
177        for part in self.name_parts:
178            if part.get_name() == name:
179                return part
180        return None

이름으로 NamePart 객체 가져오기

Args: name: NamePart 이름

Returns: NamePart 객체, 없으면 None

def add_part( self, name: str, part_type: pyjallib.namePart.NamePartType = <NamePartType.UNDEFINED: 5>, values: typing.Optional[typing.List[str]] = None, descriptions: typing.Optional[typing.List[str]] = None, korean_descriptions: typing.Optional[typing.List[str]] = None) -> bool:
182    def add_part(self, name: str, part_type: NamePartType = NamePartType.UNDEFINED, 
183                 values: Optional[List[str]] = None, descriptions: Optional[List[str]] = None,
184                 korean_descriptions: Optional[List[str]] = None) -> bool: # Add korean_descriptions parameter
185        """
186        새 NamePart 객체 추가
187        
188        Args:
189            name: 추가할 NamePart 이름
190            part_type: NamePart 타입 (기본값: UNDEFINED)
191            values: 사전 정의된 값 목록 (기본값: None)
192            descriptions: 값에 대한 설명 목록 (기본값: None, 값과 동일하게 설정됨)
193            korean_descriptions: 값에 대한 한국어 설명 목록 (기본값: None, 값과 동일하게 설정됨) # Add korean_descriptions doc
194            
195        Returns:
196            추가 성공 여부 (True/False)
197        """
198        if not name:
199            print("오류: 유효한 NamePart 이름을 입력하세요.")
200            return False
201        
202        # 이미 존재하는지 확인
203        if self.get_part(name) is not None:
204            print(f"오류: '{name}' NamePart가 이미 존재합니다.")
205            return False
206        
207        # 새 NamePart 객체 생성 - NamePart 클래스의 생성자 활용
208        new_part = NamePart(name, part_type, values or [], descriptions, False, korean_descriptions) # Pass korean_descriptions
209        
210        # 리스트에 추가
211        self.name_parts.append(new_part)
212        
213        # 순서 목록에 추가
214        if name not in self.part_order:
215            self.part_order.append(name)
216        
217        # 순서에 따라 타입 업데이트
218        self._update_part_types_based_on_order()
219        return True

새 NamePart 객체 추가

Args: name: 추가할 NamePart 이름 part_type: NamePart 타입 (기본값: UNDEFINED) values: 사전 정의된 값 목록 (기본값: None) descriptions: 값에 대한 설명 목록 (기본값: None, 값과 동일하게 설정됨) korean_descriptions: 값에 대한 한국어 설명 목록 (기본값: None, 값과 동일하게 설정됨) # Add korean_descriptions doc

Returns: 추가 성공 여부 (True/False)

def remove_part(self, name: str) -> bool:
221    def remove_part(self, name: str) -> bool:
222        """
223        NamePart 객체 제거 (필수 부분은 제거 불가)
224        
225        Args:
226            name: 제거할 NamePart 이름
227            
228        Returns:
229            제거 성공 여부 (True/False)
230        """
231        # 필수 부분은 제거 불가능
232        if name in self.required_parts:
233            print(f"오류: 필수 NamePart '{name}'는 제거할 수 없습니다.")
234            return False
235        
236        # 찾아서 제거
237        for i, part in enumerate(self.name_parts):
238            if part.get_name() == name:
239                del self.name_parts[i]
240                
241                # 순서 목록에서도 제거
242                if name in self.part_order:
243                    self.part_order.remove(name)
244                
245                # 순서에 따라 타입 업데이트
246                self._update_part_types_based_on_order()
247                return True
248        
249        print(f"오류: '{name}' NamePart가 존재하지 않습니다.")
250        return False

NamePart 객체 제거 (필수 부분은 제거 불가)

Args: name: 제거할 NamePart 이름

Returns: 제거 성공 여부 (True/False)

def reorder_parts(self, new_order: typing.List[str]) -> bool:
252    def reorder_parts(self, new_order: List[str]) -> bool:
253        """
254        NamePart 순서 변경
255        
256        Args:
257            new_order: 새로운 NamePart 이름 순서 배열
258            
259        Returns:
260            변경 성공 여부 (True/False)
261        """
262        # 배열 길이 확인
263        if len(new_order) != len(self.name_parts):
264            print("오류: 새 순서의 항목 수가 기존 NamePart와 일치하지 않습니다.")
265            return False
266        
267        # 모든 필수 부분이 포함되어 있는지 확인
268        for part in self.required_parts:
269            if part not in new_order:
270                print(f"오류: 필수 NamePart '{part}'가 새 순서에 포함되어 있지 않습니다.")
271                return False
272        
273        # 모든 이름이 현재 존재하는지 확인
274        current_names = self.get_part_names()
275        for name in new_order:
276            if name not in current_names:
277                print(f"오류: '{name}' NamePart가 존재하지 않습니다.")
278                return False
279        
280        # 순서 변경을 위한 새 리스트 생성
281        reordered_parts = []
282        for name in new_order:
283            part = self.get_part(name)
284            if part:
285                reordered_parts.append(part)
286        
287        # 새 순서로 업데이트
288        self.name_parts = reordered_parts
289        self.part_order = new_order.copy()
290        
291        # 순서에 따라 타입 업데이트
292        self._update_part_types_based_on_order()
293        return True

NamePart 순서 변경

Args: new_order: 새로운 NamePart 이름 순서 배열

Returns: 변경 성공 여부 (True/False)

def set_padding_num(self, padding_num: int) -> bool:
295    def set_padding_num(self, padding_num: int) -> bool:
296        """
297        인덱스 자릿수 설정
298        
299        Args:
300            padding_num: 설정할 패딩 자릿수
301            
302        Returns:
303            설정 성공 여부 (True/False)
304        """
305        if not isinstance(padding_num, int) or padding_num < 1:
306            print("오류: 패딩 자릿수는 1 이상의 정수여야 합니다.")
307            return False
308        
309        self.padding_num = padding_num
310        return True

인덱스 자릿수 설정

Args: padding_num: 설정할 패딩 자릿수

Returns: 설정 성공 여부 (True/False)

def set_part_type(self, part_name: str, part_type: pyjallib.namePart.NamePartType) -> bool:
312    def set_part_type(self, part_name: str, part_type: NamePartType) -> bool:
313        """
314        특정 NamePart의 타입 설정
315        
316        Args:
317            part_name: NamePart 이름
318            part_type: 설정할 타입 (NamePartType 열거형 값)
319            
320        Returns:
321            설정 성공 여부 (True/False)
322        """
323        part = self.get_part(part_name)
324        if not part:
325            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
326            return False
327        
328        # 필수 RealName 부분은 항상 REALNAME 타입이어야 함
329        if part_name == "RealName" and part_type != NamePartType.REALNAME:
330            print("오류: RealName 부분은 반드시 REALNAME 타입이어야 합니다.")
331            return False
332        
333        # Index 부분은 항상 INDEX 타입이어야 함
334        if part_name == "Index" and part_type != NamePartType.INDEX:
335            print("오류: Index 부분은 반드시 INDEX 타입이어야 합니다.")
336            return False
337        
338        part.set_type(part_type)
339        return True

특정 NamePart의 타입 설정

Args: part_name: NamePart 이름 part_type: 설정할 타입 (NamePartType 열거형 값)

Returns: 설정 성공 여부 (True/False)

def get_part_type(self, part_name: str) -> Optional[pyjallib.namePart.NamePartType]:
341    def get_part_type(self, part_name: str) -> Optional[NamePartType]:
342        """
343        특정 NamePart의 타입 가져오기
344        
345        Args:
346            part_name: NamePart 이름
347            
348        Returns:
349            NamePart 타입, 없으면 None
350        """
351        part = self.get_part(part_name)
352        if not part:
353            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
354            return None
355        
356        return part.get_type()

특정 NamePart의 타입 가져오기

Args: part_name: NamePart 이름

Returns: NamePart 타입, 없으면 None

def set_part_values( self, part_name: str, values: typing.List[str], descriptions: typing.Optional[typing.List[str]] = None, korean_descriptions: typing.Optional[typing.List[str]] = None) -> bool:
358    def set_part_values(self, part_name: str, values: List[str], 
359                        descriptions: Optional[List[str]] = None, 
360                        korean_descriptions: Optional[List[str]] = None) -> bool: # Add korean_descriptions parameter
361        """
362        특정 NamePart의 사전 정의 값 설정
363        
364        Args:
365            part_name: NamePart 이름
366            values: 설정할 사전 정의 값 리스트
367            descriptions: 설정할 설명 목록 (기본값: None, 값과 같은 설명 사용)
368            korean_descriptions: 설정할 한국어 설명 목록 (기본값: None, 값과 같은 설명 사용) # Add korean_descriptions doc
369            
370        Returns:
371            설정 성공 여부 (True/False)
372        """
373        part = self.get_part(part_name)
374        if not part:
375            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
376            return False
377        
378        # REALNAME이나 INDEX 타입은 사전 정의 값 설정 불가
379        if part.is_realname() or part.is_index():
380            print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 설정할 수 없습니다.")
381            return False
382        
383        if not values:
384            print(f"오류: {part_name} 부분의 사전 정의 값은 적어도 하나 이상 있어야 합니다.")
385            return False
386        
387        # 값 설정
388        part.set_predefined_values(values, descriptions, korean_descriptions) # Pass korean_descriptions
389        
390        return True

특정 NamePart의 사전 정의 값 설정

Args: part_name: NamePart 이름 values: 설정할 사전 정의 값 리스트 descriptions: 설정할 설명 목록 (기본값: None, 값과 같은 설명 사용) korean_descriptions: 설정할 한국어 설명 목록 (기본값: None, 값과 같은 설명 사용) # Add korean_descriptions doc

Returns: 설정 성공 여부 (True/False)

def set_part_value_by_csv(self, part_name: str, csv_file_path: str) -> bool:
392    def set_part_value_by_csv(self, part_name: str, csv_file_path: str) -> bool:
393        """
394        특정 NamePart의 사전 정의 값을 CSV 파일로 설정
395        CSV 파일 형식: value,description,koreanDescription (각 줄당)
396        
397        Args:
398            part_name: NamePart 이름
399            csv_file_path: CSV 파일 경로
400            
401        Returns:
402            설정 성공 여부 (True/False)
403        """
404        part = self.get_part(part_name)
405        if not part:
406            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
407            return False
408        
409        # REALNAME이나 INDEX 타입은 사전 정의 값 설정 불가
410        if part.is_realname() or part.is_index():
411            print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 설정할 수 없습니다.")
412            return False
413        
414        # CSV 파일에서 값, 설명, 한국어 설명 읽기
415        values = []
416        descriptions = []
417        korean_descriptions = []
418        try:
419            with open(csv_file_path, 'r', encoding='utf-8', newline='') as f:
420                reader = csv.reader(f)
421                for row in reader:
422                    if len(row) >= 3: # Ensure row has at least 3 columns
423                        value = row[0].strip()
424                        description = row[1].strip()
425                        korean_description = row[2].strip()
426                        if value: # Skip empty values
427                            values.append(value)
428                            descriptions.append(description if description else value) # Use value if description is empty
429                            korean_descriptions.append(korean_description if korean_description else value) # Use value if korean_description is empty
430                    elif len(row) == 2: # Handle case with value and description only
431                        value = row[0].strip()
432                        description = row[1].strip()
433                        if value:
434                            values.append(value)
435                            descriptions.append(description if description else value)
436                            korean_descriptions.append(value) # Use value as korean description
437                    elif len(row) == 1: # Handle case with value only
438                        value = row[0].strip()
439                        if value:
440                            values.append(value)
441                            descriptions.append(value)
442                            korean_descriptions.append(value)
443
444            if not values:
445                print(f"오류: CSV 파일 '{csv_file_path}'에서 유효한 값을 찾을 수 없습니다.")
446                return False
447
448            # 값, 설명, 한국어 설명 설정
449            return self.set_part_values(part_name, values, descriptions, korean_descriptions)
450        except FileNotFoundError:
451            print(f"오류: CSV 파일을 찾을 수 없습니다: {csv_file_path}")
452            return False
453        except Exception as e:
454            print(f"오류: CSV 파일을 읽는 중 오류 발생: {e}")
455            return False

특정 NamePart의 사전 정의 값을 CSV 파일로 설정 CSV 파일 형식: value,description,koreanDescription (각 줄당)

Args: part_name: NamePart 이름 csv_file_path: CSV 파일 경로

Returns: 설정 성공 여부 (True/False)

def add_part_value( self, part_name: str, value: str, description: typing.Optional[str] = None, korean_description: typing.Optional[str] = None) -> bool:
457    def add_part_value(self, part_name: str, value: str, 
458                       description: Optional[str] = None, 
459                       korean_description: Optional[str] = None) -> bool: # Add korean_description parameter
460        """
461        특정 NamePart에 사전 정의 값 추가
462        
463        Args:
464            part_name: NamePart 이름
465            value: 추가할 사전 정의 값
466            description: 추가할 값의 설명 (기본값: None, 값과 같은 설명 사용)
467            korean_description: 추가할 값의 한국어 설명 (기본값: None, 값과 같은 설명 사용) # Add korean_description doc
468            
469        Returns:
470            추가 성공 여부 (True/False)
471        """
472        part = self.get_part(part_name)
473        if not part:
474            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
475            return False
476        
477        # REALNAME이나 INDEX 타입은 사전 정의 값 추가 불가
478        if part.is_realname() or part.is_index():
479            print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 추가할 수 없습니다.")
480            return False
481        
482        # 값이 이미 존재하는지 확인
483        if part.contains_value(value):
484            print(f"오류: '{value}'가 이미 {part_name} 부분의 사전 정의 값에 존재합니다.")
485            return False
486        
487        # description이 없으면 값을 설명으로 사용
488        if description is None:
489            description = value
490            
491        # korean_description이 없으면 값을 설명으로 사용
492        if korean_description is None:
493            korean_description = value
494            
495        # NamePart 클래스의 add_predefined_value 메소드 직접 활용
496        return part.add_predefined_value(value, description, korean_description) # Pass korean_description

특정 NamePart에 사전 정의 값 추가

Args: part_name: NamePart 이름 value: 추가할 사전 정의 값 description: 추가할 값의 설명 (기본값: None, 값과 같은 설명 사용) korean_description: 추가할 값의 한국어 설명 (기본값: None, 값과 같은 설명 사용) # Add korean_description doc

Returns: 추가 성공 여부 (True/False)

def remove_part_value(self, part_name: str, value: str) -> bool:
498    def remove_part_value(self, part_name: str, value: str) -> bool:
499        """
500        특정 NamePart에서 사전 정의 값과 해당 설명 제거
501        
502        Args:
503            part_name: NamePart 이름
504            value: 제거할 사전 정의 값
505            
506        Returns:
507            제거 성공 여부 (True/False)
508        """
509        part = self.get_part(part_name)
510        if not part:
511            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
512            return False
513        
514        # REALNAME이나 INDEX 타입은 사전 정의 값 제거 불가
515        if part.is_realname() or part.is_index():
516            print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 사전 정의 값을 제거할 수 없습니다.")
517            return False
518        
519        # 값이 존재하는지 확인
520        if not part.contains_value(value):
521            print(f"오류: '{value}'가 {part_name} 부분의 사전 정의 값에 존재하지 않습니다.")
522            return False
523        
524        # 마지막 값인지 확인
525        if part.get_value_count() <= 1:
526            print(f"오류: {part_name} 부분의 사전 정의 값은 적어도 하나 이상 있어야 합니다.")
527            return False
528        
529        # NamePart 클래스의 remove_predefined_value 메소드 직접 활용
530        return part.remove_predefined_value(value)

특정 NamePart에서 사전 정의 값과 해당 설명 제거

Args: part_name: NamePart 이름 value: 제거할 사전 정의 값

Returns: 제거 성공 여부 (True/False)

def set_part_descriptions(self, part_name: str, descriptions: typing.List[str]) -> bool:
532    def set_part_descriptions(self, part_name: str, descriptions: List[str]) -> bool:
533        """
534        특정 NamePart의 설명 목록 설정
535        
536        Args:
537            part_name: NamePart 이름
538            descriptions: 설정할 설명 목록
539            
540        Returns:
541            설정 성공 여부 (True/False)
542        """
543        part = self.get_part(part_name)
544        if not part:
545            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
546            return False
547        
548        # REALNAME이나 INDEX 타입은 설명 설정 불가
549        if part.is_realname() or part.is_index():
550            print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 설명을 설정할 수 없습니다.")
551            return False
552        
553        # NamePart 클래스 메소드 활용하여 설명 설정
554        values = part.get_predefined_values()
555        
556        # 길이 맞추기
557        if len(descriptions) < len(values):
558            descriptions.extend([""] * (len(values) - len(descriptions)))
559        elif len(descriptions) > len(values):
560            descriptions = descriptions[:len(values)]
561        
562        # 각 값에 대한 설명 설정 (NamePart.set_description 사용)
563        success = True
564        for i, value in enumerate(values):
565            if not part.set_description(value, descriptions[i]):
566                success = False # 실패 시 기록 (이론상 발생하지 않음)
567                
568        return success

특정 NamePart의 설명 목록 설정

Args: part_name: NamePart 이름 descriptions: 설정할 설명 목록

Returns: 설정 성공 여부 (True/False)

def get_part_descriptions(self, part_name: str) -> List[str]:
570    def get_part_descriptions(self, part_name: str) -> List[str]:
571        """
572        특정 NamePart의 설명 목록 가져오기
573        
574        Args:
575            part_name: NamePart 이름
576            
577        Returns:
578            설명 목록
579        """
580        part = self.get_part(part_name)
581        if not part:
582            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
583            return []
584        
585        return part.get_descriptions()

특정 NamePart의 설명 목록 가져오기

Args: part_name: NamePart 이름

Returns: 설명 목록

def set_part_korean_descriptions(self, part_name: str, korean_descriptions: typing.List[str]) -> bool:
587    def set_part_korean_descriptions(self, part_name: str, korean_descriptions: List[str]) -> bool:
588        """
589        특정 NamePart의 한국어 설명 목록 설정
590        
591        Args:
592            part_name: NamePart 이름
593            korean_descriptions: 설정할 한국어 설명 목록
594            
595        Returns:
596            설정 성공 여부 (True/False)
597        """
598        part = self.get_part(part_name)
599        if not part:
600            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
601            return False
602        
603        # REALNAME이나 INDEX 타입은 설명 설정 불가
604        if part.is_realname() or part.is_index():
605            print(f"오류: {part_name} 부분은 {part.get_type().name} 타입이므로 한국어 설명을 설정할 수 없습니다.")
606            return False
607        
608        # NamePart 클래스 메소드 활용하여 설명 설정
609        values = part.get_predefined_values()
610        
611        # 길이 맞추기
612        if len(korean_descriptions) < len(values):
613            korean_descriptions.extend([""] * (len(values) - len(korean_descriptions)))
614        elif len(korean_descriptions) > len(values):
615            korean_descriptions = korean_descriptions[:len(values)]
616            
617        # 각 값에 대한 한국어 설명 설정 (NamePart.set_korean_description 사용)
618        success = True
619        for i, value in enumerate(values):
620            if not part.set_korean_description(value, korean_descriptions[i]):
621                success = False # 실패 시 기록 (이론상 발생하지 않음)
622                
623        return success

특정 NamePart의 한국어 설명 목록 설정

Args: part_name: NamePart 이름 korean_descriptions: 설정할 한국어 설명 목록

Returns: 설정 성공 여부 (True/False)

def get_part_korean_descriptions(self, part_name: str) -> List[str]:
625    def get_part_korean_descriptions(self, part_name: str) -> List[str]:
626        """
627        특정 NamePart의 한국어 설명 목록 가져오기
628        
629        Args:
630            part_name: NamePart 이름
631            
632        Returns:
633            한국어 설명 목록
634        """
635        part = self.get_part(part_name)
636        if not part:
637            print(f"오류: '{part_name}' NamePart가 존재하지 않습니다.")
638            return []
639        
640        return part.get_korean_descriptions()

특정 NamePart의 한국어 설명 목록 가져오기

Args: part_name: NamePart 이름

Returns: 한국어 설명 목록

def get_prefix_parts(self) -> List[pyjallib.namePart.NamePart]:
642    def get_prefix_parts(self) -> List[NamePart]:
643        """
644        모든 PREFIX 타입 NamePart 가져오기
645        
646        Returns:
647            PREFIX 타입의 NamePart 객체 리스트
648        """
649        return [part for part in self.name_parts if part.is_prefix()]

모든 PREFIX 타입 NamePart 가져오기

Returns: PREFIX 타입의 NamePart 객체 리스트

def get_suffix_parts(self) -> List[pyjallib.namePart.NamePart]:
651    def get_suffix_parts(self) -> List[NamePart]:
652        """
653        모든 SUFFIX 타입 NamePart 가져오기
654        
655        Returns:
656            SUFFIX 타입의 NamePart 객체 리스트
657        """
658        return [part for part in self.name_parts if part.is_suffix()]

모든 SUFFIX 타입 NamePart 가져오기

Returns: SUFFIX 타입의 NamePart 객체 리스트

def get_realname_part(self) -> Optional[pyjallib.namePart.NamePart]:
660    def get_realname_part(self) -> Optional[NamePart]:
661        """
662        REALNAME 타입 NamePart 가져오기
663        
664        Returns:
665            REALNAME 타입의 NamePart 객체, 없으면 None
666        """
667        for part in self.name_parts:
668            if part.is_realname():
669                return part
670        return None

REALNAME 타입 NamePart 가져오기

Returns: REALNAME 타입의 NamePart 객체, 없으면 None

def get_index_part(self) -> Optional[pyjallib.namePart.NamePart]:
672    def get_index_part(self) -> Optional[NamePart]:
673        """
674        INDEX 타입 NamePart 가져오기
675        
676        Returns:
677            INDEX 타입의 NamePart 객체, 없으면 None
678        """
679        for part in self.name_parts:
680            if part.is_index():
681                return part
682        return None

INDEX 타입 NamePart 가져오기

Returns: INDEX 타입의 NamePart 객체, 없으면 None

def save(self, file_path: typing.Optional[str] = None) -> bool:
684    def save(self, file_path: Optional[str] = None) -> bool:
685        """
686        현재 설정을 JSON 파일로 저장
687        
688        Args:
689            file_path: 저장할 파일 경로 (기본값: self.default_file_path)
690            
691        Returns:
692            저장 성공 여부 (True/False)
693        """
694        save_path = file_path or self.default_file_path
695        
696        try:
697            # 저장할 데이터 준비
698            save_data = {
699                "paddingNum": self.padding_num,
700                "partOrder": self.part_order,  # 순서 정보 저장
701                "nameParts": []
702            }
703            
704            # 각 NamePart 객체를 딕셔너리로 변환하여 추가
705            for part in self.name_parts:
706                save_data["nameParts"].append(part.to_dict())
707            
708            # JSON 파일로 저장
709            with open(save_path, 'w', encoding='utf-8') as f:
710                json.dump(save_data, f, indent=4, ensure_ascii=False)
711            
712            self.config_file_path = save_path
713            return True
714        except Exception as e:
715            print(f"설정 저장 중 오류 발생: {e}")
716            return False

현재 설정을 JSON 파일로 저장

Args: file_path: 저장할 파일 경로 (기본값: self.default_file_path)

Returns: 저장 성공 여부 (True/False)

def load(self, file_path: typing.Optional[str] = None) -> bool:
718    def load(self, file_path: Optional[str] = None) -> bool:
719        """
720        JSON 파일에서 설정 불러오기
721        
722        Args:
723            file_path: 불러올 파일 경로 (기본값: self.default_file_path)
724            
725        Returns:
726            로드 성공 여부 (True/False)
727        """
728        load_path = file_path or self.default_file_path
729        
730        try:
731            if os.path.exists(load_path):
732                with open(load_path, 'r', encoding='utf-8') as f:
733                    loaded_data = json.load(f)
734                
735                # 필수 키가 있는지 확인
736                if "nameParts" not in loaded_data:
737                    print("경고: 설정 파일에 필수 키 'nameParts'가 없습니다.")
738                    return False
739                
740                # paddingNum 불러오기
741                if "paddingNum" in loaded_data:
742                    self.padding_num = loaded_data["paddingNum"]
743                
744                # 파트 순서 불러오기
745                if "partOrder" in loaded_data:
746                    self.part_order = loaded_data["partOrder"]
747                else:
748                    # 없으면 기본 순서 생성
749                    self.part_order = [part_data["name"] for part_data in loaded_data["nameParts"]]
750                
751                # NamePart 객체 리스트 생성
752                new_parts = []
753                for part_data in loaded_data["nameParts"]:
754                    part = NamePart.from_dict(part_data)
755                    new_parts.append(part)
756                
757                # 필수 NamePart가 포함되어 있는지 확인
758                part_names = [part.get_name() for part in new_parts]
759                for required_name in self.required_parts:
760                    if required_name not in part_names:
761                        print(f"경고: 필수 NamePart '{required_name}'가 설정에 포함되어 있지 않습니다.")
762                        return False
763                
764                # 모든 확인이 통과되면 데이터 업데이트
765                self.name_parts = new_parts
766                self.config_file_path = load_path
767                
768                # 순서에 따라 타입 업데이트
769                self._update_part_types_based_on_order()
770                self._update_part_order()  # 순서 업데이트
771                return True
772            else:
773                print(f"설정 파일을 찾을 수 없습니다: {load_path}")
774                return False
775        except Exception as e:
776            print(f"설정 로드 중 오류 발생: {e}")
777            return False

JSON 파일에서 설정 불러오기

Args: file_path: 불러올 파일 경로 (기본값: self.default_file_path)

Returns: 로드 성공 여부 (True/False)

def apply_to_naming(self, naming_instance) -> bool:
779    def apply_to_naming(self, naming_instance) -> bool:
780        """
781        설정을 Naming 인스턴스에 적용
782        
783        Args:
784            naming_instance: 설정을 적용할 Naming 클래스 인스턴스
785            
786        Returns:
787            적용 성공 여부 (True/False)
788        """
789        try:
790            # NamePart 객체 리스트 복사하여 적용
791            naming_instance._nameParts = copy.deepcopy(self.name_parts)
792            
793            # paddingNum 설정
794            naming_instance._paddingNum = self.padding_num
795            
796            return True
797        except Exception as e:
798            print(f"설정 적용 중 오류 발생: {e}")
799            return False

설정을 Naming 인스턴스에 적용

Args: naming_instance: 설정을 적용할 Naming 클래스 인스턴스

Returns: 적용 성공 여부 (True/False)

def insert_part( self, name: str, part_type: pyjallib.namePart.NamePartType, position: int, values: typing.Optional[typing.List[str]] = None, descriptions: typing.Optional[typing.List[str]] = None, korean_descriptions: typing.Optional[typing.List[str]] = None) -> bool:
801    def insert_part(self, name: str, part_type: NamePartType, position: int,
802                    values: Optional[List[str]] = None, 
803                    descriptions: Optional[List[str]] = None,
804                    korean_descriptions: Optional[List[str]] = None) -> bool: # Add value/description parameters
805        """
806        특정 위치에 새 NamePart 삽입
807        
808        Args:
809            name: 삽입할 NamePart 이름
810            part_type: NamePart 타입
811            position: 삽입할 위치 (인덱스)
812            values: 사전 정의된 값 목록 (기본값: None) # Add doc
813            descriptions: 값에 대한 설명 목록 (기본값: None) # Add doc
814            korean_descriptions: 값에 대한 한국어 설명 목록 (기본값: None) # Add doc
815            
816        Returns:
817            삽입 성공 여부 (True/False)
818        """
819        if not name:
820            print("오류: 유효한 NamePart 이름을 입력하세요.")
821            return False
822        
823        # 이미 존재하는지 확인
824        if self.get_part(name) is not None:
825            print(f"오류: '{name}' NamePart가 이미 존재합니다.")
826            return False
827        
828        # 위치 범위 확인
829        if position < 0 or position > len(self.name_parts):
830            print(f"오류: 위치가 유효하지 않습니다. 0에서 {len(self.name_parts)} 사이의 값이어야 합니다.")
831            return False
832        
833        # 새 NamePart 생성 (값과 설명 포함)
834        new_part = NamePart(name, part_type, values or [], descriptions, False, korean_descriptions) # Pass values/descriptions
835        
836        # 지정된 위치에 삽입
837        self.name_parts.insert(position, new_part)
838        
839        # 순서 목록 업데이트
840        if name not in self.part_order:
841            self.part_order.insert(position, name)
842        
843        # 순서에 따라 타입 업데이트
844        self._update_part_types_based_on_order()
845        return True

특정 위치에 새 NamePart 삽입

Args: name: 삽입할 NamePart 이름 part_type: NamePart 타입 position: 삽입할 위치 (인덱스) values: 사전 정의된 값 목록 (기본값: None) # Add doc descriptions: 값에 대한 설명 목록 (기본값: None) # Add doc korean_descriptions: 값에 대한 한국어 설명 목록 (기본값: None) # Add doc

Returns: 삽입 성공 여부 (True/False)