297 lines
11 KiB
Python
297 lines
11 KiB
Python
import json
|
||
import csv
|
||
import re
|
||
from collections import defaultdict
|
||
|
||
# Đọc file JSON - thông tin diện tích
|
||
json_file = r"c:\Users\tainl\Documents\testHM\data\data-hoanmy\workload_converter\output_files\Link LLV 2025 For Dev.json"
|
||
csv_input = r"c:\Users\tainl\Documents\testHM\data\data-hoanmy\buoc1.csv"
|
||
csv_output = r"c:\Users\tainl\Documents\testHM\data\data-hoanmy\buoc2_with_area_v3.csv"
|
||
|
||
print("Đang đọc dữ liệu...")
|
||
print("=" * 100)
|
||
|
||
# Đọc JSON
|
||
with open(json_file, 'r', encoding='utf-8') as f:
|
||
dien_tich_data = json.load(f)
|
||
|
||
# Tạo index theo mã địa điểm
|
||
area_index = defaultdict(list)
|
||
for record in dien_tich_data:
|
||
ma_dd = record.get('Mã địa điểm', '')
|
||
if ma_dd:
|
||
area_index[ma_dd].append(record)
|
||
|
||
print(f"✅ Đọc được {len(dien_tich_data)} records từ JSON")
|
||
print(f"✅ Có {len(area_index)} mã địa điểm duy nhất")
|
||
|
||
# Đọc CSV
|
||
with open(csv_input, 'r', encoding='utf-8-sig') as f:
|
||
reader = csv.DictReader(f)
|
||
csv_records = list(reader)
|
||
|
||
print(f"✅ Đọc được {len(csv_records)} records từ CSV")
|
||
|
||
# Lọc các record có mã tòa trong JSON
|
||
filtered_records = []
|
||
skipped_records = []
|
||
|
||
for record in csv_records:
|
||
ma_toa = record['ma_toa_nha']
|
||
if ma_toa in area_index:
|
||
filtered_records.append(record)
|
||
else:
|
||
skipped_records.append(ma_toa)
|
||
|
||
print(f"\n{'=' * 100}")
|
||
print(f"📊 LỌC DỮ LIỆU:")
|
||
print(f"{'=' * 100}")
|
||
print(f"Records giữ lại: {len(filtered_records)}")
|
||
print(f"Records bỏ qua: {len(skipped_records)}")
|
||
|
||
if skipped_records:
|
||
unique_skipped = set(skipped_records)
|
||
print(f"\n⚠️ Các mã tòa bị loại bỏ ({len(unique_skipped)} mã):")
|
||
for ma in list(unique_skipped)[:20]:
|
||
print(f" - {ma}")
|
||
|
||
# Hàm trích xuất tầng từ text
|
||
def extract_floors(text):
|
||
"""Trích xuất số tầng từ text"""
|
||
floors = set()
|
||
text = text.lower()
|
||
|
||
# Pattern: "tầng 1", "T2", "tầng 3-5", "tầng 1,2,3", "từ tầng 2-5"
|
||
patterns = [
|
||
r'từ\s+tầng\s+(\d+)\s*[-–]\s*(\d+)', # Range với "từ"
|
||
r'tầng\s+(\d+)\s*[-–đến]+\s*(\d+)', # Range
|
||
r'tầng\s+(\d+)', # Single
|
||
r't\s*(\d+)', # T2, T3
|
||
r'(\d+)\s*tầng', # 5 tầng
|
||
]
|
||
|
||
for pattern in patterns:
|
||
matches = re.findall(pattern, text)
|
||
for match in matches:
|
||
if isinstance(match, tuple) and len(match) == 2:
|
||
# Range case
|
||
try:
|
||
start, end = int(match[0]), int(match[1])
|
||
if start <= end and end - start <= 20: # Reasonable range
|
||
floors.update(range(start, end + 1))
|
||
except:
|
||
pass
|
||
else:
|
||
# Single floor
|
||
try:
|
||
num = int(match) if isinstance(match, str) else match
|
||
if 0 <= num <= 100: # Reasonable floor number
|
||
floors.add(num)
|
||
except:
|
||
pass
|
||
|
||
return sorted(floors)
|
||
|
||
# Hàm trích xuất loại khu vực
|
||
def extract_area_types(text):
|
||
"""Trích xuất các loại khu vực từ text"""
|
||
text = text.lower()
|
||
area_types = []
|
||
|
||
# Keywords mapping - thêm nhiều từ đồng nghĩa
|
||
keywords = {
|
||
'sảnh': ['sảnh', 'lobby', 'sanh'],
|
||
'hành lang': ['hành lang', 'hanh lang', 'hàng lang', 'hang lang', 'coridol'],
|
||
'wc': ['wc', 'nhà vệ sinh', 'toilet', 'vệ sinh', 've sinh', 'vs'],
|
||
'văn phòng': ['văn phòng', 'van phong', 'vp ', 'office'],
|
||
'phòng bệnh': ['phòng bệnh', 'phong benh', 'giường bệnh', 'giuong benh'],
|
||
'phòng': ['phòng', 'phong'],
|
||
'ngoại cảnh': ['ngoại cảnh', 'ngoai canh', 'sân', 'san', 'vườn', 'vuon', 'ngoài trời'],
|
||
'thang máy': ['thang máy', 'thang may', 'lift'],
|
||
'cầu thang': ['cầu thang', 'cau thang', 'thang bộ', 'tang bo'],
|
||
'nhà ăn': ['nhà ăn', 'nha an', 'phòng ăn', 'canteen'],
|
||
'phòng mổ': ['phòng mổ', 'phong mo', 'phẫu thuật', 'phau thuat'],
|
||
'phòng khám': ['phòng khám', 'phong kham', 'khám bệnh'],
|
||
'kho': ['kho', 'warehouse'],
|
||
'nhà máy': ['nhà máy', 'nha may', 'factory', 'xưởng', 'xuong'],
|
||
}
|
||
|
||
for area_type, patterns in keywords.items():
|
||
for pattern in patterns:
|
||
if pattern in text:
|
||
area_types.append(area_type)
|
||
break
|
||
|
||
return area_types
|
||
|
||
# Hàm tính diện tích
|
||
def calculate_area(ma_toa, khu_vuc_text):
|
||
"""Tính tổng diện tích dựa vào mã tòa và text khu vực"""
|
||
if ma_toa not in area_index:
|
||
return 0, "Không tìm thấy trong JSON"
|
||
|
||
# Trích xuất thông tin từ text
|
||
floors = extract_floors(khu_vuc_text)
|
||
area_types = extract_area_types(khu_vuc_text)
|
||
|
||
total_area = 0
|
||
details = []
|
||
|
||
# Lấy tất cả records của tòa nhà
|
||
toa_records = area_index[ma_toa]
|
||
|
||
# LOGIC CẢI THIỆN: Fallback thông minh
|
||
if floors and area_types:
|
||
# Trường hợp 1: Có CẢ tầng VÀ khu vực → Match chặt chẽ (AND)
|
||
for rec in toa_records:
|
||
tang_str = str(rec.get('Tầng', '')).lower()
|
||
khu_vuc_str = str(rec.get('Khu vực', '')).lower()
|
||
|
||
# Kiểm tra tầng
|
||
tang_match = False
|
||
for floor in floors:
|
||
if f'tầng {floor}' in tang_str or f't{floor}' in tang_str or str(floor) == tang_str:
|
||
tang_match = True
|
||
break
|
||
|
||
# Kiểm tra khu vực
|
||
khu_vuc_match = False
|
||
for area_type in area_types:
|
||
if area_type in khu_vuc_str or area_type in tang_str:
|
||
khu_vuc_match = True
|
||
break
|
||
|
||
if tang_match and khu_vuc_match:
|
||
area_added = 0
|
||
for key, value in rec.items():
|
||
if key.startswith('Sàn') and isinstance(value, (int, float)) and value > 0:
|
||
area_added += value
|
||
total_area += value
|
||
|
||
if area_added > 0:
|
||
details.append(f"{rec.get('Tầng', 'N/A')} - {rec.get('Khu vực', 'N/A')}: {area_added}m²")
|
||
|
||
if total_area > 0:
|
||
detail_str = " | ".join(details[:3]) if details else ""
|
||
return total_area, f"Match tầng+khu vực: {detail_str}" if detail_str else "Match tầng+khu vực"
|
||
|
||
# Trường hợp 2: CHỈ có tầng (không parse được khu vực) → Lấy TẤT CẢ diện tích của tầng đó
|
||
if floors and not area_types:
|
||
for rec in toa_records:
|
||
tang_str = str(rec.get('Tầng', '')).lower()
|
||
|
||
tang_match = False
|
||
for floor in floors:
|
||
if f'tầng {floor}' in tang_str or f't{floor}' in tang_str or str(floor) == tang_str:
|
||
tang_match = True
|
||
break
|
||
|
||
if tang_match:
|
||
area_added = 0
|
||
for key, value in rec.items():
|
||
if key.startswith('Sàn') and isinstance(value, (int, float)) and value > 0:
|
||
area_added += value
|
||
total_area += value
|
||
|
||
if area_added > 0:
|
||
details.append(f"{rec.get('Tầng', 'N/A')}: {area_added}m²")
|
||
|
||
if total_area > 0:
|
||
return total_area, f"Tầng {','.join(map(str, floors))}"
|
||
|
||
# Trường hợp 3: CHỈ có khu vực (không parse được tầng) → Lấy khu vực đó trên TẤT CẢ tầng
|
||
if not floors and area_types:
|
||
for rec in toa_records:
|
||
khu_vuc_str = str(rec.get('Khu vực', '')).lower()
|
||
|
||
khu_vuc_match = False
|
||
for area_type in area_types:
|
||
if area_type in khu_vuc_str:
|
||
khu_vuc_match = True
|
||
break
|
||
|
||
if khu_vuc_match:
|
||
area_added = 0
|
||
for key, value in rec.items():
|
||
if key.startswith('Sàn') and isinstance(value, (int, float)) and value > 0:
|
||
area_added += value
|
||
total_area += value
|
||
|
||
if area_added > 0:
|
||
details.append(f"{rec.get('Khu vực', 'N/A')}: {area_added}m²")
|
||
|
||
if total_area > 0:
|
||
return total_area, f"Khu vực: {','.join(area_types)}"
|
||
|
||
# Trường hợp 4: Không parse được GÌ → Tính tổng toàn bộ
|
||
for rec in toa_records:
|
||
for key, value in rec.items():
|
||
if key.startswith('Sàn') and isinstance(value, (int, float)) and value > 0:
|
||
total_area += value
|
||
|
||
return total_area, "Tổng toàn bộ tòa (không parse được)"
|
||
|
||
# Tính diện tích cho từng record
|
||
print(f"\n{'=' * 100}")
|
||
print(f"🔢 ĐANG TÍNH DIỆN TÍCH...")
|
||
print(f"{'=' * 100}")
|
||
|
||
results = []
|
||
stats = {
|
||
'co_dien_tich': 0,
|
||
'khong_dien_tich': 0,
|
||
'tong_dien_tich': 0
|
||
}
|
||
|
||
for idx, record in enumerate(filtered_records, 1):
|
||
if idx % 100 == 0:
|
||
print(f" Đã xử lý {idx}/{len(filtered_records)} records...")
|
||
|
||
ma_toa = record['ma_toa_nha']
|
||
khu_vuc = record['Khu_vuc_lam_sach']
|
||
|
||
area, detail = calculate_area(ma_toa, khu_vuc)
|
||
|
||
if area > 0:
|
||
stats['co_dien_tich'] += 1
|
||
stats['tong_dien_tich'] += area
|
||
else:
|
||
stats['khong_dien_tich'] += 1
|
||
|
||
results.append({
|
||
**record,
|
||
'dien_tich_m2': round(area, 2),
|
||
'chi_tiet_tinh': detail
|
||
})
|
||
|
||
# Xuất ra CSV
|
||
with open(csv_output, 'w', encoding='utf-8-sig', newline='') as f:
|
||
fieldnames = ['ma_toa_nha', 'Khu_vuc_lam_sach', 'gio', 'So_nhan_su', 'Ten_ca', 'dien_tich_m2', 'chi_tiet_tinh', 'all_task']
|
||
writer = csv.DictWriter(f, fieldnames=fieldnames)
|
||
|
||
writer.writeheader()
|
||
writer.writerows(results)
|
||
|
||
print(f"\n{'=' * 100}")
|
||
print(f"✅ ĐÃ TẠO FILE: {csv_output}")
|
||
print(f"{'=' * 100}")
|
||
|
||
print(f"\n📊 THỐNG KÊ:")
|
||
print(f" - Records đầu ra: {len(results)}")
|
||
print(f" - Records có diện tích > 0: {stats['co_dien_tich']} ({stats['co_dien_tich']*100//len(results)}%)")
|
||
print(f" - Records diện tích = 0: {stats['khong_dien_tich']} ({stats['khong_dien_tich']*100//len(results)}%)")
|
||
print(f" - Tổng diện tích: {stats['tong_dien_tich']:,.0f} m²")
|
||
|
||
# Hiển thị mẫu
|
||
print(f"\n📝 MẪU DỮ LIỆU (10 records đầu):")
|
||
for idx, rec in enumerate(results[:10], 1):
|
||
print(f"\n{idx}. Mã: {rec['ma_toa_nha']} | DT: {rec['dien_tich_m2']} m²")
|
||
print(f" Khu vực: {rec['Khu_vuc_lam_sach'][:60]}...")
|
||
print(f" Chi tiết: {rec['chi_tiet_tinh'][:80]}...")
|
||
|
||
print(f"\n{'=' * 100}")
|
||
print(f"💡 LƯU Ý:")
|
||
print(f" - Logic phân tích text còn đơn giản, có thể chưa chính xác 100%")
|
||
print(f" - Cần review các records có diện tích = 0 để cải thiện logic")
|
||
print(f"{'=' * 100}")
|