|
import dash |
|
from dash import dcc, html, Input, Output, State |
|
import dash_bootstrap_components as dbc |
|
from dash.exceptions import PreventUpdate |
|
import google.generativeai as genai |
|
from github import Github |
|
import gitlab |
|
import requests |
|
import tempfile |
|
import docx |
|
import os |
|
import logging |
|
import threading |
|
from huggingface_hub import HfApi |
|
from flask import send_file |
|
|
|
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) |
|
server = app.server |
|
|
|
|
|
hf_api = HfApi() |
|
|
|
|
|
GITHUB_TOKEN = os.environ.get('GITHUB_TOKEN') |
|
GEMINI_API_KEY = os.environ.get('GEMINI_API_KEY') |
|
|
|
|
|
|
|
|
|
app.layout = dbc.Container([ |
|
dbc.Navbar( |
|
dbc.Container([ |
|
html.A( |
|
dbc.Row([ |
|
dbc.Col(html.Img(src="/assets/logo.png", height="30px")), |
|
dbc.Col(dbc.NavbarBrand("Automated Guide Generator", className="ms-2")), |
|
], |
|
align="center", |
|
className="g-0", |
|
), |
|
href="/", |
|
style={"textDecoration": "none"}, |
|
) |
|
]), |
|
color="primary", |
|
dark=True, |
|
), |
|
|
|
dbc.Row([ |
|
dbc.Col([ |
|
html.H1("Automated Guide Generator", className="text-center my-4"), |
|
html.P("Generate a user guide or administration guide based on the UI-related code in a Git repository using Gemini AI. Select a Git provider, enter repository details, choose the guide type, and let AI create a comprehensive guide.", className="text-center mb-4"), |
|
|
|
dbc.Card([ |
|
dbc.CardBody([ |
|
dbc.Form([ |
|
dbc.Select( |
|
id="git-provider", |
|
options=[ |
|
{"label": "GitHub", "value": "GitHub"}, |
|
{"label": "GitLab", "value": "GitLab"}, |
|
{"label": "Gitea", "value": "Gitea"} |
|
], |
|
placeholder="Select Git Provider", |
|
), |
|
dbc.Input(id="repo-url", type="text", placeholder="Repository URL (owner/repo)"), |
|
dbc.RadioItems( |
|
id="guide-type", |
|
options=[ |
|
{"label": "User Guide", "value": "User Guide"}, |
|
{"label": "Administration Guide", "value": "Administration Guide"} |
|
], |
|
inline=True, |
|
), |
|
dbc.Input(id="exclude-folders", type="text", placeholder="Exclude Folders (comma-separated)"), |
|
dbc.Button("Generate Guide", id="generate-button", color="primary", className="mt-3"), |
|
]) |
|
]) |
|
], className="mb-4"), |
|
|
|
dbc.Spinner( |
|
dbc.Card([ |
|
dbc.CardBody([ |
|
html.H4("Generated Guide", className="card-title"), |
|
html.Div([ |
|
dbc.Button("Download DOCX", id="download-docx", color="secondary", className="me-2"), |
|
dbc.Button("Download Markdown", id="download-md", color="secondary"), |
|
], className="mt-3"), |
|
dcc.Download(id="download-docx-file"), |
|
dcc.Download(id="download-md-file"), |
|
]) |
|
], className="mt-4"), |
|
color="primary", |
|
), |
|
], width=6), |
|
dbc.Col([ |
|
dbc.Card([ |
|
dbc.CardBody([ |
|
html.H4("Preview", className="card-title"), |
|
html.Div(id="generated-guide", style={"whiteSpace": "pre-wrap", "height": "400px", "overflowY": "auto"}), |
|
]) |
|
], className="mt-4"), |
|
], width=6), |
|
]) |
|
], fluid=True) |
|
|
|
@app.callback( |
|
[Output("generated-guide", "children"), |
|
Output("download-docx", "n_clicks"), |
|
Output("download-md", "n_clicks")], |
|
[Input("generate-button", "n_clicks")], |
|
[State("git-provider", "value"), |
|
State("repo-url", "value"), |
|
State("guide-type", "value"), |
|
State("exclude-folders", "value")] |
|
) |
|
def update_output(n_clicks, git_provider, repo_url, guide_type, exclude_folders): |
|
if n_clicks is None: |
|
raise PreventUpdate |
|
|
|
def generate_guide_thread(): |
|
nonlocal guide_text, docx_path, md_path |
|
guide_text, docx_path, md_path = generate_guide(git_provider, repo_url, guide_type, exclude_folders) |
|
|
|
guide_text, docx_path, md_path = None, None, None |
|
thread = threading.Thread(target=generate_guide_thread) |
|
thread.start() |
|
thread.join() |
|
|
|
return guide_text, 0, 0 |
|
|
|
@app.callback( |
|
Output("download-docx-file", "data"), |
|
Input("download-docx", "n_clicks"), |
|
prevent_initial_call=True, |
|
) |
|
def download_docx(n_clicks): |
|
if n_clicks is None: |
|
raise PreventUpdate |
|
return dcc.send_file(docx_path, filename="generated_guide.docx") |
|
|
|
@app.callback( |
|
Output("download-md-file", "data"), |
|
Input("download-md", "n_clicks"), |
|
prevent_initial_call=True, |
|
) |
|
def download_md(n_clicks): |
|
if n_clicks is None: |
|
raise PreventUpdate |
|
return dcc.send_file(md_path, filename="generated_guide.md") |
|
|
|
if __name__ == '__main__': |
|
print("Starting the Dash application...") |
|
app.run(debug=True, host='0.0.0.0', port=7860) |
|
print("Dash application has finished running.") |