oak999/WISC_report
0
1import camelot2import pandas as pd3import math4import openai5import streamlit as st6import plotly.graph_objs as go7from docx import Document8from docx.shared import Inches9from docx.enum.text import WD_ALIGN_PARAGRAPH10import tempfile11import base64 12 13def extract_text_tables(file_path):14 tables = {}15 # Extract tables16 extracted_tables = camelot.read_pdf(file_path, pages='all', flavor='stream')17 18 for page_number, table in enumerate(extracted_tables):19 # Convert the table to a Pandas DataFrame20 df = table.df21 22 # Store the table in the dictionary with the page number as the key23 tables[page_number] = df24 return tables25 26def interpret_job(score): 27 if score >= 130:28 text = "학생은 창조적, 통설적, 전문적인 일에 적합할 수 있으며, 그러한 직업의 예시로는 학자, 교수, 고급공무원 등이 있습니다."29 elif score >=120:30 text = "학생은 지도적, 전문적, 행동적인 일에 적합할 수 있으며, 그러한 직업의 예시로는 의사, 변호사, 작가, 교사 등이 있습니다." 31 elif score >=110:32 text = "학생은 행동적, 지도적인 일에 적합할 수 있으며, 그러한 직업의 예시로는 고급경영자, 고급기술자 등이 있습니다."33 elif score >=90:34 text = "학생은 행동적, 지도적인 일에 적합할 수 있으며, 그러한 직업의 예시로는 각종 상급 기능직 등이 있습니다."35 elif score >=80:36 text = "학생은 행동적, 숙련된 일에 적합할 수 있으며, 그러한 직업의 예시로는 각종 중급 기능직 등이 있습니다."37 elif score >=70:38 text = "학생은 감독하에 행동적으로 수행하는 직종에 적합할 수 있습니다."39 elif score <=69:40 text = "학생은 반복적이고 단순한 일을 수행하는 직종에 적합할 수 있습니다."41 else:42 print("해당되는 범위가 없습니다. 데이터를 다시 확인해보세요.")43 return text44 45st.set_page_config(46 page_title="보고서", 47 page_icon="📊",48 initial_sidebar_state="expanded"49 )50 51uploaded_file = st.sidebar.file_uploader('파일 업로드')52uploaded_key = st.sidebar.text_input('OpenAI 키를 입력')53 54if uploaded_file is not None:55 56 # byte object into a PDF file 57 with open("input.pdf", "wb") as f:58 base64_pdf = base64.b64encode(uploaded_file.read()).decode('utf-8')59 f.write(base64.b64decode(base64_pdf))60 f.close()61 62 korean_tables = extract_text_tables("input.pdf")63 64 df = korean_tables65 df_info = pd.DataFrame(df[0])66 67 # extract the variables68 examiner = df_info.iloc[0, 0]69 subject = df_info.iloc[0, 1]70 gender = df_info.iloc[1, 1]71 testing_date = df_info.iloc[2, 0]72 age = df_info.iloc[2, 1].split()[0][1:3]73 birth_date = df_info.iloc[2, 1].split()[1][1:-1]74 75 # subscales scores76 subscales_df = pd.DataFrame(df[1])77 subscales_df.columns = ['원점수', '환산점수', '백분위', '추정연령', '측정표준오차(SEM)']78 subscales_df.drop('원점수', axis=1, inplace=True)79 subscales_df.insert(0,'소검사', ['공통성', '어휘', '상식', '이해', '토막짜기', '퍼즐', '행렬추론', '무게비교', '공통그림찾기', '산수', '숫자', '그림기억', '순차연결', '기호쓰기', '동형찾기', '선택'])80 # print(df_subscales)81 82 # scales scores83 scales_df = pd.DataFrame(df[2])84 ci_per = scales_df.iat[0,3]85 scales_df.drop([0], axis=0, inplace=True)86 scales_df.columns = ['환산점수', '지표점수', '백분위', f'신뢰구간({ci_per}%)', '진단분류(수준)', '측정표준오차(SEM)']87 scales_df.drop('환산점수', axis=1, inplace=True)88 scales_df.insert(0,'지표', ['언어이해', '시공간', '유동추론', '작업기억', '처리속도', '전체IQ'])89 # print(df_scales)90 91 fsiq_index = scales_df[scales_df["지표"] == "전체IQ"].index[0]92 93 fsiq_score = int(scales_df.loc[fsiq_index, "지표점수"])94 fsiq_percentile = float(scales_df.loc[fsiq_index, "백분위"])95 fsiq_ranking = 100 - math.ceil(fsiq_percentile)96 fsiq_confidence = scales_df.loc[fsiq_index, "신뢰구간(95%)"]97 fsiq_level = scales_df.loc[fsiq_index, "진단분류(수준)"]98 body = f'''K-WISC-V(한국 웩슬러 아동 지능검사 5판)로 추정한 현재 지능은 '{fsiq_level}'(FSIQ: {fsiq_score}) 범위에 속하는 하며, 백분위가 {math.ceil(fsiq_percentile)}%ile로 나타났다. 이는 {fsiq_score}점 아래에 있는 학생들이 전체 중 {math.ceil(fsiq_percentile)}%가 있다는 것을 의미합니다. 좀 더 쉽게 설명하면, 전체 100명 중 {math.ceil(fsiq_percentile)}번째에 위치 하는 것이며, 앞에서 {math.ceil(fsiq_ranking)}등이라고 할 수 있습니다.99 95% 신뢰구간은 {fsiq_confidence}으로 학생이 검사를 100번 실시했을 때 95번은 {fsiq_confidence}사이의 점수를 받게 된다는 의미입니다. 학생의 컨디션이나 기타 환경적 요소 때문에 어떤 날은 좀 더 높은 점수를 받을 수 있고, 어떤 날은 좀 더 낮은 점수를 받을 수 있습니다. 대부분의 경우는 두 점수 사이의 평균적인 점수를 받게 될 것입니다. '''100 101 fig = go.Figure()102 fig.add_trace(go.Scatter(103 x=['VCI', 'VSI', 'FRI', 'WMI', 'PSI', 'FSIQ'],104 y=scales_df['지표점수'],105 mode='lines+markers+text',106 name='K-WISC-V',107 line=dict(color='green'),108 text=scales_df['지표점수'],109 textposition="top center"110 ))111 fig.update_yaxes(title_text=None, showgrid=False, showline=True, linecolor='lightgrey', linewidth=2)112 fig.update_xaxes(title_text=None, showgrid=False, showline=True, linecolor='lightgrey', linewidth=2)113 fig.update_layout(114 plot_bgcolor='rgba(0,0,0,0)',115 yaxis_range=[40, 160],116 margin=dict(l=0, r=0, t=50, b=40)117 )118 119 st.header('연구 참가자 K-WISC-V 보고서')120 st.markdown('')121 col1, col2 = st.columns(2)122 with col1:123 st.markdown(f'> 인적사항: {subject} ({gender}, {age})')124 with col2:125 st.markdown(f'> 검사일: {testing_date}')126 127 st.markdown('')128 st.markdown('')129 130 st.markdown('##### 지표 점수')131 st.dataframe(scales_df, use_container_width=True)132 133 with st.expander('지표 점수 차트'):134 st.plotly_chart(fig, use_container_width=True)135 136 st.markdown('')137 138 st.markdown('##### 소검사 점수')139 st.dataframe(subscales_df, use_container_width=True)140 141 st.markdown('')142 143 st.markdown('##### 해석')144 interpretation = st.text_area("해석",145 body+interpret_job(fsiq_score),146 height=200, 147 label_visibility="collapsed")148 generate = st.checkbox("보고서 생성")149 if generate == True:150 # Load the report.docx file using python-docx151 document = Document('./report/report.docx')152 # Replace the markers with the corresponding values153 for table in document.tables:154 for row in table.rows:155 for cell in row.cells:156 if '<<name>>' in cell.text:157 cell.text = cell.text.replace('<<name>>', subject)158 if '<<age>>' in cell.text:159 cell.text = cell.text.replace('<<age>>', age)160 if '<<test_date>>' in cell.text:161 cell.text = cell.text.replace('<<test_date>>', testing_date)162 if '<<gender>>' in cell.text:163 cell.text = cell.text.replace('<<gender>>', gender)164 if '<<grade>>' in cell.text:165 cell.text = cell.text.replace('<<grade>>', '')166 if '<<birth_date>>' in cell.text:167 cell.text = cell.text.replace('<<birth_date>>', birth_date)168 if '<<picture>>' in cell.text:169 # Save the plotly chart to a temporary file170 with tempfile.NamedTemporaryFile(suffix='.png') as tmp_file:171 fig.write_image(tmp_file.name, scale=2)172 # Insert the plotly chart as a picture173 cell.text = cell.text.replace('<<picture>>', '')174 run = cell.paragraphs[0].add_run()175 run.add_picture(tmp_file.name, width=Inches(6), height=Inches(3))176 if '<<interpretation>>' in cell.text:177 # Insert the interpretation text178 cell.text = cell.text.replace('<<interpretation>>', '')179 paragraph = cell.paragraphs[0]180 paragraph.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY181 paragraph.add_run(interpretation)182 183 # Offer the report.docx file for download with the streamlit download button184 document.save('modified_report.docx')185 186 st.download_button(187 label="보고서 받기",188 data=open('modified_report.docx', 'rb').read(),189 file_name=f"{subject} 연구 참가자 보고서.docx",190 mime="application/octet-stream"191 )192 193 openai.api_key = uploaded_key194 prompt = f'''195 {scales_df}196 {subscales_df}197 198 작성방법199 - 위 데이터는 WISC-V검사의 결과이며, 전체 IQ는 무조건 처음 언급해준다. 작성 순서는 전체 IQ, 다른 지표 점수, 소검사 순으로 해석하고 작성한다.200 - 실시를 하지 않은 검사는 언급할 필요가 없다.201 - 소검사의 환산점수의 평균은 10점이다.202 '''203 if st.button("AI 분석", type="primary"):204 st.markdown("----")205 res_box = st.empty()206 report = []207 # Looping over the response208 output = openai.ChatCompletion.create(model='gpt-3.5-turbo',209 messages=[{"role": "system", "content": "너는 임상 심리 전문가이다. 심리검사 점수는 정확하게 참고해야한다."},210 {"role": "user", "content":prompt}],211 max_tokens=2000, 212 temperature = 0.4)213 for item in output['choices']:214 chatgpt_output = item['message']['content']215 st.markdown(f''' ##### Chat-GPT 답변216{chatgpt_output}''')217 st.markdown("----")218 