Rathapoom commited on
Commit
93b0fe2
·
verified ·
1 Parent(s): fbb20e0

Update appback.py3

Browse files
Files changed (1) hide show
  1. appback.py3 +1111 -499
appback.py3 CHANGED
@@ -549,8 +549,14 @@ def init_session_state():
549
  st.session_state.clear_input = False
550
  if 'text_input' not in st.session_state:
551
  st.session_state.text_input = ""
552
- if 'story_input_area' not in st.session_state:
553
- st.session_state.story_input_area = ""
 
 
 
 
 
 
554
 
555
  def init_theme_state():
556
  """Initialize theme-specific state variables"""
@@ -746,9 +752,9 @@ def generate_story_continuation(user_input: str, level: str) -> str:
746
  }
747
 
748
  try:
749
- # Get recent story context
750
  story_context = '\n'.join([
751
- entry['content'] for entry in st.session_state.story[-3:]
752
  ]) if st.session_state.story else "Story just started"
753
 
754
  # Create prompt
@@ -811,6 +817,7 @@ def generate_story_continuation(user_input: str, level: str) -> str:
811
  logging.error(f"Error generating story continuation: {str(e)}")
812
  return "I'm having trouble continuing the story. Please try again."
813
 
 
814
  def generate_dynamic_story_starter(theme_id: str, level: str) -> Dict[str, str]:
815
  """
816
  Dynamically generate a story starter based on theme and level.
@@ -848,47 +855,6 @@ def generate_dynamic_story_starter(theme_id: str, level: str) -> Dict[str, str]:
848
  'th': 'กาลครั้งหนึ่ง...'
849
  }
850
 
851
- def handle_application_error(error: Exception):
852
- """Handle application-wide errors"""
853
- logging.error(f"Application error: {str(error)}")
854
-
855
- error_message = f"""
856
- <div style="
857
- background-color: #ffebee;
858
- padding: 20px;
859
- border-radius: 10px;
860
- border-left: 4px solid #c62828;
861
- margin: 20px 0;
862
- ">
863
- <h3 style="color: #c62828; margin: 0 0 10px 0;">
864
- ⚠️ เกิดข้อผิดพลาดในระบบ
865
- </h3>
866
- <p style="color: #333; margin: 0;">
867
- กรุณาลองใหม่อีกครั้ง หรือติดต่อผู้ดูแลระบบ
868
- </p>
869
- <div style="
870
- margin-top: 10px;
871
- padding: 10px;
872
- background-color: #f5f5f5;
873
- border-radius: 5px;
874
- font-family: monospace;
875
- font-size: 0.9em;
876
- ">
877
- Error: {str(error)}
878
- </div>
879
- </div>
880
- """
881
-
882
- st.markdown(error_message, unsafe_allow_html=True)
883
-
884
- # Show technical details in expander
885
- with st.expander("📋 รายละเอียดข้อผิดพลาด (สำหรับผู้ดูแลระบบ)"):
886
- st.code(f"""
887
- Error Type: {type(error).__name__}
888
- Error Message: {str(error)}
889
- Timestamp: {datetime.now().isoformat()}
890
- """)
891
-
892
  def update_points(is_correct_first_try: bool):
893
  """อัพเดตคะแนนตามผลการเขียน"""
894
  try:
@@ -1001,7 +967,7 @@ def update_achievements():
1001
  return False
1002
 
1003
  def provide_feedback(text: str, level: str) -> Dict[str, str]:
1004
- """Provide feedback on the user's writing with appropriate level"""
1005
 
1006
  level_prompts = {
1007
  'Beginner': """
@@ -1011,6 +977,7 @@ def provide_feedback(text: str, level: str) -> Dict[str, str]:
1011
  - Basic vocabulary
1012
  - Capitalization and periods
1013
  Provide very simple, encouraging feedback in Thai.
 
1014
  """,
1015
  'Intermediate': """
1016
  Focus on:
@@ -1020,6 +987,7 @@ def provide_feedback(text: str, level: str) -> Dict[str, str]:
1020
  - Basic punctuation
1021
  - Simple conjunctions
1022
  Provide moderately detailed feedback in Thai.
 
1023
  """,
1024
  'Advanced': """
1025
  Focus on:
@@ -1029,6 +997,7 @@ def provide_feedback(text: str, level: str) -> Dict[str, str]:
1029
  - All punctuation
1030
  - Style and flow
1031
  Provide comprehensive feedback in Thai.
 
1032
  """
1033
  }
1034
 
@@ -1059,8 +1028,8 @@ def provide_feedback(text: str, level: str) -> Dict[str, str]:
1059
  "content": f"Review this sentence: {text}"
1060
  }
1061
  ],
1062
- max_tokens=200,
1063
- temperature=0.3
1064
  )
1065
 
1066
  # Parse response
@@ -1091,6 +1060,7 @@ def provide_feedback(text: str, level: str) -> Dict[str, str]:
1091
  "difficulty_score": 5
1092
  }
1093
 
 
1094
  def get_vocabulary_suggestions(context: str = "", level: str = DEFAULT_LEVEL) -> List[str]:
1095
  """Get contextual vocabulary suggestions with Thai translations"""
1096
  try:
@@ -1488,7 +1458,7 @@ def show_story():
1488
  for idx, entry in enumerate(st.session_state.story):
1489
  if entry['role'] == 'AI':
1490
  if entry.get('is_starter'):
