|
|
|
|
|
""" |
|
|
|
|
|
!pip install gradio -q |
|
|
!pip install html -q |
|
|
|
|
|
# **Installs & Imports** |
|
|
from difflib import Differ |
|
|
from pathlib import Path |
|
|
import time |
|
|
|
|
|
import tempfile |
|
|
import atexit |
|
|
|
|
|
import os |
|
|
from io import BytesIO |
|
|
|
|
|
import gradio as gr |
|
|
import re |
|
|
import shlex |
|
|
import html |
|
|
|
|
|
import subprocess |
|
|
import shutil |
|
|
|
|
|
# For images |
|
|
from PIL import Image |
|
|
import numpy as np |
|
|
|
|
|
""" |
|
|
|
|
|
custom_theme = gr.themes.Ocean( |
|
|
primary_hue="indigo", |
|
|
secondary_hue="yellow", |
|
|
neutral_hue="indigo", |
|
|
text_size="lg", |
|
|
radius_size="lg", |
|
|
font=['IBM Plex Sans', 'ui-sans-serif', 'system-ui', gr.themes.GoogleFont('sans-serif')], |
|
|
font_mono=['Inter', 'ui-monospace', gr.themes.GoogleFont('Consolas'), 'monospace'], |
|
|
).set( |
|
|
background_fill_secondary='*secondary_50', |
|
|
background_fill_secondary_dark='*neutral_950', |
|
|
border_color_accent='*primary_50', |
|
|
border_color_primary='*neutral_300', |
|
|
color_accent='*primary_300', |
|
|
color_accent_soft_dark='*neutral_500', |
|
|
checkbox_label_background_fill='*color_accent_soft', |
|
|
button_large_radius='*radius_sm', |
|
|
button_small_radius='*radius_sm', |
|
|
button_primary_background_fill='linear-gradient(120deg, *primary_500 0%, *primary_300 60%, *primary_400 100%)', |
|
|
button_primary_background_fill_hover='radial-gradient(circle, *secondary_400 0%, *primary_300 60%, *primary_300 100%)' |
|
|
) |
|
|
|
|
|
"""# **Functions** |
|
|
|
|
|
## Global constants file paths |
|
|
""" |
|
|
|
|
|
def create_tmp_file() -> str: |
|
|
tmp_file = tempfile.NamedTemporaryFile(mode="w+", delete=False) |
|
|
tmp_file.close() |
|
|
return tmp_file.name |
|
|
|
|
|
def write_tmp_file(content: str, file_path: str): |
|
|
with open(file_path, "w") as file: |
|
|
file.write(content) |
|
|
|
|
|
def read_tmp_file(file_path: str) -> str: |
|
|
with open(file_path, "r") as file: |
|
|
return file.read() |
|
|
|
|
|
def cleanup_tmp_files(): |
|
|
os.remove(CREATOR_NAME_FILE) |
|
|
os.remove(CREATOR_EMAIL_FILE) |
|
|
os.remove(PROJECT_NAME_FILE) |
|
|
|
|
|
CREATOR_NAME_FILE = create_tmp_file() |
|
|
CREATOR_EMAIL_FILE = create_tmp_file() |
|
|
PROJECT_NAME_FILE = create_tmp_file() |
|
|
|
|
|
|
|
|
|
|
|
def diff_texts(text1, text2): |
|
|
d = Differ() |
|
|
return [ |
|
|
(token[2:], token[0] if token[0] != " " else None) |
|
|
for token in d.compare(text1, text2) |
|
|
] |
|
|
|
|
|
def download_file(): |
|
|
return [gr.UploadButton(visible=True), gr.DownloadButton(visible=False)] |
|
|
|
|
|
"""## Create project""" |
|
|
|
|
|
def validate_email(email: str, max_length: int=50): |
|
|
if not email or len(email) > max_length: |
|
|
return False |
|
|
email_regex = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' |
|
|
return re.match(email_regex, email) is not None |
|
|
|
|
|
def validate_project_name(project_name: str, max_length: int=50): |
|
|
if not project_name or len(project_name) > max_length: |
|
|
return False |
|
|
return True |
|
|
|
|
|
def validate_creator_name(creator_name: str, max_length: int=50): |
|
|
if not creator_name or len(creator_name) > max_length: |
|
|
return False |
|
|
return True |
|
|
|
|
|
|
|
|
def run_create_project_script(project_name: str, creator_name: str, creator_email): |
|
|
if validate_project_name(project_name): |
|
|
project_name = project_name.replace(" ", "_") |
|
|
else: |
|
|
return "Empty/Invalid project name" |
|
|
if not creator_name: |
|
|
return "Empty/Invalid creator name" |
|
|
if not validate_email(creator_email): |
|
|
return "Empty/Invalid email address" |
|
|
|
|
|
project_name_clean = shlex.quote(html.escape(project_name)) |
|
|
creator_name_clean = shlex.quote(html.escape(creator_name)) |
|
|
creator_email_clean = shlex.quote(html.escape(creator_email)) |
|
|
|
|
|
write_tmp_file(project_name_clean, PROJECT_NAME_FILE) |
|
|
write_tmp_file(creator_name_clean, CREATOR_NAME_FILE) |
|
|
write_tmp_file(creator_email_clean, CREATOR_EMAIL_FILE) |
|
|
|
|
|
arguments = ["./", project_name_clean, creator_name_clean, creator_email_clean] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/create_project.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
|
|
|
"""## Upload project""" |
|
|
|
|
|
def validate_uploaded_text(uploaded_text: str): |
|
|
max_length=10000 |
|
|
if uploaded_text is not None and uploaded_text != "" and len(uploaded_text) < max_length: |
|
|
return True |
|
|
return False |
|
|
|
|
|
def validate_commit_message(commit_message: str): |
|
|
max_length=1000 |
|
|
if commit_message is not None and len(commit_message) < max_length: |
|
|
return True |
|
|
return False |
|
|
|
|
|
def validate_filepath(filepath: str): |
|
|
if filepath is not None and os.path.exists(filepath): |
|
|
return True |
|
|
return False |
|
|
|
|
|
def move_file_to_project(filepath: str, project_path: str): |
|
|
if filepath == "" or filepath is None: |
|
|
return False |
|
|
if not os.path.exists(filepath): |
|
|
return False |
|
|
name = Path(filepath).name |
|
|
destination = os.path.join(project_path, name) |
|
|
shutil.move(filepath, destination) |
|
|
return os.path.exists(destination) |
|
|
|
|
|
def run_upload_project_script(project_path: str, filepath: str): |
|
|
filename = Path(filepath).name |
|
|
if move_file_to_project(filepath, project_path): |
|
|
commit_message = f"Uploaded '{filename}' to '{project_path}' project" |
|
|
if validate_commit_message(commit_message): |
|
|
arguments = [os.getcwd(), project_path, commit_message] |
|
|
!sed -i 's/\r$//' ./vcs_scripts/upload2.sh |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/upload2.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return True, f"{result.stdout}" |
|
|
else: |
|
|
return False, f"Something went wrong with commit message: {commit_message}" |
|
|
return False, "Please create a project first" |
|
|
|
|
|
|
|
|
|
|
|
def upload_text(uploaded_text: str): |
|
|
project_path = read_tmp_file(PROJECT_NAME_FILE) |
|
|
if project_path == "": |
|
|
return gr.DownloadButton(visible=False), gr.Textbox(value="Please create a project first.", visible=True) |
|
|
if not validate_uploaded_text(uploaded_text.get("text", "")): |
|
|
return gr.DownloadButton(visible=False), gr.Textbox(value="Empty/Invalid argument entered.", visible=True) |
|
|
|
|
|
|
|
|
text = shlex.quote(html.escape(uploaded_text.get("text", ""))) |
|
|
commit_message_clean = shlex.quote(html.escape("Uploaded ")) |
|
|
|
|
|
filename = uploaded_text.get("name", "untitled.txt") |
|
|
with open(filename, "w", encoding="utf-8") as file: |
|
|
file.write(uploaded_text.get("text", "")) |
|
|
status, message = run_upload_project_script(project_path, os.path.join(os.getcwd(), filename)) |
|
|
if status: |
|
|
return gr.DownloadButton(value=filename, label=f"Download {filename}", visible=True), gr.Textbox(value=message, visible=True) |
|
|
return gr.DownloadButton(visible=False), gr.Textbox(value=message, visible=True) |
|
|
|
|
|
def upload_file(filepath: str): |
|
|
project_name = read_tmp_file(PROJECT_NAME_FILE) |
|
|
if project_name == "": |
|
|
return gr.DownloadButton(visible=False), gr.Textbox(value="Please create a project first.", visible=True) |
|
|
filename = Path(filepath.name).name |
|
|
project_path = os.path.join(os.getcwd(), project_name) |
|
|
status, message = run_upload_project_script(project_path, filepath) |
|
|
if status: |
|
|
return gr.DownloadButton(value=filename, label=f"Download {filename}", visible=True), gr.Textbox(value=message, visible=True) |
|
|
return gr.DownloadButton(visible=False), gr.Textbox(value=message, visible=True) |
|
|
|
|
|
|
|
|
def sleep(img_dict): |
|
|
time.sleep(5) |
|
|
return [img_dict["background"], img_dict["layers"][0], img_dict["layers"][1], img_dict["composite"]] |
|
|
|
|
|
def show_image(img_dict): |
|
|
return img_dict["composite"] |
|
|
|
|
|
def upload_image(img_dict): |
|
|
output_path = "output_image.png" |
|
|
image = img_dict["composite"] |
|
|
image = Image.fromarray(image) |
|
|
|
|
|
|
|
|
image.save(output_path) |
|
|
return f"Image saved to {output_path}" |
|
|
|
|
|
def run_upload_project_script(project_path: str, filepath: str): |
|
|
filename = Path(filepath).name |
|
|
if move_file_to_project(filepath, project_path): |
|
|
commit_message = f"Uploaded '{filename}' to '{project_path}' project." |
|
|
if validate_commit_message(commit_message): |
|
|
arguments = [os.getcwd(), project_path, commit_message] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/upload2.sh"] + arguments, capture_output=True, text=True, check=True, shell=False) |
|
|
return True, f"Automatic commit message - {commit_message}\n{result.stdout}" |
|
|
else: |
|
|
return False, f"Something went wrong with commit message: {commit_message}" |
|
|
return False, "Please create a project first" |
|
|
|
|
|
"""## Recreate project""" |
|
|
|
|
|
def run_recreate_project_script(project_name: str, new_project_name: str): |
|
|
if validate_project_name(new_project_name): |
|
|
new_project_name = new_project_name.replace(" ", "_") |
|
|
new_project_name_clean = shlex.quote(html.escape(new_project_name)) |
|
|
else: |
|
|
return "Empty/Invalid new project name" |
|
|
if validate_project_name(project_name): |
|
|
if os.path.isdir(project_name): |
|
|
arguments = ["./vcs_scripts", "./", project_name, new_project_name_clean] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/recreate.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
elif os.path.isdir(project_name + ".git"): |
|
|
arguments = ["./vcs_scripts", "./", project_name + ".git", new_project_name_clean] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/recreate.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
else: |
|
|
return f"Project '{project_name}' does not exist." |
|
|
else: |
|
|
return f"Empty/Invalid project name {project_name}" |
|
|
|
|
|
"""## Download project""" |
|
|
|
|
|
def run_download_script(project_name: str): |
|
|
if validate_project_name(project_name): |
|
|
project_path = os.path.join(os.getcwd(), project_name) |
|
|
if os.path.isdir(project_path): |
|
|
arguments = ["./", project_name] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/download.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
elif os.path.isdir(project_path + ".git"): |
|
|
arguments = ["./", project_name + ".git"] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/download.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
else: |
|
|
return f"Project '{project_name}' does not exist in path." |
|
|
else: |
|
|
return f"Empty/Invalid project name {project_name}" |
|
|
|
|
|
"""## Revert project""" |
|
|
|
|
|
def run_revert_script(project_name: str, revert_hash: str): |
|
|
if validate_project_name(project_name): |
|
|
project_path = os.path.join(os.getcwd(), project_name) |
|
|
if os.path.isdir(project_path): |
|
|
arguments = ["./vcs_script", "./", project_name, revert_hash] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/revert2.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
elif os.path.isdir(project_path + ".git"): |
|
|
arguments = ["./vcs_script", "./", project_name + ".git", revert_hash] |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/revert2.sh"] + arguments, capture_output=True, text=True, shell=False) |
|
|
return result.stdout |
|
|
else: |
|
|
return f"Project '{project_name}' does not exist in path." |
|
|
else: |
|
|
return f"Empty/Invalid project name {project_name}" |
|
|
|
|
|
"""# **Build layout**""" |
|
|
|
|
|
os.chmod("./vcs_scripts/grant_permissions.sh", 0o755) |
|
|
result = subprocess.run(["C:\\Program Files\\Git\\bin\\bash.exe", "./vcs_scripts/grant_permissions.sh"], capture_output=True, text=True, shell=False) |
|
|
print(result.stdout) |
|
|
|
|
|
with gr.Blocks(theme=custom_theme, title="Version Control") as demo: |
|
|
gr.Markdown("<h1 style='text-align: center;'>Your project</h1>") |
|
|
with gr.Tabs(): |
|
|
with gr.Tab("Create Empty Project"): |
|
|
with gr.Row(): |
|
|
project_name = gr.Textbox(label="Project name", placeholder="my_creation, my-creation, or myCreation") |
|
|
with gr.Row(): |
|
|
creator_name = gr.Textbox(label="Creator name", placeholder="Victor Frankenstein") |
|
|
creator_email = gr.Textbox(label="Creator email", placeholder="example@gmail.com") |
|
|
with gr.Row(): |
|
|
submit_btn = gr.Button("Create project", variant="primary") |
|
|
with gr.Row(): |
|
|
output = gr.Textbox(label="status") |
|
|
submit_btn.click(fn=run_create_project_script, inputs=[project_name, creator_name, creator_email], outputs=output) |
|
|
|
|
|
with gr.Tab("Text Upload"): |
|
|
with gr.Group(): |
|
|
with gr.Accordion(label="Use basic text editor...", open=False): |
|
|
text_input = gr.MultimodalTextbox( |
|
|
label="(max 10000 characters)", |
|
|
sources=["microphone"], |
|
|
file_types=[".txt"], |
|
|
placeholder="Enter your text here or speak...", |
|
|
submit_btn="Upload", |
|
|
lines=8 |
|
|
) |
|
|
with gr.Group(): |
|
|
file_upload = gr.File( |
|
|
label="Upload a text file", |
|
|
file_types=["text"], |
|
|
file_count="single", |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
file_output = gr.DownloadButton(label="Download File", visible=False, variant="primary") |
|
|
download_button = gr.DownloadButton("Download the file", visible=False, variant="primary") |
|
|
status_textbox = gr.Textbox(label="status", visible=False, interactive=False) |
|
|
|
|
|
text_input.submit(fn=upload_text, inputs=[text_input], outputs=[file_output, status_textbox]) |
|
|
file_upload.upload(upload_file, [file_upload], [download_button, status_textbox]) |
|
|
download_button.click(download_file, None, [file_upload, download_button]) |
|
|
|
|
|
with gr.Tab("Image Upload"): |
|
|
with gr.Row(): |
|
|
img_editor = gr.ImageEditor( |
|
|
label="Image uploader & editor", |
|
|
type="numpy", |
|
|
crop_size="16:8", |
|
|
scale=4, |
|
|
brush=gr.Brush(color_mode="defaults", colors=[ |
|
|
"#FFADAD", |
|
|
"#FFD6A5", |
|
|
"#FDFFB6", |
|
|
"#CAFFBF", |
|
|
"#9BF6FF", |
|
|
"#A0C4FF", |
|
|
"#BDB2FF", |
|
|
"#FFC6FF", |
|
|
|
|
|
|
|
|
"#FFFFFF", |
|
|
"#808080", |
|
|
"#000000", |
|
|
|
|
|
|
|
|
"#F5D0C5", |
|
|
"#E0A98A", |
|
|
"#F1C6A0", |
|
|
"#EDA268", |
|
|
"#D0743A", |
|
|
"#6B3F1F", |
|
|
"#3C0000" |
|
|
] |
|
|
), |
|
|
canvas_size=(1920,1080) |
|
|
) |
|
|
|
|
|
with gr.Row(): |
|
|
img_preview = gr.Image(label="Result image") |
|
|
with gr.Row(): |
|
|
n_upload = gr.Number(0, label="Number of upload events", step=1, interactive=False) |
|
|
n_change = gr.Number(0, label="Number of change events", step=1, interactive=False) |
|
|
with gr.Row(): |
|
|
upload_btn = gr.Button("Upload image") |
|
|
with gr.Row(): |
|
|
output_text = gr.Textbox(label="Status", interactive=False) |
|
|
img_editor.upload(lambda x: x + 1, outputs=n_upload, inputs=n_upload) |
|
|
img_editor.change(lambda x: x + 1, outputs=n_change, inputs=n_change) |
|
|
img_editor.change(show_image, outputs=img_preview, inputs=img_editor, show_progress="hidden") |
|
|
|
|
|
|
|
|
upload_btn.click(fn=upload_image, inputs=img_editor, outputs=output_text) |
|
|
|
|
|
with gr.Tab("Recreate"): |
|
|
with gr.Row(): |
|
|
project_name = gr.Textbox(label="Remote project you want to recreate", |
|
|
interactive=True) |
|
|
new_project_name = gr.Textbox(label="Your new project name", |
|
|
interactive=True) |
|
|
with gr.Row(): |
|
|
submit_btn = gr.Button("Recreate project", variant="primary") |
|
|
with gr.Row(): |
|
|
recreate_output = gr.Textbox(label="status", interactive=False) |
|
|
submit_btn.click(fn=run_recreate_project_script, inputs=[project_name, new_project_name], outputs=recreate_output) |
|
|
""" |
|
|
with gr.Tab("Download"): |
|
|
with gr.Row(): |
|
|
project_name = gr.Textbox(label="Project you want to download", |
|
|
interactive=True) |
|
|
with gr.Row(): |
|
|
submit_btn = gr.Button("Download any new changes", variant="primary") |
|
|
with gr.Row(): |
|
|
download_output = gr.Textbox(label="status", interactive=False) |
|
|
submit_btn.click(fn=run_download_script, inputs=[project_name], outputs=download_output) |
|
|
""" |
|
|
with gr.Tab("Compare"): |
|
|
with gr.Row(): |
|
|
gr.Markdown("<h1>Compare version differences</h1>") |
|
|
with gr.Row(): |
|
|
original_version = gr.Textbox( |
|
|
label="Original", |
|
|
info="Initial text", |
|
|
lines=4, |
|
|
value="Blasted as thou wert, my agony was still superior to thine.", |
|
|
interactive=False, |
|
|
) |
|
|
new_version = gr.Textbox( |
|
|
label="Uploaded", |
|
|
info="New changes", |
|
|
lines=4, |
|
|
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)", |
|
|
interactive=False, |
|
|
) |
|
|
|
|
|
compare_btn = gr.Button("Compare differences", variant="primary") |
|
|
with gr.Row(): |
|
|
result = gr.HighlightedText( |
|
|
label="Diff", |
|
|
combine_adjacent=True, |
|
|
show_legend=True, |
|
|
color_map={"+": "blue", "-": "red"} |
|
|
) |
|
|
compare_btn.click(fn=diff_texts, inputs=[original_version, new_version], outputs=result) |
|
|
with gr.Tab("Revert"): |
|
|
with gr.Row(): |
|
|
project_name = gr.Textbox(label="Project name", interactive=True) |
|
|
revert_hash = gr.Textbox(label="Revert back to...", |
|
|
info="hash value", |
|
|
interactive=True |
|
|
) |
|
|
with gr.Row(): |
|
|
submit_btn = gr.Button("Revert", variant="primary") |
|
|
with gr.Row(): |
|
|
revert_output = gr.Textbox(label="status", interactive=False) |
|
|
submit_btn.click(fn=run_revert_script, inputs=[project_name, revert_hash], outputs=revert_output) |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
demo.launch(debug=True, share=True) |
|
|
atexit.register(cleanup_tmp_files) |