EATosin commited on
Commit
46d0b96
·
verified ·
1 Parent(s): 255071e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -105
app.py CHANGED
@@ -1,158 +1,125 @@
1
  import streamlit as st
2
  import google.generativeai as genai
3
  from github import Github
 
4
  import os
5
  from dotenv import load_dotenv
6
 
7
- # --- CONFIG & SETUP ---
8
- st.set_page_config(page_title="DevResume AI", layout="wide", page_icon="🚀")
9
 
10
- # Load Keys (Cloud or Local)
11
  api_key = os.getenv("GEMINI_API_KEY")
12
  github_token = os.getenv("GITHUB_TOKEN")
13
 
14
- # Fallback for local testing
15
  if not api_key:
16
  load_dotenv()
17
  api_key = os.getenv("GEMINI_API_KEY")
18
  github_token = os.getenv("GITHUB_TOKEN")
19
 
20
- # --- ENGINE 1: GITHUB CRAWLER ---
 
 
 
 
 
 
 
 
 
 
 
21
  def fetch_github_data(username):
22
- """
23
- SOTA Scraper: Fetches top repos, stars, languages, and README summaries.
24
- """
25
  try:
26
- # Auth guarantees higher rate limits
27
  g = Github(github_token) if github_token else Github()
28
  user = g.get_user(username)
 
29
 
30
- data = {
31
- "name": user.name or username,
32
- "bio": user.bio or "AI & Software Engineer",
33
- "url": user.html_url,
34
- "repos": []
35
- }
36
-
37
- # Sort repos by Stars (Metric of Impact)
38
- repos = user.get_repos()
39
- sorted_repos = sorted(repos, key=lambda x: x.stargazers_count, reverse=True)[:6]
40
-
41
- for repo in sorted_repos:
42
- # Skip forked repos to focus on original work
43
- if repo.fork:
44
- continue
45
-
46
- # Smart README Fetcher
47
- readme_text = "No details provided."
48
  try:
49
- readme = repo.get_readme()
50
- if readme.decoded_content:
51
- readme_text = readme.decoded_content.decode("utf-8")[:1500] # Context Window Optimization
52
- except:
53
- pass
54
-
55
- data["repos"].append({
56
- "name": repo.name,
57
- "stars": repo.stargazers_count,
58
- "lang": repo.language,
59
- "desc": repo.description,
60
- "readme": readme_text
61
- })
62
 
63
- return data
 
 
64
  except Exception as e:
65
- return {"error": f"GitHub API Error: {str(e)}"}
66
 
67
- # --- ENGINE 2: GEMINI RESUME ARCHITECT ---
68
- def generate_resume_content(profile, target_role):
69
  genai.configure(api_key=api_key)
70
-
71
- # Try SOTA Model first
72
  try:
73
  model = genai.GenerativeModel('gemini-2.5-flash')
74
  except:
75
  model = genai.GenerativeModel('gemini-1.5-flash')
76
 
77
- # Context Construction
78
- repo_context = ""
79
- for r in profile['repos']:
80
- repo_context += f"""
81
- - PROJECT: {r['name']} ({r['stars']} Stars, {r['lang']})
82
- DESCRIPTION: {r['desc']}
83
- TECHNICAL CONTEXT: {r['readme']}
84
- """
85
-
86
  prompt = f"""
87
- You are a Senior Technical Recruiter for a FAANG company.
88
 
89
- CANDIDATE: {profile['name']}
90
- ROLE: {target_role}
 
91
 
92
- TASK:
93
- Write the "Featured Projects" section of a resume based on their GitHub code.
94
 
95
- RULES:
96
- 1. Select the top 3 most technically impressive projects.
97
- 2. Write 3 bullet points for each project.
98
- 3. STRICTLY use the "XYZ Pattern" (Accomplished [X] as measured by [Y], by doing [Z]).
99
- 4. Quantify impact where possible (e.g., "Reduced latency by...", "Processed 10k+ requests...").
100
- 5. Highlight stack: Python, PyTorch, FastAPI, Docker, etc.
101
 
102
- INPUT DATA (GitHub Scrape):
103
- {repo_context}
104
 
105
- OUTPUT FORMAT (Markdown):
106
- ## 🚀 Featured Projects
 
 
 
 
 
107
 
108
- **[Project Name]** | *[Languages/Tools]*
109
- * [Bullet 1 using XYZ pattern]
110
- * [Bullet 2 using XYZ pattern]
111
- * [Bullet 3 using XYZ pattern]
112
  """
