devorah-ai-2016's picture
Update app.py
35f06e1 verified
Raw History Blame Contribute Delete
26.7 kB
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"
)