GitBot / app.py
acecalisto3's picture
Update app.py
73ffbb8 verified
raw
history blame
16.1 kB
import sys
import signal
import shutil
import logging
import time
import os
from datetime import datetime
from typing import List, Dict, Any
import requests
import gradio as gr
import atexit
import subprocess
import webbrowser
import urllib.parse
import warnings
import torch
import flask
from flask import Flask, request, jsonify
import spaces
from accelerate import Accelerator
from threading import Thread
# Constants
INPUT_DIRECTORY = 'input'
OUTPUT_DIRECTORY = 'output'
LOGS_DIRECTORY = 'logs'
RESOLUTIONS_DIRECTORY = 'resolutions'
REPOS_DIRECTORY = 'repos'
# Set up logging
def initialize_logger() -> logging.Logger:
log_file = f"{LOGS_DIRECTORY}/github_bot_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file),
logging.StreamHandler()
]
)
return logging.getLogger(__name__)
# Set up environment
@spaces.GPU
def initialize_environment(input_file: str, output_directory: str) -> logging.Logger:
directories = [LOGS_DIRECTORY, RESOLUTIONS_DIRECTORY, REPOS_DIRECTORY, input_file, output_directory]
for directory in directories:
os.makedirs(directory, exist_ok=True)
return initialize_logger()
# GitHub API handler
class GitHubAPI:
def __init__(self, token: str):
self.token = token
self.headers = {
'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3+json'
}
self.base_url = "https://api.github.com"
def _check_rate_limit(self) -> bool:
try:
response = requests.get(f"{self.base_url}/rate_limit", headers=self.headers)
response.raise_for_status()
limits = response.json()
remaining = limits['resources']['core']['remaining']
reset_time = limits['resources']['core']['reset']
if remaining < 10:
wait_time = max(0, reset_time - int(time.time()))
if wait_time > 0:
logger.warning(f"Rate limit nearly exceeded. Waiting {wait_time} seconds...")
time.sleep(wait_time)
return False
return True
except requests.exceptions.RequestException as e:
logger.error(f"Error checking rate limit: {str(e)}")
return True
def get_repository(self, owner: str, repo: str) -> Dict[str, Any]:
try:
response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error getting repository: {str(e)}")
return {}
def get_issues(self, owner: str, repo: str) -> List[Dict[str, Any]]:
try:
response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error getting issues: {str(e)}")
return []
def create_issue(self, owner: str, repo: str, title: str, body: str) -> Dict[str, Any]:
try:
response = requests.post(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, json={'title': title, 'body': body})
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error creating issue: {str(e)}")
return {}
def update_issue(self, owner: str, repo: str, issue_number: int, title: str, body: str) -> Dict[str, Any]:
try:
response = requests.patch(f"{self.base_url}/repos/{owner}/{repo}/issues/{issue_number}", headers=self.headers, json={'title': title, 'body': body})
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
logger.error(f"Error updating issue: {str(e)}")
return {}
app = Flask(__name__)
@app.route('/fetch-issues', methods=['POST'])
def fetch_issues() -> Dict[str, Any]:
data = request.get_json()
github_token = data['githubToken']
repo_url = data['repoUrl']
owner, repo = repo_url.split('/')[-2:]
github_api = GitHubAPI (github_token)
issues = github_api.get_issues(owner , repo)
return jsonify({'issues': issues})
@app.route('/resolve-issue', methods=['POST'])
def resolve_issue() -> Dict[str, Any]:
data = request.get_json()
github_token = data['githubToken']
repo_url = data['repoUrl']
issue = data['issue']
resolution = data['resolution']
forked_repo_url = data['forkedRepoUrl']
owner, repo = repo_url.split('/')[-2:]
github_api = GitHubAPI(github_token)
issue_number = issue['number']
github_api.update_issue(owner, repo, issue_number, issue['title'], resolution)
return jsonify({'output': f"Issue {issue_number} resolved"})
@app.route('/extract-info', methods=['POST'])
def extract_info() -> Dict[str, Any]:
data = request.get_json()
url = data['url']
# Extract info from URL
return jsonify({'output': f"Info extracted from {url}"})
def run_flask_app() -> None:
app.run(debug=True, use_reloader=False)
# HTML and CSS integration
custom_html = """
<!DOCTYPE html>
<html>
<head>
<title>GitHub Issue Manager</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/daisyui@3.1.0/dist/full.css" rel="stylesheet" type="text/css" />
<style>
body {
background: linear-gradient(to bottom right, #121212, #303030) !important;
color: #e0e0e0;
font-family: 'Roboto', sans-serif;
overflow-x: hidden; /* Prevent horizontal scrollbar */
}
.card {
border-radius: 1.5rem;
box-shadow: 0 15px 25px rgba(0, 0, 0, 0.2);
margin-bottom: 20px;
}
.input, .select, .textarea {
border: 2px solid #4a4a4a;
border-radius: 0.75rem;
padding-inline: 1.5rem;
padding-block: 0.75rem;
}
h1, h2, h3 {
color: #e0e0e0;
}
.output-area {
padding: 1.5rem;
border-radius: 0.75rem;
}
.btn {
border-radius: 0.75rem;
padding-block: 0.75rem;
padding-inline: 1.5rem;
}
.btn-primary {
background-color: #7928CA;
color: white;
border: 2px solid #7928CA;
}
.btn-success {
background-color: #22c55e;
color: white;
border: 2px solid #22c55e;
}
.loading {
display: none;
}
</style>
</head>
<body class="bg-gray-900">
<div class="container mx-auto p-4">
<h1 class="text-4xl md:text-5xl font-extrabold text-center mb-8">GitHub Issue Manager</h1>
<!-- GitHub Token & Repo URL -->
<div class="card bg-gray-800 p-8"> ```html
<div class="form-control">
<label class="label">
<span class="label-text">GitHub Token</span>
</label>
<input type="password" placeholder="Enter your GitHub Personal Access Token" class="input input-bordered input-primary" id="github-token">
</div>
<div class="form-control">
<label class="label">
<span class="label-text">Repository URL</span>
</label>
<input type="text" placeholder="Enter the full GitHub repository URL" class="input input-bordered input-primary" id="repo-url">
</div>
</div>
<!-- Fetch & Resolve Section -->
<div class="card bg-gray-800 p-8">
<div class="flex justify-between gap-4">
<button class="btn btn-primary" id="fetch-issues">Fetch Issues</button>
<select class="select select-primary w-full max-w-xs" id="issue-dropdown">
<option disabled selected>Select Issue</option>
</select>
</div>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Resolution</span>
</label>
<textarea class="textarea textarea-primary" placeholder="Enter the resolution details" id="resolution-textarea"></textarea>
</div>
<div class="form-control mt-4">
<label class="label">
<span class="label-text">Forked Repository URL</span>
</label>
<input type="text" placeholder="URL to your forked repository (Optional)" class="input input-bordered input-primary" id="forked-repo-url">
</div>
<div class="form-control mt-4">
<button class="btn btn-success" id="resolve-issue">Resolve Issue</button>
</div>
</div>
<!-- Output Area -->
<div class="card bg-gray-800 p-8 mt-4">
<label class="label">
<span class="label-text">Output</span>
</label>
<textarea class="textarea textarea-primary h-48" id="output-textarea" readonly></textarea>
</div>
<!-- URL to Extract -->
<div class="card bg-gray-800 p-8 mt-4">
<div class="form-control">
<label class="label">
<span class="label-text">URL</span>
</label>
<input type="text" placeholder="Enter a URL to extract information" class="input input-bordered input-primary" id="url-textbox">
</div>
<div class="form-control">
<button class="btn btn-primary" id="extract-info">Extract Info</button>
</div>
</div>
<div class="loading" id="loading-indicator">Loading...</div>
</div>
<script>
const githubTokenInput = document.getElementById('github-token');
const repoUrlInput = document.getElementById('repo-url');
const fetchIssuesButton = document.getElementById('fetch-issues');
const issueDropdown = document.getElementById('issue-dropdown');
const resolutionTextarea = document.getElementById('resolution-textarea');
const forkedRepoUrlInput = document.getElementById('forked-repo-url');
const resolveIssueButton = document.getElementById('resolve-issue');
const outputTextarea = document.getElementById('output-textarea');
const urlTextbox = document.getElementById('url-textbox');
const extractInfoButton = document.getElementById('extract-info');
const loadingIndicator = document.getElementById('loading-indicator');
const showLoading = () => {
loadingIndicator.style.display = 'block';
fetchIssuesButton.disabled = true;
resolveIssueButton.disabled = true;
extractInfoButton.disabled = true;
};
const hideLoading = () => {
loadingIndicator.style.display = 'none';
fetchIssuesButton.disabled = false;
resolveIssueButton.disabled = false;
extractInfoButton.disabled = false;
};
fetchIssuesButton.addEventListener('click', async () => {
const token = githubTokenInput.value;
const repoUrl = repoUrlInput.value;
if (!token || !repoUrl) {
outputTextarea.value = "Please provide both GitHub Token and Repository URL.";
return;
}
showLoading();
try {
const response = await fetch('/fetch-issues', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ githubToken: token, repoUrl: repoUrl }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.error) {
outputTextarea.value = data.error;
} else {
issueDropdown.innerHTML = '';
data.issues.forEach(issue => {
const option = document.createElement('option');
option.value = JSON.stringify(issue);
option.text = `${issue.number}: ${issue.title}`;
issueDropdown.add(option);
});
}
} catch (error) {
outputTextarea.value = `Error fetching issues: ${error.message}`;
} finally {
hideLoading();
}
});
resolveIssueButton.addEventListener('click', async () => {
const token = githubTokenInput.value;
const repoUrl = repoUrlInput.value;
const issue = JSON.parse(issueDropdown.value);
const resolution = resolutionTextarea.value;
const forkedRepoUrl = forkedRepoUrlInput.value;
if (!token || !repoUrl || !issue || !resolution) {
outputTextarea.value = "Please provide all required fields.";
return;
}
showLoading();
try {
const response = await fetch('/resolve-issue', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ githubToken: token, repoUrl: repoUrl, issue: issue, resolution: resolution, forkedRepoUrl: forkedRepoUrl }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.error) {
outputTextarea.value = data.error;
} else {
outputTextarea.value = data.output;
}
} catch (error) {
outputTextarea.value = `Error resolving issue: ${error.message}`;
} finally {
hideLoading();
}
});
extractInfoButton.addEventListener('click', async () => {
const url = urlTextbox.value;
if (!url) {
outputTextarea.value = "Please provide a URL.";
return;
}
showLoading();
try {
const response = await fetch('/extract-info', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ url: url }),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.error) {
outputTextarea.value = data.error;
} else {
outputTextarea.value = data.output;
}
} catch (error) {
outputTextarea.value = `Error extracting info: ${error.message}`;
} finally {
hideLoading();
}
});
</script>
</body>
</html>
"""
def create_gradio_interface() -> gr.Blocks:
with gr.Blocks(css=None, theme=None) as demo:
gr.HTML(custom_html)
return demo
# Cleanup function
def cleanup() -> None:
try:
temp_dirs = [REPOS_DIRECTORY]
for dir_name in temp_dirs:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
logging.shutdown()
except Exception as e:
print(f"Error during cleanup: {str(e)}")
# Signal handler
def signal_handler(signum: int, frame) -> None:
logger.info(f"Received signal {signum}")
cleanup()
sys.exit(0)
if __name__ == "__main__":
# Register cleanup handlers
atexit.register(cleanup)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Run Flask app in a separate thread
flask_thread = Thread(target=run_flask_app)
flask_thread.start()
# Create Gradio interface
demo = create_gradio_interface()
demo.launch()