nihalaninihal commited on
Commit
df3abfa
Β·
verified Β·
1 Parent(s): 3198479

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +768 -251
app.py CHANGED
@@ -1,5 +1,3 @@
1
-
2
-
3
  import gradio as gr
4
  import google.generativeai as genai
5
  import os
@@ -15,9 +13,6 @@ import tempfile
15
  from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
16
  import time
17
  import os
18
- import re
19
- from fpdf import FPDF
20
- import requests
21
 
22
 
23
 
@@ -263,175 +258,12 @@ class RepositoryAnalyzer:
263
  "total_contributors": len(contributor_data),
264
  "contributors": contributor_data
265
  }
266
- def create_pdf_from_markdown(markdown_text: str, filename: str) -> str:
267
- """Convert markdown text to PDF"""
268
- class PDF(FPDF):
269
- def header(self):
270
- self.set_font('Arial', 'B', 12)
271
- self.cell(0, 10, 'Repository Analysis Report', 0, 1, 'C')
272
- self.ln(10)
273
-
274
- def footer(self):
275
- self.set_y(-15)
276
- self.set_font('Arial', 'I', 8)
277
- self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
278
-
279
- pdf = PDF()
280
- pdf.add_page()
281
- pdf.set_auto_page_break(auto=True, margin=15)
282
-
283
- # Process markdown sections
284
- sections = markdown_text.split('\n## ')
285
-
286
- # Handle main title
287
- if sections[0].startswith('# '):
288
- title = sections[0].split('\n')[0].replace('# ', '')
289
- pdf.set_font('Arial', 'B', 16)
290
- pdf.cell(0, 10, title, 0, 1, 'C')
291
- pdf.ln(5)
292
- content = '\n'.join(sections[0].split('\n')[1:])
293
- sections[0] = content
294
-
295
- for section in sections:
296
- if section:
297
- # Extract section title and content
298
- lines = section.split('\n')
299
- if section == sections[0]: # First section (after main title)
300
- section_title = ''
301
- content = lines
302
- else:
303
- section_title = lines[0]
304
- content = lines[1:]
305
-
306
- # Add section title
307
- if section_title:
308
- pdf.set_font('Arial', 'B', 14)
309
- # Remove emojis from section titles
310
- clean_title = re.sub(r'[^\x00-\x7F]+', '', section_title)
311
- pdf.cell(0, 10, clean_title.strip(), 0, 1, 'L')
312
- pdf.ln(5)
313
-
314
- # Add content
315
- pdf.set_font('Arial', '', 11)
316
- for line in content:
317
- if line.strip():
318
- # Remove markdown formatting and emojis
319
- clean_line = re.sub(r'[\*\[\]]', '', line)
320
- clean_line = re.sub(r'[^\x00-\x7F]+', '', clean_line)
321
-
322
- if line.startswith('- '):
323
- pdf.cell(10, 5, '', 0, 0)
324
- pdf.multi_cell(0, 5, clean_line[2:])
325
- else:
326
- pdf.multi_cell(0, 5, clean_line)
327
- pdf.ln(5)
328
-
329
- # Save PDF
330
- pdf_path = f"{filename}.pdf"
331
- pdf.output(pdf_path)
332
- return pdf_path
333
-
334
- def download_noto_font():
335
- """Download Google's Noto Color Emoji font if not already present"""
336
- font_path = "NotoColorEmoji.ttf"
337
- if not os.path.exists(font_path):
338
- url = "https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf"
339
- response = requests.get(url)
340
- with open(font_path, "wb") as f:
341
- f.write(response.content)
342
- return font_path
343
-
344
- class PDFWithEmoji(FPDF):
345
- def __init__(self):
346
- super().__init__()
347
- self.add_font('DejaVu', '', 'DejaVuSansCondensed.ttf', uni=True)
348
- self.add_font('Noto', '', 'NotoColorEmoji.ttf', uni=True)
349
-
350
- def header(self):
351
- self.set_font('DejaVu', '', 12)
352
- self.cell(0, 10, 'πŸ“Š Repository Analysis Report', 0, 1, 'C')
353
- self.ln(10)
354
-
355
- def footer(self):
356
- self.set_y(-15)
357
- self.set_font('DejaVu', '', 8)
358
- self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
359
-
360
- def write_with_emoji(self, text):
361
- """Write text with proper emoji support"""
362
- self.set_font('DejaVu', '', 11)
363
- self.multi_cell(0, 5, text)
364
-
365
- def create_pdf_report(markdown_text: str) -> str:
366
- """Create a PDF report with full emoji and Unicode support"""
367
- # Ensure required fonts are available
368
- if not os.path.exists("DejaVuSansCondensed.ttf"):
369
- url = "https://github.com/dejavu-fonts/dejavu-fonts/raw/master/ttf/DejaVuSansCondensed.ttf"
370
- response = requests.get(url)
371
- with open("DejaVuSansCondensed.ttf", "wb") as f:
372
- f.write(response.content)
373
-
374
- download_noto_font()
375
-
376
- # Create PDF
377
- pdf = PDFWithEmoji()
378
- pdf.add_page()
379
- pdf.set_auto_page_break(auto=True, margin=15)
380
-
381
- # Process markdown text
382
- sections = markdown_text.split('\n## ')
383
-
384
- # Handle main title
385
- if sections[0].startswith('# '):
386
- title = sections[0].split('\n')[0].replace('# ', '')
387
- pdf.set_font('DejaVu', '', 16)
388
- pdf.cell(0, 10, title, 0, 1, 'C')
389
- pdf.ln(5)
390
- content = '\n'.join(sections[0].split('\n')[1:])
391
- sections[0] = content
392
-
393
- # Process each section
394
- for section in sections:
395
- if section:
396
- lines = section.split('\n')
397
- if section == sections[0]: # First section
398
- section_title = ''
399
- content = lines
400
- else:
401
- section_title = lines[0]
402
- content = lines[1:]
403
-
404
- # Add section title
405
- if section_title:
406
- pdf.set_font('DejaVu', '', 14)
407
- pdf.cell(0, 10, section_title, 0, 1, 'L')
408
- pdf.ln(5)
409
-
410
- # Add content
411
- pdf.set_font('DejaVu', '', 11)
412
- for line in content:
413
- if line.strip():
414
- if line.strip().startswith('- '):
415
- pdf.cell(10, 5, 'β€’', 0, 0)
416
- pdf.write_with_emoji(line.strip()[2:])
417
- pdf.ln()
418
- else:
419
- pdf.write_with_emoji(line.strip())
420
- pdf.ln()
421
-
422
- # Save PDF
423
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
424
- pdf_path = f"repo_analysis_{timestamp}.pdf"
425
- pdf.output(pdf_path)
426
- return pdf_path
427
 
