-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SkillSync - Skill Gap Analysis for Career Growth and Course Recommend…
…ation (#432) ## Pull Request for PyVerse 💡 ### Requesting to submit a pull request to the PyVerse repository. --- #### Issue Title **Please enter the title of the issue related to your pull request.** *Enter the issue title here.* - SkillSync - Skill Gap Analysis for Career Growth and Course Recommendation - [x] I have provided the issue title. --- #### Info about the Related Issue **What's the goal of the project?** *Describe the aim of the project.* - The primary aim of SkillSync is to provide a comprehensive skill gap analysis tool that assists individuals in identifying their current skill sets relative to their career goals. By leveraging advanced analytics and machine learning techniques, the platform will recommend personalized learning paths and courses to bridge identified skill gaps, thereby fostering career growth and enhancing employability. - [x] I have described the aim of the project. --- #### Name **Please mention your name.** *Enter your name here.* - J B Mugundh - [x] I have provided my name. --- #### GitHub ID **Please mention your GitHub ID.** *Enter your GitHub ID here.* - https://github.com/J-B-Mugundh - [x] I have provided my GitHub ID. --- #### Email ID **Please mention your email ID for further communication.** *Enter your email ID here.* - mugundhjb@gmail.com - [x] I have provided my email ID. --- #### Identify Yourself **Mention in which program you are contributing (e.g., WoB, GSSOC, SSOC, SWOC).** *Enter your participant role here.* - GSSOC-EXTD - [x] I have mentioned my participant role. --- #### Closes **Enter the issue number that will be closed through this PR.** *Closes: #issue-number* - #287 - [x] I have provided the issue number. --- #### Describe the Add-ons or Changes You've Made **Give a clear description of what you have added or modified.** *Describe your changes here.* - Added "SkillSync - Skill Gap Analysis for Career Growth and Course Recommendation" which performs the following - Skill Gap Analysis: The platform utilizes algorithms to compare the user’s existing skills with industry standards and the skills required for specific roles. This analysis highlights gaps that need to be addressed for career progression. - Course Recommendation: Based on the identified skill gaps, SkillSync will recommend relevant courses, workshops, and online resources tailored to the user’s learning preferences. The recommendations will be sourced from accredited platforms and institutions. - Career Insights: The platform will provide insights into industry trends, job market demands, and emerging skills to help users make informed decisions about their career paths. - [x] I have described my changes. --- #### Type of Change **Select the type of change:** - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Code style update (formatting, local variables) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] This change requires a documentation update --- #### How Has This Been Tested? **Describe how your changes have been tested.** *Describe your testing process here.* - Manual Testing in local - [x] I have described my testing process. --- #### Checklist **Please confirm the following:** - [x] My code follows the guidelines of this project. - [x] I have performed a self-review of my own code. - [x] I have commented my code, particularly wherever it was hard to understand. - [x] I have made corresponding changes to the documentation. - [x] My changes generate no new warnings. - [x] I have added things that prove my fix is effective or that my feature works. - [x] Any dependent changes have been merged and published in downstream modules.
- Loading branch information
Showing
5 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
GOOGLE_API_KEY=<YOUR_GEMINI_API_KEY> | ||
PROXYCURL_API_KEY=<YOUR_PROXYCURL_API_KEY> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# **Skill Sync** | ||
|
||
### 🎯 **Goal** | ||
|
||
The primary goal of **Skill Sync** is to bridge the skill gap by analyzing an individual’s current skills against the demands of their career goals. The application helps users align their skillsets with market demands, suggesting relevant courses for upskilling and enabling seamless skill import from platforms like LinkedIn. | ||
|
||
### 🧵 **Dataset** | ||
|
||
**Skill Sync** does not use a pre-existing dataset. Instead, it gathers user-inputted skills or imports them from LinkedIn profiles to analyze and recommend relevant courses for skill development. The system also utilizes generative models to provide skill gap analysis and career insights. | ||
|
||
### 🧾 **Description** | ||
|
||
**Skill Sync** allows users to define their career goals and manually add skills or import them from LinkedIn. Using AI models, the app compares the user's current skill set with their desired career and suggests the best upskilling path. | ||
|
||
The system helps streamline skill development by providing course recommendations, career insights, and gap analysis, improving the user's chances of achieving their career objectives. | ||
|
||
### 🧮 **What I Had Done!** | ||
|
||
- Implemented a **manual skill addition feature** where users can input their skills. | ||
- Integrated **LinkedIn API** to allow users to import their skills automatically. | ||
- Developed a **career goal matching system**, leveraging **AI** for skill gap analysis. | ||
- Suggested **relevant courses** based on skill gaps identified during analysis. | ||
|
||
### 🚀 **Models Implemented** | ||
|
||
- **Generative AI**: This model generates career insights and a comprehensive skill gap analysis based on the user’s inputs. | ||
- **Gemini AI**: Used to map and identify skill deficiencies and recommend the best learning paths. | ||
|
||
### 📚 **Libraries Needed** | ||
|
||
- Flask | ||
- requests | ||
- google-generativeai | ||
- pandas | ||
- dotenv | ||
|
||
### 📊 **Exploratory Data Analysis Results** | ||
|
||
Since **Skill Sync** deals with real-time input from users, traditional data analysis is not performed. However, the app provides insights based on real-time user data like skills vs. market requirements, skill deficiencies, and suggested learning paths. | ||
|
||
### 📈 **Performance Metrics** | ||
|
||
The performance of the system is assessed through: | ||
- **Accuracy of skill analysis**: How accurately the system identifies missing skills. | ||
- **Relevance of recommended courses**: How relevant the suggested courses are to the user's career goals. | ||
- **User satisfaction**: Feedback collected on how effective the tool is in helping users bridge their skill gaps. | ||
|
||
### 💻 How to Run | ||
|
||
To get started with **Skill Sync**, follow these steps: | ||
|
||
1. Navigate to the project directory: | ||
|
||
```bash | ||
cd SkillSync | ||
``` | ||
|
||
2. (Optional) Activate a virtual environment: | ||
|
||
```bash | ||
conda create -n venv python=3.10+ | ||
conda activate venv | ||
``` | ||
|
||
3. Install dependencies: | ||
|
||
```bash | ||
pip install -r requirements.txt | ||
``` | ||
|
||
4. Configure environment variables: | ||
|
||
``` | ||
Rename `.env-sample` to `.env`. | ||
Replace with your LinkedIn and Google API Keys. | ||
``` | ||
|
||
Kindly refer to these links for getting your own API keys: | ||
- [LinkedIn API](https://developer.linkedin.com/) or [Proxy Curl API](https://nubela.co/proxycurl/linkedin) | ||
- [Google Generative AI Key](https://ai.google.dev/tutorials/setup) | ||
|
||
5. Run the application: | ||
|
||
```bash | ||
streamlit run app.py | ||
``` | ||
|
||
### 📢 **Conclusion** | ||
|
||
**Skill Sync** is an effective tool for closing the skill gap by analyzing current skillsets and recommending relevant courses. It leverages AI models to provide detailed gap analysis and offers career-oriented guidance, ensuring that users stay on track to achieve their professional goals. | ||
|
||
### ✒️ **Signature** | ||
|
||
**[J B Mugundh]** | ||
GitHub: [Github](https://github.com/J-B-Mugundh) | ||
LinkedIn: [LinkedIn](https://www.linkedin.com/in/mugundhjb/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
from flask import Flask, request, render_template | ||
import google.generativeai as genai | ||
import os | ||
import json | ||
import requests | ||
from dotenv import load_dotenv | ||
|
||
# Load environment variables from .env file | ||
load_dotenv() | ||
|
||
app = Flask(__name__) | ||
|
||
# Load API keys from environment variables | ||
PROXYCURL_API_KEY = os.getenv('PROXYCURL_API_KEY') | ||
PROXYCURL_API_ENDPOINT = 'https://nubela.co/proxycurl/api/v2/linkedin' | ||
|
||
def get_gemini_response(career_goal, skills): | ||
"""Fetches response from Gemini API based on career goal and skills.""" | ||
|
||
query = f"Considering my career goal of '{career_goal}', what additional skills would I need to acquire if my current skills are {', '.join(skills)}? Just list them as a list. The skills should be actual programming or technical skills. Just give them concise, don't give extra words like Version control (eg. Git). List a maximum of 5 skills only. Display each with bulletin point." | ||
|
||
model = genai.GenerativeModel('gemini-pro') | ||
api_key = os.getenv("GOOGLE_API_KEY") # Retrieve Google API key from .env | ||
genai.configure(api_key=api_key) | ||
|
||
try: | ||
response = model.generate_content(query) | ||
return response.text | ||
except Exception as e: | ||
print(f"Error occurred during Gemini API call: {e}") | ||
return "An error occurred while fetching data from Gemini. Please try again later." | ||
|
||
def get_linkedin_profile(linkedin_url): | ||
"""Fetches the LinkedIn profile using Proxycurl API.""" | ||
|
||
headers = { | ||
'Authorization': f'Bearer {PROXYCURL_API_KEY}', | ||
} | ||
|
||
params = { | ||
'linkedin_profile_url': linkedin_url, | ||
'extra': 'include', | ||
'skills': 'include', | ||
'use_cache': 'if-present', | ||
'fallback_to_cache': 'on-error', | ||
} | ||
|
||
try: | ||
response = requests.get(PROXYCURL_API_ENDPOINT, params=params, headers=headers) | ||
if response.status_code == 200: | ||
return response.json() # Return profile data as a dictionary | ||
else: | ||
print(f"Error fetching LinkedIn profile: {response.status_code}") | ||
return {"error": f"Error fetching profile, status code: {response.status_code}"} | ||
|
||
except Exception as e: | ||
print(f"Error occurred during LinkedIn API proxy call: {e}") | ||
return {"error": "An error occurred while fetching data from LinkedIn. Please try again later."} | ||
|
||
@app.route('/', methods=['GET', 'POST']) | ||
def index(): | ||
if request.method == 'POST': | ||
career_goal = request.form['careerGoal'] | ||
manual_skills = request.form.getlist('skill[]') | ||
linkedin_url = request.form.get('linkedinProfileUrl') # Changed to get LinkedIn profile URL | ||
profile_data = None | ||
|
||
if manual_skills or linkedin_url: | ||
if linkedin_url: | ||
profile_data = get_linkedin_profile(linkedin_url) | ||
if 'skills' in profile_data: | ||
skills_data = {"skills": profile_data['skills']} | ||
else: | ||
skills_data = {"skills": []} | ||
else: | ||
skills_data = {"skills": manual_skills} | ||
|
||
with open('skills.json', 'w') as json_file: | ||
json.dump(skills_data, json_file) | ||
|
||
if linkedin_url: | ||
profile_data = get_linkedin_profile(linkedin_url) | ||
elif manual_skills: | ||
profile_data = manual_skills | ||
|
||
if profile_data: | ||
if 'error' in profile_data: | ||
return render_template('index.html', error=profile_data['error']) | ||
gemini_response = get_gemini_response(career_goal, profile_data['skills'] if 'skills' in profile_data else manual_skills) | ||
return render_template('index.html', profile_data=profile_data, gemini_response=gemini_response) | ||
else: | ||
return render_template('index.html', error="Please enter your career goal and skills.") | ||
|
||
return render_template('index.html') | ||
|
||
if __name__ == '__main__': | ||
app.run(debug=True, use_reloader=False) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Flask | ||
requests | ||
google-generativeai | ||
pandas | ||
dotenv |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
<title>Skill Sync</title> | ||
<!-- Bootstrap CSS --> | ||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> | ||
<!-- FontAwesome Icons for a more polished UI --> | ||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> | ||
<style> | ||
body { | ||
background-color: #f8f9fa; | ||
} | ||
h1 { | ||
font-size: 2.5rem; | ||
text-align: center; | ||
margin-bottom: 40px; | ||
color: #007bff; | ||
} | ||
form { | ||
background-color: #fff; | ||
padding: 30px; | ||
border-radius: 8px; | ||
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); | ||
} | ||
.form-group label { | ||
font-weight: bold; | ||
} | ||
.form-check-label { | ||
font-size: 1rem; | ||
} | ||
.btn { | ||
background-color: #007bff; | ||
border-color: #007bff; | ||
color: white; | ||
font-size: 1.1rem; | ||
padding: 10px 20px; | ||
border-radius: 5px; | ||
} | ||
.btn:hover { | ||
background-color: #0056b3; | ||
border-color: #0056b3; | ||
} | ||
.analysis-section { | ||
text-align: center; | ||
margin-top: 50px; | ||
} | ||
.skill-list { | ||
list-style: none; | ||
padding: 0; | ||
text-align: left; | ||
} | ||
.skill-list li { | ||
padding: 10px 0; | ||
border-bottom: 1px solid #ddd; | ||
} | ||
.form-group .dynamic-skill { | ||
margin-top: 10px; | ||
} | ||
.btn-add-skill { | ||
background-color: #28a745; | ||
margin-left: 10px; | ||
} | ||
#manualSkillsInput input { | ||
margin-bottom: 10px; | ||
} | ||
.btn-proceed { | ||
margin-top: 20px; | ||
background-color: #28a745; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div class="container mt-5"> | ||
<h1><i class="fas fa-sync-alt"></i> Skill Sync</h1> | ||
<form action="/" method="post"> | ||
<div class="form-group"> | ||
<label for="careerGoal"><i class="fas fa-bullseye"></i> Career Goal:</label> | ||
<input type="text" class="form-control" id="careerGoal" name="careerGoal" placeholder="Enter your career goal"> | ||
</div> | ||
|
||
<div class="form-check mb-3"> | ||
<input type="checkbox" class="form-check-input" id="manualSkills" name="manualSkills" onchange="toggleSkillsInput()"> | ||
<label class="form-check-label" for="manualSkills"><i class="fas fa-pencil-alt"></i> Manually add skills</label> | ||
</div> | ||
|
||
<div id="manualSkillsInput" style="display:none;"> | ||
<div class="form-group"> | ||
<label for="skill">Skill:</label> | ||
<input type="text" class="form-control" id="skill" name="skill[]" placeholder="Enter a skill"> | ||
<button type="button" class="btn btn-add-skill mt-2" onclick="addSkill()"><i class="fas fa-plus-circle"></i> Add Skill</button> | ||
</div> | ||
</div> | ||
|
||
<div class="form-check mb-3"> | ||
<input type="checkbox" class="form-check-input" id="linkedinSkills" name="linkedinSkills" onchange="toggleLinkedinInput()"> | ||
<label class="form-check-label" for="linkedinSkills"><i class="fab fa-linkedin"></i> Import skills from LinkedIn</label> | ||
</div> | ||
|
||
<div id="linkedinInput" style="display:none;"> | ||
<div class="form-group"> | ||
<label for="linkedinUsername"><i class="fab fa-linkedin"></i> LinkedIn Username:</label> | ||
<input type="text" class="form-control" id="linkedinUsername" name="linkedinUsername" placeholder="Enter your LinkedIn username"> | ||
</div> | ||
</div> | ||
|
||
<button type="submit" class="btn btn-primary btn-block mt-4">Submit</button> | ||
</form> | ||
|
||
{% if profile_data %} | ||
<div class="analysis-section"> | ||
{% if gemini_response %} | ||
<h2>Skill Gap Analysis</h2> | ||
<p>{{ gemini_response }}</p> | ||
{% endif %} | ||
{% if courses %} | ||
<h2>Recommended Courses</h2> | ||
<ul class="skill-list"> | ||
{% for course in courses %} | ||
<li>{{ course }}</li> | ||
{% endfor %} | ||
</ul> | ||
<button class="btn btn-proceed btn-lg" onclick="gotoHome()">Proceed to EduConnect</button> | ||
{% endif %} | ||
</div> | ||
{% endif %} | ||
</div> | ||
|
||
<!-- Bootstrap JS and jQuery --> | ||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js"></script> | ||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> | ||
|
||
<script> | ||
function toggleSkillsInput() { | ||
var manualSkillsInput = document.getElementById("manualSkillsInput"); | ||
if (document.getElementById("manualSkills").checked) { | ||
manualSkillsInput.style.display = "block"; | ||
} else { | ||
manualSkillsInput.style.display = "none"; | ||
} | ||
} | ||
|
||
function toggleLinkedinInput() { | ||
var linkedinInput = document.getElementById("linkedinInput"); | ||
if (document.getElementById("linkedinSkills").checked) { | ||
linkedinInput.style.display = "block"; | ||
} else { | ||
linkedinInput.style.display = "none"; | ||
} | ||
} | ||
|
||
function addSkill() { | ||
var skillInput = document.createElement("input"); | ||
skillInput.setAttribute("type", "text"); | ||
skillInput.setAttribute("class", "form-control dynamic-skill"); | ||
skillInput.setAttribute("name", "skill[]"); | ||
|
||
var skill = document.getElementById("skill").value.trim(); | ||
if (skill !== "") { | ||
skillInput.value = skill; | ||
document.getElementById("skill").value = ""; | ||
document.getElementById("manualSkillsInput").appendChild(skillInput); | ||
} | ||
} | ||
|
||
function gotoHome(){ | ||
window.location.href = "http://localhost:5000"; | ||
} | ||
</script> | ||
</body> | ||
</html> |