Spaces:
Sleeping
Sleeping
Update session_analysis.py
Browse files- session_analysis.py +186 -105
session_analysis.py
CHANGED
@@ -61,25 +61,36 @@ def show_upload_section():
|
|
61 |
def process_video_file(video_file):
|
62 |
"""Process uploaded video file"""
|
63 |
try:
|
64 |
-
#
|
65 |
temp_path = f"temp_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
|
|
|
|
|
66 |
with open(temp_path, "wb") as f:
|
67 |
f.write(video_file.getbuffer())
|
68 |
|
|
|
69 |
st.video(temp_path)
|
70 |
-
st.info("Video uploaded successfully. Please provide transcript.")
|
71 |
|
72 |
-
# Add
|
73 |
-
transcript = st.text_area(
|
|
|
|
|
|
|
|
|
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)}")
|
@@ -785,42 +796,74 @@ def format_session_data(session_data):
|
|
785 |
return formatted_text
|
786 |
|
787 |
def analyze_session_content(transcript):
|
|
|
788 |
try:
|
789 |
-
|
|
|
|
|
|
|
|
|
790 |
model = genai.GenerativeModel('gemini-pro')
|
791 |
|
792 |
-
# Prepare
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
798 |
{transcript}
|
799 |
-
|
800 |
-
{SESSION_EVALUATION_PROMPT}
|
801 |
"""
|
802 |
|
803 |
-
# Generate
|
804 |
-
response = model.generate_content(
|
805 |
-
|
806 |
-
# Parse the response
|
807 |
-
analysis_results = parse_analysis_response(response.text)
|
808 |
|
809 |
# Store results in session state
|
810 |
-
st.session_state.analysis_results =
|
|
|
|
|
|
|
811 |
|
812 |
except Exception as e:
|
813 |
-
st.error(f"Error analyzing session
|
|
|
814 |
|
815 |
def show_analysis_results():
|
816 |
"""Display the analysis results in the dashboard"""
|
817 |
-
if not st.session_state.analysis_results:
|
818 |
-
|
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
|
@@ -835,110 +878,132 @@ def show_analysis_results():
|
|
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 |
-
|
842 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
843 |
st.subheader("Strengths")
|
844 |
-
|
845 |
-
|
846 |
-
|
847 |
-
|
|
|
|
|
|
|
|
|
848 |
st.subheader("Areas for Improvement")
|
849 |
-
|
850 |
-
|
|
|
|
|
851 |
|
852 |
# Technical Skills Tab
|
853 |
with tabs[1]:
|
854 |
st.subheader("MI Technique Usage")
|
855 |
-
technique_data = analysis.get('technique_usage', {})
|
856 |
|
857 |
-
#
|
858 |
-
|
859 |
-
|
860 |
-
|
861 |
-
|
862 |
-
|
863 |
-
|
864 |
-
|
865 |
-
|
866 |
-
|
867 |
-
|
868 |
-
|
869 |
-
|
870 |
-
|
871 |
-
|
|
|
|
|
|
|
|
|
872 |
)
|
873 |
-
|
874 |
|
875 |
-
|
876 |
-
|
877 |
-
|
878 |
-
|
|
|
|
|
|
|
879 |
|
880 |
# Client Language Tab
|
881 |
with tabs[2]:
|
882 |
st.subheader("Client Language Analysis")
|
883 |
|
884 |
-
#
|
885 |
-
|
886 |
-
|
887 |
-
with col1:
|
888 |
st.markdown("### Change Talk 🌱")
|
889 |
-
|
890 |
-
|
891 |
-
|
892 |
-
st.markdown(f"- {
|
893 |
-
|
894 |
-
|
895 |
-
|
896 |
-
|
897 |
st.markdown("### Sustain Talk 🔄")
|
898 |
-
|
899 |
-
|
900 |
-
|
901 |
-
st.markdown(f"- {
|
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 |
-
#
|
910 |
-
|
911 |
-
themes
|
912 |
-
|
913 |
-
|
914 |
-
|
915 |
-
|
916 |
-
|
917 |
-
session_summary = analysis.get('session_summary', '')
|
918 |
-
if session_summary:
|
919 |
-
st.write(session_summary)
|
920 |
|
921 |
-
#
|
922 |
-
|
923 |
-
|
|
|
|
|
924 |
|
925 |
# Recommendations Tab
|
926 |
with tabs[4]:
|
927 |
st.subheader("Recommendations for Improvement")
|
928 |
|
929 |
-
#
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
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"""
|
@@ -1058,6 +1123,22 @@ def parse_analysis_response(response_text):
|
|
1058 |
st.error(f"Error parsing analysis response: {str(e)}")
|
1059 |
return None
|
1060 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1061 |
def create_gauge_chart(score):
|
1062 |
"""Create a gauge chart for MI Adherence Score"""
|
1063 |
fig = go.Figure(go.Indicator(
|
|
|
61 |
def process_video_file(video_file):
|
62 |
"""Process uploaded video file"""
|
63 |
try:
|
64 |
+
# Create a unique temporary file name
|
65 |
temp_path = f"temp_video_{datetime.now().strftime('%Y%m%d_%H%M%S')}.mp4"
|
66 |
+
|
67 |
+
# Save video temporarily
|
68 |
with open(temp_path, "wb") as f:
|
69 |
f.write(video_file.getbuffer())
|
70 |
|
71 |
+
# Display video
|
72 |
st.video(temp_path)
|
|
|
73 |
|
74 |
+
# Add transcript input
|
75 |
+
transcript = st.text_area(
|
76 |
+
"Enter the session transcript:",
|
77 |
+
height=300,
|
78 |
+
help="Paste or type the transcript of the session here."
|
79 |
+
)
|
80 |
|
81 |
# Add analyze button
|
82 |
if st.button("Analyze Transcript"):
|
83 |
+
if transcript.strip():
|
84 |
with st.spinner('Analyzing transcript...'):
|
|
|
85 |
analyze_session_content(transcript)
|
86 |
else:
|
87 |
st.warning("Please enter a transcript before analyzing.")
|
88 |
+
|
89 |
+
# Clean up temporary file
|
90 |
+
try:
|
91 |
+
os.remove(temp_path)
|
92 |
+
except:
|
93 |
+
pass
|
94 |
|
95 |
except Exception as e:
|
96 |
st.error(f"Error processing video: {str(e)}")
|
|
|
796 |
return formatted_text
|
797 |
|
798 |
def analyze_session_content(transcript):
|
799 |
+
"""Analyze the session transcript using Gemini"""
|
800 |
try:
|
801 |
+
if not transcript:
|
802 |
+
st.warning("Please provide a transcript for analysis.")
|
803 |
+
return
|
804 |
+
|
805 |
+
# Configure the model
|
806 |
model = genai.GenerativeModel('gemini-pro')
|
807 |
|
808 |
+
# Prepare prompt with structured output request
|
809 |
+
prompt = """
|
810 |
+
Analyze this MI (Motivational Interviewing) session transcript and provide analysis in the following structured format:
|
811 |
+
|
812 |
+
MI Adherence Score: [0-100]
|
813 |
+
|
814 |
+
Key Themes:
|
815 |
+
- [Theme 1]
|
816 |
+
- [Theme 2]
|
817 |
+
- [Theme 3]
|
818 |
+
|
819 |
+
Technique Usage:
|
820 |
+
- Open Questions: [count]
|
821 |
+
- Reflections: [count]
|
822 |
+
- Affirmations: [count]
|
823 |
+
- Summaries: [count]
|
824 |
+
|
825 |
+
Strengths:
|
826 |
+
- [Strength 1]
|
827 |
+
- [Strength 2]
|
828 |
+
- [Strength 3]
|
829 |
+
|
830 |
+
Areas for Improvement:
|
831 |
+
- [Area 1]
|
832 |
+
- [Area 2]
|
833 |
+
- [Area 3]
|
834 |
+
|
835 |
+
Change Talk Instances:
|
836 |
+
- [Example 1]
|
837 |
+
- [Example 2]
|
838 |
+
|
839 |
+
Sustain Talk Instances:
|
840 |
+
- [Example 1]
|
841 |
+
- [Example 2]
|
842 |
+
|
843 |
+
Session Summary:
|
844 |
+
[Brief summary of the session flow and key moments]
|
845 |
+
|
846 |
+
Transcript to analyze:
|
847 |
{transcript}
|
|
|
|
|
848 |
"""
|
849 |
|
850 |
+
# Generate response
|
851 |
+
response = model.generate_content(prompt)
|
|
|
|
|
|
|
852 |
|
853 |
# Store results in session state
|
854 |
+
st.session_state.analysis_results = response.text
|
855 |
+
|
856 |
+
# Trigger results display
|
857 |
+
show_analysis_results()
|
858 |
|
859 |
except Exception as e:
|
860 |
+
st.error(f"Error analyzing session: {str(e)}")
|
861 |
+
|
862 |
|
863 |
def show_analysis_results():
|
864 |
"""Display the analysis results in the dashboard"""
|
865 |
+
if 'analysis_results' not in st.session_state or not st.session_state.analysis_results:
|
866 |
+
st.info("No analysis results available. Please analyze a transcript first.")
|
|
|
|
|
|
|
|
|
|
|
867 |
return
|
868 |
|
869 |
# Create tabs for different aspects of analysis
|
|
|
878 |
# MI Adherence Tab
|
879 |
with tabs[0]:
|
880 |
st.subheader("MI Adherence Score")
|
|
|
|
|
881 |
|
882 |
+
# Extract score from analysis results
|
883 |
+
score_match = re.search(r'MI Adherence Score:\s*(\d+)', st.session_state.analysis_results)
|
884 |
+
if score_match:
|
885 |
+
score = int(score_match.group(1))
|
886 |
+
|
887 |
+
# Create score gauge
|
888 |
+
fig = go.Figure(go.Indicator(
|
889 |
+
mode="gauge+number",
|
890 |
+
value=score,
|
891 |
+
domain={'x': [0, 1], 'y': [0, 1]},
|
892 |
+
gauge={'axis': {'range': [0, 100]},
|
893 |
+
'bar': {'color': "rgb(26, 118, 255)"},
|
894 |
+
'steps': [
|
895 |
+
{'range': [0, 33], 'color': "lightgray"},
|
896 |
+
{'range': [33, 66], 'color': "gray"},
|
897 |
+
{'range': [66, 100], 'color': "darkgray"}
|
898 |
+
]}))
|
899 |
+
st.plotly_chart(fig)
|
900 |
+
|
901 |
+
# Extract and display strengths
|
902 |
+
strengths = re.findall(r'Strengths:\n((?:- .*\n)*)', st.session_state.analysis_results)
|
903 |
+
if strengths:
|
904 |
st.subheader("Strengths")
|
905 |
+
strength_list = strengths[0].strip().split('\n')
|
906 |
+
for strength in strength_list:
|
907 |
+
if strength.startswith('- '):
|
908 |
+
st.markdown(f"✅ {strength[2:]}")
|
909 |
+
|
910 |
+
# Extract and display areas for improvement
|
911 |
+
improvements = re.findall(r'Areas for Improvement:\n((?:- .*\n)*)', st.session_state.analysis_results)
|
912 |
+
if improvements:
|
913 |
st.subheader("Areas for Improvement")
|
914 |
+
improvement_list = improvements[0].strip().split('\n')
|
915 |
+
for improvement in improvement_list:
|
916 |
+
if improvement.startswith('- '):
|
917 |
+
st.markdown(f"🔄 {improvement[2:]}")
|
918 |
|
919 |
# Technical Skills Tab
|
920 |
with tabs[1]:
|
921 |
st.subheader("MI Technique Usage")
|
|
|
922 |
|
923 |
+
# Extract technique counts
|
924 |
+
techniques = {
|
925 |
+
'Open Questions': 0,
|
926 |
+
'Reflections': 0,
|
927 |
+
'Affirmations': 0,
|
928 |
+
'Summaries': 0
|
929 |
+
}
|
930 |
+
|
931 |
+
for technique in techniques.keys():
|
932 |
+
count_match = re.search(f'{technique}:\s*(\d+)', st.session_state.analysis_results)
|
933 |
+
if count_match:
|
934 |
+
techniques[technique] = int(count_match.group(1))
|
935 |
+
|
936 |
+
# Create bar chart
|
937 |
+
fig = go.Figure(data=[
|
938 |
+
go.Bar(
|
939 |
+
x=list(techniques.keys()),
|
940 |
+
y=list(techniques.values()),
|
941 |
+
marker_color='rgb(26, 118, 255)'
|
942 |
)
|
943 |
+
])
|
944 |
|
945 |
+
fig.update_layout(
|
946 |
+
title="Technique Usage Frequency",
|
947 |
+
xaxis_title="Technique",
|
948 |
+
yaxis_title="Count",
|
949 |
+
template="plotly_white"
|
950 |
+
)
|
951 |
+
st.plotly_chart(fig)
|
952 |
|
953 |
# Client Language Tab
|
954 |
with tabs[2]:
|
955 |
st.subheader("Client Language Analysis")
|
956 |
|
957 |
+
# Extract change talk
|
958 |
+
change_talk = re.findall(r'Change Talk Instances:\n((?:- .*\n)*)', st.session_state.analysis_results)
|
959 |
+
if change_talk:
|
|
|
960 |
st.markdown("### Change Talk 🌱")
|
961 |
+
changes = change_talk[0].strip().split('\n')
|
962 |
+
for change in changes:
|
963 |
+
if change.startswith('- '):
|
964 |
+
st.markdown(f"- {change[2:]}")
|
965 |
+
|
966 |
+
# Extract sustain talk
|
967 |
+
sustain_talk = re.findall(r'Sustain Talk Instances:\n((?:- .*\n)*)', st.session_state.analysis_results)
|
968 |
+
if sustain_talk:
|
969 |
st.markdown("### Sustain Talk 🔄")
|
970 |
+
sustains = sustain_talk[0].strip().split('\n')
|
971 |
+
for sustain in sustains:
|
972 |
+
if sustain.startswith('- '):
|
973 |
+
st.markdown(f"- {sustain[2:]}")
|
|
|
|
|
974 |
|
975 |
# Session Flow Tab
|
976 |
with tabs[3]:
|
977 |
st.subheader("Session Flow Analysis")
|
978 |
|
979 |
+
# Extract key themes
|
980 |
+
themes = re.findall(r'Key Themes:\n((?:- .*\n)*)', st.session_state.analysis_results)
|
981 |
+
if themes:
|
982 |
+
st.markdown("### Key Themes 🎯")
|
983 |
+
theme_list = themes[0].strip().split('\n')
|
984 |
+
for theme in theme_list:
|
985 |
+
if theme.startswith('- '):
|
986 |
+
st.markdown(f"- {theme[2:]}")
|
|
|
|
|
|
|
987 |
|
988 |
+
# Extract session summary
|
989 |
+
summary_match = re.search(r'Session Summary:\n(.*?)(?=\n\n|\Z)', st.session_state.analysis_results, re.DOTALL)
|
990 |
+
if summary_match:
|
991 |
+
st.markdown("### Session Summary")
|
992 |
+
st.write(summary_match.group(1).strip())
|
993 |
|
994 |
# Recommendations Tab
|
995 |
with tabs[4]:
|
996 |
st.subheader("Recommendations for Improvement")
|
997 |
|
998 |
+
# Display improvement areas as recommendations
|
999 |
+
if improvements:
|
1000 |
+
st.markdown("### Priority Areas 🎯")
|
1001 |
+
improvement_list = improvements[0].strip().split('\n')
|
1002 |
+
for i, improvement in enumerate(improvement_list[:3], 1):
|
1003 |
+
if improvement.startswith('- '):
|
1004 |
+
st.markdown(f"**{i}. {improvement[2:]}**")
|
1005 |
+
st.markdown(get_improvement_suggestion(improvement[2:]))
|
1006 |
+
|
|
|
|
|
|
|
|
|
1007 |
|
1008 |
def get_technique_description(technique):
|
1009 |
"""Return description for MI techniques"""
|
|
|
1123 |
st.error(f"Error parsing analysis response: {str(e)}")
|
1124 |
return None
|
1125 |
|
1126 |
+
def get_improvement_suggestion(area):
|
1127 |
+
"""Return specific suggestions for improvement areas"""
|
1128 |
+
suggestions = {
|
1129 |
+
"open questions": "Practice replacing closed questions with open-ended ones. For example:\n- Instead of: 'Did you exercise?'\n- Try: 'What kinds of physical activity have you been doing?'",
|
1130 |
+
"reflections": "Work on using more complex reflections by adding meaning or emotion to what the client has said. Try to make at least two complex reflections for every simple reflection.",
|
1131 |
+
"empathy": "Focus on seeing the situation from the client's perspective. Take time to verbalize your understanding of their emotions and experiences.",
|
1132 |
+
"summaries": "Use more collecting summaries to gather key points discussed and transition summaries to move between topics.",
|
1133 |
+
"affirmations": "Look for opportunities to genuinely affirm client strengths and efforts, not just outcomes."
|
1134 |
+
}
|
1135 |
+
|
1136 |
+
# Look for matching suggestions using partial string matching
|
1137 |
+
for key, value in suggestions.items():
|
1138 |
+
if key in area.lower():
|
1139 |
+
return value
|
1140 |
+
return "Focus on incorporating this element more intentionally in your sessions. Consider recording your sessions and reviewing them with a supervisor or peer."
|
1141 |
+
|
1142 |
def create_gauge_chart(score):
|
1143 |
"""Create a gauge chart for MI Adherence Score"""
|
1144 |
fig = go.Figure(go.Indicator(
|