Sumit404 commited on
Commit
0e5ac9b
·
verified ·
1 Parent(s): 4250149

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +369 -103
app.py CHANGED
@@ -25,7 +25,7 @@ except Exception as e:
25
  logger.error(f"Failed to download spaCy model: {str(download_error)}")
26
  raise Exception("Cannot proceed without 'en_core_web_sm' model. Please ensure it is installed.")
27
 
28
- # HTML template using Bootstrap (responsive design)
29
  HTML_TEMPLATE = """
30
  <!DOCTYPE html>
31
  <html lang="en">
@@ -33,104 +33,316 @@ HTML_TEMPLATE = """
33
  <meta charset="UTF-8">
34
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
35
  <title>{NAME} - IT Portfolio</title>
36
- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
 
37
  <style>
38
  body {{
39
- font-family: 'Arial', sans-serif;
40
- background: {BG_COLOR};
41
- color: {TEXT_COLOR};
42
  }}
43
- .container {{
44
- max-width: 900px;
45
- margin: 0 auto;
46
- padding: 20px;
47
- background: {CARD_BG_COLOR};
48
- border-radius: 10px;
49
- box-shadow: 0 0 15px rgba(0,0,0,0.1);
50
  }}
51
- .header {{
52
- text-align: center;
53
- margin-bottom: 30px;
 
 
 
 
 
54
  }}
55
- .header h1 {{
56
- font-size: 2.5rem;
57
- margin-bottom: 10px;
58
  }}
59
- .section {{
60
- margin-bottom: 30px;
 
 
61
  }}
62
- .section h2 {{
63
- font-size: 1.8rem;
64
- border-bottom: 2px solid {PRIMARY_COLOR};
65
- padding-bottom: 5px;
66
- margin-bottom: 15px;
67
  }}
68
- .skills-list {{
69
- display: flex;
70
- flex-wrap: wrap;
71
- gap: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }}
73
- .skill-chip {{
74
- background: {PRIMARY_COLOR};
75
- color: white;
76
- padding: 5px 15px;
77
- border-radius: 20px;
78
- font-size: 0.9rem;
 
 
 
79
  }}
80
- .download-btn {{
81
- display: block;
82
- text-align: center;
83
- margin-top: 20px;
84
  }}
85
- .download-btn a {{
86
- background: {PRIMARY_COLOR};
87
- color: white;
88
- padding: 10px 20px;
89
- text-decoration: none;
90
- border-radius: 5px;
91
  }}
92
- .download-btn a:hover {{
93
- background: {SECONDARY_COLOR};
94
  }}
95
- @media (max-width: 768px) {{
96
- .header h1 {{
97
- font-size: 1.8rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  }}
99
- .section h2 {{
100
- font-size: 1.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }}
102
  }}
103
  </style>
104
  </head>
105
  <body>
106
- <div class="container">
107
- <div class="header">
108
- <h1>{NAME}</h1>
109
- <p>{EMAIL} | {PHONE}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  </div>
111
- <div class="section">
112
- <h2>Education</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  {EDUCATION}
114
  </div>
115
- <div class="section">
116
- <h2>Work Experience</h2>
 
 
 
 
117
  {EXPERIENCE}
118
  </div>
119
- <div class="section">
120
- <h2>Projects</h2>
 
 
 
 
121
  {PROJECTS}
122
  </div>
123
- <div class="section">
124
- <h2>Skills</h2>
125
- <div class="skills-list">
 
 
 
 
126
  {SKILLS}
127
  </div>
128
  </div>
129
- <div class="download-btn">
130
- <a href="portfolio.html" download>Download Portfolio</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  </div>
132
- </div>
133
- <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  </body>
135
  </html>