1491
- # Story Starter
1492
  st.markdown(f"""
1493
  <div style="
1494
  background-color: #f0f7ff;
@@ -1509,8 +1479,39 @@ def show_story():
1509
  </div>
1510
  </div>
1511
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1512
  else:
1513
- # AI Response
 
 
 
 
 
 
1514
  st.markdown(f"""
1515
  <div style="
1516
  background-color: #f8f9fa;
@@ -1520,7 +1521,10 @@ def show_story():
1520
  border-left: 4px solid #4caf50;
1521
  ">
1522
  <div style="color: #2e7d32;">
1523
- 🤖 AI: {entry['content']}
 
 
 
1524
  </div>
1525
  </div>
1526
  """, unsafe_allow_html=True)
@@ -1531,6 +1535,12 @@ def show_story():
1531
  bg_color = "#e8f5e9" if entry.get('is_correct') else "#fff"
1532
  border_color = "#4caf50" if entry.get('is_correct') else "#1e88e5"
1533
 
 
 
 
 
 
 
1534
  st.markdown(f"""
1535
  <div style="
1536
  background-color: {bg_color};
@@ -1540,206 +1550,595 @@ def show_story():
1540
  border-left: 4px solid {border_color};
1541
  ">
1542
  <div style="color: #333;">
1543
- 👤 You: {status_icon} {entry['content']}
 
 
 
1544
  </div>
1545
  {f'<div style="font-size: 0.9em; color: #666; margin-top: 5px;">{entry.get("feedback", "")}</div>' if entry.get('feedback') else ''}
1546
  </div>
1547
  """, unsafe_allow_html=True)
1548
-
1549
- def show_settings_hint():
1550
- """Show floating hint for settings sidebar"""
1551
- st.markdown("""
1552
- <div style="
1553
- position: fixed;
1554
- left: 10px;
1555
- top: 50%;
1556
- transform: translateY(-50%);
1557
- background-color: #1e88e5;
1558
- color: white;
1559
- padding: 10px;
1560
- border-radius: 0 8px 8px 0;
1561
- box-shadow: 2px 2px 10px rgba(0,0,0,0.1);
1562
- cursor: pointer;
1563
- z-index: 1000;
1564
- animation: pulse 2s infinite;
1565
- ">
1566
- <div style="writing-mode: vertical-rl; text-orientation: mixed;">
1567
- ⚙️ ตั้งค่าระดับความยาก
1568
- </div>
1569
- </div>
1570
 
1571
- <style>
1572
- @keyframes pulse {
1573
- 0% { transform: translateY(-50%) scale(1); }
1574
- 50% { transform: translateY(-50%) scale(1.05); }
1575
- 100% { transform: translateY(-50%) scale(1); }
1576
- }
1577
- </style>
1578
- """, unsafe_allow_html=True)
1579
-
1580
- def show_feedback_section():
1581
- """Display writing feedback section"""
1582
- if not st.session_state.feedback:
1583
- return
1584
-
1585
- st.markdown("""
1586
- <div style="margin-bottom: 20px;">
1587
- <div class="thai-eng">
1588
- <div class="thai">📝 คำแนะนำจากครู</div>
1589
- <div class="eng">Writing Feedback</div>
1590
- </div>
1591
- </div>
1592
- """, unsafe_allow_html=True)
1593
-
1594
- feedback_data = st.session_state.feedback
1595
- if isinstance(feedback_data, dict) and feedback_data.get('has_errors'):
1596
- # Show error feedback
1597
- st.markdown(f"""
1598
- <div style="
1599
- background-color: #fff3e0;
1600
- padding: 20px;
1601
- border-radius: 10px;
1602
- border-left: 4px solid #ff9800;
1603
- margin: 10px 0;
1604
- ">
1605
- <p style="color: #e65100; margin-bottom: 15px;">
1606
- {feedback_data['feedback']}
1607
- </p>
1608
  <div style="
1609
- background-color: #fff;
1610
- padding: 15px;
1611
- border-radius: 8px;
1612
- margin-top: 10px;
 
 
1613
  ">
1614
- <p style="color: #666; margin-bottom: 5px;">
1615
- ประโยคที่ถูกต้อง:
1616
- </p>
1617
- <p style="
1618
- color: #2e7d32;
1619
- font-weight: 500;
1620
- font-size: 1.1em;
1621
- margin: 0;
1622
- ">
1623
- {feedback_data['corrected']}
1624
  </p>
1625
  </div>
1626
- </div>
1627
- """, unsafe_allow_html=True)
1628
-
1629
- # Correction button
1630
- if st.button(
1631
- "✍️ แก้ไขประโยคให้ถูกต้อง",
1632
- key="correct_button",
1633
- help="คลิกเพื่อแก้ไขประโยคให้ถูกต้องตามคำแนะนำ"
1634
- ):
1635
- last_user_entry_idx = next(
1636
- (i for i, entry in reversed(list(enumerate(st.session_state.story)))
1637
- if entry['role'] == 'You'),
1638
- None
1639
- )
1640
- if last_user_entry_idx is not None:
1641
- apply_correction(last_user_entry_idx, feedback_data['corrected'])
1642
- st.rerun()
1643
- else:
1644
- # Show success feedback
1645
  st.markdown(f"""
1646
  <div style="
1647
- background-color: #e8f5e9;
1648
- padding: 20px;
1649
  border-radius: 10px;
1650
- border-left: 4px solid #4caf50;
1651
  margin: 10px 0;
1652
  ">
1653
- <p style="color: #2e7d32; margin: 0;">
1654
- {feedback_data.get('feedback', '✨ เขียนได้ถูกต้องแล้วค่ะ!')}
1655
- </p>
 
 
 
 
 
 
1656
  </div>
1657
  """, unsafe_allow_html=True)
1658
 
1659
- def show_writing_tools():
1660
- """Display writing tools section"""
1661
- with st.expander("✨ เครื่องมือช่วยเขียน | Writing Tools"):
1662
- col1, col2 = st.columns(2)
1663
 
1664
- with col1:
1665
- if st.button("🎯 ขอคำใบ้", use_container_width=True):
1666
- with st.spinner("กำลังสร้างคำใบ้..."):
1667
- prompt = get_creative_prompt()
1668
- st.markdown(f"""
1669
- <div style="
1670
- background-color: #f3e5f5;
1671
- padding: 15px;
1672
- border-radius: 8px;
1673
- margin-top: 10px;
1674
- ">
1675
- <div style="color: #6a1b9a;">💭 {prompt['thai']}</div>
1676
- <div style="color: #666; font-style: italic;">
1677
- 💭 {prompt['eng']}
1678
- </div>
1679
- </div>
1680
- """, unsafe_allow_html=True)
1681
 
1682
- with col2:
1683
- if st.button("📚 คำศัพท์แนะนำ", use_container_width=True):
1684
- with st.spinner("กำลังค้นหาคำศัพท์..."):
1685
- vocab_suggestions = get_vocabulary_suggestions()
1686
- st.markdown("#### 📚 คำศัพท์น่ารู้")
1687
- for word in vocab_suggestions:
1688
- st.markdown(f"""
1689
- <div style="
1690
- background-color: #f5f5f5;
1691
- padding: 10px;
1692
- border-radius: 5px;
1693
- margin: 5px 0;
1694
- ">
1695
- • {word}
1696
- </div>
1697
- """, unsafe_allow_html=True)
1698
 
1699
- def show_achievements():
1700
- """Display achievements and stats"""
1701
- with st.container():
1702
- # Points and Streak
1703
- st.markdown(f"""
1704
- <div style="
1705
- background-color: #e3f2fd;
1706
- padding: 20px;
1707
- border-radius: 10px;
1708
- margin-bottom: 20px;
1709
- text-align: center;
1710
- ">
1711
- <h3 style="color: #1e88e5; margin: 0;">
1712
- 🌟 คะแนนรวม: {st.session_state.points['total']}
1713
- </h3>
1714
- <p style="margin: 5px 0;">
1715
- 🔥 Streak ปัจจุบัน: {st.session_state.points['streak']} ประโยค
1716
- </p>
1717
- <p style="margin: 5px 0;">
1718
- ⭐ Streak สูงสุด: {st.session_state.points['max_streak']} ประโยค
1719
- </p>
1720
- </div>
1721
- """, unsafe_allow_html=True)
1722
 
1723
- # Writing Stats
1724
- st.markdown("""
1725
- <div style="margin-bottom: 15px;">
1726
- <h3>📊 สถิติการเขียน</h3>
1727
- </div>
1728
- """, unsafe_allow_html=True)
1729
 
1730
- col1, col2 = st.columns(2)
1731
- with col1:
1732
- st.metric(
1733
- "ประโยคที่เขียนทั้งหมด",
1734
- st.session_state.stats['total_sentences'],
1735
- help="จำนวนประโยคทั้งหมดที่คุณได้เขียน"
1736
- )
1737
- st.metric(
1738
- "ถูกต้องตั้งแต่แรก",
1739
- st.session_state.stats['correct_first_try'],
1740
- help="จำนวนประโยคที่ถูกต้องโดยไม่ต้องแก้ไข"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1741
  )
1742
- with col2:
 
 
 
 
 
1743
  st.metric(
1744
  "ความแม่นยำ",
1745
  f"{st.session_state.stats['accuracy_rate']:.1f}%",
@@ -1884,35 +2283,46 @@ def show_story_input():
1884
  <div class="eng">Your Turn</div>
1885
  </div>
1886
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
1887
 
1888
- # ใช้ callback function เพื่อเคลียร์ input
1889
- def clear_text():
1890
- st.session_state.story_input_area = ""
 
1891
 
1892
- # Input area with default empty value and callback
1893
  text_input = st.text_area(
1894
  "เขียนต่อจากเรื่องราว | Continue the story:",
1895
- value="",
1896
  height=100,
1897
  key="story_input_area",
1898
  help="พิมพ์ประโยคภาษาอังกฤษเพื่อต่อเรื่อง",
1899
  label_visibility="collapsed"
1900
  )
1901
-
 
 
 
1902
  # Submit button with character count
1903
  col1, col2 = st.columns([3, 1])
1904
  with col1:
1905
- if st.button("📝 ส่งคำตอบ | Submit", use_container_width=True, on_click=clear_text):
1906
  if not text_input.strip():
1907
  st.warning("กรุณาเขียนข้อความก่อนส่ง")
1908
  return
1909
-
1910
  try:
1911
  with st.spinner("กำลังวิเคราะห์ประโยค..."):
1912
- if st.session_state.get('ending_mode'):
1913
- handle_ending_mode(text_input.strip())
1914
- else:
1915
- handle_story_submission(text_input.strip())
1916
  except Exception as e:
1917
  logging.error(f"Error submitting story: {str(e)}")
1918
  st.error("เกิดข้อผิดพลาดในการส่งคำตอบ กรุณาลองใหม่อีกครั้ง")
@@ -1931,6 +2341,12 @@ def handle_story_submission(text: str):
1931
  st.error("กรุณาเลือกธีมเรื่องราวก่อนเริ่มเขียน")
1932
  return
1933
 
 
 
 
 
 
 
1934
  try:
1935
  # Get feedback
1936
  feedback_data = provide_feedback(text, st.session_state.level)
@@ -1956,18 +2372,26 @@ def handle_story_submission(text: str):
1956
 
1957
  # Generate AI continuation
1958
  try:
1959
- ai_response = generate_story_continuation(text, st.session_state.level)
 
 
 
 
 
 
 
 
1960
  if ai_response and ai_response.strip():
1961
  st.session_state.story.append({
1962
  "role": "AI",
1963
  "content": ai_response,
1964
  "timestamp": datetime.now().isoformat()
1965
  })
 
1966
  else:
1967
- fallback_response = generate_fallback_response(
1968
- st.session_state.current_theme,
1969
- st.session_state.level
1970
- )
1971
  st.session_state.story.append({
1972
  "role": "AI",
1973
  "content": fallback_response,
@@ -1975,239 +2399,52 @@ def handle_story_submission(text: str):
1975
  })
1976
  except Exception as e:
1977
  logging.error(f"Error generating AI continuation: {str(e)}")
1978
- fallback_response = generate_fallback_response(
1979
- st.session_state.current_theme,
1980
- st.session_state.level
1981
- )
1982
  st.session_state.story.append({
1983
  "role": "AI",
1984
  "content": fallback_response,
1985
  "timestamp": datetime.now().isoformat()
1986
  })
1987
 
1988
- # Clear input after successful submission
1989
- st.session_state.story_input_area = ""
1990
-
1991
  # Update session stats
1992
  update_session_stats()
1993
 
1994
- # Rerun to update UI
1995
- st.rerun()
1996
-
1997
- except Exception as e:
1998
- logging.error(f"Error in story submission: {str(e)}")
1999
- raise
2000
-
2001
- def generate_fallback_response(theme_id: str, level: str) -> str:
2002
- """Generate a simple fallback response when AI continuation fails"""
2003
- try:
2004
- theme = story_themes.get(theme_id, {})
2005
- if theme:
2006
- # ใช้คำศัพท์จาก theme และ level ที่เลือก
2007
- vocab = theme.get('vocabulary', {}).get(level, [])
2008
- if vocab:
2009
- word = random.choice(vocab)
2010
-
2011
- # สร้างประโยคตาม level
2012
- if level == 'Beginner':
2013
- return f"The story continues with {word}..."
2014
- elif level == 'Intermediate':
2015
- return f"Something interesting happened with the {word}."
2016
- else: # Advanced
2017
- return f"The mystery deepens as we discover more about the {word}."
2018
-
2019
- # Default fallback if no theme-specific response can be generated
2020
- return "The story continues..."
2021
-
2022
- except Exception as e:
2023
- logging.error(f"Error generating fallback response: {str(e)}")
2024
- return "What happens next?"
2025
-
2026
- def show_story_progress():
2027
- """Display story progress metrics"""
2028
- if st.session_state.story:
2029
- total_sentences = len(st.session_state.story)
2030
- st.markdown(f"""
2031
- <div style="
2032
- background-color: #f0f7ff;
2033
- padding: 15px;
2034
- border-radius: 10px;
2035
- margin: 10px 0;
2036
- ">
2037
- <div style="margin-bottom: 10px;">
2038
- 📊 ความยาวเรื่อง: {total_sentences} ประโยค
2039
- </div>
2040
- <div class="progress-bar">
2041
- <div class="progress-bar-fill" style="width: {min(total_sentences * 10, 100)}%;"></div>
2042
- </div>
2043
- <div style="font-size: 0.9em; color: #666; margin-top: 5px;">
2044
- เรื่องควรยาว 10-20 ประโยค เพื่อความสมบูรณ์
2045
- </div>
2046
- </div>
2047
- """, unsafe_allow_html=True)
2048
-
2049
- def show_story_ending_options():
2050
- """Display story ending options and guidance"""
2051
- if len(st.session_state.story) >= 10: # แสดงตัวเลือกเมื่อเรื่องยาวพอ
2052
- st.markdown("### 🎭 ต้องการจบเรื่องหรือไม่?")
2053
-
2054
- # แสดงตัวเลือกวิธีจบเรื่อง
2055
- ending_type = st.radio(
2056
- "เลือกวิธีจบเรื่อง:",
2057
- options=[
2058
- "Happy Ending - จบแบบมีความสุข",
2059
- "Mysterious Ending - จบแบบทิ้งท้ายให้คิดต่อ",
2060
- "Lesson Learned - จบแบบได้ข้อคิด",
2061
- "Surprise Ending - จบแบบพลิกความคาดหมาย"
2062
- ],
2063
- index=0,
2064
- help="เลือกรูปแบบการจบเรื่องที่คุณต้องการ"
2065
- )
2066
-
2067
- if st.button("🎬 เริ่มจบเรื่อง", use_container_width=True):
2068
- st.session_state.ending_mode = True
2069
- st.session_state.ending_type = ending_type
2070
- st.session_state.sentences_to_end = 5
2071
- st.rerun()
2072
 
2073
- def handle_ending_mode(text: str):
2074
- """Handle story submission during ending mode"""
2075
- remaining = st.session_state.sentences_to_end
2076
-
2077
- # แสดงการแจ้งเตือนจำนวนประโยคที่เหลือ
2078
- st.info(f"🎯 เหลืออีก {remaining} ประโยคในการจบเรื่อง")
2079
-
2080
- # สร้าง prompt พิเศษสำหรับ AI ในโหมดจบเรื่อง
2081
- ending_prompts = {
2082
- "Happy Ending": "Work towards a positive and uplifting conclusion",
2083
- "Mysterious Ending": "Create an intriguing open-ended conclusion",
2084
- "Lesson Learned": "Incorporate a meaningful life lesson",
2085
- "Surprise Ending": "Build up to an unexpected twist"
2086
- }
2087
-
2088
- # ปรับ AI response ให้พยายามจบเรื่อง
2089
- modified_continuation = generate_ending_continuation(
2090
- text,
2091
- ending_type=st.session_state.ending_type,
2092
- remaining_sentences=remaining
2093
- )
2094
-
2095
- # อัพเดทจำนวนประโยคที่เหลือ
2096
- st.session_state.sentences_to_end -= 1
2097
-
2098
- # ตรวจสอบว่าถึงจบเรื่องหรือยัง
2099
- if st.session_state.sentences_to_end <= 0:
2100
- complete_story()
2101
 
2102
- def generate_ending_continuation(text: str, ending_type: str, remaining_sentences: int) -> str:
2103
- """Generate AI continuation focusing on story conclusion"""
2104
  try:
2105
- ending_prompts = {
2106
- "Happy Ending": """
2107
- Role: Story concluder aiming for a happy ending
2108
- Goal: Create a satisfying, positive conclusion
2109
- Rules:
2110
- - Build towards joy, success, or resolution
2111
- - Use uplifting and positive language
2112
- - Connect to previous story elements
2113
- """,
2114
- "Mysterious Ending": """
2115
- Role: Mystery writer creating intrigue
2116
- Goal: Leave readers thinking and wondering
2117
- Rules:
2118
- - Add subtle hints and clues
2119
- - Create atmospheric descriptions
2120
- - Leave some questions unanswered
2121
- """,
2122
- "Lesson Learned": """
2123
- Role: Moral story concluder
2124
- Goal: Incorporate meaningful life lessons
2125
- Rules:
2126
- - Connect actions to consequences
2127
- - Show character growth
2128
- - Express the moral naturally
2129
- """,
2130
- "Surprise Ending": """
2131
- Role: Plot twist creator
2132
- Goal: Deliver unexpected but satisfying conclusion
2133
- Rules:
2134
- - Plant subtle hints earlier
2135
- - Subvert expectations logically
2136
- - Maintain story coherence
2137
- """
2138
- }
2139
-
2140
- response = client.chat.completions.create(
2141
- model="gpt-4",
2142
- messages=[
2143
- {
2144
- "role": "system",
2145
- "content": f"""
2146
- {ending_prompts[ending_type]}
2147
-
2148
- Additional Rules:
2149
- - You have {remaining_sentences} sentences to conclude
2150
- - Each response should be max 2 sentences
2151
- - Build towards the finale naturally
2152
- - Connect to previous story elements
2153
- """
2154
- },
2155
- {
2156
- "role": "user",
2157
- "content": f"Continue and work towards ending this story: {text}"
2158
- }
2159
- ],
2160
- max_tokens=100,
2161
- temperature=0.7
2162
- )
2163
 
2164
- return response.choices[0].message.content.strip()
 
2165
 
2166
  except Exception as e:
2167
- logging.error(f"Error generating ending: {str(e)}")
2168
- return "The story moved towards its conclusion..."
2169
-
2170
- def complete_story():
2171
- """Handle story completion and celebration"""
2172
- st.balloons() # แสดงเอฟเฟคฉลอง
2173
-
2174
- # สร้าง Story Summary
2175
- story_summary = generate_story_summary(st.session_state.story)
2176
-
2177
- # แสดงหน้าจบเรื่อง
2178
- st.markdown(f"""
2179
- <div style="
2180
- background-color: #e8f5e9;
2181
- padding: 20px;
2182
- border-radius: 10px;
2183
- text-align: center;
2184
- margin: 20px 0;
2185
- ">
2186
- <h2>🎉 ยินดีด้วย! คุณเขียนเรื่องราวจบสมบูรณ์แล้ว</h2>
2187
-
2188
- <div style="margin: 20px 0;">
2189
- <h3>📝 สรุปเรื่องราว</h3>
2190
- <p>{story_summary}</p>
2191
- </div>
2192
-
2193
- <div style="margin: 20px 0;">
2194
- <h3>🏆 ความสำเร็จ</h3>
2195
- <p>จำนวนประโยค: {len(st.session_state.story)}</p>
2196
- <p>คำศัพท์ที่ใช้: {len(st.session_state.stats['vocabulary_used'])}</p>
2197
- <p>ความแม่นยำ: {st.session_state.stats['accuracy_rate']:.1f}%</p>
2198
- </div>
2199
- </div>
2200
- """, unsafe_allow_html=True)
2201
-
2202
- # แสดงตัวเลือกหลังจบเรื่อง
2203
- col1, col2 = st.columns(2)
2204
- with col1:
2205
- if st.button("💾 บันทึกเรื่องราว", use_container_width=True):
2206
- save_completed_story()
2207
- with col2:
2208
- if st.button("🔄 เริ่มเรื่องใหม่", use_container_width=True):
2209
- reset_story()
2210
- st.rerun()
2211
 
2212
  def show_main_interface():
2213
  """Display main story interface"""
@@ -2223,9 +2460,14 @@ def show_main_interface():
2223
  """, unsafe_allow_html=True)
