From 310885ed8a02ff211dd2b69f5c59aafed1b9a43b Mon Sep 17 00:00:00 2001 From: user01 Date: Mon, 25 May 2026 01:11:35 +0900 Subject: [PATCH] =?UTF-8?q?=EC=84=B1=EB=B3=84=20=EC=BB=AC=EB=9F=BC=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80=20(=ED=8C=8C=EC=8B=B1/?= =?UTF-8?q?=ED=85=9C=ED=94=8C=EB=A6=BF/export)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 22 ++++++++++++++-------- sample_용산구.xlsx | Bin 6183 -> 6132 bytes 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index a21cee5..207ca8b 100644 --- a/app.py +++ b/app.py @@ -482,6 +482,7 @@ def process_excel_file(wb, filename, force_type=None): name_idx = name_idx if name_idx is not None else 0 dob_idx = find_header_idx(headers, ['생년월일', '생일']) or 3 phone_idx = find_header_idx(headers, ['연락처', '전화', '휴대폰']) or 4 + gender_idx = find_header_idx(headers, ['성별']) if addr_idx is None: addr_idx = 2 logger.warning(f' [주의] 주소 컬럼을 찾을 수 없어 기본 인덱스 {addr_idx} 사용') @@ -571,6 +572,7 @@ def process_excel_file(wb, filename, force_type=None): name_idx = name_idx if name_idx is not None else 0 dob_idx = find_header_idx(headers, ['생년월일', '생일']) or 3 phone_idx = find_header_idx(headers, ['연락처', '전화', '휴대폰']) or 4 + gender_idx = find_header_idx(headers, ['성별']) if addr_idx is None: addr_idx = 2 @@ -587,6 +589,7 @@ def process_excel_file(wb, filename, force_type=None): 'address': addr, 'dob': str(row[dob_idx] or '').strip(), 'phone': str(row[phone_idx] or '').strip(), + 'gender': str(row[gender_idx] or '').strip() if gender_idx is not None else '', 'lat': lat, 'lng': lng, }) else: @@ -821,10 +824,10 @@ def export(): if export_type == 'unassigned': ws = wb.active ws.title = '미배치 근로자' - ws.append(['이름', '희망사항', '주소', '생년월일', '연락처']) + ws.append(['이름', '희망사항', '주소', '생년월일', '연락처', '성별']) for w in t['workers']: if w['id'] not in t['assignments']: - ws.append([w['name'], w['hope'], w['address'], w['dob'], w['phone']]) + ws.append([w['name'], w['hope'], w['address'], w['dob'], w['phone'], w['gender']]) elif export_type == 'assigned': wp_filename = t.get('workplace_filename', '') @@ -844,6 +847,7 @@ def export(): name_col = find_header_idx(headers, ['성명', '이름']) dob_col = find_header_idx(headers, ['생년월일', '생일']) phone_col = find_header_idx(headers, ['연락처', '전화', '휴대폰']) + gender_col = find_header_idx(headers, ['성별']) addr_col = find_header_idx(headers, ['주소', '위치', '소재지']) wp_name_col = find_header_idx(headers, ['투표소']) if wp_name_col is None: @@ -865,6 +869,8 @@ def export(): cells[dob_col].value = w['dob'] if phone_col is not None and cells[phone_col].value is None: cells[phone_col].value = w['phone'] + if gender_col is not None and cells[gender_col].value is None: + cells[gender_col].value = w['gender'] if addr_col is not None and cells[addr_col].value is None: cells[addr_col].value = w['address'] used_names.add(w['id']) @@ -873,25 +879,25 @@ def export(): remaining = all_assigned - used_names if remaining: ws_extra = wb.create_sheet('추가 배치') - ws_extra.append(['이름', '연락처', '생년월일', '희망사항', '주소', '배치근무지', '근무지주소']) + ws_extra.append(['이름', '연락처', '생년월일', '희망사항', '주소', '성별', '배치근무지', '근무지주소']) for w in t['workers']: if w['id'] in remaining: a_id = t['assignments'].get(w['id']) wn = wp_lookup[a_id]['name'] if a_id in wp_lookup else '' wa = wp_lookup[a_id]['address'] if a_id in wp_lookup else '' - ws_extra.append([w['name'], w['phone'], w['dob'], w['hope'], w['address'], wn, wa]) + ws_extra.append([w['name'], w['phone'], w['dob'], w['hope'], w['address'], w['gender'], wn, wa]) else: # 템플릿 없으면 배치된 근로자만 기본 리스트 출력 ws1 = wb.active ws1.title = '배치 현황' - ws1.append(['이름', '연락처', '생년월일', '희망사항', '주소', '배치근무지', '근무지주소']) + ws1.append(['이름', '연락처', '생년월일', '희망사항', '주소', '성별', '배치근무지', '근무지주소']) for w in t['workers']: a_id = t['assignments'].get(w['id']) if not a_id: continue wn = wp_lookup[a_id]['name'] if a_id in wp_lookup else '' wa = wp_lookup[a_id]['address'] if a_id in wp_lookup else '' - ws1.append([w['name'], w['phone'], w['dob'], w['hope'], w['address'], wn, wa]) + ws1.append([w['name'], w['phone'], w['dob'], w['hope'], w['address'], w['gender'], wn, wa]) ws2 = wb.create_sheet('근무지별 배치') ws2.append(['근무지명', '주소', '배치인원', '배치된근무자']) addr_groups = {} @@ -909,12 +915,12 @@ def export(): else: # all (전체) ws1 = wb.active ws1.title = '배치 현황' - ws1.append(['이름', '연락처', '생년월일', '희망사항', '주소', '배치근무지', '근무지주소']) + ws1.append(['이름', '연락처', '생년월일', '희망사항', '주소', '성별', '배치근무지', '근무지주소']) for w in t['workers']: a_id = t['assignments'].get(w['id']) wn = wp_lookup[a_id]['name'] if a_id and a_id in wp_lookup else '' wa = wp_lookup[a_id]['address'] if a_id and a_id in wp_lookup else '' - ws1.append([w['name'], w['phone'], w['dob'], w['hope'], w['address'], wn, wa]) + ws1.append([w['name'], w['phone'], w['dob'], w['hope'], w['address'], w['gender'], wn, wa]) ws2 = wb.create_sheet('근무지별 배치') ws2.append(['근무지명', '주소', '배치인원', '배치된근무자']) addr_groups = {} diff --git a/sample_용산구.xlsx b/sample_용산구.xlsx index 530e981c6e06a337775708d7d06f0baae7573504..5ad8c260d69f45327957bac0321b6635427d5152 100644 GIT binary patch delta 3989 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 RZ$~Q3S12Eu4fD^F{{i<&&kz6r delta 4011 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 HzuNbIrEDuJ