Spaces:
Sleeping
Sleeping
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()
|