리비전 업로드 시 정확한 수량 차이분 계산 로직 구현
Some checks failed
SonarQube Analysis / SonarQube Scan (push) Has been cancelled

- 기존 자재와 새 자재의 수량을 비교하여 증가분만 저장
- Rev.0: 엘보 10개, Rev.1: 엘보 12개 → Rev.1에는 2개만 저장
- 완전 신규 자재는 전체 수량 저장
- 수량 감소/동일한 자재는 저장하지 않음
- 리비전별 정확한 차이분 관리 구현
This commit is contained in:
Hyungi Ahn
2025-09-09 12:03:47 +09:00
parent 83b90ef05c
commit 881fc13580
5 changed files with 579 additions and 99 deletions

View File

@@ -205,7 +205,11 @@ async def upload_file(
parsed_count = len(materials_data)
# 로그 제거
# 리비전 업로드인 경우만 자동 리비전 생성
# 신규 자재 카운트 초기화
new_materials_count = 0
existing_materials_descriptions = set()
# 리비전 업로드인 경우만 자동 리비전 생성 및 기존 자재 조회
if parent_file_id is not None:
# 로그 제거
# 부모 파일의 정보 조회
@@ -255,6 +259,29 @@ async def upload_file(
revision = "Rev.1"
print(f"첫 번째 리비전: {revision}")
# 부모 파일의 자재 목록 조회 (기존 자재 확인용 - 수량 포함)
existing_materials_query = text("""
SELECT original_description, size_spec, SUM(quantity) as total_quantity
FROM materials
WHERE file_id = :parent_file_id
GROUP BY original_description, size_spec
""")
existing_result = db.execute(existing_materials_query, {
"parent_file_id": parent_file_id
})
existing_materials_with_quantity = {}
for row in existing_result:
# 설명과 사이즈를 조합하여 유니크 키 생성
key = f"{row.original_description}|{row.size_spec or ''}"
existing_materials_descriptions.add(key)
existing_materials_with_quantity[key] = float(row.total_quantity or 0)
print(f"기존 자재 수: {len(existing_materials_descriptions)}")
if len(existing_materials_descriptions) > 0:
print(f"기존 자재 샘플 (처음 5개): {list(existing_materials_descriptions)[:5]}")
# 파일명을 부모와 동일하게 유지
file.filename = parent_file[0]
else:
@@ -351,7 +378,66 @@ async def upload_file(
})
materials_inserted += 1
# 리비전 업로드인 경우 차이분 계산
materials_diff = []
if parent_file_id is not None:
# 새 파일의 자재들을 수량별로 그룹화
new_materials_grouped = {}
for material_data in materials_to_classify:
description = material_data["original_description"]
size_spec = material_data["size_spec"]
quantity = float(material_data.get("quantity", 0))
material_key = f"{description}|{size_spec or ''}"
if material_key in new_materials_grouped:
new_materials_grouped[material_key]["quantity"] += quantity
new_materials_grouped[material_key]["items"].append(material_data)
else:
new_materials_grouped[material_key] = {
"quantity": quantity,
"items": [material_data],
"description": description,
"size_spec": size_spec
}
# 차이분 계산
for material_key, new_data in new_materials_grouped.items():
existing_quantity = existing_materials_with_quantity.get(material_key, 0)
new_quantity = new_data["quantity"]
if new_quantity > existing_quantity:
# 증가분이 있는 경우
diff_quantity = new_quantity - existing_quantity
print(f"차이분 발견: {new_data['description'][:50]}... (증가: {diff_quantity})")
# 증가분만큼 자재 데이터 생성
for item in new_data["items"]:
if diff_quantity <= 0:
break
item_quantity = float(item.get("quantity", 0))
if item_quantity <= diff_quantity:
# 이 아이템 전체를 포함
materials_diff.append(item)
diff_quantity -= item_quantity
new_materials_count += 1
else:
# 이 아이템의 일부만 포함
item_copy = item.copy()
item_copy["quantity"] = diff_quantity
materials_diff.append(item_copy)
new_materials_count += 1
break
else:
print(f"수량 감소/동일: {new_data['description'][:50]}... (기존:{existing_quantity} 새:{new_quantity})")
# 차이분만 처리하도록 materials_to_classify 교체
materials_to_classify = materials_diff
print(f"차이분 자재 개수: {len(materials_to_classify)}")
# 분류가 필요한 자재 처리
print(f"분류할 자재 총 개수: {len(materials_to_classify)}")
for material_data in materials_to_classify:
# 자재 타입 분류기 적용 (PIPE, FITTING, VALVE 등)
description = material_data["original_description"]
@@ -462,6 +548,8 @@ async def upload_file(
material_id = material_result.fetchone()[0]
materials_inserted += 1
# 리비전 업로드인 경우 신규 자재 카운트는 이미 위에서 처리됨
# PIPE 분류 결과인 경우 상세 정보 저장
if classification_result.get("category") == "PIPE":
print("PIPE 상세 정보 저장 시작")
@@ -1063,13 +1151,20 @@ async def upload_file(
print(f"활동 로그 기록 실패: {str(e)}")
# 로그 실패는 업로드 성공에 영향을 주지 않음
# 리비전 업로드인 경우 메시지 다르게 표시
if parent_file_id is not None:
message = f"리비전 업로드 성공! {new_materials_count}개의 신규 자재가 추가되었습니다."
else:
message = f"업로드 성공! {materials_inserted}개 자재가 분류되었습니다."
return {
"success": True,
"message": f"업로드 성공! {materials_inserted}개 자재가 분류되었습니다.",
"message": message,
"original_filename": file.filename,
"file_id": file_id,
"materials_count": materials_inserted,
"saved_materials_count": materials_inserted,
"new_materials_count": new_materials_count if parent_file_id is not None else None, # 신규 자재 수
"revision": revision, # 생성된 리비전 정보 추가
"uploaded_by": username, # 업로드한 사용자 정보 추가
"parsed_count": parsed_count
@@ -1948,37 +2043,45 @@ async def compare_revisions(
})
new_materials = new_result.fetchall()
# 자재 키 생성 함수
# 자재 키 생성 함수 (전체 수량 기준)
def create_material_key(material):
return f"{material.original_description}_{material.size_spec}_{material.material_grade}"
return f"{material.original_description}|{material.size_spec or ''}|{material.material_grade or ''}"
# 기존 자재를 딕셔너리로 변환
# 기존 자재를 딕셔너리로 변환 (수량 합산)
old_materials_dict = {}
for material in old_materials:
key = create_material_key(material)
old_materials_dict[key] = {
"original_description": material.original_description,
"quantity": float(material.quantity) if material.quantity else 0,
"unit": material.unit,
"size_spec": material.size_spec,
"material_grade": material.material_grade,
"classified_category": material.classified_category,
"classification_confidence": material.classification_confidence
}
if key in old_materials_dict:
# 동일한 자재가 있으면 수량 합산
old_materials_dict[key]["quantity"] += float(material.quantity) if material.quantity else 0
else:
old_materials_dict[key] = {
"original_description": material.original_description,
"quantity": float(material.quantity) if material.quantity else 0,
"unit": material.unit,
"size_spec": material.size_spec,
"material_grade": material.material_grade,
"classified_category": material.classified_category,
"classification_confidence": material.classification_confidence
}
# 새 자재를 딕셔너리로 변환
# 새 자재를 딕셔너리로 변환 (수량 합산)
new_materials_dict = {}
for material in new_materials:
key = create_material_key(material)
new_materials_dict[key] = {
"original_description": material.original_description,
"quantity": float(material.quantity) if material.quantity else 0,
"unit": material.unit,
"size_spec": material.size_spec,
"material_grade": material.material_grade,
"classified_category": material.classified_category,
"classification_confidence": material.classification_confidence
}
if key in new_materials_dict:
# 동일한 자재가 있으면 수량 합산
new_materials_dict[key]["quantity"] += float(material.quantity) if material.quantity else 0
else:
new_materials_dict[key] = {
"original_description": material.original_description,
"quantity": float(material.quantity) if material.quantity else 0,
"unit": material.unit,
"size_spec": material.size_spec,
"material_grade": material.material_grade,
"classified_category": material.classified_category,
"classification_confidence": material.classification_confidence
}
# 변경 사항 분석
all_keys = set(old_materials_dict.keys()) | set(new_materials_dict.keys())
@@ -2006,14 +2109,22 @@ async def compare_revisions(
"change_type": "added"
})
elif old_item and new_item:
# 수량 변경 확인
if old_item["quantity"] != new_item["quantity"]:
# 수량 변경 확인 (전체 수량 기준)
old_qty = old_item["quantity"]
new_qty = new_item["quantity"]
qty_diff = new_qty - old_qty
# 수량 차이가 있으면 변경된 것으로 간주 (소수점 오차 고려)
if abs(qty_diff) > 0.001:
change_type = "quantity_increased" if qty_diff > 0 else "quantity_decreased"
changed_items.append({
"key": key,
"old_item": old_item,
"new_item": new_item,
"quantity_change": new_item["quantity"] - old_item["quantity"],
"change_type": "quantity_changed"
"quantity_change": qty_diff,
"quantity_change_abs": abs(qty_diff),
"change_type": change_type,
"change_percentage": (qty_diff / old_qty * 100) if old_qty > 0 else 0
})
# 분류별 통계