File size: 14,893 Bytes
08762b8
 
 
 
 
 
 
 
 
 
aab269f
 
 
 
08762b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aab269f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
08762b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
aab269f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
08762b8
 
 
 
 
 
 
 
 
 
 
 
 
aab269f
 
08762b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
import streamlit as st
import sys
from io import StringIO
from contextlib import redirect_stdout
import json
from datetime import datetime
from typing import Dict, List, Optional
import base64
from PIL import Image
import io
import os
import zipfile
from pathlib import Path
import shutil

class CodePlayground:
    def __init__(self):
        self.initialize_session_state()

    @staticmethod
    def initialize_session_state():
        """Initialize session state for artifacts and history"""
        if 'artifacts' not in st.session_state:
            st.session_state.artifacts = []
        if 'code_history' not in st.session_state:
            st.session_state.code_history = []
        if 'current_artifact' not in st.session_state:
            st.session_state.current_artifact = None

    def display(self):
        """Display the enhanced code playground interface"""
        st.header("Interactive Learning Playground")
        
        # Create tabs for different views
        code_tab, artifacts_tab = st.tabs(["Code Editor", "Learning Artifacts"])
        
        with code_tab:
            self.display_code_editor()
        
        with artifacts_tab:
            self.display_artifacts()

    def display_code_editor(self):
        """Display the code editor interface"""
        # Code editor
        code = st.text_area(
            "Write your Python code here",
            height=200,
            key="code_editor",
            help="Write Python code to execute. You can use print() to see outputs."
        )
        
        # Button columns
        col1, col2, col3 = st.columns(3)
        
        with col1:
            if st.button("Run Code", key="run_code"):
                output, artifact_data = self.execute_code(code)
                if artifact_data:
                    self.store_artifact(artifact_data)
        
        with col2:
            if st.button("Save as Artifact", key="save_artifact"):
                self.create_manual_artifact(code)
        
        with col3:
            if st.button("Clear Output", key="clear_output"):
                if 'code_output' in st.session_state:
                    del st.session_state.code_output

        # Display output
        if 'code_output' in st.session_state:
            st.markdown("### Output:")
            st.code(st.session_state.code_output)

    def display_artifacts(self):
        """Display stored learning artifacts"""
        st.subheader("Learning Artifacts")
        
        if not st.session_state.artifacts:
            st.info("No artifacts created yet. Run some code or create manual artifacts to see them here!")
            return
        
        # Filter options
        filter_type = st.selectbox(
            "Filter by type",
            options=["All", "Code", "Image", "Text", "Mixed Media"],
            key="artifact_filter"
        )
        
        # Export buttons
        col1, col2 = st.columns(2)
        with col1:
            if st.button("Export All Artifacts"):
                try:
                    # Use temporary directory for export
                    temp_dir = Path("temp_export")
                    temp_dir.mkdir(exist_ok=True)
                    
                    zip_path = self.export_all_artifacts(str(temp_dir))
                    
                    # Read the zip file
                    with open(zip_path, "rb") as fp:
                        zip_data = fp.read()
                        
                    # Create download button
                    st.download_button(
                        label="Download Artifacts",
                        data=zip_data,
                        file_name="learning_artifacts.zip",
                        mime="application/zip"
                    )
                    st.success("Artifacts exported successfully!")
                except Exception as e:
                    st.error(f"Error exporting artifacts: {str(e)}")
                finally:
                    # Cleanup
                    if temp_dir.exists():
                        shutil.rmtree(temp_dir)
        
        # Display artifacts in reverse chronological order
        for artifact in reversed(st.session_state.artifacts):
            if filter_type == "All" or artifact['type'] == filter_type:
                with st.expander(f"{artifact['title']} - {artifact['date']}", expanded=False):
                    st.write(f"**Type:** {artifact['type']}")
                    st.write(f"**Created:** {artifact['date']}")
                    
                    # Display content based on type
                    if artifact['type'] == "Code":
                        st.code(artifact['content'], language="python")
                    elif artifact['type'] == "Image":
                        st.image(base64.b64decode(artifact['content']))
                    elif artifact['type'] == "Text":
                        st.write(artifact['content'])
                    elif artifact['type'] == "Mixed Media":
                        for item in artifact['content']:
                            if item['type'] == "code":
                                st.code(item['data'], language="python")
                            elif item['type'] == "image":
                                st.image(base64.b64decode(item['data']))
                            elif item['type'] == "text":
                                st.write(item['data'])
                    
                    # Artifact actions
                    col1, col2, col3 = st.columns(3)
                    with col1:
                        if st.button("Load in Editor", key=f"load_{artifact['id']}"):
                            if artifact['type'] == "Code":
                                st.session_state.code_editor = artifact['content']
                                st.rerun()
                    
                    with col2:
                        if st.button("Export", key=f"export_{artifact['id']}"):
                            try:
                                # Use temporary directory for single artifact export
                                temp_dir = Path("temp_export_single")
                                temp_dir.mkdir(exist_ok=True)
                                
                                # Export the artifact
                                self.export_artifact(artifact, str(temp_dir))
                                
                                # Create zip file
                                zip_path = temp_dir / f"artifact_{artifact['id']}.zip"
                                with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                                    for root, _, files in os.walk(temp_dir):
                                        for file in files:
                                            if file != f"artifact_{artifact['id']}.zip":
                                                file_path = Path(root) / file
                                                arcname = file_path.relative_to(temp_dir)
                                                zipf.write(file_path, arcname)
                                
                                # Read the zip file
                                with open(zip_path, "rb") as fp:
                                    zip_data = fp.read()
                                    
                                # Create download button
                                st.download_button(
                                    label=f"Download Artifact {artifact['id']}",
                                    data=zip_data,
                                    file_name=f"artifact_{artifact['id']}.zip",
                                    mime="application/zip",
                                    key=f"download_{artifact['id']}"
                                )
                                st.success(f"Artifact {artifact['id']} exported successfully!")
                            except Exception as e:
                                st.error(f"Error exporting artifact: {str(e)}")
                            finally:
                                # Cleanup
                                if temp_dir.exists():
                                    shutil.rmtree(temp_dir)
                    
                    with col3:
                        if st.button("Delete", key=f"delete_{artifact['id']}"):
                            self.delete_artifact(artifact['id'])
                            st.rerun()

    def export_artifact(self, artifact: Dict, export_dir: str):
        """Export a single artifact to the specified directory"""
        # Create directory if it doesn't exist
        artifact_dir = Path(export_dir) / str(artifact['id'])
        artifact_dir.mkdir(parents=True, exist_ok=True)

        # Export metadata
        metadata = {
            'id': artifact['id'],
            'title': artifact['title'],
            'type': artifact['type'],
            'date': artifact['date']
        }
        
        with open(artifact_dir / 'metadata.json', 'w') as f:
            json.dump(metadata, f, indent=2)

        # Export content based on type
        if artifact['type'] == "Code":
            with open(artifact_dir / 'content.py', 'w') as f:
                f.write(artifact['content'])
        
        elif artifact['type'] == "Image":
            img_data = base64.b64decode(artifact['content'])
            with open(artifact_dir / 'content.png', 'wb') as f:
                f.write(img_data)
        
        elif artifact['type'] == "Text":
            with open(artifact_dir / 'content.txt', 'w') as f:
                f.write(artifact['content'])
        
        elif artifact['type'] == "Mixed Media":
            media_dir = artifact_dir / 'content'
            media_dir.mkdir(exist_ok=True)
            
            for idx, item in enumerate(artifact['content']):
                if item['type'] == 'code':
                    with open(media_dir / f'code_{idx}.py', 'w') as f:
                        f.write(item['data'])
                elif item['type'] == 'image':
                    img_data = base64.b64decode(item['data'])
                    with open(media_dir / f'image_{idx}.png', 'wb') as f:
                        f.write(img_data)
                elif item['type'] == 'text':
                    with open(media_dir / f'text_{idx}.txt', 'w') as f:
                        f.write(item['data'])

    def export_all_artifacts(self, export_dir: str):
        """Export all artifacts to a directory and create a zip file"""
        # Create temporary directory for exports
        temp_dir = Path(export_dir) / 'temp_artifacts'
        temp_dir.mkdir(parents=True, exist_ok=True)

        try:
            # Export each artifact
            for artifact in st.session_state.artifacts:
                self.export_artifact(artifact, str(temp_dir))

            # Create zip file
            zip_path = Path(export_dir) / 'learning_artifacts.zip'
            with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
                for root, _, files in os.walk(temp_dir):
                    for file in files:
                        file_path = Path(root) / file
                        arcname = file_path.relative_to(temp_dir)
                        zipf.write(file_path, arcname)

            return str(zip_path)
        finally:
            # Clean up temporary directory
            shutil.rmtree(temp_dir)

    def execute_code(self, code: str) -> tuple:
        """Execute code and capture outputs"""
        output = StringIO()
        artifact_data = None
        
        try:
            # Create a restricted environment
            globals_dict = {
                '__builtins__': __builtins__,
                'print': print,
                'Image': Image,
                'io': io,
                'base64': base64,
                'create_artifact': create_artifact
            }
            
            # Capture stdout
            with redirect_stdout(output):
                # Execute the code
                exec(code, globals_dict, {})
            
            # Store output
            output_text = output.getvalue()
            st.session_state.code_output = output_text
            
            # Check for generated artifacts in globals
            if '_artifact_output' in globals_dict:
                artifact_data = globals_dict['_artifact_output']
            
            # Store in history
            self.store_in_history(code, output_text)
            
            return output_text, artifact_data
            
        except Exception as e:
            error_msg = f"Error: {str(e)}"
            st.session_state.code_output = error_msg
            return error_msg, None

    def store_artifact(self, artifact_data: Dict):
        """Store a new artifact"""
        artifact = {
            'id': len(st.session_state.artifacts),
            'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            **artifact_data
        }
        st.session_state.artifacts.append(artifact)
        st.success(f"Created new artifact: {artifact['title']}")

    def create_manual_artifact(self, code: str):
        """Create a manual artifact from current code"""
        title = st.text_input("Artifact Title")
        if title:
            artifact = {
                'id': len(st.session_state.artifacts),
                'title': title,
                'type': "Code",
                'content': code,
                'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            }
            st.session_state.artifacts.append(artifact)
            st.success(f"Created new artifact: {title}")

    def delete_artifact(self, artifact_id: int):
        """Delete an artifact"""
        st.session_state.artifacts = [
            a for a in st.session_state.artifacts if a['id'] != artifact_id
        ]

    def store_in_history(self, code: str, output: str):
        """Store code execution in history"""
        history_entry = {
            'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            'code': code,
            'output': output
        }
        st.session_state.code_history.append(history_entry)

def create_artifact(title: str, content: any, artifact_type: str = "Mixed Media"):
    """
    Create an artifact from code execution.
    Usage example in code:
    
    # Create a text artifact
    create_artifact("My Text", "Hello World", "Text")
    
    # Create an image artifact
    img = Image.new('RGB', (100, 100), color='red')
    buffered = io.BytesIO()
    img.save(buffered, format="PNG")
    img_str = base64.b64encode(buffered.getvalue()).decode()
    create_artifact("My Image", img_str, "Image")
    """
    globals()['_artifact_output'] = {
        'title': title,
        'type': artifact_type,
        'content': content
    }