""" Main application file for PyDeploy Studio Professional Gradio application for converting Python code to deployable Gradio apps Created by Veerakumar C B """ import gradio as gr import tempfile import os import sys from pathlib import Path # Add utils and components to path sys.path.append(str(Path(__file__).parent)) from utils.converter import convert_code from utils.deployer import deploy_to_space from utils.file_handler import ( create_zip_archive, read_file_content, parse_notebook, save_individual_file ) from utils.validator import validate_inputs from components.header import create_header from components.sidebar import create_sidebar from components.api_guide import create_api_guide from components.file_downloader import create_file_download_section # Constants MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB SUPPORTED_EXTENSIONS = ['.py', '.ipynb', '.txt'] class SpaceCreatorApp: """Main application class for PyDeploy Studio""" def __init__(self): self.temp_files = [] self.current_step = 1 self.generated_files = {} def cleanup(self): """Clean up temporary files""" for file_path in self.temp_files: try: if os.path.exists(file_path): os.remove(file_path) except: pass self.temp_files = [] def process_step1(self, input_text, input_file, groq_api_key): """Step 1: Convert code to Gradio app""" try: # Validate inputs validation_errors = validate_inputs(groq_api_key, None, None, "Convert Only") if validation_errors: return None, None, None, None, self._format_errors(validation_errors), 1 # Get source code code_content = self._extract_code(input_text, input_file) if not code_content.strip(): return None, None, None, None, "Please provide Python code to convert.", 1 # Convert code conversion_result = convert_code(code_content, groq_api_key) # Store generated files self.generated_files = { "app.py": conversion_result["app_py"], "requirements.txt": conversion_result["requirements_txt"], "README.md": conversion_result["readme_md"] } # Create individual files for download file_paths = {} for filename, content in self.generated_files.items(): file_path = save_individual_file(filename, content) self.temp_files.append(file_path) file_paths[filename] = file_path status = self._create_status_message("success", "Code conversion successful!") return (file_paths["app.py"], file_paths["requirements.txt"], file_paths["README.md"], None, status, 2) except Exception as e: error_msg = self._create_status_message("error", f"Conversion failed: {str(e)}") return None, None, None, None, error_msg, 1 def process_step2(self, hf_token, space_name, deploy_mode): """Step 2: Deploy to Hugging Face""" try: if deploy_mode != "Deploy to Hugging Face Space": status = self._create_status_message("info", "Skipping deployment as requested.") return None, status, 2 # Validate deployment inputs validation_errors = validate_inputs(None, hf_token, space_name, "Deploy") if validation_errors: return None, self._format_errors(validation_errors), 2 # Deploy to Hugging Face deploy_url = deploy_to_space(hf_token, space_name, self.generated_files) # Create zip archive of all files zip_bytes = create_zip_archive(self.generated_files) zip_path = save_individual_file("gradio_app_full_package.zip", zip_bytes, binary=True) self.temp_files.append(zip_path) status = self._create_status_message("success", f"Deployment successful! Space URL: {deploy_url}") return zip_path, status, 3 except Exception as e: error_msg = self._create_status_message("error", f"Deployment failed: {str(e)}") return None, error_msg, 2 def _extract_code(self, input_text, input_file): """Extract code from text or file""" code_content = "" if input_file is not None: file_content = read_file_content(input_file) if input_file.name.endswith('.ipynb') if hasattr(input_file, 'name') else False: with tempfile.NamedTemporaryFile(mode='w', suffix='.ipynb', delete=False, encoding='utf-8') as tmp: tmp.write(file_content) tmp_path = tmp.name try: code_content = parse_notebook(tmp_path) finally: os.unlink(tmp_path) else: code_content = file_content elif input_text.strip(): code_content = input_text return code_content def _format_errors(self, errors): """Format validation errors""" error_html = '

Validation Errors:

