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}")