113
 
114
  try:
115
  response = model.generate_content(prompt)
116
  return response.text
117
  except Exception as e:
118
- return f"AI Generation Error: {str(e)}"
119
 
120
- # --- UI LAYER ---
121
- with st.sidebar:
122
- st.title("🚀 DevResume AI")
123
- st.markdown("Turns your **GitHub Code** into a **Senior Resume** instantly.")
124
- st.info("Powered by **Gemini 2.5** & **PyGithub**")
125
- st.caption("Built by Owadokun Tosin Tobi")
126
-
127
- st.title("👨‍💻 GitHub-to-Resume Generator")
128
- st.markdown("Stop writing generic bullets. Let AI analyze your actual code and write high-impact XYZ statements.")
129
 
130
  col1, col2 = st.columns([1, 2])
131
 
132
  with col1:
 
133
  gh_user = st.text_input("GitHub Username", placeholder="eatosin")
134
  role = st.text_input("Target Role", value="Senior AI Engineer")
135
- generate_btn = st.button(" Generate Resume", type="primary")
136
-
137
- with col2:
138
- if generate_btn:
139
- if not gh_user:
140
- st.warning("Please enter a username.")
141
  else:
142
- with st.status("🕷Crawling GitHub Ecosystem...", expanded=True) as status:
143
- st.write("Connecting to GitHub API...")
144
- data = fetch_github_data(gh_user)
 
 
 
145
 
146
- if "error" in data:
147
- status.update(label="❌ Error", state="error")
148
- st.error(data['error'])
149
  else:
150
- st.write(f" Found {len(data['repos'])} repositories.")
151
- st.write("🧠 Analyzing READMEs & Codebase...")
152
- resume = generate_resume_content(data, role)
153
- status.update(label="✅ Resume Generated!", state="complete")
154
-
155
- st.markdown("---")
156
- st.subheader("📄 Generated Project Section")
157
- st.markdown(resume)
158
- st.download_button("📥 Download Markdown", resume, file_name="featured_projects.md")
 
1
  import streamlit as st
2
  import google.generativeai as genai
3
  from github import Github
4
+ from pypdf import PdfReader
5
  import os
6
  from dotenv import load_dotenv
7
 
8
+ # --- CONFIG ---
9
+ st.set_page_config(page_title="DevResume Pro", layout="wide", page_icon="")
10
 
11
+ # Load Keys
12
  api_key = os.getenv("GEMINI_API_KEY")
13
  github_token = os.getenv("GITHUB_TOKEN")
14
 
 
15
  if not api_key:
16
  load_dotenv()
17
  api_key = os.getenv("GEMINI_API_KEY")
18
  github_token = os.getenv("GITHUB_TOKEN")
19
 
20
+ # --- FUNC 1: PDF PARSER ---
21
+ def extract_pdf_text(file):
22
+ try:
23
+ reader = PdfReader(file)
24
+ text = ""
25
+ for page in reader.pages:
26
+ text += page.extract_text()
27
+ return text
28
+ except:
29
+ return None
30
+
31
+ # --- FUNC 2: GITHUB CRAWLER ---
32
  def fetch_github_data(username):
 
 
 
33
  try:
 
34
  g = Github(github_token) if github_token else Github()
35
  user = g.get_user(username)
36
+ repos = sorted(user.get_repos(), key=lambda x: x.stargazers_count, reverse=True)[:4]
37
 