' return error_html def _create_status_message(self, msg_type, message): """Create formatted status message""" icons = { "success": "✓", "error": "✗", "warning": "⚠", "info": "ℹ" } colors = { "success": "#16a34a", "error": "#dc2626", "warning": "#d97706", "info": "#2563eb" } return f'''
{icons[msg_type]} {message}
''' def create_app(): """Create the Gradio application interface""" app = SpaceCreatorApp() # Custom theme with yellow and elephant grey theme = gr.themes.Base( primary_hue="yellow", secondary_hue="gray", neutral_hue="gray", font=("Inter", "Segoe UI", "Roboto", "Helvetica", "Arial", "sans-serif") ) with gr.Blocks( title="PyDeploy Studio", theme=theme, css=""" /* PyDeploy Studio CSS - Yellow & Elephant Grey Theme */ .gradio-container { max-width: 1300px !important; margin: 20px auto !important; font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #f8f9fa !important; } body { background: #f1f3f4 !important; } /* Header with gradient */ .header-main { background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%); color: white; padding: 2.5rem 2rem; border-radius: 12px; margin-bottom: 24px; box-shadow: 0 4px 20px rgba(0,0,0,0.1); position: relative; overflow: hidden; } .header-main::before { content: ''; position: absolute; top: 0; right: 0; width: 200px; height: 200px; background: linear-gradient(45deg, #fbbf24 0%, transparent 70%); opacity: 0.1; border-radius: 50%; } .header-content { position: relative; z-index: 2; } .header-content h1 { margin: 0 0 0.5rem 0; font-size: 2.5rem; font-weight: 700; background: linear-gradient(90deg, #fbbf24 0%, #f59e0b 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .header-content .subtitle { font-size: 1.1rem; color: #cbd5e1; margin-bottom: 1rem; } .creator-info { display: flex; align-items: center; gap: 10px; font-size: 0.9rem; color: #fbbf24; /* Changed to yellow */ margin-top: 0.5rem; } .creator-info a { color: #fbbf24; text-decoration: none; font-weight: 500; transition: color 0.2s; } .creator-info a:hover { color: #f59e0b; text-decoration: underline; } .creator-info .linkedin-icon { color: #0077b5; } /* Main Layout */ .main-wrapper { display: flex; gap: 24px; margin-top: 20px; } /* Sidebar */ .sidebar-container { flex: 0 0 300px; background: white; padding: 1.75rem; border-radius: 12px; border: 1px solid #e5e7eb; box-shadow: 0 2px 10px rgba(0,0,0,0.05); height: fit-content; } .sidebar-title { font-size: 1.1rem; font-weight: 600; color: #374151; margin-bottom: 1.5rem; padding-bottom: 0.75rem; border-bottom: 2px solid #fbbf24; } /* Steps */ .step-container { margin-bottom: 2rem; } .step-item { display: flex; align-items: flex-start; gap: 1rem; padding: 1.25rem; margin-bottom: 0.75rem; background: #f9fafb; border-radius: 10px; border: 1px solid #e5e7eb; transition: all 0.3s ease; cursor: pointer; } .step-item:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.08); } .step-item.active { background: linear-gradient(135deg, #fef3c7 0%, #fffbeb 100%); border-color: #fbbf24; border-left: 4px solid #fbbf24; } .step-number { width: 32px; height: 32px; background: #e5e7eb; color: #6b7280; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 0.9rem; flex-shrink: 0; transition: all 0.3s ease; } .step-item.active .step-number { background: #fbbf24; color: #1f2937; } .step-content { flex: 1; } .step-title { font-weight: 600; color: #374151; margin-bottom: 0.25rem; } .step-item.active .step-title { color: #1f2937; } .step-description { font-size: 0.875rem; color: #6b7280; line-height: 1.5; } /* Tips Box */ .tips-container { background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); border: 1px solid #e2e8f0; border-radius: 10px; padding: 1.5rem; } .tips-title { font-size: 1rem; font-weight: 600; color: #374151; margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; } .tips-title::before { content: "💡"; } .tip-item { display: flex; align-items: flex-start; gap: 0.75rem; margin-bottom: 0.875rem; } .tip-item:last-child { margin-bottom: 0; } .tip-bullet { color: #fbbf24; font-weight: bold; flex-shrink: 0; margin-top: 0.125rem; } .tip-text { font-size: 0.875rem; color: #4b5563; line-height: 1.5; } /* Main Content */ .content-container { flex: 1; background: white; padding: 2rem; border-radius: 12px; border: 1px solid #e5e7eb; box-shadow: 0 2px 10px rgba(0,0,0,0.05); } /* Step Groups */ .step-group { margin-bottom: 2rem; } .step-header { font-size: 1.5rem; font-weight: 600; color: #1f2937; margin-bottom: 1.5rem; padding-bottom: 0.75rem; border-bottom: 2px solid #fbbf24; } /* Form Elements */ .form-group { margin-bottom: 1.5rem; } .form-label { display: block; margin-bottom: 0.5rem; font-weight: 500; color: #374151; font-size: 0.95rem; } .form-info { font-size: 0.85rem; color: #6b7280; margin-top: 0.25rem; } input, textarea, select { width: 100%; padding: 0.875rem 1rem; border: 1px solid #d1d5db; border-radius: 8px; font-size: 0.95rem; font-family: 'Inter', sans-serif; transition: all 0.2s ease; color: #374151; } input:focus, textarea:focus, select:focus { outline: none; border-color: #fbbf24; box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.1); } input::placeholder, textarea::placeholder { color: #9ca3af; } /* Buttons */ button { font-family: 'Inter', sans-serif !important; font-weight: 500 !important; font-size: 0.95rem !important; padding: 0.875rem 1.75rem !important; border-radius: 8px !important; transition: all 0.3s ease !important; border: none !important; } button.primary { background: linear-gradient(135deg, #fbbf24 0%, #f59e0b 100%) !important; color: #1f2937 !important; font-weight: 600 !important; } button.primary:hover { transform: translateY(-2px) !important; box-shadow: 0 4px 12px rgba(251, 191, 36, 0.3) !important; } button.secondary { background: white !important; color: #4b5563 !important; border: 1px solid #d1d5db !important; } button.secondary:hover { background: #f9fafb !important; border-color: #9ca3af !important; } /* Tabs */ .tab-nav { border-bottom: 1px solid #e5e7eb !important; margin-bottom: 1.5rem !important; } .tab-nav button { padding: 0.75rem 1.5rem !important; background: none !important; border: none !important; border-bottom: 3px solid transparent !important; color: #6b7280 !important; border-radius: 0 !important; } .tab-nav button.selected { color: #1f2937 !important; border-bottom-color: #fbbf24 !important; font-weight: 600 !important; } /* Code Editor */ #code_editor { font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace !important; font-size: 0.9rem !important; line-height: 1.6 !important; background: #f8fafc !important; border: 1px solid #e2e8f0 !important; border-radius: 8px !important; padding: 1.25rem !important; } /* Status Messages */ .status-message { padding: 1rem 1.25rem; border-radius: 8px; margin: 1.25rem 0; border-left: 4px solid; background: #f8fafc; font-size: 0.95rem; display: flex; align-items: flex-start; gap: 0.75rem; } .status-message.success { border-left-color: #16a34a; background: #f0fdf4; } .status-message.error { border-left-color: #dc2626; background: #fef2f2; } .status-message.warning { border-left-color: #d97706; background: #fffbeb; } .status-message.info { border-left-color: #2563eb; background: #eff6ff; } /* File Cards */ .file-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem; margin: 1.5rem 0; } .file-card { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px; padding: 1.5rem; transition: all 0.3s ease; } .file-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.1); border-color: #fbbf24; } .file-card-title { font-weight: 600; color: #1f2937; margin-bottom: 0.75rem; font-size: 1.1rem; } .file-card-description { font-size: 0.875rem; color: #6b7280; margin-bottom: 1rem; } /* Error Message */ .error-message { background: linear-gradient(135deg, #fef2f2 0%, #fff5f5 100%); border: 1px solid #fecaca; border-radius: 10px; padding: 1.5rem; margin: 1.5rem 0; } .error-message h4 { margin: 0 0 0.75rem 0; color: #dc2626; font-size: 1.1rem; display: flex; align-items: center; gap: 0.5rem; } .error-message h4::before { content: "⚠"; } .error-message ul { margin: 0; padding-left: 1.5rem; } .error-message li { margin-bottom: 0.5rem; color: #7f1d1d; } /* Radio Group */ .radio-group { display: flex; gap: 2rem; margin: 1.5rem 0; } .radio-item { display: flex; align-items: center; gap: 0.5rem; } /* Footer */ .footer { text-align: center; padding: 1.5rem; margin-top: 2rem; color: #6b7280; font-size: 0.875rem; border-top: 1px solid #e5e7eb; } .footer a { color: #f59e0b; text-decoration: none; } .footer a:hover { text-decoration: underline; } /* Responsive Design */ @media (max-width: 1024px) { .main-wrapper { flex-direction: column; } .sidebar-container { width: 100%; } .gradio-container { padding: 10px !important; } } @media (max-width: 768px) { .header-content h1 { font-size: 2rem; } .content-container { padding: 1.5rem; } .file-grid { grid-template-columns: 1fr; } .radio-group { flex-direction: column; gap: 1rem; } } /* Animation for active step */ @keyframes pulse { 0% { transform: scale(1); } 50% { transform: scale(1.05); } 100% { transform: scale(1); } } .step-item.active { animation: pulse 2s infinite; } """ ) as demo: # Add fonts gr.HTML(''' ''') # Header with branding header_html = '''

