File size: 9,827 Bytes
3d833be |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
import streamlit as st
import tempfile
import json
from backend import (
clone_repository,
read_code_files,
analyze_code,
check_api_keys
)
def get_severity_color(severity):
"""Get color based on severity level."""
colors = {
"LOW": "#FFA500", # Orange
"MEDIUM": "#FF6B6B", # Light Red
"HIGH": "#FF0000" # Red
}
return colors.get(severity.upper(), "#000000")
def render_analysis_results(analysis_text):
"""Render the analysis results according to the Pydantic model schema."""
try:
# Parse the analysis text as JSON
analysis_data = json.loads(analysis_text)
# Custom CSS for styling
st.markdown("""
<style>
.severity-box {
background-color: #f0f2f6;
padding: 1rem;
border-radius: 0.5rem;
margin: 1rem 0;
}
.file-impact {
background-color: #ffffff;
padding: 1rem;
border-radius: 0.5rem;
margin: 0.5rem 0;
border: 1px solid #e1e4e8;
}
.impact-count {
background-color: #e6f3ff;
padding: 0.5rem 1rem;
border-radius: 0.5rem;
margin: 1rem 0;
}
</style>
""", unsafe_allow_html=True)
# Calculate severity level based on number of files impacted
severity_level = analysis_data['severity_level']
if(analysis_data['number_of_files_impacted'] == None or analysis_data['number_of_files_impacted'] == 0):
severity_level = "No Impact"
elif(analysis_data['number_of_files_impacted'] > 0 and analysis_data['number_of_files_impacted'] <= 3):
severity_level = "Low"
elif(analysis_data['number_of_files_impacted'] > 3 and analysis_data['number_of_files_impacted'] <= 8):
severity_level = "Medium"
else:
severity_level = "High"
# Display Severity Level with custom styling
severity_color = get_severity_color(severity_level)
st.markdown(f"""
<div class="severity-box">
<h3 style='color: {severity_color}; margin: 0; font-size: 1.5rem; font-weight: bold;'>
Severity Level: {severity_level}
</h3>
</div>
""", unsafe_allow_html=True)
# Display Number of Files Impacted with custom styling
st.markdown(f"""
<div class="impact-count">
<h3 style='color: #1f77b4; margin: 0; font-size: 1.2rem;'>
Number of Files Impacted: {analysis_data['number_of_files_impacted']}
</h3>
</div>
""", unsafe_allow_html=True)
# Display Files Impacted with custom styling
st.markdown("<h3 style='color: #2c3e50; font-size: 1.3rem;'>Files Impacted</h3>", unsafe_allow_html=True)
for file_impact in analysis_data['files_impacted']:
with st.expander(f"π {file_impact['files_impacted']}", expanded=False):
st.markdown(f"""
<div class="file-impact">
<p style='color: #34495e; font-size: 1rem; line-height: 1.6;'>
{file_impact['impact_details']}
</p>
</div>
""", unsafe_allow_html=True)
except json.JSONDecodeError:
# If the response is not valid JSON, display it as plain text
st.markdown(analysis_text)
except Exception as e:
st.error(f"Error rendering analysis results: {str(e)}")
st.markdown(analysis_text)
def main():
st.title("Git Repository Code Analyzer")
st.write("Enter a Git repository URL and a prompt to analyze the code.")
# Example data
examples = [
{
"Git URL": "https://github.com/kedar-bhumkar/SFRoutingFramework",
"Code/Config Changes": "Enum USER_INTERFACE removed from file: BaseAppLiterals.cls"
},
{
"Git URL": "https://github.com/kedar-bhumkar/SFDynamicFields",
"Code/Config Changes": "Removed a field Value__c from DynamicFieldTable__c.object"
}
]
# Initialize session state if not exists
if 'selected_example' not in st.session_state:
st.session_state.selected_example = None
if 'openai_key' not in st.session_state:
st.session_state.openai_key = ""
# API Key input section
with st.expander("π API Key Settings", expanded=False):
st.markdown("""
<style>
.api-key-section {
background-color: #f8f9fa;
padding: 1rem;
border-radius: 0.5rem;
margin: 0.5rem 0;
}
</style>
""", unsafe_allow_html=True)
st.markdown("""
<div class="api-key-section">
<p style='color: #2c3e50; font-size: 0.9rem;'>
Enter your OpenAI API key to use the GPT-4 model. The key will be stored in the session and not saved permanently.
</p>
</div>
""", unsafe_allow_html=True)
openai_key = st.text_input(
"OpenAI API Key",
value=st.session_state.openai_key,
type="password",
help="Enter your OpenAI API key to use GPT-4"
)
if openai_key:
st.session_state.openai_key = openai_key
st.success("API key saved for this session")
# Display examples table with Select buttons
st.subheader("Example Cases")
# Create columns for the table
col1, col2, col3 = st.columns([2, 2, 1])
# Table header
with col1:
st.write("**Git URL**")
with col2:
st.write("**Code/Config Changes**")
with col3:
st.write("**Action**")
# Table rows
for idx, example in enumerate(examples):
with col1:
st.write(example["Git URL"])
with col2:
st.write(example["Code/Config Changes"])
with col3:
if st.button("Select", key=f"select_{idx}"):
st.session_state.selected_example = idx
st.session_state.repo_url = example["Git URL"]
st.session_state.prompt = example["Code/Config Changes"]
st.experimental_rerun()
# Get user inputs
repo_url = st.text_input("Git Repository URL",
value=st.session_state.get("repo_url", ""))
# Model selection
model = st.selectbox(
"Select AI Model",
["gpt-4", "claude-sonnet (coming soon)"],
help="Choose the AI model to analyze the code"
)
prompt = st.text_area("Code or configuration changes",
value=st.session_state.get("prompt", "List down the code/configuration changes to be performed"))
# Clear button
if st.button("Clear Selection"):
st.session_state.selected_example = None
st.session_state.repo_url = ""
st.session_state.prompt = "List down the code/configuration changes to be performed"
st.experimental_rerun()
if st.button("Analyze"):
if not repo_url:
st.error("Please enter a Git repository URL")
return
# Check API keys
api_keys_status = check_api_keys()
if model == "gpt-4":
# First check session state for OpenAI key
if st.session_state.openai_key:
# Use the key from session state
api_keys_status["gpt-4"] = True
elif not api_keys_status["gpt-4"]:
st.error("OpenAI API key not found. Please enter your key in the API Key Settings section or set the OPENAI_API_KEY environment variable.")
return
elif model == "claude-sonnet" and not api_keys_status["claude-sonnet"]:
st.error("Anthropic API key not found. Please set the ANTHROPIC_API_KEY environment variable.")
return
with st.spinner("Cloning repository and analyzing code..."):
# Create a temporary directory
with tempfile.TemporaryDirectory() as temp_dir:
# Clone the repository
success, error = clone_repository(repo_url, temp_dir)
if not success:
st.error(f"Error cloning repository: {error}")
return
# Read code files
code_files, warnings = read_code_files(temp_dir)
# Display any warnings from reading files
for warning in warnings:
st.warning(warning)
if not code_files:
st.warning("No code files found in the repository.")
return
# Analyze the code
analysis, error = analyze_code(code_files, prompt, model)
if error:
st.error(f"Error during analysis: {error}")
return
if analysis:
st.subheader("Analysis Results")
render_analysis_results(analysis)
if __name__ == "__main__":
main() |