257 lines
10 KiB
Python
257 lines
10 KiB
Python
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()
|