🔧 볼트 재질 정보 개선 및 A320/A194M 패턴 지원
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

- bolt_classifier.py: A320/A194M 조합 패턴 처리 로직 추가
- material_grade_extractor.py: A320/A194M 패턴 추출 개선
- integrated_classifier.py: SPECIAL, U_BOLT 카테고리 우선 분류
- 데이터베이스: 492개 볼트의 material_grade를 완전한 형태로 업데이트
  - A320/A194M GR B8/8: 78개
  - A193/A194 GR B7/2H: 414개
- 프론트엔드: BOLT 카테고리 전용 UI (길이 표시)
- Excel 내보내기: BOLT용 컬럼 순서 및 재질 정보 개선
- SPECIAL, U_BOLT 카테고리 지원 추가
This commit is contained in:
Hyungi Ahn
2025-10-01 08:18:25 +09:00
parent 50570e4624
commit 2e0d91cf59
12 changed files with 2370 additions and 256 deletions

View File

@@ -12,6 +12,31 @@ def classify_bolt_material(description: str) -> Dict:
desc_upper = description.upper()
# A320/A194M 동시 처리 (예: "ASTM A320/A194M GR B8/8") - 저온용 볼트 조합
if "A320" in desc_upper and "A194" in desc_upper:
# B8/8 등급 추출
bolt_grade = "UNKNOWN"
nut_grade = "UNKNOWN"
if "B8" in desc_upper:
bolt_grade = "B8"
nut_grade = "8" # A320/A194M의 경우 보통 B8/8 조합
elif "L7" in desc_upper:
bolt_grade = "L7"
elif "B8M" in desc_upper:
bolt_grade = "B8M"
combined_grade = f"{bolt_grade}/{nut_grade}" if bolt_grade != "UNKNOWN" and nut_grade != "UNKNOWN" else f"{bolt_grade}" if bolt_grade != "UNKNOWN" else "A320/A194M"
return {
"standard": "ASTM A320/A194M",
"grade": combined_grade,
"material_type": "LOW_TEMP_STAINLESS", # 저온용 스테인리스
"manufacturing": "FORGED",
"confidence": 0.95,
"evidence": ["ASTM_A320_A194M_COMBINED"]
}
# A193/A194 동시 처리 (예: "ASTM A193/A194 GR B7/2H")
if "A193" in desc_upper and "A194" in desc_upper:
# B7/2H 등급 추출
@@ -136,6 +161,39 @@ def classify_bolt_material(description: str) -> Dict:
"evidence": ["ISO_4762_SOCKET_SCREW"]
}
# 일반적인 볼트 재질 패턴 추가 확인
if "B7" in desc_upper and "2H" in desc_upper:
return {
"standard": "ASTM A193/A194",
"grade": "B7/2H",
"material_type": "ALLOY_STEEL",
"manufacturing": "FORGED",
"confidence": 0.85,
"evidence": ["B7_2H_PATTERN"]
}
# 단독 B7 패턴
if "B7" in desc_upper:
return {
"standard": "ASTM A193",
"grade": "B7",
"material_type": "ALLOY_STEEL",
"manufacturing": "FORGED",
"confidence": 0.80,
"evidence": ["B7_PATTERN"]
}
# 단독 2H 패턴
if "2H" in desc_upper:
return {
"standard": "ASTM A194",
"grade": "2H",
"material_type": "ALLOY_STEEL",
"manufacturing": "FORGED",
"confidence": 0.80,
"evidence": ["2H_PATTERN"]
}
# 기본 재질 분류기 호출 (materials_schema 문제가 있어도 우회)
try:
return classify_material(description)
@@ -195,7 +253,7 @@ BOLT_TYPES = {
"LT_BOLT": {
"dat_file_patterns": ["LT_BOLT", "LT_BLT"],
"description_keywords": ["LT", "LOW TEMP", "저온용"],
"description_keywords": ["LT BOLT", "LT BLT", "LOW TEMP", "저온용"],
"characteristics": "저온용 특수 볼트",
"applications": "저온 환경 체결용",
"head_type": "HEXAGON",
@@ -507,7 +565,8 @@ def classify_special_application_bolts(description: str) -> Dict:
# LT 볼트 확인 (저온용 볼트)
lt_patterns = [
r'\bLT\b', # 단어 경계로 LT만
r'\bLT\s', # LT 다음에 공백이 있는 경우만 (LT BOLT, LT BLT)
r'^LT\b', # 문장 시작의 LT만
r'LOW\s+TEMP',
r'저온용',
r'CRYOGENIC',
@@ -915,20 +974,31 @@ def extract_bolt_dimensions(main_nom: str, description: str) -> Dict:
"dimension_description": nominal_size_fraction # 분수로 표시
}
# 길이 정보 추출
# 길이 정보 추출 (개선된 패턴)
length_patterns = [
r'(\d+(?:\.\d+)?)\s*LG', # 70.0000 LG 형태 (최우선)
r'(\d+(?:\.\d+)?)\s*LG', # 70.0000 LG, 145.0000 LG 형태 (최우선)
r'(\d+(?:\.\d+)?)\s*MM\s*LG', # 70MM LG 형태
r'L\s*(\d+(?:\.\d+)?)\s*MM',
r'LENGTH\s*(\d+(?:\.\d+)?)\s*MM',
r'(\d+(?:\.\d+)?)\s*MM\s*LONG',
r'X\s*(\d+(?:\.\d+)?)\s*MM' # M8 X 20MM 형태
r'X\s*(\d+(?:\.\d+)?)\s*MM', # M8 X 20MM 형태
r',\s*(\d+(?:\.\d+)?)\s*LG', # ", 145.0000 LG" 형태 (PSV, LT 볼트용)
r',\s*(\d+(?:\.\d+)?)\s+(?:CK|PSV|LT)', # ", 140 CK" 형태 (PSV 볼트용)
r'(\d+(?:\.\d+)?)\s*MM(?:\s|,|$)', # 75MM 형태 (단독)
r'(\d+(?:\.\d+)?)\s*mm(?:\s|,|$)' # 75mm 형태 (단독)
]
for pattern in length_patterns:
match = re.search(pattern, desc_upper)
if match:
dimensions["length"] = f"{match.group(1)}mm"
length_value = match.group(1)
# 소수점 제거 (145.0000 → 145)
if '.' in length_value and length_value.endswith('.0000'):
length_value = length_value.split('.')[0]
elif '.' in length_value and all(c == '0' for c in length_value.split('.')[1]):
length_value = length_value.split('.')[0]
dimensions["length"] = f"{length_value}mm"
break
# 지름 정보 (이미 main_nom에 있지만 확인)