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()