2224
 
2225
  show_story()
2226
-
2227
- if st.session_state.story:
 
2228
  show_story_input()
 
 
 
 
2229
 
2230
  with col2:
2231
  # Feedback section
@@ -2241,6 +2483,312 @@ def show_main_interface():
2241
  # Save options
2242
  show_save_options()
2243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2244
  # === 6. MAIN APPLICATION LOGIC ===
2245
  def main():
2246
  try:
@@ -2252,9 +2800,11 @@ def main():
2252
  if 'ending_mode' not in st.session_state:
2253
  st.session_state.ending_mode = False
2254
  if 'sentences_to_end' not in st.session_state:
2255
- st.session_state.sentences_to_end = 5
2256
  if 'ending_type' not in st.session_state:
2257
  st.session_state.ending_type = None
 
 
2258
 
2259
  # Add watermark
2260
  st.markdown("""
@@ -2284,36 +2834,104 @@ def main():
2284
  else:
2285
  # Show story progress if story exists
2286
  if st.session_state.story:
2287
- show_story_progress()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2288
 
2289
- # Show ending options if story is long enough and not in ending mode
2290
- if (len(st.session_state.story) >= 10 and
2291
- not st.session_state.ending_mode):
2292
- show_story_ending_options()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2293
 
2294
- # Show ending mode warning if active
2295
- if st.session_state.ending_mode:
2296
- st.warning(f"""
2297
- 🎭 กำลังอยู่ในโหมดจบเรื่อง - เหลือ {st.session_state.sentences_to_end} ประโยค
2298
- \nรูปแบบการจบ: {st.session_state.ending_type}
2299
- """)
2300
-
2301
- # Show main interface
 
 
 
 
 
 
 
 
 
 
 
2302
  show_main_interface()
2303
 
2304
- # Check if story is complete
2305
- if (st.session_state.ending_mode and
2306
- st.session_state.sentences_to_end <= 0):
2307
- complete_story()
2308
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2309
  # Handle reset if needed
2310
- if st.session_state.should_reset:
2311
  reset_story()
2312
- # Reset ending mode states
2313
  st.session_state.ending_mode = False
2314
- st.session_state.sentences_to_end = 5
2315
  st.session_state.ending_type = None
2316
-
 
 
2317
  # Auto-save progress periodically
2318
  if st.session_state.story:
2319
  auto_save_progress()
@@ -2321,6 +2939,7 @@ def main():
2321
  except Exception as e:
2322
  handle_application_error(e)
2323
 
 
2324
  def check_session_status():
2325
  """Check and maintain session status"""
2326
  try:
@@ -2328,23 +2947,16 @@ def check_session_status():
2328
  if 'session_start' not in st.session_state:
2329
  st.session_state.session_start = datetime.now()
2330
 
2331
- # Calculate session duration
2332
- current_time = datetime.now()
2333
- session_duration = (current_time - st.session_state.session_start).total_seconds()
2334
-
2335
  # Check for session timeout (2 hours)
 
2336
  if session_duration > 7200: # 2 hours
2337
- st.warning("⚠️ เซสชันหมดอายุ กรุณาบันทึกความก้าวหน้าและรีเฟรชหน้าเว็บ")
2338
 
2339
  # Check for inactivity (30 minutes)
2340
- if 'last_interaction' not in st.session_state:
2341
- st.session_state.last_interaction = datetime.now().isoformat()
2342
-
2343
  last_interaction = datetime.fromisoformat(st.session_state.last_interaction)
2344
- inactivity_duration = (current_time - last_interaction).total_seconds()
2345
-
2346
  if inactivity_duration > 1800: # 30 minutes
2347
- st.info("💤 ไม่มีกิจกรรมเป็นเวลานาน กรุณาบันทึกความก้าวหน้าเพื่อความปลอดภัย")
2348
 
2349
  # Update stats if story exists
2350
  if st.session_state.story:
 
549
  st.session_state.clear_input = False
550
  if 'text_input' not in st.session_state:
551
  st.session_state.text_input = ""
552
+ if 'ending_mode' not in st.session_state:
553
+ st.session_state.ending_mode = False
554
+ if 'sentences_to_end' not in st.session_state:
555
+ st.session_state.sentences_to_end = 0
556
+ if 'ending_type' not in st.session_state:
557
+ st.session_state.ending_type = None
558
+ if 'story_completed' not in st.session_state:
559
+ st.session_state.story_completed = False
560
 
561
  def init_theme_state():
562
  """Initialize theme-specific state variables"""
 
752
  }
753
 
754
  try:
755
+ # Get recent story context (last 5 entries for more coherence)
756
  story_context = '\n'.join([
757
+ entry['content'] for entry in st.session_state.story[-5:]
758
  ]) if st.session_state.story else "Story just started"
759
 
760
  # Create prompt
 
817
  logging.error(f"Error generating story continuation: {str(e)}")
818
  return "I'm having trouble continuing the story. Please try again."
819
 
820
+
821
  def generate_dynamic_story_starter(theme_id: str, level: str) -> Dict[str, str]:
822
  """
823
  Dynamically generate a story starter based on theme and level.
 
855
  'th': 'กาลครั้งหนึ่ง...'
856
  }
857
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
858
  def update_points(is_correct_first_try: bool):
859
  """อัพเดตคะแนนตามผลการเขียน"""
860
  try:
 
967
  return False
968
 
