File size: 10,157 Bytes
32c4e29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
import gradio as gr
import os # Keep os import in case it's used elsewhere, though not for Streamlit env vars now

# Import your existing modules
from modules.wiki_fetcher import fetch_topic_summary, fetch_wikibooks_links, fetch_wikidata_facts, fetch_wikiversity_resources
from modules.ai_engine import mentor_chat_response, generate_flashcards

# --- Gradio Theme and Global Styling ---
# Gradio handles light/dark mode automatically with its themes.
# We'll use a theme that generally looks good. You can experiment with others like
# gr.themes.Default(), gr.themes.Monochrome(), gr.themes.Glass()
theme = gr.themes.Soft()

# Custom CSS for the flashcards to mimic the Streamlit look
# Gradio's theming will handle the overall app background/text colors.
custom_css = """
/* Basic styling for the banner */
.banner {
    text-align: center;
    padding: 20px;
    background-color: var(--background-fill-primary); /* Gradio's primary background color */
    color: var(--text-color-body); /* Gradio's body text color */
    border-radius: var(--radius-xl);
    margin-bottom: 20px;
}
.banner h1 {
    font-size: 2.5em;
    margin-bottom: 0.2em;
    color: var(--text-color-body);
}
.banner p {
    font-size: 1.1em;
    color: var(--text-color-subdued);
}

/* Styling for flashcards, adapting to Gradio's theme variables */
.flashcard {
    background-color: var(--background-fill-secondary); /* Gradio's secondary background color */
    padding: 1em;
    margin-bottom: 1em;
    border-radius: var(--radius-xl);
    border: 1px solid var(--border-color-primary);
    box-shadow: var(--shadow-drop-lg);
}
.flashcard h4 {
    color: var(--text-color-body);
    margin-top: 0;
    margin-bottom: 0.5em;
}
.flashcard details {
    margin-top: 0.5em;
}
.flashcard summary {
    color: var(--text-color-subdued);
    cursor: pointer;
    font-weight: bold;
}
.flashcard p {
    color: var(--text-color-subdued);
    padding-top: 0.5em;
    margin-bottom: 0;
}
"""

# --- Functions to handle logic for Gradio UI interactions ---

def learn_topic_action(topic):
    """
    Handles the logic for the 'Learn a Topic' tab.
    Fetches summary, links, facts, resources, and generates flashcards.
    Returns multiple outputs for Gradio components.
    """
    summary_output = ""
    books_output = ""
    facts_output = ""
    resources_output = ""
    flashcard_status = ""
    flashcards_data = [] # Data to be stored in session state

    if not topic:
        # Return empty strings for outputs and a message for status
        return "", "", "", "", "Please enter a topic to learn.", []

    # Fetch summary
    # Gradio handles loading indicators automatically when functions are running
    summary = fetch_topic_summary(topic)
    if summary:
        summary_output = f"### πŸ“– Topic Summary\n\n{summary}"
    else:
        summary_output = "Couldn't find that topic. Try again with a simpler keyword."
        # If summary fails, no other data can be fetched, so return early
        return summary_output, "", "", "", "", []

    # Fetch Wikibooks links
    books = fetch_wikibooks_links(topic)
    if books:
        books_output = "### πŸ”— Wikibooks Links\n\n" + "\n".join([f"- πŸ“˜ [{title}]({url})" for title, url in books])
    else:
        books_output = "No related books found on Wikibooks."

    # Fetch Wikidata facts
    facts = fetch_wikidata_facts(topic)
    if facts:
        facts_output = "### πŸ”¬ Wikidata Facts\n\n" + "\n".join([f"- οΏ½ {fact}" for fact in facts])
    else:
        facts_output = "No structured facts found on Wikidata."

    # Fetch Wikiversity resources
    resources = fetch_wikiversity_resources(topic)
    if resources:
        resources_output = "### πŸŽ“ Wikiversity Resources\n\n" + "\n".join([f"- πŸŽ“ [{title}]({link})" for title, link in resources])
    else:
        resources_output = "No learning resources found on Wikiversity."

    # Generate flashcards
    flashcards = generate_flashcards(summary)
    if flashcards:
        flashcards_data = flashcards # Store for session state
        flashcard_status = "βœ… Flashcards generated! Check the Flashcard Review tab."
    else:
        flashcard_status = "⚠️ No flashcards were generated. Try a different topic."

    return summary_output, books_output, facts_output, resources_output, flashcard_status, flashcards_data


def mentor_chat_action(user_query, chat_history):
    """
    Handles the AI Mentor chat functionality.
    Takes user query and current chat history, generates a response, and updates history.
    """
    if not user_query:
        # If query is empty, return current history without modification
        return chat_history, chat_history

    reply = mentor_chat_response(user_query)
    chat_history.append([user_query, reply]) # Gradio Chatbot expects list of [user_msg, bot_msg]
    return chat_history, chat_history # Return updated history for display and state


