Spaces:
Paused
Paused
Update LLM analysis format and fix navigation issues
Browse files- Updated LLM analyzer to use clean format without emojis or em dashes
- Changed to numbered format (1-3) for Strengths, Areas for Improvement, and Practice Tips
- Updated practice tips to only use drills from golf articles CSV
- Removed putting/short game drills, kept only swing technique and setup drills
- Fixed navigation issue between improvements page and chatbot page
- Added session state safeguards to prevent unnecessary video re-uploads
- Enhanced chatbot access with graceful fallbacks for missing analysis data
- app/golf_swing_articles_complete.csv +0 -0
- app/models/llm_analyzer.py +94 -62
- app/par-ity project horizontal logo.png +0 -3
- app/streamlit_app.py +22 -10
app/golf_swing_articles_complete.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
app/models/llm_analyzer.py
CHANGED
|
@@ -531,36 +531,40 @@ Use the benchmarks above to guide your evaluation. Follow this exact format:
|
|
| 531 |
**PERFORMANCE_CLASSIFICATION:** [XX%]
|
| 532 |
(XX = number from 10% to 100%)
|
| 533 |
|
| 534 |
-
**
|
| 535 |
-
|
| 536 |
-
|
|
|
|
|
|
|
| 537 |
- Compare to professional benchmarks
|
| 538 |
-
- Highlight what's working well and
|
| 539 |
-
- Use
|
| 540 |
|
| 541 |
Example:
|
| 542 |
-
|
|
|
|
|
|
|
|
|
|
| 543 |
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
- Use
|
| 547 |
-
-
|
| 548 |
-
-
|
| 549 |
-
- Don't suggest fixes here—save those for the next section
|
| 550 |
|
| 551 |
Example:
|
| 552 |
-
|
|
|
|
| 553 |
|
| 554 |
-
**
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
-
|
| 558 |
-
-
|
| 559 |
-
-
|
| 560 |
-
- Emphasize benefits
|
| 561 |
|
| 562 |
Example:
|
| 563 |
-
|
| 564 |
|
| 565 |
**SCORING GUIDELINES (Use to help decide % score)**
|
| 566 |
|
|
@@ -585,12 +589,26 @@ Hip Mobility: Try increasing your hip rotation during the downswing to unlock mo
|
|
| 585 |
- **10–39%**: Novice
|
| 586 |
|
| 587 |
**STYLE & FORMATTING RULES:**
|
| 588 |
-
- Use these headers: PERFORMANCE_CLASSIFICATION,
|
| 589 |
-
-
|
|
|
|
|
|
|
| 590 |
- Tie all points to professional standards
|
| 591 |
- Use a positive, coaching tone throughout
|
| 592 |
- Avoid saying "perfect" — say "strong" or "meets standards"
|
| 593 |
- Focus on biomechanics, not timing (e.g. tempo, frame count)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 594 |
"""
|
| 595 |
|
| 596 |
return prompt
|
|
@@ -637,23 +655,29 @@ def parse_and_format_analysis(raw_analysis):
|
|
| 637 |
break
|
| 638 |
|
| 639 |
# Extract strengths using the new structured format
|
| 640 |
-
strengths_match = re.search(r'\*\*
|
| 641 |
if strengths_match:
|
| 642 |
strengths_text = strengths_match.group(1)
|
| 643 |
-
# Extract
|
| 644 |
-
strength_items = re.findall(r'
|
| 645 |
-
formatted_analysis['strengths'] = [
|
| 646 |
|
| 647 |
-
# Extract
|
| 648 |
-
weaknesses_match = re.search(r'\*\*
|
| 649 |
if weaknesses_match:
|
| 650 |
weaknesses_text = weaknesses_match.group(1)
|
| 651 |
-
# Extract
|
| 652 |
-
weakness_items = re.findall(r'
|
| 653 |
-
formatted_analysis['weaknesses'] = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 654 |
|
| 655 |
-
# Extract
|
| 656 |
-
priority_match = re.search(r'\*\*
|
| 657 |
if priority_match:
|
| 658 |
priority_text = priority_match.group(1)
|
| 659 |
# First try to parse numbered format: "1. Topic: Description"
|
|
@@ -680,11 +704,11 @@ def parse_and_format_analysis(raw_analysis):
|
|
| 680 |
|
| 681 |
# Ensure exactly 3 priority improvements with distinct topics
|
| 682 |
if len(formatted_analysis['priority_improvements']) < 3:
|
| 683 |
-
# Define 3 distinct
|
| 684 |
common_improvements = [
|
| 685 |
-
"
|
| 686 |
-
"
|
| 687 |
-
"
|
| 688 |
]
|
| 689 |
|
| 690 |
# Get existing topics to avoid duplicates
|
|
@@ -784,27 +808,27 @@ def parse_and_format_analysis(raw_analysis):
|
|
| 784 |
percentage = formatted_analysis['classification']
|
| 785 |
if percentage >= 80:
|
| 786 |
formatted_analysis['priority_improvements'] = [
|
| 787 |
-
{'rank': 1, 'description': '
|
| 788 |
-
{'rank': 2, 'description': '
|
| 789 |
-
{'rank': 3, 'description': '
|
| 790 |
]
|
| 791 |
elif percentage >= 60:
|
| 792 |
formatted_analysis['priority_improvements'] = [
|
| 793 |
-
{'rank': 1, 'description': '
|
| 794 |
-
{'rank': 2, 'description': '
|
| 795 |
-
{'rank': 3, 'description': '
|
| 796 |
]
|
| 797 |
elif percentage >= 40:
|
| 798 |
formatted_analysis['priority_improvements'] = [
|
| 799 |
-
{'rank': 1, 'description': '
|
| 800 |
-
{'rank': 2, 'description': '
|
| 801 |
-
{'rank': 3, 'description': '
|
| 802 |
]
|
| 803 |
else: # Below 40%
|
| 804 |
formatted_analysis['priority_improvements'] = [
|
| 805 |
-
{'rank': 1, 'description': '
|
| 806 |
-
{'rank': 2, 'description': '
|
| 807 |
-
{'rank': 3, 'description': '
|
| 808 |
]
|
| 809 |
|
| 810 |
return formatted_analysis
|
|
@@ -823,7 +847,7 @@ def display_formatted_analysis(analysis_data):
|
|
| 823 |
# Display classification in black bolded header
|
| 824 |
st.markdown(f"""
|
| 825 |
<h2 style='color: black; font-weight: bold; text-align: center; margin-bottom: 20px;'>
|
| 826 |
-
|
| 827 |
</h2>
|
| 828 |
""", unsafe_allow_html=True)
|
| 829 |
|
|
@@ -904,38 +928,46 @@ def display_formatted_analysis(analysis_data):
|
|
| 904 |
st.markdown("---")
|
| 905 |
|
| 906 |
# 2. Strengths and Weaknesses Table
|
| 907 |
-
st.subheader("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 908 |
|
| 909 |
-
# Create two columns for the table with a visual divider
|
| 910 |
col_left, col_divider, col_right = st.columns([5, 1, 5])
|
| 911 |
|
| 912 |
with col_left:
|
| 913 |
st.markdown("""
|
| 914 |
<div style='background-color: #e8f5e8; padding: 15px; border-radius: 10px; height: 100%;'>
|
| 915 |
-
<h4 style='color: #2d5a2d; margin-top: 0;'
|
| 916 |
""", unsafe_allow_html=True)
|
| 917 |
-
for strength in analysis_data['strengths']:
|
| 918 |
-
st.markdown(f"
|
| 919 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 920 |
|
| 921 |
with col_divider:
|
| 922 |
st.markdown("""
|
| 923 |
-
<div style='width: 2px; background-color: #ddd; height: 200px; margin: 20px auto;'></div>
|
| 924 |
""", unsafe_allow_html=True)
|
| 925 |
|
| 926 |
with col_right:
|
| 927 |
st.markdown("""
|
| 928 |
<div style='background-color: #fff5e6; padding: 15px; border-radius: 10px; height: 100%;'>
|
| 929 |
-
<h4 style='color: #cc6600; margin-top: 0;'
|
| 930 |
""", unsafe_allow_html=True)
|
| 931 |
-
for weakness in analysis_data['weaknesses']:
|
| 932 |
-
st.markdown(f"
|
| 933 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 934 |
|
| 935 |
st.markdown("---")
|
| 936 |
|
| 937 |
-
# 3.
|
| 938 |
-
st.subheader("
|
| 939 |
|
| 940 |
for priority in sorted(analysis_data['priority_improvements'], key=lambda x: x['rank']):
|
| 941 |
rank = priority['rank']
|
|
|
|
| 531 |
**PERFORMANCE_CLASSIFICATION:** [XX%]
|
| 532 |
(XX = number from 10% to 100%)
|
| 533 |
|
| 534 |
+
**Strengths**
|
| 535 |
+
|
| 536 |
+
List exactly 3 strengths numbered 1-3. Each should:
|
| 537 |
+
- Include the topic name followed by a colon
|
| 538 |
+
- Be qualitative and positive
|
| 539 |
- Compare to professional benchmarks
|
| 540 |
+
- Highlight what's working well and the benefit
|
| 541 |
+
- Use supportive tone
|
| 542 |
|
| 543 |
Example:
|
| 544 |
+
1. Arm Extension:
|
| 545 |
+
Excellent extension at impact, this maintains a wide swing arc, just like elite players.
|
| 546 |
+
|
| 547 |
+
**Areas for Improvement**
|
| 548 |
|
| 549 |
+
List exactly 3 areas for improvement numbered 1-3. Each should:
|
| 550 |
+
- Include the topic name and your specific metric vs pro range
|
| 551 |
+
- Use format: "Topic — Your metric, pro range"
|
| 552 |
+
- Follow with explanation of impact and drill suggestion
|
| 553 |
+
- Focus on what needs work and why
|
|
|
|
| 554 |
|
| 555 |
Example:
|
| 556 |
+
1. Hip Rotation — Only 12.7°, pros range from 25–90°
|
| 557 |
+
Try drills that increase hip mobility during backswing. More rotation = more power and distance.
|
| 558 |
|
| 559 |
+
**Practice Tips**
|
| 560 |
+
|
| 561 |
+
List exactly 3 practice tips numbered 1-3. Each should:
|
| 562 |
+
- Include the topic name followed by "Drill:"
|
| 563 |
+
- Provide specific, actionable drill instructions
|
| 564 |
+
- Be easy to understand and practice
|
|
|
|
| 565 |
|
| 566 |
Example:
|
| 567 |
+
1. Ground Force Timing: Practice pushing against the ground earlier in the downswing to improve power transfer.
|
| 568 |
|
| 569 |
**SCORING GUIDELINES (Use to help decide % score)**
|
| 570 |
|
|
|
|
| 589 |
- **10–39%**: Novice
|
| 590 |
|
| 591 |
**STYLE & FORMATTING RULES:**
|
| 592 |
+
- Use these headers: PERFORMANCE_CLASSIFICATION, Strengths, Areas for Improvement, Practice Tips
|
| 593 |
+
- No emojis anywhere in the response
|
| 594 |
+
- No em dashes, use colons for topic separation
|
| 595 |
+
- Use numbered lists (1-3) for each section
|
| 596 |
- Tie all points to professional standards
|
| 597 |
- Use a positive, coaching tone throughout
|
| 598 |
- Avoid saying "perfect" — say "strong" or "meets standards"
|
| 599 |
- Focus on biomechanics, not timing (e.g. tempo, frame count)
|
| 600 |
+
|
| 601 |
+
**PRACTICE TIPS MUST USE ONLY THESE PROVEN SWING DRILLS:**
|
| 602 |
+
- Ground Force Timing (push against ground earlier in downswing)
|
| 603 |
+
- Halfway Back Drill (check club position when lead arm parallel to ground)
|
| 604 |
+
- Takeaway Drill (practice one-piece takeaway movement)
|
| 605 |
+
- Impact Drill (work on strong left side position at impact)
|
| 606 |
+
- Extension & Rotation Drill (perfect arm rotation through impact)
|
| 607 |
+
- Feet Together L-to-L Swings (continuous swings with feet together)
|
| 608 |
+
- Two-Tee Drill (swing through tee gate for driver path)
|
| 609 |
+
- Mirror Work (practice setup and positions)
|
| 610 |
+
- Ballistic Exercises (explosive movements for power development)
|
| 611 |
+
- Strength Training (for mobility and power foundation)
|
| 612 |
"""
|
| 613 |
|
| 614 |
return prompt
|
|
|
|
| 655 |
break
|
| 656 |
|
| 657 |
# Extract strengths using the new structured format
|
| 658 |
+
strengths_match = re.search(r'\*\*Strengths\*\*\s*(.*?)(?=\*\*Areas for Improvement\*\*|\*\*Practice Tips\*\*|$)', raw_analysis, re.IGNORECASE | re.DOTALL)
|
| 659 |
if strengths_match:
|
| 660 |
strengths_text = strengths_match.group(1)
|
| 661 |
+
# Extract numbered items (1. Topic: Description)
|
| 662 |
+
strength_items = re.findall(r'\d+\.\s*([^:]+):\s*([^\n\d]+)', strengths_text)
|
| 663 |
+
formatted_analysis['strengths'] = [f"{topic.strip()}: {desc.strip()}" for topic, desc in strength_items if topic.strip() and desc.strip()]
|
| 664 |
|
| 665 |
+
# Extract areas for improvement using the new structured format
|
| 666 |
+
weaknesses_match = re.search(r'\*\*Areas for Improvement\*\*\s*(.*?)(?=\*\*Practice Tips\*\*|$)', raw_analysis, re.IGNORECASE | re.DOTALL)
|
| 667 |
if weaknesses_match:
|
| 668 |
weaknesses_text = weaknesses_match.group(1)
|
| 669 |
+
# Extract numbered items (1. Topic — metric info\nExplanation)
|
| 670 |
+
weakness_items = re.findall(r'\d+\.\s*([^—\n]+)—([^\n]+)\n?([^\d]+)?', weaknesses_text)
|
| 671 |
+
formatted_analysis['weaknesses'] = []
|
| 672 |
+
for topic, metric, explanation in weakness_items:
|
| 673 |
+
if topic.strip() and metric.strip():
|
| 674 |
+
full_text = f"{topic.strip()} — {metric.strip()}"
|
| 675 |
+
if explanation and explanation.strip():
|
| 676 |
+
full_text += f"\n{explanation.strip()}"
|
| 677 |
+
formatted_analysis['weaknesses'].append(full_text)
|
| 678 |
|
| 679 |
+
# Extract practice tips using the new structured format
|
| 680 |
+
priority_match = re.search(r'\*\*Practice Tips\*\*\s*(.*?)$', raw_analysis, re.IGNORECASE | re.DOTALL)
|
| 681 |
if priority_match:
|
| 682 |
priority_text = priority_match.group(1)
|
| 683 |
# First try to parse numbered format: "1. Topic: Description"
|
|
|
|
| 704 |
|
| 705 |
# Ensure exactly 3 priority improvements with distinct topics
|
| 706 |
if len(formatted_analysis['priority_improvements']) < 3:
|
| 707 |
+
# Define 3 distinct practice tips using drills from golf articles CSV
|
| 708 |
common_improvements = [
|
| 709 |
+
"Ground Force Timing: Practice pushing against the ground earlier in the downswing to improve power transfer efficiency.",
|
| 710 |
+
"Halfway Back Drill: Work on swing path by checking club position when lead arm is parallel to ground.",
|
| 711 |
+
"Feet Together L-to-L Swings: Make continuous three-quarter swings with feet together to improve balance and timing."
|
| 712 |
]
|
| 713 |
|
| 714 |
# Get existing topics to avoid duplicates
|
|
|
|
| 808 |
percentage = formatted_analysis['classification']
|
| 809 |
if percentage >= 80:
|
| 810 |
formatted_analysis['priority_improvements'] = [
|
| 811 |
+
{'rank': 1, 'description': 'Ground Force Timing: Practice pushing against ground earlier in downswing for elite power transfer.'},
|
| 812 |
+
{'rank': 2, 'description': 'Extension & Rotation Drill: Perfect arm rotation through impact for maximum efficiency.'},
|
| 813 |
+
{'rank': 3, 'description': 'Ballistic Exercises: Use explosive movements for advanced power development and speed training.'}
|
| 814 |
]
|
| 815 |
elif percentage >= 60:
|
| 816 |
formatted_analysis['priority_improvements'] = [
|
| 817 |
+
{'rank': 1, 'description': 'Halfway Back Drill: Check club position when lead arm parallel to ground for proper swing path.'},
|
| 818 |
+
{'rank': 2, 'description': 'Two-Tee Drill: Practice swinging through tee gate to improve driver swing path and reduce slices.'},
|
| 819 |
+
{'rank': 3, 'description': 'Impact Drill: Work on strong left side position at impact for better ball striking.'}
|
| 820 |
]
|
| 821 |
elif percentage >= 40:
|
| 822 |
formatted_analysis['priority_improvements'] = [
|
| 823 |
+
{'rank': 1, 'description': 'Takeaway Drill: Practice one-piece takeaway movement for consistent swing foundation.'},
|
| 824 |
+
{'rank': 2, 'description': 'Feet Together L-to-L Swings: Continuous three-quarter swings with feet together for balance and timing.'},
|
| 825 |
+
{'rank': 3, 'description': 'Mirror Work: Practice setup positions and swing mechanics for consistency and muscle memory.'}
|
| 826 |
]
|
| 827 |
else: # Below 40%
|
| 828 |
formatted_analysis['priority_improvements'] = [
|
| 829 |
+
{'rank': 1, 'description': 'Mirror Work: Practice basic setup position focusing on proper spine angle and athletic stance.'},
|
| 830 |
+
{'rank': 2, 'description': 'Takeaway Drill: Work on simple one-piece takeaway movement for swing foundation.'},
|
| 831 |
+
{'rank': 3, 'description': 'Strength Training: Focus on mobility and foundational strength for better swing mechanics.'}
|
| 832 |
]
|
| 833 |
|
| 834 |
return formatted_analysis
|
|
|
|
| 847 |
# Display classification in black bolded header
|
| 848 |
st.markdown(f"""
|
| 849 |
<h2 style='color: black; font-weight: bold; text-align: center; margin-bottom: 20px;'>
|
| 850 |
+
Performance Score: {user_percentage}%
|
| 851 |
</h2>
|
| 852 |
""", unsafe_allow_html=True)
|
| 853 |
|
|
|
|
| 928 |
st.markdown("---")
|
| 929 |
|
| 930 |
# 2. Strengths and Weaknesses Table
|
| 931 |
+
st.subheader("Strengths & Areas for Improvement")
|
| 932 |
+
|
| 933 |
+
# Create responsive columns - divider hidden on mobile
|
| 934 |
+
st.markdown("""
|
| 935 |
+
<style>
|
| 936 |
+
@media (max-width: 768px) {
|
| 937 |
+
.desktop-divider { display: none !important; }
|
| 938 |
+
}
|
| 939 |
+
</style>
|
| 940 |
+
""", unsafe_allow_html=True)
|
| 941 |
|
|
|
|
| 942 |
col_left, col_divider, col_right = st.columns([5, 1, 5])
|
| 943 |
|
| 944 |
with col_left:
|
| 945 |
st.markdown("""
|
| 946 |
<div style='background-color: #e8f5e8; padding: 15px; border-radius: 10px; height: 100%;'>
|
| 947 |
+
<h4 style='color: #2d5a2d; margin-top: 0;'>Strengths</h4>
|
| 948 |
""", unsafe_allow_html=True)
|
| 949 |
+
for i, strength in enumerate(analysis_data['strengths'], 1):
|
| 950 |
+
st.markdown(f"{i}. {strength}")
|
| 951 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 952 |
|
| 953 |
with col_divider:
|
| 954 |
st.markdown("""
|
| 955 |
+
<div class='desktop-divider' style='width: 2px; background-color: #ddd; height: 200px; margin: 20px auto;'></div>
|
| 956 |
""", unsafe_allow_html=True)
|
| 957 |
|
| 958 |
with col_right:
|
| 959 |
st.markdown("""
|
| 960 |
<div style='background-color: #fff5e6; padding: 15px; border-radius: 10px; height: 100%;'>
|
| 961 |
+
<h4 style='color: #cc6600; margin-top: 0;'>Areas for Improvement</h4>
|
| 962 |
""", unsafe_allow_html=True)
|
| 963 |
+
for i, weakness in enumerate(analysis_data['weaknesses'], 1):
|
| 964 |
+
st.markdown(f"{i}. {weakness}")
|
| 965 |
st.markdown("</div>", unsafe_allow_html=True)
|
| 966 |
|
| 967 |
st.markdown("---")
|
| 968 |
|
| 969 |
+
# 3. Practice Tips
|
| 970 |
+
st.subheader("Practice Tips")
|
| 971 |
|
| 972 |
for priority in sorted(analysis_data['priority_improvements'], key=lambda x: x['rank']):
|
| 973 |
rank = priority['rank']
|
app/par-ity project horizontal logo.png
DELETED
Git LFS Details
|
app/streamlit_app.py
CHANGED
|
@@ -769,6 +769,9 @@ def main():
|
|
| 769 |
st.rerun()
|
| 770 |
|
| 771 |
if st.button("💬 Ask Questions", key="nav_chatbot", use_container_width=True):
|
|
|
|
|
|
|
|
|
|
| 772 |
st.session_state.current_step = 5
|
| 773 |
st.rerun()
|
| 774 |
|
|
@@ -781,15 +784,23 @@ def main():
|
|
| 781 |
st.rerun()
|
| 782 |
|
| 783 |
# Step-based content rendering
|
| 784 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 785 |
render_step_1()
|
| 786 |
-
elif
|
| 787 |
render_step_2()
|
| 788 |
-
elif
|
| 789 |
render_step_3()
|
| 790 |
-
elif
|
| 791 |
render_step_4()
|
| 792 |
-
elif
|
| 793 |
render_step_5()
|
| 794 |
|
| 795 |
st.markdown("---")
|
|
@@ -857,8 +868,6 @@ def render_step_2():
|
|
| 857 |
"""Step 2: Analyzing Video and Pose"""
|
| 858 |
st.markdown('<h2 style="color: #0B3B0B; font-family: Georgia, serif;">Step 2: Analyzing Video and Pose</h2>', unsafe_allow_html=True)
|
| 859 |
|
| 860 |
-
st.markdown("🔄 **Processing your swing video...**")
|
| 861 |
-
|
| 862 |
video_path = st.session_state.analysis_data.get('video_path')
|
| 863 |
|
| 864 |
if video_path and not st.session_state.video_analyzed:
|
|
@@ -934,7 +943,6 @@ def render_step_3():
|
|
| 934 |
display_video(video_path, width=300)
|
| 935 |
|
| 936 |
# Main action buttons - larger and more prominent
|
| 937 |
-
st.markdown("### Choose Your Path:")
|
| 938 |
|
| 939 |
col1, col2 = st.columns(2)
|
| 940 |
|
|
@@ -981,8 +989,6 @@ def render_step_4():
|
|
| 981 |
# Generate detailed analysis with recommendations
|
| 982 |
analysis = generate_swing_analysis(pose_data, swing_phases, trajectory_data)
|
| 983 |
|
| 984 |
-
st.markdown("## 🎯 Personalized Swing Improvements")
|
| 985 |
-
|
| 986 |
# Check available services
|
| 987 |
llm_services = check_llm_services()
|
| 988 |
any_service_available = llm_services['ollama']['available'] or llm_services['openai']['available']
|
|
@@ -1011,6 +1017,12 @@ def render_step_5():
|
|
| 1011 |
|
| 1012 |
st.markdown("💬 **Ready to answer your swing questions!**")
|
| 1013 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1014 |
if RAG_AVAILABLE:
|
| 1015 |
render_rag_interface()
|
| 1016 |
else:
|
|
|
|
| 769 |
st.rerun()
|
| 770 |
|
| 771 |
if st.button("💬 Ask Questions", key="nav_chatbot", use_container_width=True):
|
| 772 |
+
# Ensure we preserve the analysis state when navigating
|
| 773 |
+
if st.session_state.get('video_analyzed', False):
|
| 774 |
+
st.session_state.video_analyzed = True # Explicitly preserve this
|
| 775 |
st.session_state.current_step = 5
|
| 776 |
st.rerun()
|
| 777 |
|
|
|
|
| 784 |
st.rerun()
|
| 785 |
|
| 786 |
# Step-based content rendering
|
| 787 |
+
current_step = st.session_state.current_step
|
| 788 |
+
|
| 789 |
+
# Safeguard: If user tries to access step 4 or 5 without analysis,
|
| 790 |
+
# but they have analysis data, let them proceed
|
| 791 |
+
if current_step >= 4 and not st.session_state.get('video_analyzed', False):
|
| 792 |
+
if 'analysis_data' in st.session_state and st.session_state.analysis_data.get('video_path'):
|
| 793 |
+
st.session_state.video_analyzed = True # Restore the flag if we have data
|
| 794 |
+
|
| 795 |
+
if current_step == 1:
|
| 796 |
render_step_1()
|
| 797 |
+
elif current_step == 2:
|
| 798 |
render_step_2()
|
| 799 |
+
elif current_step == 3:
|
| 800 |
render_step_3()
|
| 801 |
+
elif current_step == 4:
|
| 802 |
render_step_4()
|
| 803 |
+
elif current_step == 5:
|
| 804 |
render_step_5()
|
| 805 |
|
| 806 |
st.markdown("---")
|
|
|
|
| 868 |
"""Step 2: Analyzing Video and Pose"""
|
| 869 |
st.markdown('<h2 style="color: #0B3B0B; font-family: Georgia, serif;">Step 2: Analyzing Video and Pose</h2>', unsafe_allow_html=True)
|
| 870 |
|
|
|
|
|
|
|
| 871 |
video_path = st.session_state.analysis_data.get('video_path')
|
| 872 |
|
| 873 |
if video_path and not st.session_state.video_analyzed:
|
|
|
|
| 943 |
display_video(video_path, width=300)
|
| 944 |
|
| 945 |
# Main action buttons - larger and more prominent
|
|
|
|
| 946 |
|
| 947 |
col1, col2 = st.columns(2)
|
| 948 |
|
|
|
|
| 989 |
# Generate detailed analysis with recommendations
|
| 990 |
analysis = generate_swing_analysis(pose_data, swing_phases, trajectory_data)
|
| 991 |
|
|
|
|
|
|
|
| 992 |
# Check available services
|
| 993 |
llm_services = check_llm_services()
|
| 994 |
any_service_available = llm_services['ollama']['available'] or llm_services['openai']['available']
|
|
|
|
| 1017 |
|
| 1018 |
st.markdown("💬 **Ready to answer your swing questions!**")
|
| 1019 |
|
| 1020 |
+
# Check if we have any analysis data, but don't block access if we don't
|
| 1021 |
+
has_analysis = st.session_state.get('video_analyzed', False) and 'analysis_data' in st.session_state
|
| 1022 |
+
|
| 1023 |
+
if not has_analysis:
|
| 1024 |
+
st.info("💡 **Tip**: For personalized answers about your swing, complete the video analysis first. You can still ask general golf questions!")
|
| 1025 |
+
|
| 1026 |
if RAG_AVAILABLE:
|
| 1027 |
render_rag_interface()
|
| 1028 |
else:
|