pydeploy-studio / app.py
codeboosterstech's picture
Update app.py
7be7a2c verified
"""
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 = '<div class="error-message"><h4>Validation Errors:</h4><ul>'
for error in errors:
error_html += f'<li>{error}</li>'
error_html += '</ul></div>'
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'''
<div class="status-message {msg_type}">
<span style="font-weight: bold; color: {colors[msg_type]}; margin-right: 8px;">
{icons[msg_type]}
</span>
<span>{message}</span>
</div>
'''
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('''
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
''')
# Header with branding
header_html = '''
<div class="header-main">
<div class="header-content">
<h1>PyDeploy Studio</h1>
<div class="subtitle">Professional Python to Gradio Application Converter</div>
<div style="color: #fbbf24; margin-top: 0.5rem;">
<span>Created by</span>
<a href="https://linkedin.com/in/cbvk28" target="_blank" style="color: #fbbf24; font-weight: 600; text-decoration: none;">
Veerakumar C B
</a>
<span>Streamline your deployment workflow</span>
</div>
</div>
</div>
'''
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 = '''
<div class="sidebar-title">Workflow Steps</div>
<div class="step-container">
<div class="step-item active">
<div class="step-number">1</div>
<div class="step-content">
<div class="step-title">Input Code</div>
<div class="step-description">Upload or paste your Python code</div>
</div>
</div>
<div class="step-item">
<div class="step-number">2</div>
<div class="step-content">
<div class="step-title">Generate Files</div>
<div class="step-description">Download app.py, requirements.txt & README</div>
</div>
</div>
<div class="step-item">
<div class="step-number">3</div>
<div class="step-content">
<div class="step-title">Deploy</div>
<div class="step-description">Deploy to Hugging Face Spaces</div>
</div>
</div>
</div>
<div class="tips-container">
<div class="tips-title">Quick Tips</div>
<div class="tip-item">
<span class="tip-bullet">•</span>
<span class="tip-text">Ensure your code has clear functions for best conversion</span>
</div>
<div class="tip-item">
<span class="tip-bullet">•</span>
<span class="tip-text">API keys are used securely and never stored</span>
</div>
<div class="tip-item">
<span class="tip-bullet">•</span>
<span class="tip-text">Test locally before deploying to production</span>
</div>
</div>
'''
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('<div class="step-header">Step 1: Provide Your Code</div>')
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('<div class="step-header" style="margin-top: 2rem;">Step 2: API Configuration</div>')
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('<div class="step-header">Step 2: Generated Files</div>')
gr.Markdown("### Your Gradio Application is Ready!")
# File Cards Grid
file_cards_html = '''
<div class="file-grid">
<div class="file-card">
<div class="file-card-title">app.py</div>
<div class="file-card-description">Main application file with Gradio interface</div>
</div>
<div class="file-card">
<div class="file-card-title">requirements.txt</div>
<div class="file-card-description">Python dependencies for your application</div>
</div>
<div class="file-card">
<div class="file-card-title">README.md</div>
<div class="file-card-description">Documentation with YAML frontmatter</div>
</div>
</div>
'''
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('<div class="step-header" style="margin-top: 2rem;">Step 3: Deployment Options</div>')
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('<div class="step-header">Step 3: Deployment Complete 🎉</div>')
success_html = '''
<div style="text-align: center; padding: 2rem;">
<div style="font-size: 4rem; margin-bottom: 1rem;">✅</div>
<h2 style="color: #16a34a; margin-bottom: 1rem;">Deployment Successful!</h2>
<p style="color: #6b7280; margin-bottom: 2rem;">Your application is now live on Hugging Face Spaces</p>
</div>
'''
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 = '''
<div class="footer">
<p>PyDeploy Studio • Created by <a href="https://linkedin.com/in/cbvk28" target="_blank">Veerakumar C B</a></p>
<p style="font-size: 0.8rem; color: #9ca3af; margin-top: 0.5rem;">Transform your Python code into production-ready applications</p>
</div>
'''
gr.HTML(footer_html)
# JavaScript for updating steps
js_code = """
<script>
function updateSteps(step) {
const steps = document.querySelectorAll('.step-item');
steps.forEach((stepEl, index) => {
stepEl.classList.remove('active');
if (index === step - 1) {
stepEl.classList.add('active');
}
});
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateSteps(1);
});
</script>
"""
gr.HTML(js_code)
# Event handlers
def update_step_display(step):
return f"""
<script>
updateSteps({step});
</script>
"""
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
)