136
  """
@@ -164,7 +376,7 @@ def extract_details(resume_text):
164
  email = "your.email@example.com"
165
  phone = "555-555-5555"
166
  email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
167
- phone_pattern = r'\b\d{3}-\d{3}-\d{4}\b'
168
  for line in lines:
169
  if re.search(email_pattern, line):
170
  email = html.escape(re.search(email_pattern, line).group())
@@ -172,23 +384,59 @@ def extract_details(resume_text):
172
  phone = html.escape(re.search(phone_pattern, line).group())
173
 
174
  # Extract education
175
- education_keywords = ["degree", "university", "college", "master", "bachelor", "b.tech", "m.tech"]
176
  education = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in education_keywords)]
177
- education_html = "".join(f"<p>{html.escape(edu)}</p>" for edu in education) if education else "<p>Not provided</p>"
 
 
 
 
 
 
 
 
 
 
 
 
178
 
179
  # Extract experience
180
- experience_keywords = ["intern", "software", "research", "engineer", "developer"]
181
  experience = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in experience_keywords)]
182
- experience_html = "".join(f"<p>{html.escape(exp)}</p>" for exp in experience) if experience else "<p>Not provided</p>"
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
  # Extract projects
185
- project_keywords = ["project", "developed", "built"]
186
  projects = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in project_keywords)]
187
- projects_html = "".join(f"<p>{html.escape(proj)}</p>" for proj in projects) if projects else "<p>Not provided</p>"
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
  # Extract skills
190
  skills_line = next((line for line in lines if "skills" in line.lower()), "")
191
- known_skills = ["python", "javascript", "nlp", "cnns", "aws", "flask", "fastapi", "sql", "docker", "kubernetes", "teamwork", "communication"]
192
  skills = []
193
  if skills_line:
194
  for skill in known_skills:
@@ -198,13 +446,44 @@ def extract_details(resume_text):
198
  for skill in trending_skills:
199
  if skill.lower() not in [s.lower() for s in skills]:
200
  skills.append(skill)
201
- skills_html = "".join(f'<span class="skill-chip">{html.escape(skill.title())}</span>' for skill in skills) if skills else "<span>Not provided</span>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
 
203
  logger.info("Successfully extracted details from resume.")
204
- return name, email, phone, education_html, experience_html, projects_html, skills_html, skills
205
  except Exception as e:
206
  logger.error(f"Error extracting details: {str(e)}")
207
- return "[Your Name]", "your.email@example.com", "555-555-5555", f"Error: {str(e)}", f"Error: {str(e)}", f"Error: {str(e)}", f"Error: {str(e)}", []
208
 
209
  def generate_portfolio(file, theme="Light"):
210
  try:
@@ -227,21 +506,7 @@ def generate_portfolio(file, theme="Light"):
227
  return resume_text, resume_text
228
 
229
  # Extract details from the resume
230
- name, email, phone, education_html, experience_html, projects_html, skills_html, skills = extract_details(resume_text)
231
-
232
- # Determine theme colors
233
- if theme == "Dark":
234
- bg_color = "#1a202c"
235
- text_color = "#e2e8f0"
236
- card_bg_color = "#2d3748"
237
- primary_color = "#63b3ed"
238
- secondary_color = "#4299e1"
239
- else:
240
- bg_color = "#f5f7fa"
241
- text_color = "#1a202c"
242
- card_bg_color = "#ffffff"
243
- primary_color = "#4a90e2"
244
- secondary_color = "#63b3ed"
245
 
246
  # Generate the portfolio HTML
247
  html_code = HTML_TEMPLATE.format(
@@ -252,22 +517,19 @@ def generate_portfolio(file, theme="Light"):
252
  EXPERIENCE=experience_html,
253
  PROJECTS=projects_html,
254
  SKILLS=skills_html,
255
- BG_COLOR=bg_color,
256
- TEXT_COLOR=text_color,
257
- CARD_BG_COLOR=card_bg_color,
258
- PRIMARY_COLOR=primary_color,
259
- SECONDARY_COLOR=secondary_color
260
  )
261
 
262
  # Save to index.html
263
  with open("index.html", "w", encoding="utf-8") as f:
264
  f.write(html_code)
265
 
266
- # Preview HTML for Gradio
267
  html_preview = f"""
268
- <div style='font-family: Arial; max-width: 800px; margin: auto; padding: 20px; background: {card_bg_color}; box-shadow: 0 0 10px rgba(0,0,0,0.1);'>
269
  <h1 style='text-align: center;'>{name}</h1>
