mch84 commited on
Commit
381081e
·
verified ·
1 Parent(s): 5d0e62a
Files changed (1) hide show
  1. UI_VCS.py +462 -0
UI_VCS.py ADDED
@@ -0,0 +1,462 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # **Installs & imports**
2
+ """
3
+
4
+ !pip install gradio -q
5
+ !pip install html -q
6
+
7
+ # **Installs & Imports**
8
+ from difflib import Differ
9
+ from pathlib import Path
10
+ import time
11
+
12
+ import tempfile
13
+ import atexit
14
+
15
+ import os
16
+ from io import BytesIO
17
+
18
+ import gradio as gr
19
+ import re
20
+ import shlex
21
+ import html
22
+
23
+ import subprocess
24
+ import shutil
25
+
26
+ # For images
27
+ from PIL import Image
28
+ import numpy as np
29
+
30
+ """# **Build custom theme**"""
31
+
32
+ custom_theme = gr.themes.Ocean(
33
+ primary_hue="indigo",
34
+ secondary_hue="yellow",
35
+ neutral_hue="indigo",
36
+ text_size="lg",
37
+ radius_size="lg",
38
+ font=['IBM Plex Sans', 'ui-sans-serif', 'system-ui', gr.themes.GoogleFont('sans-serif')],
39
+ font_mono=['Inter', 'ui-monospace', gr.themes.GoogleFont('Consolas'), 'monospace'],
40
+ ).set(
41
+ background_fill_secondary='*secondary_50',
42
+ background_fill_secondary_dark='*neutral_950',
43
+ border_color_accent='*primary_50',
44
+ border_color_primary='*neutral_300',
45
+ color_accent='*primary_300',
46
+ color_accent_soft_dark='*neutral_500',
47
+ checkbox_label_background_fill='*color_accent_soft',
48
+ button_large_radius='*radius_sm',
49
+ button_small_radius='*radius_sm',
50
+ button_primary_background_fill='linear-gradient(120deg, *primary_500 0%, *primary_300 60%, *primary_400 100%)',
51
+ button_primary_background_fill_hover='radial-gradient(circle, *secondary_400 0%, *primary_300 60%, *primary_300 100%)'
52
+ )
53
+
54
+ """# **Functions**
55
+
56
+ ## Global constants file paths
57
+ """
58
+
59
+ def create_tmp_file() -> str:
60
+ tmp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False)
61
+ tmp_file.close()
62
+ return tmp_file.name
63
+
64
+ def write_tmp_file(content: str, file_path: str):
65
+ with open(file_path, "w") as file:
66
+ file.write(content)
67
+
68
+ def read_tmp_file(file_path: str) -> str:
69
+ with open(file_path, "r") as file:
70
+ return file.read()
71
+
72
+ def cleanup_tmp_files():
73
+ os.remove(CREATOR_NAME_FILE)
74
+ os.remove(CREATOR_EMAIL_FILE)
75
+ os.remove(PROJECT_NAME_FILE)
76
+
77
+ CREATOR_NAME_FILE = create_tmp_file()
78
+ CREATOR_EMAIL_FILE = create_tmp_file()
79
+ PROJECT_NAME_FILE = create_tmp_file()
80
+
81
+ # **Define UI functions**
82
+ # For texts
83
+ def diff_texts(text1, text2):
84
+ d = Differ()
85
+ return [
86
+ (token[2:], token[0] if token[0] != " " else None)
87
+ for token in d.compare(text1, text2)
88
+ ]
89
+
90
+ def download_file():
91
+ return [gr.UploadButton(visible=True), gr.DownloadButton(visible=False)]
92
+
93
+ """## Create project"""
94
+
95
+ def validate_email(email: str, max_length: int=50):
96
+ if not email or len(email) > max_length:
97
+ return False
98
+ email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
99
+ return re.match(email_regex, email) is not None
100
+
101
+ def validate_project_name(project_name: str, max_length: int=50):
102
+ if not project_name or len(project_name) > max_length:
103
+ return False
104
+ return True
105
+
106
+ def validate_creator_name(creator_name: str, max_length: int=50):
107
+ if not creator_name or len(creator_name) > max_length:
108
+ return False
109
+ return True
110
+
111
+
112
+ def run_create_project_script(project_name: str, creator_name: str, creator_email):
113
+ if validate_project_name(project_name):
114
+ project_name = project_name.replace(" ", "_")
115
+ else:
116
+ return "Empty/Invalid project name"
117
+ if not creator_name:
118
+ return "Empty/Invalid creator name"
119
+ if not validate_email(creator_email):
120
+ return "Empty/Invalid email address"
121
+
122
+ project_name_clean = shlex.quote(html.escape(project_name))
123
+ creator_name_clean = shlex.quote(html.escape(creator_name))
124
+ creator_email_clean = shlex.quote(html.escape(creator_email))
125
+
126
+ write_tmp_file(project_name_clean, PROJECT_NAME_FILE)
127
+ write_tmp_file(creator_name_clean, CREATOR_NAME_FILE)
128
+ write_tmp_file(creator_email_clean, CREATOR_EMAIL_FILE)
129
+
130
+ arguments = ["./", project_name_clean, creator_name_clean, creator_email_clean]
131
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/create_project.sh"] + arguments, capture_output=True, text=True, shell=False)
132
+ return result.stdout
133
+
134
+ """## Upload project"""
135
+
136
+ def validate_uploaded_text(uploaded_text: str):
137
+ max_length=10000
138
+ if uploaded_text is not None and uploaded_text != "" and len(uploaded_text) < max_length:
139
+ return True
140
+ return False
141
+
142
+ def validate_commit_message(commit_message: str):
143
+ max_length=1000
144
+ if commit_message is not None and len(commit_message) < max_length:
145
+ return True
146
+ return False
147
+
148
+ def validate_filepath(filepath: str):
149
+ if filepath is not None and os.path.exists(filepath):
150
+ return True
151
+ return False
152
+
153
+ def move_file_to_project(filepath: str, project_path: str):
154
+ if filepath == "" or filepath is None:
155
+ return False
156
+ if not os.path.exists(filepath):
157
+ return False
158
+ name = Path(filepath).name
159
+ destination = os.path.join(project_path, name) # Create destination path: target_dir/filename
160
+ shutil.move(filepath, destination)
161
+ return os.path.exists(destination)
162
+
163
+ def run_upload_project_script(project_path: str, filepath: str):
164
+ filename = Path(filepath).name
165
+ if move_file_to_project(filepath, project_path):
166
+ commit_message = f"Uploaded '{filename}' to '{project_path}' project"
167
+ if validate_commit_message(commit_message):
168
+ arguments = [os.getcwd(), project_path, commit_message]
169
+ !sed -i 's/\r$//' ./vcs_scripts/upload2.sh
170
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/upload2.sh"] + arguments, capture_output=True, text=True, shell=False)
171
+ return True, f"{result.stdout}"
172
+ else:
173
+ return False, f"Something went wrong with commit message: {commit_message}"
174
+ return False, "Please create a project first"
175
+
176
+
177
+
178
+ def upload_text(uploaded_text: str):
179
+ project_path = read_tmp_file(PROJECT_NAME_FILE)
180
+ if project_path == "":
181
+ return gr.DownloadButton(visible=False), gr.Textbox(value="Please create a project first.", visible=True)
182
+ if not validate_uploaded_text(uploaded_text.get("text", "")):
183
+ return gr.DownloadButton(visible=False), gr.Textbox(value="Empty/Invalid argument entered.", visible=True)
184
+
185
+ # Sanitize text input
186
+ text = shlex.quote(html.escape(uploaded_text.get("text", "")))
187
+ commit_message_clean = shlex.quote(html.escape("Uploaded "))
188
+
189
+ filename = uploaded_text.get("name", "untitled.txt")
190
+ with open(filename, "w", encoding="utf-8") as file:
191
+ file.write(uploaded_text.get("text", ""))
192
+ status, message = run_upload_project_script(project_path, os.path.join(os.getcwd(), filename))
193
+ if status:
194
+ return gr.DownloadButton(value=filename, label=f"Download {filename}", visible=True), gr.Textbox(value=message, visible=True)
195
+ return gr.DownloadButton(visible=False), gr.Textbox(value=message, visible=True)
196
+
197
+ def upload_file(filepath: str):
198
+ project_name = read_tmp_file(PROJECT_NAME_FILE)
199
+ if project_name == "":
200
+ return gr.DownloadButton(visible=False), gr.Textbox(value="Please create a project first.", visible=True)
201
+ filename = Path(filepath.name).name
202
+ project_path = os.path.join(os.getcwd(), project_name)
203
+ status, message = run_upload_project_script(project_path, filepath)
204
+ if status:
205
+ return gr.DownloadButton(value=filename, label=f"Download {filename}", visible=True), gr.Textbox(value=message, visible=True) # gr.Textbox(value=message, visible=True)
206
+ return gr.DownloadButton(visible=False), gr.Textbox(value=message, visible=True)
207
+
208
+ # For image uploads
209
+ def sleep(img_dict):
210
+ time.sleep(5)
211
+ return [img_dict["background"], img_dict["layers"][0], img_dict["layers"][1], img_dict["composite"]]
212
+
213
+ def show_image(img_dict):
214
+ return img_dict["composite"]
215
+
216
+ def upload_image(img_dict):
217
+ output_path = "output_image.png"
218
+ image = img_dict["composite"]
219
+ image = Image.fromarray(image)
220
+
221
+ # Save the image to the output path
222
+ image.save(output_path)
223
+ return f"Image saved to {output_path}"
224
+
225
+ def run_upload_project_script(project_path: str, filepath: str):
226
+ filename = Path(filepath).name
227
+ if move_file_to_project(filepath, project_path):
228
+ commit_message = f"Uploaded '{filename}' to '{project_path}' project."
229
+ if validate_commit_message(commit_message):
230
+ arguments = [os.getcwd(), project_path, commit_message]
231
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/upload2.sh"] + arguments, capture_output=True, text=True, check=True, shell=False)#, shell=True)
232
+ return True, f"Automatic commit message - {commit_message}\n{result.stdout}"
233
+ else:
234
+ return False, f"Something went wrong with commit message: {commit_message}"
235
+ return False, "Please create a project first"
236
+
237
+ """## Recreate project"""
238
+
239
+ def run_recreate_project_script(project_name: str, new_project_name: str):
240
+ if validate_project_name(new_project_name):
241
+ new_project_name = new_project_name.replace(" ", "_")
242
+ new_project_name_clean = shlex.quote(html.escape(new_project_name))
243
+ else:
244
+ return "Empty/Invalid new project name"
245
+ if validate_project_name(project_name):
246
+ if os.path.isdir(project_name):
247
+ arguments = ["./vcs_scripts", "./", project_name, new_project_name_clean]
248
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/recreate.sh"] + arguments, capture_output=True, text=True, shell=False)
249
+ return result.stdout
250
+ elif os.path.isdir(project_name + ".git"): # If user forget to include .git part in entered remote project name
251
+ arguments = ["./vcs_scripts", "./", project_name + ".git", new_project_name_clean]
252
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/recreate.sh"] + arguments, capture_output=True, text=True, shell=False)
253
+ return result.stdout
254
+ else:
255
+ return f"Project '{project_name}' does not exist."
256
+ else:
257
+ return f"Empty/Invalid project name {project_name}"
258
+
259
+ """## Download project"""
260
+
261
+ def run_download_script(project_name: str):
262
+ if validate_project_name(project_name):
263
+ project_path = os.path.join(os.getcwd(), project_name)
264
+ if os.path.isdir(project_path):
265
+ arguments = ["./", project_name]
266
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/download.sh"] + arguments, capture_output=True, text=True, shell=False)
267
+ return result.stdout
268
+ elif os.path.isdir(project_path + ".git"): # If user forget to include .git part in entered remote project name
269
+ arguments = ["./", project_name + ".git"]
270
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/download.sh"] + arguments, capture_output=True, text=True, shell=False)
271
+ return result.stdout
272
+ else:
273
+ return f"Project '{project_name}' does not exist in path."
274
+ else:
275
+ return f"Empty/Invalid project name {project_name}"
276
+
277
+ """## Revert project"""
278
+
279
+ def run_revert_script(project_name: str, revert_hash: str):
280
+ if validate_project_name(project_name):
281
+ project_path = os.path.join(os.getcwd(), project_name)
282
+ if os.path.isdir(project_path):
283
+ arguments = ["./vcs_script", "./", project_name, revert_hash]
284
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/revert2.sh"] + arguments, capture_output=True, text=True, shell=False)
285
+ return result.stdout
286
+ elif os.path.isdir(project_path + ".git"): # If user forget to include .git part in entered project name
287
+ arguments = ["./vcs_script", "./", project_name + ".git", revert_hash]
288
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/revert2.sh"] + arguments, capture_output=True, text=True, shell=False)
289
+ return result.stdout
290
+ else:
291
+ return f"Project '{project_name}' does not exist in path."
292
+ else:
293
+ return f"Empty/Invalid project name {project_name}"
294
+
295
+ """# **Build layout**"""
296
+
297
+ os.chmod("./vcs_scripts/grant_permissions.sh", 0o755)
298
+ result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/grant_permissions.sh"], capture_output=True, text=True, shell=False)
299
+ print(result.stdout)
300
+
301
+ with gr.Blocks(theme=custom_theme, title="Version Control") as demo:
302
+ gr.Markdown("<h1 style='text-align: center;'>Your project</h1>")
303
+ with gr.Tabs():
304
+ with gr.Tab("Create Empty Project"):
305
+ with gr.Row():
306
+ project_name = gr.Textbox(label="Project name", placeholder="my_creation, my-creation, or myCreation")
307
+ with gr.Row():
308
+ creator_name = gr.Textbox(label="Creator name", placeholder="Victor Frankenstein")
309
+ creator_email = gr.Textbox(label="Creator email", placeholder="example@gmail.com")
310
+ with gr.Row():
311
+ submit_btn = gr.Button("Create project", variant="primary")
312
+ with gr.Row():
313
+ output = gr.Textbox(label="status")
314
+ submit_btn.click(fn=run_create_project_script, inputs=[project_name, creator_name, creator_email], outputs=output)
315
+
316
+ with gr.Tab("Text Upload"):
317
+ with gr.Group():
318
+ with gr.Accordion(label="Use basic text editor...", open=False):
319
+ text_input = gr.MultimodalTextbox(
320
+ label="(max 10000 characters)",
321
+ sources=["microphone"],
322
+ file_types=[".txt"],
323
+ placeholder="Enter your text here or speak...",
324
+ submit_btn="Upload",
325
+ lines=8
326
+ )
327
+ with gr.Group():
328
+ file_upload = gr.File(
329
+ label="Upload a text file",
330
+ file_types=["text"],
331
+ file_count="single",
332
+ interactive=True
333
+ )
334
+
335
+ #commit_textbox = gr.Textbox(label="commit message", placeholder="Commit message must be under 1000 chars", visible=True, interactive=True)
336
+
337
+ file_output = gr.DownloadButton(label="Download File", visible=False, variant="primary")
338
+ download_button = gr.DownloadButton("Download the file", visible=False, variant="primary")
339
+ status_textbox = gr.Textbox(label="status", visible=False, interactive=False)
340
+
341
+ text_input.submit(fn=upload_text, inputs=[text_input], outputs=[file_output, status_textbox])
342
+ file_upload.upload(upload_file, [file_upload], [download_button, status_textbox])
343
+ download_button.click(download_file, None, [file_upload, download_button])
344
+
345
+ with gr.Tab("Image Upload"):
346
+ with gr.Row():
347
+ img_editor = gr.ImageEditor(
348
+ label="Image uploader & editor",
349
+ type="numpy",
350
+ crop_size="16:8",
351
+ scale=4,
352
+ brush=gr.Brush(color_mode="defaults", colors=[
353
+ "#FFADAD",
354
+ "#FFD6A5",
355
+ "#FDFFB6",
356
+ "#CAFFBF",
357
+ "#9BF6FF",
358
+ "#A0C4FF",
359
+ "#BDB2FF",
360
+ "#FFC6FF",
361
+
362
+ # Neutral colors:
363
+ "#FFFFFF", # White
364
+ "#808080", # Gray
365
+ "#000000", # Black
366
+
367
+ # Skin tones and browns:
368
+ "#F5D0C5", # Peach
369
+ "#E0A98A", # Sand
370
+ "#F1C6A0", # Gold
371
+ "#EDA268", # Tan
372
+ "#D0743A", # Bronze
373
+ "#6B3F1F", # Chocolate
374
+ "#3C0000" # Mahogany
375
+ ]
376
+ ),
377
+ canvas_size=(1920,1080)
378
+ )
379
+
380
+ with gr.Row():
381
+ img_preview = gr.Image(label="Result image")
382
+ with gr.Row():
383
+ n_upload = gr.Number(0, label="Number of upload events", step=1, interactive=False)
384
+ n_change = gr.Number(0, label="Number of change events", step=1, interactive=False)
385
+ with gr.Row():
386
+ upload_btn = gr.Button("Upload image")
387
+ with gr.Row():
388
+ output_text = gr.Textbox(label="Status", interactive=False)
389
+ img_editor.upload(lambda x: x + 1, outputs=n_upload, inputs=n_upload)
390
+ img_editor.change(lambda x: x + 1, outputs=n_change, inputs=n_change)
391
+ img_editor.change(show_image, outputs=img_preview, inputs=img_editor, show_progress="hidden")
392
+
393
+
394
+ upload_btn.click(fn=upload_image, inputs=img_editor, outputs=output_text)
395
+
396
+ with gr.Tab("Recreate"):
397
+ with gr.Row():
398
+ project_name = gr.Textbox(label="Remote project you want to recreate",
399
+ interactive=True)
400
+ new_project_name = gr.Textbox(label="Your new project name",
401
+ interactive=True)
402
+ with gr.Row():
403
+ submit_btn = gr.Button("Recreate project", variant="primary")
404
+ with gr.Row():
405
+ recreate_output = gr.Textbox(label="status", interactive=False)
406
+ submit_btn.click(fn=run_recreate_project_script, inputs=[project_name, new_project_name], outputs=recreate_output)
407
+ """
408
+ with gr.Tab("Download"):
409
+ with gr.Row():
410
+ project_name = gr.Textbox(label="Project you want to download",
411
+ interactive=True)
412
+ with gr.Row():
413
+ submit_btn = gr.Button("Download any new changes", variant="primary")
414
+ with gr.Row():
415
+ download_output = gr.Textbox(label="status", interactive=False)
416
+ submit_btn.click(fn=run_download_script, inputs=[project_name], outputs=download_output)
417
+ """
418
+ with gr.Tab("Compare"):
419
+ with gr.Row():
420
+ gr.Markdown("<h1>Compare version differences</h1>")
421
+ with gr.Row():
422
+ original_version = gr.Textbox(
423
+ label="Original",
424
+ info="Initial text",
425
+ lines=4,
426
+ value="Blasted as thou wert, my agony was still superior to thine.",
427
+ interactive=False,
428
+ )
429
+ new_version = gr.Textbox(
430
+ label="Uploaded",
431
+ info="New changes",
432
+ lines=4,
433
+ value="Blasted as thou (Victor Frankenstein) wert, my (the creature's) agony was still superior to his (because creature is ugly and has no friends)",
434
+ interactive=False,
435
+ )
436
+
437
+ compare_btn = gr.Button("Compare differences", variant="primary")
438
+ with gr.Row():
439
+ result = gr.HighlightedText(
440
+ label="Diff",
441
+ combine_adjacent=True,
442
+ show_legend=True,
443
+ color_map={"+": "blue", "-": "red"}
444
+ )
445
+ compare_btn.click(fn=diff_texts, inputs=[original_version, new_version], outputs=result)
446
+ with gr.Tab("Revert"):
447
+ with gr.Row():
448
+ project_name = gr.Textbox(label="Project name", interactive=True)
449
+ revert_hash = gr.Textbox(label="Revert back to...",
450
+ info="hash value",
451
+ interactive=True
452
+ )
453
+ with gr.Row():
454
+ submit_btn = gr.Button("Revert", variant="primary")
455
+ with gr.Row():
456
+ revert_output = gr.Textbox(label="status", interactive=False)
457
+ submit_btn.click(fn=run_revert_script, inputs=[project_name, revert_hash], outputs=revert_output)
458
+
459
+
460
+ if __name__ == "__main__":
461
+ demo.launch(debug=True, share=True)
462
+ atexit.register(cleanup_tmp_files)