diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,5 +1,3 @@ -# app.py - import streamlit as st from streamlit_option_menu import option_menu from langchain_groq import ChatGroq @@ -14,9 +12,9 @@ from datetime import datetime, timedelta from streamlit_chat import message import streamlit_authenticator as stauth import yaml +import json import os -# Ensure API keys are stored securely using secrets.toml GROQ_API_KEY = st.secrets["GROQ_API_KEY"] RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"] @@ -27,1150 +25,1180 @@ llm = ChatGroq( ) def load_authentication(): + """ + Loads and parses authentication configurations from Streamlit secrets. + + Returns: + dict: Parsed authentication configurations with 'credentials' and 'cookie' as dictionaries. + """ # Load authentication config from secrets.toml config = st.secrets["auth"] + + # Parse credentials from YAML string to dict + try: + config['credentials'] = yaml.safe_load(config['credentials']) + except yaml.YAMLError as e: + st.error(f"Error parsing credentials: {e}") + return {} + + # Parse cookie from JSON string to dict + try: + config['cookie'] = json.loads(config['cookie']) + except json.JSONDecodeError as e: + st.error(f"Error parsing cookie configuration: {e}") + return {} + return config config = load_authentication() -authenticator = stauth.Authenticate( - config['credentials'], - config['cookie']['name'], - config['cookie']['key'], - config['cookie']['expiry_days'] -) - -name, authentication_status, username = authenticator.login('Login', 'main') - -if authentication_status: - authenticator.logout('Logout', 'sidebar') - st.sidebar.success(f'Welcome *{name}*') - - - @st.cache_data(ttl=3600) - def extract_text_from_pdf(pdf_file): - """ - Extracts text from an uploaded PDF file. - """ - text = "" - try: - with fitz.open(stream=pdf_file.read(), filetype="pdf") as doc: - for page in doc: - text += page.get_text() - return text - except Exception as e: - st.error(f"Error extracting text from PDF: {e}") - return "" - - @st.cache_data(ttl=3600) - def extract_job_description(job_link): - """ - Fetches and extracts job description text from a given URL. - """ - try: +if config: + authenticator = stauth.Authenticate( + config['credentials'], + config['cookie']['name'], + config['cookie']['key'], + config['cookie']['expiry_days'] + ) + + name, authentication_status, username = authenticator.login('Login', 'main') + + if authentication_status: + authenticator.logout('Logout', 'sidebar') + st.sidebar.success(f'Welcome *{name}*') + + # Proceed with the rest of the app + # ------------------------------- + # Define Helper Functions with Caching + # ------------------------------- + + @st.cache_data(ttl=3600) + def extract_text_from_pdf(pdf_file): + """ + Extracts text from an uploaded PDF file. + """ + text = "" + try: + with fitz.open(stream=pdf_file.read(), filetype="pdf") as doc: + for page in doc: + text += page.get_text() + return text + except Exception as e: + st.error(f"Error extracting text from PDF: {e}") + return "" + + @st.cache_data(ttl=3600) + def extract_job_description(job_link): + """ + Fetches and extracts job description text from a given URL. + """ + try: + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" + } + response = requests.get(job_link, headers=headers) + response.raise_for_status() + soup = BeautifulSoup(response.text, 'html.parser') + # You might need to adjust the selectors based on the website's structure + job_description = soup.get_text(separator='\n') + return job_description.strip() + except Exception as e: + st.error(f"Error fetching job description: {e}") + return "" + + @st.cache_data(ttl=3600) + def extract_requirements(job_description): + """ + Uses Groq to extract job requirements from the job description. + """ + prompt = f""" + The following is a job description: + + {job_description} + + Extract the list of job requirements, qualifications, and skills from the job description. Provide them as a numbered list. + + Requirements: + """ + + try: + response = llm.invoke(prompt) + requirements = response.content.strip() + return requirements + except Exception as e: + st.error(f"Error extracting requirements: {e}") + return "" + + @st.cache_data(ttl=3600) + def generate_email(job_description, requirements, resume_text): + """ + Generates a personalized cold email using Groq based on the job description, requirements, and resume. + """ + prompt = f""" + You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Craft a concise and professional cold email to a potential employer based on the following information: + + **Job Description:** + {job_description} + + **Extracted Requirements:** + {requirements} + + **Your Resume:** + {resume_text} + + **Email Requirements:** + - **Introduction:** Briefly introduce yourself and mention the specific job you are applying for. + - **Body:** Highlight your relevant skills, projects, internships, and leadership experiences that align with the job requirements. + - **Value Proposition:** Explain how your fresh perspective and recent academic knowledge can add value to the company. + - **Closing:** Express enthusiasm for the opportunity, mention your willingness for an interview, and thank the recipient for their time. + + **Email:** + """ + + try: + response = llm.invoke(prompt) + email_text = response.content.strip() + return email_text + except Exception as e: + st.error(f"Error generating email: {e}") + return "" + + @st.cache_data(ttl=3600) + def generate_cover_letter(job_description, requirements, resume_text): + """ + Generates a personalized cover letter using Groq based on the job description, requirements, and resume. + """ + prompt = f""" + You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Compose a personalized and professional cover letter based on the following information: + + **Job Description:** + {job_description} + + **Extracted Requirements:** + {requirements} + + **Your Resume:** + {resume_text} + + **Cover Letter Requirements:** + 1. **Greeting:** Address the hiring manager by name if available; otherwise, use a generic greeting such as "Dear Hiring Manager." + 2. **Introduction:** Begin with an engaging opening that mentions the specific position you are applying for and conveys your enthusiasm. + 3. **Body:** + - **Skills and Experiences:** Highlight relevant technical skills, projects, internships, and leadership roles that align with the job requirements. + - **Alignment:** Demonstrate how your academic background and hands-on experiences make you a suitable candidate for the role. + 4. **Value Proposition:** Explain how your fresh perspective, recent academic knowledge, and eagerness to learn can contribute to the company's success. + 5. **Conclusion:** End with a strong closing statement expressing your interest in an interview, your availability, and gratitude for the hiring manager’s time and consideration. + 6. **Professional Tone:** Maintain a respectful and professional tone throughout the letter. + + **Cover Letter:** + """ + + try: + response = llm.invoke(prompt) + cover_letter = response.content.strip() + return cover_letter + except Exception as e: + st.error(f"Error generating cover letter: {e}") + return "" + + @st.cache_data(ttl=3600) + def extract_skills(text): + """ + Extracts a list of skills from the resume text using Groq. + """ + prompt = f""" + Extract a comprehensive list of technical and soft skills from the following resume text. Provide the skills as a comma-separated list. + + Resume Text: + {text} + + Skills: + """ + + try: + response = llm.invoke(prompt) + skills = response.content.strip() + # Clean and split the skills + skills_list = [skill.strip() for skill in re.split(',|\n', skills) if skill.strip()] + return skills_list + except Exception as e: + st.error(f"Error extracting skills: {e}") + return [] + + @st.cache_data(ttl=3600) + def suggest_keywords(resume_text, job_description=None): + """ + Suggests additional relevant keywords to enhance resume compatibility with ATS. + """ + prompt = f""" + Analyze the following resume text and suggest additional relevant keywords that can enhance its compatibility with Applicant Tracking Systems (ATS). If a job description is provided, tailor the keywords to align with the job requirements. + + Resume Text: + {resume_text} + + Job Description: + {job_description if job_description else "N/A"} + + Suggested Keywords: + """ + + try: + response = llm.invoke(prompt) + keywords = response.content.strip() + keywords_list = [keyword.strip() for keyword in re.split(',|\n', keywords) if keyword.strip()] + return keywords_list + except Exception as e: + st.error(f"Error suggesting keywords: {e}") + return [] + + @st.cache_data(ttl=3600) + def get_job_recommendations(job_title, location="India"): + """ + Fetches salary estimates using the Job Salary Data API based on the job title and location. + """ + url = "https://job-salary-data.p.rapidapi.com/job-salary" + querystring = { + "job_title": job_title.strip(), + "location": location.strip(), + "radius": "100" # Adjust radius as needed + } + headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)" + "x-rapidapi-key": RAPIDAPI_KEY, # Securely access the API key + "x-rapidapi-host": "job-salary-data.p.rapidapi.com" } - response = requests.get(job_link, headers=headers) - response.raise_for_status() - soup = BeautifulSoup(response.text, 'html.parser') - # You might need to adjust the selectors based on the website's structure - job_description = soup.get_text(separator='\n') - return job_description.strip() - except Exception as e: - st.error(f"Error fetching job description: {e}") - return "" - - @st.cache_data(ttl=3600) - def extract_requirements(job_description): - """ - Uses Groq to extract job requirements from the job description. - """ - prompt = f""" - The following is a job description: - - {job_description} - - Extract the list of job requirements, qualifications, and skills from the job description. Provide them as a numbered list. - - Requirements: - """ - - try: - response = llm.invoke(prompt) - requirements = response.content.strip() - return requirements - except Exception as e: - st.error(f"Error extracting requirements: {e}") - return "" - - @st.cache_data(ttl=3600) - def generate_email(job_description, requirements, resume_text): - """ - Generates a personalized cold email using Groq based on the job description, requirements, and resume. - """ - prompt = f""" - You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Craft a concise and professional cold email to a potential employer based on the following information: - - **Job Description:** - {job_description} - - **Extracted Requirements:** - {requirements} - - **Your Resume:** - {resume_text} - - **Email Requirements:** - - **Introduction:** Briefly introduce yourself and mention the specific job you are applying for. - - **Body:** Highlight your relevant skills, projects, internships, and leadership experiences that align with the job requirements. - - **Value Proposition:** Explain how your fresh perspective and recent academic knowledge can add value to the company. - - **Closing:** Express enthusiasm for the opportunity, mention your willingness for an interview, and thank the recipient for their time. - - **Email:** - """ - - try: - response = llm.invoke(prompt) - email_text = response.content.strip() - return email_text - except Exception as e: - st.error(f"Error generating email: {e}") - return "" - - @st.cache_data(ttl=3600) - def generate_cover_letter(job_description, requirements, resume_text): - """ - Generates a personalized cover letter using Groq based on the job description, requirements, and resume. - """ - prompt = f""" - You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Compose a personalized and professional cover letter based on the following information: - - **Job Description:** - {job_description} - - **Extracted Requirements:** - {requirements} - - **Your Resume:** - {resume_text} - - **Cover Letter Requirements:** - 1. **Greeting:** Address the hiring manager by name if available; otherwise, use a generic greeting such as "Dear Hiring Manager." - 2. **Introduction:** Begin with an engaging opening that mentions the specific position you are applying for and conveys your enthusiasm. - 3. **Body:** - - **Skills and Experiences:** Highlight relevant technical skills, projects, internships, and leadership roles that align with the job requirements. - - **Alignment:** Demonstrate how your academic background and hands-on experiences make you a suitable candidate for the role. - 4. **Value Proposition:** Explain how your fresh perspective, recent academic knowledge, and eagerness to learn can contribute to the company's success. - 5. **Conclusion:** End with a strong closing statement expressing your interest in an interview, your availability, and gratitude for the hiring manager’s time and consideration. - 6. **Professional Tone:** Maintain a respectful and professional tone throughout the letter. - - **Cover Letter:** - """ - - try: - response = llm.invoke(prompt) - cover_letter = response.content.strip() - return cover_letter - except Exception as e: - st.error(f"Error generating cover letter: {e}") - return "" - - @st.cache_data(ttl=3600) - def extract_skills(text): - """ - Extracts a list of skills from the resume text using Groq. - """ - prompt = f""" - Extract a comprehensive list of technical and soft skills from the following resume text. Provide the skills as a comma-separated list. - - Resume Text: - {text} - - Skills: - """ - - try: - response = llm.invoke(prompt) - skills = response.content.strip() - # Clean and split the skills - skills_list = [skill.strip() for skill in re.split(',|\n', skills) if skill.strip()] - return skills_list - except Exception as e: - st.error(f"Error extracting skills: {e}") - return [] - - @st.cache_data(ttl=3600) - def suggest_keywords(resume_text, job_description=None): - """ - Suggests additional relevant keywords to enhance resume compatibility with ATS. - """ - prompt = f""" - Analyze the following resume text and suggest additional relevant keywords that can enhance its compatibility with Applicant Tracking Systems (ATS). If a job description is provided, tailor the keywords to align with the job requirements. - - Resume Text: - {resume_text} - - Job Description: - {job_description if job_description else "N/A"} - - Suggested Keywords: - """ - - try: - response = llm.invoke(prompt) - keywords = response.content.strip() - keywords_list = [keyword.strip() for keyword in re.split(',|\n', keywords) if keyword.strip()] - return keywords_list - except Exception as e: - st.error(f"Error suggesting keywords: {e}") - return [] - - @st.cache_data(ttl=3600) - def get_job_recommendations(job_title, location="India"): - """ - Fetches salary estimates using the Job Salary Data API based on the job title and location. - """ - url = "https://job-salary-data.p.rapidapi.com/job-salary" - querystring = { - "job_title": job_title.strip(), - "location": location.strip(), - "radius": "100" # Adjust radius as needed - } - - headers = { - "x-rapidapi-key": RAPIDAPI_KEY, # Securely access the API key - "x-rapidapi-host": "job-salary-data.p.rapidapi.com" - } - - try: - response = requests.get(url, headers=headers, params=querystring) - response.raise_for_status() - salary_data = response.json() - - # Adjust the keys based on the API's response structure - min_salary = salary_data.get("min_salary") - avg_salary = salary_data.get("avg_salary") - max_salary = salary_data.get("max_salary") - - if not all([min_salary, avg_salary, max_salary]): - st.error("Incomplete salary data received from the API.") + + try: + response = requests.get(url, headers=headers, params=querystring) + response.raise_for_status() + salary_data = response.json() + + # Adjust the keys based on the API's response structure + min_salary = salary_data.get("min_salary") + avg_salary = salary_data.get("avg_salary") + max_salary = salary_data.get("max_salary") + + if not all([min_salary, avg_salary, max_salary]): + st.error("Incomplete salary data received from the API.") + return {} + + return { + "min_salary": min_salary, + "avg_salary": avg_salary, + "max_salary": max_salary + } + except requests.exceptions.HTTPError as http_err: + st.error(f"HTTP error occurred: {http_err}") return {} - - return { - "min_salary": min_salary, - "avg_salary": avg_salary, - "max_salary": max_salary - } - except requests.exceptions.HTTPError as http_err: - st.error(f"HTTP error occurred: {http_err}") - return {} - except Exception as err: - st.error(f"An error occurred: {err}") - return {} - - def create_skill_distribution_chart(skills): - """ - Creates a bar chart showing the distribution of skills. - """ - skill_counts = {} - for skill in skills: - skill_counts[skill] = skill_counts.get(skill, 0) + 1 - df = pd.DataFrame(list(skill_counts.items()), columns=['Skill', 'Count']) - fig = px.bar(df, x='Skill', y='Count', title='Skill Distribution') - return fig - - def create_experience_timeline(resume_text): - """ - Creates an experience timeline from the resume text. - """ - # Extract work experience details using Groq - prompt = f""" - From the following resume text, extract the job titles, companies, and durations of employment. Provide the information in a table format with columns: Job Title, Company, Duration (in years). - - Resume Text: - {resume_text} - - Table: - """ - - try: - response = llm.invoke(prompt) - table_text = response.content.strip() - # Parse the table_text to create a DataFrame - data = [] - for line in table_text.split('\n'): - if line.strip() and not line.lower().startswith("job title"): - parts = line.split('|') - if len(parts) == 3: - job_title = parts[0].strip() - company = parts[1].strip() - duration = parts[2].strip() - # Convert duration to a float representing years - duration_years = parse_duration(duration) - data.append({"Job Title": job_title, "Company": company, "Duration (years)": duration_years}) - df = pd.DataFrame(data) - if not df.empty: - # Create a cumulative duration for timeline - df['Start Year'] = df['Duration (years)'].cumsum() - df['Duration (years)'] - df['End Year'] = df['Duration (years)'].cumsum() - fig = px.timeline(df, x_start="Start Year", x_end="End Year", y="Job Title", color="Company", title="Experience Timeline") - fig.update_yaxes(categoryorder="total ascending") - return fig - else: + except Exception as err: + st.error(f"An error occurred: {err}") + return {} + + def create_skill_distribution_chart(skills): + """ + Creates a bar chart showing the distribution of skills. + """ + skill_counts = {} + for skill in skills: + skill_counts[skill] = skill_counts.get(skill, 0) + 1 + df = pd.DataFrame(list(skill_counts.items()), columns=['Skill', 'Count']) + fig = px.bar(df, x='Skill', y='Count', title='Skill Distribution') + return fig + + def create_experience_timeline(resume_text): + """ + Creates an experience timeline from the resume text. + """ + # Extract work experience details using Groq + prompt = f""" + From the following resume text, extract the job titles, companies, and durations of employment. Provide the information in a table format with columns: Job Title, Company, Duration (in years). + + Resume Text: + {resume_text} + + Table: + """ + + try: + response = llm.invoke(prompt) + table_text = response.content.strip() + # Parse the table_text to create a DataFrame + data = [] + for line in table_text.split('\n'): + if line.strip() and not line.lower().startswith("job title"): + parts = line.split('|') + if len(parts) == 3: + job_title = parts[0].strip() + company = parts[1].strip() + duration = parts[2].strip() + # Convert duration to a float representing years + duration_years = parse_duration(duration) + data.append({"Job Title": job_title, "Company": company, "Duration (years)": duration_years}) + df = pd.DataFrame(data) + if not df.empty: + # Create a cumulative duration for timeline + df['Start Year'] = df['Duration (years)'].cumsum() - df['Duration (years)'] + df['End Year'] = df['Duration (years)'].cumsum() + fig = px.timeline(df, x_start="Start Year", x_end="End Year", y="Job Title", color="Company", title="Experience Timeline") + fig.update_yaxes(categoryorder="total ascending") + return fig + else: + return None + except Exception as e: + st.error(f"Error creating experience timeline: {e}") return None - except Exception as e: - st.error(f"Error creating experience timeline: {e}") - return None - - def parse_duration(duration_str): - """ - Parses duration strings like '2 years' or '6 months' into float years. - """ - try: - if 'year' in duration_str.lower(): - years = float(re.findall(r'\d+\.?\d*', duration_str)[0]) - return years - elif 'month' in duration_str.lower(): - months = float(re.findall(r'\d+\.?\d*', duration_str)[0]) - return months / 12 - else: + + def parse_duration(duration_str): + """ + Parses duration strings like '2 years' or '6 months' into float years. + """ + try: + if 'year' in duration_str.lower(): + years = float(re.findall(r'\d+\.?\d*', duration_str)[0]) + return years + elif 'month' in duration_str.lower(): + months = float(re.findall(r'\d+\.?\d*', duration_str)[0]) + return months / 12 + else: + return 0 + except: return 0 - except: - return 0 - - # ------------------------------- - # Database Functions - # ------------------------------- - - def init_db(): - """ - Initializes the SQLite database for application tracking. - """ - conn = sqlite3.connect('applications.db') - c = conn.cursor() - c.execute(''' - CREATE TABLE IF NOT EXISTS applications ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - job_title TEXT, - company TEXT, - application_date TEXT, - status TEXT, - deadline TEXT, - notes TEXT, - job_description TEXT, - resume_text TEXT, - skills TEXT - ) - ''') - conn.commit() - conn.close() - - def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills): - """ - Adds a new application to the database. - """ - conn = sqlite3.connect('applications.db') - c = conn.cursor() - c.execute(''' - INSERT INTO applications (job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) - ''', (job_title, company, application_date, status, deadline, notes, job_description, resume_text, ', '.join(skills))) - conn.commit() - conn.close() - - def fetch_applications(): - """ - Fetches all applications from the database. - """ - conn = sqlite3.connect('applications.db') - c = conn.cursor() - c.execute('SELECT * FROM applications') - data = c.fetchall() - conn.close() - applications = [] - for app in data: - applications.append({ - "ID": app[0], - "Job Title": app[1], - "Company": app[2], - "Application Date": app[3], - "Status": app[4], - "Deadline": app[5], - "Notes": app[6], - "Job Description": app[7], - "Resume Text": app[8], - "Skills": app[9].split(', ') if app[9] else [] - }) - return applications - - def update_application_status(app_id, new_status): - """ - Updates the status of an application. - """ - conn = sqlite3.connect('applications.db') - c = conn.cursor() - c.execute('UPDATE applications SET status = ? WHERE id = ?', (new_status, app_id)) - conn.commit() - conn.close() - - def delete_application(app_id): - """ - Deletes an application from the database. - """ - conn = sqlite3.connect('applications.db') - c = conn.cursor() - c.execute('DELETE FROM applications WHERE id = ?', (app_id,)) - conn.commit() - conn.close() - - def generate_learning_path(career_goal, current_skills): - """ - Generates a personalized learning path using Groq based on career goal and current skills. - """ - prompt = f""" - Based on the following career goal and current skills, create a personalized learning path that includes recommended courses, projects, and milestones to achieve the career goal. - - **Career Goal:** - {career_goal} - - **Current Skills:** - {current_skills} - - **Learning Path:** - """ - - try: - response = llm.invoke(prompt) - learning_path = response.content.strip() - return learning_path - except Exception as e: - st.error(f"Error generating learning path: {e}") - return "" - - # ------------------------------- - # Page Functions - # ------------------------------- - - def email_generator_page(): - st.header("Automated Email Generator") - - st.write(""" - Generate personalized cold emails based on job postings and your resume. - """) - - # Create two columns for input fields - col1, col2 = st.columns(2) - with col1: - job_link = st.text_input("Enter the job link:") - with col2: - uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf") - - if st.button("Generate Email"): - if not job_link: - st.error("Please enter a job link.") - return - if not uploaded_file: - st.error("Please upload your resume.") - return - - with st.spinner("Processing..."): - # Extract job description - job_description = extract_job_description(job_link) - if not job_description: - st.error("Failed to extract job description.") - return - - # Extract requirements - requirements = extract_requirements(job_description) - if not requirements: - st.error("Failed to extract requirements.") + + # ------------------------------- + # Database Functions + # ------------------------------- + + def init_db(): + """ + Initializes the SQLite database for application tracking. + """ + conn = sqlite3.connect('applications.db') + c = conn.cursor() + c.execute(''' + CREATE TABLE IF NOT EXISTS applications ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + job_title TEXT, + company TEXT, + application_date TEXT, + status TEXT, + deadline TEXT, + notes TEXT, + job_description TEXT, + resume_text TEXT, + skills TEXT + ) + ''') + conn.commit() + conn.close() + + def add_application(job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills): + """ + Adds a new application to the database. + """ + conn = sqlite3.connect('applications.db') + c = conn.cursor() + c.execute(''' + INSERT INTO applications (job_title, company, application_date, status, deadline, notes, job_description, resume_text, skills) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ''', (job_title, company, application_date, status, deadline, notes, job_description, resume_text, ', '.join(skills))) + conn.commit() + conn.close() + + def fetch_applications(): + """ + Fetches all applications from the database. + """ + conn = sqlite3.connect('applications.db') + c = conn.cursor() + c.execute('SELECT * FROM applications') + data = c.fetchall() + conn.close() + applications = [] + for app in data: + applications.append({ + "ID": app[0], + "Job Title": app[1], + "Company": app[2], + "Application Date": app[3], + "Status": app[4], + "Deadline": app[5], + "Notes": app[6], + "Job Description": app[7], + "Resume Text": app[8], + "Skills": app[9].split(', ') if app[9] else [] + }) + return applications + + def update_application_status(app_id, new_status): + """ + Updates the status of an application. + """ + conn = sqlite3.connect('applications.db') + c = conn.cursor() + c.execute('UPDATE applications SET status = ? WHERE id = ?', (new_status, app_id)) + conn.commit() + conn.close() + + def delete_application(app_id): + """ + Deletes an application from the database. + """ + conn = sqlite3.connect('applications.db') + c = conn.cursor() + c.execute('DELETE FROM applications WHERE id = ?', (app_id,)) + conn.commit() + conn.close() + + def generate_learning_path(career_goal, current_skills): + """ + Generates a personalized learning path using Groq based on career goal and current skills. + """ + prompt = f""" + Based on the following career goal and current skills, create a personalized learning path that includes recommended courses, projects, and milestones to achieve the career goal. + + **Career Goal:** + {career_goal} + + **Current Skills:** + {current_skills} + + **Learning Path:** + """ + + try: + response = llm.invoke(prompt) + learning_path = response.content.strip() + return learning_path + except Exception as e: + st.error(f"Error generating learning path: {e}") + return "" + + # ------------------------------- + # Page Functions + # ------------------------------- + + def email_generator_page(): + st.header("Automated Email Generator") + + st.write(""" + Generate personalized cold emails based on job postings and your resume. + """) + + # Create two columns for input fields + col1, col2 = st.columns(2) + with col1: + job_link = st.text_input("Enter the job link:") + with col2: + uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf") + + if st.button("Generate Email"): + if not job_link: + st.error("Please enter a job link.") return - - # Extract resume text - resume_text = extract_text_from_pdf(uploaded_file) - if not resume_text: - st.error("Failed to extract text from resume.") + if not uploaded_file: + st.error("Please upload your resume.") return - - # Generate email - email_text = generate_email(job_description, requirements, resume_text) - if email_text: - st.subheader("Generated Email:") - st.write(email_text) - # Provide download option - st.download_button( - label="Download Email", - data=email_text, - file_name="generated_email.txt", - mime="text/plain" - ) - else: - st.error("Failed to generate email.") - - def cover_letter_generator_page(): - st.header("Automated Cover Letter Generator") - - st.write(""" - Generate personalized cover letters based on job postings and your resume. - """) - - # Create two columns for input fields - col1, col2 = st.columns(2) - with col1: - job_link = st.text_input("Enter the job link:") - with col2: - uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf") - - if st.button("Generate Cover Letter"): - if not job_link: - st.error("Please enter a job link.") - return - if not uploaded_file: - st.error("Please upload your resume.") - return - - with st.spinner("Processing..."): - # Extract job description - job_description = extract_job_description(job_link) - if not job_description: - st.error("Failed to extract job description.") + + with st.spinner("Processing..."): + # Extract job description + job_description = extract_job_description(job_link) + if not job_description: + st.error("Failed to extract job description.") + return + + # Extract requirements + requirements = extract_requirements(job_description) + if not requirements: + st.error("Failed to extract requirements.") + return + + # Extract resume text + resume_text = extract_text_from_pdf(uploaded_file) + if not resume_text: + st.error("Failed to extract text from resume.") + return + + # Generate email + email_text = generate_email(job_description, requirements, resume_text) + if email_text: + st.subheader("Generated Email:") + st.write(email_text) + # Provide download option + st.download_button( + label="Download Email", + data=email_text, + file_name="generated_email.txt", + mime="text/plain" + ) + else: + st.error("Failed to generate email.") + + def cover_letter_generator_page(): + st.header("Automated Cover Letter Generator") + + st.write(""" + Generate personalized cover letters based on job postings and your resume. + """) + + # Create two columns for input fields + col1, col2 = st.columns(2) + with col1: + job_link = st.text_input("Enter the job link:") + with col2: + uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf") + + if st.button("Generate Cover Letter"): + if not job_link: + st.error("Please enter a job link.") return - - # Extract requirements - requirements = extract_requirements(job_description) - if not requirements: - st.error("Failed to extract requirements.") + if not uploaded_file: + st.error("Please upload your resume.") return - - # Extract resume text + + with st.spinner("Processing..."): + # Extract job description + job_description = extract_job_description(job_link) + if not job_description: + st.error("Failed to extract job description.") + return + + # Extract requirements + requirements = extract_requirements(job_description) + if not requirements: + st.error("Failed to extract requirements.") + return + + # Extract resume text + resume_text = extract_text_from_pdf(uploaded_file) + if not resume_text: + st.error("Failed to extract text from resume.") + return + + # Generate cover letter + cover_letter = generate_cover_letter(job_description, requirements, resume_text) + if cover_letter: + st.subheader("Generated Cover Letter:") + st.write(cover_letter) + # Provide download option + st.download_button( + label="Download Cover Letter", + data=cover_letter, + file_name="generated_cover_letter.txt", + mime="text/plain" + ) + else: + st.error("Failed to generate cover letter.") + + def resume_analysis_page(): + st.header("Resume Analysis and Optimization") + + uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf") + + if uploaded_file: resume_text = extract_text_from_pdf(uploaded_file) - if not resume_text: - st.error("Failed to extract text from resume.") - return - - # Generate cover letter - cover_letter = generate_cover_letter(job_description, requirements, resume_text) - if cover_letter: - st.subheader("Generated Cover Letter:") - st.write(cover_letter) - # Provide download option - st.download_button( - label="Download Cover Letter", - data=cover_letter, - file_name="generated_cover_letter.txt", - mime="text/plain" - ) - else: - st.error("Failed to generate cover letter.") - - def resume_analysis_page(): - st.header("Resume Analysis and Optimization") - - uploaded_file = st.file_uploader("Upload your resume (PDF format):", type="pdf") - - if uploaded_file: - resume_text = extract_text_from_pdf(uploaded_file) - if resume_text: - st.success("Resume uploaded successfully!") - # Perform analysis - st.subheader("Extracted Information") - # Extracted skills - skills = extract_skills(resume_text) - st.write("**Skills:**", ', '.join(skills) if skills else "No skills extracted.") - # Extract keywords - keywords = suggest_keywords(resume_text) - st.write("**Suggested Keywords for ATS Optimization:**", ', '.join(keywords) if keywords else "No keywords suggested.") - # Provide optimization suggestions - st.subheader("Optimization Suggestions") - if keywords: - st.write("- **Keyword Optimization:** Incorporate the suggested keywords to improve ATS compatibility.") - else: - st.write("- **Keyword Optimization:** No keywords suggested.") - st.write("- **Formatting:** Ensure consistent formatting for headings and bullet points to enhance readability.") - st.write("- **Experience Details:** Provide specific achievements and quantify your accomplishments where possible.") - - # Visual Resume Analytics - st.subheader("Visual Resume Analytics") - # Skill Distribution Chart - if skills: - st.write("**Skill Distribution:**") - fig_skills = create_skill_distribution_chart(skills) - st.plotly_chart(fig_skills) - else: - st.write("**Skill Distribution:** No skills to display.") - - # Experience Timeline (if applicable) - fig_experience = create_experience_timeline(resume_text) - if fig_experience: - st.write("**Experience Timeline:**") - st.plotly_chart(fig_experience) + if resume_text: + st.success("Resume uploaded successfully!") + # Perform analysis + st.subheader("Extracted Information") + # Extracted skills + skills = extract_skills(resume_text) + st.write("**Skills:**", ', '.join(skills) if skills else "No skills extracted.") + # Extract keywords + keywords = suggest_keywords(resume_text) + st.write("**Suggested Keywords for ATS Optimization:**", ', '.join(keywords) if keywords else "No keywords suggested.") + # Provide optimization suggestions + st.subheader("Optimization Suggestions") + if keywords: + st.write("- **Keyword Optimization:** Incorporate the suggested keywords to improve ATS compatibility.") + else: + st.write("- **Keyword Optimization:** No keywords suggested.") + st.write("- **Formatting:** Ensure consistent formatting for headings and bullet points to enhance readability.") + st.write("- **Experience Details:** Provide specific achievements and quantify your accomplishments where possible.") + + # Visual Resume Analytics + st.subheader("Visual Resume Analytics") + # Skill Distribution Chart + if skills: + st.write("**Skill Distribution:**") + fig_skills = create_skill_distribution_chart(skills) + st.plotly_chart(fig_skills) + else: + st.write("**Skill Distribution:** No skills to display.") + + # Experience Timeline (if applicable) + fig_experience = create_experience_timeline(resume_text) + if fig_experience: + st.write("**Experience Timeline:**") + st.plotly_chart(fig_experience) + else: + st.write("**Experience Timeline:** Not enough data to generate a timeline.") + + # Save the resume and analysis to the database + if st.button("Save Resume Analysis"): + add_application( + job_title="N/A", + company="N/A", + application_date=datetime.now().strftime("%Y-%m-%d"), + status="N/A", + deadline="N/A", + notes="Resume Analysis", + job_description="N/A", + resume_text=resume_text, + skills=skills + ) + st.success("Resume analysis saved successfully!") else: - st.write("**Experience Timeline:** Not enough data to generate a timeline.") - - # Save the resume and analysis to the database - if st.button("Save Resume Analysis"): + st.error("Failed to extract text from resume.") + + def application_tracking_dashboard(): + st.header("Application Tracking Dashboard") + + # Initialize database + init_db() + + # Form to add a new application + st.subheader("Add New Application") + with st.form("add_application"): + job_title = st.text_input("Job Title") + company = st.text_input("Company") + application_date = st.date_input("Application Date", datetime.today()) + status = st.selectbox("Status", ["Applied", "Interviewing", "Offered", "Rejected"]) + deadline = st.date_input("Application Deadline", datetime.today() + timedelta(days=30)) + notes = st.text_area("Notes") + uploaded_file = st.file_uploader("Upload Job Description (PDF)", type="pdf") + uploaded_resume = st.file_uploader("Upload Resume (PDF)", type="pdf") + submitted = st.form_submit_button("Add Application") + if submitted: + if uploaded_file: + job_description = extract_text_from_pdf(uploaded_file) + else: + job_description = "" + if uploaded_resume: + resume_text = extract_text_from_pdf(uploaded_resume) + skills = extract_skills(resume_text) + else: + resume_text = "" + skills = [] add_application( - job_title="N/A", - company="N/A", - application_date=datetime.now().strftime("%Y-%m-%d"), - status="N/A", - deadline="N/A", - notes="Resume Analysis", - job_description="N/A", + job_title=job_title, + company=company, + application_date=application_date.strftime("%Y-%m-%d"), + status=status, + deadline=deadline.strftime("%Y-%m-%d"), + notes=notes, + job_description=job_description, resume_text=resume_text, skills=skills ) - st.success("Resume analysis saved successfully!") - else: - st.error("Failed to extract text from resume.") - - def application_tracking_dashboard(): - st.header("Application Tracking Dashboard") - - # Initialize database - init_db() - - # Form to add a new application - st.subheader("Add New Application") - with st.form("add_application"): - job_title = st.text_input("Job Title") - company = st.text_input("Company") - application_date = st.date_input("Application Date", datetime.today()) - status = st.selectbox("Status", ["Applied", "Interviewing", "Offered", "Rejected"]) - deadline = st.date_input("Application Deadline", datetime.today() + timedelta(days=30)) - notes = st.text_area("Notes") - uploaded_file = st.file_uploader("Upload Job Description (PDF)", type="pdf") - uploaded_resume = st.file_uploader("Upload Resume (PDF)", type="pdf") - submitted = st.form_submit_button("Add Application") - if submitted: - if uploaded_file: - job_description = extract_text_from_pdf(uploaded_file) - else: - job_description = "" - if uploaded_resume: - resume_text = extract_text_from_pdf(uploaded_resume) - skills = extract_skills(resume_text) - else: - resume_text = "" - skills = [] - add_application( - job_title=job_title, - company=company, - application_date=application_date.strftime("%Y-%m-%d"), - status=status, - deadline=deadline.strftime("%Y-%m-%d"), - notes=notes, - job_description=job_description, - resume_text=resume_text, - skills=skills + st.success("Application added successfully!") + + # Display applications + st.subheader("Your Applications") + applications = fetch_applications() + if applications: + df = pd.DataFrame(applications) + df = df.drop(columns=["Job Description", "Resume Text", "Skills"]) + st.dataframe(df) + + # Export Button + csv = df.to_csv(index=False).encode('utf-8') + st.download_button( + label="Download Applications as CSV", + data=csv, + file_name='applications.csv', + mime='text/csv', ) - st.success("Application added successfully!") - - # Display applications - st.subheader("Your Applications") - applications = fetch_applications() - if applications: - df = pd.DataFrame(applications) - df = df.drop(columns=["Job Description", "Resume Text", "Skills"]) - st.dataframe(df) - - # Export Button - csv = df.to_csv(index=False).encode('utf-8') - st.download_button( - label="Download Applications as CSV", - data=csv, - file_name='applications.csv', - mime='text/csv', - ) - - # Import Button - st.subheader("Import Applications") - uploaded_csv = st.file_uploader("Upload a CSV file", type="csv") - if uploaded_csv: - try: - imported_df = pd.read_csv(uploaded_csv) - # Validate required columns - required_columns = {"Job Title", "Company", "Application Date", "Status", "Deadline", "Notes"} - if not required_columns.issubset(imported_df.columns): - st.error("Uploaded CSV is missing required columns.") - else: - for index, row in imported_df.iterrows(): - job_title = row.get("Job Title", "N/A") - company = row.get("Company", "N/A") - application_date = row.get("Application Date", datetime.now().strftime("%Y-%m-%d")) - status = row.get("Status", "Applied") - deadline = row.get("Deadline", "") - notes = row.get("Notes", "") - job_description = row.get("Job Description", "") - resume_text = row.get("Resume Text", "") - skills = row.get("Skills", "").split(', ') if row.get("Skills") else [] - add_application( - job_title=job_title, - company=company, - application_date=application_date, - status=status, - deadline=deadline, - notes=notes, - job_description=job_description, - resume_text=resume_text, - skills=skills - ) - st.success("Applications imported successfully!") - except Exception as e: - st.error(f"Error importing applications: {e}") - - # Actions: Update Status or Delete - for app in applications: - with st.expander(f"{app['Job Title']} at {app['Company']}"): - st.write(f"**Application Date:** {app['Application Date']}") - st.write(f"**Deadline:** {app['Deadline']}") - st.write(f"**Status:** {app['Status']}") - st.write(f"**Notes:** {app['Notes']}") - if app['Job Description']: - st.write("**Job Description:**") - st.write(app['Job Description'][:500] + "...") - if app['Skills']: - st.write("**Skills:**", ', '.join(app['Skills'])) - # Update status - new_status = st.selectbox("Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}") - if st.button("Update Status", key=f"update_{app['ID']}"): - update_application_status(app['ID'], new_status) - st.success("Status updated successfully!") - # Delete application - if st.button("Delete Application", key=f"delete_{app['ID']}"): - delete_application(app['ID']) - st.success("Application deleted successfully!") - else: - st.write("No applications found.") - - def interview_preparation_module(): - st.header("Interview Preparation") - - st.write(""" - Prepare for your interviews with tailored mock questions and expert tips. - """) - - # Create two columns for input fields - col1, col2 = st.columns(2) - with col1: - job_title = st.text_input("Enter the job title you're applying for:") - with col2: - company = st.text_input("Enter the company name:") - - if st.button("Generate Mock Interview Questions"): - if not job_title or not company: - st.error("Please enter both job title and company name.") - return - with st.spinner("Generating questions..."): - prompt = f""" - Generate a list of 10 interview questions for a {job_title} position at {company}. Include a mix of technical and behavioral questions. - """ - try: - questions = llm.invoke(prompt).content.strip() - st.subheader("Mock Interview Questions:") - st.write(questions) - - # Optionally, provide sample answers or tips - if st.checkbox("Show Sample Answers"): - sample_prompt = f""" - Provide sample answers for the following interview questions for a {job_title} position at {company}. - - Questions: - {questions} - - Sample Answers: - """ - try: - sample_answers = llm.invoke(sample_prompt).content.strip() - st.subheader("Sample Answers:") - st.write(sample_answers) - except Exception as e: - st.error(f"Error generating sample answers: {e}") - except Exception as e: - st.error(f"Error generating interview questions: {e}") - - def personalized_learning_paths_module(): - st.header("Personalized Learning Paths") - - st.write(""" - Receive tailored learning plans to help you acquire the skills needed for your desired career. - """) - - # Create two columns for input fields - col1, col2 = st.columns(2) - with col1: - career_goal = st.text_input("Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):") - with col2: - current_skills = st.text_input("Enter your current skills (comma-separated):") - - if st.button("Generate Learning Path"): - if not career_goal or not current_skills: - st.error("Please enter both career goal and current skills.") - return - with st.spinner("Generating your personalized learning path..."): - learning_path = generate_learning_path(career_goal, current_skills) - if learning_path: - st.subheader("Your Personalized Learning Path:") - st.write(learning_path) - else: - st.error("Failed to generate learning path.") - - def networking_opportunities_module(): - st.header("Networking Opportunities") - - st.write(""" - Expand your professional network by connecting with relevant industry peers and joining professional groups. - """) - - # Create two columns for input fields - col1, col2 = st.columns(2) - with col1: - user_skills = st.text_input("Enter your key skills (comma-separated):") - with col2: - industry = st.text_input("Enter your industry (e.g., Technology, Finance):") - - if st.button("Find Networking Opportunities"): - if not user_skills or not industry: - st.error("Please enter both key skills and industry.") - return - with st.spinner("Fetching networking opportunities..."): - # Suggest LinkedIn groups or connections based on skills and industry - prompt = f""" - Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking. - """ - try: - suggestions = llm.invoke(prompt).content.strip() - st.subheader("Recommended Networking Groups and Events:") - st.write(suggestions) - except Exception as e: - st.error(f"Error fetching networking opportunities: {e}") - - def salary_estimation_module(): - st.header("Salary Estimation and Negotiation Tips") - - st.write(""" - Understand the salary expectations for your desired roles and learn effective negotiation strategies. - """) - - # Create two columns for input fields - col1, col2 = st.columns(2) - with col1: - job_title = st.text_input("Enter the job title:") - with col2: - location = st.text_input("Enter the location (e.g., New York, NY, USA):") - - if st.button("Get Salary Estimate"): - if not job_title or not location: - st.error("Please enter both job title and location.") - return - with st.spinner("Fetching salary data..."): - # Job Salary Data API Integration - salary_data = get_job_recommendations(job_title, location) - if salary_data: - min_salary = salary_data.get("min_salary") - avg_salary = salary_data.get("avg_salary") - max_salary = salary_data.get("max_salary") - - if min_salary and avg_salary and max_salary: - st.subheader("Salary Estimate:") - st.write(f"**Minimum Salary:** ${min_salary:,}") - st.write(f"**Average Salary:** ${avg_salary:,}") - st.write(f"**Maximum Salary:** ${max_salary:,}") - - # Visualization - salary_df = pd.DataFrame({ - "Salary Range": ["Minimum", "Average", "Maximum"], - "Amount": [min_salary, avg_salary, max_salary] - }) - - fig = px.bar(salary_df, x="Salary Range", y="Amount", - title=f"Salary Estimates for {job_title} in {location}", - labels={"Amount": "Salary (USD)"}, - text_auto=True) - st.plotly_chart(fig) + + # Import Button + st.subheader("Import Applications") + uploaded_csv = st.file_uploader("Upload a CSV file", type="csv") + if uploaded_csv: + try: + imported_df = pd.read_csv(uploaded_csv) + # Validate required columns + required_columns = {"Job Title", "Company", "Application Date", "Status", "Deadline", "Notes"} + if not required_columns.issubset(imported_df.columns): + st.error("Uploaded CSV is missing required columns.") + else: + for index, row in imported_df.iterrows(): + job_title = row.get("Job Title", "N/A") + company = row.get("Company", "N/A") + application_date = row.get("Application Date", datetime.now().strftime("%Y-%m-%d")) + status = row.get("Status", "Applied") + deadline = row.get("Deadline", "") + notes = row.get("Notes", "") + job_description = row.get("Job Description", "") + resume_text = row.get("Resume Text", "") + skills = row.get("Skills", "").split(', ') if row.get("Skills") else [] + add_application( + job_title=job_title, + company=company, + application_date=application_date, + status=status, + deadline=deadline, + notes=notes, + job_description=job_description, + resume_text=resume_text, + skills=skills + ) + st.success("Applications imported successfully!") + except Exception as e: + st.error(f"Error importing applications: {e}") + + # Actions: Update Status or Delete + for app in applications: + with st.expander(f"{app['Job Title']} at {app['Company']}"): + st.write(f"**Application Date:** {app['Application Date']}") + st.write(f"**Deadline:** {app['Deadline']}") + st.write(f"**Status:** {app['Status']}") + st.write(f"**Notes:** {app['Notes']}") + if app['Job Description']: + st.write("**Job Description:**") + st.write(app['Job Description'][:500] + "...") + if app['Skills']: + st.write("**Skills:**", ', '.join(app['Skills'])) + # Update status + new_status = st.selectbox("Update Status:", ["Applied", "Interviewing", "Offered", "Rejected"], key=f"status_{app['ID']}") + if st.button("Update Status", key=f"update_{app['ID']}"): + update_application_status(app['ID'], new_status) + st.success("Status updated successfully!") + # Delete application + if st.button("Delete Application", key=f"delete_{app['ID']}"): + delete_application(app['ID']) + st.success("Application deleted successfully!") + else: + st.write("No applications found.") + + def interview_preparation_module(): + st.header("Interview Preparation") + + st.write(""" + Prepare for your interviews with tailored mock questions and expert tips. + """) + + # Create two columns for input fields + col1, col2 = st.columns(2) + with col1: + job_title = st.text_input("Enter the job title you're applying for:") + with col2: + company = st.text_input("Enter the company name:") + + if st.button("Generate Mock Interview Questions"): + if not job_title or not company: + st.error("Please enter both job title and company name.") + return + with st.spinner("Generating questions..."): + prompt = f""" + Generate a list of 10 interview questions for a {job_title} position at {company}. Include a mix of technical and behavioral questions. + """ + try: + questions = llm.invoke(prompt).content.strip() + st.subheader("Mock Interview Questions:") + st.write(questions) + + # Optionally, provide sample answers or tips + if st.checkbox("Show Sample Answers"): + sample_prompt = f""" + Provide sample answers for the following interview questions for a {job_title} position at {company}. + + Questions: + {questions} + + Sample Answers: + """ + try: + sample_answers = llm.invoke(sample_prompt).content.strip() + st.subheader("Sample Answers:") + st.write(sample_answers) + except Exception as e: + st.error(f"Error generating sample answers: {e}") + except Exception as e: + st.error(f"Error generating interview questions: {e}") + + def personalized_learning_paths_module(): + st.header("Personalized Learning Paths") + + st.write(""" + Receive tailored learning plans to help you acquire the skills needed for your desired career. + """) + + # Create two columns for input fields + col1, col2 = st.columns(2) + with col1: + career_goal = st.text_input("Enter your career goal (e.g., Data Scientist, Machine Learning Engineer):") + with col2: + current_skills = st.text_input("Enter your current skills (comma-separated):") + + if st.button("Generate Learning Path"): + if not career_goal or not current_skills: + st.error("Please enter both career goal and current skills.") + return + with st.spinner("Generating your personalized learning path..."): + learning_path = generate_learning_path(career_goal, current_skills) + if learning_path: + st.subheader("Your Personalized Learning Path:") + st.write(learning_path) else: - st.error("Salary data not available for the provided job title and location.") - - # Generate negotiation tips using Groq - tips_prompt = f""" - Provide a list of 5 effective tips for negotiating a salary for a {job_title} position in {location}. + st.error("Failed to generate learning path.") + + def networking_opportunities_module(): + st.header("Networking Opportunities") + + st.write(""" + Expand your professional network by connecting with relevant industry peers and joining professional groups. + """) + + # Create two columns for input fields + col1, col2 = st.columns(2) + with col1: + user_skills = st.text_input("Enter your key skills (comma-separated):") + with col2: + industry = st.text_input("Enter your industry (e.g., Technology, Finance):") + + if st.button("Find Networking Opportunities"): + if not user_skills or not industry: + st.error("Please enter both key skills and industry.") + return + with st.spinner("Fetching networking opportunities..."): + # Suggest LinkedIn groups or connections based on skills and industry + prompt = f""" + Based on the following skills: {user_skills}, and industry: {industry}, suggest relevant LinkedIn groups, professional organizations, and industry events for networking. """ try: - tips = llm.invoke(tips_prompt).content.strip() - st.subheader("Negotiation Tips:") - st.write(tips) + suggestions = llm.invoke(prompt).content.strip() + st.subheader("Recommended Networking Groups and Events:") + st.write(suggestions) except Exception as e: - st.error(f"Error generating negotiation tips: {e}") - else: - st.error("Failed to retrieve salary data.") - - def feedback_and_improvement_module(): - st.header("Feedback and Continuous Improvement") - - st.write(""" - We value your feedback! Let us know how we can improve your experience. - """) - - with st.form("feedback_form"): - name = st.text_input("Your Name") - email = st.text_input("Your Email") - feedback_type = st.selectbox("Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"]) - feedback = st.text_area("Your Feedback") - submitted = st.form_submit_button("Submit") - - if submitted: - if not name or not email or not feedback: - st.error("Please fill in all the fields.") - else: - # Here you can implement logic to store feedback, e.g., in a database or send via email - # For demonstration, we'll print to the console - print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}") - st.success("Thank you for your feedback!") - - def gamification_module(): - st.header("Gamification and Achievements") - - st.write(""" - Stay motivated by earning badges and tracking your progress! - """) - - # Initialize database - init_db() - - # Example achievements - applications = fetch_applications() - num_apps = len(applications) - achievements = { - "First Application": num_apps >= 1, - "5 Applications": num_apps >= 5, - "10 Applications": num_apps >= 10, - "Resume Optimized": any(app['Skills'] for app in applications), - "Interview Scheduled": any(app['Status'] == 'Interviewing' for app in applications) - } - - for achievement, earned in achievements.items(): - if earned: - st.success(f"🎉 {achievement}") - else: - st.info(f"🔜 {achievement}") - - # Progress Bar - progress = min(num_apps / 10, 1.0) # Ensure progress is between 0.0 and 1.0 - st.write("**Overall Progress:**") - st.progress(progress) - st.write(f"{progress * 100:.0f}% complete") - - def resource_library_page(): - st.header("Resource Library") - - st.write(""" - Access a collection of templates and guides to enhance your job search. - """) - - resources = [ - { - "title": "Resume Template", - "description": "A professional resume template in DOCX format.", - "file": "./resume_template.docx" - }, - { - "title": "Cover Letter Template", - "description": "A customizable cover letter template.", - "file": "./cover_letter_template.docx" - }, - { - "title": "Job Application Checklist", - "description": "Ensure you have all the necessary steps covered during your job search.", - "file": "./application_checklist.pdf" - } - ] - - for resource in resources: - st.markdown(f"### {resource['title']}") - st.write(resource['description']) - try: - with open(resource['file'], "rb") as file: - btn = st.download_button( - label="Download", - data=file, - file_name=os.path.basename(resource['file']), - mime="application/octet-stream" - ) - except FileNotFoundError: - st.error(f"File {resource['file']} not found. Please ensure the file is in the correct directory.") - st.write("---") - - def success_stories_page(): - st.header("Success Stories") - - st.write(""" - Hear from our users who have successfully landed their dream jobs with our assistance! - """) - - # Example testimonials - testimonials = [ - { - "name": "Rahul Sharma", - "position": "Data Scientist at TechCorp", - "testimonial": "This app transformed my job search process. The resume analysis and personalized emails were game-changers!", - "image": "images/user1.jpg" # Replace with actual image paths - }, - { - "name": "Priya Mehta", - "position": "Machine Learning Engineer at InnovateX", - "testimonial": "The interview preparation module helped me ace my interviews with confidence. Highly recommended!", - "image": "images/user2.jpg" - } - ] - - for user in testimonials: - col1, col2 = st.columns([1, 3]) + st.error(f"Error fetching networking opportunities: {e}") + + def salary_estimation_module(): + st.header("Salary Estimation and Negotiation Tips") + + st.write(""" + Understand the salary expectations for your desired roles and learn effective negotiation strategies. + """) + + # Create two columns for input fields + col1, col2 = st.columns(2) with col1: - try: - st.image(user["image"], width=100) - except: - st.write("") + job_title = st.text_input("Enter the job title:") with col2: - st.write(f"**{user['name']}**") - st.write(f"*{user['position']}*") - st.write(f"\"{user['testimonial']}\"") - st.write("---") - - def help_page(): - st.header("Help & FAQ") - - with st.expander("How do I generate a cover letter?"): + location = st.text_input("Enter the location (e.g., New York, NY, USA):") + + if st.button("Get Salary Estimate"): + if not job_title or not location: + st.error("Please enter both job title and location.") + return + with st.spinner("Fetching salary data..."): + # Job Salary Data API Integration + salary_data = get_job_recommendations(job_title, location) + if salary_data: + min_salary = salary_data.get("min_salary") + avg_salary = salary_data.get("avg_salary") + max_salary = salary_data.get("max_salary") + + if min_salary and avg_salary and max_salary: + st.subheader("Salary Estimate:") + st.write(f"**Minimum Salary:** ${min_salary:,}") + st.write(f"**Average Salary:** ${avg_salary:,}") + st.write(f"**Maximum Salary:** ${max_salary:,}") + + # Visualization + salary_df = pd.DataFrame({ + "Salary Range": ["Minimum", "Average", "Maximum"], + "Amount": [min_salary, avg_salary, max_salary] + }) + + fig = px.bar(salary_df, x="Salary Range", y="Amount", + title=f"Salary Estimates for {job_title} in {location}", + labels={"Amount": "Salary (USD)"}, + text_auto=True) + st.plotly_chart(fig) + else: + st.error("Salary data not available for the provided job title and location.") + + # Generate negotiation tips using Groq + tips_prompt = f""" + Provide a list of 5 effective tips for negotiating a salary for a {job_title} position in {location}. + """ + try: + tips = llm.invoke(tips_prompt).content.strip() + st.subheader("Negotiation Tips:") + st.write(tips) + except Exception as e: + st.error(f"Error generating negotiation tips: {e}") + else: + st.error("Failed to retrieve salary data.") + + def feedback_and_improvement_module(): + st.header("Feedback and Continuous Improvement") + st.write(""" - To generate a cover letter, navigate to the **Cover Letter Generator** section, enter the job link, upload your resume, and click on **Generate Cover Letter**. + We value your feedback! Let us know how we can improve your experience. """) - - with st.expander("How do I track my applications?"): + + with st.form("feedback_form"): + name = st.text_input("Your Name") + email = st.text_input("Your Email") + feedback_type = st.selectbox("Type of Feedback", ["Bug Report", "Feature Request", "General Feedback"]) + feedback = st.text_area("Your Feedback") + submitted = st.form_submit_button("Submit") + + if submitted: + if not name or not email or not feedback: + st.error("Please fill in all the fields.") + else: + # Here you can implement logic to store feedback, e.g., in a database or send via email + # For demonstration, we'll print to the console + print(f"Feedback from {name} ({email}): {feedback_type} - {feedback}") + st.success("Thank you for your feedback!") + + def gamification_module(): + st.header("Gamification and Achievements") + st.write(""" - Use the **Application Tracking** dashboard to add new applications, update their status, and monitor deadlines. + Stay motivated by earning badges and tracking your progress! """) - - with st.expander("How can I optimize my resume?"): + + # Initialize database + init_db() + + # Example achievements + applications = fetch_applications() + num_apps = len(applications) + achievements = { + "First Application": num_apps >= 1, + "5 Applications": num_apps >= 5, + "10 Applications": num_apps >= 10, + "Resume Optimized": any(app['Skills'] for app in applications), + "Interview Scheduled": any(app['Status'] == 'Interviewing' for app in applications) + } + + for achievement, earned in achievements.items(): + if earned: + st.success(f"🎉 {achievement}") + else: + st.info(f"🔜 {achievement}") + + # Progress Bar + progress = min(num_apps / 10, 1.0) # Ensure progress is between 0.0 and 1.0 + st.write("**Overall Progress:**") + st.progress(progress) + st.write(f"{progress * 100:.0f}% complete") + + def resource_library_page(): + st.header("Resource Library") + st.write(""" - Upload your resume in the **Resume Analysis** section to extract skills and receive optimization suggestions. + Access a collection of templates and guides to enhance your job search. """) - - with st.expander("How do I import my applications?"): + + resources = [ + { + "title": "Resume Template", + "description": "A professional resume template in DOCX format.", + "file": "./resume_template.docx" + }, + { + "title": "Cover Letter Template", + "description": "A customizable cover letter template.", + "file": "./cover_letter_template.docx" + }, + { + "title": "Job Application Checklist", + "description": "Ensure you have all the necessary steps covered during your job search.", + "file": "./application_checklist.pdf" + } + ] + + for resource in resources: + st.markdown(f"### {resource['title']}") + st.write(resource['description']) + try: + with open(resource['file'], "rb") as file: + btn = st.download_button( + label="Download", + data=file, + file_name=os.path.basename(resource['file']), + mime="application/octet-stream" + ) + except FileNotFoundError: + st.error(f"File {resource['file']} not found. Please ensure the file is in the correct directory.") + st.write("---") + + def success_stories_page(): + st.header("Success Stories") + st.write(""" - In the **Application Tracking** dashboard, use the **Import Applications** section to upload a CSV file containing your applications. Ensure the CSV has the required columns. + Hear from our users who have successfully landed their dream jobs with our assistance! """) - - with st.expander("How do I provide feedback?"): + + # Example testimonials + testimonials = [ + { + "name": "Rahul Sharma", + "position": "Data Scientist at TechCorp", + "testimonial": "This app transformed my job search process. The resume analysis and personalized emails were game-changers!", + "image": "images/user1.jpg" # Replace with actual image paths + }, + { + "name": "Priya Mehta", + "position": "Machine Learning Engineer at InnovateX", + "testimonial": "The interview preparation module helped me ace my interviews with confidence. Highly recommended!", + "image": "images/user2.jpg" + } + ] + + for user in testimonials: + col1, col2 = st.columns([1, 3]) + with col1: + try: + st.image(user["image"], width=100) + except: + st.write("") + with col2: + st.write(f"**{user['name']}**") + st.write(f"*{user['position']}*") + st.write(f"\"{user['testimonial']}\"") + st.write("---") + + def help_page(): + st.header("Help & FAQ") + + with st.expander("How do I generate a cover letter?"): + st.write(""" + To generate a cover letter, navigate to the **Cover Letter Generator** section, enter the job link, upload your resume, and click on **Generate Cover Letter**. + """) + + with st.expander("How do I track my applications?"): + st.write(""" + Use the **Application Tracking** dashboard to add new applications, update their status, and monitor deadlines. + """) + + with st.expander("How can I optimize my resume?"): + st.write(""" + Upload your resume in the **Resume Analysis** section to extract skills and receive optimization suggestions. + """) + + with st.expander("How do I import my applications?"): + st.write(""" + In the **Application Tracking** dashboard, use the **Import Applications** section to upload a CSV file containing your applications. Ensure the CSV has the required columns. + """) + + with st.expander("How do I provide feedback?"): + st.write(""" + Navigate to the **Feedback and Continuous Improvement** section, fill out the form, and submit your feedback. + """) + + def chatbot_support_page(): + st.header("AI-Powered Chatbot Support") + st.write(""" - Navigate to the **Feedback and Continuous Improvement** section, fill out the form, and submit your feedback. + Have questions or need assistance? Chat with our AI-powered assistant! """) - - def chatbot_support_page(): - st.header("AI-Powered Chatbot Support") - - st.write(""" - Have questions or need assistance? Chat with our AI-powered assistant! - """) - - # Initialize session state for chatbot - if 'chat_history' not in st.session_state: - st.session_state['chat_history'] = [] - - # User input - user_input = st.text_input("You:", key="user_input") - - if st.button("Send"): - if user_input: - # Append user message to chat history - st.session_state['chat_history'].append({"message": user_input, "is_user": True}) - prompt = f""" - You are a helpful assistant for a Job Application Assistant app. Answer the user's query based on the following context: - - {user_input} + + # Initialize session state for chatbot + if 'chat_history' not in st.session_state: + st.session_state['chat_history'] = [] + + # User input + user_input = st.text_input("You:", key="user_input") + + if st.button("Send"): + if user_input: + # Append user message to chat history + st.session_state['chat_history'].append({"message": user_input, "is_user": True}) + prompt = f""" + You are a helpful assistant for a Job Application Assistant app. Answer the user's query based on the following context: + + {user_input} + """ + try: + # Invoke the LLM to get a response + response = llm.invoke(prompt).content.strip() + # Append assistant response to chat history + st.session_state['chat_history'].append({"message": response, "is_user": False}) + except Exception as e: + error_message = "Sorry, I encountered an error while processing your request." + st.session_state['chat_history'].append({"message": error_message, "is_user": False}) + st.error(f"Error in chatbot: {e}") + + # Display chat history using streamlit-chat + for chat in st.session_state['chat_history']: + if chat['is_user']: + message(chat['message'], is_user=True, avatar_style="thumbs") + else: + message(chat['message'], is_user=False, avatar_style="bottts") + + # ------------------------------- + # Main App Function + # ------------------------------- + + def main_app(): + # Apply a consistent theme or style + st.markdown( """ - try: - # Invoke the LLM to get a response - response = llm.invoke(prompt).content.strip() - # Append assistant response to chat history - st.session_state['chat_history'].append({"message": response, "is_user": False}) - except Exception as e: - error_message = "Sorry, I encountered an error while processing your request." - st.session_state['chat_history'].append({"message": error_message, "is_user": False}) - st.error(f"Error in chatbot: {e}") - - # Display chat history using streamlit-chat - for chat in st.session_state['chat_history']: - if chat['is_user']: - message(chat['message'], is_user=True, avatar_style="thumbs") - else: - message(chat['message'], is_user=False, avatar_style="bottts") - - - def main_app(): - # Apply a consistent theme or style - st.markdown( - """ - <style> - .reportview-container { - background-color: #f5f5f5; - } - .sidebar .sidebar-content { - background-image: linear-gradient(#2e7bcf, #2e7bcf); - color: white; - } - </style> - """, - unsafe_allow_html=True - ) - - # Sidebar Navigation - with st.sidebar: - selected = option_menu( - "Main Menu", - ["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking", - "Interview Preparation", "Personalized Learning Paths", "Networking Opportunities", - "Salary Estimation", "Feedback", "Gamification", "Resource Library", "Success Stories", "Chatbot Support", "Help"], - icons=["envelope", "file-earmark-text", "file-person", "briefcase", "gear", - "book", "people", "currency-dollar", "chat-left-text", "trophy", "collection", "star", "chat", "question-circle"], - menu_icon="cast", - default_index=0, + <style> + .reportview-container { + background-color: #f5f5f5; + } + .sidebar .sidebar-content { + background-image: linear-gradient(#2e7bcf, #2e7bcf); + color: white; + } + </style> + """, + unsafe_allow_html=True ) - - # Route to the selected page - if selected == "Email Generator": - email_generator_page() - elif selected == "Cover Letter Generator": - cover_letter_generator_page() - elif selected == "Resume Analysis": - resume_analysis_page() - elif selected == "Application Tracking": - application_tracking_dashboard() - elif selected == "Interview Preparation": - interview_preparation_module() - elif selected == "Personalized Learning Paths": - personalized_learning_paths_module() - elif selected == "Networking Opportunities": - networking_opportunities_module() - elif selected == "Salary Estimation": - salary_estimation_module() - elif selected == "Feedback": - feedback_and_improvement_module() - elif selected == "Gamification": - gamification_module() - elif selected == "Resource Library": - resource_library_page() - elif selected == "Success Stories": - success_stories_page() - elif selected == "Chatbot Support": - chatbot_support_page() - elif selected == "Help": - help_page() - - - if __name__ == "__main__": - main_app() - -elif authentication_status == False: - st.error('Username/password is incorrect') -elif authentication_status == None: - st.warning('Please enter your username and password') + + # Sidebar Navigation + with st.sidebar: + selected = option_menu( + "Main Menu", + ["Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking", + "Interview Preparation", "Personalized Learning Paths", "Networking Opportunities", + "Salary Estimation", "Feedback", "Gamification", "Resource Library", "Success Stories", "Chatbot Support", "Help"], + icons=["envelope", "file-earmark-text", "file-person", "briefcase", "gear", + "book", "people", "currency-dollar", "chat-left-text", "trophy", "collection", "star", "chat", "question-circle"], + menu_icon="cast", + default_index=0, + ) + + # Route to the selected page + if selected == "Email Generator": + email_generator_page() + elif selected == "Cover Letter Generator": + cover_letter_generator_page() + elif selected == "Resume Analysis": + resume_analysis_page() + elif selected == "Application Tracking": + application_tracking_dashboard() + elif selected == "Interview Preparation": + interview_preparation_module() + elif selected == "Personalized Learning Paths": + personalized_learning_paths_module() + elif selected == "Networking Opportunities": + networking_opportunities_module() + elif selected == "Salary Estimation": + salary_estimation_module() + elif selected == "Feedback": + feedback_and_improvement_module() + elif selected == "Gamification": + gamification_module() + elif selected == "Resource Library": + resource_library_page() + elif selected == "Success Stories": + success_stories_page() + elif selected == "Chatbot Support": + chatbot_support_page() + elif selected == "Help": + help_page() + + + + if __name__ == "__main__": + main_app() + + elif authentication_status == False: + st.error('Username/password is incorrect') + elif authentication_status == None: + st.warning('Please enter your username and password') \ No newline at end of file