270
- <p style='text-align: center;'>{email} | {phone}</p>
271
  <h2>Education</h2>
272
  {education_html}
273
  <h2>Work Experience</h2>
@@ -278,6 +540,10 @@ def generate_portfolio(file, theme="Light"):
278
  <div style='display: flex; flex-wrap: wrap; gap: 10px;'>
279
  {skills_html}
280
  </div>
 
 
 
 
281
  </div>
282
  """
283
 
 
25
  logger.error(f"Failed to download spaCy model: {str(download_error)}")
26
  raise Exception("Cannot proceed without 'en_core_web_sm' model. Please ensure it is installed.")
27
 
28
+ # HTML template using Tailwind CSS and AOS (provided by user)
29
  HTML_TEMPLATE = """
30
  <!DOCTYPE html>
31
  <html lang="en">
 
33
  <meta charset="UTF-8">
34
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
35
  <title>{NAME} - IT Portfolio</title>
36
+ <script src="https://cdn.tailwindcss.com"></script>
37
+ <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
38
  <style>
39
  body {{
40
+ font-family: 'Inter', sans-serif;
41
+ scroll-behavior: smooth;
42
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
43
  }}
44
+ .section-heading {{
45
+ font-size: 1.25rem sm:1.5rem lg:2rem;
46
+ font-weight: 800;
47
+ color: #1a202c;
48
+ margin-bottom: 1.25rem;
49
+ text-transform: uppercase;
50
+ letter-spacing: 1px;
51
  }}
52
+ .card {{
53
+ background: rgba(255, 255, 255, 0.9);
54
+ border-radius: 1rem;
55
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
56
+ padding: 1rem sm:1.5rem;
57
+ margin-bottom: 1rem;
58
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
59
+ transform: perspective(1000px) rotateX(0deg) rotateY(0deg);
60
  }}
61
+ .card:hover {{
62
+ transform: perspective(1000px) rotateX(2deg) rotateY(2deg);
63
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.2);
64
  }}
65
+ .navbar {{
66
+ backdrop-filter: blur(12px);
67
+ background: rgba(255, 255, 255, 0.1);
68
+ border-bottom: 1px solid rgba(255, 255, 255, 0.2);
69
  }}
70
+ .nav-link {{
71
+ position: relative;
72
+ transition: color 0.3s ease;
 
 
73
  }}
74
+ .nav-link:hover::after {{
75
+ content: '';
76
+ position: absolute;
77
+ width: 100%;
78
+ height: 2px;
79
+ bottom: -4px;
80
+ left: 0;
81
+ background: #4a90e2;
82
+ animation: underline 0.3s ease forwards;
83
+ }}
84
+ @keyframes underline {{
85
+ from {{ width: 0; }}
86
+ to {{ width: 100%; }}
87
+ }}
88
+ .skill-bar {{
89
+ height: 8px;
90
+ background: #e2e8f0;
91
+ border-radius: 4px;
92
+ overflow: hidden;
93
+ margin-top: 0.5rem;
94
+ }}
95
+ .skill-progress {{
96
+ height: 100%;
97
+ background: linear-gradient(90deg, #4a90e2, #63b3ed);
98
+ transition: width 1s ease-in-out;
99
+ }}
100
+ .hero-text {{
101
+ animation: fadeInUp 1s ease-out;
102
+ }}
103
+ @keyframes fadeInUp {{
104
+ from {{ opacity: 0; transform: translateY(20px); }}
105
+ to {{ opacity: 1; transform: translateY(0); }}
106
+ }}
107
+ .btn-3d {{
108
+ position: relative;
109
+ overflow: hidden;
110
+ transition: transform 0.2s ease;
111
+ }}
112
+ .btn-3d:hover {{
113
+ transform: translateY(-2px);
114
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
115
  }}
116
+ .btn-3d::before {{
117
+ content: '';
118
+ position: absolute;
119
+ top: 0;
120
+ left: -100%;
121
+ width: 100%;
122
+ height: 100%;
123
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent);
124
+ transition: 0.5s;
125
  }}
