danivpv's picture
chore: update project dependencies and add README and TODO files for better documentation and feature tracking
89d54b1
import os
import gradio as gr
from dotenv import load_dotenv
from app.constants import ROLE_PLACEHOLDER, SAMPLE_RESULT
from app.utils import copy_to_clipboard, export_state
from linkedinadvice.career_analysis import CareerAnalyzer
from linkedinadvice.monitoring import monitor_api
# Load environment variables
load_dotenv()
# Check if API key is set
if not os.getenv("OPENAI_API_KEY"):
raise ValueError("OPENAI_API_KEY not found in environment variables")
# Initialize career analyzer
analyzer = CareerAnalyzer(model_name="gpt-4o-mini")
@monitor_api
def analyze_career(
*args,
):
result = analyzer.analyze(*args)
return result
# Building the interface
with gr.Blocks(theme="soft") as demo:
# State for number of roles
role_count = gr.State(1) # Start with 1 role field
education_count = gr.State(1) # Start with 1 education field
professional_background_dict = gr.State({}) # State to store combined roles data
education_background_dict = gr.State({}) # State to store combined education data
professional_background = gr.State("") # State to store combined roles data
professional_achievements = gr.State("") # State to store combined roles data
education_background = gr.State("") # State to store combined education background
education_achievements = gr.State("") # State to store education achievements
goals = gr.State("") # State to store combined goals
insights = gr.State("") # State to store combined insights
time_preference = gr.State("Mid-term (10 years)") # State to store time preference
financial_weight = gr.State(2) # State to store financial weight
impact_weight = gr.State(2) # State to store impact weight
opportunity_weight = gr.State(2) # State to store opportunity weight
output = gr.State("") # State to store output
raw_list_inputs = gr.State([]) # State to store raw list inputs
gr.Markdown(
"""
# Career Pathway Analysis
This tool analyzes your professional background and provides tailored career path recommendations.
It evaluates options based on financial potential, human impact, and opportunity creation.
"""
)
with gr.Row():
submit_btn = gr.Button("Analyze Career Paths", variant="primary", size="lg")
clear_btn = gr.Button("Clear", variant="stop", size="lg")
example_btn = gr.Button("Load Example", variant="secondary", size="lg")
with gr.Row():
with gr.Column(scale=3):
with gr.Group():
gr.Markdown("### Professional Information")
gr.Markdown("*Add your current and previous professional roles*")
with gr.Row():
add_role_btn = gr.Button(
"βž• Add", variant="secondary", size="sm", key="add_role_btn"
)
remove_role_btn = gr.Button(
"βž– Remove",
variant="secondary",
size="sm",
key="remove_role_btn",
)
add_role_btn.click(
lambda x: x + 1,
inputs=[role_count],
outputs=[role_count],
)
remove_role_btn.click(
lambda x: max(x - 1, 1),
inputs=[role_count],
outputs=[role_count],
)
@gr.render(inputs=role_count)
def render_roles(r_count):
for i in range(r_count):
if i not in professional_background_dict.value:
professional_background_dict.value[i] = {
"role": "",
"exp": "",
"professional_achievement": "",
}
with gr.Row():
interactive = i == r_count - 1
role = gr.Textbox(
key=f"role_{i}",
label=f"Role {i + 1}"
+ (" (Current)" if i == 0 else ""),
placeholder=ROLE_PLACEHOLDER[i % len(ROLE_PLACEHOLDER)],
scale=3,
interactive=interactive,
inputs=professional_background_dict.value[i].get(
"role", ""
),
)
def update_professional_background_dict(key, value, i):
professional_background_dict.value[i][key] = value
final_string = ""
for i in professional_background_dict.value:
role = professional_background_dict.value[i].get(
"role"
)
exp = professional_background_dict.value[i].get(
"exp"
)
professional_achievement = (
professional_background_dict.value[i].get(
"professional_achievement"
)
)
final_string += f"{role} - {exp} years. Achieved: \n{professional_achievement}\n\n"
return professional_background_dict.value, final_string
role.input(
lambda x: update_professional_background_dict(
"role", x, i
),
inputs=[role],
outputs=[
professional_background_dict,
professional_background,
],
)
exp = gr.Number(
key=f"exp_{i}",
label="Years",
value=1,
minimum=0,
scale=1,
interactive=interactive,
)
exp.input(
lambda x: update_professional_background_dict(
"exp", x, i
),
inputs=[exp],
outputs=[
professional_background_dict,
professional_background,
],
)
professional_achievement = gr.Textbox(
key=f"professional_achievement_{i}",
label="Notable Achievements",
lines=2,
placeholder="List key accomplishments, awards, or significant contributions.",
interactive=interactive,
)
professional_achievement.input(
lambda x: update_professional_background_dict(
"professional_achievement", x, i
),
inputs=[professional_achievement],
outputs=[
professional_background_dict,
professional_background,
],
)
raw_list_inputs.value.extend(
[role, exp, professional_achievement]
)
with gr.Group():
gr.Markdown("### Educational Background")
gr.Markdown("*Add your current and previous academic experiences*")
with gr.Row():
add_education_btn = gr.Button(
"βž• Add",
variant="secondary",
size="sm",
key="add_education_btn",
)
remove_education_btn = gr.Button(
"βž– Remove",
variant="secondary",
size="sm",
key="remove_education_btn",
)
add_education_btn.click(
lambda x: x + 1,
inputs=[education_count],
outputs=[education_count],
)
remove_education_btn.click(
lambda x: max(x - 1, 1),
inputs=[education_count],
outputs=[education_count],
)
@gr.render(inputs=education_count)
def render_education(e_count):
for i in range(e_count):
if i not in education_background_dict.value:
education_background_dict.value[i] = {
"education": "",
"education_achievement": "",
}
with gr.Row():
education = gr.Textbox(
key=f"education_{i}",
label="Academic Experience",
lines=3,
placeholder="e.g. Bachelor of Science in Computer Science, University of Technology",
)
def update_educational_background_dict(key, value, i):
education_background_dict.value[i][key] = value
final_string = ""
for i in education_background_dict.value:
education = education_background_dict.value[i].get(
"education"
)
edu_achievement = education_background_dict.value[
i
].get("edu_achievement")
final_string += f"{education}. Achieved: \n{edu_achievement}\n\n"
return education_background_dict.value, final_string
education.input(
lambda x: update_educational_background_dict(
"education", x, i
),
inputs=[education],
outputs=[
education_background_dict,
education_background,
],
)
edu_achievement = gr.Textbox(
key=f"edu_achievement_{i}",
label="Academic Achievements",
lines=3,
placeholder="Awards, honors, notable projects or research during your education",
)
edu_achievement.input(
lambda x: update_educational_background_dict(
"education_achievement", x, i
),
inputs=[education_achievements],
outputs=[
education_background_dict,
education_background,
],
)
if education.value:
education_background.value += education.value
if edu_achievement.value:
education_achievements.value += edu_achievement.value
raw_list_inputs.value.extend([education, edu_achievement])
with gr.Group():
gr.Markdown("### Future Plans")
goals = gr.Textbox(
label="Career Goals",
lines=3,
placeholder="Describe your short-term and long-term career objectives",
key="goals",
)
insights = gr.Textbox(
label="Additional Insights",
lines=3,
placeholder="Other relevant information like skills, interests, or constraints",
key="insights",
)
with gr.Group():
gr.Markdown("### Analysis Preferences")
gr.Markdown(
"*Adjust the importance of each factor in career evaluation*"
)
time_preference = gr.Radio(
[
"Short-term (3 years)",
"Mid-term (10 years)",
"Long-term (10+ years)",
],
label="Time Horizon Preference",
value="Mid-term (10 years)",
key="time_preference",
)
with gr.Row():
with gr.Column(scale=1):
financial_weight = gr.Radio(
[1, 2, 3],
value=2,
label="πŸ’° Financial Potential",
info="Earning potential and financial growth",
elem_classes=["weight-radio"],
key="financial_weight",
)
with gr.Column(scale=1):
impact_weight = gr.Radio(
[1, 2, 3],
value=2,
label="πŸ’« Human Impact",
info="Positive impact on others and society",
elem_classes=["weight-radio"],
key="impact_weight",
)
with gr.Column(scale=1):
opportunity_weight = gr.Radio(
[1, 2, 3],
value=2,
label="πŸšͺ Opportunity Creation",
info="Doors opened for future growth and options",
elem_classes=["weight-radio"],
key="opportunity_weight",
)
with gr.Column(scale=2):
output_box = gr.Markdown(
output.value,
container=True,
height=614,
show_copy_button=True,
key="output_box",
)
with gr.Row():
copy_btn = gr.Button("πŸ“‹ Copy to Clipboard", variant="secondary")
share_btn = gr.Button("πŸ”— Share on LinkedIn", variant="secondary")
# Copy and share functionality
copy_btn.click(
copy_to_clipboard,
inputs=output_box,
)
# Add info section at the bottom
with gr.Accordion("About This Tool", open=False):
gr.Markdown(
"""
This career analysis tool uses AI to generate personalized career path recommendations
based on your professional background, education, and goals.
**How It Works:**
1. Enter your current and previous roles with years of experience
2. Provide information about your achievements and education
3. Set your preferences for analysis (time horizon and factor weights)
4. Get a detailed analysis of potential career paths with scoring
The analysis evaluates each career path on three dimensions:
- **Financial Potential**: Earning capacity and financial growth
- **Human Impact**: Contribution to society and positive influence
- **Opportunity Creation**: Future opportunities and career flexibility
You can adjust the weights to prioritize what matters most to you.
"""
)
submit_btn.click(
lambda *args: analyze_career(*args) if export_state(*args) or True else None,
inputs=[
professional_background,
education_background,
goals,
insights,
time_preference,
financial_weight,
impact_weight,
opportunity_weight,
],
outputs=[output_box],
)
clear_btn.click(
fn=lambda: None, inputs=[], outputs=[], js="() => location.reload()"
)
example_btn.click(
fn=lambda _: SAMPLE_RESULT,
inputs=[output_box],
outputs=[output_box],
)
# Launch the app
if __name__ == "__main__":
demo.launch()