broadfield-dev commited on
Commit
8bd1285
·
verified ·
1 Parent(s): 716c000

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +275 -0
app.py ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import re
4
+ import tempfile
5
+ import shutil
6
+ import git
7
+ from huggingface_hub import (
8
+ create_repo,
9
+ upload_folder,
10
+ list_repo_files,
11
+ delete_file,
12
+ Repository,
13
+ whoami,
14
+ )
15
+ from huggingface_hub.utils import HfHubHTTPError
16
+ import logging
17
+ from pathlib import Path
18
+
19
+ # Configure logging
20
+ logging.basicConfig(level=logging.INFO)
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Function to parse markdown input
24
+ def parse_markdown(markdown_input):
25
+ """Parse markdown input to extract space details and file structure."""
26
+ space_info = {"repo_name": "", "owner": "", "sdk": "gradio", "files": []}
27
+ current_file = None
28
+ file_content = []
29
+ in_file_content = False
30
+
31
+ lines = markdown_input.strip().split("\n")
32
+ for line in lines:
33
+ line = line.strip()
34
+ # Extract space name
35
+ if line.startswith("# Space:"):
36
+ space_info["repo_name"] = line.replace("# Space:", "").strip()
37
+ if "/" in space_info["repo_name"]:
38
+ space_info["owner"], space_info["repo_name"] = space_info["repo_name"].split("/", 1)
39
+ # Detect file structure section
40
+ elif line.startswith("## File Structure"):
41
+ continue
42
+ # Detect file in structure
43
+ elif line.startswith("📄") or line.startswith("📁"):
44
+ if current_file and file_content:
45
+ space_info["files"].append({"path": current_file, "content": "\n".join(file_content)})
46
+ file_content = []
47
+ current_file = line[2:].strip()
48
+ in_file_content = False
49
+ # Detect file content section
50
+ elif line.startswith("### File:"):
51
+ if current_file and file_content:
52
+ space_info["files"].append({"path": current_file, "content": "\n".join(file_content)})
53
+ file_content = []
54
+ current_file = line.replace("### File:", "").strip()
55
+ in_file_content = True
56
+ # Handle file content
57
+ elif in_file_content and line.startswith("```"):
58
+ if file_content:
59
+ space_info["files"].append({"path": current_file, "content": "\n".join(file_content)})
60
+ file_content = []
61
+ in_file_content = False
62
+ else:
63
+ in_file_content = True
64
+ elif in_file_content:
65
+ file_content.append(line)
66
+
67
+ # Append the last file
68
+ if current_file and file_content:
69
+ space_info["files"].append({"path": current_file, "content": "\n".join(file_content)})
70
+
71
+ return space_info
72
+
73
+ # Function to create and populate a Space
74
+ def create_space(api_token, space_name, owner, sdk, markdown_input):
75
+ """Create a Hugging Face Space and populate it with files from markdown input."""
76
+ try:
77
+ # Authenticate with Hugging Face Hub
78
+ if not api_token:
79
+ return "Error: Please provide a valid Hugging Face API token."
80
+
81
+ # Get user info
82
+ user_info = whoami(token=api_token)
83
+ if not owner:
84
+ owner = user_info["name"]
85
+ repo_id = f"{owner}/{space_name}"
86
+
87
+ # Create temporary directory
88
+ with tempfile.TemporaryDirectory() as temp_dir:
89
+ repo_path = os.path.join(temp_dir, space_name)
90
+ os.makedirs(repo_path, exist_ok=True)
91
+
92
+ # Parse markdown input
93
+ space_info = parse_markdown(markdown_input)
94
+ if space_info["repo_name"] != f"{owner}/{space_name}":
95
+ return "Error: Space name in markdown does not match provided owner/space_name."
96
+
97
+ # Write files to temporary directory
98
+ for file_info in space_info["files"]:
99
+ file_path = os.path.join(repo_path, file_info["path"])
100
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
101
+ with open(file_path, "w", encoding="utf-8") as f:
102
+ f.write(file_info["content"])
103
+ logger.info(f"Wrote file: {file_path}")
104
+
105
+ # Create repository on Hugging Face
106
+ try:
107
+ create_repo(
108
+ repo_id=repo_id,
109
+ token=api_token,
110
+ repo_type="space",
111
+ space_sdk=sdk,
112
+ private=False,
113
+ )
114
+ logger.info(f"Created Space: {repo_id}")
115
+ except HfHubHTTPError as e:
116
+ if "already exists" in str(e):
117
+ logger.info(f"Space {repo_id} already exists, proceeding to update.")
118
+ else:
119
+ return f"Error creating Space: {str(e)}"
120
+
121
+ # Initialize Git repository
122
+ repo = git.Repo.init(repo_path)
123
+ repo.git.add(all=True)
124
+ repo.index.commit("Initial commit from Gradio app")
125
+
126
+ # Push to Hugging Face Space
127
+ upload_folder(
128
+ repo_id=repo_id,
129
+ folder_path=repo_path,
130
+ path_in_repo=".",
131
+ token=api_token,
132
+ commit_message="Initial Space setup",
133
+ )
134
+
135
+ return f"Successfully created Space: https://huggingface.co/spaces/{repo_id}"
136
+
137
+ except Exception as e:
138
+ logger.error(f"Error: {str(e)}")
139
+ return f"Error: {str(e)}"
140
+
141
+ # Function to view Space files
142
+ def view_space_files(api_token, space_name, owner):
143
+ """List files in a Hugging Face Space."""
144
+ try:
145
+ if not api_token:
146
+ return "Error: Please provide a valid Hugging Face API token."
147
+ repo_id = f"{owner}/{space_name}" if owner else space_name
148
+ files = list_repo_files(repo_id=repo_id, token=api_token, repo_type="space")
149
+ return "\n".join(files) or "No files found in the Space."
150
+ except Exception as e:
151
+ return f"Error: {str(e)}"
152
+
153
+ # Function to update a Space file
154
+ def update_space_file(api_token, space_name, owner, file_path, file_content, commit_message):
155
+ """Update a file in a Hugging Face Space with a commit."""
156
+ try:
157
+ if not api_token:
158
+ return "Error: Please provide a valid Hugging Face API token."
159
+ repo_id = f"{owner}/{space_name}" if owner else space_name
160
+
161
+ # Create temporary directory for cloning
162
+ with tempfile.TemporaryDirectory() as temp_dir:
163
+ repo_path = os.path.join(temp_dir, space_name)
164
+ repo = Repository(
165
+ local_dir=repo_path,
166
+ clone_from=f"https://huggingface.co/spaces/{repo_id}",
167
+ repo_type="space",
168
+ use_auth_token=api_token,
169
+ )
170
+
171
+ # Write updated file
172
+ full_path = os.path.join(repo_path, file_path)
173
+ os.makedirs(os.path.dirname(full_path), exist_ok=True)
174
+ with open(full_path, "w", encoding="utf-8") as f:
175
+ f.write(file_content)
176
+
177
+ # Commit and push changes
178
+ repo.git_add(file_path)
179
+ repo.git_commit(commit_message or f"Update {file_path}")
180
+ repo.git_push()
181
+
182
+ return f"Successfully updated {file_path} in Space: https://huggingface.co/spaces/{repo_id}"
183
+
184
+ except Exception as e:
185
+ return f"Error: {str(e)}"
186
+
187
+ # Gradio interface
188
+ def main():
189
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
190
+ gr.Markdown("# Hugging Face Space Builder")
191
+ gr.Markdown("Create, view, and update Hugging Face Spaces with a custom file structure.")
192
+
193
+ # Authentication
194
+ with gr.Group():
195
+ gr.Markdown("## Authentication")
196
+ api_token = gr.Textbox(
197
+ label="Hugging Face API Token",
198
+ type="password",
199
+ placeholder="Enter your Hugging Face API token",
200
+ )
201
+
202
+ # Create Space
203
+ with gr.Group():
204
+ gr.Markdown("## Create a New Space")
205
+ space_name = gr.Textbox(label="Space Name", placeholder="my-space")
206
+ owner = gr.Textbox(
207
+ label="Owner (optional)", placeholder="Leave blank for current user"
208
+ )
209
+ sdk = gr.Dropdown(
210
+ label="SDK", choices=["gradio", "streamlit", "docker", "static"], value="gradio"
211
+ )
212
+ markdown_input = gr.Textbox(
213
+ label="Markdown File Structure",
214
+ placeholder="Paste markdown with file structure and contents",
215
+ lines=10,
216
+ )
217
+ create_btn = gr.Button("Create Space")
218
+ create_output = gr.Textbox(label="Result")
219
+
220
+ # View Space Files
221
+ with gr.Group():
222
+ gr.Markdown("## View Space Files")
223
+ view_space_name = gr.Textbox(label="Space Name", placeholder="my-space")
224
+ view_owner = gr.Textbox(
225
+ label="Owner (optional)", placeholder="Leave blank for current user"
226
+ )
227
+ view_btn = gr.Button("List Files")
228
+ view_output = gr.Textbox(label="Files in Space")
229
+
230
+ # Update Space File
231
+ with gr.Group():
232
+ gr.Markdown("## Update Space File")
233
+ update_space_name = gr.Textbox(label="Space Name", placeholder="my-space")
234
+ update_owner = gr.Textbox(
235
+ label="Owner (optional)", placeholder="Leave blank for current user"
236
+ )
237
+ file_path = gr.Textbox(label="File Path", placeholder="path/to/file.py")
238
+ file_content = gr.Textbox(
239
+ label="File Content", placeholder="Enter new file content", lines=10
240
+ )
241
+ commit_message = gr.Textbox(
242
+ label="Commit Message", placeholder="Update file content"
243
+ )
244
+ update_btn = gr.Button("Update File")
245
+ update_output = gr.Textbox(label="Result")
246
+
247
+ # Event handlers
248
+ create_btn.click(
249
+ fn=create_space,
250
+ inputs=[api_token, space_name, owner, sdk, markdown_input],
251
+ outputs=create_output,
252
+ )
253
+ view_btn.click(
254
+ fn=view_space_files,
255
+ inputs=[api_token, view_space_name, view_owner],
256
+ outputs=view_output,
257
+ )
258
+ update_btn.click(
259
+ fn=update_space_file,
260
+ inputs=[
261
+ api_token,
262
+ update_space_name,
263
+ update_owner,
264
+ file_path,
265
+ file_content,
266
+ commit_message,
267
+ ],
268
+ outputs=update_output,
269
+ )
270
+
271
+ return demo
272
+
273
+ if __name__ == "__main__":
274
+ demo = main()
275
+ demo.launch()