126
+ .btn-3d:hover::before {{
127
+ left: 100%;
 
 
128
  }}
129
+ .profile-img {{
130
+ border: 4px solid white;
131
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
 
 
 
132
  }}
133
+ .hamburger {{
134
+ display: none;
135
  }}
136
+ .nav-menu {{
137
+ display: flex;
138
+ }}
139
+ @media (max-width: 640px) {{
140
+ .hamburger {{
141
+ display: block;
142
+ cursor: pointer;
143
+ }}
144
+ .nav-menu {{
145
+ display: none;
146
+ flex-direction: column;
147
+ position: absolute;
148
+ top: 64px;
149
+ right: 0;
150
+ width: 100%;
151
+ background: rgba(255, 255, 255, 0.95);
152
+ padding: 0.75rem;
153
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
154
+ }}
155
+ .nav-menu.active {{
156
+ display: flex;
157
+ }}
158
+ .nav-link {{
159
+ padding: 0.5rem 1rem;
160
+ text-align: center;
161
+ }}
162
+ .section-heading {{
163
+ font-size: 1rem;
164
+ }}
165
+ .card {{
166
+ padding: 0.75rem;
167
+ }}
168
+ .profile-img {{
169
+ width: 100px;
170
+ height: 100px;
171
  }}
172
+ .hero-text h1 {{
173
+ font-size: 1.75rem;
174
+ }}
175
+ .hero-text p {{
176
+ font-size: 0.875rem;
177
+ }}
178
+ .btn-3d {{
179
+ padding: 0.5rem 1rem;
180
+ font-size: 0.75rem;
181
+ }}
182
+ }}
183
+ @media (min-width: 641px) and (max-width: 768px) {{
184
+ .profile-img {{
185
+ width: 120px;
186
+ height: 120px;
187
  }}
188
  }}
189
  </style>
190
  </head>
191
  <body>
192
+ <!-- Navbar -->
193
+ <nav class="navbar fixed top-0 w-full z-50">
194
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
195
+ <div class="flex justify-between h-16">
196
+ <div class="flex items-center">
197
+ <span class="text-base sm:text-lg lg:text-xl font-bold text-gray-800">{NAME}</span>
198
+ </div>
199
+ <div class="flex items-center">
200
+ <div class="hamburger sm:hidden">
201
+ <svg class="w-5 h-5 sm:w-6 sm:h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
202
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
203
+ </svg>
204
+ </div>
205
+ <div class="nav-menu sm:flex sm:items-center sm:space-x-2 lg:space-x-4">
206
+ <a href="#home" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Home</a>
207
+ <a href="#about" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">About</a>
208
+ <a href="#education" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Education</a>
209
+ <a href="#experience" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Experience</a>
210
+ <a href="#projects" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Projects</a>
211
+ <a href="#skills" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Skills</a>
212
+ <a href="#involvement" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Involvement</a>
213
+ <a href="#contact" class="nav-link text-gray-600 hover:text-gray-900 px-2 py-2 rounded-md text-xs sm:text-sm font-medium">Contact</a>
214
+ </div>
215
+ </div>
216
+ </div>
217
  </div>
