Update app.py
Browse files
app.py
CHANGED
@@ -11,9 +11,6 @@ import requests
|
|
11 |
from fpdf import FPDF
|
12 |
import markdown
|
13 |
import tempfile
|
14 |
-
import re
|
15 |
-
import spacy
|
16 |
-
from collections import defaultdict
|
17 |
|
18 |
|
19 |
# Database setup
|
@@ -81,8 +78,8 @@ def main():
|
|
81 |
project_view_page()
|
82 |
elif st.session_state.page == "generate_documentation":
|
83 |
generate_documentation_page()
|
84 |
-
elif st.session_state.page == "
|
85 |
-
|
86 |
|
87 |
def login_page():
|
88 |
st.subheader("Please Log In or Register to Continue")
|
@@ -101,7 +98,7 @@ def login_page():
|
|
101 |
st.session_state.page = "workspace"
|
102 |
else:
|
103 |
st.error("Invalid username or password. Please try again.")
|
104 |
-
|
105 |
elif auth_mode == "Register":
|
106 |
st.subheader("Register")
|
107 |
username = st.text_input("Create Username", key="register_username")
|
@@ -208,6 +205,8 @@ def workspace_page():
|
|
208 |
except Exception as e:
|
209 |
st.error(f"Failed to clone repository: {e}")
|
210 |
|
|
|
|
|
211 |
|
212 |
# Configure Gemini API
|
213 |
gemini = os.getenv("GEMINI")
|
@@ -249,15 +248,14 @@ def generate_prompt(file_contents, functionality_description):
|
|
249 |
|
250 |
for file_path, content in file_contents.items():
|
251 |
prompt += f"File: {os.path.basename(file_path)}\n{content}\n\n"
|
252 |
-
|
253 |
prompt += "For each relevant function, provide:\n"
|
254 |
prompt += "1. Which file the function is found in.\n"
|
255 |
prompt += "2. The function name.\n"
|
256 |
prompt += "3. Dependencies on other functions or modules.\n"
|
257 |
|
258 |
-
prompt += """
|
259 |
Return your output in the following format providing no commentary:
|
260 |
-
|
261 |
Project Summary:
|
262 |
<A summary of the project>
|
263 |
Functionality:
|
@@ -275,16 +273,16 @@ def identify_required_functions(project_path, functionality_description):
|
|
275 |
"""Identifies required functions for a specified functionality."""
|
276 |
# Gather all file paths in the project directory
|
277 |
file_paths = read_project_files(project_path)
|
278 |
-
|
279 |
# Read file contents
|
280 |
file_contents = read_files(file_paths)
|
281 |
-
|
282 |
# Generate a refined prompt for Gemini
|
283 |
prompt = generate_prompt(file_contents, functionality_description)
|
284 |
-
|
285 |
# Call the Gemini model
|
286 |
response = model.generate_content(prompt)
|
287 |
-
|
288 |
# Process and return the response
|
289 |
return response.text
|
290 |
|
@@ -339,9 +337,7 @@ def generate_detailed_documentation(file_contents, functionality_description):
|
|
339 |
"""
|
340 |
prompt = f"""
|
341 |
The following code files are provided. Analyze their contents and generate comprehensive documentation.
|
342 |
-
|
343 |
Functionality description: '{functionality_description}'
|
344 |
-
|
345 |
Tasks:
|
346 |
1. Generate a project summary:
|
347 |
'
|
@@ -375,7 +371,6 @@ def generate_detailed_documentation(file_contents, functionality_description):
|
|
375 |
- Example Usage: <Example demonstrating usage>
|
376 |
'
|
377 |
Please return only the required documentation in the specified format.
|
378 |
-
|
379 |
Code files:
|
380 |
"""
|
381 |
for file_path, content in file_contents.items():
|
@@ -387,28 +382,28 @@ def generate_detailed_documentation(file_contents, functionality_description):
|
|
387 |
def process_gemini_output(output):
|
388 |
"""
|
389 |
Processes the raw output from Gemini to format it appropriately.
|
390 |
-
|
391 |
- Removes ``` at the top and bottom of the output.
|
392 |
- Removes all pairs of **.
|
393 |
- Replaces ` used as a quote with '.
|
394 |
- Replaces leading '*' with '-' for lists.
|
395 |
-
|
396 |
Args:
|
397 |
output (str): The raw Gemini output.
|
398 |
-
|
399 |
Returns:
|
400 |
str: The processed and cleaned output.
|
401 |
"""
|
402 |
# Remove leading and trailing ```
|
403 |
if output.startswith("```") and output.endswith("```"):
|
404 |
output = output[3:-3].strip()
|
405 |
-
|
406 |
# Remove all pairs of **
|
407 |
output = output.replace("**", "")
|
408 |
-
|
409 |
# Replace ` with '
|
410 |
output = output.replace("`", "'")
|
411 |
-
|
412 |
# Replace leading '*' with '-' for list items
|
413 |
lines = output.splitlines()
|
414 |
processed_lines = []
|
@@ -419,9 +414,9 @@ def process_gemini_output(output):
|
|
419 |
processed_lines.append(line.replace("*", "-", 1))
|
420 |
else:
|
421 |
processed_lines.append(line)
|
422 |
-
|
423 |
return "\n".join(processed_lines)
|
424 |
-
|
425 |
def generate_documentation_page():
|
426 |
# Sidebar with "Log Out" and "Back to Project" buttons
|
427 |
st.sidebar.title(f"Project: {st.session_state.current_project}")
|
@@ -469,26 +464,6 @@ def generate_documentation_page():
|
|
469 |
# Display the final documentation
|
470 |
st.success("Documentation generated successfully!")
|
471 |
st.text_area("Generated Documentation", documentation, height=600)
|
472 |
-
|
473 |
-
#---------------------------------------------------------------------------
|
474 |
-
# Example Gemini output for testing
|
475 |
-
test_gemini_output = """
|
476 |
-
Functionality Summary:
|
477 |
-
Authenticate the user by verifying their credentials. Once authenticated, fetch user-specific data such as preferences and usage history. Handle errors during both authentication and data retrieval.
|
478 |
-
|
479 |
-
Project Summary:
|
480 |
-
<Some project details>
|
481 |
-
|
482 |
-
Functionality Flow:
|
483 |
-
<Flow details>
|
484 |
-
"""
|
485 |
-
|
486 |
-
# Streamlit testing
|
487 |
-
if "page" not in st.session_state or st.session_state.page == "test_functionality":
|
488 |
-
st.title("Test Functionality Extraction")
|
489 |
-
display_functionality_analysis(test_gemini_output)
|
490 |
-
#---------------------------------------------------------------------------
|
491 |
-
|
492 |
except Exception as e:
|
493 |
st.error(f"An error occurred: {e}")
|
494 |
else:
|
@@ -496,8 +471,6 @@ def generate_documentation_page():
|
|
496 |
else:
|
497 |
st.error("Please enter the functionality to analyze.")
|
498 |
|
499 |
-
|
500 |
-
|
501 |
# Add export/download buttons if documentation is available
|
502 |
if "generated_documentation" in st.session_state and st.session_state.generated_documentation:
|
503 |
documentation = st.session_state.generated_documentation
|
@@ -588,7 +561,6 @@ def generate_pdf(documentation, pdf_path):
|
|
588 |
def generate_markdown_file(documentation, output_path):
|
589 |
"""
|
590 |
Generates a markdown file from the documentation with structured formatting.
|
591 |
-
|
592 |
Args:
|
593 |
documentation (str): The documentation content to format and save.
|
594 |
output_path (str): Path to save the generated markdown file.
|
@@ -610,121 +582,47 @@ def generate_markdown_file(documentation, output_path):
|
|
610 |
with open(output_path, "w") as f:
|
611 |
f.write("\n".join(formatted_lines))
|
612 |
|
613 |
-
#---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
614 |
-
|
615 |
-
# Load the NLP model (use the small model for speed)
|
616 |
-
nlp = spacy.load("en_core_web_sm")
|
617 |
-
|
618 |
-
def extract_functionality_and_relationships(gemini_output):
|
619 |
-
"""
|
620 |
-
Extracts functionality descriptions and sub-functionality relationships from Gemini output.
|
621 |
-
Args:
|
622 |
-
gemini_output (str): The raw output from Gemini.
|
623 |
-
Returns:
|
624 |
-
dict: A dictionary with functionality and its relationships.
|
625 |
-
"""
|
626 |
-
functionality_summary = None
|
627 |
-
relationships = defaultdict(list)
|
628 |
-
|
629 |
-
# Extract the 'Functionality Summary' section
|
630 |
-
functionality_match = re.search(r"Functionality Summary:\n(.*?)\n\n", gemini_output, re.DOTALL)
|
631 |
-
if functionality_match:
|
632 |
-
functionality_summary = functionality_match.group(1).strip()
|
633 |
-
|
634 |
-
# Parse the functionality summary with NLP
|
635 |
-
if functionality_summary:
|
636 |
-
doc = nlp(functionality_summary)
|
637 |
-
|
638 |
-
# Extract relationships between sub-functionalities
|
639 |
-
for sentence in doc.sents:
|
640 |
-
sub_functionalities = []
|
641 |
-
for token in sentence:
|
642 |
-
# Capture sub-functionalities using noun chunks or proper nouns
|
643 |
-
if token.dep_ in {"nsubj", "dobj", "pobj"} or token.ent_type_ in {"ORG", "PRODUCT"}:
|
644 |
-
sub_functionalities.append(token.text)
|
645 |
-
if len(sub_functionalities) > 1:
|
646 |
-
# Create relationships between identified sub-functionalities
|
647 |
-
main_functionality = sub_functionalities[0]
|
648 |
-
for related in sub_functionalities[1:]:
|
649 |
-
relationships[main_functionality].append(related)
|
650 |
-
|
651 |
-
return functionality_summary, dict(relationships)
|
652 |
-
|
653 |
-
|
654 |
-
def display_functionality_analysis(gemini_output):
|
655 |
-
"""
|
656 |
-
Extracts and displays functionality analysis for testing purposes.
|
657 |
-
Args:
|
658 |
-
gemini_output (str): The raw output from Gemini.
|
659 |
-
"""
|
660 |
-
# Extract functionality and relationships
|
661 |
-
functionality, relationships = extract_functionality_and_relationships(gemini_output)
|
662 |
-
|
663 |
-
# Display extracted functionality
|
664 |
-
st.subheader("Extracted Functionality Summary")
|
665 |
-
if functionality:
|
666 |
-
st.text_area("Functionality Summary", functionality, height=200)
|
667 |
-
else:
|
668 |
-
st.error("No functionality summary found in the Gemini output.")
|
669 |
-
|
670 |
-
# Display extracted relationships
|
671 |
-
st.subheader("Extracted Relationships")
|
672 |
-
if relationships:
|
673 |
-
for main, related in relationships.items():
|
674 |
-
st.write(f"**{main}** is related to: {', '.join(related)}")
|
675 |
-
else:
|
676 |
-
st.info("No relationships between sub-functionalities identified.")
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
|
681 |
|
682 |
#------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
683 |
-
def
|
684 |
-
|
685 |
-
st.
|
686 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
687 |
st.session_state.page = "project_view"
|
688 |
st.rerun()
|
689 |
-
if st.sidebar.button("Log Out"):
|
690 |
-
st.session_state.authenticated = False
|
691 |
-
st.session_state.username = None
|
692 |
-
st.session_state.page = "login"
|
693 |
-
st.rerun()
|
694 |
-
|
695 |
-
st.subheader(f"Saved Documentation for {st.session_state.current_project}")
|
696 |
-
st.write("Below is a list of saved documentation for this project. Click to download.")
|
697 |
-
|
698 |
-
# Define paths for generated files
|
699 |
-
user_folder = os.path.join("user_projects", st.session_state.username)
|
700 |
-
pdf_path = os.path.join(user_folder, f"{st.session_state.current_project}_Documentation.pdf")
|
701 |
-
markdown_path = os.path.join(user_folder, f"{st.session_state.current_project}_Documentation.md")
|
702 |
-
|
703 |
-
# Check if files exist
|
704 |
-
pdf_exists = os.path.exists(pdf_path)
|
705 |
-
markdown_exists = os.path.exists(markdown_path)
|
706 |
-
|
707 |
-
# Display available documentation
|
708 |
-
if not pdf_exists and not markdown_exists:
|
709 |
-
st.info("No documentation has been generated yet. Please go to the 'Generate Documentation' page.")
|
710 |
-
else:
|
711 |
-
if pdf_exists:
|
712 |
-
with open(pdf_path, "rb") as pdf_file:
|
713 |
-
st.download_button(
|
714 |
-
label="Download PDF Documentation",
|
715 |
-
data=pdf_file.read(),
|
716 |
-
file_name=f"{st.session_state.current_project}_Documentation.pdf",
|
717 |
-
mime="application/pdf",
|
718 |
-
)
|
719 |
-
|
720 |
-
if markdown_exists:
|
721 |
-
with open(markdown_path, "rb") as markdown_file:
|
722 |
-
st.download_button(
|
723 |
-
label="Download Markdown Documentation",
|
724 |
-
data=markdown_file.read(),
|
725 |
-
file_name=f"{st.session_state.current_project}_Documentation.md",
|
726 |
-
mime="text/markdown",
|
727 |
-
)
|
728 |
|
729 |
|
730 |
|
@@ -749,8 +647,8 @@ def project_view_page():
|
|
749 |
st.session_state.page = "generate_documentation"
|
750 |
st.rerun()
|
751 |
|
752 |
-
if st.button("
|
753 |
-
st.session_state.page = "
|
754 |
st.rerun()
|
755 |
|
756 |
# Toggle file structure display (if required)
|
|
|
11 |
from fpdf import FPDF
|
12 |
import markdown
|
13 |
import tempfile
|
|
|
|
|
|
|
14 |
|
15 |
|
16 |
# Database setup
|
|
|
78 |
project_view_page()
|
79 |
elif st.session_state.page == "generate_documentation":
|
80 |
generate_documentation_page()
|
81 |
+
elif st.session_state.page == "view_documentation":
|
82 |
+
view_documentation_page()
|
83 |
|
84 |
def login_page():
|
85 |
st.subheader("Please Log In or Register to Continue")
|
|
|
98 |
st.session_state.page = "workspace"
|
99 |
else:
|
100 |
st.error("Invalid username or password. Please try again.")
|
101 |
+
|
102 |
elif auth_mode == "Register":
|
103 |
st.subheader("Register")
|
104 |
username = st.text_input("Create Username", key="register_username")
|
|
|
205 |
except Exception as e:
|
206 |
st.error(f"Failed to clone repository: {e}")
|
207 |
|
208 |
+
#------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
209 |
+
|
210 |
|
211 |
# Configure Gemini API
|
212 |
gemini = os.getenv("GEMINI")
|
|
|
248 |
|
249 |
for file_path, content in file_contents.items():
|
250 |
prompt += f"File: {os.path.basename(file_path)}\n{content}\n\n"
|
251 |
+
|
252 |
prompt += "For each relevant function, provide:\n"
|
253 |
prompt += "1. Which file the function is found in.\n"
|
254 |
prompt += "2. The function name.\n"
|
255 |
prompt += "3. Dependencies on other functions or modules.\n"
|
256 |
|
257 |
+
prompt += """
|
258 |
Return your output in the following format providing no commentary:
|
|
|
259 |
Project Summary:
|
260 |
<A summary of the project>
|
261 |
Functionality:
|
|
|
273 |
"""Identifies required functions for a specified functionality."""
|
274 |
# Gather all file paths in the project directory
|
275 |
file_paths = read_project_files(project_path)
|
276 |
+
|
277 |
# Read file contents
|
278 |
file_contents = read_files(file_paths)
|
279 |
+
|
280 |
# Generate a refined prompt for Gemini
|
281 |
prompt = generate_prompt(file_contents, functionality_description)
|
282 |
+
|
283 |
# Call the Gemini model
|
284 |
response = model.generate_content(prompt)
|
285 |
+
|
286 |
# Process and return the response
|
287 |
return response.text
|
288 |
|
|
|
337 |
"""
|
338 |
prompt = f"""
|
339 |
The following code files are provided. Analyze their contents and generate comprehensive documentation.
|
|
|
340 |
Functionality description: '{functionality_description}'
|
|
|
341 |
Tasks:
|
342 |
1. Generate a project summary:
|
343 |
'
|
|
|
371 |
- Example Usage: <Example demonstrating usage>
|
372 |
'
|
373 |
Please return only the required documentation in the specified format.
|
|
|
374 |
Code files:
|
375 |
"""
|
376 |
for file_path, content in file_contents.items():
|
|
|
382 |
def process_gemini_output(output):
|
383 |
"""
|
384 |
Processes the raw output from Gemini to format it appropriately.
|
385 |
+
|
386 |
- Removes ``` at the top and bottom of the output.
|
387 |
- Removes all pairs of **.
|
388 |
- Replaces ` used as a quote with '.
|
389 |
- Replaces leading '*' with '-' for lists.
|
390 |
+
|
391 |
Args:
|
392 |
output (str): The raw Gemini output.
|
393 |
+
|
394 |
Returns:
|
395 |
str: The processed and cleaned output.
|
396 |
"""
|
397 |
# Remove leading and trailing ```
|
398 |
if output.startswith("```") and output.endswith("```"):
|
399 |
output = output[3:-3].strip()
|
400 |
+
|
401 |
# Remove all pairs of **
|
402 |
output = output.replace("**", "")
|
403 |
+
|
404 |
# Replace ` with '
|
405 |
output = output.replace("`", "'")
|
406 |
+
|
407 |
# Replace leading '*' with '-' for list items
|
408 |
lines = output.splitlines()
|
409 |
processed_lines = []
|
|
|
414 |
processed_lines.append(line.replace("*", "-", 1))
|
415 |
else:
|
416 |
processed_lines.append(line)
|
417 |
+
|
418 |
return "\n".join(processed_lines)
|
419 |
+
|
420 |
def generate_documentation_page():
|
421 |
# Sidebar with "Log Out" and "Back to Project" buttons
|
422 |
st.sidebar.title(f"Project: {st.session_state.current_project}")
|
|
|
464 |
# Display the final documentation
|
465 |
st.success("Documentation generated successfully!")
|
466 |
st.text_area("Generated Documentation", documentation, height=600)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
467 |
except Exception as e:
|
468 |
st.error(f"An error occurred: {e}")
|
469 |
else:
|
|
|
471 |
else:
|
472 |
st.error("Please enter the functionality to analyze.")
|
473 |
|
|
|
|
|
474 |
# Add export/download buttons if documentation is available
|
475 |
if "generated_documentation" in st.session_state and st.session_state.generated_documentation:
|
476 |
documentation = st.session_state.generated_documentation
|
|
|
561 |
def generate_markdown_file(documentation, output_path):
|
562 |
"""
|
563 |
Generates a markdown file from the documentation with structured formatting.
|
|
|
564 |
Args:
|
565 |
documentation (str): The documentation content to format and save.
|
566 |
output_path (str): Path to save the generated markdown file.
|
|
|
582 |
with open(output_path, "w") as f:
|
583 |
f.write("\n".join(formatted_lines))
|
584 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
585 |
|
586 |
|
587 |
#------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
588 |
+
def view_documentation_page():
|
589 |
+
st.subheader(f"View Documentation for {st.session_state.current_project}")
|
590 |
+
st.write("This page displays the generated documentation for the selected project.")
|
591 |
+
|
592 |
+
# Check if files are available in session state
|
593 |
+
pdf_path = st.session_state.get("pdf_file_path")
|
594 |
+
markdown_path = st.session_state.get("markdown_file_path")
|
595 |
+
|
596 |
+
# Display PDF file if it exists
|
597 |
+
if pdf_path and os.path.exists(pdf_path):
|
598 |
+
st.write("### PDF Documentation")
|
599 |
+
with open(pdf_path, "rb") as pdf_file:
|
600 |
+
pdf_data = pdf_file.read()
|
601 |
+
st.download_button(
|
602 |
+
label="Download PDF",
|
603 |
+
data=pdf_data,
|
604 |
+
file_name=os.path.basename(pdf_path),
|
605 |
+
mime="application/pdf",
|
606 |
+
)
|
607 |
+
st.write("View PDF Documentation below:")
|
608 |
+
st.pdf_viewer(pdf_path)
|
609 |
+
|
610 |
+
# Display Markdown file if it exists
|
611 |
+
if markdown_path and os.path.exists(markdown_path):
|
612 |
+
st.write("### Markdown Documentation")
|
613 |
+
with open(markdown_path, "r") as md_file:
|
614 |
+
markdown_content = md_file.read()
|
615 |
+
st.download_button(
|
616 |
+
label="Download Markdown",
|
617 |
+
data=markdown_content,
|
618 |
+
file_name=os.path.basename(markdown_path),
|
619 |
+
mime="text/markdown",
|
620 |
+
)
|
621 |
+
st.markdown(f"```\n{markdown_content}\n```", unsafe_allow_html=True)
|
622 |
+
|
623 |
+
if st.button("Back to Project"):
|
624 |
st.session_state.page = "project_view"
|
625 |
st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
626 |
|
627 |
|
628 |
|
|
|
647 |
st.session_state.page = "generate_documentation"
|
648 |
st.rerun()
|
649 |
|
650 |
+
if st.button("View Documentation"):
|
651 |
+
st.session_state.page = "view_documentation"
|
652 |
st.rerun()
|
653 |
|
654 |
# Toggle file structure display (if required)
|