acecalisto3 commited on
Commit
d0928a8
·
verified ·
1 Parent(s): 9af74bf

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +118 -151
app.py CHANGED
@@ -1,4 +1,3 @@
1
-
2
  import sys
3
  import signal
4
  import warnings
@@ -8,8 +7,7 @@ import spaces
8
  import time
9
  import os
10
  from datetime import datetime
11
- from typing import List, Dict, Optional, Union
12
- from pathlib import Path
13
  import requests
14
  import gradio as gr
15
  import atexit
@@ -17,160 +15,150 @@ import subprocess
17
  import webbrowser
18
  import urllib.parse
19
  import http.client
20
- from functools import lru_cache
21
- import json
22
- from concurrent.futures import ThreadPoolExecutor
23
-
24
- # Security and configuration constants
25
- MAX_RETRIES = 3
26
- REQUEST_TIMEOUT = 30
27
- RATE_LIMIT_THRESHOLD = 10
28
- CACHE_TTL = 300 # 5 minutes cache
29
-
30
- # Suppress warnings and set environment
31
- warnings.filterwarnings('ignore', category=UserWarning)
32
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
33
-
34
- class ConfigManager:
35
- """Configuration management class"""
36
-
37
- @staticmethod
38
- def load_config(config_file: str = 'config.json') -> Dict:
39
- try:
40
- with open(config_file, 'r') as f:
41
- return json.load(f)
42
- except FileNotFoundError:
43
- return {}
44
 
45
- @staticmethod
46
- def save_config(config: Dict, config_file: str = 'config.json') -> None:
47
- with open(config_file, 'w') as f:
48
- json.dump(config, f, indent=4)
49
-
50
- # Example usage
51
- warnings.warn("This is a user warning", UserWarning)
52
-
53
- class SecurityManager:
54
- """Security management class"""
55
-
56
- @staticmethod
57
- def validate_token(token: str) -> bool:
58
- return bool(token and len(token) >= 40)
59
 
60
- @staticmethod
61
- def sanitize_input(input_str: str) -> str:
62
- return urllib.parse.quote(input_str)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
  class GitHubAPI:
65
- """Enhanced GitHub API handler with caching and retry mechanism"""
66
 
67
  def __init__(self, token: str):
68
  self.token = token
69
  self.headers = {
70
  'Authorization': f'token {token}',
71
- 'Accept': 'application/vnd.github.v3+json',
72
- 'User-Agent': 'GitHub-Issue-Manager'
73
  }
74
  self.base_url = "https://api.github.com"
75
- self.session = requests.Session()
76
- self.executor = ThreadPoolExecutor(max_workers=4)
77
 
78
- @lru_cache(maxsize=100)
79
- def _make_request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
80
- """Make HTTP request with retry mechanism"""
81
- for attempt in range(MAX_RETRIES):
82
- try:
83
- response = self.session.request(
84
- method,
85
- f"{self.base_url}/{endpoint}",
86
- headers=self.headers,
87
- timeout=REQUEST_TIMEOUT,
88
- **kwargs
89
- )
90
- response.raise_for_status()
91
- return response
92
- except requests.exceptions.RequestException as e:
93
- if attempt == MAX_RETRIES - 1:
94
- raise
95
- time.sleep(2 ** attempt) # Exponential backoff
 
96
 
97
  def get_repository(self, owner: str, repo: str) -> Dict:
98
- """Get repository information with caching"""
99
- response = self._make_request('GET', f"repos/{owner}/{repo}")
100
- return response.json()
 
 
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- async def get_issues_async(self, owner: str, repo: str, state: str = 'open') -> List[Dict]:
103
- """Asynchronously fetch repository issues"""
104
  try:
105
- response = await self.executor.submit(
106
- self._make_request,
107
- 'GET',
108
- f"repos/{owner}/{repo}/issues",
109
- params={'state': state}
110
- )
111
  issues = response.json()