218
+ </nav>
219
+
220
+ <!-- Hero Section -->
221
+ <section id="home" class="min-h-screen flex items-center justify-center bg-gradient-to-r from-blue-600 to-purple-700">
222
+ <div class="text-center text-white hero-text" data-aos="fade-up">
223
+ <img src="https://via.placeholder.com/150" alt="{NAME}" class="profile-img w-24 h-24 sm:w-36 sm:h-36 lg:w-48 lg:h-48 rounded-full mx-auto mb-3 sm:mb-4 object-cover">
224
+ <h1 class="text-xl sm:text-3xl lg:text-5xl font-bold mb-3 sm:mb-4">{NAME}</h1>
225
+ <p class="text-sm sm:text-base lg:text-xl mb-4 sm:mb-6">{SKILLS_SUMMARY}</p>
226
+ <a href="#contact" class="btn-3d bg-white text-blue-600 px-3 sm:px-4 py-1 sm:py-2 rounded-full font-semibold text-xs sm:text-base">Get in Touch</a>
227
+ </div>
228
+ </section>
229
+
230
+ <!-- About Section -->
231
+ <section id="about" class="py-8 sm:py-12 lg:py-20">
232
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
233
+ <h2 class="section-heading text-center" data-aos="fade-up">About Me</h2>
234
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
235
+ <p class="text-gray-700 leading-relaxed text-xs sm:text-sm lg:text-base">
236
+ I’m {NAME}, a passionate IT professional specializing in electrical engineering and machine learning. I thrive in dynamic environments, driving innovation through projects like Short-term Traffic Prediction Using DTC. My expertise includes building scalable solutions with a focus on tensor completion and traffic data analysis.
237
+ </p>
238
+ </div>
239
+ </div>
240
+ </section>
241
+
242
+ <!-- Education Section -->
243
+ <section id="education" class="py-8 sm:py-12 lg:py-20 bg-gray-50">
244
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
245
+ <h2 class="section-heading text-center" data-aos="fade-up">Education</h2>
246
  {EDUCATION}
247
  </div>
248
+ </section>
249
+
250
+ <!-- Experience Section -->
251
+ <section id="experience" class="py-8 sm:py-12 lg:py-20">
252
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
253
+ <h2 class="section-heading text-center" data-aos="fade-up">Experience</h2>
254
  {EXPERIENCE}
255
  </div>
256
+ </section>
257
+
258
+ <!-- Projects Section -->
259
+ <section id="projects" class="py-8 sm:py-12 lg:py-20 bg-gray-50">
260
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
261
+ <h2 class="section-heading text-center" data-aos="fade-up">Projects</h2>
262
  {PROJECTS}
263
  </div>
264
+ </section>
265
+
266
+ <!-- Skills Section -->
267
+ <section id="skills" class="py-8 sm:py-12 lg:py-20">
268
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
269
+ <h2 class="section-heading text-center" data-aos="fade-up">Skills</h2>
270
+ <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4 lg:gap-6">
271
  {SKILLS}
272
  </div>
273
  </div>
274
+ </section>
275
+
276
+ <!-- Involvement Section -->
277
+ <section id="involvement" class="py-8 sm:py-12 lg:py-20 bg-gray-50">
278
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
279
+ <h2 class="section-heading text-center" data-aos="fade-up">Involvement</h2>
280
+ {INVOLVEMENT}
281
+ </div>
282
+ </section>
283
+
284
+ <!-- Contact Section -->
285
+ <section id="contact" class="py-8 sm:py-12 lg:py-20 bg-gray-50">
286
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
287
+ <h2 class="section-heading text-center" data-aos="fade-up">Contact</h2>
288
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
289
+ <p class="text-gray-700 text-xs sm:text-sm">Email: <a href="mailto:{EMAIL}" class="text-blue-600 hover:underline">{EMAIL}</a></p>
290
+ <p class="text-gray-700 text-xs sm:text-sm">Phone: {PHONE}</p>
291
+ </div>
292
+ </div>
293
+ </section>
294
+
295
+ <!-- Footer -->
296
+ <footer class="bg-gray-900 text-white py-4 sm:py-6">
297
+ <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
298
+ <p class="text-xs sm:text-sm">© 2025 {NAME}. All rights reserved.</p>
299
  </div>
