Spaces:
Sleeping
Sleeping
Upload casl_analysis.py
Browse files- casl_analysis.py +4 -321
casl_analysis.py
CHANGED
@@ -1012,195 +1012,6 @@ def analyze_transcript(transcript, age, gender):
|
|
1012 |
|
1013 |
return results, plot_image, radar_image, response
|
1014 |
|
1015 |
-
def generate_report(patient_info, analysis_results, report_type="formal"):
|
1016 |
-
"""Generate a professional report based on analysis results"""
|
1017 |
-
|
1018 |
-
patient_name = patient_info.get("name", "")
|
1019 |
-
record_id = patient_info.get("record_id", "")
|
1020 |
-
age = patient_info.get("age", "")
|
1021 |
-
gender = patient_info.get("gender", "")
|
1022 |
-
assessment_date = patient_info.get("assessment_date", datetime.now().strftime('%m/%d/%Y'))
|
1023 |
-
clinician = patient_info.get("clinician", "")
|
1024 |
-
|
1025 |
-
prompt = f"""
|
1026 |
-
You are a professional Speech-Language Pathologist creating a {report_type} report based on an assessment.
|
1027 |
-
|
1028 |
-
PATIENT INFORMATION:
|
1029 |
-
Name: {patient_name}
|
1030 |
-
Record ID: {record_id}
|
1031 |
-
Age: {age}
|
1032 |
-
Gender: {gender}
|
1033 |
-
Assessment Date: {assessment_date}
|
1034 |
-
Clinician: {clinician}
|
1035 |
-
|
1036 |
-
ASSESSMENT RESULTS:
|
1037 |
-
{analysis_results}
|
1038 |
-
|
1039 |
-
Please create a professional {report_type} report that includes:
|
1040 |
-
1. Patient information and assessment details
|
1041 |
-
2. Summary of findings (strengths and areas of concern)
|
1042 |
-
3. Detailed analysis of language domains
|
1043 |
-
4. Specific recommendations for therapy that directly address the patient's unique speech patterns
|
1044 |
-
5. Recommendation for frequency and duration of services
|
1045 |
-
6. 2-3 specific questions to ask the patient that target their particular speech difficulties
|
1046 |
-
|
1047 |
-
IMPORTANT: For both recommendations and questions, refer to specific examples from the patient's speech sample to make them personalized. Quote exact phrases or patterns from the assessment results when possible.
|
1048 |
-
|
1049 |
-
Use clear, professional language appropriate for {'educational professionals' if report_type == 'formal' else 'parents and caregivers'}.
|
1050 |
-
Format the report with proper headings and sections.
|
1051 |
-
"""
|
1052 |
-
|
1053 |
-
# Call the API or use demo mode
|
1054 |
-
if bedrock_client:
|
1055 |
-
report = call_bedrock(prompt, max_tokens=6000)
|
1056 |
-
else:
|
1057 |
-
# For demo, create a simulated report
|
1058 |
-
if report_type == 'formal':
|
1059 |
-
report = f"""
|
1060 |
-
# FORMAL LANGUAGE ASSESSMENT REPORT
|
1061 |
-
|
1062 |
-
**Date of Assessment:** {assessment_date}
|
1063 |
-
**Clinician:** {clinician}
|
1064 |
-
|
1065 |
-
## PATIENT INFORMATION
|
1066 |
-
**Name:** {patient_name}
|
1067 |
-
**Record ID:** {record_id}
|
1068 |
-
**Age:** {age}
|
1069 |
-
**Gender:** {gender}
|
1070 |
-
|
1071 |
-
## ASSESSMENT SUMMARY
|
1072 |
-
|
1073 |
-
The patient was assessed using the Comprehensive Assessment of Spoken Language, Second Edition (CASL-2) to evaluate language skills across multiple domains. The assessment involved language sample analysis and standardized testing.
|
1074 |
-
|
1075 |
-
## KEY FINDINGS
|
1076 |
-
|
1077 |
-
**Areas of Strength:**
|
1078 |
-
- Ability to maintain conversational topics
|
1079 |
-
- Good vocabulary for everyday topics
|
1080 |
-
- Strong nonverbal communication skills
|
1081 |
-
|
1082 |
-
**Areas of Challenge:**
|
1083 |
-
- Word-finding difficulties during conversation
|
1084 |
-
- Grammatical errors in complex sentences
|
1085 |
-
- Difficulty with abstract language concepts
|
1086 |
-
|
1087 |
-
## DETAILED ANALYSIS
|
1088 |
-
|
1089 |
-
**Lexical/Semantic Skills:** Standard Score 91 (27th percentile) - Low Average Range
|
1090 |
-
The student demonstrates adequate vocabulary but struggles with retrieving specific words during conversation. Word-finding pauses were noted throughout the language sample.
|
1091 |
-
|
1092 |
-
**Syntactic Skills:** Standard Score 85 (16th percentile) - Low Average Range
|
1093 |
-
The student shows difficulty with complex grammatical structures, particularly verb tense consistency and complex sentence formation.
|
1094 |
-
|
1095 |
-
**Supralinguistic Skills:** Standard Score 83 (13th percentile) - Below Average Range
|
1096 |
-
The student struggles with understanding figurative language, making inferences, and comprehending abstract concepts.
|
1097 |
-
|
1098 |
-
## RECOMMENDATIONS
|
1099 |
-
|
1100 |
-
1. Speech-Language Therapy focused on:
|
1101 |
-
- Word-finding strategies using semantic feature analysis
|
1102 |
-
- Structured grammatical exercises to improve sentence complexity
|
1103 |
-
- Explicit instruction in figurative language comprehension
|
1104 |
-
- Narrative language development using visual supports
|
1105 |
-
|
1106 |
-
2. Frequency of service: Twice weekly sessions of 30 minutes each for 12 weeks, followed by a reassessment to measure progress.
|
1107 |
-
|
1108 |
-
3. Classroom accommodations including:
|
1109 |
-
- Extended time for verbal responses
|
1110 |
-
- Visual supports for complex instructions
|
1111 |
-
- Pre-teaching of vocabulary for academic units
|
1112 |
-
|
1113 |
-
## PROGNOSIS
|
1114 |
-
|
1115 |
-
The prognosis for improvement is good with consistent therapeutic intervention and support. Regular reassessment is recommended to monitor progress.
|
1116 |
-
|
1117 |
-
## TARGETED QUESTIONS FOR ASSESSMENT FOLLOW-UP
|
1118 |
-
|
1119 |
-
1. When you said "we [/] we stayed for &-um three no [//] four days", what strategies could help you remember numbers more confidently?
|
1120 |
-
|
1121 |
-
2. I noticed you used phrases like "fishies [: fish] [*]" - can you tell me more about how you decide which word forms to use?
|
1122 |
-
|
1123 |
-
3. When you're trying to think of a word like when you said "&-um &-um sprinkles! that's the word", what helps you find the right word?
|
1124 |
-
|
1125 |
-
Respectfully submitted,
|
1126 |
-
|
1127 |
-
{clinician}
|
1128 |
-
Speech-Language Pathologist
|
1129 |
-
"""
|
1130 |
-
else:
|
1131 |
-
report = f"""
|
1132 |
-
# PARENT-FRIENDLY LANGUAGE ASSESSMENT SUMMARY
|
1133 |
-
|
1134 |
-
**Date of Assessment:** {assessment_date}
|
1135 |
-
**Clinician:** {clinician}
|
1136 |
-
|
1137 |
-
## PATIENT INFORMATION
|
1138 |
-
**Name:** {patient_name}
|
1139 |
-
**Record ID:** {record_id}
|
1140 |
-
**Age:** {age}
|
1141 |
-
**Gender:** {gender}
|
1142 |
-
|
1143 |
-
## ASSESSMENT SUMMARY
|
1144 |
-
|
1145 |
-
We completed a language assessment to better understand your child's communication strengths and challenges. This helps us create a plan to support their development.
|
1146 |
-
|
1147 |
-
## KEY FINDINGS
|
1148 |
-
|
1149 |
-
**Areas of Strength:**
|
1150 |
-
- Ability to maintain conversational topics
|
1151 |
-
- Good vocabulary for everyday topics
|
1152 |
-
- Strong nonverbal communication skills
|
1153 |
-
|
1154 |
-
**Areas of Challenge:**
|
1155 |
-
- Word-finding difficulties during conversation
|
1156 |
-
- Grammatical errors in complex sentences
|
1157 |
-
- Difficulty with abstract language concepts
|
1158 |
-
|
1159 |
-
## DETAILED ANALYSIS
|
1160 |
-
|
1161 |
-
**Lexical/Semantic Skills:** Standard Score 91 (27th percentile) - Low Average Range
|
1162 |
-
The student demonstrates adequate vocabulary but struggles with retrieving specific words during conversation. Word-finding pauses were noted throughout the language sample.
|
1163 |
-
|
1164 |
-
**Syntactic Skills:** Standard Score 85 (16th percentile) - Low Average Range
|
1165 |
-
The student shows difficulty with complex grammatical structures, particularly verb tense consistency and complex sentence formation.
|
1166 |
-
|
1167 |
-
**Supralinguistic Skills:** Standard Score 83 (13th percentile) - Below Average Range
|
1168 |
-
The student struggles with understanding figurative language, making inferences, and comprehending abstract concepts.
|
1169 |
-
|
1170 |
-
## RECOMMENDATIONS
|
1171 |
-
|
1172 |
-
We recommend:
|
1173 |
-
- Word-finding strategies using semantic feature analysis
|
1174 |
-
- Structured grammatical exercises to improve sentence complexity
|
1175 |
-
- Explicit instruction in figurative language comprehension
|
1176 |
-
- Narrative language development using visual supports
|
1177 |
-
|
1178 |
-
2. We recommend therapy twice a week for 30 minutes. This consistency will help your child make better progress.
|
1179 |
-
|
1180 |
-
3. In school, your child may benefit from:
|
1181 |
-
- Extended time for verbal responses
|
1182 |
-
- Visual supports for complex instructions
|
1183 |
-
- Pre-teaching of vocabulary for academic units
|
1184 |
-
|
1185 |
-
## PROGNOSIS
|
1186 |
-
|
1187 |
-
With regular therapy and support at home, we expect your child to make good progress in these areas.
|
1188 |
-
|
1189 |
-
## QUESTIONS TO ASK AT HOME
|
1190 |
-
|
1191 |
-
1. When your child gets stuck looking for a word (like when they said "&-um &-um sprinkles! that's the word"), what helps them find it most effectively?
|
1192 |
-
|
1193 |
-
2. Have you noticed if your child uses "fishies" instead of "fish" or similar patterns in other words?
|
1194 |
-
|
1195 |
-
3. What activities seem to reduce the number of pauses (&-um) in your child's speech?
|
1196 |
-
|
1197 |
-
Please reach out with any questions!
|
1198 |
-
|
1199 |
-
{clinician}
|
1200 |
-
Speech-Language Pathologist
|
1201 |
-
"""
|
1202 |
-
|
1203 |
-
return report
|
1204 |
|
1205 |
def transcribe_audio(audio_path, patient_age):
|
1206 |
"""Transcribe an audio recording using CHAT format"""
|
@@ -1496,7 +1307,6 @@ def create_interface():
|
|
1496 |
|
1497 |
with gr.Row():
|
1498 |
load_record_btn = gr.Button("Load for Analysis", variant="primary")
|
1499 |
-
load_to_report_btn = gr.Button("Load to Report Generator", variant="secondary")
|
1500 |
|
1501 |
with gr.Column(scale=1):
|
1502 |
# Record details
|
@@ -1582,11 +1392,11 @@ def create_interface():
|
|
1582 |
# Load record into analysis tab
|
1583 |
def load_patient_record_to_analysis(record_id):
|
1584 |
if not record_id:
|
1585 |
-
return gr.update(selected=1),
|
1586 |
|
1587 |
record_data = load_patient_record(record_id)
|
1588 |
if not record_data:
|
1589 |
-
return gr.update(selected=1), "", "", "", "male", "", "", ""
|
1590 |
|
1591 |
# Extract data
|
1592 |
patient_info = record_data.get("patient_info", {})
|
@@ -1622,53 +1432,11 @@ def create_interface():
|
|
1622 |
]
|
1623 |
)
|
1624 |
|
1625 |
-
# ===============================
|
1626 |
-
# Report Generator Tab
|
1627 |
-
# ===============================
|
1628 |
-
with gr.TabItem("Report Generator", id=2):
|
1629 |
-
with gr.Row():
|
1630 |
-
with gr.Column(scale=1):
|
1631 |
-
gr.Markdown("### Generate Professional Reports")
|
1632 |
-
|
1633 |
-
# Patient info
|
1634 |
-
with gr.Group(elem_classes="container patient-info"):
|
1635 |
-
gr.Markdown("#### Patient Information")
|
1636 |
-
report_patient_name = gr.Textbox(label="Patient Name", placeholder="Enter patient name")
|
1637 |
-
report_record_id = gr.Textbox(label="Record ID", placeholder="Enter record ID")
|
1638 |
-
report_age = gr.Number(label="Age", value=8, minimum=1, maximum=120)
|
1639 |
-
report_gender = gr.Radio(["male", "female", "other"], label="Gender", value="male")
|
1640 |
-
report_date = gr.Textbox(
|
1641 |
-
label="Assessment Date",
|
1642 |
-
placeholder="MM/DD/YYYY",
|
1643 |
-
value=datetime.now().strftime('%m/%d/%Y')
|
1644 |
-
)
|
1645 |
-
report_clinician = gr.Textbox(label="Clinician", placeholder="Enter clinician name")
|
1646 |
-
|
1647 |
-
with gr.Group():
|
1648 |
-
gr.Markdown("#### Assessment Results")
|
1649 |
-
report_results = gr.Textbox(
|
1650 |
-
label="Paste assessment results or notes here",
|
1651 |
-
placeholder="Include key findings, test scores, and observations...",
|
1652 |
-
lines=10
|
1653 |
-
)
|
1654 |
-
|
1655 |
-
report_type = gr.Radio(
|
1656 |
-
["Formal (for professionals)", "Parent-friendly"],
|
1657 |
-
label="Report Type",
|
1658 |
-
value="Formal (for professionals)"
|
1659 |
-
)
|
1660 |
-
|
1661 |
-
generate_report_btn = gr.Button("Generate Report", variant="primary")
|
1662 |
-
|
1663 |
-
with gr.Column(scale=1):
|
1664 |
-
report_output = gr.Markdown()
|
1665 |
-
report_download_btn = gr.Button("Download Report as PDF", variant="secondary")
|
1666 |
-
report_download_status = gr.Markdown("")
|
1667 |
|
1668 |
# ===============================
|
1669 |
# Transcription Tool Tab
|
1670 |
# ===============================
|
1671 |
-
with gr.TabItem("Transcription Tool", id=
|
1672 |
with gr.Row():
|
1673 |
with gr.Column(scale=1):
|
1674 |
gr.Markdown("### Audio Transcription Tool")
|
@@ -1694,7 +1462,7 @@ def create_interface():
|
|
1694 |
# ===============================
|
1695 |
# SLP Assistant Tab
|
1696 |
# ===============================
|
1697 |
-
with gr.TabItem("SLP Assistant", id=
|
1698 |
with gr.Row():
|
1699 |
with gr.Column(scale=1):
|
1700 |
gr.Markdown("### SLP Knowledge Assistant")
|
@@ -2205,49 +1973,6 @@ def create_interface():
|
|
2205 |
outputs=[export_status]
|
2206 |
)
|
2207 |
|
2208 |
-
report_download_btn.click(
|
2209 |
-
export_pdf,
|
2210 |
-
inputs=[
|
2211 |
-
report_output,
|
2212 |
-
report_patient_name,
|
2213 |
-
report_record_id,
|
2214 |
-
report_age,
|
2215 |
-
report_gender,
|
2216 |
-
report_date,
|
2217 |
-
report_clinician
|
2218 |
-
],
|
2219 |
-
outputs=[report_download_status]
|
2220 |
-
)
|
2221 |
-
|
2222 |
-
# Report generator button
|
2223 |
-
def on_generate_report(name, record_id, age, gender, date, clinician, results, report_type):
|
2224 |
-
patient_info = {
|
2225 |
-
"name": name,
|
2226 |
-
"record_id": record_id,
|
2227 |
-
"age": age,
|
2228 |
-
"gender": gender,
|
2229 |
-
"assessment_date": date,
|
2230 |
-
"clinician": clinician
|
2231 |
-
}
|
2232 |
-
|
2233 |
-
report_type_val = "formal" if "Formal" in report_type else "parent-friendly"
|
2234 |
-
|
2235 |
-
try:
|
2236 |
-
report = generate_report(patient_info, results, report_type_val)
|
2237 |
-
return report
|
2238 |
-
except Exception as e:
|
2239 |
-
logger.exception("Error generating report")
|
2240 |
-
return f"Error generating report: {str(e)}"
|
2241 |
-
|
2242 |
-
generate_report_btn.click(
|
2243 |
-
on_generate_report,
|
2244 |
-
inputs=[
|
2245 |
-
report_patient_name, report_record_id, report_age,
|
2246 |
-
report_gender, report_date, report_clinician,
|
2247 |
-
report_results, report_type
|
2248 |
-
],
|
2249 |
-
outputs=[report_output]
|
2250 |
-
)
|
2251 |
|
2252 |
# Transcription button
|
2253 |
def on_transcribe_audio(audio_path, age):
|
@@ -2298,48 +2023,6 @@ def create_interface():
|
|
2298 |
q3_btn.click(lambda: "What activities help with word finding difficulties?", outputs=[question_input])
|
2299 |
q4_btn.click(lambda: "When should I reassess a patient?", outputs=[question_input])
|
2300 |
|
2301 |
-
# Function to load patient record into the report generator tab
|
2302 |
-
def load_patient_record_to_report(record_id):
|
2303 |
-
if not record_id:
|
2304 |
-
return gr.update(selected=1), "", "", "", "male", "", "", ""
|
2305 |
-
|
2306 |
-
record_data = load_patient_record(record_id)
|
2307 |
-
if not record_data:
|
2308 |
-
return gr.update(selected=1), "", "", "", "male", "", "", ""
|
2309 |
-
|
2310 |
-
# Extract data
|
2311 |
-
patient_info = record_data.get("patient_info", {})
|
2312 |
-
analysis_results = record_data.get("analysis_results", {})
|
2313 |
-
|
2314 |
-
# Get the raw analysis results as a string
|
2315 |
-
raw_response = analysis_results.get("raw_response", "")
|
2316 |
-
|
2317 |
-
# Create status message for the record loading
|
2318 |
-
status_msg = f"✅ Record loaded successfully: {patient_info.get('name', 'Unknown')} ({record_id})"
|
2319 |
-
|
2320 |
-
return (
|
2321 |
-
gr.update(selected=2), # Switch to Report Generator tab
|
2322 |
-
patient_info.get("name", ""),
|
2323 |
-
patient_info.get("record_id", ""),
|
2324 |
-
patient_info.get("age", ""),
|
2325 |
-
patient_info.get("gender", "male"),
|
2326 |
-
patient_info.get("assessment_date", ""),
|
2327 |
-
patient_info.get("clinician", ""),
|
2328 |
-
raw_response,
|
2329 |
-
status_msg
|
2330 |
-
)
|
2331 |
-
|
2332 |
-
# Connect the load_to_report_btn to its handler
|
2333 |
-
load_to_report_btn.click(
|
2334 |
-
load_patient_record_to_report,
|
2335 |
-
inputs=[selected_record_id],
|
2336 |
-
outputs=[
|
2337 |
-
main_tabs,
|
2338 |
-
report_patient_name, report_record_id, report_age, report_gender,
|
2339 |
-
report_date, report_clinician, report_results,
|
2340 |
-
records_status
|
2341 |
-
]
|
2342 |
-
)
|
2343 |
|
2344 |
return app
|
2345 |
|
|
|
1012 |
|
1013 |
return results, plot_image, radar_image, response
|
1014 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1015 |
|
1016 |
def transcribe_audio(audio_path, patient_age):
|
1017 |
"""Transcribe an audio recording using CHAT format"""
|
|
|
1307 |
|
1308 |
with gr.Row():
|
1309 |
load_record_btn = gr.Button("Load for Analysis", variant="primary")
|
|
|
1310 |
|
1311 |
with gr.Column(scale=1):
|
1312 |
# Record details
|
|
|
1392 |
# Load record into analysis tab
|
1393 |
def load_patient_record_to_analysis(record_id):
|
1394 |
if not record_id:
|
1395 |
+
return gr.update(selected=1), "", "", "", "male", "", "", "", ""
|
1396 |
|
1397 |
record_data = load_patient_record(record_id)
|
1398 |
if not record_data:
|
1399 |
+
return gr.update(selected=1), "", "", "", "male", "", "", "", ""
|
1400 |
|
1401 |
# Extract data
|
1402 |
patient_info = record_data.get("patient_info", {})
|
|
|
1432 |
]
|
1433 |
)
|
1434 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1435 |
|
1436 |
# ===============================
|
1437 |
# Transcription Tool Tab
|
1438 |
# ===============================
|
1439 |
+
with gr.TabItem("Transcription Tool", id=2):
|
1440 |
with gr.Row():
|
1441 |
with gr.Column(scale=1):
|
1442 |
gr.Markdown("### Audio Transcription Tool")
|
|
|
1462 |
# ===============================
|
1463 |
# SLP Assistant Tab
|
1464 |
# ===============================
|
1465 |
+
with gr.TabItem("SLP Assistant", id=3):
|
1466 |
with gr.Row():
|
1467 |
with gr.Column(scale=1):
|
1468 |
gr.Markdown("### SLP Knowledge Assistant")
|
|
|
1973 |
outputs=[export_status]
|
1974 |
)
|
1975 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1976 |
|
1977 |
# Transcription button
|
1978 |
def on_transcribe_audio(audio_path, age):
|
|
|
2023 |
q3_btn.click(lambda: "What activities help with word finding difficulties?", outputs=[question_input])
|
2024 |
q4_btn.click(lambda: "When should I reassess a patient?", outputs=[question_input])
|
2025 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026 |
|
2027 |
return app
|
2028 |
|