Update app.py
Browse files
app.py
CHANGED
@@ -1602,6 +1602,397 @@ def show_story_progress():
|
|
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
|
@@ -2566,50 +2957,365 @@ def show_completion_options():
|
|
2566 |
""", unsafe_allow_html=True)
|
2567 |
|
2568 |
# Show statistics
|
2569 |
-
|
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 |
-
|
2583 |
-
|
2584 |
-
|
2585 |
-
|
2586 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2587 |
)
|
2588 |
-
|
2589 |
-
|
2590 |
-
|
2591 |
-
|
|
|
|
|
|
|
2592 |
)
|
2593 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2594 |
# Save options
|
2595 |
-
st.markdown("###
|
2596 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2597 |
|
2598 |
# New story option
|
2599 |
if st.button("🔄 เริ่มเรื่องใหม่",
|
2600 |
-
key="
|
2601 |
use_container_width=True):
|
2602 |
-
if st.checkbox("✅ ยืนยันการเริ่มใหม่",
|
2603 |
-
|
2604 |
-
|
2605 |
-
|
2606 |
-
|
2607 |
-
reset_story()
|
2608 |
-
st.rerun()
|
2609 |
-
|
2610 |
except Exception as e:
|
2611 |
-
logging.error(f"Error showing
|
2612 |
-
st.error("
|
2613 |
|
2614 |
def show_save_dialog():
|
2615 |
"""Display save options dialog"""
|
|
|
1602 |
</div>
|
1603 |
""", unsafe_allow_html=True)
|
1604 |
|
1605 |
+
def generate_story_summary(story_entries: List[dict]) -> str:
|
1606 |
+
"""Generate a concise summary of the story for the stitching process"""
|
1607 |
+
try:
|
1608 |
+
# Extract main story content
|
1609 |
+
story_text = []
|
1610 |
+
for entry in story_entries:
|
1611 |
+
if entry.get('content'):
|
1612 |
+
text = entry['content'].strip()
|
1613 |
+
if text.endswith('.'):
|
1614 |
+
text = text[:-1] # Remove trailing period for better concatenation
|
1615 |
+
story_text.append(text)
|
1616 |
+
|
1617 |
+
story_summary = '. '.join(story_text) + '.'
|
1618 |
+
return story_summary
|
1619 |
+
|
1620 |
+
except Exception as e:
|
1621 |
+
logging.error(f"Error generating story summary: {str(e)}")
|
1622 |
+
return "Error creating story summary."
|
1623 |
+
|
1624 |
+
def stitch_story(raw_story: List[dict], theme_id: str, level: str) -> Dict[str, str]:
|
1625 |
+
"""Create a polished, coherent version of the story with Thai translation"""
|
1626 |
+
try:
|
1627 |
+
# Get theme details for context
|
1628 |
+
theme = story_themes.get(theme_id, {})
|
1629 |
+
theme_context = f"Theme: {theme.get('name_en', 'General')} - {theme.get('description_en', '')}"
|
1630 |
+
|
1631 |
+
# Get story summary
|
1632 |
+
story_summary = generate_story_summary(raw_story)
|
1633 |
+
|
1634 |
+
# Create level-appropriate instructions
|
1635 |
+
level_context = {
|
1636 |
+
'Beginner': {
|
1637 |
+
'instructions': """
|
1638 |
+
Create a simple, clear narrative using:
|
1639 |
+
- Basic vocabulary
|
1640 |
+
- Short, straightforward sentences
|
1641 |
+
- Present tense
|
1642 |
+
- Clear transitions
|
1643 |
+
Maintain the original story's key points but make it flow smoothly.
|
1644 |
+
""",
|
1645 |
+
'max_tokens': 500,
|
1646 |
+
'temperature': 0.7
|
1647 |
+
},
|
1648 |
+
'Intermediate': {
|
1649 |
+
'instructions': """
|
1650 |
+
Create an engaging narrative using:
|
1651 |
+
- Grade-appropriate vocabulary
|
1652 |
+
- Mix of simple and compound sentences
|
1653 |
+
- Present and past tense
|
1654 |
+
- Natural transitions
|
1655 |
+
Preserve the original story's elements while enhancing the flow.
|
1656 |
+
""",
|
1657 |
+
'max_tokens': 700,
|
1658 |
+
'temperature': 0.7
|
1659 |
+
},
|
1660 |
+
'Advanced': {
|
1661 |
+
'instructions': """
|
1662 |
+
Create a sophisticated narrative using:
|
1663 |
+
- Rich vocabulary
|
1664 |
+
- Varied sentence structures
|
1665 |
+
- Multiple tenses
|
1666 |
+
- Elegant transitions
|
1667 |
+
Maintain the story's essence while adding literary flourish.
|
1668 |
+
""",
|
1669 |
+
'max_tokens': 1000,
|
1670 |
+
'temperature': 0.8
|
1671 |
+
}
|
1672 |
+
}
|
1673 |
+
|
1674 |
+
level_settings = level_context[level]
|
1675 |
+
|
1676 |
+
# Generate polished English version
|
1677 |
+
response = client.chat.completions.create(
|
1678 |
+
model="gpt-4",
|
1679 |
+
messages=[
|
1680 |
+
{
|
1681 |
+
"role": "system",
|
1682 |
+
"content": f"""
|
1683 |
+
You are a professional children's story editor.
|
1684 |
+
{level_settings['instructions']}
|
1685 |
+
Context: {theme_context}
|
1686 |
+
|
1687 |
+
Task: Create a polished, coherent version of this story while:
|
1688 |
+
1. Maintaining the original plot points
|
1689 |
+
2. Improving flow and transitions
|
1690 |
+
3. Adding appropriate descriptive elements
|
1691 |
+
4. Making the narrative more engaging
|
1692 |
+
5. Keeping the language level appropriate for {level} students
|
1693 |
+
"""
|
1694 |
+
},
|
1695 |
+
{
|
1696 |
+
"role": "user",
|
1697 |
+
"content": f"Original story:\n{story_summary}\n\nCreate a polished version:"
|
1698 |
+
}
|
1699 |
+
],
|
1700 |
+
max_tokens=level_settings['max_tokens'],
|
1701 |
+
temperature=level_settings['temperature']
|
1702 |
+
)
|
1703 |
+
|
1704 |
+
polished_english = response.choices[0].message.content.strip()
|
1705 |
+
|
1706 |
+
# Generate Thai translation
|
1707 |
+
translation_response = client.chat.completions.create(
|
1708 |
+
model="gpt-4",
|
1709 |
+
messages=[
|
1710 |
+
{
|
1711 |
+
"role": "system",
|
1712 |
+
"content": """
|
1713 |
+
You are a professional Thai-English translator specializing in children's literature.
|
1714 |
+
Create a natural, flowing Thai translation that:
|
1715 |
+
1. Captures the story's meaning and emotion
|
1716 |
+
2. Uses appropriate Thai language for the target age group
|
1717 |
+
3. Maintains cultural relevance
|
1718 |
+
4. Reads naturally in Thai
|
1719 |
+
"""
|
1720 |
+
},
|
1721 |
+
{
|
1722 |
+
"role": "user",
|
1723 |
+
"content": f"Translate this story to Thai:\n{polished_english}"
|
1724 |
+
}
|
1725 |
+
],
|
1726 |
+
max_tokens=1000,
|
1727 |
+
temperature=0.7
|
1728 |
+
)
|
1729 |
+
|
1730 |
+
thai_translation = translation_response.choices[0].message.content.strip()
|
1731 |
+
|
1732 |
+
return {
|
1733 |
+
'original': story_summary,
|
1734 |
+
'polished_english': polished_english,
|
1735 |
+
'thai_translation': thai_translation
|
1736 |
+
}
|
1737 |
+
|
1738 |
+
except Exception as e:
|
1739 |
+
logging.error(f"Error in story stitching: {str(e)}")
|
1740 |
+
raise
|
1741 |
+
|
1742 |
+
def show_stitched_story(story_data: Dict[str, str]):
|
1743 |
+
"""Display the stitched story with translation"""
|
1744 |
+
try:
|
1745 |
+
st.markdown("""
|
1746 |
+
<div style="
|
1747 |
+
background-color: #f8f9fa;
|
1748 |
+
padding: 20px;
|
1749 |
+
border-radius: 10px;
|
1750 |
+
margin: 20px 0;
|
1751 |
+
border: 2px solid #4caf50;
|
1752 |
+
">
|
1753 |
+
<h3 style="color: #2e7d32; text-align: center; margin-bottom: 20px;">
|
1754 |
+
📖 Your Polished Story | เรื่องราวฉบับสมบูรณ์
|
1755 |
+
</h3>
|
1756 |
+
|
1757 |
+
<div style="
|
1758 |
+
background-color: white;
|
1759 |
+
padding: 15px;
|
1760 |
+
border-radius: 8px;
|
1761 |
+
margin-bottom: 15px;
|
1762 |
+
">
|
1763 |
+
<h4 style="color: #1565c0;">English Version</h4>
|
1764 |
+
<p style="color: #333; font-size: 1.1em; line-height: 1.6;">
|
1765 |
+
{story_data['polished_english']}
|
1766 |
+
</p>
|
1767 |
+
</div>
|
1768 |
+
|
1769 |
+
<div style="
|
1770 |
+
background-color: white;
|
1771 |
+
padding: 15px;
|
1772 |
+
border-radius: 8px;
|
1773 |
+
">
|
1774 |
+
<h4 style="color: #1565c0;">ฉบับภาษาไทย</h4>
|
1775 |
+
<p style="color: #333; font-size: 1.1em; line-height: 1.6;">
|
1776 |
+
{story_data['thai_translation']}
|
1777 |
+
</p>
|
1778 |
+
</div>
|
1779 |
+
</div>
|
1780 |
+
""", unsafe_allow_html=True)
|
1781 |
+
|
1782 |
+
# Save options
|
1783 |
+
st.markdown("### 💾 บันทึกเรื่องราวฉบับสมบูรณ์")
|
1784 |
+
save_col1, save_col2 = st.columns(2)
|
1785 |
+
|
1786 |
+
with save_col1:
|
1787 |
+
if st.button("📥 บันทึกเป็น PDF",
|
1788 |
+
key="save_stitched_pdf",
|
1789 |
+
use_container_width=True):
|
1790 |
+
pdf_data = create_bilingual_story_pdf(story_data)
|
1791 |
+
st.download_button(
|
1792 |
+
"ดาวน์โหลด PDF",
|
1793 |
+
data=pdf_data,
|
1794 |
+
file_name=f"my_story_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf",
|
1795 |
+
mime="application/pdf",
|
1796 |
+
key="download_stitched_pdf"
|
1797 |
+
)
|
1798 |
+
|
1799 |
+
with save_col2:
|
1800 |
+
if st.button("💾 บันทึกข้อความ",
|
1801 |
+
key="save_stitched_text",
|
1802 |
+
use_container_width=True):
|
1803 |
+
text_data = json.dumps(story_data, ensure_ascii=False, indent=2)
|
1804 |
+
st.download_button(
|
1805 |
+
"ดาวน์โหลดข้อความ",
|
1806 |
+
data=text_data,
|
1807 |
+
file_name=f"my_story_{datetime.now().strftime('%Y%m%d_%H%M')}.json",
|
1808 |
+
mime="application/json",
|
1809 |
+
key="download_stitched_text"
|
1810 |
+
)
|
1811 |
+
|
1812 |
+
except Exception as e:
|
1813 |
+
logging.error(f"Error showing stitched story: {str(e)}")
|
1814 |
+
st.error("เกิดข้อผิดพลาดในการแสดงเรื่องราวฉบับสมบูรณ์")
|
1815 |
+
|
1816 |
+
def create_bilingual_story_pdf(story_data: Dict[str, str]) -> bytes:
|
1817 |
+
"""Create a PDF with both English and Thai versions of the story"""
|
1818 |
+
try:
|
1819 |
+
buffer = io.BytesIO()
|
1820 |
+
doc = SimpleDocTemplate(
|
1821 |
+
buffer,
|
1822 |
+
pagesize=A4,
|
1823 |
+
rightMargin=72,
|
1824 |
+
leftMargin=72,
|
1825 |
+
topMargin=72,
|
1826 |
+
bottomMargin=72
|
1827 |
+
)
|
1828 |
+
|
1829 |
+
# Create styles
|
1830 |
+
styles = getSampleStyleSheet()
|
1831 |
+
title_style = ParagraphStyle(
|
1832 |
+
'CustomTitle',
|
1833 |
+
parent=styles['Title'],
|
1834 |
+
fontSize=24,
|
1835 |
+
spaceAfter=30,
|
1836 |
+
alignment=1 # Center alignment
|
1837 |
+
)
|
1838 |
+
heading_style = ParagraphStyle(
|
1839 |
+
'CustomHeading',
|
1840 |
+
parent=styles['Heading1'],
|
1841 |
+
fontSize=18,
|
1842 |
+
spaceAfter=12,
|
1843 |
+
textColor=colors.blue
|
1844 |
+
)
|
1845 |
+
body_style = ParagraphStyle(
|
1846 |
+
'CustomBody',
|
1847 |
+
parent=styles['Normal'],
|
1848 |
+
fontSize=12,
|
1849 |
+
spaceBefore=6,
|
1850 |
+
spaceAfter=6,
|
1851 |
+
leading=16
|
1852 |
+
)
|
1853 |
+
|
1854 |
+
# Create story elements
|
1855 |
+
elements = []
|
1856 |
+
|
1857 |
+
# Add title
|
1858 |
+
elements.append(Paragraph("My Story - JoyStory", title_style))
|
1859 |
+
elements.append(Spacer(1, 20))
|
1860 |
+
|
1861 |
+
# Add English version
|
1862 |
+
elements.append(Paragraph("English Version", heading_style))
|
1863 |
+
elements.append(Paragraph(story_data['polished_english'], body_style))
|
1864 |
+
elements.append(Spacer(1, 20))
|
1865 |
+
|
1866 |
+
# Add Thai version
|
1867 |
+
elements.append(Paragraph("ฉบับภาษาไทย", heading_style))
|
1868 |
+
elements.append(Paragraph(story_data['thai_translation'], body_style))
|
1869 |
+
|
1870 |
+
# Build PDF
|
1871 |
+
doc.build(elements)
|
1872 |
+
pdf = buffer.getvalue()
|
1873 |
+
buffer.close()
|
1874 |
+
|
1875 |
+
return pdf
|
1876 |
+
|
1877 |
+
except Exception as e:
|
1878 |
+
logging.error(f"Error creating bilingual PDF: {str(e)}")
|
1879 |
+
raise
|
1880 |
+
|
1881 |
+
def show_story_stats():
|
1882 |
+
"""Display comprehensive story statistics"""
|
1883 |
+
try:
|
1884 |
+
st.markdown("""
|
1885 |
+
<div style="
|
1886 |
+
background-color: #f3e5f5;
|
1887 |
+
padding: 20px;
|
1888 |
+
border-radius: 10px;
|
1889 |
+
margin: 20px 0;
|
1890 |
+
">
|
1891 |
+
<h3 style="color: #6a1b9a; margin-bottom: 15px; text-align: center;">
|
1892 |
+
📊 สถิติการเขียนเรื่องราว
|
1893 |
+
</h3>
|
1894 |
+
""", unsafe_allow_html=True)
|
1895 |
+
|
1896 |
+
# Basic statistics in 2 columns
|
1897 |
+
col1, col2 = st.columns(2)
|
1898 |
+
|
1899 |
+
with col1:
|
1900 |
+
st.metric(
|
1901 |
+
"จำนวนประโยคทั้งหมด",
|
1902 |
+
len(st.session_state.story),
|
1903 |
+
help="จำนวนประโยคในเรื่องทั้งหมด"
|
1904 |
+
)
|
1905 |
+
st.metric(
|
1906 |
+
"คำศัพท์ที่ใช้",
|
1907 |
+
len(st.session_state.stats['vocabulary_used']),
|
1908 |
+
help="จำนวนคำศัพท์ที่ไม่ซ้ำกัน"
|
1909 |
+
)
|
1910 |
+
st.metric(
|
1911 |
+
"ประโยคที่ถูกต้องแล้ว",
|
1912 |
+
st.session_state.stats['correct_first_try'],
|
1913 |
+
help="จำนวนประโยคที่เขียนถูกต้องตั้งแต่ครั้งแรก"
|
1914 |
+
)
|
1915 |
+
|
1916 |
+
with col2:
|
1917 |
+
st.metric(
|
1918 |
+
"คะแนนรวม",
|
1919 |
+
st.session_state.points['total'],
|
1920 |
+
help="คะแนนรวมที่ได้รับ"
|
1921 |
+
)
|
1922 |
+
st.metric(
|
1923 |
+
"ความแม่นยำ",
|
1924 |
+
f"{st.session_state.stats['accuracy_rate']:.1f}%",
|
1925 |
+
help="อัตราการเขียนถูกต้อง"
|
1926 |
+
)
|
1927 |
+
st.metric(
|
1928 |
+
"Streak สูงสุด",
|
1929 |
+
st.session_state.points['max_streak'],
|
1930 |
+
help="จำนวนประโยคถูกต้องติดต่อกันมากที่สุด"
|
1931 |
+
)
|
1932 |
+
|
1933 |
+
# Show achievements
|
1934 |
+
if st.session_state.achievements:
|
1935 |
+
st.markdown("""
|
1936 |
+
<h4 style="color: #6a1b9a; margin: 20px 0 10px 0;">
|
1937 |
+
🏆 ความสำเร็จที่ได้รับ
|
1938 |
+
</h4>
|
1939 |
+
""", unsafe_allow_html=True)
|
1940 |
+
|
1941 |
+
for achievement in st.session_state.achievements:
|
1942 |
+
st.success(achievement)
|
1943 |
+
|
1944 |
+
# Show writing progress details
|
1945 |
+
st.markdown("""
|
1946 |
+
<h4 style="color: #6a1b9a; margin: 20px 0 10px 0;">
|
1947 |
+
📝 รายละเอียดการเขียน
|
1948 |
+
</h4>
|
1949 |
+
""", unsafe_allow_html=True)
|
1950 |
+
|
1951 |
+
details_col1, details_col2 = st.columns(2)
|
1952 |
+
|
1953 |
+
with details_col1:
|
1954 |
+
# Story composition metrics
|
1955 |
+
st.markdown("##### 📖 องค์ประกอบเรื่อง")
|
1956 |
+
avg_sentence_length = st.session_state.stats.get('average_sentence_length', 0)
|
1957 |
+
st.markdown(f"""
|
1958 |
+
- ความยาวประโยคเฉลี่ย: {avg_sentence_length:.1f} คำ
|
1959 |
+
- จำนวนคำทั้งหมด: {st.session_state.stats.get('total_words', 0)} คำ
|
1960 |
+
- การแก้ไข: {st.session_state.stats.get('corrections_made', 0)} ครั้ง
|
1961 |
+
""")
|
1962 |
+
|
1963 |
+
with details_col2:
|
1964 |
+
# Time and progress metrics
|
1965 |
+
st.markdown("##### ⏱️ เวลาและความก้าวหน้า")
|
1966 |
+
session_duration = st.session_state.stats.get('session_duration', 0)
|
1967 |
+
duration_minutes = session_duration / 60
|
1968 |
+
st.markdown(f"""
|
1969 |
+
- เวลาที่ใช้: {duration_minutes:.1f} นาที
|
1970 |
+
- ความก้าวหน้า: {min(len(st.session_state.story) * 5, 100)}%
|
1971 |
+
- สถานะ: {"จบเรื่องแล้ว" if st.session_state.story_completed else "กำลังเขีย��"}
|
1972 |
+
""")
|
1973 |
+
|
1974 |
+
# Show vocabulary usage
|
1975 |
+
if st.session_state.stats['vocabulary_used']:
|
1976 |
+
with st.expander("📚 คำศัพท์ที่ใช้"):
|
1977 |
+
vocab_list = sorted(list(st.session_state.stats['vocabulary_used']))
|
1978 |
+
st.markdown(f"""
|
1979 |
+
<div style="
|
1980 |
+
background-color: white;
|
1981 |
+
padding: 10px;
|
1982 |
+
border-radius: 5px;
|
1983 |
+
max-height: 200px;
|
1984 |
+
overflow-y: auto;
|
1985 |
+
">
|
1986 |
+
{', '.join(vocab_list)}
|
1987 |
+
</div>
|
1988 |
+
""", unsafe_allow_html=True)
|
1989 |
+
|
1990 |
+
st.markdown("</div>", unsafe_allow_html=True)
|
1991 |
+
|
1992 |
+
except Exception as e:
|
1993 |
+
logging.error(f"Error showing story stats: {str(e)}")
|
1994 |
+
st.error("เกิดข้อผิดพลาดในการแสดงสถิติ")
|
1995 |
+
|
1996 |
def show_story_ending_options():
|
1997 |
"""Display story ending options and guidance"""
|
1998 |
if len(st.session_state.story) >= 10: # Show options when story is long enough
|
|
|
2957 |
""", unsafe_allow_html=True)
|
2958 |
|
2959 |
# Show statistics
|
2960 |
+
show_story_stats()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2961 |
|
2962 |
+
# Offer story stitching option
|
2963 |
+
st.markdown("### ✨ ต้องการให้ AI ช่วยเรียบเรียงเรื่องราวให้สมบูรณ์ขึ้นไหม?")
|
2964 |
+
|
2965 |
+
if st.button("🎨 เรียบเรียงเรื่องราว",
|
2966 |
+
key="stitch_story_button",
|
2967 |
+
use_container_width=True):
|
2968 |
+
with st.spinner("กำลังเรียบเรียงเรื่องราว..."):
|
2969 |
+
try:
|
2970 |
+
stitched_story = stitch_story(
|
2971 |
+
st.session_state.story,
|
2972 |
+
st.session_state.current_theme,
|
2973 |
+
st.session_state.level
|
2974 |
+
)
|
2975 |
+
show_stitched_story(stitched_story)
|
2976 |
+
except Exception as e:
|
2977 |
+
logging.error(f"Error in story stitching: {str(e)}")
|
2978 |
+
st.error("เกิดข้อผิดพลาดในการเรียบเรียงเรื่องราว กรุณาลองใหม่อีกครั้ง")
|
2979 |
+
|
2980 |
+
# Original save options
|
2981 |
+
st.markdown("### 💾 บันทึกเรื่องราวต้นฉบับ")
|
2982 |
+
save_col1, save_col2 = st.columns(2)
|
2983 |
+
|
2984 |
+
with save_col1:
|
2985 |
+
if st.button("📑 บันทึกเป็น PDF",
|
2986 |
+
key="save_original_pdf",
|
2987 |
+
use_container_width=True):
|
2988 |
+
pdf_data = create_story_pdf()
|
2989 |
+
st.download_button(
|
2990 |
+
"ดาวน์โหลด PDF",
|
2991 |
+
data=pdf_data,
|
2992 |
+
file_name=f"original_story_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf",
|
2993 |
+
mime="application/pdf",
|
2994 |
+
key="download_original_pdf"
|
2995 |
+
)
|
2996 |
+
|
2997 |
+
with save_col2:
|
2998 |
+
if st.button("🔄 เริ่มเรื่องใหม่",
|
2999 |
+
key="new_story_button",
|
3000 |
+
use_container_width=True):
|
3001 |
+
if st.checkbox("✅ ยืนยันการเริ่มใหม่",
|
3002 |
+
key="confirm_new_story"):
|
3003 |
+
reset_story()
|
3004 |
+
st.rerun()
|
3005 |
+
|
3006 |
+
except Exception as e:
|
3007 |
+
logging.error(f"Error showing completion options: {str(e)}")
|
3008 |
+
st.error("เกิดข้อผิดพลาดในการแสดงตัวเลือกหลังจบเรื่อง")
|
3009 |
+
|
3010 |
+
def show_story_stitching_options():
|
3011 |
+
"""Display story stitching options and handle the flow"""
|
3012 |
+
try:
|
3013 |
+
st.markdown("""
|
3014 |
+
<div style="
|
3015 |
+
background-color: #e3f2fd;
|
3016 |
+
padding: 20px;
|
3017 |
+
border-radius: 10px;
|
3018 |
+
margin: 20px 0;
|
3019 |
+
">
|
3020 |
+
<h3 style="color: #1565c0; margin-bottom: 15px;">
|
3021 |
+
✨ เรียบเรียงเรื่องราวให้สมบูรณ์
|
3022 |
+
</h3>
|
3023 |
+
<p style="color: #333;">
|
3024 |
+
AI จะช่วย:
|
3025 |
+
- ปรับแต่งการเชื่อมประโยคให้ลื่นไหล
|
3026 |
+
- เพิ่มรายละเอียดที่น่าสนใจ
|
3027 |
+
- แปลเป็นภาษาไทย
|
3028 |
+
- รักษาเนื้อเรื่องและความคิดสร้างสรรค์ของคุณไว้
|
3029 |
+
</p>
|
3030 |
+
</div>
|
3031 |
+
""", unsafe_allow_html=True)
|
3032 |
+
|
3033 |
+
col1, col2 = st.columns(2)
|
3034 |
+
|
3035 |
+
with col1:
|
3036 |
+
style_option = st.selectbox(
|
3037 |
+
"เลือกรูปแบบการเล่าเรื่อง:",
|
3038 |
+
options=[
|
3039 |
+
"Classic Fairytale - นิทานแบบคลาสสิก",
|
3040 |
+
"Modern Adventure - การผจญภัยสมัยใหม่",
|
3041 |
+
"Poetic Style - แบบกวีนิพนธ์",
|
3042 |
+
"Simple and Clear - เรียบง่ายและชัดเจน"
|
3043 |
+
],
|
3044 |
+
key="story_style_selector"
|
3045 |
)
|
3046 |
+
|
3047 |
+
with col2:
|
3048 |
+
detail_level = st.select_slider(
|
3049 |
+
"ระดับรายละเอียด:",
|
3050 |
+
options=["น้อย", "ปานกลาง", "มาก"],
|
3051 |
+
value="ปานกลาง",
|
3052 |
+
key="detail_level_selector"
|
3053 |
)
|
3054 |
|
3055 |
+
if st.button("🎨 เริ่มเรียบเรียงเรื่องราว",
|
3056 |
+
key="start_stitching",
|
3057 |
+
use_container_width=True):
|
3058 |
+
with st.spinner("กำลังเรียบเรียงเรื่องราว..."):
|
3059 |
+
try:
|
3060 |
+
stitched_story = generate_stitched_story(
|
3061 |
+
story=st.session_state.story,
|
3062 |
+
style=style_option,
|
3063 |
+
detail_level=detail_level,
|
3064 |
+
theme=st.session_state.current_theme,
|
3065 |
+
level=st.session_state.level
|
3066 |
+
)
|
3067 |
+
|
3068 |
+
# Save stitched story to session state
|
3069 |
+
st.session_state.stitched_story = stitched_story
|
3070 |
+
|
3071 |
+
# Show the result
|
3072 |
+
show_stitched_result(stitched_story)
|
3073 |
+
|
3074 |
+
except Exception as e:
|
3075 |
+
logging.error(f"Error in story stitching: {str(e)}")
|
3076 |
+
st.error("เกิดข้อผิดพลาดในการเรียบเรียงเรื่องราว กรุณาลองใหม่อีกครั้ง")
|
3077 |
+
|
3078 |
+
except Exception as e:
|
3079 |
+
logging.error(f"Error showing stitching options: {str(e)}")
|
3080 |
+
st.error("เกิดข้อผิดพลาดในการแสดงตัวเลือกการเรียบเรียง")
|
3081 |
+
|
3082 |
+
def generate_stitched_story(story: List[dict], style: str, detail_level: str, theme: str, level: str) -> Dict[str, str]:
|
3083 |
+
"""Generate a polished version of the story based on selected style and detail level"""
|
3084 |
+
try:
|
3085 |
+
# Convert detail level to English
|
3086 |
+
detail_map = {
|
3087 |
+
"น้อย": "minimal",
|
3088 |
+
"ปานกลาง": "moderate",
|
3089 |
+
"มาก": "detailed"
|
3090 |
+
}
|
3091 |
+
detail_level_en = detail_map.get(detail_level, "moderate")
|
3092 |
+
|
3093 |
+
# Extract style name
|
3094 |
+
style_name = style.split(" - ")[0]
|
3095 |
+
|
3096 |
+
# Create style-specific instructions
|
3097 |
+
style_instructions = {
|
3098 |
+
"Classic Fairytale": """
|
3099 |
+
Create a classic fairytale style with:
|
3100 |
+
- Traditional opening and closing phrases
|
3101 |
+
- Magical and whimsical elements
|
3102 |
+
- Clear moral or lesson
|
3103 |
+
- Flowing, storybook language
|
3104 |
+
""",
|
3105 |
+
"Modern Adventure": """
|
3106 |
+
Create a contemporary adventure style with:
|
3107 |
+
- Dynamic pacing
|
3108 |
+
- Modern settings and references
|
3109 |
+
- Engaging action sequences
|
3110 |
+
- Relatable characters
|
3111 |
+
""",
|
3112 |
+
"Poetic Style": """
|
3113 |
+
Create a poetic narrative style with:
|
3114 |
+
- Rich imagery and metaphors
|
3115 |
+
- Rhythmic language
|
3116 |
+
- Evocative descriptions
|
3117 |
+
- Artistic expression
|
3118 |
+
""",
|
3119 |
+
"Simple and Clear": """
|
3120 |
+
Create a clear, straightforward style with:
|
3121 |
+
- Direct language
|
3122 |
+
- Clear sequence of events
|
3123 |
+
- Focused storytelling
|
3124 |
+
- Easy-to-follow structure
|
3125 |
+
"""
|
3126 |
+
}
|
3127 |
+
|
3128 |
+
# Create detail level instructions
|
3129 |
+
detail_instructions = {
|
3130 |
+
"minimal": "Keep descriptions concise and focus on key events.",
|
3131 |
+
"moderate": "Balance description and action with moderate detail.",
|
3132 |
+
"detailed": "Include rich descriptions and elaborate on scenes and characters."
|
3133 |
+
}
|
3134 |
+
|
3135 |
+
# Generate polished version
|
3136 |
+
response = client.chat.completions.create(
|
3137 |
+
model="gpt-4",
|
3138 |
+
messages=[
|
3139 |
+
{
|
3140 |
+
"role": "system",
|
3141 |
+
"content": f"""
|
3142 |
+
You are a professional children's story editor and translator.
|
3143 |
+
|
3144 |
+
Style Guidelines:
|
3145 |
+
{style_instructions[style_name]}
|
3146 |
+
|
3147 |
+
Detail Level:
|
3148 |
+
{detail_instructions[detail_level_en]}
|
3149 |
+
|
3150 |
+
Educational Level: {level}
|
3151 |
+
|
3152 |
+
Create a polished version that:
|
3153 |
+
1. Maintains the original story's core elements
|
3154 |
+
2. Adapts to the requested style
|
3155 |
+
3. Provides appropriate detail level
|
3156 |
+
4. Ensures age-appropriate language
|
3157 |
+
5. Creates a cohesive narrative flow
|
3158 |
+
"""
|
3159 |
+
},
|
3160 |
+
{
|
3161 |
+
"role": "user",
|
3162 |
+
"content": f"Original story:\n{generate_story_summary(story)}\n\nCreate a polished version:"
|
3163 |
+
}
|
3164 |
+
],
|
3165 |
+
max_tokens=1500,
|
3166 |
+
temperature=0.7
|
3167 |
+
)
|
3168 |
+
|
3169 |
+
polished_english = response.choices[0].message.content.strip()
|
3170 |
+
|
3171 |
+
# Generate Thai translation with matching style
|
3172 |
+
translation_response = client.chat.completions.create(
|
3173 |
+
model="gpt-4",
|
3174 |
+
messages=[
|
3175 |
+
{
|
3176 |
+
"role": "system",
|
3177 |
+
"content": f"""
|
3178 |
+
You are a professional Thai translator specializing in children's literature.
|
3179 |
+
|
3180 |
+
Create a Thai translation that:
|
3181 |
+
1. Matches the {style_name} style
|
3182 |
+
2. Maintains the same detail level ({detail_level_en})
|
3183 |
+
3. Uses natural, flowing Thai language
|
3184 |
+
4. Preserves the story's tone and feeling
|
3185 |
+
5. Adapts cultural elements appropriately
|
3186 |
+
"""
|
3187 |
+
},
|
3188 |
+
{
|
3189 |
+
"role": "user",
|
3190 |
+
"content": f"Translate this story to Thai:\n{polished_english}"
|
3191 |
+
}
|
3192 |
+
],
|
3193 |
+
max_tokens=1500,
|
3194 |
+
temperature=0.7
|
3195 |
+
)
|
3196 |
+
|
3197 |
+
thai_translation = translation_response.choices[0].message.content.strip()
|
3198 |
+
|
3199 |
+
return {
|
3200 |
+
'original': generate_story_summary(story),
|
3201 |
+
'polished_english': polished_english,
|
3202 |
+
'thai_translation': thai_translation,
|
3203 |
+
'style': style_name,
|
3204 |
+
'detail_level': detail_level_en
|
3205 |
+
}
|
3206 |
+
|
3207 |
+
except Exception as e:
|
3208 |
+
logging.error(f"Error generating stitched story: {str(e)}")
|
3209 |
+
raise
|
3210 |
+
|
3211 |
+
def show_stitched_result(story_data: Dict[str, str]):
|
3212 |
+
"""Display the stitched story result with enhanced formatting"""
|
3213 |
+
try:
|
3214 |
+
st.markdown("""
|
3215 |
+
<div style="
|
3216 |
+
background-color: #f8f9fa;
|
3217 |
+
padding: 20px;
|
3218 |
+
border-radius: 10px;
|
3219 |
+
margin: 20px 0;
|
3220 |
+
">
|
3221 |
+
<h2 style="color: #1565c0; text-align: center; margin-bottom: 20px;">
|
3222 |
+
✨ เรื่องราวฉบับสมบูรณ์ | Complete Story
|
3223 |
+
</h2>
|
3224 |
+
|
3225 |
+
<div style="
|
3226 |
+
background-color: white;
|
3227 |
+
padding: 20px;
|
3228 |
+
border-radius: 8px;
|
3229 |
+
margin-bottom: 20px;
|
3230 |
+
border: 1px solid #e0e0e0;
|
3231 |
+
">
|
3232 |
+
<h3 style="color: #2e7d32; margin-bottom: 10px;">
|
3233 |
+
English Version
|
3234 |
+
</h3>
|
3235 |
+
<div style="
|
3236 |
+
color: #333;
|
3237 |
+
font-size: 1.1em;
|
3238 |
+
line-height: 1.6;
|
3239 |
+
white-space: pre-line;
|
3240 |
+
">
|
3241 |
+
{story_data['polished_english']}
|
3242 |
+
</div>
|
3243 |
+
</div>
|
3244 |
+
|
3245 |
+
<div style="
|
3246 |
+
background-color: white;
|
3247 |
+
padding: 20px;
|
3248 |
+
border-radius: 8px;
|
3249 |
+
border: 1px solid #e0e0e0;
|
3250 |
+
">
|
3251 |
+
<h3 style="color: #2e7d32; margin-bottom: 10px;">
|
3252 |
+
ฉบับภาษาไทย
|
3253 |
+
</h3>
|
3254 |
+
<div style="
|
3255 |
+
color: #333;
|
3256 |
+
font-size: 1.1em;
|
3257 |
+
line-height: 1.6;
|
3258 |
+
white-space: pre-line;
|
3259 |
+
">
|
3260 |
+
{story_data['thai_translation']}
|
3261 |
+
</div>
|
3262 |
+
</div>
|
3263 |
+
</div>
|
3264 |
+
""", unsafe_allow_html=True)
|
3265 |
+
|
3266 |
# Save options
|
3267 |
+
st.markdown("### 💾 บันทึกเรื่องราว")
|
3268 |
+
|
3269 |
+
save_col1, save_col2 = st.columns(2)
|
3270 |
+
|
3271 |
+
with save_col1:
|
3272 |
+
if st.button("📥 บันทึกเป็น PDF",
|
3273 |
+
key="save_final_pdf",
|
3274 |
+
use_container_width=True):
|
3275 |
+
try:
|
3276 |
+
pdf_data = create_bilingual_story_pdf(story_data)
|
3277 |
+
file_name = f"my_story_{datetime.now().strftime('%Y%m%d_%H%M')}.pdf"
|
3278 |
+
st.download_button(
|
3279 |
+
"ดาวน์โหลด PDF",
|
3280 |
+
data=pdf_data,
|
3281 |
+
file_name=file_name,
|
3282 |
+
mime="application/pdf",
|
3283 |
+
key="download_final_pdf"
|
3284 |
+
)
|
3285 |
+
except Exception as e:
|
3286 |
+
logging.error(f"Error saving PDF: {str(e)}")
|
3287 |
+
st.error("เกิดข้อผิดพลาดในการสร้างไฟล์ PDF")
|
3288 |
+
|
3289 |
+
with save_col2:
|
3290 |
+
if st.button("📝 บันทึกข้อความ",
|
3291 |
+
key="save_final_text",
|
3292 |
+
use_container_width=True):
|
3293 |
+
try:
|
3294 |
+
text_data = json.dumps(story_data, ensure_ascii=False, indent=2)
|
3295 |
+
file_name = f"my_story_{datetime.now().strftime('%Y%m%d_%H%M')}.json"
|
3296 |
+
st.download_button(
|
3297 |
+
"ดาวน์โหลดข้อความ",
|
3298 |
+
data=text_data,
|
3299 |
+
file_name=file_name,
|
3300 |
+
mime="application/json",
|
3301 |
+
key="download_final_text"
|
3302 |
+
)
|
3303 |
+
except Exception as e:
|
3304 |
+
logging.error(f"Error saving text: {str(e)}")
|
3305 |
+
st.error("เกิดข้อผิดพลาดในการบันทึกข้อความ")
|
3306 |
|
3307 |
# New story option
|
3308 |
if st.button("🔄 เริ่มเรื่องใหม่",
|
3309 |
+
key="new_story_after_stitch",
|
3310 |
use_container_width=True):
|
3311 |
+
if st.checkbox("✅ ยืนยันการเริ่มใหม่",
|
3312 |
+
key="confirm_new_after_stitch"):
|
3313 |
+
reset_story()
|
3314 |
+
st.rerun()
|
3315 |
+
|
|
|
|
|
|
|
3316 |
except Exception as e:
|
3317 |
+
logging.error(f"Error showing stitched result: {str(e)}")
|
3318 |
+
st.error("เกิดข้อผิดพลาดในการแสดงผลลัพธ์")
|
3319 |
|
3320 |
def show_save_dialog():
|
3321 |
"""Display save options dialog"""
|