def flashcard_review_display(flashcards_data):
    """
    Generates HTML for displaying flashcards based on the session state data.
    """
    if not flashcards_data:
        return "<p style='text-align: center; color: var(--text-color-subdued);'>Flashcards will appear here after learning a topic.</p>"

    flashcard_html = ""
    for idx, fc in enumerate(flashcards_data):
        # Apply inline styles for the flashcard to match the Streamlit look
        # These styles are derived from the custom_css block
        flashcard_html += f"""
        <div class='flashcard'>
            <h4>Q{idx+1}: {fc['question']}</h4>
            <details>
                <summary>Show Answer</summary>
                <p>{fc['answer']}</p>
            </details>
        </div>
        """
    return flashcard_html

# --- Gradio UI Layout ---
# Use gr.Blocks for a multi-tab application
with gr.Blocks(theme=theme, title="WikiMentor", css=custom_css) as demo:
    # App Title and Description (mimicking Streamlit's banner)
    gr.HTML("""
    <div class="banner">
        <h1>πŸŽ“ WikiMentor</h1>
        <p>Your AI-Powered Smart Learning Companion</p>
    </div>
    """)

    # Persistent state variables for chat history and flashcards
    # These will maintain their values across different tab interactions within a session
    chat_history_state = gr.State([])
    flashcards_state = gr.State([])

    # Main navigation using Gradio Tabs
    with gr.Tabs() as tabs:
        # --- Learn a Topic Tab ---
        with gr.TabItem("🏫 Learn a Topic", id=0):
            gr.Markdown("## πŸ“˜ Learn a New Topic")
            topic_input = gr.Textbox(label="πŸ” Enter a topic to learn:", placeholder="e.g., Quantum Physics", scale=4)
            learn_button = gr.Button("πŸ“š Fetch Topic Info", scale=1)

            # Output components for the fetched information
            summary_output = gr.Markdown(label="Topic Summary", show_label=False)
            books_output = gr.Markdown(label="Wikibooks Links", show_label=False)
            facts_output = gr.Markdown(label="Wikidata Facts", show_label=False)
            resources_output = gr.Markdown(label="Wikiversity Resources", show_label=False)
            flashcard_status_output = gr.Markdown(label="Flashcard Generation Status", show_label=False)

            # Connect the button click to the learning function
            learn_button.click(
                fn=learn_topic_action,
                inputs=[topic_input],
                outputs=[
                    summary_output,
                    books_output,
                    facts_output,
                    resources_output,
                    flashcard_status_output,
                    flashcards_state # Update the flashcards_state with new data
                ]
            )

        # --- Mentor Chat Tab ---
        with gr.TabItem("πŸ’¬ Mentor Chat", id=1):
            gr.Markdown("## πŸ’¬ Ask Your AI Mentor")
            # Gradio's Chatbot component handles displaying messages
            chatbot = gr.Chatbot(label="Mentor Chat")
            user_query_input = gr.Textbox(label="Type your question:", placeholder="Ask me anything about your topic...")
            send_button = gr.Button("🧠 Get Answer")

            # Connect the send button to the chat function
            # The .then() method is used to clear the input box after sending
            send_button.click(
                fn=mentor_chat_action,
                inputs=[user_query_input, chat_history_state], # Pass user query and current history
                outputs=[chatbot, chat_history_state] # Update chatbot display and history state
            ).then(
                lambda: "", # Function to clear the input
                outputs=[user_query_input]
            )

        # --- Flashcard Review Tab ---
        with gr.TabItem("πŸ—‚οΈ Flashcard Review", id=2):
            gr.Markdown("## πŸ—‚οΈ Flashcard Review Mode")
            gr.Markdown("Use these flashcards to revise and reinforce your memory.")
            flashcard_display_area = gr.HTML(label="Your Flashcards")

            # This function will be called when the tab is selected to display flashcards
            # The `load` event of the `Blocks` interface can trigger this when the tab changes
            tabs.select(
                fn=flashcard_review_display,
                inputs=[flashcards_state], # Pass the current flashcards data
                outputs=[flashcard_display_area],
                # This ensures the flashcards are updated when the tab is switched to
                # only if the selected tab is the flashcard review tab
                # condition=lambda selected_tab: selected_tab == 2 # This condition can be tricky with tabs.select
            )
            # A simpler way to ensure flashcards are loaded when the tab is first viewed
            # or when the app loads:
            demo.load(
                fn=flashcard_review_display,
                inputs=[flashcards_state],
                outputs=[flashcard_display_area]
            )


# Launch the Gradio application
# server_name="0.0.0.0" and server_port=7860 are crucial for Hugging Face Spaces deployment
demo.launch(server_name="0.0.0.0", server_port=7860)