112
  return [issue for issue in issues if 'pull_request' not in issue]
 
 
 
113
  except Exception as e:
114
  logger.error(f"Error fetching issues: {str(e)}")
115
  return []
116
 
117
  class GitHubBot:
118
- """Enhanced GitHub bot implementation"""
119
 
120
  def __init__(self):
121
  self.github_api = None
122
- self.config_manager = ConfigManager()
123
- self.security_manager = SecurityManager()
124
 
125
- def initialize_api(self, token: str) -> None:
126
- """Initialize GitHub API with validation"""
127
- if not self.security_manager.validate_token(token):
128
- raise ValueError("Invalid GitHub token")
129
  self.github_api = GitHubAPI(token)
130
 
131
- async def fetch_issues(self, token: str, username: str, repo: str) -> List[Dict]:
132
- """Fetch issues with async support"""
133
  try:
134
  self.initialize_api(token)
135
- return await self.github_api.get_issues_async(username, repo)
136
  except Exception as e:
137
  logger.error(f"Error fetching issues: {str(e)}")
138
  return []
139
 
140
- def resolve_issue(self, token: str, username: str, repo: str,
141
- issue_number: int, resolution: str, forked_repo: str) -> str:
142
- """Enhanced issue resolution with better error handling"""
143
  try:
144
  self.initialize_api(token)
145
- repo_info = self.github_api.get_repository(username, repo)
146
-
147
- # Create resolution directory if it doesn't exist
148
- resolution_dir = Path('resolutions')
149
- resolution_dir.mkdir(exist_ok=True)
150
 
151
- # Save resolution with sanitized content
152
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
153
- resolution_file = resolution_dir / f"resolution_{issue_number}_{timestamp}.md"
154
-
155
- with resolution_file.open('w') as f:
156
  f.write(f"# Resolution for Issue #{issue_number}\n\n{resolution}")
157
 
158
- # Handle git operations with better error checking
159
- repo_path = Path('/tmp') / forked_repo.split('/')[-1]
160
- if repo_path.exists():
161
- shutil.rmtree(repo_path)
162
-
163
- # Clone and setup repository
164
- result = subprocess.run(
165
- ['git', 'clone', forked_repo],
166
- cwd='/tmp',
167
- capture_output=True,
168
- text=True
169
- )
170
- if result.returncode != 0:
171
- raise RuntimeError(f"Git clone failed: {result.stderr}")
 
 
172
 
173
- # Continue with remaining implementation...
174
  return f"Resolution saved: {resolution_file}"
175
 
176
  except Exception as e:
@@ -246,10 +234,17 @@ custom_css = """
246
  }
247
  """
248
 
249
- def create_gradio_interface():
250
- """Create and configure Gradio interface with custom styling"""
251
- bot = GitHubBot()
252
 
 
 
 
 
 
 
 
 
253
  with gr.Blocks(css=custom_css, theme=gr.themes.Base()) as demo:
254
  gr.HTML("""
255
  <div class="container">
@@ -373,37 +368,9 @@ def signal_handler(signum, frame):
373
  cleanup()
374
  sys.exit(0)
375
 
376
- if __name__ == "__main__":
377
- # Register cleanup handlers
378
- atexit.register(cleanup)
379
- signal.signal(signal.SIGINT, signal_handler)
380
- signal.signal(signal.SIGTERM, signal_handler)
381
-
382
- try:
383
- # Create and launch interface
384
- demo = create_gradio_interface()
385
-
386
- # Configure launch parameters
387
- is_on_spaces = os.getenv("SPACE_ID") is not None
388
- launch_kwargs = {
389
- "server_name": "0.0.0.0",
390
- "server_port": 7860,
391
- "debug": True,
392
- }
393
 
394
- if not is_on_spaces:
395
- launch_kwargs["share"] = True
396
- logger.info("Running in local mode with public URL enabled")
397
- else:
398
- logger.info("Running on Hugging Face Spaces")
399
-
400
- # Launch application
401
- logger.info("Launching Gradio interface...")
402
- demo = demo.queue()
403
- demo.launch(**launch_kwargs)
404
-
405
- except Exception as e:
406
- logger.error(f"Error launching application: {str(e)}")
407
- raise
408
- finally:
409
- logger.info("Application shutdown")
 
 
1
  import sys
2
  import signal
3
  import warnings
 
7
  import time
8
  import os
9
  from datetime import datetime
10
+ from typing import List, Dict
 
11
  import requests
12
  import gradio as gr
13
  import atexit
 
15
  import webbrowser
16
  import urllib.parse
17
  import http.client
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
+ # Suppress warnings
20
+ warnings.filterwarnings('ignore', category='User Warning')
21
+ os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ def initialize_environment():
24
+ """Initialize application environment and configurations"""
25
+ # Create necessary directories
26
+ directories = ['logs', 'resolutions', 'repos']
27
+ for directory in directories:
28
+ os.makedirs(directory, exist_ok=True)
29
+
30
+ # Configure logging
31
+ logging.basicConfig(
32
+ level=logging.DEBUG,
33
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
34
+ handlers=[
35
+ logging.FileHandler(log_file),
36
+ logging.StreamHandler()
37
+ ])
38
+ )
39
+ # Set up global exception handler
40
+ def handle_exception(exc_type, exc_value, exc_traceback):
41
+ if issubclass(exc_type, KeyboardInterrupt):
42
+ sys.__excepthook__(exc_type, exc_value, exc_traceback)
43
+ return
44
+ logging.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
45
+
46
+ sys.excepthook = handle_exception
47
+ return logging.getLogger(__name__)
48
+
49
+ logger = initialize_environment()
50
 
51
  class GitHubAPI:
52
+ """GitHub API handler with rate limiting and error handling"""
53
 
54
  def __init__(self, token: str):
55
  self.token = token
56
  self.headers = {
57
  'Authorization': f'token {token}',
58
+ 'Accept': 'application/vnd.github.v3+json'
 
59
  }
60
  self.base_url = "https://api.github.com"
 
 
61
 
62
+ def _check_rate_limit(self) -> bool:
63
+ """Check and handle GitHub API rate limits"""
64
+ try:
65
+ response = requests.get(f"{self.base_url}/rate_limit", headers=self.headers)
66
+ response.raise_for_status()
67
+ limits = response.json()
68
+ remaining = limits['resources']['core']['remaining']
69
+ reset_time = limits['resources']['core']['reset']
70
+
71
+ if remaining < 10:
72
+ wait_time = max(0, reset_time - int(time.time()))
73
+ if wait_time > 0:
74
+ logger.warning(f"Rate limit nearly exceeded. Waiting {wait_time} seconds...")
75
+ time.sleep(wait_time)
76
+ return False
77
+ return True
78
+ except Exception as e:
79
+ logger.error(f"Error checking rate limit: {str(e)}")
80
+ return True
81
 
82
  def get_repository(self, owner: str, repo: str) -> Dict:
83
+ """Get repository information"""
84
+ try:
85
+ response = requests.get(f"{self.base_url}/repos/{owner}/{repo}", headers=self.headers)
86
+ response.raise_for_status()
87
+ return response.json()
88
+ except requests.HTTPError as e:
89
+ logger.error(f"HTTP error getting repository info: {str(e)}")
90
+ raise
91
+ except Exception as e:
92
+ logger.error(f"Error getting repository info: {str(e)}")
93
+ raise
94
+
95
+ def get_issues(self, owner: str, repo: str, state: str = 'open') -> List[Dict]:
96
+ """Fetch repository issues"""
97
+ if not self._check_rate_limit():
98
+ return []
99
 
 
 
100
  try:
101
+ response = requests.get(f"{self.base_url}/repos/{owner}/{repo}/issues", headers=self.headers, params={'state': state})
102
+ response.raise_for_status()
 
 
 
 
103
  issues = response.json()
104
  return [issue for issue in issues if 'pull_request' not in issue]
105
+ except requests.HTTPError as e:
106
+ logger.error(f"HTTP error fetching issues: {str(e)}")
107
+ return []
108
  except Exception as e:
109
  logger.error(f"Error fetching issues: {str(e)}")
110
  return []
111
 
112
  class GitHubBot:
113
+ """Main GitHub bot implementation"""
114
 
115
  def __init__(self):
116
  self.github_api = None
 
 
117
 
118
+ def initialize_api(self, token: str):
119
+ """Initialize GitHub API with token"""
 
 
120
  self.github_api = GitHubAPI(token)
121
 
122
+ def fetch_issues(self, token: str, username: str, repo: str) -> List[Dict]:
123
+ """Fetch issues from GitHub repository"""
124
  try:
125
  self.initialize_api(token)
126
+ return self.github_api.get_issues(username, repo)
127
  except Exception as e:
128
  logger.error(f"Error fetching issues: {str(e)}")
129
  return []
130
 
131
+ def resolve_issue(self)
132
+ , token: str, username: str, repo: str, issue_number: int, resolution: str, forked_repo: str) -> str:
133
+ """Resolve a GitHub issue and submit PR."""
134
  try:
135
  self.initialize_api(token)
136
+ self.github_api.get_repository(username, repo)
 
 
 
 
137
 
138
+ # Create resolution file
139
  timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
140
+ resolution_file = f"resolutions/resolution_{issue_number}_{timestamp}.md"
141
+
142
+ with open(resolution_file, "w") as f:
143
  f.write(f"# Resolution for Issue #{issue_number}\n\n{resolution}")
144
 
145
+ # Clone the forked repo
146
+ subprocess.run(['git', '-C', '/tmp', 'clone', forked_repo])
147
+
148
+ # Change to the cloned directory
149
+ subprocess.run(['cd', '/tmp/' + forked_repo.split('/')[-1]])
150
+
151
+ # Assuming manual intervention now
152
+ answer = input("Apply the fix manually and stage the changes (press ENTER)? ")
153
+
154
+ # Commit and push the modifications
155
+ subprocess.run(['git', 'add', '.'])
156
+ subprocess.run(['git', 'commit', '-m', f"Resolved issue #{issue_number} ({urllib.parse.quote(resolution)})"])
157
+ subprocess.run(['git', 'push', 'origin', 'HEAD'])
158
+
159
+ # Open Pull Request page
160
+ webbrowser.open('https://github.com/' + forked_repo.split('/')[-1] + '/compare/master...' + username + ':' + forked_repo.split('/')[-1] + '_resolved_issue_' + str(issue_number))
161
 
 
162
  return f"Resolution saved: {resolution_file}"
163
 
164
  except Exception as e:
 
234
  }
235
  """
236
 
237
+ def greet(name):
238
+ return f"Hello {name}!"
 
239
 
240
+ def create_gradio_interface():
241
+ with gr.Blocks() as demo:
242
+ name = gr.Textbox(label="Name")
243
+ output = gr.Textbox(label="Output")
244
+ greet_btn = gr.Button("Greet")
245
+ greet_btn.click(fn=greet, inputs=name, outputs=output)
246
+ return demo
247
+
248
  with gr.Blocks(css=custom_css, theme=gr.themes.Base()) as demo:
249
  gr.HTML("""
250
  <div class="container">
 
368
  cleanup()
369
  sys.exit(0)
370
 
371
+ logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
 
373
+ if __name__ == "__main__":
374
+ demo = create_gradio_interface()
375
+ demo.launch(server_name="0.0.0.0", server_port=7860)
376
+ logger.info("Launch successful!")