볼트 분류 개선 및 업로드 성능 최적화
- 볼트 길이 추출 로직 개선: '70.0000 LG' 형태 인식 추가 - 재질 중복 표시 수정: 'ASTM A193 ASTM A193 B7' → 'B7' - A193/A194 등급 추출 로직 개선: 'GR B7/2H' 형태 지원 - bolt_details 테이블에 pressure_rating 컬럼 추가 - 볼트 분류기 오분류 방지: 플랜지/피팅이 볼트로 분류되지 않도록 수정 - 업로드 성능 개선: 키워드 기반 빠른 분류기 선택 로직 추가 - 분류 키워드 대폭 확장: 피팅/파이프/플랜지 키워드 추가
This commit is contained in:
@@ -7,6 +7,125 @@ import re
|
||||
from typing import Dict, List, Optional
|
||||
from .material_classifier import classify_material
|
||||
|
||||
def classify_bolt_material(description: str) -> Dict:
|
||||
"""볼트용 재질 분류 (ASTM A193, A194 등)"""
|
||||
|
||||
desc_upper = description.upper()
|
||||
|
||||
# ASTM A193 (볼트용 강재)
|
||||
if any(pattern in desc_upper for pattern in ["A193", "ASTM A193"]):
|
||||
# B7, B8 등 등급 추출 (GR B7/2H 형태도 지원)
|
||||
grade = "UNKNOWN"
|
||||
if "GR B7" in desc_upper or " B7" in desc_upper:
|
||||
grade = "B7"
|
||||
elif "GR B8" in desc_upper or " B8" in desc_upper:
|
||||
grade = "B8"
|
||||
elif "GR B16" in desc_upper or " B16" in desc_upper:
|
||||
grade = "B16"
|
||||
|
||||
return {
|
||||
"standard": "ASTM A193",
|
||||
"grade": grade if grade != "UNKNOWN" else "ASTM A193",
|
||||
"material_type": "ALLOY_STEEL" if "B7" in grade else "STAINLESS_STEEL",
|
||||
"manufacturing": "FORGED",
|
||||
"confidence": 0.95,
|
||||
"evidence": ["ASTM_A193_BOLT_MATERIAL"]
|
||||
}
|
||||
|
||||
# ASTM A194 (너트용 강재)
|
||||
if any(pattern in desc_upper for pattern in ["A194", "ASTM A194"]):
|
||||
grade = "UNKNOWN"
|
||||
if "GR 2H" in desc_upper or " 2H" in desc_upper or "/2H" in desc_upper:
|
||||
grade = "2H"
|
||||
elif "GR 8" in desc_upper or " 8" in desc_upper:
|
||||
grade = "8"
|
||||
|
||||
return {
|
||||
"standard": "ASTM A194",
|
||||
"grade": grade if grade != "UNKNOWN" else "ASTM A194",
|
||||
"material_type": "ALLOY_STEEL" if "2H" in grade else "STAINLESS_STEEL",
|
||||
"manufacturing": "FORGED",
|
||||
"confidence": 0.95,
|
||||
"evidence": ["ASTM_A194_NUT_MATERIAL"]
|
||||
}
|
||||
|
||||
# ASTM A320 (저온용 볼트)
|
||||
if any(pattern in desc_upper for pattern in ["A320", "ASTM A320"]):
|
||||
grade = "UNKNOWN"
|
||||
if "L7" in desc_upper:
|
||||
grade = "L7"
|
||||
elif "L43" in desc_upper:
|
||||
grade = "L43"
|
||||
elif "B8M" in desc_upper:
|
||||
grade = "B8M"
|
||||
|
||||
return {
|
||||
"standard": "ASTM A320",
|
||||
"grade": grade if grade != "UNKNOWN" else "ASTM A320",
|
||||
"material_type": "LOW_TEMP_STEEL",
|
||||
"manufacturing": "FORGED",
|
||||
"confidence": 0.95,
|
||||
"evidence": ["ASTM_A320_LOW_TEMP_BOLT"]
|
||||
}
|
||||
|
||||
# ASTM A325 (구조용 볼트)
|
||||
if any(pattern in desc_upper for pattern in ["A325", "ASTM A325"]):
|
||||
return {
|
||||
"standard": "ASTM A325",
|
||||
"grade": "ASTM A325",
|
||||
"material_type": "STRUCTURAL_STEEL",
|
||||
"manufacturing": "HEAT_TREATED",
|
||||
"confidence": 0.95,
|
||||
"evidence": ["ASTM_A325_STRUCTURAL_BOLT"]
|
||||
}
|
||||
|
||||
# ASTM A490 (고강도 구조용 볼트)
|
||||
if any(pattern in desc_upper for pattern in ["A490", "ASTM A490"]):
|
||||
return {
|
||||
"standard": "ASTM A490",
|
||||
"grade": "ASTM A490",
|
||||
"material_type": "HIGH_STRENGTH_STEEL",
|
||||
"manufacturing": "HEAT_TREATED",
|
||||
"confidence": 0.95,
|
||||
"evidence": ["ASTM_A490_HIGH_STRENGTH_BOLT"]
|
||||
}
|
||||
|
||||
# DIN 934 (DIN 너트)
|
||||
if any(pattern in desc_upper for pattern in ["DIN 934", "DIN934"]):
|
||||
return {
|
||||
"standard": "DIN 934",
|
||||
"grade": "DIN 934",
|
||||
"material_type": "CARBON_STEEL",
|
||||
"manufacturing": "FORGED",
|
||||
"confidence": 0.90,
|
||||
"evidence": ["DIN_934_NUT"]
|
||||
}
|
||||
|
||||
# ISO 4762 (소켓 헤드 캡 스크류)
|
||||
if any(pattern in desc_upper for pattern in ["ISO 4762", "ISO4762", "DIN 912", "DIN912"]):
|
||||
return {
|
||||
"standard": "ISO 4762",
|
||||
"grade": "ISO 4762",
|
||||
"material_type": "ALLOY_STEEL",
|
||||
"manufacturing": "HEAT_TREATED",
|
||||
"confidence": 0.90,
|
||||
"evidence": ["ISO_4762_SOCKET_SCREW"]
|
||||
}
|
||||
|
||||
# 기본 재질 분류기 호출 (materials_schema 문제가 있어도 우회)
|
||||
try:
|
||||
return classify_material(description)
|
||||
except:
|
||||
# materials_schema에 문제가 있으면 기본값 반환
|
||||
return {
|
||||
"standard": "UNKNOWN",
|
||||
"grade": "UNKNOWN",
|
||||
"material_type": "UNKNOWN",
|
||||
"manufacturing": "UNKNOWN",
|
||||
"confidence": 0.0,
|
||||
"evidence": ["MATERIAL_SCHEMA_ERROR"]
|
||||
}
|
||||
|
||||
# ========== 볼트 타입별 분류 ==========
|
||||
BOLT_TYPES = {
|
||||
"HEX_BOLT": {
|
||||
@@ -26,16 +145,16 @@ BOLT_TYPES = {
|
||||
},
|
||||
|
||||
"STUD_BOLT": {
|
||||
"dat_file_patterns": ["STUD_", "STUD_BOLT"],
|
||||
"description_keywords": ["STUD BOLT", "STUD", "스터드볼트", "전나사"],
|
||||
"dat_file_patterns": ["STUD_", "STUD_BOLT", "_TK", "BLT_"],
|
||||
"description_keywords": ["STUD BOLT", "STUD", "스터드볼트", "전나사", "BLT"],
|
||||
"characteristics": "양끝 나사 스터드",
|
||||
"applications": "플랜지 체결용",
|
||||
"head_type": "NONE"
|
||||
},
|
||||
|
||||
"FLANGE_BOLT": {
|
||||
"dat_file_patterns": ["FLG_BOLT", "FLANGE_BOLT"],
|
||||
"description_keywords": ["FLANGE BOLT", "플랜지볼트"],
|
||||
"dat_file_patterns": ["FLG_BOLT", "FLANGE_BOLT", "BLT_150", "BLT_300", "BLT_600"],
|
||||
"description_keywords": ["FLANGE BOLT", "플랜지볼트", "150LB", "300LB", "600LB"],
|
||||
"characteristics": "플랜지 전용 볼트",
|
||||
"applications": "플랜지 체결 전용",
|
||||
"head_type": "HEXAGON"
|
||||
@@ -183,7 +302,7 @@ BOLT_GRADES = {
|
||||
}
|
||||
}
|
||||
|
||||
def classify_bolt(dat_file: str, description: str, main_nom: str, length: float = None) -> Dict:
|
||||
def classify_bolt(dat_file: str, description: str, main_nom: str, length: Optional[float] = None) -> Dict:
|
||||
"""
|
||||
완전한 BOLT 분류
|
||||
|
||||
@@ -196,8 +315,10 @@ def classify_bolt(dat_file: str, description: str, main_nom: str, length: float
|
||||
완전한 볼트 분류 결과
|
||||
"""
|
||||
|
||||
# 1. 재질 분류 (공통 모듈 사용)
|
||||
material_result = classify_material(description)
|
||||
|
||||
|
||||
# 1. 재질 분류 (볼트 전용 버전)
|
||||
material_result = classify_bolt_material(description)
|
||||
|
||||
# 2. 체결재 타입 분류 (볼트/너트/와셔)
|
||||
fastener_category = classify_fastener_category(dat_file, description)
|
||||
@@ -282,7 +403,7 @@ def classify_fastener_category(dat_file: str, description: str) -> Dict:
|
||||
combined_text = f"{dat_upper} {desc_upper}"
|
||||
|
||||
# 볼트 키워드
|
||||
bolt_keywords = ["BOLT", "SCREW", "STUD", "볼트", "나사", "스크류"]
|
||||
bolt_keywords = ["BOLT", "SCREW", "STUD", "BLT", "볼트", "나사", "스크류", "A193", "A194", "A320", "A325", "A490"]
|
||||
if any(keyword in combined_text for keyword in bolt_keywords):
|
||||
return {
|
||||
"category": "BOLT",
|
||||
@@ -308,11 +429,30 @@ def classify_fastener_category(dat_file: str, description: str) -> Dict:
|
||||
"evidence": ["WASHER_KEYWORDS"]
|
||||
}
|
||||
|
||||
# 기본값: BOLT
|
||||
|
||||
# 볼트가 아닌 것 같은 키워드들 체크
|
||||
non_bolt_keywords = [
|
||||
"PIPE", "TUBE", "파이프", "배관", # 파이프
|
||||
"ELBOW", "TEE", "REDUCER", "CAP", "엘보", "티", "리듀서", # 피팅
|
||||
"VALVE", "GATE", "BALL", "GLOBE", "CHECK", "밸브", # 밸브
|
||||
"FLANGE", "FLG", "플랜지", # 플랜지
|
||||
"GASKET", "GASK", "가스켓", # 가스켓
|
||||
"GAUGE", "METER", "TRANSMITTER", "INDICATOR", "SENSOR", "계기", "게이지", # 계기
|
||||
"THERMOWELL", "ORIFICE", "MANOMETER" # 특수 계기
|
||||
]
|
||||
|
||||
if any(keyword in combined_text for keyword in non_bolt_keywords):
|
||||
return {
|
||||
"category": "UNKNOWN",
|
||||
"confidence": 0.1,
|
||||
"evidence": ["NON_BOLT_KEYWORDS_DETECTED"]
|
||||
}
|
||||
|
||||
# 기본값: BOLT (하지만 낮은 신뢰도)
|
||||
return {
|
||||
"category": "BOLT",
|
||||
"confidence": 0.6,
|
||||
"evidence": ["DEFAULT_BOLT"]
|
||||
"confidence": 0.1, # 0.6에서 0.1로 낮춤
|
||||
"evidence": ["DEFAULT_BOLT_LOW_CONFIDENCE"]
|
||||
}
|
||||
|
||||
def classify_specific_fastener_type(dat_file: str, description: str,
|
||||
@@ -429,6 +569,8 @@ def extract_bolt_dimensions(main_nom: str, description: str) -> Dict:
|
||||
|
||||
# 길이 정보 추출
|
||||
length_patterns = [
|
||||
r'(\d+(?:\.\d+)?)\s*LG', # 70.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',
|
||||
@@ -513,19 +655,41 @@ def classify_bolt_grade(description: str, thread_result: Dict) -> Dict:
|
||||
}
|
||||
|
||||
def calculate_bolt_confidence(confidence_scores: Dict) -> float:
|
||||
"""볼트 분류 전체 신뢰도 계산"""
|
||||
"""볼트 분류 전체 신뢰도 계산 (개선된 버전)"""
|
||||
|
||||
scores = [score for score in confidence_scores.values() if score > 0]
|
||||
# 기본 점수들
|
||||
material_conf = confidence_scores.get("material", 0)
|
||||
fastener_type_conf = confidence_scores.get("fastener_type", 0)
|
||||
thread_conf = confidence_scores.get("thread", 0)
|
||||
grade_conf = confidence_scores.get("grade", 0)
|
||||
|
||||
if not scores:
|
||||
return 0.0
|
||||
# 체결재 카테고리 신뢰도 (BOLT인지 확실한가?)
|
||||
fastener_category_conf = confidence_scores.get("fastener_category", 0)
|
||||
|
||||
# 가중 평균
|
||||
# 볼트 확신도 보너스
|
||||
bolt_certainty_bonus = 0.0
|
||||
|
||||
# 1. 체결재 카테고리가 BOLT이고 신뢰도가 높으면 보너스
|
||||
if fastener_category_conf >= 0.8:
|
||||
bolt_certainty_bonus += 0.2
|
||||
|
||||
# 2. 피팅 타입이 명확하게 인식되면 보너스
|
||||
if fastener_type_conf >= 0.8:
|
||||
bolt_certainty_bonus += 0.1
|
||||
|
||||
# 3. ASTM 볼트 재질이 인식되면 큰 보너스
|
||||
if material_conf >= 0.8:
|
||||
bolt_certainty_bonus += 0.2
|
||||
elif material_conf >= 0.5:
|
||||
bolt_certainty_bonus += 0.1
|
||||
|
||||
# 기본 가중 평균 (재질 비중을 낮추고 체결재 타입 비중 증가)
|
||||
weights = {
|
||||
"material": 0.2,
|
||||
"fastener_type": 0.4,
|
||||
"thread": 0.3,
|
||||
"grade": 0.1
|
||||
"fastener_category": 0.3, # 새로 추가
|
||||
"material": 0.1, # 낮춤 (0.2 -> 0.1)
|
||||
"fastener_type": 0.4, # 유지
|
||||
"thread": 0.15, # 낮춤 (0.3 -> 0.15)
|
||||
"grade": 0.05 # 낮춤 (0.1 -> 0.05)
|
||||
}
|
||||
|
||||
weighted_sum = sum(
|
||||
@@ -533,7 +697,11 @@ def calculate_bolt_confidence(confidence_scores: Dict) -> float:
|
||||
for key, weight in weights.items()
|
||||
)
|
||||
|
||||
return round(weighted_sum, 2)
|
||||
# 최종 신뢰도 = 기본 가중평균 + 보너스
|
||||
final_confidence = weighted_sum + bolt_certainty_bonus
|
||||
|
||||
# 최대값 1.0으로 제한
|
||||
return round(min(final_confidence, 1.0), 2)
|
||||
|
||||
# ========== 특수 기능들 ==========
|
||||
|
||||
|
||||
@@ -225,6 +225,9 @@ def classify_fitting(dat_file: str, description: str, main_nom: str,
|
||||
# 4. 압력 등급 분류
|
||||
pressure_result = classify_pressure_rating(dat_file, description)
|
||||
|
||||
# 4.5. 스케줄 분류 (니플 등에 중요)
|
||||
schedule_result = classify_fitting_schedule(description)
|
||||
|
||||
# 5. 제작 방법 추정
|
||||
manufacturing_result = determine_fitting_manufacturing(
|
||||
material_result, connection_result, pressure_result, main_nom
|
||||
@@ -279,6 +282,14 @@ def classify_fitting(dat_file: str, description: str, main_nom: str,
|
||||
"requires_two_sizes": fitting_type_result.get('requires_two_sizes', False)
|
||||
},
|
||||
|
||||
"schedule_info": {
|
||||
"schedule": schedule_result.get('schedule', 'UNKNOWN'),
|
||||
"schedule_number": schedule_result.get('schedule_number', ''),
|
||||
"wall_thickness": schedule_result.get('wall_thickness', ''),
|
||||
"pressure_class": schedule_result.get('pressure_class', ''),
|
||||
"confidence": schedule_result.get('confidence', 0.0)
|
||||
},
|
||||
|
||||
# 전체 신뢰도
|
||||
"overall_confidence": calculate_fitting_confidence({
|
||||
"material": material_result.get('confidence', 0),
|
||||
@@ -615,3 +626,51 @@ def get_fitting_purchase_info(fitting_result: Dict) -> Dict:
|
||||
"purchase_category": f"{fitting_type} {connection} {pressure}",
|
||||
"manufacturing_note": fitting_result["manufacturing"]["characteristics"]
|
||||
}
|
||||
|
||||
def classify_fitting_schedule(description: str) -> Dict:
|
||||
"""피팅 스케줄 분류 (특히 니플용)"""
|
||||
|
||||
desc_upper = description.upper()
|
||||
|
||||
# 스케줄 패턴 매칭
|
||||
schedule_patterns = [
|
||||
r'SCH\s*(\d+)',
|
||||
r'SCHEDULE\s*(\d+)',
|
||||
r'스케줄\s*(\d+)'
|
||||
]
|
||||
|
||||
for pattern in schedule_patterns:
|
||||
match = re.search(pattern, desc_upper)
|
||||
if match:
|
||||
schedule_number = match.group(1)
|
||||
schedule = f"SCH {schedule_number}"
|
||||
|
||||
# 일반적인 스케줄 정보
|
||||
common_schedules = {
|
||||
"10": {"wall": "얇음", "pressure": "저압"},
|
||||
"20": {"wall": "얇음", "pressure": "저압"},
|
||||
"40": {"wall": "표준", "pressure": "중압"},
|
||||
"80": {"wall": "두꺼움", "pressure": "고압"},
|
||||
"120": {"wall": "매우 두꺼움", "pressure": "고압"},
|
||||
"160": {"wall": "매우 두꺼움", "pressure": "초고압"}
|
||||
}
|
||||
|
||||
schedule_info = common_schedules.get(schedule_number, {"wall": "비표준", "pressure": "확인 필요"})
|
||||
|
||||
return {
|
||||
"schedule": schedule,
|
||||
"schedule_number": schedule_number,
|
||||
"wall_thickness": schedule_info["wall"],
|
||||
"pressure_class": schedule_info["pressure"],
|
||||
"confidence": 0.95,
|
||||
"matched_pattern": pattern
|
||||
}
|
||||
|
||||
return {
|
||||
"schedule": "UNKNOWN",
|
||||
"schedule_number": "",
|
||||
"wall_thickness": "",
|
||||
"pressure_class": "",
|
||||
"confidence": 0.0,
|
||||
"matched_pattern": ""
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ def classify_flange(dat_file: str, description: str, main_nom: str,
|
||||
dat_upper = dat_file.upper()
|
||||
|
||||
# 1. 명칭 우선 확인 (플랜지 키워드가 있으면 플랜지)
|
||||
flange_keywords = ['FLG', 'FLANGE', '플랜지']
|
||||
flange_keywords = ['FLG', 'FLANGE', '플랜지', 'ORIFICE', 'SPECTACLE', 'PADDLE', 'SPACER']
|
||||
is_flange = any(keyword in desc_upper or keyword in dat_upper for keyword in flange_keywords)
|
||||
|
||||
if not is_flange:
|
||||
|
||||
@@ -205,7 +205,8 @@ def classify_gasket(dat_file: str, description: str, main_nom: str, length: floa
|
||||
"material": gasket_material_result.get('material', 'UNKNOWN'),
|
||||
"characteristics": gasket_material_result.get('characteristics', ''),
|
||||
"temperature_range": gasket_material_result.get('temperature_range', ''),
|
||||
"confidence": gasket_material_result.get('confidence', 0.0)
|
||||
"confidence": gasket_material_result.get('confidence', 0.0),
|
||||
"swg_details": gasket_material_result.get('swg_details', {})
|
||||
},
|
||||
|
||||
# 가스켓 분류 정보
|
||||
@@ -294,16 +295,72 @@ def classify_gasket_type(dat_file: str, description: str) -> Dict:
|
||||
"applications": ""
|
||||
}
|
||||
|
||||
def parse_swg_details(description: str) -> Dict:
|
||||
"""SWG (Spiral Wound Gasket) 상세 정보 파싱"""
|
||||
|
||||
desc_upper = description.upper()
|
||||
result = {
|
||||
"face_type": "UNKNOWN",
|
||||
"outer_ring": "UNKNOWN",
|
||||
"filler": "UNKNOWN",
|
||||
"inner_ring": "UNKNOWN",
|
||||
"thickness": None,
|
||||
"detailed_construction": "",
|
||||
"confidence": 0.0
|
||||
}
|
||||
|
||||
# H/F/I/O 패턴 파싱 (Head/Face/Inner/Outer)
|
||||
hfio_pattern = r'H/F/I/O|HFIO'
|
||||
if re.search(hfio_pattern, desc_upper):
|
||||
result["face_type"] = "H/F/I/O"
|
||||
result["confidence"] += 0.3
|
||||
|
||||
# 재질 구성 파싱 (SS304/GRAPHITE/CS/CS)
|
||||
# H/F/I/O 다음에 나오는 재질 구성을 찾음
|
||||
material_pattern = r'H/F/I/O\s+([A-Z0-9]+)/([A-Z]+)/([A-Z0-9]+)/([A-Z0-9]+)'
|
||||
material_match = re.search(material_pattern, desc_upper)
|
||||
if material_match:
|
||||
result["outer_ring"] = material_match.group(1) # SS304
|
||||
result["filler"] = material_match.group(2) # GRAPHITE
|
||||
result["inner_ring"] = material_match.group(3) # CS
|
||||
# 네 번째는 보통 outer ring 반복
|
||||
result["detailed_construction"] = f"{material_match.group(1)}/{material_match.group(2)}/{material_match.group(3)}/{material_match.group(4)}"
|
||||
result["confidence"] += 0.4
|
||||
else:
|
||||
# H/F/I/O 없이 재질만 있는 경우
|
||||
material_pattern_simple = r'([A-Z0-9]+)/([A-Z]+)/([A-Z0-9]+)/([A-Z0-9]+)'
|
||||
material_match = re.search(material_pattern_simple, desc_upper)
|
||||
if material_match:
|
||||
result["outer_ring"] = material_match.group(1)
|
||||
result["filler"] = material_match.group(2)
|
||||
result["inner_ring"] = material_match.group(3)
|
||||
result["detailed_construction"] = f"{material_match.group(1)}/{material_match.group(2)}/{material_match.group(3)}/{material_match.group(4)}"
|
||||
result["confidence"] += 0.3
|
||||
|
||||
# 두께 파싱 (4.5mm)
|
||||
thickness_pattern = r'(\d+(?:\.\d+)?)\s*MM'
|
||||
thickness_match = re.search(thickness_pattern, desc_upper)
|
||||
if thickness_match:
|
||||
result["thickness"] = float(thickness_match.group(1))
|
||||
result["confidence"] += 0.3
|
||||
|
||||
return result
|
||||
|
||||
def classify_gasket_material(description: str) -> Dict:
|
||||
"""가스켓 전용 재질 분류"""
|
||||
"""가스켓 전용 재질 분류 (SWG 상세 정보 포함)"""
|
||||
|
||||
desc_upper = description.upper()
|
||||
|
||||
# 가스켓 전용 재질 확인
|
||||
# SWG 상세 정보 파싱
|
||||
swg_details = None
|
||||
if "SWG" in desc_upper or "SPIRAL WOUND" in desc_upper:
|
||||
swg_details = parse_swg_details(description)
|
||||
|
||||
# 기본 가스켓 재질 확인
|
||||
for material_type, material_data in GASKET_MATERIALS.items():
|
||||
for keyword in material_data["keywords"]:
|
||||
if keyword in desc_upper:
|
||||
return {
|
||||
result = {
|
||||
"material": material_type,
|
||||
"characteristics": material_data["characteristics"],
|
||||
"temperature_range": material_data["temperature_range"],
|
||||
@@ -311,6 +368,13 @@ def classify_gasket_material(description: str) -> Dict:
|
||||
"matched_keyword": keyword,
|
||||
"applications": material_data["applications"]
|
||||
}
|
||||
|
||||
# SWG 상세 정보 추가
|
||||
if swg_details and swg_details["confidence"] > 0:
|
||||
result["swg_details"] = swg_details
|
||||
result["confidence"] = min(0.95, result["confidence"] + swg_details["confidence"] * 0.1)
|
||||
|
||||
return result
|
||||
|
||||
# 일반 재질 키워드 확인
|
||||
if any(keyword in desc_upper for keyword in ["RUBBER", "고무"]):
|
||||
|
||||
@@ -46,7 +46,7 @@ INSTRUMENT_TYPES = {
|
||||
}
|
||||
}
|
||||
|
||||
def classify_instrument(dat_file: str, description: str, main_nom: str, length: float = None) -> Dict:
|
||||
def classify_instrument(dat_file: str, description: str, main_nom: str, length: Optional[float] = None) -> Dict:
|
||||
"""
|
||||
간단한 INSTRUMENT 분류
|
||||
|
||||
@@ -59,16 +59,62 @@ def classify_instrument(dat_file: str, description: str, main_nom: str, length:
|
||||
간단한 계기 분류 결과
|
||||
"""
|
||||
|
||||
# 1. 재질 분류 (공통 모듈)
|
||||
# 1. 먼저 계기인지 확인 (계기 키워드가 있어야 함)
|
||||
desc_upper = description.upper()
|
||||
dat_upper = dat_file.upper()
|
||||
combined_text = f"{dat_upper} {desc_upper}"
|
||||
|
||||
# 계기 관련 키워드 확인
|
||||
instrument_keywords = [
|
||||
"GAUGE", "METER", "TRANSMITTER", "INDICATOR", "SENSOR",
|
||||
"THERMOMETER", "MANOMETER", "ROTAMETER", "THERMOWELL",
|
||||
"ORIFICE PLATE", "DISPLAY", "4-20MA", "4-20 MA",
|
||||
"압력계", "온도계", "유량계", "액위계", "게이지", "계기",
|
||||
"트랜스미터", "지시계", "센서"
|
||||
]
|
||||
|
||||
# 계기가 아닌 것들의 키워드
|
||||
non_instrument_keywords = [
|
||||
"BOLT", "SCREW", "STUD", "NUT", "WASHER", "볼트", "나사", "너트", "와셔",
|
||||
"PIPE", "TUBE", "파이프", "배관",
|
||||
"ELBOW", "TEE", "REDUCER", "CAP", "엘보", "티", "리듀서",
|
||||
"VALVE", "GATE", "BALL", "GLOBE", "CHECK", "밸브",
|
||||
"FLANGE", "FLG", "플랜지",
|
||||
"GASKET", "GASK", "가스켓"
|
||||
]
|
||||
|
||||
# 계기가 아닌 키워드가 있으면 거부
|
||||
if any(keyword in combined_text for keyword in non_instrument_keywords):
|
||||
return {
|
||||
"category": "UNKNOWN",
|
||||
"overall_confidence": 0.1,
|
||||
"reason": "NON_INSTRUMENT_KEYWORDS_DETECTED"
|
||||
}
|
||||
|
||||
# 계기 키워드가 없으면 거부
|
||||
has_instrument_keyword = any(keyword in combined_text for keyword in instrument_keywords)
|
||||
if not has_instrument_keyword:
|
||||
return {
|
||||
"category": "UNKNOWN",
|
||||
"overall_confidence": 0.1,
|
||||
"reason": "NO_INSTRUMENT_KEYWORDS_FOUND"
|
||||
}
|
||||
|
||||
# 2. 재질 분류 (공통 모듈)
|
||||
material_result = classify_material(description)
|
||||
|
||||
# 2. 계기 타입 분류
|
||||
# 3. 계기 타입 분류
|
||||
instrument_type_result = classify_instrument_type(dat_file, description)
|
||||
|
||||
# 3. 측정 범위 추출 (있다면)
|
||||
# 4. 측정 범위 추출 (있다면)
|
||||
measurement_range = extract_measurement_range(description)
|
||||
|
||||
# 4. 최종 결과
|
||||
# 5. 전체 신뢰도 계산
|
||||
base_confidence = 0.8 if has_instrument_keyword else 0.1
|
||||
instrument_confidence = instrument_type_result.get('confidence', 0.0)
|
||||
overall_confidence = (base_confidence + instrument_confidence) / 2
|
||||
|
||||
# 6. 최종 결과
|
||||
return {
|
||||
"category": "INSTRUMENT",
|
||||
|
||||
@@ -105,11 +151,8 @@ def classify_instrument(dat_file: str, description: str, main_nom: str, length:
|
||||
"note": "사양서 확인 후 주문"
|
||||
},
|
||||
|
||||
# 간단한 신뢰도
|
||||
"overall_confidence": calculate_simple_confidence([
|
||||
material_result.get('confidence', 0),
|
||||
instrument_type_result.get('confidence', 0)
|
||||
])
|
||||
# 전체 신뢰도
|
||||
"overall_confidence": overall_confidence
|
||||
}
|
||||
|
||||
def classify_instrument_type(dat_file: str, description: str) -> Dict:
|
||||
|
||||
@@ -87,18 +87,19 @@ MATERIAL_STANDARDS = {
|
||||
"manufacturing": "FORGED"
|
||||
}
|
||||
},
|
||||
"A105": {
|
||||
"patterns": [
|
||||
r"ASTM\s+A105(?:\s+(?:GR\s*)?([ABC]))?",
|
||||
r"A105(?:\s+(?:GR\s*)?([ABC]))?",
|
||||
r"ASME\s+SA105"
|
||||
],
|
||||
"description": "탄소강 단조품",
|
||||
"composition": "탄소강",
|
||||
"applications": "일반 압력용 단조품",
|
||||
"manufacturing": "FORGED",
|
||||
"pressure_rating": "150LB ~ 9000LB"
|
||||
}
|
||||
|
||||
"A105": {
|
||||
"patterns": [
|
||||
r"ASTM\s+A105(?:\s+(?:GR\s*)?([ABC]))?",
|
||||
r"A105(?:\s+(?:GR\s*)?([ABC]))?",
|
||||
r"ASME\s+SA105"
|
||||
],
|
||||
"description": "탄소강 단조품",
|
||||
"composition": "탄소강",
|
||||
"applications": "일반 압력용 단조품",
|
||||
"manufacturing": "FORGED",
|
||||
"pressure_rating": "150LB ~ 9000LB"
|
||||
}
|
||||
},
|
||||
|
||||
"WELDED_GRADES": {
|
||||
|
||||
Reference in New Issue
Block a user