Spaces:
Sleeping
Sleeping
Delete app.py
Browse files
app.py
DELETED
|
@@ -1,260 +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
|
| 7 |
-
|
| 8 |
-
st.set_page_config(page_title="BeeCare 방문목욕 문서센터", page_icon="🛁", layout="wide")
|
| 9 |
-
|
| 10 |
-
st.markdown("""
|
| 11 |
-
<style>
|
| 12 |
-
.stApp { background:#fff7fb; }
|
| 13 |
-
.main-title {
|
| 14 |
-
text-align:center; font-size:38px; font-weight:900;
|
| 15 |
-
color:#b91558; margin-bottom:10px;
|
| 16 |
-
}
|
| 17 |
-
.sub-title {
|
| 18 |
-
text-align:center; font-size:18px; color:#555; margin-bottom:30px;
|
| 19 |
-
}
|
| 20 |
-
.card {
|
| 21 |
-
background:white; border:1px solid #f3c1d4; border-radius:20px;
|
| 22 |
-
padding:22px; box-shadow:0 4px 14px rgba(190,40,110,.12);
|
| 23 |
-
}
|
| 24 |
-
</style>
|
| 25 |
-
""", unsafe_allow_html=True)
|
| 26 |
-
|
| 27 |
-
st.markdown('<div class="main-title">🛁 방문목욕 문서센터</div>', unsafe_allow_html=True)
|
| 28 |
-
st.markdown(
|
| 29 |
-
'<div class="sub-title">방문목욕 상담일지, 제공기록지, 안전점검표, 목욕 전후 상태확인 기록을 엑셀로 생성합니다.</div>',
|
| 30 |
-
unsafe_allow_html=True
|
| 31 |
-
)
|
| 32 |
-
|
| 33 |
-
def style_sheet(ws, title):
|
| 34 |
-
ws.sheet_view.showGridLines = False
|
| 35 |
-
|
| 36 |
-
for col in range(1, 10):
|
| 37 |
-
ws.column_dimensions[get_column_letter(col)].width = 14
|
| 38 |
-
|
| 39 |
-
ws.column_dimensions["A"].width = 16
|
| 40 |
-
ws.column_dimensions["B"].width = 16
|
| 41 |
-
ws.column_dimensions["C"].width = 16
|
| 42 |
-
ws.column_dimensions["D"].width = 16
|
| 43 |
-
ws.column_dimensions["E"].width = 16
|
| 44 |
-
ws.column_dimensions["F"].width = 16
|
| 45 |
-
ws.column_dimensions["G"].width = 16
|
| 46 |
-
ws.column_dimensions["H"].width = 14
|
| 47 |
-
ws.column_dimensions["I"].width = 14
|
| 48 |
-
|
| 49 |
-
thin = Side(style="thin", color="999999")
|
| 50 |
-
border = Border(left=thin, right=thin, top=thin, bottom=thin)
|
| 51 |
-
fill = PatternFill("solid", fgColor="FCE4EC")
|
| 52 |
-
section_fill = PatternFill("solid", fgColor="F8BBD0")
|
| 53 |
-
|
| 54 |
-
# 상단 제목 + 결재라인
|
| 55 |
-
ws.merge_cells("A1:G3")
|
| 56 |
-
ws["A1"] = title
|
| 57 |
-
ws["A1"].font = Font(size=30, bold=True)
|
| 58 |
-
ws["A1"].alignment = Alignment(horizontal="center", vertical="center")
|
| 59 |
-
|
| 60 |
-
ws["H1"] = "담당자"
|
| 61 |
-
ws["I1"] = "시설장"
|
| 62 |
-
ws["H2"] = ""
|
| 63 |
-
ws["I2"] = ""
|
| 64 |
-
|
| 65 |
-
for cell in ["H1", "I1"]:
|
| 66 |
-
ws[cell].font = Font(size=12, bold=True)
|
| 67 |
-
ws[cell].fill = fill
|
| 68 |
-
ws[cell].alignment = Alignment(horizontal="center", vertical="center")
|
| 69 |
-
ws[cell].border = border
|
| 70 |
-
|
| 71 |
-
for cell in ["H2", "I2"]:
|
| 72 |
-
ws[cell].alignment = Alignment(horizontal="center", vertical="center")
|
| 73 |
-
ws[cell].border = border
|
| 74 |
-
|
| 75 |
-
ws.row_dimensions[1].height = 26
|
| 76 |
-
ws.row_dimensions[2].height = 38
|
| 77 |
-
ws.row_dimensions[3].height = 20
|
| 78 |
-
|
| 79 |
-
return border, fill, section_fill
|
| 80 |
-
|
| 81 |
-
def section(ws, row, title, border, section_fill):
|
| 82 |
-
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=9)
|
| 83 |
-
cell = ws.cell(row=row, column=1)
|
| 84 |
-
cell.value = title
|
| 85 |
-
cell.font = Font(size=25, bold=True)
|
| 86 |
-
cell.fill = section_fill
|
| 87 |
-
cell.alignment = Alignment(horizontal="center", vertical="center")
|
| 88 |
-
cell.border = border
|
| 89 |
-
ws.row_dimensions[row].height = 34
|
| 90 |
-
return row + 1
|
| 91 |
-
|
| 92 |
-
def label_value(ws, row, label, value, border, fill):
|
| 93 |
-
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=2)
|
| 94 |
-
ws.merge_cells(start_row=row, start_column=3, end_row=row, end_column=5)
|
| 95 |
-
ws.merge_cells(start_row=row, start_column=6, end_row=row, end_column=7)
|
| 96 |
-
ws.merge_cells(start_row=row, start_column=8, end_row=row, end_column=9)
|
| 97 |
-
|
| 98 |
-
ws.cell(row, 1).value = label[0]
|
| 99 |
-
ws.cell(row, 3).value = value[0]
|
| 100 |
-
ws.cell(row, 6).value = label[1]
|
| 101 |
-
ws.cell(row, 8).value = value[1]
|
| 102 |
-
|
| 103 |
-
for col in range(1, 10):
|
| 104 |
-
c = ws.cell(row, col)
|
| 105 |
-
c.border = border
|
| 106 |
-
c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
| 107 |
-
if col in [1, 2, 6, 7]:
|
| 108 |
-
c.fill = fill
|
| 109 |
-
c.font = Font(bold=True)
|
| 110 |
-
|
| 111 |
-
ws.row_dimensions[row].height = 28
|
| 112 |
-
return row + 1
|
| 113 |
-
|
| 114 |
-
def text_box(ws, row, title, text, border, fill):
|
| 115 |
-
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=9)
|
| 116 |
-
ws.cell(row, 1).value = title
|
| 117 |
-
ws.cell(row, 1).font = Font(size=13, bold=True)
|
| 118 |
-
ws.cell(row, 1).fill = fill
|
| 119 |
-
ws.cell(row, 1).alignment = Alignment(horizontal="center", vertical="center")
|
| 120 |
-
for col in range(1, 10):
|
| 121 |
-
ws.cell(row, col).border = border
|
| 122 |
-
|
| 123 |
-
row += 1
|
| 124 |
-
ws.merge_cells(start_row=row, start_column=1, end_row=row + 2, end_column=9)
|
| 125 |
-
ws.cell(row, 1).value = text
|
| 126 |
-
ws.cell(row, 1).alignment = Alignment(horizontal="left", vertical="top", wrap_text=True)
|
| 127 |
-
ws.cell(row, 1).font = Font(size=12)
|
| 128 |
-
for r in range(row, row + 3):
|
| 129 |
-
for col in range(1, 10):
|
| 130 |
-
ws.cell(r, col).border = border
|
| 131 |
-
ws.row_dimensions[row].height = 70
|
| 132 |
-
return row + 3
|
| 133 |
-
|
| 134 |
-
def make_excel(doc_type, data):
|
| 135 |
-
wb = Workbook()
|
| 136 |
-
ws = wb.active
|
| 137 |
-
ws.title = doc_type[:31]
|
| 138 |
-
|
| 139 |
-
border, fill, section_fill = style_sheet(ws, doc_type)
|
| 140 |
-
|
| 141 |
-
row = 4
|
| 142 |
-
|
| 143 |
-
row = section(ws, row, "기본정보", border, section_fill)
|
| 144 |
-
row = label_value(ws, row, ["수급자명", "장기요양등급"], [data["name"], data["grade"]], border, fill)
|
| 145 |
-
row = label_value(ws, row, ["제공일자", "제공시간"], [str(data["bath_date"]), data["bath_time"]], border, fill)
|
| 146 |
-
row = label_value(ws, row, ["제공장소", "제공인력"], [data["place"], data["staff"]], border, fill)
|
| 147 |
-
|
| 148 |
-
row = section(ws, row, "작성내용", border, section_fill)
|
| 149 |
-
|
| 150 |
-
row = text_box(
|
| 151 |
-
ws, row, "목욕 전 상태",
|
| 152 |
-
data["before"] or "목욕 전 건강상태, 피부상태, 어지러움, 통증, 낙상위험 여부를 확인함.",
|
| 153 |
-
border, fill
|
| 154 |
-
)
|
| 155 |
-
|
| 156 |
-
row = text_box(
|
| 157 |
-
ws, row, "목욕 제공 내용",
|
| 158 |
-
data["service"] or "방문목욕 급여제공계획에 따라 신체청결, 피부상태 확인, 위생관리, 안전관리를 실시함.",
|
| 159 |
-
border, fill
|
| 160 |
-
)
|
| 161 |
-
|
| 162 |
-
row = text_box(
|
| 163 |
-
ws, row, "목욕 후 상태",
|
| 164 |
-
data["after"] or "목욕 후 피로감, 어지러움, 피부 이상, 낙상위험 여부를 확인하였으며 특이사항 없음.",
|
| 165 |
-
border, fill
|
| 166 |
-
)
|
| 167 |
-
|
| 168 |
-
row = text_box(
|
| 169 |
-
ws, row, "특이사항 및 조치사항",
|
| 170 |
-
data["note"] or "목욕 제공 중 특이사항은 없었으며, 상태변화 발생 시 보호자 및 담당자에게 보고하기로 함.",
|
| 171 |
-
border, fill
|
| 172 |
-
)
|
| 173 |
-
|
| 174 |
-
row = section(ws, row, "확인", border, section_fill)
|
| 175 |
-
ws.merge_cells(start_row=row, start_column=1, end_row=row, end_column=5)
|
| 176 |
-
ws.merge_cells(start_row=row, start_column=6, end_row=row, end_column=7)
|
| 177 |
-
ws.merge_cells(start_row=row, start_column=8, end_row=row, end_column=9)
|
| 178 |
-
|
| 179 |
-
ws.cell(row, 1).value = "위 내용과 같이 방문목욕 관련 기록을 작성합니다."
|
| 180 |
-
ws.cell(row, 6).value = "작성자"
|
| 181 |
-
ws.cell(row, 8).value = data["writer"]
|
| 182 |
-
|
| 183 |
-
for col in range(1, 10):
|
| 184 |
-
c = ws.cell(row, col)
|
| 185 |
-
c.border = border
|
| 186 |
-
c.alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
| 187 |
-
if col in [6, 7]:
|
| 188 |
-
c.fill = fill
|
| 189 |
-
c.font = Font(bold=True)
|
| 190 |
-
|
| 191 |
-
ws.row_dimensions[row].height = 32
|
| 192 |
-
|
| 193 |
-
# 불필요한 J열 이후 사용 방지
|
| 194 |
-
ws.print_area = "A1:I{}".format(row)
|
| 195 |
-
ws.page_setup.orientation = "portrait"
|
| 196 |
-
ws.page_setup.fitToWidth = 1
|
| 197 |
-
ws.page_setup.fitToHeight = 0
|
| 198 |
-
ws.sheet_properties.pageSetUpPr.fitToPage = True
|
| 199 |
-
|
| 200 |
-
output = BytesIO()
|
| 201 |
-
wb.save(output)
|
| 202 |
-
output.seek(0)
|
| 203 |
-
return output
|
| 204 |
-
|
| 205 |
-
st.markdown("### 생성할 문서를 선택하세요")
|
| 206 |
-
|
| 207 |
-
doc_type = st.selectbox(
|
| 208 |
-
"문서 종류",
|
| 209 |
-
[
|
| 210 |
-
"방문목욕 상담일지",
|
| 211 |
-
"목욕급여 제공기록지",
|
| 212 |
-
"방문목욕 안전점검표",
|
| 213 |
-
"목욕 전후 상태확인 기록",
|
| 214 |
-
"방문목욕 종사자 교육일지"
|
| 215 |
-
]
|
| 216 |
-
)
|
| 217 |
-
|
| 218 |
-
col1, col2 = st.columns(2)
|
| 219 |
-
|
| 220 |
-
with col1:
|
| 221 |
-
name = st.text_input("수급자명")
|
| 222 |
-
grade = st.selectbox("장기요양등급", ["1등급", "2등급", "3등급", "4등급", "5등급", "인지지원등급"])
|
| 223 |
-
bath_date = st.date_input("제공일자", value=date.today())
|
| 224 |
-
bath_time = st.text_input("제공시간", placeholder="예: 10:00~11:00")
|
| 225 |
-
|
| 226 |
-
with col2:
|
| 227 |
-
place = st.text_input("제공장소", placeholder="예: 수급자 자택")
|
| 228 |
-
staff = st.text_input("제공인력")
|
| 229 |
-
writer = st.text_input("작성자")
|
| 230 |
-
service = st.text_area("목욕 제공 내용", height=120)
|
| 231 |
-
|
| 232 |
-
before = st.text_area("목욕 전 상태", height=100)
|
| 233 |
-
after = st.text_area("목욕 후 상태", height=100)
|
| 234 |
-
note = st.text_area("특이사항 및 조치사항", height=100)
|
| 235 |
-
|
| 236 |
-
data = {
|
| 237 |
-
"name": name,
|
| 238 |
-
"grade": grade,
|
| 239 |
-
"bath_date": bath_date,
|
| 240 |
-
"bath_time": bath_time,
|
| 241 |
-
"place": place,
|
| 242 |
-
"staff": staff,
|
| 243 |
-
"writer": writer,
|
| 244 |
-
"service": service,
|
| 245 |
-
"before": before,
|
| 246 |
-
"after": after,
|
| 247 |
-
"note": note,
|
| 248 |
-
}
|
| 249 |
-
|
| 250 |
-
if st.button("엑셀 문서 생성하기", type="primary"):
|
| 251 |
-
excel_file = make_excel(doc_type, data)
|
| 252 |
-
|
| 253 |
-
st.success("엑셀 문서가 생성되었습니다.")
|
| 254 |
-
|
| 255 |
-
st.download_button(
|
| 256 |
-
label="📥 엑셀 다운로드",
|
| 257 |
-
data=excel_file,
|
| 258 |
-
file_name=f"{doc_type}_{name or '수급자'}.xlsx",
|
| 259 |
-
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 260 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|