969
  def provide_feedback(text: str, level: str) -> Dict[str, str]:
970
+ """Provide more detailed feedback on the user's writing with appropriate level"""
971
 
972
  level_prompts = {
973
  'Beginner': """
 
977
  - Basic vocabulary
978
  - Capitalization and periods
979
  Provide very simple, encouraging feedback in Thai.
980
+ Explain any mistakes and how to correct them.
981
  """,
982
  'Intermediate': """
983
  Focus on:
 
987
  - Basic punctuation
988
  - Simple conjunctions
989
  Provide moderately detailed feedback in Thai.
990
+ Explain any mistakes and suggest improvements.
991
  """,
992
  'Advanced': """
993
  Focus on:
 
997
  - All punctuation
998
  - Style and flow
999
  Provide comprehensive feedback in Thai.
1000
+ Explain any mistakes and provide examples for improvement.
1001
  """
1002
  }
1003
 
 
1028
  "content": f"Review this sentence: {text}"
1029
  }
1030
  ],
1031
+ max_tokens=300, # Increased from 200
1032
+ temperature=0.5 # Adjusted for balanced variety
1033
  )
1034
 
1035
  # Parse response
 
1060
  "difficulty_score": 5
1061
  }
1062
 
1063
+
1064
  def get_vocabulary_suggestions(context: str = "", level: str = DEFAULT_LEVEL) -> List[str]:
1065
  """Get contextual vocabulary suggestions with Thai translations"""
1066
  try:
 
1458
  for idx, entry in enumerate(st.session_state.story):
1459
  if entry['role'] == 'AI':
1460
  if entry.get('is_starter'):
1461
+ # Story Starter (คงเดิม)
1462
  st.markdown(f"""
1463
  <div style="
1464
  background-color: #f0f7ff;
 
1479
  </div>
1480
  </div>
1481
  """, unsafe_allow_html=True)
1482
+ elif entry.get('is_final'): # เพิ่มเงื่อนไขสำหรับประโยคจบ
1483
+ # Final Ending Display
1484
+ st.markdown(f"""
1485
+ <div style="
1486
+ background-color: #e8f5e9;
1487
+ padding: 25px;
1488
+ border-radius: 10px;
1489
+ margin: 15px 0;
1490
+ border: 2px solid #4caf50;
1491
+ text-align: center;
1492
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
1493
+ ">
1494
+ <div style="font-size: 1.3em; color: #2e7d32; margin-bottom: 15px;">
1495
+ 🎭 จบเรื่อง | The End
1496
+ </div>
1497
+ <div style="color: #333; font-size: 1.1em; font-style: italic;">
1498
+ {entry['content']}
1499
+ </div>
1500
+ <div style="margin-top: 15px; padding-top: 15px; border-top: 1px solid #a5d6a7;">
1501
+ <span style="color: #666;">
1502
+ ✨ รูปแบบการจบ: {st.session_state.ending_type}
1503
+ </span>
1504
+ </div>
1505
+ </div>
1506
+ """, unsafe_allow_html=True)
1507
  else:
1508
+ # Regular AI Response with ending mode indicator
1509
+ ending_info = ""
1510
+ # แสดงจำนวนประโยคที่เหลือเฉพาะเมื่ออยู่ในโหมดจบเรื่อง
1511
+ if (st.session_state.get('ending_mode') and
1512
+ 'remaining_sentences' in entry):
1513
+ ending_info = f"🎭 เหลืออีก {entry['remaining_sentences']} ประโยค"
1514
+
1515
  st.markdown(f"""
1516
  <div style="
1517
  background-color: #f8f9fa;
 
1521
  border-left: 4px solid #4caf50;
1522
  ">
1523
  <div style="color: #2e7d32;">
1524
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1525
+ <span>🤖 AI: {entry['content']}</span>
1526
+ <span style="color: #f57c00; font-size: 0.9em;">{ending_info}</span>
1527
+ </div>
1528
  </div>
1529
  </div>
1530
  """, unsafe_allow_html=True)
 
1535
  bg_color = "#e8f5e9" if entry.get('is_correct') else "#fff"
1536
  border_color = "#4caf50" if entry.get('is_correct') else "#1e88e5"
1537
 
