chelouche9
commited on
Commit
Β·
e88040b
1
Parent(s):
0e66a86
first commit
Browse files- .gitignore +2 -0
- app.py +140 -0
- requirements.txt +58 -0
.gitignore
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.venv
|
| 2 |
+
.env
|
app.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import os
|
| 3 |
+
import requests
|
| 4 |
+
import git
|
| 5 |
+
import tempfile
|
| 6 |
+
import shutil
|
| 7 |
+
from openai import OpenAI
|
| 8 |
+
import dotenv
|
| 9 |
+
|
| 10 |
+
def generate_prompt(repo_url, role, repo_data, focus_areas=""):
|
| 11 |
+
"""Generates the AI analysis prompt with repo details."""
|
| 12 |
+
return f"""
|
| 13 |
+
You are an AI expert in evaluating software engineering candidates based on their GitHub repositories. Your goal is to assess the quality, organization, and best practices of the submitted code repository. Below is the candidate's information:
|
| 14 |
+
|
| 15 |
+
### Candidate Information:
|
| 16 |
+
- **Role Applied For**: {role}
|
| 17 |
+
- **GitHub Repository URL**: {repo_url}
|
| 18 |
+
- **Optional Key Focus Areas**: {focus_areas}
|
| 19 |
+
|
| 20 |
+
### Repository Analysis:
|
| 21 |
+
You are provided with the repository's cloned structure and its contents. Analyze the following aspects:
|
| 22 |
+
{repo_data}
|
| 23 |
+
|
| 24 |
+
1. **Code Organization & Architecture** (20 points)
|
| 25 |
+
2. **Code Quality & Best Practices** (25 points)
|
| 26 |
+
3. **Language Proficiency & Best Practices** (20 points)
|
| 27 |
+
4. **Use of Frameworks & Libraries** (15 points)
|
| 28 |
+
5. **Testing & Documentation** (20 points)
|
| 29 |
+
|
| 30 |
+
### **Final Score Calculation**
|
| 31 |
+
- Score the repository **out of 100** based on the criteria above.
|
| 32 |
+
- Justify the **score** by explaining the candidateβs strengths and weaknesses.
|
| 33 |
+
|
| 34 |
+
### **Expected Output:**
|
| 35 |
+
- **Strengths**: What is done well?
|
| 36 |
+
- **Weaknesses**: What needs improvement?
|
| 37 |
+
- **Final Score (0-100)**: Provide a numeric score with an explanation.
|
| 38 |
+
- **Summary**: Briefly summarize the candidateβs proficiency based on this analysis.
|
| 39 |
+
"""
|
| 40 |
+
|
| 41 |
+
# Load environment variables
|
| 42 |
+
dotenv.load_dotenv()
|
| 43 |
+
client = OpenAI()
|
| 44 |
+
|
| 45 |
+
PASSWORD = os.getenv("PASSWORD", "defaultpass")
|
| 46 |
+
|
| 47 |
+
def authenticate(password):
|
| 48 |
+
"""Check if the entered password is correct."""
|
| 49 |
+
if password != PASSWORD:
|
| 50 |
+
return "β Incorrect password! Access denied.", None
|
| 51 |
+
return None, "β
Access granted! You may proceed."
|
| 52 |
+
|
| 53 |
+
def analyze_repo(repo_url, role, focus_areas, password):
|
| 54 |
+
auth_error, auth_success = authenticate(password)
|
| 55 |
+
if auth_error:
|
| 56 |
+
return auth_error, gr.update(visible=False) # If incorrect password, return error
|
| 57 |
+
|
| 58 |
+
"""Clone and analyze a GitHub repository with a loading state."""
|
| 59 |
+
if not repo_url.startswith("https://github.com/"):
|
| 60 |
+
return "β Invalid GitHub URL!", gr.update(visible=False)
|
| 61 |
+
|
| 62 |
+
# Extract repo details
|
| 63 |
+
repo_name = repo_url.split("/")[-1]
|
| 64 |
+
temp_dir = tempfile.mkdtemp()
|
| 65 |
+
|
| 66 |
+
try:
|
| 67 |
+
progress = gr.update(value="π Cloning repository...", visible=True)
|
| 68 |
+
|
| 69 |
+
# Clone the repo
|
| 70 |
+
repo_path = os.path.join(temp_dir, repo_name)
|
| 71 |
+
git.Repo.clone_from(repo_url, repo_path)
|
| 72 |
+
|
| 73 |
+
progress = gr.update(value="π Analyzing repository structure...", visible=True)
|
| 74 |
+
|
| 75 |
+
# Gather repository file structure and contents
|
| 76 |
+
repo_data = ""
|
| 77 |
+
file_count = 0 # Initialize file counter
|
| 78 |
+
|
| 79 |
+
for root, _, filenames in os.walk(repo_path):
|
| 80 |
+
for file in filenames:
|
| 81 |
+
file_count += 1 # Increment file counter
|
| 82 |
+
file_path = os.path.join(root, file)
|
| 83 |
+
|
| 84 |
+
try:
|
| 85 |
+
with open(file_path, "r", encoding="utf-8") as f:
|
| 86 |
+
file_content = f.read()
|
| 87 |
+
repo_data += f"\n**File {file_count}:** {file_path.replace(repo_path, '')}\n```\n{file_content[:1000]}\n```\n"
|
| 88 |
+
except Exception:
|
| 89 |
+
repo_data += f"\n**File {file_count}:** {file_path.replace(repo_path, '')} (β οΈ Cannot read binary file)\n"
|
| 90 |
+
|
| 91 |
+
progress = gr.update(value="π€ Sending data to AI for evaluation...", visible=True)
|
| 92 |
+
|
| 93 |
+
# AI-based evaluation
|
| 94 |
+
evaluation = f"β
**Evaluation for Role: {role}**\n\n"
|
| 95 |
+
evaluation += f"π Repository `{repo_name}` has `{file_count}` files.\n"
|
| 96 |
+
evaluation += f"π‘ Key focus areas: {focus_areas}\n\n"
|
| 97 |
+
evaluation += "**π Code Quality Analysis:**\n"
|
| 98 |
+
|
| 99 |
+
completion = client.chat.completions.create(
|
| 100 |
+
model="o1",
|
| 101 |
+
messages=[
|
| 102 |
+
{
|
| 103 |
+
"role": "user",
|
| 104 |
+
"content": generate_prompt(repo_url, role, repo_data, focus_areas)
|
| 105 |
+
}
|
| 106 |
+
]
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
evaluation += "\n" + completion.choices[0].message.content
|
| 110 |
+
|
| 111 |
+
progress = gr.update(value="β
Analysis complete!", visible=True)
|
| 112 |
+
return evaluation, progress
|
| 113 |
+
|
| 114 |
+
except Exception as e:
|
| 115 |
+
return f"β Error analyzing repository: {str(e)}", gr.update(visible=False)
|
| 116 |
+
|
| 117 |
+
finally:
|
| 118 |
+
shutil.rmtree(temp_dir) # Cleanup
|
| 119 |
+
|
| 120 |
+
# Gradio UI
|
| 121 |
+
with gr.Blocks() as app:
|
| 122 |
+
gr.Markdown("# π οΈ AI-Powered Candidate Evaluation System")
|
| 123 |
+
|
| 124 |
+
password = gr.Textbox(label="Enter Password")
|
| 125 |
+
role = gr.Textbox(label="Role the Candidate is Applying For")
|
| 126 |
+
repo_url = gr.Textbox(label="GitHub Repository URL")
|
| 127 |
+
focus_areas = gr.Textbox(label="Optional Focus Areas (e.g., Clean Code, Performance)")
|
| 128 |
+
|
| 129 |
+
output = gr.Markdown()
|
| 130 |
+
progress = gr.Markdown(visible=False) # Loader
|
| 131 |
+
|
| 132 |
+
submit_btn = gr.Button("π Evaluate")
|
| 133 |
+
|
| 134 |
+
submit_btn.click(
|
| 135 |
+
fn=analyze_repo,
|
| 136 |
+
inputs=[repo_url, role, focus_areas, password],
|
| 137 |
+
outputs=[output, progress]
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
app.launch()
|
requirements.txt
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
aiofiles==23.2.1
|
| 2 |
+
annotated-types==0.7.0
|
| 3 |
+
anyio==4.8.0
|
| 4 |
+
certifi==2025.1.31
|
| 5 |
+
charset-normalizer==3.4.1
|
| 6 |
+
click==8.1.8
|
| 7 |
+
distro==1.9.0
|
| 8 |
+
fastapi==0.115.8
|
| 9 |
+
ffmpy==0.5.0
|
| 10 |
+
filelock==3.17.0
|
| 11 |
+
fsspec==2025.2.0
|
| 12 |
+
gitdb==4.0.12
|
| 13 |
+
GitPython==3.1.44
|
| 14 |
+
gradio==5.15.0
|
| 15 |
+
gradio_client==1.7.0
|
| 16 |
+
h11==0.14.0
|
| 17 |
+
httpcore==1.0.7
|
| 18 |
+
httpx==0.28.1
|
| 19 |
+
huggingface-hub==0.28.1
|
| 20 |
+
idna==3.10
|
| 21 |
+
Jinja2==3.1.5
|
| 22 |
+
jiter==0.8.2
|
| 23 |
+
markdown-it-py==3.0.0
|
| 24 |
+
MarkupSafe==2.1.5
|
| 25 |
+
mdurl==0.1.2
|
| 26 |
+
numpy==2.2.2
|
| 27 |
+
openai==1.61.1
|
| 28 |
+
orjson==3.10.15
|
| 29 |
+
packaging==24.2
|
| 30 |
+
pandas==2.2.3
|
| 31 |
+
pillow==11.1.0
|
| 32 |
+
pydantic==2.10.6
|
| 33 |
+
pydantic_core==2.27.2
|
| 34 |
+
pydub==0.25.1
|
| 35 |
+
Pygments==2.19.1
|
| 36 |
+
python-dateutil==2.9.0.post0
|
| 37 |
+
python-dotenv==1.0.1
|
| 38 |
+
python-multipart==0.0.20
|
| 39 |
+
pytz==2025.1
|
| 40 |
+
PyYAML==6.0.2
|
| 41 |
+
requests==2.32.3
|
| 42 |
+
rich==13.9.4
|
| 43 |
+
ruff==0.9.4
|
| 44 |
+
safehttpx==0.1.6
|
| 45 |
+
semantic-version==2.10.0
|
| 46 |
+
shellingham==1.5.4
|
| 47 |
+
six==1.17.0
|
| 48 |
+
smmap==5.0.2
|
| 49 |
+
sniffio==1.3.1
|
| 50 |
+
starlette==0.45.3
|
| 51 |
+
tomlkit==0.13.2
|
| 52 |
+
tqdm==4.67.1
|
| 53 |
+
typer==0.15.1
|
| 54 |
+
typing_extensions==4.12.2
|
| 55 |
+
tzdata==2025.1
|
| 56 |
+
urllib3==2.3.0
|
| 57 |
+
uvicorn==0.34.0
|
| 58 |
+
websockets==14.2
|