38
+ repo_data = ""
39
+ for repo in repos:
40
+ readme = "No README"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  try:
42
+ if repo.get_readme().decoded_content:
43
+ readme = repo.get_readme().decoded_content.decode("utf-8")[:1000]
44
+ except: pass
 
 
 
 
 
 
 
 
 
 
45
 
46
+ repo_data += f"Project: {repo.name} ({repo.stargazers_count} Stars, {repo.language})\nDesc: {repo.description}\nContext: {readme}\n\n"
47
+
48
+ return repo_data
49
  except Exception as e:
50
+ return f"Error fetching GitHub: {str(e)}"
51
 
52
+ # --- FUNC 3: THE RESUME ARCHITECT ---
53
+ def generate_full_resume(old_resume_text, github_data, target_role):
54
  genai.configure(api_key=api_key)
 
 
55
  try:
56
  model = genai.GenerativeModel('gemini-2.5-flash')
57
  except:
58
  model = genai.GenerativeModel('gemini-1.5-flash')
59
 
 
 
 
 
 
 
 
 
 
60
  prompt = f"""
61
+ You are an Elite Tech Resume Writer.
62
 
63
+ SOURCE MATERIAL:
64
+ 1. OLD RESUME TEXT:
65
+ {old_resume_text[:4000]}
66
 
67
+ 2. GITHUB PORTFOLIO ANALYSIS:
68
+ {github_data}
69
 
70
+ TARGET ROLE: {target_role}
 
 
 
 
 
71
 
72
+ TASK:
73
+ Write a complete, SOTA Resume (Markdown Format).
74
 
75
+ STRUCTURE:
76
+ 1. **Header:** Name, Title (Target Role), Links (GitHub/LinkedIn).
77
+ 2. **Professional Summary:** Synthesize the old resume + new GitHub achievements into a killer 3-line bio.
78
+ 3. **Technical Skills:** Grouped by category (Languages, Frameworks, Tools).
79
+ 4. **Featured Projects:** Use the GitHub Data. Write "XYZ Style" impact bullets.
80
+ 5. **Experience:** Polish the old resume's experience section. Make it punchy.
81
+ 6. **Education:** Keep it simple.
82
 
83
+ TONE: Senior, High-Impact, Quantified.
 
 
 
84
  """
85
 
86
  try:
87
  response = model.generate_content(prompt)
88
  return response.text
89
  except Exception as e:
90
+ return f"AI Error: {e}"
91
 
92
+ # --- UI ---
93
+ st.title("⚡ DevResume Pro: The Hybrid Architect")
94
+ st.markdown("Combines your **Old Resume** + **Real GitHub Code** to build the ultimate portfolio.")
 
 
 
 
 
 
95
 
96
  col1, col2 = st.columns([1, 2])
97
 
98
  with col1:
99
+ st.subheader("1. Your Inputs")
100
  gh_user = st.text_input("GitHub Username", placeholder="eatosin")
101
  role = st.text_input("Target Role", value="Senior AI Engineer")
102
+ uploaded_file = st.file_uploader("Upload Old Resume (PDF)", type="pdf")
103
+
104
+ if st.button("🚀 Build Resume", type="primary"):
105
+ if not gh_user or not uploaded_file:
106
+ st.error("Please provide both GitHub User and PDF Resume.")
 
107
  else:
108
+ with st.spinner("🕵‍♂️ Reading PDF & Scraping GitHub..."):
109
+ # 1. Read PDF
110
+ pdf_text = extract_pdf_text(uploaded_file)
111
+
112
+ # 2. Scrape GitHub
113
+ gh_data = fetch_github_data(gh_user)
114
 
115
+ # 3. Generate
116
+ if pdf_text and gh_data:
117
+ st.session_state['resume'] = generate_full_resume(pdf_text, gh_data, role)
118
  else:
119
+ st.error("Failed to read inputs.")
120
+
121
+ with col2:
122
+ if 'resume' in st.session_state:
123
+ st.subheader("📄 Your New Resume")
124
+ st.markdown(st.session_state['resume'])
125
+ st.download_button("📥 Download Markdown", st.session_state['resume'], file_name="SOTA_Resume.md")