Spaces:
Sleeping
Sleeping
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,432 +0,0 @@
|
|
| 1 |
-
import streamlit as st
|
| 2 |
-
from openpyxl import Workbook
|
| 3 |
-
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
|
| 4 |
-
from openpyxl.utils import get_column_letter
|
| 5 |
-
from io import BytesIO
|
| 6 |
-
from datetime import date, time
|
| 7 |
-
import sqlite3
|
| 8 |
-
|
| 9 |
-
st.set_page_config(page_title="방문목욕 수급자 상담일지 자동생성기", layout="wide")
|
| 10 |
-
|
| 11 |
-
st.title("🛁 방문목욕 수급자 상담일지 자동생성기 v1")
|
| 12 |
-
st.caption("상담영역 → 문제상황 → 원인 → 조치 → 급여반영 → 엑셀 다운로드")
|
| 13 |
-
st.warning("⚠️ **※ 본 생성기는 하루 2회 이용 한도 제한이 있습니다.**")
|
| 14 |
-
|
| 15 |
-
DB_FILE = "usage.db"
|
| 16 |
-
|
| 17 |
-
def init_db():
|
| 18 |
-
conn = sqlite3.connect(DB_FILE)
|
| 19 |
-
cursor = conn.cursor()
|
| 20 |
-
cursor.execute("""
|
| 21 |
-
CREATE TABLE IF NOT EXISTS usage_limit (
|
| 22 |
-
institution TEXT,
|
| 23 |
-
user_id TEXT,
|
| 24 |
-
use_date TEXT,
|
| 25 |
-
count INTEGER,
|
| 26 |
-
PRIMARY KEY (institution, user_id, use_date)
|
| 27 |
-
)
|
| 28 |
-
""")
|
| 29 |
-
conn.commit()
|
| 30 |
-
conn.close()
|
| 31 |
-
|
| 32 |
-
def get_today_usage(institution, user_id):
|
| 33 |
-
if not institution or not user_id:
|
| 34 |
-
return 0
|
| 35 |
-
init_db()
|
| 36 |
-
today_str = date.today().isoformat()
|
| 37 |
-
conn = sqlite3.connect(DB_FILE)
|
| 38 |
-
cursor = conn.cursor()
|
| 39 |
-
cursor.execute(
|
| 40 |
-
"SELECT count FROM usage_limit WHERE institution = ? AND user_id = ? AND use_date = ?",
|
| 41 |
-
(institution.strip(), user_id.strip(), today_str)
|
| 42 |
-
)
|
| 43 |
-
row = cursor.fetchone()
|
| 44 |
-
conn.close()
|
| 45 |
-
return row[0] if row else 0
|
| 46 |
-
|
| 47 |
-
def increase_today_usage(institution, user_id):
|
| 48 |
-
if not institution or not user_id:
|
| 49 |
-
return
|
| 50 |
-
init_db()
|
| 51 |
-
today_str = date.today().isoformat()
|
| 52 |
-
conn = sqlite3.connect(DB_FILE)
|
| 53 |
-
cursor = conn.cursor()
|
| 54 |
-
cursor.execute(
|
| 55 |
-
"SELECT count FROM usage_limit WHERE institution = ? AND user_id = ? AND use_date = ?",
|
| 56 |
-
(institution.strip(), user_id.strip(), today_str)
|
| 57 |
-
)
|
| 58 |
-
row = cursor.fetchone()
|
| 59 |
-
if row:
|
| 60 |
-
cursor.execute(
|
| 61 |
-
"UPDATE usage_limit SET count = count + 1 WHERE institution = ? AND user_id = ? AND use_date = ?",
|
| 62 |
-
(institution.strip(), user_id.strip(), today_str)
|
| 63 |
-
)
|
| 64 |
-
else:
|
| 65 |
-
cursor.execute(
|
| 66 |
-
"INSERT INTO usage_limit (institution, user_id, use_date, count) VALUES (?, ?, ?, 1)",
|
| 67 |
-
(institution.strip(), user_id.strip(), today_str)
|
| 68 |
-
)
|
| 69 |
-
conn.commit()
|
| 70 |
-
conn.close()
|
| 71 |
-
|
| 72 |
-
if "generated_data" not in st.session_state:
|
| 73 |
-
st.session_state.generated_data = None
|
| 74 |
-
|
| 75 |
-
inst_val = st.session_state.get("institution", "").strip()
|
| 76 |
-
user_val = st.session_state.get("user_id", "").strip()
|
| 77 |
-
today_count = get_today_usage(inst_val, user_val)
|
| 78 |
-
|
| 79 |
-
# Sidebar Usage Panel
|
| 80 |
-
st.sidebar.markdown("### 📊 사용 한도 관리")
|
| 81 |
-
if not inst_val or not user_val:
|
| 82 |
-
st.sidebar.warning("⚠️ 기관명과 사용자 식별값을 입력해주세요.")
|
| 83 |
-
else:
|
| 84 |
-
st.sidebar.write(f"오늘 생성 횟수: **{today_count} / 2회**")
|
| 85 |
-
if today_count >= 2:
|
| 86 |
-
st.sidebar.error("❌ 생성 한도 초과 (2/2)")
|
| 87 |
-
else:
|
| 88 |
-
st.sidebar.success(f"✅ 생성 가능 (남은 횟수: {2 - today_count}회)")
|
| 89 |
-
|
| 90 |
-
REQUIRED_ITEMS = [
|
| 91 |
-
"수급자 성명", "생년월일", "성별", "장기요양 등급", "계약일/시작일", "본인부담률",
|
| 92 |
-
"상담 일시", "상담 구분", "상담 장소", "상담 방법", "상담 대상자 성명", "상담자와의 관계",
|
| 93 |
-
"기관 담당자 직책 및 성명", "동석자", "상담 목적 및 사유",
|
| 94 |
-
"신체 기능 상태 변화 및 낙상 위험", "인지 기능 및 심리 상태", "피부 상태 및 위생 상태",
|
| 95 |
-
"목욕 차량/장비 안전 상태", "보호자의 요청 사항",
|
| 96 |
-
"기관의 설명 및 안내 사항", "문제 해결 또는 제공된 정보 내용", "향후 급여 제공 계획",
|
| 97 |
-
"급여 반영 여부", "사후 조치 및 모니터링 계획", "보호자 전달/공지사항",
|
| 98 |
-
"상담자 서명", "대상자 또는 보호자 서명"
|
| 99 |
-
]
|
| 100 |
-
|
| 101 |
-
AREA_DATA = {
|
| 102 |
-
"목욕 거부/협조도": {
|
| 103 |
-
"problems": ["목욕 거부", "탈의 거부", "물에 대한 공포심", "피부 접촉 예민", "피로감 호소", "샤워기 소리에 불안"],
|
| 104 |
-
"causes": ["치매 인지 저하", "추위에 민감함", "과거 부정적 경험", "신체 노출 수치심", "피부 건조 및 가려움", "컨디션 저하"],
|
| 105 |
-
"actions": ["따뜻한 실내 온도 유지", "충분한 지지적 대화", "타월로 몸을 감싼 채 진행", "선호하는 음악 틀기", "간호사 공유", "보호자 안내"],
|
| 106 |
-
},
|
| 107 |
-
"피부 상태/발적/상처": {
|
| 108 |
-
"problems": ["피부 건조", "엉덩이 발적", "가려움증 호소", "피부 짓무름", "미세 찰과상", "각질 심화"],
|
| 109 |
-
"causes": ["혈액 순환 저하", "오랫동안 앉아있음", "기저귀 장기 착용", "수분 섭취 부족", "피부 장벽 약화"],
|
| 110 |
-
"actions": ["목욕 후 보습제 도포", "마사지 도움", "피부 유심히 관찰 및 기록", "간호사/보호자 보고", "기저귀 교체 주기 단축 검토", "건조 기능 강화"],
|
| 111 |
-
},
|
| 112 |
-
"체온/바이탈 변화": {
|
| 113 |
-
"problems": ["목욕 전후 혈압 상승", "목욕 후 한기 호소", "미열 발생", "어지러움 호소", "급격한 피로감"],
|
| 114 |
-
"causes": ["실내외 온도차", "수분 손실", "체력 소모", "온수 자극", "기저 건강 상태"],
|
| 115 |
-
"actions": ["목욕 시간 단축(20~30분 이내)", "목욕 후 따뜻한 음료 제공", "체온 상시 측정", "간호사 보고 및 응급조치 준비", "보호자 상담"],
|
| 116 |
-
},
|
| 117 |
-
"낙상/이동 불안정": {
|
| 118 |
-
"problems": ["미끄러운 바닥 위험", "욕조 출입 시 균형 상실", "부축 시 체중 실림", "휠체어 이동 시 불안정", "기립 시 어지러움"],
|
| 119 |
-
"causes": ["하지 근력 약화", "바닥 물기 및 비눗물", "인지 기능 저하", "이동 보조기구 미비"],
|
| 120 |
-
"actions": ["미끄럼 방지 매트 추가 설치", "안전 손잡이 활용 보조", "목욕 의자 상시 사용", "이동 보조 인력 2인 협조", "안전 수칙 교육"],
|
| 121 |
-
},
|
| 122 |
-
"위생/일반 건강상태": {
|
| 123 |
-
"problems": ["두피 각질/비듬", "손발톱 관리 필요", "배설물 오염", "구강 위생 불량", "체취 심화"],
|
| 124 |
-
"causes": ["개인위생 관리 부족", "인지 저하로 위생 거부", "신체 동작 범위 제한"],
|
| 125 |
-
"actions": ["두피 집중 세정", "목욕 후 손발톱 정리", "위생 가이드 적용", "구강 케어 병행", "청결 용품 선호도 조사"],
|
| 126 |
-
},
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
def make_sentence(area, problems, causes, actions, age, grade, mobility, cognition):
|
| 130 |
-
problem_text = ", ".join(problems)
|
| 131 |
-
cause_text = ", ".join(causes)
|
| 132 |
-
action_text = ", ".join(actions)
|
| 133 |
-
|
| 134 |
-
consult = (
|
| 135 |
-
f"수급자는 만 {age}세, 장기요양 {grade}이며 현재 이동상태는 '{mobility}', 인지상태는 '{cognition}'으로 확인된다.\n\n"
|
| 136 |
-
f"방문목욕 급여제공 과정 및 {area} 관련 상담을 실시한 결과, 주요 문제상황으로 {problem_text}이/가 확인되었다. "
|
| 137 |
-
f"목욕 서비스 진행 시 수급자의 현재 신체적 가동 범위, 인지 능력, 심리 상태에 따른 세부 관찰이 요망된다.\n\n"
|
| 138 |
-
f"상담 과정에서 {cause_text} 등이 원인으로 작용할 가능성을 확인하였다. "
|
| 139 |
-
f"기관에서는 목욕 중 급격한 체력 저하나 혈압 변화가 발생하는지 예의 주시하기로 하였다."
|
| 140 |
-
)
|
| 141 |
-
|
| 142 |
-
action = (
|
| 143 |
-
f"방문목욕 진행 시 수급자의 요구와 상태에 맞추어 {action_text} 등의 조치를 실시하거나 계획하기로 하였다. "
|
| 144 |
-
f"목욕 요양보호사 및 동행 직원에게 세부 조치 사항을 전파하고 안전한 급여 제공이 이루어지도록 조치하였다.\n\n"
|
| 145 |
-
f"향후 목욕 거부나 낙상 위험 등 특이사항 지속 시 욕구사정에 반영하고, 필요 시 방문요양/간호 서비스 연계를 검토하기로 하였다."
|
| 146 |
-
)
|
| 147 |
-
|
| 148 |
-
reflection = (
|
| 149 |
-
"상담을 통해 확인된 요구사항 및 조치 계획은 장기요양 급여제공 과정에 적극 반영한다. "
|
| 150 |
-
"반영 가능한 세부 사항은 다음 방문목욕 일정 시 적용하고, "
|
| 151 |
-
"장기요양기관 평가 고시 기준에 맞추어 급여제공계획서 보완을 검토하기로 한다."
|
| 152 |
-
)
|
| 153 |
-
|
| 154 |
-
monitoring = (
|
| 155 |
-
"목욕 담당 요양보호사의 서비스 후 피드백 및 피부/바이탈 모니터링 기록을 주 1회 이상 검토한다. "
|
| 156 |
-
"다음 상담 시 문제상황의 경감 여부와 조치의 효과성을 모니터링하여 지속적인 위생 관리를 실시하기로 한다."
|
| 157 |
-
)
|
| 158 |
-
|
| 159 |
-
return consult, action, reflection, monitoring
|
| 160 |
-
|
| 161 |
-
def make_excel(data, consult, action, reflection, monitoring, checked_items, missing_items):
|
| 162 |
-
wb = Workbook()
|
| 163 |
-
ws = wb.active
|
| 164 |
-
ws.title = "수급자 상담일지"
|
| 165 |
-
|
| 166 |
-
thin = Side(style="thin", color="000000")
|
| 167 |
-
border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
| 168 |
-
header_fill = PatternFill("solid", fgColor="EAF2F8") # Soft blue
|
| 169 |
-
|
| 170 |
-
for col in range(1, 10):
|
| 171 |
-
ws.column_dimensions[get_column_letter(col)].width = 15
|
| 172 |
-
|
| 173 |
-
current_row = 1
|
| 174 |
-
|
| 175 |
-
ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=9)
|
| 176 |
-
ws.cell(current_row, 1).value = f"( {data['기관명']} ) 방문목욕 상담일지"
|
| 177 |
-
ws.cell(current_row, 1).font = Font(bold=True, size=20)
|
| 178 |
-
ws.cell(current_row, 1).alignment = Alignment(horizontal="center", vertical="center")
|
| 179 |
-
ws.row_dimensions[current_row].height = 36
|
| 180 |
-
current_row += 1
|
| 181 |
-
|
| 182 |
-
info_rows = [
|
| 183 |
-
("수급자 성명", data["수급자 성명"], "생년월일", data["생년월일"], "성별", data["성별"]),
|
| 184 |
-
("나이", data["나이"], "장기요양 등급", data["장기요양 등급"], "시작일", str(data["시작일"])),
|
| 185 |
-
("본인부담률", data["본인부담률"], "상담일자", str(data["상담일자"]), "상담시간", str(data["상담시간"])),
|
| 186 |
-
("상담구분", data["상담구분"], "상담장소", data["상��장소"], "상담방법", data["상담방법"]),
|
| 187 |
-
("상담대상자", data["상담대상자"], "관계", data["관계"], "동석자", data["동석자"]),
|
| 188 |
-
("담당자", data["담당자"], "직책", data["직책"], "상담영역", data["상담영역"]),
|
| 189 |
-
("이동상태", data["이동상태"], "인지상태", data["인지상태"], "목욕유형", data["목욕유형"]),
|
| 190 |
-
]
|
| 191 |
-
|
| 192 |
-
for row_data in info_rows:
|
| 193 |
-
ws.cell(current_row, 1).value = row_data[0]
|
| 194 |
-
ws.merge_cells(start_row=current_row, start_column=2, end_row=current_row, end_column=3)
|
| 195 |
-
ws.cell(current_row, 2).value = row_data[1]
|
| 196 |
-
ws.cell(current_row, 4).value = row_data[2]
|
| 197 |
-
ws.merge_cells(start_row=current_row, start_column=5, end_row=current_row, end_column=6)
|
| 198 |
-
ws.cell(current_row, 5).value = row_data[3]
|
| 199 |
-
ws.cell(current_row, 7).value = row_data[4]
|
| 200 |
-
ws.merge_cells(start_row=current_row, start_column=8, end_row=current_row, end_column=9)
|
| 201 |
-
ws.cell(current_row, 8).value = row_data[5]
|
| 202 |
-
|
| 203 |
-
for col in [1, 4, 7]:
|
| 204 |
-
ws.cell(current_row, col).fill = header_fill
|
| 205 |
-
ws.cell(current_row, col).font = Font(bold=True)
|
| 206 |
-
ws.cell(current_row, col).alignment = Alignment(horizontal="center", vertical="center")
|
| 207 |
-
current_row += 1
|
| 208 |
-
|
| 209 |
-
sections = [
|
| 210 |
-
("상담 내용 (수급자 상태 변화 및 요구사항)", consult, 3),
|
| 211 |
-
("조치 및 해결 계획", action, 3),
|
| 212 |
-
("급여제공계획 반영 내용", reflection, 3),
|
| 213 |
-
("사후 조치 및 모니터링 계획", monitoring, 3),
|
| 214 |
-
]
|
| 215 |
-
|
| 216 |
-
for title, text, height in sections:
|
| 217 |
-
ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=9)
|
| 218 |
-
ws.cell(current_row, 1).value = title
|
| 219 |
-
ws.cell(current_row, 1).fill = header_fill
|
| 220 |
-
ws.cell(current_row, 1).font = Font(bold=True)
|
| 221 |
-
ws.cell(current_row, 1).alignment = Alignment(horizontal="center")
|
| 222 |
-
current_row += 1
|
| 223 |
-
|
| 224 |
-
ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row + height - 1, end_column=9)
|
| 225 |
-
ws.cell(current_row, 1).value = text
|
| 226 |
-
ws.cell(current_row, 1).alignment = Alignment(horizontal="left", vertical="top", wrap_text=True)
|
| 227 |
-
|
| 228 |
-
# 텍스트 길이에 따라 높이를 동적으로 자동 계산하여 3개 행에 나누어 배정
|
| 229 |
-
lines = len(text) // 65 + 1 + text.count('\n') if text else 1
|
| 230 |
-
total_height = max(72, lines * 20 + 10)
|
| 231 |
-
single_row_height = total_height / height
|
| 232 |
-
for r_offset in range(height):
|
| 233 |
-
ws.row_dimensions[current_row + r_offset].height = single_row_height
|
| 234 |
-
|
| 235 |
-
current_row += height
|
| 236 |
-
|
| 237 |
-
ws.merge_cells(start_row=current_row, start_column=1, end_row=current_row, end_column=3)
|
| 238 |
-
ws.merge_cells(start_row=current_row, start_column=4, end_row=current_row, end_column=6)
|
| 239 |
-
ws.merge_cells(start_row=current_row, start_column=7, end_row=current_row, end_column=9)
|
| 240 |
-
|
| 241 |
-
ws.cell(current_row, 1).value = "상담자 서명"
|
| 242 |
-
ws.cell(current_row, 4).value = f"{data['담당자']} (서명)"
|
| 243 |
-
ws.cell(current_row, 7).value = "대상자/보호자 (서명)"
|
| 244 |
-
ws.cell(current_row, 1).fill = header_fill
|
| 245 |
-
ws.cell(current_row, 7).fill = header_fill
|
| 246 |
-
|
| 247 |
-
max_row = current_row
|
| 248 |
-
|
| 249 |
-
for row in ws.iter_rows(min_row=1, max_row=max_row, min_col=1, max_col=9):
|
| 250 |
-
for cell in row:
|
| 251 |
-
cell.border = border
|
| 252 |
-
cell.alignment = Alignment(
|
| 253 |
-
horizontal=cell.alignment.horizontal or "left",
|
| 254 |
-
vertical=cell.alignment.vertical or "center",
|
| 255 |
-
wrap_text=True
|
| 256 |
-
)
|
| 257 |
-
|
| 258 |
-
for r in range(1, max_row + 1):
|
| 259 |
-
if ws.row_dimensions[r].height is None:
|
| 260 |
-
ws.row_dimensions[r].height = 24
|
| 261 |
-
|
| 262 |
-
ws.page_setup.paperSize = ws.PAPERSIZE_A4
|
| 263 |
-
ws.page_setup.orientation = "portrait"
|
| 264 |
-
ws.page_setup.fitToWidth = 1
|
| 265 |
-
ws.page_setup.fitToHeight = 0
|
| 266 |
-
ws.sheet_properties.pageSetUpPr.fitToPage = True
|
| 267 |
-
|
| 268 |
-
ws.page_margins.left = 0.3
|
| 269 |
-
ws.page_margins.right = 0.3
|
| 270 |
-
ws.page_margins.top = 0.4
|
| 271 |
-
ws.page_margins.bottom = 0.4
|
| 272 |
-
ws.print_options.horizontalCentered = True
|
| 273 |
-
|
| 274 |
-
output = BytesIO()
|
| 275 |
-
wb.save(output)
|
| 276 |
-
output.seek(0)
|
| 277 |
-
return output
|
| 278 |
-
|
| 279 |
-
col1, col2, col3 = st.columns(3)
|
| 280 |
-
|
| 281 |
-
with col1:
|
| 282 |
-
facility = st.text_input("기관명", value="BeeCare 재가복지센터", key="institution")
|
| 283 |
-
user_id = st.text_input("사용자 식별값 (예: 휴대폰번호 뒷자리 등)", value="", key="user_id", help="하루 2회 제한 관리를 위해 사용됩니다.")
|
| 284 |
-
name = st.text_input("수급자 성명", "이길순")
|
| 285 |
-
birth = st.text_input("생년월일", "1945-05-15")
|
| 286 |
-
gender = st.selectbox("성별", ["여", "남"])
|
| 287 |
-
age = st.number_input("나이", min_value=60, max_value=110, value=81)
|
| 288 |
-
|
| 289 |
-
with col2:
|
| 290 |
-
grade = st.selectbox("장기요양 등급", ["1등급", "2등급", "3등급", "4등급", "5등급"])
|
| 291 |
-
admission_date = st.date_input("계약일/시작일", date.today())
|
| 292 |
-
copay = st.selectbox("본인부담���", ["15%", "0%", "6%", "9%", "12%", "20%"])
|
| 293 |
-
consult_date = st.date_input("상담일자", date.today())
|
| 294 |
-
|
| 295 |
-
with col3:
|
| 296 |
-
consult_time = st.time_input("상담시간", time(14, 0))
|
| 297 |
-
consult_type = st.selectbox("상담 구분", ["정기 상담", "수시 상담", "신규 계약 상담", "보호자 요청 상담"])
|
| 298 |
-
place = st.selectbox("상담 장소", ["수급자 자택", "센터 사무실", "유선상담", "기타"])
|
| 299 |
-
method = st.selectbox("상담 방법", ["대면 면담", "유선/전화 상담", "기타"])
|
| 300 |
-
target = st.text_input("상담 대상자 성명", "자녀")
|
| 301 |
-
|
| 302 |
-
col4, col5, col6 = st.columns(3)
|
| 303 |
-
|
| 304 |
-
with col4:
|
| 305 |
-
relation = st.selectbox("상담자와의 관계", ["딸", "아들", "본인", "배우자", "며느리", "사위", "기타"])
|
| 306 |
-
staff_position = st.selectbox("기관 담당자 직책", ["사회복지사", "요양보호사", "간호사", "시설장"])
|
| 307 |
-
staff_name = st.text_input("기관 담당자 성명", "사회복지사")
|
| 308 |
-
|
| 309 |
-
with col5:
|
| 310 |
-
companion = st.text_input("동석자", "없음")
|
| 311 |
-
mobility = st.selectbox("이동상태", ["부축 필요", "독립보행", "지팡이 보행", "워커 보행", "휠체어 이동", "와상 상태"])
|
| 312 |
-
cognition = st.selectbox("인지상태", ["경도 치매", "인지 양호", "중등도 치매", "중증 치매", "의사소통 어려움"])
|
| 313 |
-
|
| 314 |
-
with col6:
|
| 315 |
-
bath_type = st.selectbox("목욕유형", ["방문목욕(차량내)", "방문목욕(가정내욕조)", "가정내샤워도움", "기타"])
|
| 316 |
-
area = st.selectbox("상담영역 선택", list(AREA_DATA.keys()))
|
| 317 |
-
|
| 318 |
-
# --- GENERATION FLOW WITH EXPLICIT LIMIT LIMITATION ---
|
| 319 |
-
st.markdown("---")
|
| 320 |
-
|
| 321 |
-
is_inputs_filled = bool(facility.strip() and user_id.strip())
|
| 322 |
-
today_count = get_today_usage(facility, user_id) if is_inputs_filled else 0
|
| 323 |
-
can_generate = is_inputs_filled and (today_count < 2)
|
| 324 |
-
|
| 325 |
-
if not is_inputs_filled:
|
| 326 |
-
st.warning("⚠️ 생성 기능을 이용하시려면 상단 입력창에서 [기관명]과 [사용자 식별값]을 정확히 입력해 주세요.")
|
| 327 |
-
elif today_count >= 2:
|
| 328 |
-
st.error("🚨 오늘 사용 가능 횟수(2회)를 모두 사용했습니다. 새로운 문서 생성은 불가하며, 아래 기존 생성된 문서의 수정 및 다운로드만 가능합니다.")
|
| 329 |
-
|
| 330 |
-
checked_items = []
|
| 331 |
-
st.subheader("필수 기재항목 체크리스트")
|
| 332 |
-
for item in REQUIRED_ITEMS:
|
| 333 |
-
if st.checkbox(item, value=True, key=f"check_{item}"):
|
| 334 |
-
checked_items.append(item)
|
| 335 |
-
|
| 336 |
-
missing_items = [item for item in REQUIRED_ITEMS if item not in checked_items]
|
| 337 |
-
score = int((len(checked_items) / len(REQUIRED_ITEMS)) * 100)
|
| 338 |
-
|
| 339 |
-
if score == 100:
|
| 340 |
-
st.success(f"상담일지 완성도: {score}점 / 누락 항목 없음")
|
| 341 |
-
elif score >= 85:
|
| 342 |
-
st.warning(f"상담일지 완성도: {score}점 / 일부 항목 확인 필요")
|
| 343 |
-
else:
|
| 344 |
-
st.error(f"상담일지 완성도: {score}점 / 필수항목 보완 필요")
|
| 345 |
-
|
| 346 |
-
data = {
|
| 347 |
-
"기관명": facility,
|
| 348 |
-
"수급자 성명": name,
|
| 349 |
-
"생년월일": birth,
|
| 350 |
-
"성별": gender,
|
| 351 |
-
"나이": age,
|
| 352 |
-
"장기요양 등급": grade,
|
| 353 |
-
"시작일": admission_date,
|
| 354 |
-
"본인부담률": copay,
|
| 355 |
-
"상담일자": consult_date,
|
| 356 |
-
"상담시간": consult_time,
|
| 357 |
-
"상담구분": consult_type,
|
| 358 |
-
"상담장소": place,
|
| 359 |
-
"상담방법": method,
|
| 360 |
-
"상담대상자": target,
|
| 361 |
-
"관계": relation,
|
| 362 |
-
"담당자": staff_name,
|
| 363 |
-
"직책": staff_position,
|
| 364 |
-
"동석자": companion,
|
| 365 |
-
"상담영역": area,
|
| 366 |
-
"이동상태": mobility,
|
| 367 |
-
"인지상태": cognition,
|
| 368 |
-
"목욕유형": bath_type,
|
| 369 |
-
}
|
| 370 |
-
|
| 371 |
-
if st.button("📝 상담일지 & 엑셀 생성하기", disabled=not can_generate):
|
| 372 |
-
if not is_inputs_filled:
|
| 373 |
-
st.error("기관명과 사용자 식별값을 입력해야 생성할 수 있습니다.")
|
| 374 |
-
st.stop()
|
| 375 |
-
|
| 376 |
-
if today_count >= 2:
|
| 377 |
-
st.error("오늘 사용 가능 횟수를 모두 사용했습니다.")
|
| 378 |
-
st.stop()
|
| 379 |
-
|
| 380 |
-
# 생성 성공 시 DB에 횟수 추가
|
| 381 |
-
increase_today_usage(facility, user_id)
|
| 382 |
-
|
| 383 |
-
# 텍스트 자동 생성
|
| 384 |
-
gen_consult, gen_action, gen_reflection, gen_monitoring = make_sentence(
|
| 385 |
-
area, problems, causes, actions, age, grade, mobility, cognition
|
| 386 |
-
)
|
| 387 |
-
|
| 388 |
-
st.session_state.generated_data = {
|
| 389 |
-
"consult": gen_consult,
|
| 390 |
-
"action": gen_action,
|
| 391 |
-
"reflection": gen_reflection,
|
| 392 |
-
"monitoring": gen_monitoring,
|
| 393 |
-
"data": data,
|
| 394 |
-
"checked_items": checked_items,
|
| 395 |
-
"missing_items": missing_items
|
| 396 |
-
}
|
| 397 |
-
st.success("🎉 상담일지가 성공적으로 생성되었습니다! 아래에서 내용을 확인 및 수정한 후 엑셀 파일을 다운로드하세요.")
|
| 398 |
-
st.rerun()
|
| 399 |
-
|
| 400 |
-
# 생성된 데이터가 있을 경우에만 필드 및 다운로드 제공
|
| 401 |
-
if st.session_state.generated_data is not None:
|
| 402 |
-
g_data = st.session_state.generated_data
|
| 403 |
-
|
| 404 |
-
st.subheader("상담 내용")
|
| 405 |
-
ui_consult = st.text_area("상담내용 수정 가능", g_data["consult"], height=220)
|
| 406 |
-
|
| 407 |
-
st.subheader("조치 내용")
|
| 408 |
-
ui_action = st.text_area("조치내용 수정 가능", g_data["action"], height=180)
|
| 409 |
-
|
| 410 |
-
st.subheader("급여제공 반영 여부")
|
| 411 |
-
ui_reflection = st.text_area("급여제공 반영 내용 수정 가능", g_data["reflection"], height=130)
|
| 412 |
-
|
| 413 |
-
st.subheader("사후 조치 및 모니터링 계획")
|
| 414 |
-
ui_monitoring = st.text_area("모니터링 계획 수정 가능", g_data["monitoring"], height=130)
|
| 415 |
-
|
| 416 |
-
# 수정된 내용으로 엑셀 빌드
|
| 417 |
-
excel_file = make_excel(
|
| 418 |
-
g_data["data"],
|
| 419 |
-
ui_consult,
|
| 420 |
-
ui_action,
|
| 421 |
-
ui_reflection,
|
| 422 |
-
ui_monitoring,
|
| 423 |
-
g_data["checked_items"],
|
| 424 |
-
g_data["missing_items"]
|
| 425 |
-
)
|
| 426 |
-
|
| 427 |
-
st.download_button(
|
| 428 |
-
label="📥 수급자 상담일지 엑셀 다운로드",
|
| 429 |
-
data=excel_file,
|
| 430 |
-
file_name=f"{g_data['data']['수급자 성명']}_{g_data['data']['상담영역']}_수급자상담일지.xlsx",
|
| 431 |
-
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 432 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|