import streamlit as st from openpyxl import Workbook from openpyxl.styles import Font, Alignment, Border, Side, PatternFill from openpyxl.utils import get_column_letter from io import BytesIO from datetime import date, time import sqlite3 import random st.set_page_config(page_title="방문목욕 수급자 상담일지 자동생성기", layout="wide") st.title("🛁 방문목욕 수급자 상담일지 자동생성기 v2") st.caption("상담영역 → 문제상황 → 원인 → 조치 → 급여반영 → 랜덤 템플릿 기반 엑셀 다운로드") st.warning("⚠️ **※ 본 생성기는 하루 2회 이용 한도 제한이 있습니다.**") DB_FILE = "usage.db" def init_db(): conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS usage_limit ( institution TEXT, user_id TEXT, use_date TEXT, count INTEGER, PRIMARY KEY (institution, user_id, use_date) ) """) conn.commit() conn.close() def get_today_usage(institution, user_id): if not institution or not user_id: return 0 init_db() today_str = date.today().isoformat() conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute( "SELECT count FROM usage_limit WHERE institution = ? AND user_id = ? AND use_date = ?", (institution.strip(), user_id.strip(), today_str) ) row = cursor.fetchone() conn.close() return row[0] if row else 0 def increase_today_usage(institution, user_id): if not institution or not user_id: return init_db() today_str = date.today().isoformat() conn = sqlite3.connect(DB_FILE) cursor = conn.cursor() cursor.execute( "SELECT count FROM usage_limit WHERE institution = ? AND user_id = ? AND use_date = ?", (institution.strip(), user_id.strip(), today_str) ) row = cursor.fetchone() if row: cursor.execute( "UPDATE usage_limit SET count = count + 1 WHERE institution = ? AND user_id = ? AND use_date = ?", (institution.strip(), user_id.strip(), today_str) ) else: cursor.execute( "INSERT INTO usage_limit (institution, user_id, use_date, count) VALUES (?, ?, ?, 1)", (institution.strip(), user_id.strip(), today_str) ) conn.commit() conn.close() if "generated_data" not in st.session_state: st.session_state.generated_data = None inst_val = st.session_state.get("institution", "").strip() user_val = st.session_state.get("user_id", "").strip() today_count = get_today_usage(inst_val, user_val) # Sidebar Usage Panel st.sidebar.markdown("### 📊 사용 한도 관리") if not inst_val or not user_val: st.sidebar.warning("⚠️ 기관명과 사용자 식별값을 입력해주세요.") else: st.sidebar.write(f"오늘 생성 횟수: **{today_count} / 2회**") if today_count >= 2: st.sidebar.error("❌ 생성 한도 초과 (2/2)") else: st.sidebar.success(f"✅ 생성 가능 (남은 횟수: {2 - today_count}회)") REQUIRED_ITEMS = [ "수급자 성명", "생년월일", "성별", "장기요양 등급", "계약일/시작일", "본인부담률", "상담 일시", "상담 구분", "상담 장소", "상담 방법", "상담 대상자 성명", "상담자와의 관계", "기관 담당자 직책 및 성명", "동석자", "상담 목적 및 사유", "신체 기능 상태 변화 및 낙상 위험", "인지 기능 및 심리 상태", "피부 상태 및 위생 상태", "목욕 차량/장비 안전 상태", "보호자의 요청 사항", "기관의 설명 및 안내 사항", "문제 해결 또는 제공된 정보 내용", "향후 급여 제공 계획", "급여 반영 여부", "사후 조치 및 모니터링 계획", "보호자 전달/공지사항", "상담자 서명", "대상자 또는 보호자 서명" ] # 공단 평가지표를 반영하여 안전/장비 및 만족도 영역 추가 AREA_DATA = { "목욕 거부/협조도": { "problems": ["목욕 거부", "탈의 거부", "물에 대한 공포심", "피부 접촉 예민", "피로감 호소", "샤워기 소리에 불안"], "causes": ["치매로 인한 인지 저하", "추위에 민감함", "과거 부정적 경험", "신체 노출 수치심", "피부 건조 및 가려움", "당일 컨디션 저하"], "actions": ["따뜻한 실내 온도(24~26도) 유지", "충분한 지지적 대화 및 라포 형성", "타월로 몸을 감싼 채 안정감 제공", "선호하는 음악/환경 조성", "간호사 및 보호자 특이사항 공유"], }, "피부 상태/발적/상처": { "problems": ["피부 건조 심화", "둔부/미골 발적", "가려움증(소양증) 호소", "피부 짓무름/백선", "미세 찰과상 발견", "각질 심화"], "causes": ["혈액 순환 저하", "장시간 와상/좌식 생활", "기저귀 장기 착용으로 인한 습기", "수분 섭취 부족", "노화로 인한 피부 장벽 약화"], "actions": ["목욕 직후 보습제 충분히 도포", "마찰 방지 및 가벼운 마사지 제공", "피부 상태 유심히 관찰 및 급여제공기록지 기재", "간호사/보호자 즉각 보고", "기저귀 교체 주기 단축 안내"], }, "체온/바이탈 변화": { "problems": ["목욕 전후 혈압 상승/저하", "목욕 후 한기/오한 호소", "미열 발생", "기립성 어지러움 호소", "급격한 피로감"], "causes": ["실내외(욕실) 온도차", "목욕 중 수분 손실", "체력 소모 과다", "과도한 온수 자극", "기저 질환(고혈압/당뇨 등) 영향"], "actions": ["목욕 시간 단축(20~30분 이내로 조정)", "목욕 전후 따뜻한 수분 섭취 제공", "서비스 전후 체온/혈압 등 바이탈 상시 측정", "간호사 보고 및 응급조치 체계 대기", "보호자에게 휴식 권고 안내"], }, "낙상/이동 불안정": { "problems": ["미끄러운 바닥에서 보행 위험", "욕조 출입 시 균형 상실", "부축 이동 시 체중 쏠림", "휠체어 이동/트랜스퍼 시 불안정", "기립 시 다리 힘 풀림"], "causes": ["하지 근력 저하 및 편마비", "바닥 물기 및 비눗물 잔류", "인지 기능 저하로 인한 지시 불이행", "이동 보조기구 미비 및 공간 협소"], "actions": ["미끄럼 방지 매트/스티커 필수 사용", "욕실 내 안전 손잡이 위치 파악 및 보조", "목욕 의자 상시 사용 원칙 준수", "이동/이승 시 2인 1조 협조 체계 강화", "수급자 및 보호자 대상 낙상 예방 수칙 재교육"], }, "위생/일반 건강상태": { "problems": ["두피 각질/비듬 심화", "손발톱 위생 관리 필요", "배설물 오염 방치", "구강 위생 불량", "체취/욕창 냄새 심화"], "causes": ["개인위생 자가 관리 능력 상실", "인지 저하로 인한 위생 케어 거부", "관절 구축 등 신체 동작 범위 제한"], "actions": ["두피 전용 샴푸로 집중 세정", "목욕 후 손발톱 안전하게 정리", "배설물 오염 부위 부분 세정 가이드 적용", "보호자에게 구강 케어 병행 권고", "청결 용품 선호도 조사 및 반영"], }, "환경/차량/장비 안전": { "problems": ["목욕 차량 내 온도 조절 미흡", "리프트 탑승 시 극도 불안감", "차량 내 수압 약함/온수 부족", "이동식 욕조 위생 상태 우려"], "causes": ["동절기 기온 급강하", "기계 작동 소음에 대한 공포", "차량 노후화 및 점검 지연", "보호자의 감염/위생 민감도 증가"], "actions": ["차량 방문 전 히터/온수 사전 가동", "리프트 이용 시 밀착하여 안심 시키기", "정기적인 차량/장비 유지보수 점검", "보호자에게 목욕조 소독 일지 확인 및 철저한 소독 안내"], }, "서비스 만족도 및 요구사항": { "problems": ["서비스 제공 시간 변경 요청", "특정 요양보호사 선호/기피", "요양보호사와의 의사소통 부재", "서비스 시간(소요시간) 부족 호소"], "causes": ["보호자 개인 일정 변경", "수급자와 종사자 간 성향 차이", "수급자 청각 저하/치매", "중증도로 인한 케어 시간 지연"], "actions": ["센터 일정 확인 후 조율 가능 여부 즉시 안내", "요원 교체 면담 및 내부 회의 진행", "큰 목소리로 천천히 설명하도록 종사자 재교육", "한도 내에서 최대한 효율적이고 꼼꼼한 진행 약속"], }, } def make_sentence(area, problems, causes, actions, age, grade, mobility, cognition): problem_text = ", ".join(problems) cause_text = ", ".join(causes) action_text = ", ".join(actions) # 1. 상담내용 (3가지 랜덤 템플릿) consult_templates = [ f"수급자는 만 {age}세, 장기요양 {grade}이며 현재 이동상태는 '{mobility}', 인지상태는 '{cognition}'으로 확인됨.\n\n방문목욕 급여제공 과정 및 {area} 관련 정기/수시 상담을 실시한 결과, 주요 문제상황으로 {problem_text} 특이사항이 확인됨. 목욕 서비스 진행 시 수급자의 현재 신체적 가동 범위와 인지, 심리 상태에 따른 세밀한 관찰이 요망됨.\n\n상담 과정에서 위 문제의 주된 원인으로 {cause_text} 등이 작용할 가능성을 보호자 및 종사자와 공유하였으며, 서비스 중 급격한 컨디션 저하 방지에 중점을 두기로 함.", f"대상자(만 {age}세, 장기요양 {grade}, 이동상태: {mobility}, 인지상태: {cognition})의 최근 방문목욕 서비스 이용 실태와 {area} 영역에 대해 심층 상담을 진행함.\n\n최근 급여제공 중 {problem_text} 위주의 문제점 및 욕구가 지속적으로 관찰됨. 주요 원인으로는 {cause_text} 등이 지목되었으며, 수급자의 안전 확보와 서비스 질 향상을 위한 대책 및 환경 개선이 필요한 상황임.", f"본 수급자(만 {age}세, {grade})는 평소 {mobility} 수준의 신체기능을 보이며 인지 상태는 {cognition}임. 금일 {area} 관련하여 수급자/보호자와 면담을 진행한 결과 {problem_text} 등의 애로사항이 발생하고 있음.\n\n이는 {cause_text}에 기인한 것으로 파악되며, 방문목욕 중 발생할 수 있는 안전사고나 감염, 체력 저하를 예방하기 위해 목욕 전후 세심한 상태 파악이 필수적임." ] # 2. 조치내용 (3가지 랜덤 템플릿) action_templates = [ f"방문목욕 진행 시 수급자의 당일 요구와 컨디션에 맞추어 {action_text} 등의 조치를 즉시 실시(또는 계획)하기로 함. 목욕 요양보호사 2인에게 세부 조치 및 안전 수칙을 전파하여 평가 기준에 부합하는 안전한 급여 제공이 이루어지도록 조치함.", f"해결 방안으로 {action_text} 조치를 현장에 적용하기로 함. 담당 요양보호사에게 해당 내용을 전달하여 서비스 절차에 반영토록 교육하였으며, 향후 낙상/피부이상/거부 등 특이사항 지속 시 방문간호 등 타 직역과의 연계 및 정보 공유를 강화하기로 함.", f"상담 결과에 따라 {action_text} 등 수급자 맞춤형 조치를 제공하기로 결정함. 센터 내 회의를 통해 변경된 서비스 제공 지침을 목욕 차량 및 동행 직원에게 공지하였으며, 즉각적인 현장 적용을 통해 보호자의 불안감을 해소시키고자 함." ] # 3. 급여제공 반영내용 (3가지 랜덤 템플릿) reflection_templates = [ "상담을 통해 확인된 요구사항 및 조치 계획은 장기요양 급여제공 과정에 적극 반영함. 즉각 반영 가능한 세부 사항은 다음 방문목욕 일정부터 바로 적용하며, 필요시 장기요양기관 평가 고시 기준에 맞추어 '급여제공계획서' 변경 및 보완을 검토하기로 함.", "금일 파악된 수급자의 욕구 및 문제 상황을 다음 달 급여제공일정 및 서비스 내용에 반영함. 종사자 인수인계 대장에 기록하여 목욕 요원이 변동되더라도 연속적인 케어가 이루어질 수 있도록 조치함.", "수급자의 안전 및 위생 증진을 위해 상담 결과를 현행 급여제공계획에 반영함. 상태 변화가 뚜렷할 경우 급여제공계획서를 재작성하여 수급자 및 보호자에게 재교부/동의를 얻을 예정임." ] # 4. 모니터링 계획 (3가지 랜덤 템플릿) monitoring_templates = [ "목욕 담당 요양보호사의 서비스 제공 기록지와 특이사항 피드백을 매주 검토할 예정임. 다음 상담 시 해당 문제상황이 얼마나 경감되었는지, 조치의 효과성은 어떠한지 평가하여 지속적이고 쾌적한 목욕 관리를 실시하기로 함.", "조치사항 적용 후 1~2주간 방문목욕 모니터링을 집중적으로 실시함. 수급자의 표정 변화, 바이탈, 피부 상태 개선 여부를 유선 또는 방문을 통해 확인하고 보호자와 주기적으로 소통할 계획임.", "수급자의 목욕 후 만족도 및 상태 변화를 요양보호사를 통해 매회 전달받기로 함. 추가적인 문제 발생 시 센터장 및 사회복지사가 즉각 개입하여 원인을 분석하고 대안을 재수립하기로 함." ] return random.choice(consult_templates), random.choice(action_templates), random.choice(reflection_templates), random.choice(monitoring_templates) def make_excel(data, consult, action, reflection, monitoring, checked_items, missing_items): wb = Workbook() ws = wb.active ws.title = "수급자 상담일지" thin = Side(style="thin", color="000000") border = Border(left=thin, right=thin, top=thin, bottom=thin) header_fill = PatternFill("solid", fgColor="EAF2F8") # Soft blue for col in range(1, 10): ws.column_dimensions[get_column_letter(col)].width = 15 current_row = 1 ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=9) ws.cell(current_row, 1).value = f"( {data['기관명']} ) 방문목욕 상담일지" ws.cell(current_row, 1).font = Font(bold=True, size=20) ws.cell(current_row, 1).alignment = Alignment(horizontal="center", vertical="center") ws.row_dimensions[current_row].height = 36 current_row += 1 info_rows = [ ("수급자 성명", data["수급자 성명"], "생년월일", data["생년월일"], "성별", data["성별"]), ("나이", f"만 {data['나이']}세", "장기요양등급", data["장기요양 등급"], "계약/시작일", str(data["시작일"])), ("본인부담률", data["본인부담률"], "상담일자", str(data["상담일자"]), "상담시간", str(data["상담시간"])), ("상담구분", data["상담구분"], "상담장소", data["상담장소"], "상담방법", data["상담방법"]), ("상담대상자", data["상담대상자"], "관계", data["관계"], "동석자", data["동석자"]), ("담당자", data["담당자"], "직책", data["직책"], "상담영역", data["상담영역"]), ("이동상태", data["이동상태"], "인지상태", data["인지상태"], "목욕유형", data["목욕유형"]), ] for row_data in info_rows: ws.cell(current_row, 1).value = row_data[0] ws.merge_cells(start_row=current_row, start_column=2, end_row=current_row, end_column=3) ws.cell(current_row, 2).value = row_data[1] ws.cell(current_row, 4).value = row_data[2] ws.merge_cells(start_row=current_row, start_column=5, end_row=current_row, end_column=6) ws.cell(current_row, 5).value = row_data[3] ws.cell(current_row, 7).value = row_data[4] ws.merge_cells(start_row=current_row, start_column=8, end_row=current_row, end_column=9) ws.cell(current_row, 8).value = row_data[5] for col in [1, 4, 7]: ws.cell(current_row, col).fill = header_fill ws.cell(current_row, col).font = Font(bold=True) ws.cell(current_row, col).alignment = Alignment(horizontal="center", vertical="center") current_row += 1 sections = [ ("상담 내용 (수급자 상태 변화 및 문제상황/원인 분석)", consult, 3), ("조치 내용 및 제공된 정보", action, 3), ("장기요양 급여제공계획 반영 내용", reflection, 3), ("사후 조치 및 모니터링 계획", monitoring, 3), ] for title, text, height in sections: ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=9) ws.cell(current_row, 1).value = title ws.cell(current_row, 1).fill = header_fill ws.cell(current_row, 1).font = Font(bold=True) ws.cell(current_row, 1).alignment = Alignment(horizontal="center", vertical="center") current_row += 1 ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row + height - 1, end_column=9) ws.cell(current_row, 1).value = text ws.cell(current_row, 1).alignment = Alignment(horizontal="left", vertical="top", wrap_text=True) lines = len(text) // 65 + 1 + text.count('\n') if text else 1 total_height = max(72, lines * 20 + 10) single_row_height = total_height / height for r_offset in range(height): ws.row_dimensions[current_row + r_offset].height = single_row_height current_row += height ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=3) ws.merge_cells(start_row=current_row, start_column=4, end_row=current_row, end_column=6) ws.merge_cells(start_row=current_row, start_column=7, end_row=current_row, end_column=9) ws.cell(current_row, 1).value = "기관 담당자 서명" ws.cell(current_row, 4).value = f"{data['담당자']} (서명)" ws.cell(current_row, 7).value = "대상자/보호자 (서명)" ws.cell(current_row, 1).fill = header_fill ws.cell(current_row, 7).fill = header_fill max_row = current_row for row in ws.iter_rows(min_row=1, max_row=max_row, min_col=1, max_col=9): for cell in row: cell.border = border cell.alignment = Alignment( horizontal=cell.alignment.horizontal or "left", vertical=cell.alignment.vertical or "center", wrap_text=True ) for r in range(1, max_row + 1): if ws.row_dimensions[r].height is None: ws.row_dimensions[r].height = 24 ws.page_setup.paperSize = ws.PAPERSIZE_A4 ws.page_setup.orientation = "portrait" ws.page_setup.fitToWidth = 1 ws.page_setup.fitToHeight = 0 ws.sheet_properties.pageSetUpPr.fitToPage = True ws.page_margins.left = 0.3 ws.page_margins.right = 0.3 ws.page_margins.top = 0.4 ws.page_margins.bottom = 0.4 ws.print_options.horizontalCentered = True output = BytesIO() wb.save(output) output.seek(0) return output col1, col2, col3 = st.columns(3) with col1: facility = st.text_input("기관명", value="BeeCare 재가복지센터", key="institution") user_id = st.text_input("사용자 식별값 (예: 휴대폰번호 뒷자리 등)", value="", key="user_id", help="하루 2회 제한 관리를 위해 사용됩니다.") name = st.text_input("수급자 성명", "이길순") birth = st.text_input("생년월일", "1945-05-15") gender = st.selectbox("성별", ["여", "남"]) age = st.number_input("나이", min_value=60, max_value=110, value=81) with col2: grade = st.selectbox("장기요양 등급", ["1등급", "2등급", "3등급", "4등급", "5등급", "인지지원등급"]) admission_date = st.date_input("계약일/시작일", date.today()) copay = st.selectbox("본인부담률", ["15%", "0%", "6%", "9%", "12%", "20%"]) consult_date = st.date_input("상담일자", date.today()) with col3: consult_time = st.time_input("상담시간", time(14, 0)) consult_type = st.selectbox("상담 구분", ["정기 상담", "수시 상담", "신규 계약 상담", "보호자 요청 상담", "불만 처리 상담"]) place = st.selectbox("상담 장소", ["수급자 자택", "센터 사무실", "유선상담", "기타"]) method = st.selectbox("상담 방법", ["대면 면담", "유선/전화 상담", "보호자 앱 알림장", "기타"]) target = st.text_input("상담 대상자 성명", "자녀") col4, col5, col6 = st.columns(3) with col4: relation = st.selectbox("상담자와의 관계", ["딸", "아들", "본인", "배우자", "며느리", "사위", "기타 보호자"]) staff_position = st.selectbox("기관 담당자 직책", ["사회복지사", "방문간호사", "요양보호사", "시설장"]) staff_name = st.text_input("기관 담당자 성명", "사회복지사") with col5: companion = st.text_input("동석자", "없음") mobility = st.selectbox("이동상태", ["부축 필요", "독립보행", "지팡이 보행", "워커 보행", "휠체어 이동", "와상 상태"]) cognition = st.selectbox("인지상태", ["경도 치매", "인지 양호", "중등도 치매", "중증 치매", "의사소통 어려움"]) with col6: bath_type = st.selectbox("목욕유형", ["방문목욕(차량내)", "방문목욕(가정내욕조)", "가정내샤워도움", "기타"]) area = st.selectbox("상담영역 선택", list(AREA_DATA.keys())) st.subheader("문제상황 / 원인 / 조치 선택 (평가지표 연계)") problems = st.multiselect("문제상황 선택", AREA_DATA[area]["problems"], default=AREA_DATA[area]["problems"][:2], key=f"problems_{area}") causes = st.multiselect("상담하게 된 원인 선택", AREA_DATA[area]["causes"], default=AREA_DATA[area]["causes"][:2], key=f"causes_{area}") actions = st.multiselect("조치내용 선택", AREA_DATA[area]["actions"], default=AREA_DATA[area]["actions"][:2], key=f"actions_{area}") # --- GENERATION FLOW WITH EXPLICIT LIMIT LIMITATION --- st.markdown("---") is_inputs_filled = bool(facility.strip() and user_id.strip()) today_count = get_today_usage(facility, user_id) if is_inputs_filled else 0 can_generate = is_inputs_filled and (today_count < 2) if not is_inputs_filled: st.warning("⚠️ 생성 기능을 이용하시려면 상단 입력창에서 [기관명]과 [사용자 식별값]을 정확히 입력해 주세요.") elif today_count >= 2: st.error("🚨 오늘 사용 가능 횟수(2회)를 모두 사용했습니다. 새로운 문서 생성은 불가하며, 아래 기존 생성된 문서의 수정 및 다운로드만 가능합니다.") checked_items = [] st.subheader("필수 기재항목 체크리스트 (공단 평가지표 기준)") for item in REQUIRED_ITEMS: if st.checkbox(item, value=True, key=f"check_{item}"): checked_items.append(item) missing_items = [item for item in REQUIRED_ITEMS if item not in checked_items] score = int((len(checked_items) / len(REQUIRED_ITEMS)) * 100) if score == 100: st.success(f"상담일지 완성도: {score}점 / 누락 항목 없음 (평가지표 충족)") elif score >= 85: st.warning(f"상담일지 완성도: {score}점 / 일부 항목 확인 필요") else: st.error(f"상담일지 완성도: {score}점 / 필수항목 보완 필요") data = { "기관명": facility, "수급자 성명": name, "생년월일": birth, "성별": gender, "나이": age, "장기요양 등급": grade, "시작일": admission_date, "본인부담률": copay, "상담일자": consult_date, "상담시간": consult_time, "상담구분": consult_type, "상담장소": place, "상담방법": method, "상담대상자": target, "관계": relation, "담당자": staff_name, "직책": staff_position, "동석자": companion, "상담영역": area, "이동상태": mobility, "인지상태": cognition, "목욕유형": bath_type, } if st.button("📝 상담일지 & 엑셀 생성하기", disabled=not can_generate): if not is_inputs_filled: st.error("기관명과 사용자 식별값을 입력해야 생성할 수 있습니다.") st.stop() if today_count >= 2: st.error("오늘 사용 가능 횟수를 모두 사용했습니다.") st.stop() increase_today_usage(facility, user_id) gen_consult, gen_action, gen_reflection, gen_monitoring = make_sentence( area, problems, causes, actions, age, grade, mobility, cognition ) st.session_state.generated_data = { "consult": gen_consult, "action": gen_action, "reflection": gen_reflection, "monitoring": gen_monitoring, "data": data, "checked_items": checked_items, "missing_items": missing_items } st.success("🎉 평가지표에 맞춘 상담일지가 무작위 템플릿으로 생성되었습니다! 아래에서 내용을 확인 및 수정한 후 엑셀 파일을 다운로드하세요.") st.rerun() if st.session_state.generated_data is not None: g_data = st.session_state.generated_data st.subheader("상담 내용 (수급자 상태 변화 및 문제상황/원인 분석)") ui_consult = st.text_area("상담내용 수정 가능", g_data["consult"], height=220) st.subheader("조치 내용 및 제공된 정보") ui_action = st.text_area("조치내용 수정 가능", g_data["action"], height=180) st.subheader("장기요양 급여제공계획 반영 내용") ui_reflection = st.text_area("급여제공 반영 내용 수정 가능", g_data["reflection"], height=130) st.subheader("사후 조치 및 모니터링 계획") ui_monitoring = st.text_area("모니터링 계획 수정 가능", g_data["monitoring"], height=130) excel_file = make_excel( g_data["data"], ui_consult, ui_action, ui_reflection, ui_monitoring, g_data["checked_items"], g_data["missing_items"] ) st.download_button( label="📥 수급자 상담일지 엑셀 다운로드", data=excel_file, file_name=f"{g_data['data']['수급자 성명']}_{g_data['data']['상담영역']}_방문목욕상담일지.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" )