300
+ </footer>
301
+
302
+ <!-- Scripts -->
303
+ <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
304
+ <script>
305
+ // Initialize AOS
306
+ AOS.init({{
307
+ duration: 800,
308
+ once: true
309
+ }});
310
+
311
+ // Hamburger Menu Toggle
312
+ const hamburger = document.querySelector('.hamburger');
313
+ const navMenu = document.querySelector('.nav-menu');
314
+ hamburger.addEventListener('click', () => {{
315
+ navMenu.classList.toggle('active');
316
+ }});
317
+
318
+ // Close menu when clicking a link
319
+ document.querySelectorAll('.nav-link').forEach(link => {{
320
+ link.addEventListener('click', (e) => {{
321
+ e.preventDefault();
322
+ navMenu.classList.remove('active');
323
+ const targetId = link.getAttribute('href').substring(1);
324
+ const targetSection = document.getElementById(targetId);
325
+ targetSection.scrollIntoView({{ behavior: 'smooth' }});
326
+ }});
327
+ }});
328
+
329
+ // Skill Bar Animation
330
+ const skillBars = document.querySelectorAll('.skill-progress');
331
+ const animateSkills = () => {{
332
+ skillBars.forEach(bar => {{
333
+ const progress = bar.getAttribute('data-progress');
334
+ bar.style.width = `${{progress}}%`;
335
+ }});
336
+ }};
337
+ window.addEventListener('scroll', () => {{
338
+ const skillsSection = document.getElementById('skills');
339
+ const sectionTop = skillsSection.getBoundingClientRect().top;
340
+ if (sectionTop < window.innerHeight * 0.8) {{
341
+ animateSkills();
342
+ window.removeEventListener('scroll', animateSkills);
343
+ }}
344
+ }});
345
+ </script>
346
  </body>
347
  </html>
