import json import os import csv from collections import defaultdict def extract_valid_positions_from_json(file_path): """Trích xuất chỉ các vị trí hợp lệ từ file JSON""" try: with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) valid_records = [] skipped_positions = [] # Lấy mã tòa nhà từ tên file (bỏ đuôi .json) ma_toa_nha = os.path.basename(file_path).replace('.json', '') # Duyệt qua từng vị trí trong Thong_tin_nhan_su for nhan_su_item in data.get('Thong_tin_nhan_su', []): ten_vi_tri = nhan_su_item.get('Tên vị trí', '') khu_vuc = nhan_su_item.get('Khu Vực làm sạch', '') # Kiểm tra Ca_lam_viec ca_lam_viec_list = nhan_su_item.get('Ca_lam_viec', []) if not ca_lam_viec_list: skipped_positions.append(f"{ten_vi_tri}: Thiếu Ca_lam_viec") continue # Kiểm tra Chi_tiet_vi_tri chi_tiet = data.get('Chi_tiet_vi_tri', {}).get(ten_vi_tri, {}) if not chi_tiet: skipped_positions.append(f"{ten_vi_tri}: Không có trong Chi_tiet_vi_tri") continue # Kiểm tra Công việc thường cong_viec_thuong = chi_tiet.get('Công việc thường', []) if not cong_viec_thuong: skipped_positions.append(f"{ten_vi_tri}: Thiếu/rỗng Công việc thường") continue # Nếu vị trí hợp lệ, trích xuất dữ liệu for ca_item in ca_lam_viec_list: ten_ca = ca_item.get('Ten_ca', '') gio = ca_item.get('Gio', '') so_nhan_su = ca_item.get('So_nhan_su', 0) # Lấy all_task all_tasks = [] for cv in cong_viec_thuong: noi_dung = cv.get('Nội dung công việc', '').strip() if noi_dung: all_tasks.append(noi_dung) if not all_tasks: skipped_positions.append(f"{ten_vi_tri}: Không có nội dung công việc") continue all_task_str = ' / '.join(all_tasks) valid_records.append({ 'ma_toa_nha': ma_toa_nha, 'Khu_vuc_lam_sach': khu_vuc.strip(), 'gio': gio.strip(), 'So_nhan_su': so_nhan_su, 'Ten_ca': ten_ca.strip(), 'all_task': all_task_str, 'file_source': os.path.basename(file_path), 'vi_tri': ten_vi_tri }) return valid_records, skipped_positions except Exception as e: return [], [f"Lỗi: {e}"] def main(): success_folder = r"c:\Users\tainl\Documents\testHM\data\data-hoanmy\converters\output\sodonhansu\success" null_folder = r"c:\Users\tainl\Documents\testHM\data\data-hoanmy\converters\output\sodonhansu\null" output_csv = r"c:\Users\tainl\Documents\testHM\data\data-hoanmy\buoc1.csv" # Lấy danh sách file từ cả 2 folder success_files = [(os.path.join(success_folder, f), f, 'success') for f in os.listdir(success_folder) if f.endswith('.json')] null_files = [(os.path.join(null_folder, f), f, 'null') for f in os.listdir(null_folder) if f.endswith('.json')] all_file_paths = success_files + null_files print(f"Đang xử lý files từ 2 folders...") print(f" - Folder success: {len(success_files)} files") print(f" - Folder null: {len(null_files)} files") print(f" - Tổng: {len(all_file_paths)} files") print("=" * 100) # Trích xuất các vị trí hợp lệ từ tất cả các file all_valid_records = [] file_stats = [] total_skipped = 0 print(f"\nĐang trích xuất các vị trí hợp lệ...") for idx, (file_path, file_name, folder_type) in enumerate(all_file_paths, 1): if idx % 50 == 0: print(f" Đã xử lý {idx}/{len(all_file_paths)} files...") valid_records, skipped_positions = extract_valid_positions_from_json(file_path) if valid_records: all_valid_records.extend(valid_records) if skipped_positions: total_skipped += len(skipped_positions) file_stats.append({ 'file': file_name, 'folder': folder_type, 'valid': len(valid_records), 'skipped': len(skipped_positions), 'skipped_reasons': skipped_positions[:3] }) print(f"\n✅ Trích xuất được {len(all_valid_records)} vị trí hợp lệ từ {len(all_file_paths)} files") print(f"⚠️ Đã bỏ qua {total_skipped} vị trí không hợp lệ") # Thống kê theo folder success_records = [r for r in all_valid_records if r['file_source'] in [f[1] for f in success_files]] null_records = [r for r in all_valid_records if r['file_source'] in [f[1] for f in null_files]] print(f"\n📊 Chi tiết:") print(f" - Từ folder success: {len(success_records)} vị trí hợp lệ") print(f" - Từ folder null: {len(null_records)} vị trí hợp lệ") # Hiển thị một số file có vị trí bị bỏ qua if file_stats: print(f"\n📝 Ví dụ các file có vị trí bị bỏ qua (10 files đầu):") for stat in file_stats[:10]: print(f" - {stat['file']} ({stat['folder']}): {stat['valid']} hợp lệ, {stat['skipped']} bỏ qua") for reason in stat['skipped_reasons']: print(f" • {reason}") # Gộp các record có cùng MÃ TÒA NHÀ và KHU VỰC LÀM SẠCH print(f"\n{'=' * 100}") print(f"Đang gộp các record có cùng tòa nhà và khu vực...") grouped_data = defaultdict(lambda: { 'So_nhan_su': 0, 'all_task': [], 'gio': '', 'Ten_ca': '', 'ma_toa_nha': '', 'Khu_vuc_lam_sach': '' }) for record in all_valid_records: # Key = (mã tòa nhà, khu vực làm sạch) key = (record['ma_toa_nha'], record['Khu_vuc_lam_sach']) grouped_data[key]['So_nhan_su'] += record['So_nhan_su'] # Gộp all_task if record['all_task']: grouped_data[key]['all_task'].append(record['all_task']) # Lưu thông tin tòa nhà và khu vực grouped_data[key]['ma_toa_nha'] = record['ma_toa_nha'] grouped_data[key]['Khu_vuc_lam_sach'] = record['Khu_vuc_lam_sach'] # Lấy thông tin ca đầu tiên if not grouped_data[key]['gio']: grouped_data[key]['gio'] = record['gio'] grouped_data[key]['Ten_ca'] = record['Ten_ca'] # Tạo danh sách final và lọc các record có giờ null/rỗng final_records = [] skipped_null_gio = 0 for (ma_toa_nha, khu_vuc), info in grouped_data.items(): if not khu_vuc: continue # Loại bỏ các record có giờ rỗng/null if not info['gio'] or info['gio'].strip() == '': skipped_null_gio += 1 continue all_task_combined = ' / '.join(info['all_task']) final_records.append({ 'ma_toa_nha': info['ma_toa_nha'], 'Khu_vuc_lam_sach': info['Khu_vuc_lam_sach'], 'gio': info['gio'], 'So_nhan_su': info['So_nhan_su'], 'Ten_ca': info['Ten_ca'], 'all_task': all_task_combined }) # Sắp xếp theo Mã tòa nhà, rồi đến Khu vực final_records.sort(key=lambda x: (x['ma_toa_nha'], x['Khu_vuc_lam_sach'])) print(f"✅ Sau khi gộp: {len(final_records)} records duy nhất") if skipped_null_gio > 0: print(f"⚠️ Đã loại bỏ thêm {skipped_null_gio} records có giờ null/rỗng") # Xuất ra CSV with open(output_csv, 'w', encoding='utf-8-sig', newline='') as f: fieldnames = ['ma_toa_nha', 'Khu_vuc_lam_sach', 'gio', 'So_nhan_su', 'Ten_ca', 'all_task'] writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() writer.writerows(final_records) print(f"\n{'=' * 100}") print(f"✅ ĐÃ TẠO FILE: {output_csv}") print(f"{'=' * 100}") print(f"\n📊 TỔNG KẾT:") print(f" - Tổng files xử lý: {len(all_file_paths)} (success: {len(success_files)}, null: {len(null_files)})") print(f" - Vị trí hợp lệ: {len(all_valid_records)} (success: {len(success_records)}, null: {len(null_records)})") print(f" - Vị trí bị bỏ qua: {total_skipped}") print(f" - Records sau gộp: {len(final_records)}") print(f" - Số nhân sự tổng: {sum(r['So_nhan_su'] for r in final_records)}") # Hiển thị mẫu print(f"\n📝 MẪU DỮ LIỆU (5 records đầu):") for idx, record in enumerate(final_records[:5], 1): print(f"\n{idx}. Mã tòa nhà: {record['ma_toa_nha']}") print(f" Khu vực: {record['Khu_vuc_lam_sach'][:60]}...") print(f" Ca: {record['Ten_ca']} | Giờ: {record['gio']} | Số NS: {record['So_nhan_su']}") print(f" Tasks: {record['all_task'][:100]}...") # Kiểm tra vấn đề print(f"\n{'=' * 100}") print(f"🔍 KIỂM TRA VẤN ĐỀ:") empty_khu_vuc = [r for r in final_records if not r['Khu_vuc_lam_sach']] empty_gio = [r for r in final_records if not r['gio'] or r['gio'].strip() == ''] empty_tasks = [r for r in final_records if not r['all_task']] zero_nhan_su = [r for r in final_records if r['So_nhan_su'] == 0] empty_ma_toa = [r for r in final_records if not r['ma_toa_nha']] if empty_khu_vuc: print(f" ⚠️ {len(empty_khu_vuc)} records có Khu vực rỗng") if empty_gio: print(f" ⚠️ {len(empty_gio)} records có Giờ rỗng/null") if empty_tasks: print(f" ⚠️ {len(empty_tasks)} records có all_task rỗng") if zero_nhan_su: print(f" ⚠️ {len(zero_nhan_su)} records có số nhân sự = 0") if empty_ma_toa: print(f" ⚠️ {len(empty_ma_toa)} records có mã tòa nhà rỗng") if not (empty_khu_vuc or empty_gio or empty_tasks or zero_nhan_su or empty_ma_toa): print(f" ✅ Không phát hiện vấn đề về dữ liệu!") if __name__ == "__main__": main()