428
  @retry(
429
  retry=retry_if_exception_type(Exception),
430
  stop=stop_after_attempt(3),
431
  wait=wait_exponential(multiplier=1, min=4, max=10)
432
  )
433
-
434
-
435
  def analyze_repository(repo_url: str, progress=gr.Progress()) -> Tuple[str, str, str]:
436
  """Analyze repository and generate LLM summary with rate limit handling"""
437
  try:
@@ -531,31 +363,12 @@ Please provide detailed analysis for each section while maintaining the formatti
531
  json.dump(analysis_data, f, indent=2)
532
  analysis_file = f.name
533
 
534
- progress(0.9, desc="Generating PDF report...")
535
- try:
536
- pdf_path = create_pdf_report(response.text)
537
- except Exception as pdf_error:
538
- print(f"PDF generation error: {str(pdf_error)}")
539
- pdf_path = ""
540
-
541
-
542
-
543
- # Generate PDF
544
- pdf_path = create_pdf_from_markdown(response.text, f"analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}")
545
-
546
- with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
547
- json.dump(analysis_data, f, indent=2)
548
- analysis_file = f.name
549
-
550
-
551
-
552
-
553
- progress(1.0, desc="✨ Analysis complete!")
554
- return response.text, analysis_file, pdf_path, "βœ… Analysis completed successfully!"
555
 
556
  except Exception as e:
557
  error_message = f"❌ Error analyzing repository: {str(e)}"
558
- return "", "", "", error_message # Return 4 empty values when there's an error
559
 
560
  def create_chat_session() -> Any:
561
  """Create a new chat session for follow-up questions"""
