import os import uuid import tempfile from dataclasses import dataclass from datetime import datetime from pathlib import Path import gradio as gr # --------------------------- # Configuration (no hardcodes) # --------------------------- @dataclass class Config: # App texts TITLE: str INSTRUCTIONS_MD: str LABEL_COUNT: str LABEL_OUTPUT: str LABEL_GENERATE_BTN: str LABEL_DOWNLOAD_BTN: str # Behavior and limits DEFAULT_COUNT: int MIN_UUIDS: int MAX_UUIDS: int # UI layout TEXTBOX_LINES: int BUTTON_VARIANT: str # e.g., "primary" | "secondary" # File output OUTPUT_DIR: str # if empty, uses temp dir FILENAME_PREFIX: str TIME_FORMAT: str # strftime format, e.g. "%Y%m%d_%H%M%S" FILE_EXTENSION: str # e.g., ".txt" def getenv_int(key: str, default: int) -> int: """Safely parse an int from environment variables with a default.""" try: return int(os.getenv(key, str(default))) except Exception: return default CFG = Config( # Texts TITLE=os.getenv("APP_TITLE", "UUID Generator"), INSTRUCTIONS_MD=os.getenv( "APP_INSTRUCTIONS_MD", "Enter how many UUIDs you want and click **Generate**.", ), LABEL_COUNT=os.getenv("LABEL_COUNT", "How many UUIDs?"), LABEL_OUTPUT=os.getenv("LABEL_OUTPUT", "UUIDs (one per line)"), LABEL_GENERATE_BTN=os.getenv("LABEL_GENERATE_BTN", "Generate"), LABEL_DOWNLOAD_BTN=os.getenv("LABEL_DOWNLOAD_BTN", "Download as .txt"), # Behavior and limits DEFAULT_COUNT=getenv_int("DEFAULT_COUNT", 10), MIN_UUIDS=getenv_int("MIN_UUIDS", 1), MAX_UUIDS=getenv_int("MAX_UUIDS", 1000), # UI layout TEXTBOX_LINES=getenv_int("TEXTBOX_LINES", 16), BUTTON_VARIANT=os.getenv("BUTTON_VARIANT", "primary"), # File output OUTPUT_DIR=os.getenv("OUTPUT_DIR", ""), # "" -> tempdir FILENAME_PREFIX=os.getenv("FILENAME_PREFIX", "uuids"), TIME_FORMAT=os.getenv("TIME_FORMAT", "%Y%m%d_%H%M%S"), FILE_EXTENSION=os.getenv("FILE_EXTENSION", ".txt"), ) # --------------------------- # Core logic # --------------------------- def _resolve_output_dir() -> Path: """Pick output directory from config or fall back to a temporary directory.""" if CFG.OUTPUT_DIR.strip(): p = Path(CFG.OUTPUT_DIR).expanduser().resolve() p.mkdir(parents=True, exist_ok=True) return p # HF Spaces-friendly temp dir return Path(tempfile.gettempdir()) def generate_uuids_and_file(count: int): """Generate UUIDs (one per line) and create a downloadable file path.""" # Parse and clamp try: n = int(count) except Exception: n = CFG.DEFAULT_COUNT n = max(CFG.MIN_UUIDS, min(n, CFG.MAX_UUIDS)) # Build newline-separated UUIDs text = "\n".join(str(uuid.uuid4()) for _ in range(n)) # Prepare file path ts = datetime.utcnow().strftime(CFG.TIME_FORMAT) fname = f"{CFG.FILENAME_PREFIX}_{n}_{ts}{CFG.FILE_EXTENSION}" outdir = _resolve_output_dir() fpath = outdir / fname # Write to file fpath.write_text(text, encoding="utf-8") # IMPORTANT: use gr.update(...) instead of DownloadButton.update(...) return text, gr.update(value=str(fpath), visible=True) # --------------------------- # Gradio UI # --------------------------- with gr.Blocks(title=CFG.TITLE) as demo: gr.Markdown(f"# {CFG.TITLE}\n{CFG.INSTRUCTIONS_MD}") with gr.Row(): num = gr.Number( value=CFG.DEFAULT_COUNT, precision=0, minimum=CFG.MIN_UUIDS, maximum=CFG.MAX_UUIDS, label=CFG.LABEL_COUNT, ) btn = gr.Button(CFG.LABEL_GENERATE_BTN, variant=CFG.BUTTON_VARIANT) out = gr.Textbox( label=CFG.LABEL_OUTPUT, lines=CFG.TEXTBOX_LINES, show_copy_button=True, ) # Initially hidden; becomes visible with a valid file path dbtn = gr.DownloadButton(CFG.LABEL_DOWNLOAD_BTN, visible=False) btn.click(generate_uuids_and_file, inputs=num, outputs=[out, dbtn]) if __name__ == "__main__": # Tip: on HF Spaces, set share=True if you want a public link demo.launch()