1538
+ ending_info = ""
1539
+ # แสดงจำนวนประโยคที่เหลือเฉพาะเมื่ออยู่ในโหมดจบเรื่อง
1540
+ if (st.session_state.get('ending_mode') and
1541
+ 'remaining_sentences' in entry):
1542
+ ending_info = f"🎭 เหลืออีก {entry['remaining_sentences']} ประโยค"
1543
+
1544
  st.markdown(f"""
1545
  <div style="
1546
  background-color: {bg_color};
 
1550
  border-left: 4px solid {border_color};
1551
  ">
1552
  <div style="color: #333;">
1553
+ <div style="display: flex; justify-content: space-between; align-items: center;">
1554
+ <span>👤 You: {status_icon} {entry['content']}</span>
1555
+ <span style="color: #f57c00; font-size: 0.9em;">{ending_info}</span>
1556
+ </div>
1557
  </div>
1558
  {f'<div style="font-size: 0.9em; color: #666; margin-top: 5px;">{entry.get("feedback", "")}</div>' if entry.get('feedback') else ''}
1559
  </div>
1560
  """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1561
 
1562
+ # Show completion message if story is done
1563
+ if st.session_state.get('story_completed'):
1564
+ st.markdown("""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1565
  <div style="
1566
+ background-color: #f3e5f5;
1567
+ padding: 20px;
1568
+ border-radius: 10px;
1569
+ margin: 20px 0;
1570
+ text-align: center;
1571
+ border: 2px dashed #9c27b0;
1572
  ">
1573
+ <h2 style="color: #9c27b0; margin-bottom: 10px;">
1574
+ 🎉 ยินดีด้วย! คุณเขียนเรื่องราวจบสมบูรณ์แล้ว
1575
+ </h2>
1576
+ <p style="color: #666;">
1577
+ คุณสามารถบันทึกเรื่องราวหรือเริ่มเรื่องใหม่ได้
 
 
 
 
 
1578
  </p>
1579
  </div>
1580
+ """, unsafe_allow_html=True)
1581
+
1582
+ def show_story_progress():
1583
+ """Display story progress metrics"""
1584
+ if st.session_state.story:
1585
+ total_sentences = len(st.session_state.story)
 
 
 
 
 
 
 
 
 
 
 
 
 
1586
  st.markdown(f"""
1587
  <div style="
1588
+ background-color: #f0f7ff;
1589
+ padding: 15px;
1590
  border-radius: 10px;
 
1591
  margin: 10px 0;
1592
  ">
1593
+ <div style="margin-bottom: 10px;">
1594
+ 📊 ความยาวเรื่อง: {total_sentences} ประโยค
1595
+ </div>
1596
+ <div class="progress-bar">
1597
+ <div class="progress-bar-fill" style="width: {min(total_sentences * 10, 100)}%;"></div>
1598
+ </div>
1599
+ <div style="font-size: 0.9em; color: #666; margin-top: 5px;">
1600
+ เรื่องควรยาว 10-20 ประโยค เพื่อความสมบูรณ์
1601
+ </div>
1602
  </div>
1603
  """, unsafe_allow_html=True)
1604
 
1605
+ def show_story_ending_options():
1606
+ """Display story ending options and guidance"""
1607
+ if len(st.session_state.story) >= 10: # Show options when story is long enough
1608
+ st.markdown("### 🎭 ต้องการจบเรื่องหรือไม่?")
1609
 
1610
+ # Display ending type options
1611
+ ending_type = st.radio(
1612
+ "เลือกวิธีจบเรื่อง:",
1613
+ options=[
1614
+ "Happy Ending - จบแบบมีความสุข",
1615
+ "Mysterious Ending - จบแบบทิ้งท้ายให้คิดต่อ",
1616
+ "Lesson Learned - จบแบบได้ข้อคิด",
1617
+ "Surprise Ending - จบแบบพลิกความคาดหมาย"
1618
+ ],
1619
+ index=0,
1620
+ help="เลือกรูปแบบการจบเรื่องที่คุณต้องการ"
1621
+ )
 
 
 
 
 
1622
 
1623
+ if st.button("🎬 เริ่มจบเรื่อง", use_container_width=True):
1624
+ st.session_state.ending_mode = True
1625
+ st.session_state.ending_type = ending_type
1626
+ st.session_state.sentences_to_end = 5
1627
+ st.rerun() # Using st.rerun() instead of st.experimental_rerun()
 
 
 
 
 
 
 
 
 
 
 
1628
 
1629
+ def handle_ending_mode(text: str):
1630
+ """Handle story submission during ending mode"""
1631
+ try:
1632
+ # Get current remaining sentences
1633
+ remaining = st.session_state.sentences_to_end
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1634
 
1635
+ # Get feedback
1636
+ feedback_data = provide_feedback(text, st.session_state.level)
1637
+ st.session_state.feedback = feedback_data
1638
+ is_correct = not feedback_data.get('has_errors', False)
 
 
1639
 
1640
+ # Add user's sentence
1641
+ st.session_state.story.append({
1642
+ "role": "You",
1643
+ "content": text,
1644
+ "is_corrected": False,
1645
+ "is_correct": is_correct,
1646
+ "timestamp": datetime.now().isoformat(),
1647
+ "remaining_sentences": remaining
1648
+ })
1649
+
1650
+ # Update vocabulary
1651
+ words = set(text.lower().split())
1652
+ st.session_state.stats['vocabulary_used'].update(words)
1653
+
1654
+ # Update points and achievements
1655
+ update_points(is_correct)
1656
+ update_achievements()
1657
+
1658
+ # Decrease remaining count for user's sentence
1659
+ st.session_state.sentences_to_end -= 1
1660
+ remaining = st.session_state.sentences_to_end
1661
+
1662
+ if remaining > 0:
1663
+ # Use corrected text if there are errors
1664
+ text_for_continuation = feedback_data['corrected'] if feedback_data.get('has_errors') else text
1665
+
1666
+ # Generate AI's ending continuation
1667
+ ai_response = generate_ending_continuation(
1668
+ text_for_continuation,
1669
+ ending_type=st.session_state.ending_type,
1670
+ remaining_sentences=remaining,
1671
+ level=st.session_state.level
1672
+ )
1673
+
1674
+ # Add AI response
1675
+ st.session_state.story.append({
1676
+ "role": "AI",
1677
+ "content": ai_response,
1678
+ "timestamp": datetime.now().isoformat(),
1679
+ "remaining_sentences": remaining
1680
+ })
1681
+
1682
+ # Decrease remaining sentences
1683
+ st.session_state.sentences_to_end -= 1
1684
+
1685
+ else:
1686
+ # Generate final ending
1687
+ final_response = generate_final_ending(
1688
+ st.session_state.story,
1689
+ st.session_state.ending_type,
1690
+ level=st.session_state.level
1691
+ )
1692
+
1693
+ # Add final AI response
1694
+ st.session_state.story.append({
1695
+ "role": "AI",
1696
+ "content": final_response,
1697
+ "timestamp": datetime.now().isoformat(),
1698
+ "is_final": True
1699
+ })
1700
+
1701
+ # Mark story as complete
1702
+ st.session_state.story_completed = True
1703
+
1704
+ # Clear input and rerun
1705
+ st.session_state.text_input = ""
1706
+ st.session_state.clear_input = True
1707
+ st.rerun()
1708
+
1709
+ except Exception as e:
1710
+ logging.error(f"Error in ending mode: {str(e)}")
1711
+ st.error("เกิดข้อผิดพลาดในโหมดจบเรื่อง กรุณาลองใหม่อีกครั้ง")
1712
+
1713
+
1714
+
1715
+ def generate_final_ending(story: List[dict], ending_type: str, level: str) -> str:
1716
+ """Generate the final ending sentences based on the story and chosen ending type"""
1717
+ try:
1718
+ # Create a comprehensive prompt for the final ending
1719
+ story_summary = " ".join([entry['content'] for entry in story[-5:]]) # Last 5 sentences
1720
+
1721
+ ending_prompts = {
1722
+ "Happy Ending": """
1723
+ Create a final happy ending that:
1724
+ - Brings joy and satisfaction
1725
+ - Resolves the main story elements
1726
+ - Ends on a positive note
1727
+ - Use 2-3 sentences
1728
+ """,
1729
+ "Mysterious Ending": """
1730
+ Create a final mysterious ending that:
1731
+ - Leaves an intriguing question
1732
+ - Creates a sense of wonder
1733
+ - Maintains some mystery
1734
+ - Use 2-3 sentences
1735
+ """,
1736
+ "Lesson Learned": """
1737
+ Create a final ending that:
1738
+ - Shows what was learned
1739
+ - Provides a moral lesson
1740
+ - Connects to the story's events
1741
+ - Use 2-3 sentences
1742
+ """,
1743
+ "Surprise Ending": """
1744
+ Create a final twist ending that:
1745
+ - Provides an unexpected but logical conclusion
1746
+ - Connects to previous story elements
1747
+ - Creates a satisfying surprise
1748
+ - Use 2-3 sentences
1749
+ """
1750
+ }
1751
+
1752
+ level_context = {
1753
+ 'Beginner': {
1754
+ 'instructions': """
1755
+ Additional Guidelines:
1756
+ - Use simple sentences
1757
+ - Use Present Simple Tense
1758
+ - Basic vocabulary
1759
+ - 7-10 words per sentence maximum
1760
+ """,
1761
+ 'max_tokens': 100, # Increased to allow for longer endings
1762
+ 'temperature': 0.6
1763
+ },
1764
+ 'Intermediate': {
1765
+ 'instructions': """
1766
+ Additional Guidelines:
1767
+ - Use 2-3 sentences
1768
+ - Can use Present or Past Tense
1769
+ - Keep each sentence under 15 words
1770
+ - Grade-appropriate vocabulary
1771
+ """,
1772
+ 'max_tokens': 120, # Increased
1773
+ 'temperature': 0.7
1774
+ },
1775
+ 'Advanced': {
1776
+ 'instructions': """
1777
+ Additional Guidelines:
1778
+ - Use 2-3 sentences
1779
+ - Various tenses allowed
1780
+ - Natural sentence length but keep overall response concise
1781
+ - More sophisticated vocabulary and structures
1782
+ """,
1783
+ 'max_tokens': 150, # Increased
1784
+ 'temperature': 0.8
1785
+ }
1786
+ }
1787
+
1788
+ level_settings = level_context[level]
1789
+
1790
+ response = client.chat.completions.create(
1791
+ model="gpt-4o-mini",
1792
+ messages=[
1793
+ {
1794
+ "role": "system",
1795
+ "content": f"""
1796
+ {ending_prompts[ending_type]}
1797
+
1798
+ {level_settings['instructions']}
1799
+
1800
+ This must be the absolute final sentences of the story.
1801
+ Make it conclusive and satisfying.
1802
+ """
1803
+ },
1804
+ {
1805
+ "role": "user",
1806
+ "content": f"Story context:\n{story_summary}\n\nCreate the final ending:"
1807
+ }
1808
+ ],
1809
+ max_tokens=level_settings['max_tokens'],
1810
+ temperature=level_settings['temperature']
1811
+ )
1812
+
1813
+ return response.choices[0].message.content.strip()
1814
+
1815
+ except Exception as e:
1816
+ logging.error(f"Error generating final ending: {str(e)}")
1817
+ return "And so, the story came to an end."
1818
+
1819
+
1820
+
1821
+ def generate_ending_continuation(text: str, ending_type: str, remaining_sentences: int, level: str) -> str:
1822
+ """Generate AI continuation focusing on story conclusion"""
1823
+ try:
1824
+ ending_prompts = {
1825
+ "Happy Ending": """
1826
+ Role: Story concluder aiming for a happy ending
1827
+ Goal: Create a satisfying, positive conclusion
1828
+ Rules:
1829
+ - Build towards joy, success, or resolution
1830
+ - Use uplifting and positive language
1831
+ - Connect to previous story elements
1832
+ """,
1833
+ "Mysterious Ending": """
1834
+ Role: Mystery writer creating intrigue
1835
+ Goal: Leave readers thinking and wondering
1836
+ Rules:
1837
+ - Add subtle hints and clues
1838
+ - Create atmospheric descriptions
1839
+ - Leave some questions unanswered
1840
+ """,
1841
+ "Lesson Learned": """
1842
+ Role: Moral story concluder
1843
+ Goal: Incorporate meaningful life lessons
1844
+ Rules:
1845
+ - Connect actions to consequences
1846
+ - Show character growth
1847
+ - Express the moral naturally
1848
+ """,
1849
+ "Surprise Ending": """
1850
+ Role: Plot twist creator
1851
+ Goal: Deliver unexpected but satisfying conclusion
1852
+ Rules:
1853
+ - Plant subtle hints earlier
1854
+ - Subvert expectations logically
1855
+ - Maintain story coherence
1856
+ """
1857
+ }
1858
+
1859
+ level_context = {
1860
+ 'Beginner': {
1861
+ 'instructions': """
1862
+ Additional Guidelines:
1863
+ - Use only 1-2 VERY simple sentences
1864
+ - Use Present Simple Tense only
1865
+ - Basic vocabulary
1866
+ - 5-7 words per sentence maximum
1867
+ - Focus on clear, basic responses
1868
+ """,
1869
+ 'max_tokens': 30,
1870
+ 'temperature': 0.6
1871
+ },
1872
+ 'Intermediate': {
1873
+ 'instructions': """
1874
+ Additional Guidelines:
1875
+ - Use exactly 2 sentences maximum
1876
+ - Can use Present or Past Tense
1877
+ - Keep each sentence under 12 words
1878
+ - Grade-appropriate vocabulary
1879
+ - Add simple descriptions but stay concise
1880
+ """,
1881
+ 'max_tokens': 40,
1882
+ 'temperature': 0.7
1883
+ },
1884
+ 'Advanced': {
1885
+ 'instructions': """
1886
+ Additional Guidelines:
1887
+ - Use 2-3 sentences maximum
1888
+ - Various tenses allowed
1889
+ - Natural sentence length but keep overall response concise
1890
+ - More sophisticated vocabulary and structures
1891
+ - Create engaging responses that encourage creative continuation
1892
+ """,
1893
+ 'max_tokens': 50,
1894
+ 'temperature': 0.8
1895
+ }
1896
+ }
1897
+
1898
+ # Get recent story context
1899
+ story_context = '\n'.join([
1900
+ entry['content'] for entry in st.session_state.story[-5:]
1901
+ ]) if st.session_state.story else "Story just started"
1902
+
1903
+ level_settings = level_context[level]
1904
+
1905
+ response = client.chat.completions.create(
1906
+ model="gpt-4o-mini",
1907
+ messages=[
1908
+ {
1909
+ "role": "system",
1910
+ "content": f"""
1911
+ {ending_prompts[ending_type]}
1912
+
1913
+ {level_settings['instructions']}
1914
+
1915
+ Additional Rules:
1916
+ - You have {remaining_sentences} sentences left to conclude the story.
1917
+ - Each response should be maximum 2 sentences.
1918
+ - Build towards the finale naturally.
1919
+ - Ensure coherence with previous story events.
1920
+ """
1921
+ },
1922
+ {
1923
+ "role": "user",
1924
+ "content": f"Story context:\n{story_context}\nStudent's input:\n{text}\nContinue and work towards ending this story:"
1925
+ }
1926
+ ],
1927
+ max_tokens=level_settings['max_tokens'],
1928
+ temperature=level_settings['temperature']
1929
+ )
1930
+
1931
+ return response.choices[0].message.content.strip()
1932
+
1933
+ except Exception as e:
1934
+ logging.error(f"Error generating ending continuation: {str(e)}")
1935
+ return "The story moved towards its conclusion..."
1936
+
1937
+ def complete_story():
1938
+ """Handle story completion and celebration"""
1939
+ st.balloons() # แสดงเอฟเฟคฉลอง
1940
+
1941
+ # สร้าง Story Summary
1942
+ story_summary = generate_story_summary(st.session_state.story)
1943
+
1944
+ # แสดงหน้าจบเรื่อง
1945
+ st.markdown(f"""
1946
+ <div style="
1947
+ background-color: #e8f5e9;
1948
+ padding: 20px;
1949
+ border-radius: 10px;
1950
+ text-align: center;
1951
+ margin: 20px 0;
1952
+ ">
1953
+ <h2>🎉 ยินดีด้วย! คุณเขียนเรื่องราวจบสมบูรณ์แล้ว</h2>
1954
+
1955
+ <div style="margin: 20px 0;">
1956
+ <h3>📝 สรุปเรื่องราว</h3>
1957
+ <p>{story_summary}</p>
1958
+ </div>
1959
+
1960
+ <div style="margin: 20px 0;">
1961
+ <h3>🏆 ความสำเร็จ</h3>
1962
+ <p>จำนวนประโยค: {len(st.session_state.story)}</p>
1963
+ <p>คำศัพท์ที่ใช้: {len(st.session_state.stats['vocabulary_used'])}</p>
1964
+ <p>ความแม่นยำ: {st.session_state.stats['accuracy_rate']:.1f}%</p>
1965
+ </div>
1966
+ </div>
1967
+ """, unsafe_allow_html=True)
1968
+
1969
+ # แสดงตัวเลือกหลังจบเรื่อง
1970
+ col1, col2 = st.columns(2)
1971
+ with col1:
1972
+ if st.button("💾 บันทึกเรื่องราว", use_container_width=True):
1973
+ save_completed_story()
1974
+ with col2:
1975
+ if st.button("🔄 เริ่มเรื่องใหม่", use_container_width=True):
1976
+ reset_story()
1977
+ st.rerun()
1978
+
1979
+ def show_feedback_section():
1980
+ """Display writing feedback section"""
1981
+ if not st.session_state.feedback:
1982
+ return
1983
+
1984
+ st.markdown("""
1985
+ <div style="margin-bottom: 20px;">
1986
+ <div class="thai-eng">
1987
+ <div class="thai">📝 คำแนะนำจากครู</div>
1988
+ <div class="eng">Writing Feedback</div>
1989
+ </div>
1990
+ </div>
1991
+ """, unsafe_allow_html=True)
1992
+
1993
+ feedback_data = st.session_state.feedback
1994
+ if isinstance(feedback_data, dict) and feedback_data.get('has_errors'):
1995
+ # Show error feedback
1996
+ st.markdown(f"""
1997
+ <div style="
1998
+ background-color: #fff3e0;
1999
+ padding: 20px;
2000
+ border-radius: 10px;
2001
+ border-left: 4px solid #ff9800;
2002
+ margin: 10px 0;
2003
+ ">
2004
+ <p style="color: #e65100; margin-bottom: 15px;">
2005
+ {feedback_data['feedback']}
2006
+ </p>
2007
+ <div style="
2008
+ background-color: #fff;
2009
+ padding: 15px;
2010
+ border-radius: 8px;
2011
+ margin-top: 10px;
2012
+ ">
2013
+ <p style="color: #666; margin-bottom: 5px;">
2014
+ ประโยคที่ถูกต้อง:
2015
+ </p>
2016
+ <p style="
2017
+ color: #2e7d32;
2018
+ font-weight: 500;
2019
+ font-size: 1.1em;
2020
+ margin: 0;
2021
+ ">
2022
+ {feedback_data['corrected']}
2023
+ </p>
2024
+ </div>
2025
+ </div>
2026
+ """, unsafe_allow_html=True)
2027
+
2028
+ # Correction button
2029
+ if st.button(
2030
+ "✍️ แก้ไขประโยคให้ถูกต้อง",
2031
+ key="correct_button",
2032
+ help="คลิกเพื่อแก้ไขประโยคให้ถูกต้องตามคำแนะนำ"
2033
+ ):
2034
+ last_user_entry_idx = next(
2035
+ (i for i, entry in reversed(list(enumerate(st.session_state.story)))
2036
+ if entry['role'] == 'You'),
2037
+ None
2038
+ )
2039
+ if last_user_entry_idx is not None:
2040
+ apply_correction(last_user_entry_idx, feedback_data['corrected'])
2041
+ st.rerun()
2042
+ else:
2043
+ # Show success feedback
2044
+ st.markdown(f"""
2045
+ <div style="
2046
+ background-color: #e8f5e9;
2047
+ padding: 20px;
2048
+ border-radius: 10px;
2049
+ border-left: 4px solid #4caf50;
2050
+ margin: 10px 0;
2051
+ ">
2052
+ <p style="color: #2e7d32; margin: 0;">
2053
+ {feedback_data.get('feedback', '✨ เขียนได้ถูกต้องแล้วค่ะ!')}
2054
+ </p>
2055
+ </div>
2056
+ """, unsafe_allow_html=True)
2057
+
2058
+ def show_writing_tools():
2059
+ """Display writing tools section"""
2060
+ with st.expander("✨ เครื่องมือช่วยเขียน | Writing Tools"):
2061
+ col1, col2 = st.columns(2)
2062
+
2063
+ with col1:
2064
+ if st.button("🎯 ขอคำใบ้", use_container_width=True):
2065
+ with st.spinner("กำลังสร้างคำใบ้..."):
2066
+ prompt = get_creative_prompt()
2067
+ st.markdown(f"""
2068
+ <div style="
2069
+ background-color: #f3e5f5;
2070
+ padding: 15px;
2071
+ border-radius: 8px;
2072
+ margin-top: 10px;
2073
+ ">
2074
+ <div style="color: #6a1b9a;">💭 {prompt['thai']}</div>
2075
+ <div style="color: #666; font-style: italic;">
2076
+ 💭 {prompt['eng']}
2077
+ </div>
2078
+ </div>
2079
+ """, unsafe_allow_html=True)
2080
+
2081
+ with col2:
2082
+ if st.button("📚 คำศัพท์แนะนำ", use_container_width=True):
2083
+ with st.spinner("กำลังค้นหาคำศัพท์..."):
2084
+ vocab_suggestions = get_vocabulary_suggestions()
2085
+ st.markdown("#### 📚 คำศัพท์น่ารู้")
2086
+ for word in vocab_suggestions:
2087
+ st.markdown(f"""
2088
+ <div style="
2089
+ background-color: #f5f5f5;
2090
+ padding: 10px;
2091
+ border-radius: 5px;
2092
+ margin: 5px 0;
2093
+ ">
2094
+ • {word}
2095
+ </div>
2096
+ """, unsafe_allow_html=True)
2097
+
2098
+ def show_achievements():
2099
+ """Display achievements and stats"""
2100
+ with st.container():
2101
+ # Points and Streak
2102
+ st.markdown(f"""
2103
+ <div style="
2104
+ background-color: #e3f2fd;
2105
+ padding: 20px;
2106
+ border-radius: 10px;
2107
+ margin-bottom: 20px;
2108
+ text-align: center;
2109
+ ">
2110
+ <h3 style="color: #1e88e5; margin: 0;">
2111
+ 🌟 คะแนนรวม: {st.session_state.points['total']}
2112
+ </h3>
2113
+ <p style="margin: 5px 0;">
2114
+ 🔥 Streak ปัจจุบ���น: {st.session_state.points['streak']} ประโยค
2115
+ </p>
2116
+ <p style="margin: 5px 0;">
2117
+ ⭐ Streak สูงสุด: {st.session_state.points['max_streak']} ประโยค
2118
+ </p>
2119
+ </div>
2120
+ """, unsafe_allow_html=True)
2121
+
2122
+ # Writing Stats
2123
+ st.markdown("""
2124
+ <div style="margin-bottom: 15px;">
2125
+ <h3>📊 สถิติการเขียน</h3>
2126
+ </div>
2127
+ """, unsafe_allow_html=True)
2128
+
2129
+ col1, col2 = st.columns(2)
2130
+ with col1:
2131
+ st.metric(
2132
+ "ประโยคที่เขียนทั้งหมด",
2133
+ st.session_state.stats['total_sentences'],
2134
+ help="จำนวนประโยคทั้งหมดที่คุณได้เขียน"
2135
  )
2136
+ st.metric(
2137
+ "ถูกต้องตั้งแต่แรก",
2138
+ st.session_state.stats['correct_first_try'],
2139
+ help="จำนวนประโยคที่ถูกต้องโดยไม่ต้องแก้ไข"
2140
+ )
2141
+ with col2:
2142
  st.metric(
2143
  "ความแม่นยำ",
2144
  f"{st.session_state.stats['accuracy_rate']:.1f}%",
 
2283
  <div class="eng">Your Turn</div>
2284
  </div>
2285
  """, unsafe_allow_html=True)
2286
+
2287
+ # Initialize clear_input flag if not exists
2288
+ if 'clear_input' not in st.session_state:
2289
+ st.session_state.clear_input = False
2290
+
2291
+ # Default value for text input
2292
+ default_value = "" if st.session_state.clear_input else st.session_state.get('text_input', "")
2293
+
2294
+ # If clear_input flag is True, reset it
2295
+ if st.session_state.clear_input:
2296
+ st.session_state.clear_input = False
2297
 
2298
+ # Show remaining sentences if in ending mode
2299
+ if st.session_state.get('ending_mode'):
2300
+ remaining = st.session_state.sentences_to_end
2301
+ st.info(f"🎭 โหมดจบเรื่อง - เหลืออีก {remaining} ประโยค")
2302
 
2303
+ # Input area
2304
  text_input = st.text_area(
2305
  "เขียนต่อจากเรื่องราว | Continue the story:",
2306
+ value=st.session_state.get('text_input', ""),
2307
  height=100,
2308
  key="story_input_area",
2309
  help="พิมพ์ประโยคภาษาอังกฤษเพื่อต่อเรื่อง",
2310
  label_visibility="collapsed"
2311
  )
2312
+
2313
+ # Save current input to session state
2314
+ st.session_state.text_input = text_input
2315
+
2316
  # Submit button with character count
2317
  col1, col2 = st.columns([3, 1])
2318
  with col1:
2319
+ if st.button("📝 ส่งคำตอบ | Submit", use_container_width=True):
2320
  if not text_input.strip():
2321
  st.warning("กรุณาเขียนข้อความก่อนส่ง")
2322
  return
 
2323
  try:
2324
  with st.spinner("กำลังวิเคราะห์ประโยค..."):
2325
+ handle_story_submission(text_input.strip())
 
 
 
2326
  except Exception as e:
2327
  logging.error(f"Error submitting story: {str(e)}")
2328
  st.error("เกิดข้อผิดพลาดในการส่งคำตอบ กรุณาลองใหม่อีกครั้ง")
 
2341
  st.error("กรุณาเลือกธีมเรื่องราวก่อนเริ่มเขียน")
2342
  return
2343
 
2344
+ # Check if in ending mode
2345
+ if st.session_state.get('ending_mode'):
2346
+ handle_ending_mode(text)
2347
+ return
2348
+
2349
+ # Regular processing
2350
  try:
2351
  # Get feedback
2352
  feedback_data = provide_feedback(text, st.session_state.level)
 
2372
 
2373
  # Generate AI continuation
2374
  try:
2375
+ logging.info("Attempting to generate AI continuation...")
2376
+ # Use the corrected text if there are errors
2377
+ text_for_continuation = feedback_data['corrected'] if feedback_data.get('has_errors') else text
2378
+
2379
+ ai_response = generate_story_continuation(text_for_continuation, st.session_state.level)
2380
+
2381
+ # Log the AI response
2382
+ logging.info(f"AI Response generated: {ai_response}")
2383
+
2384
  if ai_response and ai_response.strip():
2385
  st.session_state.story.append({
2386
  "role": "AI",
2387
  "content": ai_response,
2388
  "timestamp": datetime.now().isoformat()
2389
  })
2390
+ logging.info("AI response added to story successfully")
2391
  else:
2392
+ logging.error("AI generated empty response")
2393
+ # Create a fallback response
2394
+ fallback_response = generate_fallback_response(st.session_state.current_theme, st.session_state.level)
 
2395
  st.session_state.story.append({
2396
  "role": "AI",
2397
  "content": fallback_response,
 
2399
  })
2400
  except Exception as e:
2401
  logging.error(f"Error generating AI continuation: {str(e)}")
2402
+ # Create a fallback response
2403
+ fallback_response = generate_fallback_response(st.session_state.current_theme, st.session_state.level)
 
 
2404
  st.session_state.story.append({
2405
  "role": "AI",
2406
  "content": fallback_response,
2407
  "timestamp": datetime.now().isoformat()
2408
  })
2409
 
 
 
 
2410
  # Update session stats
2411
  update_session_stats()
2412
 
2413
+ # Set flag to clear input on next rerun
2414
+ st.session_state.clear_input = True
2415
+
2416
+ # Rerun to update UI
2417
+ st.rerun()
2418
+
2419
+ except Exception as e:
2420
+ logging.error(f"Error in story submission: {str(e)}")
2421
+ raise
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2422
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2423
 
2424
+ def generate_fallback_response(theme_id: str, level: str) -> str:
2425
+ """Generate a simple fallback response when AI continuation fails"""
2426
  try:
2427
+ theme = story_themes.get(theme_id, {})
2428
+ if theme:
2429
+ # ใช้คำศัพท์จาก theme และ level ที่เลือก
2430
+ vocab = theme.get('vocabulary', {}).get(level, [])
2431
+ if vocab:
2432
+ word = random.choice(vocab)
2433
+
2434
+ # สร้างประโยคตาม level
2435
+ if level == 'Beginner':
2436
+ return f"The story continues with {word}..."
2437
+ elif level == 'Intermediate':
2438
+ return f"Something interesting happened with the {word}."
2439
+ else: # Advanced
2440
+ return f"The mystery deepens as we discover more about the {word}."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2441
 
2442
+ # Default fallback if no theme-specific response can be generated
2443
+ return "The story continues..."
2444
 
2445
  except Exception as e:
2446
+ logging.error(f"Error generating fallback response: {str(e)}")
2447
+ return "What happens next?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2448
 
2449
  def show_main_interface():
2450
  """Display main story interface"""
 
2460
  """, unsafe_allow_html=True)
2461
 
2462
  show_story()
2463
+
2464
+ # Only show input if story is not completed
2465
+ if st.session_state.story and not st.session_state.get('story_completed'):
2466
  show_story_input()
2467
+
2468
+ # Show completion options if story is done
2469
+ if st.session_state.get('story_completed'):
2470
+ show_completion_options()
2471
 
2472
  with col2:
2473
  # Feedback section
 
2483
  # Save options
2484
  show_save_options()
2485
 
2486
+ def save_completed_story():
2487
+ """Save completed story with all available formats"""
2488
+ try:
2489
+ st.markdown("### 💾 บันทึกเรื่องราว", unsafe_allow_html=True)
2490
+
2491
+ save_col1, save_col2 = st.columns(2)
2492
+
2493
+ with save_col1:
2494
+ # Create and offer PDF download
2495
+ try:
2496
+ pdf_data = create_story_pdf()
2497
+ st.download_button(
2498
+ label="📥 ดาวน์โหลด PDF",
2499
+ data=pdf_data,
2500
+ file_name=f"joystory_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf",
2501
+ mime="application/pdf",
2502
+ key="download_story_pdf",
2503
+ use_container_width=True
2504
+ )
2505
+ except Exception as e:
2506
+ logging.error(f"PDF creation failed: {str(e)}")
2507
+ st.error("ไม่สามารถสร้างไฟล์ PDF ได้")
2508
+
2509
+ with save_col2:
2510
+ # Create and offer JSON download
2511
+ try:
2512
+ story_data = {
2513
+ 'timestamp': datetime.now().isoformat(),
2514
+ 'level': st.session_state.level,
2515
+ 'theme': st.session_state.current_theme,
2516
+ 'story': st.session_state.story,
2517
+ 'stats': {
2518
+ key: list(value) if isinstance(value, set) else value
2519
+ for key, value in st.session_state.stats.items()
2520
+ },
2521
+ 'points': st.session_state.points,
2522
+ 'achievements': st.session_state.achievements,
2523
+ 'ending_type': st.session_state.ending_type
2524
+ }
2525
+
2526
+ json_str = json.dumps(story_data, ensure_ascii=False, indent=2)
2527
+
2528
+ st.download_button(
2529
+ label="📥 ดาวน์โหลดเรื่องราว",
2530
+ data=json_str,
2531
+ file_name=f"joystory_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
2532
+ mime="application/json",
2533
+ key="download_story_json",
2534
+ use_container_width=True
2535
+ )
2536
+ except Exception as e:
2537
+ logging.error(f"JSON creation failed: {str(e)}")
2538
+ st.error("ไม่สามารถสร้างไฟล์บันทึกได้")
2539
+
2540
+ except Exception as e:
2541
+ logging.error(f"Error in save_completed_story: {str(e)}")
2542
+ st.error("เกิดข้อผิดพลาดในการบันทึกเรื่องราว")
2543
+
2544
+ def show_completion_options():
2545
+ """Display options and summary when story is completed"""
2546
+ try:
2547
+ st.balloons()
2548
+
2549
+ # Show completion message
2550
+ st.markdown("""
2551
+ <div style="
2552
+ background-color: #e8f5e9;
2553
+ padding: 20px;
2554
+ border-radius: 10px;
2555
+ text-align: center;
2556
+ margin: 20px 0;
2557
+ border: 2px solid #4caf50;
2558
+ ">
2559
+ <h2 style="color: #2e7d32; margin-bottom: 15px;">
2560
+ 🎉 ยินดีด้วย! คุณเขียนเรื่องราวจบสมบูรณ์แล้ว
2561
+ </h2>
2562
+ <p style="color: #1b5e20;">
2563
+ เรื่องราวของคุณจบลงด้วย: {st.session_state.ending_type}
2564
+ </p>
2565
+ </div>
2566
+ """, unsafe_allow_html=True)
2567
+
2568
+ # Show statistics
2569
+ metrics_col1, metrics_col2 = st.columns(2)
2570
+ with metrics_col1:
2571
+ st.metric(
2572
+ "จำนวนประโยคทั้งหมด",
2573
+ len(st.session_state.story),
2574
+ help="จำนวนประโยคในเรื่องทั้งหมด"
2575
+ )
2576
+ st.metric(
2577
+ "คำศัพท์ที่ใช้",
2578
+ len(st.session_state.stats['vocabulary_used']),
2579
+ help="จำนวนคำศัพท์ที่ไม่ซ้ำกัน"
2580
+ )
2581
+
2582
+ with metrics_col2:
2583
+ st.metric(
2584
+ "คะแนนรวม",
2585
+ st.session_state.points['total'],
2586
+ help="คะแนนรวมที่ได้รับ"
2587
+ )
2588
+ st.metric(
2589
+ "ความแม่นยำ",
2590
+ f"{st.session_state.stats['accuracy_rate']:.1f}%",
2591
+ help="อัตราการเขียนถูกต้อง"
2592
+ )
2593
+
2594
+ # Save options
2595
+ st.markdown("### 🎯 ต้องการทำอะไรต่อ?")
2596
+ save_completed_story()
2597
+
2598
+ # New story option
2599
+ if st.button("🔄 เริ่มเรื่องใหม่",
2600
+ key="new_story_button",
2601
+ use_container_width=True):
2602
+ if st.checkbox("✅ ยืนยันการเริ่มใหม่", key="confirm_reset"):
2603
+ st.warning("การเริ่มใหม่จะลบเรื่องราวปัจจุบันทั้งหมด")
2604
+ if st.button("🔄 ยืนยันการเริ่มใหม่",
2605
+ key="final_confirm_reset",
2606
+ use_container_width=True):
2607
+ reset_story()
2608
+ st.rerun()
2609
+
2610
+ except Exception as e:
2611
+ logging.error(f"Error showing completion options: {str(e)}")
2612
+ st.error("เกิดข้อผิดพลาดในการแสดงตัวเลือกหลังจบเรื่อง")
2613
+
2614
+ def show_save_dialog():
2615
+ """Display save options dialog"""
2616
+ try:
2617
+ st.markdown("### 💾 บันทึกเรื่องราว")
2618
+
2619
+ col1, col2 = st.columns(2)
2620
+
2621
+ with col1:
2622
+ # PDF Export with unique key
2623
+ if st.button("📑 บันทึกเป็น PDF",
2624
+ key="save_pdf_button",
2625
+ use_container_width=True):
2626
+ try:
2627
+ pdf = create_story_pdf()
2628
+ st.download_button(
2629
+ "📥 ดาวน์โหลด PDF",
2630
+ data=pdf,
2631
+ file_name=f"story_{datetime.now().strftime('%Y%m%d')}.pdf",
2632
+ mime="application/pdf",
2633
+ key="download_pdf_button"
2634
+ )
2635
+ except Exception as e:
2636
+ logging.error(f"Error creating PDF: {str(e)}")
2637
+ st.error("เกิดข้อผิดพลาดในการสร้างไฟล์ PDF")
2638
+
2639
+ with col2:
2640
+ # JSON Save with unique key
2641
+ if st.button("💾 บันทึกความก้าวหน้า",
2642
+ key="save_progress_button",
2643
+ use_container_width=True):
2644
+ try:
2645
+ story_data = {
2646
+ 'timestamp': datetime.now().isoformat(),
2647
+ 'level': st.session_state.level,
2648
+ 'story': st.session_state.story,
2649
+ 'achievements': st.session_state.achievements,
2650
+ 'stats': {
2651
+ key: list(value) if isinstance(value, set) else value
2652
+ for key, value in st.session_state.stats.items()
2653
+ },
2654
+ 'points': st.session_state.points
2655
+ }
2656
+
2657
+ st.download_button(
2658
+ "📥 ดาวน์โหลดไฟล์บันทึก",
2659
+ data=json.dumps(story_data, ensure_ascii=False, indent=2),
2660
+ file_name=f"story_progress_{datetime.now().strftime('%Y%m%d')}.json",
2661
+ mime="application/json",
2662
+ key="download_json_button"
2663
+ )
2664
+ except Exception as e:
2665
+ logging.error(f"Error saving progress: {str(e)}")
2666
+ st.error("เกิดข้อผิดพลาดในการบันทึกความก้าวหน้า")
2667
+ except Exception as e:
2668
+ logging.error(f"Error showing save dialog: {str(e)}")
2669
+ st.error("เกิดข้อผิดพลาดในการแสดงตัวเลือกการบันทึก")
2670
+
2671
+ def reset_story_with_confirmation():
2672
+ """Reset story with confirmation dialog"""
2673
+ try:
2674
+ if st.session_state.story:
2675
+ if st.checkbox("✅ ยืนยันการเริ่มใหม่",
2676
+ key="reset_confirmation_checkbox"):
2677
+ st.warning("การเริ่มใหม่จะลบเรื่องราวปัจจุบันทั้งหมด")
2678
+ if st.button("🔄 ยืนยันการเริ่มใหม่",
2679
+ key="confirm_reset_button",
2680
+ use_container_width=True):
2681
+ reset_story()
2682
+ st.success("เริ่มต้นใหม่เรียบร้อยแล้ว!")
2683
+ st.rerun()
2684
+ except Exception as e:
2685
+ logging.error(f"Error in reset confirmation: {str(e)}")
2686
+ st.error("เกิดข้อผิดพลาดในการรีเซ็ตเรื่อง")
2687
+
2688
+ def create_story_pdf():
2689
+ """Create PDF from story content"""
2690
+ try:
2691
+ buffer = io.BytesIO()
2692
+ doc = SimpleDocTemplate(
2693
+ buffer,
2694
+ pagesize=A4,
2695
+ rightMargin=72,
2696
+ leftMargin=72,
2697
+ topMargin=72,
2698
+ bottomMargin=72
2699
+ )
2700
+
2701
+ # Prepare styles
2702
+ styles = getSampleStyleSheet()
2703
+ title_style = ParagraphStyle(
2704
+ 'CustomTitle',
2705
+ parent=styles['Title'],
2706
+ fontSize=24,
2707
+ spaceAfter=30,
2708
+ alignment=1 # Center alignment
2709
+ )
2710
+ normal_style = ParagraphStyle(
2711
+ 'CustomBody',
2712
+ parent=styles['Normal'],
2713
+ fontSize=12,
2714
+ spaceBefore=6,
2715
+ spaceAfter=6
2716
+ )
2717
+
2718
+ # Create story elements list
2719
+ elements = []
2720
+
2721
+ # Add title
2722
+ title_text = "My Story - JoyStory"
2723
+ elements.append(Paragraph(title_text, title_style))
2724
+ elements.append(Spacer(1, 12))
2725
+
2726
+ # Add metadata
2727
+ metadata_style = ParagraphStyle(
2728
+ 'Metadata',
2729
+ parent=styles['Normal'],
2730
+ fontSize=10,
2731
+ textColor=colors.gray,
2732
+ alignment=1
2733
+ )
2734
+ metadata_text = f"Level: {st.session_state.level}<br/>Date: {datetime.now().strftime('%Y-%m-%d')}"
2735
+ elements.append(Paragraph(metadata_text, metadata_style))
2736
+ elements.append(Spacer(1, 20))
2737
+
2738
+ # Add story content
2739
+ for entry in st.session_state.story:
2740
+ # Skip system messages or empty entries
2741
+ if not entry.get('content'):
2742
+ continue
2743
+
2744
+ # Format based on role
2745
+ if entry['role'] == 'AI':
2746
+ text = f"🤖 AI: {entry['content']}"
2747
+ color = colors.blue
2748
+ else:
2749
+ text = f"👤 You: {entry['content']}"
2750
+ color = colors.black
2751
+
2752
+ # Create paragraph style with color
2753
+ style = ParagraphStyle(
2754
+ 'ColoredText',
2755
+ parent=normal_style,
2756
+ textColor=color
2757
+ )
2758
+
2759
+ # Add paragraph
2760
+ elements.append(Paragraph(text, style))
2761
+ elements.append(Spacer(1, 6))
2762
+
2763
+ # Add statistics
2764
+ elements.append(Spacer(1, 20))
2765
+ stats_style = ParagraphStyle(
2766
+ 'Stats',
2767
+ parent=styles['Normal'],
2768
+ fontSize=10,
2769
+ textColor=colors.gray
2770
+ )
2771
+
2772
+ stats_text = f"""
2773
+ Story Statistics:<br/>
2774
+ Total Sentences: {len(st.session_state.story)}<br/>
2775
+ Unique Words: {len(st.session_state.stats['vocabulary_used'])}<br/>
2776
+ Accuracy Rate: {st.session_state.stats['accuracy_rate']:.1f}%<br/>
2777
+ Total Points: {st.session_state.points['total']}
2778
+ """
2779
+ elements.append(Paragraph(stats_text, stats_style))
2780
+
2781
+ # Build PDF
2782
+ doc.build(elements)
2783
+ pdf = buffer.getvalue()
2784
+ buffer.close()
2785
+
2786
+ return pdf
2787
+
2788
+ except Exception as e:
2789
+ logging.error(f"Error creating PDF: {str(e)}")
2790
+ raise
2791
+
2792
  # === 6. MAIN APPLICATION LOGIC ===
2793
  def main():
2794
  try:
 
2800
  if 'ending_mode' not in st.session_state:
2801
  st.session_state.ending_mode = False
2802
  if 'sentences_to_end' not in st.session_state:
2803
+ st.session_state.sentences_to_end = 0 # Set to 0 by default
2804
  if 'ending_type' not in st.session_state:
2805
  st.session_state.ending_type = None
2806
+ if 'story_completed' not in st.session_state:
2807
+ st.session_state.story_completed = False
2808
 
2809
  # Add watermark
2810
  st.markdown("""
 
2834
  else:
2835
  # Show story progress if story exists
2836
  if st.session_state.story:
2837
+ # Display story progress
2838
+ total_sentences = len(st.session_state.story)
2839
+ st.markdown(f"""
2840
+ <div style="
2841
+ background-color: #f0f7ff;
2842
+ padding: 15px;
2843
+ border-radius: 10px;
2844
+ margin: 10px 0;
2845
+ ">
2846
+ <div style="margin-bottom: 10px;">
2847
+ 📊 ความยาวเรื่อง: {total_sentences} ประโยค
2848
+ </div>
2849
+ <div class="progress-bar">
2850
+ <div class="progress-bar-fill" style="width: {min(total_sentences * 5, 100)}%;"></div>
2851
+ </div>
2852
+ <div style="font-size: 0.9em; color: #666; margin-top: 5px;">
2853
+ เรื่องควรยาว 10-20 ประโยค เพื่อความสมบูรณ์
2854
+ </div>
2855
+ </div>
2856
+ """, unsafe_allow_html=True)
2857
 
2858
+ # Show ending options if story is long enough and ending mode is not active
2859
+ if (total_sentences >= 10 and not st.session_state.get('ending_mode') and not st.session_state.get('story_completed')):
2860
+ st.markdown("### 🎭 ต้องการจบเรื่องหรือไม่?")
2861
+ ending_type = st.radio(
2862
+ "เลือกวิธีจบเรื่อง:",
2863
+ options=[
2864
+ "Happy Ending",
2865
+ "Mysterious Ending",
2866
+ "Lesson Learned",
2867
+ "Surprise Ending"
2868
+ ],
2869
+ index=0,
2870
+ key="ending_type_selector"
2871
+ )
2872
+
2873
+ if st.button("🎬 เริ่มจบเรื่อง", use_container_width=True):
2874
+ st.session_state.ending_mode = True
2875
+ st.session_state.ending_type = ending_type
2876
+ st.session_state.sentences_to_end = 5 # Set the countdown
2877
+ st.success(f"โหมดจบเรื่องเริ่มต้นแล้ว! รูปแบบการจบ: {ending_type}")
2878
+ st.rerun()
2879
 
2880
+ # Show ending mode notification
2881
+ if st.session_state.get('ending_mode'):
2882
+ remaining = st.session_state.sentences_to_end
2883
+ if remaining > 0:
2884
+ st.warning(f"""
2885
+ 🎭 กำลังอยู่ในโหมดจบเรื่อง
2886
+ - เหลืออีก {remaining} ประโยค
2887
+ - รูปแบบการจบ: {st.session_state.ending_type}
2888
+
2889
+ พยายามเขียนให้เรื่องจบอย่างสมบูรณ์!
2890
+ """)
2891
+ elif remaining <= 0 and not st.session_state.get('story_completed'):
2892
+ # Story should be marked as completed
2893
+ st.success("🎉 เรื่องราวของคุณจบลงแล้ว!")
2894
+ st.balloons()
2895
+ st.session_state.ending_mode = False
2896
+ st.session_state.story_completed = True
2897
+
2898
+ # Display main interface
2899
  show_main_interface()
2900
 
2901
+ # Handle story completion
2902
+ if st.session_state.get('story_completed'):
2903
+ st.markdown("""
2904
+ <div style="
2905
+ background-color: #e8f5e9;
2906
+ padding: 20px;
2907
+ border-radius: 10px;
2908
+ text-align: center;
2909
+ margin: 20px 0;
2910
+ ">
2911
+ <h2>🎉 ยินดีด้วย! คุณเขียนเรื่องราวจบสมบูรณ์แล้ว</h2>
2912
+ <p>คุณสามารถบันทึกเรื่องราวหรือเริ่มเรื่องใหม่ได้</p>
2913
+ </div>
2914
+ """, unsafe_allow_html=True)
2915
+ # Show save and reset options
2916
+ col1, col2 = st.columns(2)
2917
+ with col1:
2918
+ if st.button("💾 บันทึกเรื่องราว", use_container_width=True):
2919
+ save_completed_story()
2920
+ with col2:
2921
+ if st.button("🔄 เริ่มเรื่องใหม่", use_container_width=True):
2922
+ reset_story()
2923
+ st.rerun()
2924
+
2925
  # Handle reset if needed
2926
+ if st.session_state.get('should_reset'):
2927
  reset_story()
2928
+ # Reset ending states
2929
  st.session_state.ending_mode = False
2930
+ st.session_state.sentences_to_end = 0
2931
  st.session_state.ending_type = None
2932
+ st.session_state.story_completed = False
2933
+ st.rerun() # Using st.rerun() instead of st.experimental_rerun()
2934
+
2935
  # Auto-save progress periodically
2936
  if st.session_state.story:
2937
  auto_save_progress()
 
2939
  except Exception as e:
2940
  handle_application_error(e)
2941
 
2942
+
2943
  def check_session_status():
2944
  """Check and maintain session status"""
2945
  try:
 
2947
  if 'session_start' not in st.session_state:
2948
  st.session_state.session_start = datetime.now()
2949
 
 
 
 
 
2950
  # Check for session timeout (2 hours)
2951
+ session_duration = (datetime.now() - st.session_state.session_start).total_seconds()
2952
  if session_duration > 7200: # 2 hours
2953
+ st.warning("เซสชันหมดอายุ กรุณาบันทึกความก้าวหน้าและรีเฟรชหน้าเว็บ")
2954
 
2955
  # Check for inactivity (30 minutes)
 
 
 
2956
  last_interaction = datetime.fromisoformat(st.session_state.last_interaction)
2957
+ inactivity_duration = (datetime.now() - last_interaction).total_seconds()
 
2958
  if inactivity_duration > 1800: # 30 minutes
2959
+ st.info("ไม่มีกิจกรรมเป็นเวลานาน กรุณาบันทึกความก้าวหน้าเพื่อความปลอดภัย")
2960
 
2961
  # Update stats if story exists
2962
  if st.session_state.story: