Spaces:
Sleeping
Sleeping
Update session_analysis.py
Browse files- session_analysis.py +265 -37
session_analysis.py
CHANGED
@@ -67,13 +67,19 @@ def process_video_file(video_file):
|
|
67 |
f.write(video_file.getbuffer())
|
68 |
|
69 |
st.video(temp_path)
|
70 |
-
st.info("Video uploaded successfully. Please
|
71 |
|
72 |
# Add manual transcript input
|
73 |
-
transcript = st.text_area("
|
74 |
-
|
75 |
-
|
76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
except Exception as e:
|
79 |
st.error(f"Error processing video: {str(e)}")
|
@@ -87,13 +93,19 @@ def process_audio_file(audio_file):
|
|
87 |
f.write(audio_file.getbuffer())
|
88 |
|
89 |
st.audio(temp_path)
|
90 |
-
st.info("Audio uploaded successfully. Please
|
91 |
|
92 |
# Add manual transcript input
|
93 |
-
transcript = st.text_area("
|
94 |
-
|
95 |
-
|
96 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
except Exception as e:
|
99 |
st.error(f"Error processing audio: {str(e)}")
|
@@ -170,23 +182,83 @@ def get_processing_step_name(step):
|
|
170 |
return steps[step]
|
171 |
|
172 |
def process_text_file(file):
|
|
|
173 |
try:
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
|
|
|
|
|
|
|
|
187 |
except Exception as e:
|
188 |
st.error(f"Error processing file: {str(e)}")
|
189 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
def show_manual_input_form():
|
191 |
st.subheader("Session Details")
|
192 |
|
@@ -741,13 +813,16 @@ def analyze_session_content(transcript):
|
|
741 |
st.error(f"Error analyzing session content: {str(e)}")
|
742 |
|
743 |
def show_analysis_results():
|
744 |
-
"""Display
|
745 |
if not st.session_state.analysis_results:
|
746 |
-
st.warning("No analysis results available.")
|
747 |
return
|
748 |
|
749 |
-
|
750 |
-
|
|
|
|
|
|
|
|
|
751 |
# Create tabs for different aspects of analysis
|
752 |
tabs = st.tabs([
|
753 |
"MI Adherence",
|
@@ -756,21 +831,174 @@ def show_analysis_results():
|
|
756 |
"Session Flow",
|
757 |
"Recommendations"
|
758 |
])
|
759 |
-
|
760 |
-
#
|
761 |
-
analysis = parse_analysis_results(st.session_state.analysis_results)
|
762 |
-
|
763 |
-
# Display results in respective tabs
|
764 |
with tabs[0]:
|
765 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
766 |
with tabs[1]:
|
767 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
768 |
with tabs[2]:
|
769 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
770 |
with tabs[3]:
|
771 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
772 |
with tabs[4]:
|
773 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
774 |
|
775 |
def parse_analysis_response(response_text):
|
776 |
"""Parse the AI response into structured analysis results"""
|
|
|
67 |
f.write(video_file.getbuffer())
|
68 |
|
69 |
st.video(temp_path)
|
70 |
+
st.info("Video uploaded successfully. Please provide transcript.")
|
71 |
|
72 |
# Add manual transcript input
|
73 |
+
transcript = st.text_area("Enter the session transcript:", height=300)
|
74 |
+
|
75 |
+
# Add analyze button
|
76 |
+
if st.button("Analyze Transcript"):
|
77 |
+
if transcript:
|
78 |
+
with st.spinner('Analyzing transcript...'):
|
79 |
+
st.session_state.current_transcript = transcript
|
80 |
+
analyze_session_content(transcript)
|
81 |
+
else:
|
82 |
+
st.warning("Please enter a transcript before analyzing.")
|
83 |
|
84 |
except Exception as e:
|
85 |
st.error(f"Error processing video: {str(e)}")
|
|
|
93 |
f.write(audio_file.getbuffer())
|
94 |
|
95 |
st.audio(temp_path)
|
96 |
+
st.info("Audio uploaded successfully. Please provide transcript.")
|
97 |
|
98 |
# Add manual transcript input
|
99 |
+
transcript = st.text_area("Enter the session transcript:", height=300)
|
100 |
+
|
101 |
+
# Add analyze button
|
102 |
+
if st.button("Analyze Transcript"):
|
103 |
+
if transcript:
|
104 |
+
with st.spinner('Analyzing transcript...'):
|
105 |
+
st.session_state.current_transcript = transcript
|
106 |
+
analyze_session_content(transcript)
|
107 |
+
else:
|
108 |
+
st.warning("Please enter a transcript before analyzing.")
|
109 |
|
110 |
except Exception as e:
|
111 |
st.error(f"Error processing audio: {str(e)}")
|
|
|
182 |
return steps[step]
|
183 |
|
184 |
def process_text_file(file):
|
185 |
+
"""Process uploaded text file"""
|
186 |
try:
|
187 |
+
# Read file content
|
188 |
+
content = file.getvalue().decode("utf-8")
|
189 |
+
st.session_state.current_transcript = content
|
190 |
+
|
191 |
+
# Display transcript with edit option
|
192 |
+
edited_transcript = st.text_area(
|
193 |
+
"Review and edit transcript if needed:",
|
194 |
+
value=content,
|
195 |
+
height=300
|
196 |
+
)
|
197 |
+
|
198 |
+
# Add analyze button
|
199 |
+
if st.button("Analyze Transcript"):
|
200 |
+
with st.spinner('Analyzing transcript...'):
|
201 |
+
st.session_state.current_transcript = edited_transcript
|
202 |
+
analyze_session_content(edited_transcript)
|
203 |
+
|
204 |
except Exception as e:
|
205 |
st.error(f"Error processing file: {str(e)}")
|
206 |
|
207 |
+
def parse_analysis_results(raw_results):
|
208 |
+
"""Parse the raw analysis results into structured format"""
|
209 |
+
if isinstance(raw_results, dict):
|
210 |
+
return raw_results # Already parsed
|
211 |
+
|
212 |
+
try:
|
213 |
+
# If it's a string, try to extract structured data
|
214 |
+
analysis = {
|
215 |
+
'mi_adherence_score': 0,
|
216 |
+
'key_themes': [],
|
217 |
+
'technique_usage': {},
|
218 |
+
'strengths': [],
|
219 |
+
'areas_for_improvement': [],
|
220 |
+
'session_summary': ''
|
221 |
+
}
|
222 |
+
|
223 |
+
# Extract score (assuming it's in format "Score: XX")
|
224 |
+
score_match = re.search(r'Score:\s*(\d+)', raw_results)
|
225 |
+
if score_match:
|
226 |
+
analysis['mi_adherence_score'] = int(score_match.group(1))
|
227 |
+
|
228 |
+
# Extract themes (assuming they're listed after "Key Themes:")
|
229 |
+
themes_match = re.search(r'Key Themes:(.*?)(?=\n\n|\Z)', raw_results, re.DOTALL)
|
230 |
+
if themes_match:
|
231 |
+
themes = themes_match.group(1).strip().split('\n')
|
232 |
+
analysis['key_themes'] = [t.strip('- ') for t in themes if t.strip()]
|
233 |
+
|
234 |
+
# Extract techniques (assuming they're listed with counts)
|
235 |
+
techniques = re.findall(r'(\w+\s*\w*)\s*:\s*(\d+)', raw_results)
|
236 |
+
if techniques:
|
237 |
+
analysis['technique_usage'] = {t[0]: int(t[1]) for t in techniques}
|
238 |
+
|
239 |
+
# Extract strengths
|
240 |
+
strengths_match = re.search(r'Strengths:(.*?)(?=Areas for Improvement|\Z)', raw_results, re.DOTALL)
|
241 |
+
if strengths_match:
|
242 |
+
strengths = strengths_match.group(1).strip().split('\n')
|
243 |
+
analysis['strengths'] = [s.strip('- ') for s in strengths if s.strip()]
|
244 |
+
|
245 |
+
# Extract areas for improvement
|
246 |
+
improvements_match = re.search(r'Areas for Improvement:(.*?)(?=\n\n|\Z)', raw_results, re.DOTALL)
|
247 |
+
if improvements_match:
|
248 |
+
improvements = improvements_match.group(1).strip().split('\n')
|
249 |
+
analysis['areas_for_improvement'] = [i.strip('- ') for i in improvements if i.strip()]
|
250 |
+
|
251 |
+
# Extract summary
|
252 |
+
summary_match = re.search(r'Summary:(.*?)(?=\n\n|\Z)', raw_results, re.DOTALL)
|
253 |
+
if summary_match:
|
254 |
+
analysis['session_summary'] = summary_match.group(1).strip()
|
255 |
+
|
256 |
+
return analysis
|
257 |
+
|
258 |
+
except Exception as e:
|
259 |
+
st.error(f"Error parsing analysis results: {str(e)}")
|
260 |
+
return None
|
261 |
+
|
262 |
def show_manual_input_form():
|
263 |
st.subheader("Session Details")
|
264 |
|
|
|
813 |
st.error(f"Error analyzing session content: {str(e)}")
|
814 |
|
815 |
def show_analysis_results():
|
816 |
+
"""Display the analysis results in the dashboard"""
|
817 |
if not st.session_state.analysis_results:
|
|
|
818 |
return
|
819 |
|
820 |
+
# Parse the results
|
821 |
+
analysis = parse_analysis_results(st.session_state.analysis_results)
|
822 |
+
if not analysis:
|
823 |
+
st.error("Unable to parse analysis results")
|
824 |
+
return
|
825 |
+
|
826 |
# Create tabs for different aspects of analysis
|
827 |
tabs = st.tabs([
|
828 |
"MI Adherence",
|
|
|
831 |
"Session Flow",
|
832 |
"Recommendations"
|
833 |
])
|
834 |
+
|
835 |
+
# MI Adherence Tab
|
|
|
|
|
|
|
836 |
with tabs[0]:
|
837 |
+
st.subheader("MI Adherence Score")
|
838 |
+
score = analysis.get('mi_adherence_score', 0)
|
839 |
+
create_gauge_chart(score)
|
840 |
+
|
841 |
+
col1, col2 = st.columns(2)
|
842 |
+
with col1:
|
843 |
+
st.subheader("Strengths")
|
844 |
+
for strength in analysis.get('strengths', []):
|
845 |
+
st.markdown(f"✅ {strength}")
|
846 |
+
|
847 |
+
with col2:
|
848 |
+
st.subheader("Areas for Improvement")
|
849 |
+
for area in analysis.get('areas_for_improvement', []):
|
850 |
+
st.markdown(f"🔄 {area}")
|
851 |
+
|
852 |
+
# Technical Skills Tab
|
853 |
with tabs[1]:
|
854 |
+
st.subheader("MI Technique Usage")
|
855 |
+
technique_data = analysis.get('technique_usage', {})
|
856 |
+
|
857 |
+
# Create bar chart for technique usage
|
858 |
+
if technique_data:
|
859 |
+
fig = go.Figure(data=[
|
860 |
+
go.Bar(
|
861 |
+
x=list(technique_data.keys()),
|
862 |
+
y=list(technique_data.values()),
|
863 |
+
marker_color='rgb(26, 118, 255)'
|
864 |
+
)
|
865 |
+
])
|
866 |
+
|
867 |
+
fig.update_layout(
|
868 |
+
title="Technique Usage Frequency",
|
869 |
+
xaxis_title="Technique",
|
870 |
+
yaxis_title="Count",
|
871 |
+
template="plotly_white"
|
872 |
+
)
|
873 |
+
st.plotly_chart(fig)
|
874 |
+
|
875 |
+
# Technique breakdown
|
876 |
+
for technique, count in technique_data.items():
|
877 |
+
with st.expander(f"{technique} ({count} instances)"):
|
878 |
+
st.write(get_technique_description(technique))
|
879 |
+
|
880 |
+
# Client Language Tab
|
881 |
with tabs[2]:
|
882 |
+
st.subheader("Client Language Analysis")
|
883 |
+
|
884 |
+
# Create columns for different types of client language
|
885 |
+
col1, col2 = st.columns(2)
|
886 |
+
|
887 |
+
with col1:
|
888 |
+
st.markdown("### Change Talk 🌱")
|
889 |
+
change_talk = analysis.get('change_talk', [])
|
890 |
+
if change_talk:
|
891 |
+
for talk in change_talk:
|
892 |
+
st.markdown(f"- {talk}")
|
893 |
+
else:
|
894 |
+
st.info("No specific change talk identified")
|
895 |
+
|
896 |
+
with col2:
|
897 |
+
st.markdown("### Sustain Talk 🔄")
|
898 |
+
sustain_talk = analysis.get('sustain_talk', [])
|
899 |
+
if sustain_talk:
|
900 |
+
for talk in sustain_talk:
|
901 |
+
st.markdown(f"- {talk}")
|
902 |
+
else:
|
903 |
+
st.info("No specific sustain talk identified")
|
904 |
+
|
905 |
+
# Session Flow Tab
|
906 |
with tabs[3]:
|
907 |
+
st.subheader("Session Flow Analysis")
|
908 |
+
|
909 |
+
# Display key themes
|
910 |
+
st.markdown("### Key Themes 🎯")
|
911 |
+
themes = analysis.get('key_themes', [])
|
912 |
+
for theme in themes:
|
913 |
+
st.markdown(f"- {theme}")
|
914 |
+
|
915 |
+
# Session structure
|
916 |
+
st.markdown("### Session Structure")
|
917 |
+
session_summary = analysis.get('session_summary', '')
|
918 |
+
if session_summary:
|
919 |
+
st.write(session_summary)
|
920 |
+
|
921 |
+
# Add timeline visualization if available
|
922 |
+
if 'timeline' in analysis:
|
923 |
+
create_session_timeline(analysis['timeline'])
|
924 |
+
|
925 |
+
# Recommendations Tab
|
926 |
with tabs[4]:
|
927 |
+
st.subheader("Recommendations for Improvement")
|
928 |
+
|
929 |
+
# Priority recommendations
|
930 |
+
st.markdown("### Priority Areas 🎯")
|
931 |
+
for area in analysis.get('areas_for_improvement', [])[:3]: # Top 3 priorities
|
932 |
+
st.markdown(f"**1️⃣ {area}**")
|
933 |
+
st.markdown(get_improvement_suggestion(area))
|
934 |
+
|
935 |
+
# Specific action items
|
936 |
+
st.markdown("### Action Items ✅")
|
937 |
+
create_action_items(analysis)
|
938 |
+
|
939 |
+
# Resources
|
940 |
+
st.markdown("### Helpful Resources 📚")
|
941 |
+
show_relevant_resources(analysis)
|
942 |
+
|
943 |
+
def get_technique_description(technique):
|
944 |
+
"""Return description for MI techniques"""
|
945 |
+
descriptions = {
|
946 |
+
"Open Questions": "Questions that allow for elaboration and cannot be answered with a simple yes/no.",
|
947 |
+
"Reflections": "Statements that mirror, rephrase, or elaborate on the client's speech.",
|
948 |
+
"Affirmations": "Statements that recognize client strengths and acknowledge behaviors that lead to positive change.",
|
949 |
+
"Summaries": "Statements that collect, link, and transition between client statements.",
|
950 |
+
"Information Giving": "Providing information with permission and in response to client needs.",
|
951 |
+
# Add more techniques as needed
|
952 |
+
}
|
953 |
+
return descriptions.get(technique, "Description not available")
|
954 |
+
|
955 |
+
def create_session_timeline(timeline_data):
|
956 |
+
"""Create a visual timeline of the session"""
|
957 |
+
if not timeline_data:
|
958 |
+
st.info("Detailed timeline not available")
|
959 |
+
return
|
960 |
+
|
961 |
+
fig = go.Figure()
|
962 |
+
# Add timeline visualization code here
|
963 |
+
st.plotly_chart(fig)
|
964 |
+
|
965 |
+
def get_improvement_suggestion(area):
|
966 |
+
"""Return specific suggestions for improvement areas"""
|
967 |
+
suggestions = {
|
968 |
+
"Open Questions": "Try replacing closed questions with open-ended ones. Instead of 'Did you exercise?', ask 'What kinds of physical activity have you been doing?'",
|
969 |
+
"Reflections": "Practice using more complex reflections by adding meaning or emotion to what the client has said.",
|
970 |
+
"Empathy": "Focus on seeing the situation from the client's perspective and verbalize your understanding.",
|
971 |
+
# Add more suggestions as needed
|
972 |
+
}
|
973 |
+
return suggestions.get(area, "Work on incorporating this element more intentionally in your sessions.")
|
974 |
+
|
975 |
+
def create_action_items(analysis):
|
976 |
+
"""Create specific action items based on analysis"""
|
977 |
+
st.write("Based on the analysis, consider focusing on these specific actions:")
|
978 |
+
|
979 |
+
# Example action items
|
980 |
+
action_items = [
|
981 |
+
"Practice one new MI skill each session",
|
982 |
+
"Record and review your sessions",
|
983 |
+
"Focus on developing complex reflections",
|
984 |
+
"Track change talk/sustain talk ratio"
|
985 |
+
]
|
986 |
+
|
987 |
+
for item in action_items:
|
988 |
+
st.checkbox(item)
|
989 |
+
|
990 |
+
def show_relevant_resources(analysis):
|
991 |
+
"""Display relevant resources based on analysis"""
|
992 |
+
resources = [
|
993 |
+
{"title": "MI Practice Exercises", "url": "#"},
|
994 |
+
{"title": "Reflection Templates", "url": "#"},
|
995 |
+
{"title": "Change Talk Recognition Guide", "url": "#"},
|
996 |
+
{"title": "MI Community of Practice", "url": "#"}
|
997 |
+
]
|
998 |
+
|
999 |
+
for resource in resources:
|
1000 |
+
st.markdown(f"[{resource['title']}]({resource['url']})")
|
1001 |
+
|
1002 |
|
1003 |
def parse_analysis_response(response_text):
|
1004 |
"""Parse the AI response into structured analysis results"""
|