Spaces:
Running
Running
Upload casl_analysis.py
Browse files- casl_analysis.py +579 -149
casl_analysis.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
|
| 2 |
import boto3
|
| 3 |
import json
|
| 4 |
import pandas as pd
|
|
@@ -11,9 +11,31 @@ import pickle
|
|
| 11 |
import csv
|
| 12 |
from PIL import Image
|
| 13 |
import io
|
| 14 |
-
import PyPDF2
|
| 15 |
import uuid
|
| 16 |
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
# Configure logging
|
| 19 |
logging.basicConfig(level=logging.INFO)
|
|
@@ -25,10 +47,14 @@ AWS_ACCESS_KEY = os.getenv("AWS_ACCESS_KEY", "")
|
|
| 25 |
AWS_SECRET_KEY = os.getenv("AWS_SECRET_KEY", "")
|
| 26 |
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
|
| 27 |
|
| 28 |
-
# Initialize
|
| 29 |
bedrock_client = None
|
|
|
|
|
|
|
|
|
|
| 30 |
if AWS_ACCESS_KEY and AWS_SECRET_KEY:
|
| 31 |
try:
|
|
|
|
| 32 |
bedrock_client = boto3.client(
|
| 33 |
'bedrock-runtime',
|
| 34 |
aws_access_key_id=AWS_ACCESS_KEY,
|
|
@@ -36,8 +62,30 @@ if AWS_ACCESS_KEY and AWS_SECRET_KEY:
|
|
| 36 |
region_name=AWS_REGION
|
| 37 |
)
|
| 38 |
logger.info("Bedrock client initialized successfully")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
except Exception as e:
|
| 40 |
-
logger.error(f"Failed to initialize
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
# Sample transcript for the demo
|
| 43 |
SAMPLE_TRANSCRIPT = """*PAR: today I would &-um like to talk about &-um a fun trip I took last &-um summer with my family.
|
|
@@ -60,23 +108,38 @@ SAMPLE_TRANSCRIPT = """*PAR: today I would &-um like to talk about &-um a fun tr
|
|
| 60 |
# ===============================
|
| 61 |
|
| 62 |
# Create data directories if they don't exist
|
| 63 |
-
DATA_DIR = "patient_data"
|
| 64 |
RECORDS_FILE = os.path.join(DATA_DIR, "patient_records.csv")
|
| 65 |
ANALYSES_DIR = os.path.join(DATA_DIR, "analyses")
|
|
|
|
|
|
|
| 66 |
|
| 67 |
def ensure_data_dirs():
|
| 68 |
"""Ensure data directories exist"""
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
# Initialize data directories
|
| 82 |
ensure_data_dirs()
|
|
@@ -278,6 +341,9 @@ def delete_patient_record(record_id):
|
|
| 278 |
|
| 279 |
def read_pdf(file_path):
|
| 280 |
"""Read text from a PDF file"""
|
|
|
|
|
|
|
|
|
|
| 281 |
try:
|
| 282 |
with open(file_path, 'rb') as file:
|
| 283 |
pdf_reader = PyPDF2.PdfReader(file)
|
|
@@ -318,7 +384,10 @@ def process_upload(file):
|
|
| 318 |
|
| 319 |
file_path = file.name
|
| 320 |
if file_path.endswith('.pdf'):
|
| 321 |
-
|
|
|
|
|
|
|
|
|
|
| 322 |
elif file_path.endswith('.cha'):
|
| 323 |
return read_cha_file(file_path)
|
| 324 |
else:
|
|
@@ -424,10 +493,15 @@ def generate_demo_response(prompt):
|
|
| 424 |
|
| 425 |
return response
|
| 426 |
|
| 427 |
-
def generate_demo_transcription(
|
| 428 |
"""Generate a simulated transcription response"""
|
| 429 |
-
|
| 430 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 431 |
|
| 432 |
def generate_demo_qa_response(question):
|
| 433 |
"""Generate a simulated Q&A response"""
|
|
@@ -877,109 +951,302 @@ def analyze_transcript(transcript, age, gender):
|
|
| 877 |
|
| 878 |
# Instructions for the LLM analysis
|
| 879 |
instructions = """
|
| 880 |
-
|
| 881 |
-
|
| 882 |
-
The factors of speech that you need to count are:
|
| 883 |
-
|
| 884 |
-
1. Difficulty producing fluent, grammatical speech - speech that is slow, halting, with pauses while searching for words
|
| 885 |
-
2. Word retrieval issues - trouble thinking of specific words, use of filler words like um, circumlocution, semantically similar word substitutions
|
| 886 |
-
3. Grammatical errors - missing/incorrect function words, problems with verb tenses, conjugation, agreement, simplified sentences
|
| 887 |
-
4. Repetitions and revisions - repeating or restating words, phrases or sentences due to trouble finding the right words
|
| 888 |
-
5. Neologisms - creating nonexistent "new" words
|
| 889 |
-
6. Perseveration - unintentionally repeating words or phrases over and over
|
| 890 |
-
7. Comprehension issues - trouble understanding complex sentences, fast speech, relying more on context and cues
|
| 891 |
-
|
| 892 |
-
For each factor, provide:
|
| 893 |
-
- Number of occurrences
|
| 894 |
-
- Severity percentile (estimate based on your clinical judgment)
|
| 895 |
-
- At least 2-3 specific quotes from the transcript as examples
|
| 896 |
-
|
| 897 |
-
Then evaluate using the CASL-2 Speech and Language Analysis Framework across these domains:
|
| 898 |
-
|
| 899 |
-
1. Lexical/Semantic Skills:
|
| 900 |
-
- Assess vocabulary diversity, word-finding abilities, semantic precision
|
| 901 |
-
- Provide Standard Score (mean=100, SD=15), percentile rank, and performance level
|
| 902 |
-
- Include SPECIFIC QUOTES as evidence
|
| 903 |
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 908 |
|
| 909 |
-
3. Supralinguistic Skills:
|
| 910 |
-
- Assess figurative language use, inferencing, and abstract reasoning
|
| 911 |
-
- Provide Standard Score, percentile rank, and performance level
|
| 912 |
-
- Include SPECIFIC QUOTES as evidence
|
| 913 |
-
|
| 914 |
-
YOUR RESPONSE MUST USE THESE EXACT SECTION MARKERS FOR PARSING:
|
| 915 |
-
|
| 916 |
-
<SPEECH_FACTORS_START>
|
| 917 |
-
Difficulty producing fluent, grammatical speech: (occurrences), (percentile)
|
| 918 |
-
Examples:
|
| 919 |
-
- "(direct quote from transcript)"
|
| 920 |
-
- "(direct quote from transcript)"
|
| 921 |
-
|
| 922 |
-
Word retrieval issues: (occurrences), (percentile)
|
| 923 |
-
Examples:
|
| 924 |
-
- "(direct quote from transcript)"
|
| 925 |
-
- "(direct quote from transcript)"
|
| 926 |
-
|
| 927 |
-
(And so on for each factor)
|
| 928 |
-
<SPEECH_FACTORS_END>
|
| 929 |
-
|
| 930 |
-
<CASL_SKILLS_START>
|
| 931 |
-
Lexical/Semantic Skills: Standard Score (X), Percentile Rank (X%), Performance Level
|
| 932 |
-
Examples:
|
| 933 |
-
- "(direct quote showing strength or weakness)"
|
| 934 |
-
- "(direct quote showing strength or weakness)"
|
| 935 |
-
|
| 936 |
-
Syntactic Skills: Standard Score (X), Percentile Rank (X%), Performance Level
|
| 937 |
-
Examples:
|
| 938 |
-
- "(direct quote showing strength or weakness)"
|
| 939 |
-
- "(direct quote showing strength or weakness)"
|
| 940 |
-
|
| 941 |
-
Supralinguistic Skills: Standard Score (X), Percentile Rank (X%), Performance Level
|
| 942 |
-
Examples:
|
| 943 |
-
- "(direct quote showing strength or weakness)"
|
| 944 |
-
- "(direct quote showing strength or weakness)"
|
| 945 |
-
<CASL_SKILLS_END>
|
| 946 |
-
|
| 947 |
-
<TREATMENT_RECOMMENDATIONS_START>
|
| 948 |
-
- (treatment recommendation)
|
| 949 |
-
- (treatment recommendation)
|
| 950 |
-
- (treatment recommendation)
|
| 951 |
-
<TREATMENT_RECOMMENDATIONS_END>
|
| 952 |
-
|
| 953 |
-
<EXPLANATION_START>
|
| 954 |
-
(brief diagnostic rationale based on findings)
|
| 955 |
-
<EXPLANATION_END>
|
| 956 |
-
|
| 957 |
-
<ADDITIONAL_ANALYSIS_START>
|
| 958 |
-
(specific insights that would be helpful for treatment planning)
|
| 959 |
-
<ADDITIONAL_ANALYSIS_END>
|
| 960 |
-
|
| 961 |
-
<DIAGNOSTIC_IMPRESSIONS_START>
|
| 962 |
-
(summarize findings across domains using specific examples and clear explanations)
|
| 963 |
-
<DIAGNOSTIC_IMPRESSIONS_END>
|
| 964 |
-
|
| 965 |
-
<ERROR_EXAMPLES_START>
|
| 966 |
-
(Copy all the specific quote examples here again, organized by error type or skill domain)
|
| 967 |
-
<ERROR_EXAMPLES_END>
|
| 968 |
-
|
| 969 |
-
MOST IMPORTANT:
|
| 970 |
-
1. Use EXACTLY the section markers provided (like <SPEECH_FACTORS_START>) to make parsing reliable
|
| 971 |
-
2. For EVERY factor and domain you analyze, you MUST provide direct quotes from the transcript as evidence
|
| 972 |
-
3. Be very specific and cite the exact text
|
| 973 |
-
4. Do not omit any of the required sections
|
| 974 |
-
"""
|
| 975 |
-
|
| 976 |
-
# Prepare prompt for Claude with the user's role context
|
| 977 |
-
role_context = """
|
| 978 |
-
You are a speech pathologist, a healthcare professional who specializes in evaluating, diagnosing, and treating communication disorders, including speech, language, cognitive-communication, voice, swallowing, and fluency disorders. Your role is to help patients improve their speech and communication skills through various therapeutic techniques and exercises.
|
| 979 |
-
|
| 980 |
-
You are working with a student with speech impediments.
|
| 981 |
-
|
| 982 |
-
The most important thing is that you stay kind to the child. Be constructive and helpful rather than critical.
|
| 983 |
"""
|
| 984 |
|
| 985 |
prompt = f"""
|
|
@@ -1013,19 +1280,190 @@ def analyze_transcript(transcript, age, gender):
|
|
| 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"""
|
| 1018 |
-
|
| 1019 |
-
|
| 1020 |
-
|
| 1021 |
-
|
| 1022 |
-
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
-
|
| 1027 |
-
|
| 1028 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1029 |
|
| 1030 |
def answer_slp_question(question):
|
| 1031 |
"""Answer a question about SLP practice or CASL assessment"""
|
|
@@ -1334,12 +1772,8 @@ def create_interface():
|
|
| 1334 |
outputs=[patient_records_table, records_status]
|
| 1335 |
)
|
| 1336 |
|
| 1337 |
-
#
|
| 1338 |
-
|
| 1339 |
-
lambda tab_id: refresh_patient_records() if tab_id == 1 else (pd.DataFrame(), ""),
|
| 1340 |
-
inputs=[main_tabs],
|
| 1341 |
-
outputs=[patient_records_table, records_status]
|
| 1342 |
-
)
|
| 1343 |
|
| 1344 |
# Load record when a row is selected
|
| 1345 |
def handle_record_selection(evt: gr.SelectData, records):
|
|
@@ -1737,15 +2171,11 @@ def create_interface():
|
|
| 1737 |
|
| 1738 |
# Improved PDF export functionality
|
| 1739 |
def export_pdf(report_text, patient_name="Patient", record_id="", age="", gender="", assessment_date="", clinician=""):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1740 |
try:
|
| 1741 |
-
from reportlab.lib.pagesizes import letter
|
| 1742 |
-
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
|
| 1743 |
-
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 1744 |
-
from reportlab.lib import colors
|
| 1745 |
-
import tempfile
|
| 1746 |
-
import webbrowser
|
| 1747 |
-
import os
|
| 1748 |
-
import shutil
|
| 1749 |
|
| 1750 |
# Create a proper downloads directory in the app folder
|
| 1751 |
downloads_dir = os.path.join(DATA_DIR, "downloads")
|
|
@@ -2060,4 +2490,4 @@ if __name__ == "__main__":
|
|
| 2060 |
|
| 2061 |
# Launch the Gradio app
|
| 2062 |
app = create_interface()
|
| 2063 |
-
app.launch()
|
|
|
|
| 1 |
+
eimport gradio as gr
|
| 2 |
import boto3
|
| 3 |
import json
|
| 4 |
import pandas as pd
|
|
|
|
| 11 |
import csv
|
| 12 |
from PIL import Image
|
| 13 |
import io
|
|
|
|
| 14 |
import uuid
|
| 15 |
from datetime import datetime
|
| 16 |
+
import tempfile
|
| 17 |
+
import time
|
| 18 |
+
|
| 19 |
+
# Try to import ReportLab (needed for PDF generation)
|
| 20 |
+
try:
|
| 21 |
+
from reportlab.lib.pagesizes import letter
|
| 22 |
+
from reportlab.lib import colors
|
| 23 |
+
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
|
| 24 |
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
| 25 |
+
REPORTLAB_AVAILABLE = True
|
| 26 |
+
except ImportError:
|
| 27 |
+
logger = logging.getLogger(__name__)
|
| 28 |
+
logger.warning("ReportLab library not available - PDF export will be disabled")
|
| 29 |
+
REPORTLAB_AVAILABLE = False
|
| 30 |
+
|
| 31 |
+
# Try to import PyPDF2 (needed for PDF reading)
|
| 32 |
+
try:
|
| 33 |
+
import PyPDF2
|
| 34 |
+
PYPDF2_AVAILABLE = True
|
| 35 |
+
except ImportError:
|
| 36 |
+
logger = logging.getLogger(__name__)
|
| 37 |
+
logger.warning("PyPDF2 library not available - PDF reading will be disabled")
|
| 38 |
+
PYPDF2_AVAILABLE = False
|
| 39 |
|
| 40 |
# Configure logging
|
| 41 |
logging.basicConfig(level=logging.INFO)
|
|
|
|
| 47 |
AWS_SECRET_KEY = os.getenv("AWS_SECRET_KEY", "")
|
| 48 |
AWS_REGION = os.getenv("AWS_REGION", "us-east-1")
|
| 49 |
|
| 50 |
+
# Initialize AWS clients if credentials are available
|
| 51 |
bedrock_client = None
|
| 52 |
+
transcribe_client = None
|
| 53 |
+
s3_client = None
|
| 54 |
+
|
| 55 |
if AWS_ACCESS_KEY and AWS_SECRET_KEY:
|
| 56 |
try:
|
| 57 |
+
# Initialize Bedrock client for AI analysis
|
| 58 |
bedrock_client = boto3.client(
|
| 59 |
'bedrock-runtime',
|
| 60 |
aws_access_key_id=AWS_ACCESS_KEY,
|
|
|
|
| 62 |
region_name=AWS_REGION
|
| 63 |
)
|
| 64 |
logger.info("Bedrock client initialized successfully")
|
| 65 |
+
|
| 66 |
+
# Initialize Transcribe client for speech-to-text
|
| 67 |
+
transcribe_client = boto3.client(
|
| 68 |
+
'transcribe',
|
| 69 |
+
aws_access_key_id=AWS_ACCESS_KEY,
|
| 70 |
+
aws_secret_access_key=AWS_SECRET_KEY,
|
| 71 |
+
region_name=AWS_REGION
|
| 72 |
+
)
|
| 73 |
+
logger.info("Transcribe client initialized successfully")
|
| 74 |
+
|
| 75 |
+
# Initialize S3 client for storing audio files
|
| 76 |
+
s3_client = boto3.client(
|
| 77 |
+
's3',
|
| 78 |
+
aws_access_key_id=AWS_ACCESS_KEY,
|
| 79 |
+
aws_secret_access_key=AWS_SECRET_KEY,
|
| 80 |
+
region_name=AWS_REGION
|
| 81 |
+
)
|
| 82 |
+
logger.info("S3 client initialized successfully")
|
| 83 |
except Exception as e:
|
| 84 |
+
logger.error(f"Failed to initialize AWS clients: {str(e)}")
|
| 85 |
+
|
| 86 |
+
# S3 bucket for storing audio files
|
| 87 |
+
S3_BUCKET = os.environ.get("S3_BUCKET", "casl-audio-files")
|
| 88 |
+
S3_PREFIX = "transcribe-audio/"
|
| 89 |
|
| 90 |
# Sample transcript for the demo
|
| 91 |
SAMPLE_TRANSCRIPT = """*PAR: today I would &-um like to talk about &-um a fun trip I took last &-um summer with my family.
|
|
|
|
| 108 |
# ===============================
|
| 109 |
|
| 110 |
# Create data directories if they don't exist
|
| 111 |
+
DATA_DIR = os.environ.get("DATA_DIR", "patient_data")
|
| 112 |
RECORDS_FILE = os.path.join(DATA_DIR, "patient_records.csv")
|
| 113 |
ANALYSES_DIR = os.path.join(DATA_DIR, "analyses")
|
| 114 |
+
DOWNLOADS_DIR = os.path.join(DATA_DIR, "downloads")
|
| 115 |
+
AUDIO_DIR = os.path.join(DATA_DIR, "audio")
|
| 116 |
|
| 117 |
def ensure_data_dirs():
|
| 118 |
"""Ensure data directories exist"""
|
| 119 |
+
global DOWNLOADS_DIR, AUDIO_DIR
|
| 120 |
+
try:
|
| 121 |
+
os.makedirs(DATA_DIR, exist_ok=True)
|
| 122 |
+
os.makedirs(ANALYSES_DIR, exist_ok=True)
|
| 123 |
+
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
|
| 124 |
+
os.makedirs(AUDIO_DIR, exist_ok=True)
|
| 125 |
+
logger.info(f"Data directories created: {DATA_DIR}, {ANALYSES_DIR}, {DOWNLOADS_DIR}, {AUDIO_DIR}")
|
| 126 |
+
|
| 127 |
+
# Create records file if it doesn't exist
|
| 128 |
+
if not os.path.exists(RECORDS_FILE):
|
| 129 |
+
with open(RECORDS_FILE, 'w', newline='') as f:
|
| 130 |
+
writer = csv.writer(f)
|
| 131 |
+
writer.writerow([
|
| 132 |
+
"ID", "Name", "Record ID", "Age", "Gender",
|
| 133 |
+
"Assessment Date", "Clinician", "Analysis Date", "File Path"
|
| 134 |
+
])
|
| 135 |
+
except Exception as e:
|
| 136 |
+
logger.warning(f"Could not create data directories: {str(e)}")
|
| 137 |
+
# Fallback to tmp directory on HF Spaces
|
| 138 |
+
DOWNLOADS_DIR = os.path.join(tempfile.gettempdir(), "casl_downloads")
|
| 139 |
+
AUDIO_DIR = os.path.join(tempfile.gettempdir(), "casl_audio")
|
| 140 |
+
os.makedirs(DOWNLOADS_DIR, exist_ok=True)
|
| 141 |
+
os.makedirs(AUDIO_DIR, exist_ok=True)
|
| 142 |
+
logger.info(f"Using fallback directories: {DOWNLOADS_DIR}, {AUDIO_DIR}")
|
| 143 |
|
| 144 |
# Initialize data directories
|
| 145 |
ensure_data_dirs()
|
|
|
|
| 341 |
|
| 342 |
def read_pdf(file_path):
|
| 343 |
"""Read text from a PDF file"""
|
| 344 |
+
if not PYPDF2_AVAILABLE:
|
| 345 |
+
return "Error: PDF reading is not available - PyPDF2 library is not installed"
|
| 346 |
+
|
| 347 |
try:
|
| 348 |
with open(file_path, 'rb') as file:
|
| 349 |
pdf_reader = PyPDF2.PdfReader(file)
|
|
|
|
| 384 |
|
| 385 |
file_path = file.name
|
| 386 |
if file_path.endswith('.pdf'):
|
| 387 |
+
if PYPDF2_AVAILABLE:
|
| 388 |
+
return read_pdf(file_path)
|
| 389 |
+
else:
|
| 390 |
+
return "Error: PDF reading is disabled - PyPDF2 library is not installed"
|
| 391 |
elif file_path.endswith('.cha'):
|
| 392 |
return read_cha_file(file_path)
|
| 393 |
else:
|
|
|
|
| 493 |
|
| 494 |
return response
|
| 495 |
|
| 496 |
+
def generate_demo_transcription():
|
| 497 |
"""Generate a simulated transcription response"""
|
| 498 |
+
return """*PAR: today I want to tell you about my favorite toy.
|
| 499 |
+
*PAR: it's a &-um teddy bear that I got for my birthday.
|
| 500 |
+
*PAR: he has &-um brown fur and a red bow.
|
| 501 |
+
*PAR: I like to sleep with him every night.
|
| 502 |
+
*PAR: sometimes I take him to school in my backpack.
|
| 503 |
+
*INV: what's your teddy bear's name?
|
| 504 |
+
*PAR: his name is &-um Brownie because he's brown."""
|
| 505 |
|
| 506 |
def generate_demo_qa_response(question):
|
| 507 |
"""Generate a simulated Q&A response"""
|
|
|
|
| 951 |
|
| 952 |
# Instructions for the LLM analysis
|
| 953 |
instructions = """
|
| 954 |
+
Advanced Linguistic Analysis Protocol for Adolescent Language Samples (Ages 14-18)
|
| 955 |
+
You are a highly specialized assistant supporting speech-language pathologists in conducting comprehensive linguistic analyses of adolescent language samples. Your analysis must adhere to evidence-based practice standards for secondary-level language assessment while producing results that inform both clinical decision-making and family understanding.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 956 |
|
| 957 |
+
Initial Sample Assessment
|
| 958 |
+
1. Document sample metadata:
|
| 959 |
+
* Total number of utterances
|
| 960 |
+
* Sample collection context (conversational, narrative, expository, persuasive, procedural)
|
| 961 |
+
* Sample elicitation method (if indicated)
|
| 962 |
+
* Total duration/length of interaction
|
| 963 |
+
* Any transcription conventions used (e.g., SALT, CHAT)
|
| 964 |
+
2. Determine analysis approach:
|
| 965 |
+
* For samples with ≤50 utterances: Perform complete analysis on all utterances
|
| 966 |
+
* For samples >50 utterances: Perform complete analysis, then select 50 representative utterances that capture key patterns across all linguistic domains while maintaining the natural distribution of features
|
| 967 |
+
Utterance-Level Microanalysis
|
| 968 |
+
For each utterance, provide detailed linguistic breakdown including:
|
| 969 |
+
1. Structural Components
|
| 970 |
+
* Utterance number and full text (verbatim)
|
| 971 |
+
* Word count (excluding fillers, false starts, and repetitions)
|
| 972 |
+
* C-unit segmentation (when applicable)
|
| 973 |
+
* MLU in words and morphemes
|
| 974 |
+
* Syntactic classification:
|
| 975 |
+
* Simple, compound, complex, or compound-complex
|
| 976 |
+
* Complete or fragmentary
|
| 977 |
+
* Declarative, interrogative, imperative, or exclamatory
|
| 978 |
+
* Clausal density (number of clauses/utterance)
|
| 979 |
+
2. Syntactic Analysis
|
| 980 |
+
* Constituent structure identification:
|
| 981 |
+
* Subject and predicate components
|
| 982 |
+
* Noun phrases (including pre- and post-modification)
|
| 983 |
+
* Verb phrases (including auxiliaries, complements)
|
| 984 |
+
* Adverbial phrases and clauses (including position and function)
|
| 985 |
+
* Prepositional phrases (including syntactic role)
|
| 986 |
+
* Subordinate clauses (including type and function)
|
| 987 |
+
* Embedding depth and recursion patterns
|
| 988 |
+
* Syntactic movement and transformations
|
| 989 |
+
* Non-canonical structures (passives, clefts, etc.)
|
| 990 |
+
3. Morphological Analysis
|
| 991 |
+
* Bound morpheme usage (inflectional and derivational)
|
| 992 |
+
* Tense marking consistency
|
| 993 |
+
* Agreement patterns (subject-verb, pronoun-antecedent)
|
| 994 |
+
* Morphological errors with classification
|
| 995 |
+
4. Error Analysis (for each identified error)
|
| 996 |
+
* Precise error location:
|
| 997 |
+
* Utterance number and full quotation
|
| 998 |
+
* Position within utterance (initial, medial, final)
|
| 999 |
+
* Syntactic position (e.g., main clause, subordinate clause, noun phrase)
|
| 1000 |
+
* Proximity to other linguistic features (e.g., complex vocabulary, disfluencies)
|
| 1001 |
+
* Error type classification:
|
| 1002 |
+
* Morphosyntactic (agreement, tense, etc.)
|
| 1003 |
+
* Lexical-semantic (word selection, collocational)
|
| 1004 |
+
* Phonological (if transcribed)
|
| 1005 |
+
* Pragmatic (if contextually inappropriate)
|
| 1006 |
+
* Error pattern (developmental vs. atypical)
|
| 1007 |
+
* Clinical significance of location:
|
| 1008 |
+
* Relationship to sentence complexity (errors increasing with complexity)
|
| 1009 |
+
* Patterns related to linguistic context (e.g., errors occurring after disfluencies)
|
| 1010 |
+
* Consistency across similar syntactic environments
|
| 1011 |
+
* Relationship to cognitive load (e.g., errors increasing in dense information units)
|
| 1012 |
+
* Error frequency and distribution across contexts
|
| 1013 |
+
* Self-correction attempts and success rate
|
| 1014 |
+
5. Fluency Markers
|
| 1015 |
+
* Mazes (false starts, repetitions, reformulations)
|
| 1016 |
+
* Filled pauses (um, uh, like, etc.)
|
| 1017 |
+
* Silent pauses (duration if indicated)
|
| 1018 |
+
* Disruption patterns and positions
|
| 1019 |
+
* Impact on communicative effectiveness
|
| 1020 |
+
QUANTITATIVE ANALYSIS & METRICS
|
| 1021 |
+
Calculate the following evidence-based metrics with interpretations relevant to adolescent language development:
|
| 1022 |
+
Productivity Measures
|
| 1023 |
+
1. Total Output Measures
|
| 1024 |
+
* Total number of words (TNW)
|
| 1025 |
+
* Total number of utterances (TNU)
|
| 1026 |
+
* Total number of different words (TDW)
|
| 1027 |
+
* Total communication units (T-units/C-units)
|
| 1028 |
+
2. Length Measures
|
| 1029 |
+
* Mean length of utterance in words (MLU-w)
|
| 1030 |
+
* Mean length of utterance in morphemes (MLU-m)
|
| 1031 |
+
* Mean length of C-unit (MLCU)
|
| 1032 |
+
* Words per minute (if timing available)
|
| 1033 |
+
Complexity Measures
|
| 1034 |
+
1. Syntactic Complexity
|
| 1035 |
+
* Clausal density (clauses per C-unit)
|
| 1036 |
+
* Subordination index (SI)
|
| 1037 |
+
* Coordination index
|
| 1038 |
+
* Embedding depth (max levels of embedding)
|
| 1039 |
+
* T-unit complexity ratio
|
| 1040 |
+
* Percentage of complex sentences
|
| 1041 |
+
2. Phrase-Level Complexity
|
| 1042 |
+
* Mean noun phrase length
|
| 1043 |
+
* Mean verb phrase complexity
|
| 1044 |
+
* Prepositional phrase frequency
|
| 1045 |
+
* Adverbial complexity
|
| 1046 |
+
Accuracy Measures
|
| 1047 |
+
1. Error Analysis Summary
|
| 1048 |
+
* Percentage of grammatically correct utterances
|
| 1049 |
+
* Errors per C-unit
|
| 1050 |
+
* Error pattern distribution (morphological, syntactic, lexical)
|
| 1051 |
+
* Most frequent error types with specific examples
|
| 1052 |
+
* Error location patterns:
|
| 1053 |
+
* Distribution across utterance positions (initial, medial, final)
|
| 1054 |
+
* Distribution across syntactic structures (simple vs. complex)
|
| 1055 |
+
* Correlation with utterance length and complexity
|
| 1056 |
+
* Patterns related to information density or processing demands
|
| 1057 |
+
* Clinical significance of error locations:
|
| 1058 |
+
* Interpretation of position-specific error patterns
|
| 1059 |
+
* Analysis of syntactic contexts where errors predominate
|
| 1060 |
+
* Relationship between error location and communicative impact
|
| 1061 |
+
Lexical Diversity & Sophistication
|
| 1062 |
+
1. Vocabulary Metrics
|
| 1063 |
+
* Type-token ratio (TTR)
|
| 1064 |
+
* Moving-average type-token ratio (MATTR)
|
| 1065 |
+
* Number of different words (NDW)
|
| 1066 |
+
* Vocabulary diversity (D)
|
| 1067 |
+
* Lexical density (content words/total words)
|
| 1068 |
+
2. Lexical Sophistication
|
| 1069 |
+
* Low-frequency word usage
|
| 1070 |
+
* Academic vocabulary presence
|
| 1071 |
+
* Abstract word usage
|
| 1072 |
+
* Word specificity analysis
|
| 1073 |
+
Fluency & Formulation Measures
|
| 1074 |
+
1. Disruption Analysis
|
| 1075 |
+
* Percentage of mazes
|
| 1076 |
+
* Total maze words/total words
|
| 1077 |
+
* Revisions per utterance
|
| 1078 |
+
* Hesitation frequency
|
| 1079 |
+
* Incomplete utterance percentage
|
| 1080 |
+
* Word-finding difficulties (frequency and patterns)
|
| 1081 |
+
CASL-2 DOMAIN ALIGNMENT
|
| 1082 |
+
Analyze the sample according to the Comprehensive Assessment of Spoken Language (CASL-2) framework, providing detailed evidence for each domain:
|
| 1083 |
+
1. Lexical/Semantic Domain
|
| 1084 |
+
* Vocabulary Range Assessment
|
| 1085 |
+
* Basic vs. precise vocabulary usage
|
| 1086 |
+
* Abstract vs. concrete terminology
|
| 1087 |
+
* Academic language presence
|
| 1088 |
+
* Subject-specific terminology
|
| 1089 |
+
* Register-appropriate lexicon
|
| 1090 |
+
* Word Relationships
|
| 1091 |
+
* Synonym/antonym usage
|
| 1092 |
+
* Categorical relationships
|
| 1093 |
+
* Part-whole relationships
|
| 1094 |
+
* Semantic networks
|
| 1095 |
+
* Word Retrieval Patterns
|
| 1096 |
+
* Word-finding hesitations
|
| 1097 |
+
* Circumlocutions
|
| 1098 |
+
* Semantic substitutions
|
| 1099 |
+
* Retrieval strategies
|
| 1100 |
+
* Evidence Summary
|
| 1101 |
+
* Provide specific examples from transcript
|
| 1102 |
+
* Compare to age-appropriate expectations
|
| 1103 |
+
* Estimate standard score range (using clinical judgment)
|
| 1104 |
+
* Indicate percentile rank range
|
| 1105 |
+
* Assign performance level category
|
| 1106 |
+
2. Syntactic Domain
|
| 1107 |
+
* Sentence Structure Analysis
|
| 1108 |
+
* Distribution of sentence types
|
| 1109 |
+
* Complex syntax usage patterns
|
| 1110 |
+
* Syntactic versatility
|
| 1111 |
+
* Age-appropriate structures
|
| 1112 |
+
* Morphosyntactic Elements
|
| 1113 |
+
* Regular and irregular morphology
|
| 1114 |
+
* Verb tense system mastery
|
| 1115 |
+
* Complex verb forms (perfect, progressive)
|
| 1116 |
+
* Advanced agreement patterns
|
| 1117 |
+
* Syntactic Maturity Indicators
|
| 1118 |
+
* Clause combining strategies
|
| 1119 |
+
* Embedding types and frequency
|
| 1120 |
+
* Noun phrase elaboration
|
| 1121 |
+
* Adverbial complexity
|
| 1122 |
+
* Evidence Summary
|
| 1123 |
+
* Provide specific examples from transcript
|
| 1124 |
+
* Compare to age-appropriate expectations
|
| 1125 |
+
* Estimate standard score range
|
| 1126 |
+
* Indicate percentile rank range
|
| 1127 |
+
* Assign performance level category
|
| 1128 |
+
3. Supralinguistic Domain
|
| 1129 |
+
* Figurative Language
|
| 1130 |
+
* Idiomatic expressions
|
| 1131 |
+
* Metaphors and analogies
|
| 1132 |
+
* Humor or wordplay
|
| 1133 |
+
* Understanding of non-literal content
|
| 1134 |
+
* Higher-Order Reasoning
|
| 1135 |
+
* Inferential language
|
| 1136 |
+
* Ambiguity recognition
|
| 1137 |
+
* Abstract concept expression
|
| 1138 |
+
* Perspective-taking indicators
|
| 1139 |
+
* Metalinguistic Awareness
|
| 1140 |
+
* Self-monitoring
|
| 1141 |
+
* Linguistic reflection
|
| 1142 |
+
* Awareness of language rules
|
| 1143 |
+
* Metacognitive comments
|
| 1144 |
+
* Evidence Summary
|
| 1145 |
+
* Provide specific examples from transcript
|
| 1146 |
+
* Note limitations of assessment from sample
|
| 1147 |
+
* Estimate standard score range (if sufficient evidence)
|
| 1148 |
+
* Indicate percentile rank range (if applicable)
|
| 1149 |
+
* Assign performance level category (or note insufficient evidence)
|
| 1150 |
+
4. Pragmatic Domain
|
| 1151 |
+
* Discourse Management
|
| 1152 |
+
* Topic initiation, maintenance, and change
|
| 1153 |
+
* Turn-taking patterns
|
| 1154 |
+
* Response contingency
|
| 1155 |
+
* Conversational repair strategies
|
| 1156 |
+
* Social Communication
|
| 1157 |
+
* Perspective-taking
|
| 1158 |
+
* Register variation
|
| 1159 |
+
* Politeness conventions
|
| 1160 |
+
* Social inferencing
|
| 1161 |
+
* Narrative/Expository Skills (if applicable)
|
| 1162 |
+
* Coherence and cohesion
|
| 1163 |
+
* Organizational structure
|
| 1164 |
+
* Use of cohesive devices
|
| 1165 |
+
* Information density
|
| 1166 |
+
* Evidence Summary
|
| 1167 |
+
* Provide specific examples from transcript
|
| 1168 |
+
* Note contextual limitations
|
| 1169 |
+
* Estimate standard score range (if sufficient evidence)
|
| 1170 |
+
* Indicate percentile rank range (if applicable)
|
| 1171 |
+
* Assign performance level category (or note insufficient evidence)
|
| 1172 |
+
DEVELOPMENTAL PROFILE ANALYSIS
|
| 1173 |
+
Compare observed language features to established adolescent language development patterns:
|
| 1174 |
+
1. Age-Based Comparison
|
| 1175 |
+
* Alignment with typical syntactic development (14-18)
|
| 1176 |
+
* Lexical development expectations
|
| 1177 |
+
* Discourse maturity indicators
|
| 1178 |
+
* Academic language benchmarks
|
| 1179 |
+
2. Strength-Challenge Pattern Analysis
|
| 1180 |
+
* Identify domains of relative strength with evidence
|
| 1181 |
+
* Identify domains requiring support with evidence
|
| 1182 |
+
* Note any asynchronous development patterns
|
| 1183 |
+
* Document compensatory strategies observed
|
| 1184 |
+
3. Developmental Trajectory Indicators
|
| 1185 |
+
* Features suggesting typical development
|
| 1186 |
+
* Features suggesting delayed development
|
| 1187 |
+
* Features suggesting disordered development
|
| 1188 |
+
* Features suggesting language difference vs. disorder
|
| 1189 |
+
COMPREHENSIVE REPORTING FORMAT
|
| 1190 |
+
1. Professional Clinical Summary (SLP-Oriented)
|
| 1191 |
+
* Sample characteristics and analysis methodology
|
| 1192 |
+
* Key quantitative findings table with age-based interpretation
|
| 1193 |
+
* CASL-2 domain profiles with evidence-based rationales
|
| 1194 |
+
* Error pattern analysis with clinical implications
|
| 1195 |
+
* Identified strengths and challenges
|
| 1196 |
+
* Differential considerations
|
| 1197 |
+
* Recommendations for further assessment
|
| 1198 |
+
* Potential treatment targets based on evidence
|
| 1199 |
+
2. Family-Friendly Summary Report
|
| 1200 |
+
* Introduction
|
| 1201 |
+
* Purpose of language sample analysis
|
| 1202 |
+
* Brief explanation of what was analyzed
|
| 1203 |
+
* How this information helps understand communication
|
| 1204 |
+
* Your Adolescent's Language Profile
|
| 1205 |
+
* Overall communication strengths (with clear examples)
|
| 1206 |
+
* Areas for continued growth (with supportive examples)
|
| 1207 |
+
* How these patterns may impact academic and social communication
|
| 1208 |
+
* Understanding the Assessment
|
| 1209 |
+
* Simple explanations of key findings
|
| 1210 |
+
* Comparison to typical adolescent language patterns
|
| 1211 |
+
* Visual representation of language profile
|
| 1212 |
+
* Accessible examples from the transcript
|
| 1213 |
+
* Supporting Language Development
|
| 1214 |
+
* Practical strategies aligned with findings
|
| 1215 |
+
* Communication opportunities that leverage strengths
|
| 1216 |
+
* Questions to discuss with the SLP
|
| 1217 |
+
* Resources for family understanding
|
| 1218 |
+
* Next Steps
|
| 1219 |
+
* Connections to academic and social communication
|
| 1220 |
+
* Relevance to current educational goals
|
| 1221 |
+
* Partnership opportunities between home and therapy
|
| 1222 |
+
3. Educational Implications (if requested)
|
| 1223 |
+
* Connections to academic standards
|
| 1224 |
+
* Impact on classroom participation
|
| 1225 |
+
* Alignment with IEP goals (if applicable)
|
| 1226 |
+
* Recommendations for classroom support
|
| 1227 |
+
IMPLEMENTATION GUIDELINES
|
| 1228 |
+
1. Analysis Integrity
|
| 1229 |
+
* Analyze only what is directly observable in the transcript
|
| 1230 |
+
* Clearly differentiate observations from interpretations
|
| 1231 |
+
* Note when certain domains cannot be adequately assessed
|
| 1232 |
+
* Document analysis limitations based on sample constraints
|
| 1233 |
+
2. Clinical Reasoning
|
| 1234 |
+
* Apply evidence-based standards for adolescent language
|
| 1235 |
+
* Consider developmental appropriateness for ages 14-18
|
| 1236 |
+
* Document patterns rather than isolated instances
|
| 1237 |
+
* Provide context for interpretations
|
| 1238 |
+
3. Reporting Ethics
|
| 1239 |
+
* Use person-first, strength-based language
|
| 1240 |
+
* Avoid definitive diagnostic statements
|
| 1241 |
+
* Focus on functional communication impact
|
| 1242 |
+
* Maintain appropriate scope of analysis
|
| 1243 |
+
4. Flexibility Adaptations
|
| 1244 |
+
* For different discourse types (narrative, expository, conversational)
|
| 1245 |
+
* For different cultural and linguistic backgrounds
|
| 1246 |
+
* For various academic and social contexts
|
| 1247 |
+
* For potential co-occurring conditions
|
| 1248 |
+
This protocol produces a comprehensive linguistic analysis tailored to adolescents (14-18) that provides both clinically relevant information and family-accessible insights while maintaining the flexibility to adapt to various sample types and contexts.
|
| 1249 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1250 |
"""
|
| 1251 |
|
| 1252 |
prompt = f"""
|
|
|
|
| 1280 |
return results, plot_image, radar_image, response
|
| 1281 |
|
| 1282 |
|
| 1283 |
+
def transcribe_audio(audio_path, patient_age=8):
|
| 1284 |
+
"""Transcribe an audio recording using Amazon Transcribe and format in CHAT format"""
|
| 1285 |
+
if not os.path.exists(audio_path):
|
| 1286 |
+
logger.error(f"Audio file not found: {audio_path}")
|
| 1287 |
+
return "Error: Audio file not found."
|
| 1288 |
+
|
| 1289 |
+
if not transcribe_client or not s3_client:
|
| 1290 |
+
logger.warning("AWS clients not initialized, using demo transcription")
|
| 1291 |
+
return generate_demo_transcription()
|
| 1292 |
+
|
| 1293 |
+
try:
|
| 1294 |
+
# Get file info
|
| 1295 |
+
file_name = os.path.basename(audio_path)
|
| 1296 |
+
file_size = os.path.getsize(audio_path)
|
| 1297 |
+
_, file_extension = os.path.splitext(file_name)
|
| 1298 |
+
|
| 1299 |
+
# Check file format
|
| 1300 |
+
supported_formats = ['.mp3', '.mp4', '.wav', '.flac', '.ogg', '.amr', '.webm']
|
| 1301 |
+
if file_extension.lower() not in supported_formats:
|
| 1302 |
+
logger.error(f"Unsupported audio format: {file_extension}")
|
| 1303 |
+
return f"Error: Unsupported audio format. Please use one of: {', '.join(supported_formats)}"
|
| 1304 |
+
|
| 1305 |
+
# Generate a unique job name
|
| 1306 |
+
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
|
| 1307 |
+
job_name = f"casl-transcription-{timestamp}"
|
| 1308 |
+
s3_key = f"{S3_PREFIX}{job_name}{file_extension}"
|
| 1309 |
+
|
| 1310 |
+
# Upload to S3
|
| 1311 |
+
logger.info(f"Uploading {file_name} to S3 bucket {S3_BUCKET}")
|
| 1312 |
+
try:
|
| 1313 |
+
with open(audio_path, 'rb') as audio_file:
|
| 1314 |
+
s3_client.upload_fileobj(audio_file, S3_BUCKET, s3_key)
|
| 1315 |
+
except Exception as e:
|
| 1316 |
+
logger.error(f"Failed to upload to S3: {str(e)}")
|
| 1317 |
+
|
| 1318 |
+
# If upload fails, try to create the bucket
|
| 1319 |
+
try:
|
| 1320 |
+
s3_client.create_bucket(Bucket=S3_BUCKET)
|
| 1321 |
+
logger.info(f"Created S3 bucket: {S3_BUCKET}")
|
| 1322 |
+
|
| 1323 |
+
# Try upload again
|
| 1324 |
+
with open(audio_path, 'rb') as audio_file:
|
| 1325 |
+
s3_client.upload_fileobj(audio_file, S3_BUCKET, s3_key)
|
| 1326 |
+
except Exception as bucket_error:
|
| 1327 |
+
logger.error(f"Failed to create bucket and upload: {str(bucket_error)}")
|
| 1328 |
+
return "Error: Failed to upload audio file. Please check your AWS permissions."
|
| 1329 |
+
|
| 1330 |
+
# Start transcription job
|
| 1331 |
+
logger.info(f"Starting transcription job: {job_name}")
|
| 1332 |
+
media_format = file_extension.lower()[1:] # Remove the dot
|
| 1333 |
+
if media_format == 'webm':
|
| 1334 |
+
media_format = 'webm' # Amazon Transcribe expects this
|
| 1335 |
+
|
| 1336 |
+
# Determine language settings based on patient age
|
| 1337 |
+
if patient_age < 10:
|
| 1338 |
+
# For younger children, enabling child language model is helpful
|
| 1339 |
+
language_options = {
|
| 1340 |
+
'LanguageCode': 'en-US',
|
| 1341 |
+
'Settings': {
|
| 1342 |
+
'ShowSpeakerLabels': True,
|
| 1343 |
+
'MaxSpeakerLabels': 2 # Typically patient + clinician
|
| 1344 |
+
}
|
| 1345 |
+
}
|
| 1346 |
+
else:
|
| 1347 |
+
language_options = {
|
| 1348 |
+
'LanguageCode': 'en-US',
|
| 1349 |
+
'Settings': {
|
| 1350 |
+
'ShowSpeakerLabels': True,
|
| 1351 |
+
'MaxSpeakerLabels': 2 # Typically patient + clinician
|
| 1352 |
+
}
|
| 1353 |
+
}
|
| 1354 |
+
|
| 1355 |
+
transcribe_client.start_transcription_job(
|
| 1356 |
+
TranscriptionJobName=job_name,
|
| 1357 |
+
Media={
|
| 1358 |
+
'MediaFileUri': f"s3://{S3_BUCKET}/{s3_key}"
|
| 1359 |
+
},
|
| 1360 |
+
MediaFormat=media_format,
|
| 1361 |
+
**language_options
|
| 1362 |
+
)
|
| 1363 |
+
|
| 1364 |
+
# Wait for the job to complete (with timeout)
|
| 1365 |
+
logger.info("Waiting for transcription to complete...")
|
| 1366 |
+
max_tries = 30 # 5 minutes max wait
|
| 1367 |
+
tries = 0
|
| 1368 |
+
|
| 1369 |
+
while tries < max_tries:
|
| 1370 |
+
try:
|
| 1371 |
+
job = transcribe_client.get_transcription_job(TranscriptionJobName=job_name)
|
| 1372 |
+
status = job['TranscriptionJob']['TranscriptionJobStatus']
|
| 1373 |
+
|
| 1374 |
+
if status == 'COMPLETED':
|
| 1375 |
+
# Get the transcript
|
| 1376 |
+
transcript_uri = job['TranscriptionJob']['Transcript']['TranscriptFileUri']
|
| 1377 |
+
|
| 1378 |
+
# Download the transcript
|
| 1379 |
+
import urllib.request
|
| 1380 |
+
import json
|
| 1381 |
+
|
| 1382 |
+
with urllib.request.urlopen(transcript_uri) as response:
|
| 1383 |
+
transcript_json = json.loads(response.read().decode('utf-8'))
|
| 1384 |
+
|
| 1385 |
+
# Convert to CHAT format
|
| 1386 |
+
chat_transcript = format_as_chat(transcript_json)
|
| 1387 |
+
return chat_transcript
|
| 1388 |
+
|
| 1389 |
+
elif status == 'FAILED':
|
| 1390 |
+
reason = job['TranscriptionJob'].get('FailureReason', 'Unknown failure')
|
| 1391 |
+
logger.error(f"Transcription job failed: {reason}")
|
| 1392 |
+
return f"Error: Transcription failed - {reason}"
|
| 1393 |
+
|
| 1394 |
+
# Still in progress, wait and try again
|
| 1395 |
+
tries += 1
|
| 1396 |
+
time.sleep(10) # Check every 10 seconds
|
| 1397 |
+
|
| 1398 |
+
except Exception as e:
|
| 1399 |
+
logger.error(f"Error checking transcription job: {str(e)}")
|
| 1400 |
+
return f"Error getting transcription: {str(e)}"
|
| 1401 |
+
|
| 1402 |
+
# If we got here, we timed out
|
| 1403 |
+
return "Error: Transcription timed out. The process is taking longer than expected."
|
| 1404 |
+
|
| 1405 |
+
except Exception as e:
|
| 1406 |
+
logger.exception("Error in audio transcription")
|
| 1407 |
+
return f"Error transcribing audio: {str(e)}"
|
| 1408 |
+
|
| 1409 |
+
def format_as_chat(transcript_json):
|
| 1410 |
+
"""Format the Amazon Transcribe JSON result as CHAT format"""
|
| 1411 |
+
try:
|
| 1412 |
+
# Get transcript items
|
| 1413 |
+
items = transcript_json['results']['items']
|
| 1414 |
+
|
| 1415 |
+
# Get speaker labels if available
|
| 1416 |
+
speakers = {}
|
| 1417 |
+
if 'speaker_labels' in transcript_json['results']:
|
| 1418 |
+
speaker_segments = transcript_json['results']['speaker_labels']['segments']
|
| 1419 |
+
|
| 1420 |
+
# Map each item to its speaker
|
| 1421 |
+
for segment in speaker_segments:
|
| 1422 |
+
for item in segment['items']:
|
| 1423 |
+
start_time = item['start_time']
|
| 1424 |
+
speakers[start_time] = segment['speaker_label']
|
| 1425 |
+
|
| 1426 |
+
# Build transcript by combining words into utterances by speaker
|
| 1427 |
+
current_speaker = None
|
| 1428 |
+
current_utterance = []
|
| 1429 |
+
utterances = []
|
| 1430 |
+
|
| 1431 |
+
for item in items:
|
| 1432 |
+
# Skip non-pronunciation items (like punctuation)
|
| 1433 |
+
if item['type'] != 'pronunciation':
|
| 1434 |
+
continue
|
| 1435 |
+
|
| 1436 |
+
word = item['alternatives'][0]['content']
|
| 1437 |
+
start_time = item.get('start_time')
|
| 1438 |
+
|
| 1439 |
+
# Determine speaker if available
|
| 1440 |
+
speaker = speakers.get(start_time, 'spk_0')
|
| 1441 |
+
|
| 1442 |
+
# If speaker changed, start a new utterance
|
| 1443 |
+
if speaker != current_speaker and current_utterance:
|
| 1444 |
+
utterances.append((current_speaker, ' '.join(current_utterance)))
|
| 1445 |
+
current_utterance = []
|
| 1446 |
+
|
| 1447 |
+
current_speaker = speaker
|
| 1448 |
+
current_utterance.append(word)
|
| 1449 |
+
|
| 1450 |
+
# Add the last utterance
|
| 1451 |
+
if current_utterance:
|
| 1452 |
+
utterances.append((current_speaker, ' '.join(current_utterance)))
|
| 1453 |
+
|
| 1454 |
+
# Format as CHAT
|
| 1455 |
+
chat_lines = []
|
| 1456 |
+
for speaker, text in utterances:
|
| 1457 |
+
# Map speakers to CHAT format
|
| 1458 |
+
# Assuming spk_0 is the patient (PAR) and spk_1 is the clinician (INV)
|
| 1459 |
+
chat_speaker = "*PAR:" if speaker == "spk_0" else "*INV:"
|
| 1460 |
+
chat_lines.append(f"{chat_speaker} {text}.")
|
| 1461 |
+
|
| 1462 |
+
return '\n'.join(chat_lines)
|
| 1463 |
+
|
| 1464 |
+
except Exception as e:
|
| 1465 |
+
logger.exception("Error formatting transcript")
|
| 1466 |
+
return "*PAR: (Error formatting transcript)"
|
| 1467 |
|
| 1468 |
def answer_slp_question(question):
|
| 1469 |
"""Answer a question about SLP practice or CASL assessment"""
|
|
|
|
| 1772 |
outputs=[patient_records_table, records_status]
|
| 1773 |
)
|
| 1774 |
|
| 1775 |
+
# Note: The automatic tab selection event was removed because it's not supported in newer Gradio versions
|
| 1776 |
+
# Instead, we'll rely on the refresh button that's already in place
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1777 |
|
| 1778 |
# Load record when a row is selected
|
| 1779 |
def handle_record_selection(evt: gr.SelectData, records):
|
|
|
|
| 2171 |
|
| 2172 |
# Improved PDF export functionality
|
| 2173 |
def export_pdf(report_text, patient_name="Patient", record_id="", age="", gender="", assessment_date="", clinician=""):
|
| 2174 |
+
# Check if ReportLab is available
|
| 2175 |
+
if not REPORTLAB_AVAILABLE:
|
| 2176 |
+
return "Error: ReportLab library is not installed. Please install it with 'pip install reportlab'."
|
| 2177 |
+
|
| 2178 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2179 |
|
| 2180 |
# Create a proper downloads directory in the app folder
|
| 2181 |
downloads_dir = os.path.join(DATA_DIR, "downloads")
|
|
|
|
| 2490 |
|
| 2491 |
# Launch the Gradio app
|
| 2492 |
app = create_interface()
|
| 2493 |
+
app.launch()
|