CoolFace
Apppublic

oak999/WISC_report

sourceHugging Faceopenrailupdated 3y agoView on Hugging Face
0likes
app.py218 linesDownload Raw Back to root
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