PyDeploy Studio

Professional Python to Gradio Application Converter
Created by Veerakumar C B Streamline your deployment workflow
''' gr.HTML(header_html) with gr.Row(elem_classes="main-wrapper"): # Sidebar with gr.Column(scale=1, elem_classes="sidebar-container"): current_step = gr.State(1) # Steps steps_html = '''
1
Input Code
Upload or paste your Python code
2
Generate Files
Download app.py, requirements.txt & README
3
Deploy
Deploy to Hugging Face Spaces
Quick Tips
Ensure your code has clear functions for best conversion
API keys are used securely and never stored
Test locally before deploying to production
''' gr.HTML(steps_html) # Main Content with gr.Column(scale=3, elem_classes="content-container"): # Step 1: Code Input with gr.Group(visible=True) as step1_group: gr.Markdown('
Step 1: Provide Your Code
') with gr.Tabs(): with gr.Tab("📁 Upload File"): file_input = gr.File( label="Upload Python or Jupyter Notebook", file_types=SUPPORTED_EXTENSIONS, type="filepath", elem_classes="form-group" ) with gr.Tab("📝 Paste Code"): text_input = gr.Textbox( label="Python Code", lines=15, placeholder="# Paste your Python code here...\n# Example: Machine Learning model, Data processing function, etc.\n\nimport numpy as np\n\ndef process_data(input_data):\n # Your code here\n return processed_result", elem_id="code_editor" ) gr.Markdown('
Step 2: API Configuration
') with gr.Row(): with gr.Column(): groq_api_key = gr.Textbox( label="Groq API Key", type="password", placeholder="gsk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", info="Required for AI-powered conversion. Get yours from console.groq.com", elem_classes="form-group" ) # API Guide with gr.Accordion("🔑 Need help creating API keys?", open=False): with gr.Tabs(): with gr.Tab("Groq API Key"): gr.Markdown(""" ### Creating a Groq API Key 1. Visit [console.groq.com](https://console.groq.com) 2. Sign up or log in to your account 3. Navigate to **API Keys** section 4. Click **Create API Key** 5. Give your key a descriptive name 6. Copy the generated key (starts with `gsk_`) 7. Paste it in the field above *Note: Keep your API key secure and never share it publicly.* """) with gr.Tab("Hugging Face Token"): gr.Markdown(""" ### Creating a Hugging Face Token 1. Go to [huggingface.co](https://huggingface.co) 2. Click on your profile picture → **Settings** 3. Navigate to **Access Tokens** 4. Click **New Token** 5. Select token type: **Write** 6. Copy the generated token (starts with `hf_`) 7. Paste it in Step 2 *Write token is required for creating and updating Spaces.* """) convert_btn = gr.Button( "🚀 Convert Code", variant="primary", size="lg", elem_classes="mt-3" ) # Step 2: Generated Files with gr.Group(visible=False) as step2_group: gr.Markdown('
Step 2: Generated Files
') gr.Markdown("### Your Gradio Application is Ready!") # File Cards Grid file_cards_html = '''
app.py
Main application file with Gradio interface
requirements.txt
Python dependencies for your application
README.md
Documentation with YAML frontmatter
''' gr.HTML(file_cards_html) with gr.Row(): with gr.Column(): app_py_download = gr.File( label="Download app.py", file_types=[".py"], interactive=False ) with gr.Column(): requirements_download = gr.File( label="Download requirements.txt", file_types=[".txt"], interactive=False ) with gr.Column(): readme_download = gr.File( label="Download README.md", file_types=[".md"], interactive=False ) gr.Markdown('
Step 3: Deployment Options
') with gr.Row(): with gr.Column(): hf_token = gr.Textbox( label="Hugging Face Token", type="password", placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", info="Write token required for deployment", elem_classes="form-group" ) with gr.Column(): space_name = gr.Textbox( label="Space Name", placeholder="my-awesome-gradio-app", info="3-30 chars, lowercase letters, numbers, and hyphens only", elem_classes="form-group" ) gr.Markdown("### Select Deployment Action") deploy_mode = gr.Radio( choices=["📥 Download Only", "🚀 Deploy to Hugging Face Space"], label="", value="📥 Download Only", elem_classes="radio-group" ) with gr.Row(): back_btn_step2 = gr.Button("← Back", variant="secondary") deploy_btn = gr.Button("Proceed to Deployment →", variant="primary") # Step 3: Deployment Results with gr.Group(visible=False) as step3_group: gr.Markdown('
Step 3: Deployment Complete 🎉
') success_html = '''