@@ -613,7 +426,6 @@ def ask_question(question: str, analysis_file: str, chat_history: List[Tuple[str
613
 
614
 
615
 
616
- # Create Gradio interface
617
  # Create Gradio interface
618
  with gr.Blocks(theme=gr.themes.Soft()) as app:
619
  gr.Markdown("""
@@ -623,7 +435,8 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
623
  1. πŸ“Š Analyze repository structure and patterns
624
  2. πŸ’‘ Generate insights about development practices
625
  3. πŸ’­ Allow you to ask follow-up questions about the analysis
626
- 4. πŸ“‘ Generate a downloadable PDF report
 
627
  """)
628
 
629
  with gr.Row():
@@ -632,24 +445,16 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
632
  placeholder="https://github.com/owner/repo",
633
  scale=4
634
  )
 
635
 
636
- with gr.Row():
637
- with gr.Column(scale=1):
638
- analyze_btn = gr.Button("πŸ” Analyze", variant="primary")
639
- download_pdf_btn = gr.Button("πŸ“„ Download PDF", variant="secondary")
640
-
641
- # Status message
642
  status_msg = gr.Markdown("", elem_id="status_message")
643
-
644
- # File output for PDF
645
- pdf_output = gr.File(
646
- label="Analysis Report",
647
- visible=False,
648
- interactive=True
649
- )
650
-
651
  with gr.Row():
652
- summary = gr.Markdown(label="Analysis Summary")
 
 
 
653
 
654
  with gr.Row():
655
  chatbot = gr.Chatbot(
@@ -667,32 +472,13 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
667
  ask_btn = gr.Button("πŸ’­ Ask", variant="primary", scale=1)
668
  clear_btn = gr.Button("πŸ—‘οΈ Clear Chat", variant="secondary", scale=1)
669
 
670
- # Hidden states
671
  analysis_file = gr.State("")
672
- current_pdf_path = gr.State("")
673
-
674
- def handle_pdf_download(pdf_path):
675
- """Handle PDF download when button is clicked"""
676
- if pdf_path and os.path.exists(pdf_path):
677
- return {
678
- pdf_output: pdf_path
679
- }
680
- return {
681
- pdf_output: None
682
- }
683
 
684
  def clear_outputs():
685
- """Clear all outputs"""
686
- return {
687
- summary: "",
688
- chatbot: [],
689
- question: "",
690
- status_msg: "",
691
- pdf_output: None,
692
- current_pdf_path: ""
693
- }
694
 
695
- # Event handlers
696
  analyze_btn.click(
697
  fn=lambda: "⏳ Analysis in progress...",
698
  inputs=None,
@@ -701,30 +487,15 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
701
  ).then(
702
  analyze_repository,
703
  inputs=[repo_url],
704
- outputs=[summary, analysis_file, current_pdf_path, status_msg]
705
- ).then(
706
- lambda: gr.update(visible=True),
707
- None,
708
- download_pdf_btn
709
- )
710
-
711
- # PDF download handler
712
- download_pdf_btn.click(
713
- handle_pdf_download,
714
- inputs=[current_pdf_path],
715
- outputs=pdf_output
716
- ).then(
717
- lambda: gr.update(visible=True),
718
- None,
719
- pdf_output
720
  )
721
 
722
  ask_btn.click(
723
  ask_question,
724
  inputs=[question, analysis_file, chatbot],
725
- outputs=[chatbot]
726
  ).then(
727
- lambda: "",
728
  None,
729
  question,
730
  queue=False
@@ -733,14 +504,760 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
733
  clear_btn.click(
734
  clear_outputs,
735
  inputs=None,
736
- outputs=[summary, chatbot, question, status_msg, pdf_output, current_pdf_path]
 
737
  )
738
 
739
-
740
-
741
  # Launch the app
742
  if __name__ == "__main__":
743
  app.launch(
744
  share=True,
745
  debug=True
746
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
  import google.generativeai as genai
3
  import os
 
13
  from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
14
  import time
15
  import os
 
 
 
16
 
17
 
18
 
 
258
  "total_contributors": len(contributor_data),
259
  "contributors": contributor_data
260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
  @retry(
263
  retry=retry_if_exception_type(Exception),
264
  stop=stop_after_attempt(3),
265
  wait=wait_exponential(multiplier=1, min=4, max=10)
266
  )
 
 
267
  def analyze_repository(repo_url: str, progress=gr.Progress()) -> Tuple[str, str, str]:
268
  """Analyze repository and generate LLM summary with rate limit handling"""
269
  try:
 
363
  json.dump(analysis_data, f, indent=2)
364
  analysis_file = f.name
365
 
366
+ progress(1.0, desc="Analysis complete!")
367
+ return response.text, analysis_file, "βœ… Analysis completed successfully!"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
 
369
  except Exception as e:
370
  error_message = f"❌ Error analyzing repository: {str(e)}"
371
+ return "", "", error_message
372
 
373
  def create_chat_session() -> Any:
374
  """Create a new chat session for follow-up questions"""
 
426
 
427
 
428
 
 
429
  # Create Gradio interface
430
  with gr.Blocks(theme=gr.themes.Soft()) as app:
431
  gr.Markdown("""
 
435
  1. πŸ“Š Analyze repository structure and patterns
436
  2. πŸ’‘ Generate insights about development practices
437
  3. πŸ’­ Allow you to ask follow-up questions about the analysis
438
+
439
+ Enter a GitHub repository URL (e.g., `https://github.com/owner/repo`)
440
  """)
441
 
442
  with gr.Row():
 
445
  placeholder="https://github.com/owner/repo",
446
  scale=4
447
  )
448
+ analyze_btn = gr.Button("πŸ” Analyze", variant="primary", scale=1)
449
 
450
+ # Add status message
 
 
 
 
 
451
  status_msg = gr.Markdown("", elem_id="status_message")
452
+
 
 
 
 
 
 
 
453
  with gr.Row():
454
+ # Use Markdown instead of Textbox for better formatting
455
+ summary = gr.Markdown(
456
+ label="Analysis Summary",
457
+ )
458
 
459
  with gr.Row():
460
  chatbot = gr.Chatbot(
 
472
  ask_btn = gr.Button("πŸ’­ Ask", variant="primary", scale=1)
473
  clear_btn = gr.Button("πŸ—‘οΈ Clear Chat", variant="secondary", scale=1)
474
 
475
+ # Hidden state for analysis file
476
  analysis_file = gr.State("")
 
 
 
 
 
 
 
 
 
 
 
477
 
478
  def clear_outputs():
479
+ return "", [], "", ""
 
 
 
 
 
 
 
 
480
 
481
+ # Set up event handlers
482
  analyze_btn.click(
483
  fn=lambda: "⏳ Analysis in progress...",
484
  inputs=None,
 
487
  ).then(
488
  analyze_repository,
489
  inputs=[repo_url],
490
+ outputs=[summary, analysis_file, status_msg],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
491
  )
492
 
493
  ask_btn.click(
494
  ask_question,
495
  inputs=[question, analysis_file, chatbot],
496
+ outputs=[chatbot],
497
  ).then(
498
+ lambda: "", # Clear the question input
499
  None,
500
  question,
501
  queue=False
 
504
  clear_btn.click(
505
  clear_outputs,
506
  inputs=None,
507
+ outputs=[summary, chatbot, question, status_msg],
508
+ queue=False
509
  )
510
 
 
 
511
  # Launch the app
512
  if __name__ == "__main__":
513
  app.launch(
514
  share=True,
515
  debug=True
516
+ )
517
+
518
+
519
+
520
+ # import gradio as gr
521
+ # import google.generativeai as genai
522
+ # import os
523
+ # from dotenv import load_dotenv
524
+ # from github import Github
525
+ # import json
526
+ # from pathlib import Path
527
+ # from datetime import datetime
528
+ # from collections import defaultdict
529
+ # import base64
530
+ # from typing import Dict, List, Any, Optional, Tuple
531
+ # import tempfile
532
+ # from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
533
+ # import time
534
+ # import os
535
+ # import re
536
+ # from fpdf import FPDF
537
+ # import requests
538
+
539
+
540
+
541
+ # # Load environment variables
542
+ # load_dotenv()
543
+
544
+ # # Configure API keys
545
+ # GITHUB_TOKEN = os.getenv("github_api")
546
+ # GEMINI_API_KEY = os.getenv("gemini_api")
547
+
548
+ # if not GITHUB_TOKEN or not GEMINI_API_KEY:
549
+ # raise ValueError("Both GITHUB_TOKEN and GEMINI_API_KEY must be set in environment")
550
+
551
+ # # Initialize APIs
552
+ # gh = Github(GITHUB_TOKEN)
553
+ # genai.configure(api_key=GEMINI_API_KEY)
554
+ # model = genai.GenerativeModel(
555
+ # model_name="gemini-1.5-pro-latest",
556
+ # generation_config = {
557
+ # "temperature": 1,
558
+ # "top_p": 0.95,
559
+ # "top_k": 40,
560
+ # "max_output_tokens": 8192,
561
+ # "response_mime_type": "text/plain",
562
+ # },
563
+
564
+ # safety_settings=[
565
+ # {
566
+ # "category": "HARM_CATEGORY_HARASSMENT",
567
+ # "threshold": "BLOCK_MEDIUM_AND_ABOVE"
568
+ # },
569
+ # {
570
+ # "category": "HARM_CATEGORY_HATE_SPEECH",
571
+ # "threshold": "BLOCK_MEDIUM_AND_ABOVE"
572
+ # },
573
+ # {
574
+ # "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
575
+ # "threshold": "BLOCK_MEDIUM_AND_ABOVE"
576
+ # },
577
+ # {
578
+ # "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
579
+ # "threshold": "BLOCK_MEDIUM_AND_ABOVE"
580
+ # },
581
+ # ]
582
+ # )
583
+
584
+ # RELEVANT_EXTENSIONS = {
585
+ # ".py", ".js", ".ts", ".jsx", ".tsx", ".java", ".cpp", ".c", ".h",
586
+ # ".hpp", ".rb", ".php", ".go", ".rs", ".swift", ".kt"
587
+ # }
588
+
589
+ # class RepositoryAnalyzer:
590
+ # """Handles GitHub repository analysis"""
591
+
592
+ # def __init__(self, repo_url: str):
593
+ # # Extract owner and repo name from URL
594
+ # parts = repo_url.rstrip('/').split('/')
595
+ # if len(parts) < 2:
596
+ # raise ValueError("Invalid repository URL format")
597
+
598
+ # self.repo_name = parts[-1]
599
+ # self.owner = parts[-2]
600
+ # self.repo = gh.get_repo(f"{self.owner}/{self.repo_name}")
601
+ # self.analysis_data: Dict[str, Any] = {}
602
+
603
+ # def analyze(self) -> Dict[str, Any]:
604
+ # """Perform complete repository analysis"""
605
+ # try:
606
+ # # Basic repository information
607
+ # self.analysis_data["basic_info"] = {
608
+ # "name": self.repo.name,
609
+ # "owner": self.repo.owner.login,
610
+ # "description": self.repo.description or "No description available",
611
+ # "stars": self.repo.stargazers_count,
612
+ # "forks": self.repo.forks_count,
613
+ # "created_at": self.repo.created_at.isoformat(),
614
+ # "last_updated": self.repo.updated_at.isoformat(),
615
+ # "primary_language": self.repo.language or "Not specified",
616
+ # }
617
+
618
+ # # Analyze repository structure
619
+ # self.analysis_data["structure"] = self._analyze_structure()
620
+
621
+ # # Analyze code patterns
622
+ # self.analysis_data["code_patterns"] = self._analyze_code_patterns()
623
+
624
+ # # Analyze commit history
625
+ # self.analysis_data["commit_history"] = self._analyze_commits()
626
+
627
+ # # Get contributor statistics
628
+ # self.analysis_data["contributors"] = self._analyze_contributors()
629
+
630
+ # return self.analysis_data
631
+
632
+ # except Exception as e:
633
+ # raise Exception(f"Error analyzing repository: {str(e)}")
634
+
635
+ # def _analyze_structure(self) -> Dict[str, Any]:
636
+ # """Analyze repository structure and organization"""
637
+ # structure = {
638
+ # "files": defaultdict(int),
639
+ # "directories": set(),
640
+ # "total_size": 0,
641
+ # }
642
+
643
+ # try:
644
+ # contents = self.repo.get_contents("")
645
+ # while contents:
646
+ # content = contents.pop(0)
647
+ # if content.type == "dir":
648
+ # structure["directories"].add(content.path)
649
+ # contents.extend(self.repo.get_contents(content.path))
650
+ # else:
651
+ # ext = Path(content.path).suffix.lower()
652
+ # if ext in RELEVANT_EXTENSIONS:
653
+ # structure["files"][ext] += 1
654
+ # structure["total_size"] += content.size
655
+ # except Exception as e:
656
+ # print(f"Error analyzing structure: {str(e)}")
657
+
658
+ # return {
659
+ # "file_types": dict(structure["files"]),
660
+ # "directory_count": len(structure["directories"]),
661
+ # "total_size": structure["total_size"],
662
+ # "file_count": sum(structure["files"].values())
663
+ # }
664
+
665
+ # def _analyze_code_patterns(self) -> Dict[str, Any]:
666
+ # """Analyze code patterns and style"""
667
+ # patterns = {
668
+ # "samples": [],
669
+ # "languages": defaultdict(int),
670
+ # "complexity_metrics": defaultdict(list)
671
+ # }
672
+
673
+ # try:
674
+ # files = self.repo.get_contents("")
675
+ # analyzed = 0
676
+
677
+ # while files and analyzed < 5:
678
+ # file = files.pop(0)
679
+ # if file.type == "dir":
680
+ # files.extend(self.repo.get_contents(file.path))
681
+ # elif Path(file.path).suffix.lower() in RELEVANT_EXTENSIONS:
682
+ # try:
683
+ # content = base64.b64decode(file.content).decode('utf-8')
684
+ # lines = content.splitlines()
685
+
686
+ # if not lines:
687
+ # continue
688
+
689
+ # loc = len([line for line in lines if line.strip()])
690
+ # avg_line_length = sum(len(line) for line in lines) / len(lines)
691
+
692
+ # patterns["samples"].append({
693
+ # "path": file.path,
694
+ # "language": Path(file.path).suffix[1:],
695
+ # "loc": loc,
696
+ # "avg_line_length": round(avg_line_length, 2)
697
+ # })
698
+
699
+ # patterns["languages"][Path(file.path).suffix[1:]] += loc
700
+ # patterns["complexity_metrics"]["loc"].append(loc)
701
+ # patterns["complexity_metrics"]["avg_line_length"].append(avg_line_length)
702
+
703
+ # analyzed += 1
704
+
705
+ # except Exception as e:
706
+ # print(f"Error analyzing file {file.path}: {str(e)}")
707
+ # continue
708
+
709
+ # except Exception as e:
710
+ # print(f"Error in code pattern analysis: {str(e)}")
711
+
712
+ # return patterns
713
+
714
+ # def _analyze_commits(self) -> Dict[str, Any]:
715
+ # """Analyze commit history and patterns"""
716
+ # commit_data = []
717
+ # commit_times = []
718
+
719
+ # try:
720
+ # commits = list(self.repo.get_commits()[:100]) # Get last 100 commits
721
+
722
+ # for commit in commits:
723
+ # try:
724
+ # commit_info = {
725
+ # "sha": commit.sha,
726
+ # "author": commit.author.login if commit.author else "Unknown",
727
+ # "date": commit.commit.author.date.isoformat(),
728
+ # "message": commit.commit.message,
729
+ # "changes": {
730
+ # "additions": commit.stats.additions,
731
+ # "deletions": commit.stats.deletions,
732
+ # }
733
+ # }
734
+ # commit_data.append(commit_info)
735
+ # commit_times.append(commit.commit.author.date.hour)
736
+ # except Exception as e:
737
+ # print(f"Error processing commit {commit.sha}: {str(e)}")
738
+ # continue
739
+
740
+ # # Analyze commit patterns
741
+ # commit_hours = defaultdict(int)
742
+ # for hour in commit_times:
743
+ # commit_hours[hour] += 1
744
+
745
+ # total_commits = len(commit_data)
746
+ # return {
747
+ # "commits": commit_data,
748
+ # "total_commits": total_commits,
749
+ # "commit_hours": dict(commit_hours),
750
+ # "avg_additions": sum(c["changes"]["additions"] for c in commit_data) / total_commits if total_commits else 0,
751
+ # "avg_deletions": sum(c["changes"]["deletions"] for c in commit_data) / total_commits if total_commits else 0,
752
+ # }
753
+
754
+ # except Exception as e:
755
+ # print(f"Error in commit analysis: {str(e)}")
756
+ # return {
757
+ # "commits": [],
758
+ # "total_commits": 0,
759
+ # "commit_hours": {},
760
+ # "avg_additions": 0,
761
+ # "avg_deletions": 0
762
+ # }
763
+
764
+ # def _analyze_contributors(self) -> Dict[str, Any]:
765
+ # """Analyze contributor statistics"""
766
+ # contributor_data = []
767
+
768
+ # try:
769
+ # contributors = list(self.repo.get_contributors())
770
+ # for contributor in contributors:
771
+ # contributor_data.append({
772
+ # "login": contributor.login,
773
+ # "contributions": contributor.contributions,
774
+ # "type": contributor.type,
775
+ # })
776
+ # except Exception as e:
777
+ # print(f"Error analyzing contributors: {str(e)}")
778
+
779
+ # return {
780
+ # "total_contributors": len(contributor_data),
781
+ # "contributors": contributor_data
782
+ # }
783
+ # def create_pdf_from_markdown(markdown_text: str, filename: str) -> str:
784
+ # """Convert markdown text to PDF"""
785
+ # class PDF(FPDF):
786
+ # def header(self):
787
+ # self.set_font('Arial', 'B', 12)
788
+ # self.cell(0, 10, 'Repository Analysis Report', 0, 1, 'C')
789
+ # self.ln(10)
790
+
791
+ # def footer(self):
792
+ # self.set_y(-15)
793
+ # self.set_font('Arial', 'I', 8)
794
+ # self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
795
+
796
+ # pdf = PDF()
797
+ # pdf.add_page()
798
+ # pdf.set_auto_page_break(auto=True, margin=15)
799
+
800
+ # # Process markdown sections
801
+ # sections = markdown_text.split('\n## ')
802
+
803
+ # # Handle main title
804
+ # if sections[0].startswith('# '):
805
+ # title = sections[0].split('\n')[0].replace('# ', '')
806
+ # pdf.set_font('Arial', 'B', 16)
807
+ # pdf.cell(0, 10, title, 0, 1, 'C')
808
+ # pdf.ln(5)
809
+ # content = '\n'.join(sections[0].split('\n')[1:])
810
+ # sections[0] = content
811
+
812
+ # for section in sections:
813
+ # if section:
814
+ # # Extract section title and content
815
+ # lines = section.split('\n')
816
+ # if section == sections[0]: # First section (after main title)
817
+ # section_title = ''
818
+ # content = lines
819
+ # else:
820
+ # section_title = lines[0]
821
+ # content = lines[1:]
822
+
823
+ # # Add section title
824
+ # if section_title:
825
+ # pdf.set_font('Arial', 'B', 14)
826
+ # # Remove emojis from section titles
827
+ # clean_title = re.sub(r'[^\x00-\x7F]+', '', section_title)
828
+ # pdf.cell(0, 10, clean_title.strip(), 0, 1, 'L')
829
+ # pdf.ln(5)
830
+
831
+ # # Add content
832
+ # pdf.set_font('Arial', '', 11)
833
+ # for line in content:
834
+ # if line.strip():
835
+ # # Remove markdown formatting and emojis
836
+ # clean_line = re.sub(r'[\*\[\]]', '', line)
837
+ # clean_line = re.sub(r'[^\x00-\x7F]+', '', clean_line)
838
+
839
+ # if line.startswith('- '):
840
+ # pdf.cell(10, 5, '', 0, 0)
841
+ # pdf.multi_cell(0, 5, clean_line[2:])
842
+ # else:
843
+ # pdf.multi_cell(0, 5, clean_line)
844
+ # pdf.ln(5)
845
+
846
+ # # Save PDF
847
+ # pdf_path = f"{filename}.pdf"
848
+ # pdf.output(pdf_path)
849
+ # return pdf_path
850
+
851
+ # def download_noto_font():
852
+ # """Download Google's Noto Color Emoji font if not already present"""
853
+ # font_path = "NotoColorEmoji.ttf"
854
+ # if not os.path.exists(font_path):
855
+ # url = "https://github.com/googlefonts/noto-emoji/raw/main/fonts/NotoColorEmoji.ttf"
856
+ # response = requests.get(url)
857
+ # with open(font_path, "wb") as f:
858
+ # f.write(response.content)
859
+ # return font_path
860
+
861
+ # class PDFWithEmoji(FPDF):
862
+ # def __init__(self):
863
+ # super().__init__()
864
+ # self.add_font('DejaVu', '', 'DejaVuSansCondensed.ttf', uni=True)
865
+ # self.add_font('Noto', '', 'NotoColorEmoji.ttf', uni=True)
866
+
867
+ # def header(self):
868
+ # self.set_font('DejaVu', '', 12)
869
+ # self.cell(0, 10, 'πŸ“Š Repository Analysis Report', 0, 1, 'C')
870
+ # self.ln(10)
871
+
872
+ # def footer(self):
873
+ # self.set_y(-15)
874
+ # self.set_font('DejaVu', '', 8)
875
+ # self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C')
876
+
877
+ # def write_with_emoji(self, text):
878
+ # """Write text with proper emoji support"""
879
+ # self.set_font('DejaVu', '', 11)
880
+ # self.multi_cell(0, 5, text)
881
+
882
+ # def create_pdf_report(markdown_text: str) -> str:
883
+ # """Create a PDF report with full emoji and Unicode support"""
884
+ # # Ensure required fonts are available
885
+ # if not os.path.exists("DejaVuSansCondensed.ttf"):
886
+ # url = "https://github.com/dejavu-fonts/dejavu-fonts/raw/master/ttf/DejaVuSansCondensed.ttf"
887
+ # response = requests.get(url)
888
+ # with open("DejaVuSansCondensed.ttf", "wb") as f:
889
+ # f.write(response.content)
890
+
891
+ # download_noto_font()
892
+
893
+ # # Create PDF
894
+ # pdf = PDFWithEmoji()
895
+ # pdf.add_page()
896
+ # pdf.set_auto_page_break(auto=True, margin=15)
897
+
898
+ # # Process markdown text
899
+ # sections = markdown_text.split('\n## ')
900
+
901
+ # # Handle main title
902
+ # if sections[0].startswith('# '):
903
+ # title = sections[0].split('\n')[0].replace('# ', '')
904
+ # pdf.set_font('DejaVu', '', 16)
905
+ # pdf.cell(0, 10, title, 0, 1, 'C')
906
+ # pdf.ln(5)
907
+ # content = '\n'.join(sections[0].split('\n')[1:])
908
+ # sections[0] = content
909
+
910
+ # # Process each section
911
+ # for section in sections:
912
+ # if section:
913
+ # lines = section.split('\n')
914
+ # if section == sections[0]: # First section
915
+ # section_title = ''
916
+ # content = lines
917
+ # else:
918
+ # section_title = lines[0]
919
+ # content = lines[1:]
920
+
921
+ # # Add section title
922
+ # if section_title:
923
+ # pdf.set_font('DejaVu', '', 14)
924
+ # pdf.cell(0, 10, section_title, 0, 1, 'L')
925
+ # pdf.ln(5)
926
+
927
+ # # Add content
928
+ # pdf.set_font('DejaVu', '', 11)
929
+ # for line in content:
930
+ # if line.strip():
931
+ # if line.strip().startswith('- '):
932
+ # pdf.cell(10, 5, 'β€’', 0, 0)
933
+ # pdf.write_with_emoji(line.strip()[2:])
934
+ # pdf.ln()
935
+ # else:
936
+ # pdf.write_with_emoji(line.strip())
937
+ # pdf.ln()
938
+
939
+ # # Save PDF
940
+ # timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
941
+ # pdf_path = f"repo_analysis_{timestamp}.pdf"
942
+ # pdf.output(pdf_path)
943
+ # return pdf_path
944
+
945
+ # @retry(
946
+ # retry=retry_if_exception_type(Exception),
947
+ # stop=stop_after_attempt(3),
948
+ # wait=wait_exponential(multiplier=1, min=4, max=10)
949
+ # )
950
+
951
+
952
+ # def analyze_repository(repo_url: str, progress=gr.Progress()) -> Tuple[str, str, str]:
953
+ # """Analyze repository and generate LLM summary with rate limit handling"""
954
+ # try:
955
+ # # Initialize analyzer
956
+ # progress(0, desc="Initializing repository analysis...")
957
+ # analyzer = RepositoryAnalyzer(repo_url)
958
+
959
+ # # Perform analysis
960
+ # progress(0.3, desc="Analyzing repository structure and patterns...")
961
+ # analysis_data = analyzer.analyze()
962
+
963
+ # # Generate LLM summary
964
+ # progress(0.7, desc="Generating analysis summary...")
965
+
966
+ # system_prompt = """You are an expert code analyst with deep experience in software architecture, development practices, and team dynamics. Analyze the provided repository data and create a detailed, insightful analysis using the following markdown template:
967
+
968
+ # # Repository Analysis
969
+
970
+ # ## πŸ“Š Project Overview
971
+ # [Provide a comprehensive overview including:
972
+ # - Project purpose and scope
973
+ # - Age and maturity of the project
974
+ # - Current activity level and maintenance status
975
+ # - Key metrics (stars, forks, etc.)
976
+ # - Primary technologies and languages used]
977
+
978
+ # ## πŸ—οΈ Architecture and Code Organization
979
+ # [Analyze in detail:
980
+ # - Repository structure and organization
981
+ # - Code distribution across different technologies
982
+ # - File and directory organization patterns
983
+ # - Project size and complexity metrics
984
+ # - Code modularity and component structure
985
+ # - Presence of key architectural patterns]
986
+
987
+ # ## πŸ’» Development Practices & Code Quality
988
+ # [Evaluate:
989
+ # - Coding standards and consistency
990
+ # - Code complexity and maintainability metrics
991
+ # - Documentation practices
992
+ # - Testing approach and coverage (if visible)
993
+ # - Error handling and logging practices
994
+ # - Use of design patterns and best practices]
995
+
996
+ # ## πŸ“ˆ Development Workflow & History
997
+ # [Analyze:
998
+ # - Commit patterns and frequency
999
+ # - Release cycles and versioning
1000
+ # - Branch management strategy
1001
+ # - Code review practices
1002
+ # - Continuous integration/deployment indicators
1003
+ # - Peak development periods and cycles]
1004
+
1005
+ # ## πŸ‘₯ Team Dynamics & Collaboration
1006
+ # [Examine:
1007
+ # - Team size and composition
1008
+ # - Contribution patterns
1009
+ # - Core maintainer identification
1010
+ # - Community engagement level
1011
+ # - Communication patterns
1012
+ # - Collaboration efficiency]
1013
+
1014
+ # ## πŸ”§ Technical Depth & Innovation
1015
+ # [Assess:
1016
+ # - Technical sophistication level
1017
+ # - Innovative approaches or solutions
1018
+ # - Complex problem-solving examples
1019
+ # - Performance optimization efforts
1020
+ # - Security considerations
1021
+ # - Scalability approach]
1022
+
1023
+ # ## πŸš€ Project Health & Sustainability
1024
+ # [Evaluate:
1025
+ # - Project momentum and growth trends
1026
+ # - Maintenance patterns
1027
+ # - Community health indicators
1028
+ # - Documentation completeness
1029
+ # - Onboarding friendliness
1030
+ # - Long-term viability indicators]
1031
+
1032
+ # ## πŸ’‘ Key Insights & Recommendations
1033
+ # [Provide:
1034
+ # - 3-5 key strengths identified
1035
+ # - 3-5 potential improvement areas
1036
+ # - Notable patterns or practices
1037
+ # - Unique characteristics
1038
+ # - Strategic recommendations]
1039
+
1040
+ # Please provide detailed analysis for each section while maintaining the formatting and emojis. Support insights with specific metrics and examples from the repository data where possible."""
1041
+
1042
+ # chat = model.start_chat(history=[])
1043
+ # response = chat.send_message(f"{system_prompt}\n\nRepository Analysis Data:\n{json.dumps(analysis_data, indent=2)}")
1044
+
1045
+ # # Save analysis data
1046
+ # progress(0.9, desc="Saving analysis results...")
1047
+ # with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
1048
+ # json.dump(analysis_data, f, indent=2)
1049
+ # analysis_file = f.name
1050
+
1051
+ # progress(0.9, desc="Generating PDF report...")
1052
+ # try:
1053
+ # pdf_path = create_pdf_report(response.text)
1054
+ # except Exception as pdf_error:
1055
+ # print(f"PDF generation error: {str(pdf_error)}")
1056
+ # pdf_path = ""
1057
+
1058
+
1059
+
1060
+ # # Generate PDF
1061
+ # pdf_path = create_pdf_from_markdown(response.text, f"analysis_{datetime.now().strftime('%Y%m%d_%H%M%S')}")
1062
+
1063
+ # with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f:
1064
+ # json.dump(analysis_data, f, indent=2)
1065
+ # analysis_file = f.name
1066
+
1067
+
1068
+
1069
+
1070
+ # progress(1.0, desc="✨ Analysis complete!")
1071
+ # return response.text, analysis_file, pdf_path, "βœ… Analysis completed successfully!"
1072
+
1073
+ # except Exception as e:
1074
+ # error_message = f"❌ Error analyzing repository: {str(e)}"
1075
+ # return "", "", "", error_message # Return 4 empty values when there's an error
1076
+
1077
+ # def create_chat_session() -> Any:
1078
+ # """Create a new chat session for follow-up questions"""
1079
+ # return genai.GenerativeModel(
1080
+ # model_name="gemini-pro",
1081
+ # generation_config={
1082
+ # 'temperature': 0.7,
1083
+ # 'top_p': 0.8,
1084
+ # 'top_k': 40,
1085
+ # 'max_output_tokens': 2048,
1086
+ # }
1087
+ # )
1088
+
1089
+ # @retry(
1090
+ # retry=retry_if_exception_type(Exception),
1091
+ # stop=stop_after_attempt(3),
1092
+ # wait=wait_exponential(multiplier=1, min=4, max=10)
1093
+ # )
1094
+ # def ask_question(question: str, analysis_file: str, chat_history: List[Tuple[str, str]]) -> List[Tuple[str, str]]:
1095
+ # """Process a follow-up question about the analysis"""
1096
+ # if not analysis_file:
1097
+ # return chat_history + [(question, "Please analyze a repository first before asking questions.")]
1098
+
1099
+ # try:
1100
+ # # Load analysis data
1101
+ # with open(analysis_file, 'r') as f:
1102
+ # analysis_data = json.load(f)
1103
+
1104
+ # # Initialize chat model
1105
+ # model = create_chat_session()
1106
+
1107
+ # # Build context from chat history and current question
1108
+ # context = "You are an expert code analyst helping users understand repository analysis results.\n\n"
1109
+ # context += f"Repository Analysis Data:\n{json.dumps(analysis_data, indent=2)}\n\n"
1110
+
1111
+ # # Add chat history context
1112
+ # if chat_history:
1113
+ # context += "Previous conversation:\n"
1114
+ # for user_msg, assistant_msg in chat_history:
1115
+ # context += f"User: {user_msg}\nAssistant: {assistant_msg}\n"
1116
+
1117
+ # # Add current question
1118
+ # prompt = context + f"\nUser: {question}\nPlease provide your analysis:"
1119
+
1120
+ # # Get response
1121
+ # response = model.generate_content(prompt)
1122
+
1123
+ # # Return in the correct tuple format for Gradio chatbot
1124
+ # return chat_history + [(question, response.text)]
1125
+
1126
+ # except Exception as e:
1127
+ # error_message = f"Error processing question: {str(e)}"
1128
+ # return chat_history + [(question, error_message)]
1129
+
1130
+
1131
+
1132
+
1133
+ # # Create Gradio interface
1134
+ # # Create Gradio interface
1135
+ # with gr.Blocks(theme=gr.themes.Soft()) as app:
1136
+ # gr.Markdown("""
1137
+ # # πŸ” GitHub Repository Analyzer
1138
+
1139
+ # Analyze any public GitHub repository using AI. The tool will:
1140
+ # 1. πŸ“Š Analyze repository structure and patterns
1141
+ # 2. πŸ’‘ Generate insights about development practices
1142
+ # 3. πŸ’­ Allow you to ask follow-up questions about the analysis
1143
+ # 4. πŸ“‘ Generate a downloadable PDF report
1144
+ # """)
1145
+
1146
+ # with gr.Row():
1147
+ # repo_url = gr.Textbox(
1148
+ # label="GitHub Repository URL",
1149
+ # placeholder="https://github.com/owner/repo",
1150
+ # scale=4
1151
+ # )
1152
+
1153
+ # with gr.Row():
1154
+ # with gr.Column(scale=1):
1155
+ # analyze_btn = gr.Button("πŸ” Analyze", variant="primary")
1156
+ # download_pdf_btn = gr.Button("πŸ“„ Download PDF", variant="secondary")
1157
+
1158
+ # # Status message
1159
+ # status_msg = gr.Markdown("", elem_id="status_message")
1160
+
1161
+ # # File output for PDF
1162
+ # pdf_output = gr.File(
1163
+ # label="Analysis Report",
1164
+ # visible=False,
1165
+ # interactive=True
1166
+ # )
1167
+
1168
+ # with gr.Row():
1169
+ # summary = gr.Markdown(label="Analysis Summary")
1170
+
1171
+ # with gr.Row():
1172
+ # chatbot = gr.Chatbot(
1173
+ # label="Ask Questions",
1174
+ # height=400,
1175
+ # show_label=True
1176
+ # )
1177
+
1178
+ # with gr.Row():
1179
+ # question = gr.Textbox(
1180
+ # label="Your Question",
1181
+ # placeholder="Ask about the analysis...",
1182
+ # scale=4
1183
+ # )
1184
+ # ask_btn = gr.Button("πŸ’­ Ask", variant="primary", scale=1)
1185
+ # clear_btn = gr.Button("πŸ—‘οΈ Clear Chat", variant="secondary", scale=1)
1186
+
1187
+ # # Hidden states
1188
+ # analysis_file = gr.State("")
1189
+ # current_pdf_path = gr.State("")
1190
+
1191
+ # def handle_pdf_download(pdf_path):
1192
+ # """Handle PDF download when button is clicked"""
1193
+ # if pdf_path and os.path.exists(pdf_path):
1194
+ # return {
1195
+ # pdf_output: pdf_path
1196
+ # }
1197
+ # return {
1198
+ # pdf_output: None
1199
+ # }
1200
+
1201
+ # def clear_outputs():
1202
+ # """Clear all outputs"""
1203
+ # return {
1204
+ # summary: "",
1205
+ # chatbot: [],
1206
+ # question: "",
1207
+ # status_msg: "",
1208
+ # pdf_output: None,
1209
+ # current_pdf_path: ""
1210
+ # }
1211
+
1212
+ # # Event handlers
1213
+ # analyze_btn.click(
1214
+ # fn=lambda: "⏳ Analysis in progress...",
1215
+ # inputs=None,
1216
+ # outputs=status_msg,
1217
+ # queue=False
1218
+ # ).then(
1219
+ # analyze_repository,
1220
+ # inputs=[repo_url],
1221
+ # outputs=[summary, analysis_file, current_pdf_path, status_msg]
1222
+ # ).then(
1223
+ # lambda: gr.update(visible=True),
1224
+ # None,
1225
+ # download_pdf_btn
1226
+ # )
1227
+
1228
+ # # PDF download handler
1229
+ # download_pdf_btn.click(
1230
+ # handle_pdf_download,
1231
+ # inputs=[current_pdf_path],
1232
+ # outputs=pdf_output
1233
+ # ).then(
1234
+ # lambda: gr.update(visible=True),
1235
+ # None,
1236
+ # pdf_output
1237
+ # )
1238
+
1239
+ # ask_btn.click(
1240
+ # ask_question,
1241
+ # inputs=[question, analysis_file, chatbot],
1242
+ # outputs=[chatbot]
1243
+ # ).then(
1244
+ # lambda: "",
1245
+ # None,
1246
+ # question,
1247
+ # queue=False
1248
+ # )
1249
+
1250
+ # clear_btn.click(
1251
+ # clear_outputs,
1252
+ # inputs=None,
1253
+ # outputs=[summary, chatbot, question, status_msg, pdf_output, current_pdf_path]
1254
+ # )
1255
+
1256
+
1257
+
1258
+ # # Launch the app
1259
+ # if __name__ == "__main__":
1260
+ # app.launch(
1261
+ # share=True,
1262
+ # debug=True
1263
+ # )