348
  """
 
376
  email = "your.email@example.com"
377
  phone = "555-555-5555"
378
  email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
379
+ phone_pattern = r'\b\d{3}-\d{3}-\d{4}\b|\+\d{2}-\d{10}\b'
380
  for line in lines:
381
  if re.search(email_pattern, line):
382
  email = html.escape(re.search(email_pattern, line).group())
 
384
  phone = html.escape(re.search(phone_pattern, line).group())
385
 
386
  # Extract education
387
+ education_keywords = ["degree", "university", "college", "master", "bachelor", "b.tech", "m.tech", "school", "12th", "10th"]
388
  education = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in education_keywords)]
389
+ education_html = ""
390
+ for i, edu in enumerate(education, 1):
391
+ edu = html.escape(edu)
392
+ education_html += f"""
393
+ <div class="card" data-aos="fade-up" data-aos-delay="{100 * i}">
394
+ <p class="text-gray-700 text-xs sm:text-sm">{edu}</p>
395
+ </div>
396
+ """
397
+ education_html = education_html if education else """
398
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
399
+ <p class="text-gray-700 text-xs sm:text-sm">Not provided</p>
400
+ </div>
401
+ """
402
 
403
  # Extract experience
404
+ experience_keywords = ["intern", "software", "research", "engineer", "developer", "mentor", "guide"]
405
  experience = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in experience_keywords)]
406
+ experience_html = ""
407
+ for i, exp in enumerate(experience, 1):
408
+ exp = html.escape(exp)
409
+ experience_html += f"""
410
+ <div class="card" data-aos="fade-up" data-aos-delay="{100 * i}">
411
+ <p class="text-gray-700 text-xs sm:text-sm">{exp}</p>
412
+ </div>
413
+ """
414
+ experience_html = experience_html if experience else """
415
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
416
+ <p class="text-gray-700 text-xs sm:text-sm">Not provided</p>
417
+ </div>
418
+ """
419
 
420
  # Extract projects
421
+ project_keywords = ["project", "developed", "built", "implemented"]
422
  projects = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in project_keywords)]
423
+ projects_html = ""
424
+ for i, proj in enumerate(projects, 1):
425
+ proj = html.escape(proj)
426
+ projects_html += f"""
427
+ <div class="card" data-aos="fade-up" data-aos-delay="{100 * i}">
428
+ <p class="text-gray-700 text-xs sm:text-sm">{proj}</p>
429
+ </div>
430
+ """
431
+ projects_html = projects_html if projects else """
432
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
433
+ <p class="text-gray-700 text-xs sm:text-sm">Not provided</p>
434
+ </div>
435
+ """
436
 
437
  # Extract skills
438
  skills_line = next((line for line in lines if "skills" in line.lower()), "")
439
+ known_skills = ["python", "javascript", "nlp", "cnns", "aws", "flask", "fastapi", "sql", "docker", "kubernetes", "teamwork", "communication", "c++", "matlab", "html/css"]
440
  skills = []
441
  if skills_line:
442
  for skill in known_skills:
 
446
  for skill in trending_skills:
447
  if skill.lower() not in [s.lower() for s in skills]:
448
  skills.append(skill)
449
+ skills_html = ""
450
+ for i, skill in enumerate(skills, 1):
451
+ skill = html.escape(skill.title())
452
+ skills_html += f"""
453
+ <div class="card" data-aos="fade-up" data-aos-delay="{100 * i}">
454
+ <h3 class="text-xs sm:text-sm lg:text-base font-semibold text-gray-800">{skill}</h3>
455
+ <div class="skill-bar"><div class="skill-progress" style="width: 0%;" data-progress="{80 + (i % 20)}"></div></div>
456
+ </div>
457
+ """
458
+ skills_html = skills_html if skills else """
459
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
460
+ <p class="text-gray-700 text-xs sm:text-sm">Not provided</p>
461
+ </div>
462
+ """
463
+ skills_summary = ", ".join(skills[:3]) if skills else "IT Professional"
464
+
465
+ # Extract involvement
466
+ involvement_keywords = ["co-captain", "volunteer", "participant", "member"]
467
+ involvement = [line.strip() for line in lines if any(keyword.lower() in line.lower() for keyword in involvement_keywords)]
468
+ involvement_html = ""
469
+ for i, inv in enumerate(involvement, 1):
470
+ inv = html.escape(inv)
471
+ involvement_html += f"""
472
+ <div class="card" data-aos="fade-up" data-aos-delay="{100 * i}">
473
+ <p class="text-gray-700 text-xs sm:text-sm">{inv}</p>
474
+ </div>
475
+ """
476
+ involvement_html = involvement_html if involvement else """
477
+ <div class="card" data-aos="fade-up" data-aos-delay="100">
478
+ <p class="text-gray-700 text-xs sm:text-sm">Not provided</p>
479
+ </div>
480
+ """
481
 
482
  logger.info("Successfully extracted details from resume.")
483
+ return name, email, phone, education_html, experience_html, projects_html, skills_html, involvement_html, skills_summary, skills
484
  except Exception as e:
485
  logger.error(f"Error extracting details: {str(e)}")
486
+ return "[Your Name]", "your.email@example.com", "555-555-5555", f"Error: {str(e)}", f"Error: {str(e)}", f"Error: {str(e)}", f"Error: {str(e)}", f"Error: {str(e)}", "IT Professional", []
487
 
488
  def generate_portfolio(file, theme="Light"):
489
  try:
 
506
  return resume_text, resume_text
507
 
508
  # Extract details from the resume
509
+ name, email, phone, education_html, experience_html, projects_html, skills_html, involvement_html, skills_summary, skills = extract_details(resume_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
510
 
511
  # Generate the portfolio HTML
512
  html_code = HTML_TEMPLATE.format(
 
517
  EXPERIENCE=experience_html,
518
  PROJECTS=projects_html,
519
  SKILLS=skills_html,
520
+ INVOLVEMENT=involvement_html,
521
+ SKILLS_SUMMARY=skills_summary
 
 
 
522
  )
523
 
524
  # Save to index.html
525
  with open("index.html", "w", encoding="utf-8") as f:
526
  f.write(html_code)
527
 
528
+ # Preview HTML for Gradio (simplified, as Gradio can't render full animations)
529
  html_preview = f"""
530
+ <div style='font-family: Arial; max-width: 800px; margin: auto; padding: 20px; background: #fff; box-shadow: 0 0 10px rgba(0,0,0,0.1);'>
531
  <h1 style='text-align: center;'>{name}</h1>
532
+ <p style='text-align: center;'>{skills_summary}</p>
533
  <h2>Education</h2>
534
  {education_html}
535
  <h2>Work Experience</h2>
 
540
  <div style='display: flex; flex-wrap: wrap; gap: 10px;'>
541
  {skills_html}
542
  </div>
543
+ <h2>Involvement</h2>
544
+ {involvement_html}
545
+ <h2>Contact</h2>
546
+ <p>Email: {email} | Phone: {phone}</p>
547
  </div>
548
  """
549