Deployment Successful!

Your application is now live on Hugging Face Spaces

''' gr.HTML(success_html) with gr.Row(): with gr.Column(scale=2): gr.Markdown("### Download Complete Package") full_package_download = gr.File( label="gradio_app_full_package.zip", file_types=[".zip"], interactive=False ) gr.Markdown("*Contains all files ready for deployment*") with gr.Column(scale=1): gr.Markdown("### Next Steps") gr.Markdown(""" 1. **Visit** your Space on Hugging Face 2. **Monitor** deployment logs 3. **Share** your app with others 4. **Customize** further if needed """) deployment_status = gr.Markdown() with gr.Row(): back_btn_step3 = gr.Button("🔄 Start New Project", variant="secondary") finish_btn = gr.Button("🎯 Finish", variant="primary") # Status output status_output = gr.HTML( value='', elem_id="status_output" ) # Footer footer_html = ''' ''' gr.HTML(footer_html) # JavaScript for updating steps js_code = """ """ gr.HTML(js_code) # Event handlers def update_step_display(step): return f""" """ convert_btn.click( fn=app.process_step1, inputs=[text_input, file_input, groq_api_key], outputs=[ app_py_download, requirements_download, readme_download, full_package_download, status_output, current_step ] ).then( fn=lambda step: (gr.update(visible=step==1), gr.update(visible=step==2), gr.update(visible=step==3)), inputs=[current_step], outputs=[step1_group, step2_group, step3_group] ).then( fn=update_step_display, inputs=[current_step], outputs=[status_output] ) deploy_btn.click( fn=app.process_step2, inputs=[hf_token, space_name, deploy_mode], outputs=[full_package_download, status_output, current_step] ).then( fn=lambda step: (gr.update(visible=step==1), gr.update(visible=step==2), gr.update(visible=step==3)), inputs=[current_step], outputs=[step1_group, step2_group, step3_group] ).then( fn=update_step_display, inputs=[current_step], outputs=[status_output] ) back_btn_step2.click( fn=lambda: (1, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)), outputs=[current_step, step1_group, step2_group, step3_group] ).then( fn=update_step_display, inputs=[current_step], outputs=[status_output] ) back_btn_step3.click( fn=lambda: (1, gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)), outputs=[current_step, step1_group, step2_group, step3_group] ).then( fn=app.cleanup, inputs=None, outputs=None ).then( fn=update_step_display, inputs=[current_step], outputs=[status_output] ) finish_btn.click( fn=app.cleanup, inputs=None, outputs=None ) return demo if __name__ == "__main__": print("=" * 60) print("PyDeploy Studio - Professional Deployment Platform") print("Created by Veerakumar C B") print("=" * 60) print("Starting server...") print(f"Local URL: http://localhost:7860") print("=" * 60) app = create_app() app.launch( server_name="0.0.0.0", server_port=7860, share=False, debug=False, favicon_path=None, show_error=True )