import streamlit as st import requests from langchain_groq import ChatGroq from streamlit_chat import message import plotly.express as px import pandas as pd import sqlite3 from datetime import datetime, timedelta import re import os import fitz # PyMuPDF from bs4 import BeautifulSoup from streamlit_option_menu import option_menu # Secrets and API Keys GROQ_API_KEY = st.secrets["GROQ_API_KEY"] RAPIDAPI_KEY = st.secrets["RAPIDAPI_KEY"] YOUTUBE_API_KEY = st.secrets["YOUTUBE_API_KEY"] THE_MUSE_API_KEY = st.secrets.get("THE_MUSE_API_KEY", "") BLS_API_KEY = st.secrets.get("BLS_API_KEY", "") llm = ChatGroq( temperature=0, groq_api_key=GROQ_API_KEY, model_name="llama-3.1-70b-versatile" ) # ------------------------------- # PDF and HTML Extraction Functions # ------------------------------- @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') 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) return response.content.strip() except Exception as e: st.error(f"Error extracting requirements: {e}") return "" # ------------------------------- # Email and Cover Letter Generation # ------------------------------- @st.cache_data(ttl=3600) def generate_email(job_description, requirements, resume_text): """ Generates a personalized cold email using Groq. """ 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. - Value Proposition: Explain how your fresh perspective can add value to the company. - Closing: Express enthusiasm and request an interview. """ try: response = llm.invoke(prompt) return response.content.strip() 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. """ prompt = f""" You are Adithya S Nair, a recent Computer Science graduate specializing in Artificial Intelligence and Machine Learning. Compose a 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. 2. Introduction: Mention the position and your enthusiasm. 3. Body: Highlight skills, experiences, and relevant projects. 4. Value Proposition: Explain how you can contribute to the company. 5. Conclusion: Express interest in an interview and thank the reader. """ try: response = llm.invoke(prompt) return response.content.strip() except Exception as e: st.error(f"Error generating cover letter: {e}") return "" # ------------------------------- # Resume Analysis Functions # ------------------------------- @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() 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 for ATS optimization. """ 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 accordingly. 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 [] 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. """ 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() 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() 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: 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 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 # ------------------------------- # Job API Integration Functions # ------------------------------- @st.cache_data(ttl=86400) def fetch_remotive_jobs_api(job_title, location=None, category=None, remote=True, max_results=50): """ Fetches job listings from Remotive API. """ base_url = "https://remotive.com/api/remote-jobs" params = {"search": job_title, "limit": max_results} if category: params["category"] = category try: response = requests.get(base_url, params=params) response.raise_for_status() jobs = response.json().get("jobs", []) if remote: jobs = [job for job in jobs if job.get("candidate_required_location") == "Worldwide" or job.get("remote") == True] return jobs except requests.exceptions.RequestException as e: st.error(f"Error fetching jobs from Remotive: {e}") return [] @st.cache_data(ttl=86400) def fetch_muse_jobs_api(job_title, location=None, category=None, max_results=50): """ Fetches job listings from The Muse API. """ base_url = "https://www.themuse.com/api/public/jobs" headers = {"Content-Type": "application/json"} params = {"page": 1, "per_page": max_results, "category": category, "location": location, "company": None} try: response = requests.get(base_url, params=params, headers=headers) response.raise_for_status() jobs = response.json().get("results", []) filtered_jobs = [job for job in jobs if job_title.lower() in job.get("name", "").lower()] return filtered_jobs except requests.exceptions.RequestException as e: st.error(f"Error fetching jobs from The Muse: {e}") return [] @st.cache_data(ttl=86400) def fetch_indeed_jobs_list_api(job_title, location="United States", distance="1.0", language="en_GB", remoteOnly="false", datePosted="month", employmentTypes="fulltime;parttime;intern;contractor", index=0, page_size=10): """ Fetches a list of job IDs from Indeed API. """ url = "https://jobs-api14.p.rapidapi.com/list" querystring = { "query": job_title, "location": location, "distance": distance, "language": language, "remoteOnly": remoteOnly, "datePosted": datePosted, "employmentTypes": employmentTypes, "index": str(index), "page_size": str(page_size) } headers = {"x-rapidapi-key": RAPIDAPI_KEY, "x-rapidapi-host": "jobs-api14.p.rapidapi.com"} try: response = requests.get(url, headers=headers, params=querystring) response.raise_for_status() data = response.json() job_ids = [job["id"] for job in data.get("jobs", [])] return job_ids except requests.exceptions.RequestException as e: st.error(f"Error fetching job IDs from Indeed: {e}") return [] @st.cache_data(ttl=86400) def fetch_indeed_job_details_api(job_id, language="en_GB"): """ Fetches job details from Indeed API. """ url = "https://jobs-api14.p.rapidapi.com/get" querystring = {"id": job_id, "language": language} headers = {"x-rapidapi-key": RAPIDAPI_KEY, "x-rapidapi-host": "jobs-api14.p.rapidapi.com"} try: response = requests.get(url, headers=headers, params=querystring) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: st.error(f"Error fetching job details from Indeed: {e}") return {} def recommend_indeed_jobs(user_skills, user_preferences): """ Recommends jobs from Indeed API based on user skills and preferences. """ job_title = user_preferences.get("job_title", "") location = user_preferences.get("location", "United States") category = user_preferences.get("category", "") language = "en_GB" job_ids = fetch_indeed_jobs_list_api(job_title, location=location, category=category, page_size=5) recommended_jobs = [] api_calls_needed = len(job_ids) if not can_make_api_calls(api_calls_needed): st.error("❌ You have reached your monthly API request limit. Please try again later.") return [] for job_id in job_ids: job_details = fetch_indeed_job_details_api(job_id, language=language) if job_details and not job_details.get("hasError", True): job_description = job_details.get("description", "").lower() match_score = sum(skill.lower() in job_description for skill in user_skills) if match_score > 0: recommended_jobs.append((match_score, job_details)) decrement_api_calls(1) recommended_jobs.sort(reverse=True, key=lambda x: x[0]) return [job for score, job in recommended_jobs[:10]] def recommend_jobs(user_skills, user_preferences): """ Combines job recommendations from Remotive, The Muse, and Indeed. """ remotive_jobs = fetch_remotive_jobs_api(user_preferences.get("job_title", ""), user_preferences.get("location"), user_preferences.get("category")) muse_jobs = fetch_muse_jobs_api(user_preferences.get("job_title", ""), user_preferences.get("location"), user_preferences.get("category")) indeed_jobs = recommend_indeed_jobs(user_skills, user_preferences) combined_jobs = remotive_jobs + muse_jobs + indeed_jobs unique_jobs = {} for job in combined_jobs: url = job.get("url") or job.get("redirect_url") or job.get("url_standard") if url and url not in unique_jobs: unique_jobs[url] = job return list(unique_jobs.values()) # ------------------------------- # API Usage Counter Functions # ------------------------------- def init_api_usage_db(): """ Initializes the SQLite database and creates the api_usage table if it doesn't exist. """ conn = sqlite3.connect('applications.db') c = conn.cursor() c.execute(''' CREATE TABLE IF NOT EXISTS api_usage ( id INTEGER PRIMARY KEY AUTOINCREMENT, count INTEGER, last_reset DATE ) ''') c.execute('SELECT COUNT(*) FROM api_usage') if c.fetchone()[0] == 0: c.execute('INSERT INTO api_usage (count, last_reset) VALUES (?, ?)', (25, datetime.now().date())) conn.commit() conn.close() def get_api_usage(): """ Retrieves the current API usage count and last reset date. """ conn = sqlite3.connect('applications.db') c = conn.cursor() c.execute('SELECT count, last_reset FROM api_usage WHERE id = 1') row = c.fetchone() conn.close() if row: return row[0], datetime.strptime(row[1], "%Y-%m-%d").date() else: return 25, datetime.now().date() def reset_api_usage(): """ Resets the API usage count to 25. """ conn = sqlite3.connect('applications.db') c = conn.cursor() c.execute('UPDATE api_usage SET count = ?, last_reset = ? WHERE id = 1', (25, datetime.now().date())) conn.commit() conn.close() def can_make_api_calls(requests_needed): """ Checks if there are enough API calls remaining. """ count, last_reset = get_api_usage() today = datetime.now().date() if today >= last_reset + timedelta(days=30): reset_api_usage() count, last_reset = get_api_usage() return count >= requests_needed def decrement_api_calls(requests_used): """ Decrements the API usage count. """ conn = sqlite3.connect('applications.db') c = conn.cursor() c.execute('SELECT count FROM api_usage WHERE id = 1') row = c.fetchone() if row: new_count = max(row[0] - requests_used, 0) c.execute('UPDATE api_usage SET count = ? WHERE id = 1', (new_count,)) conn.commit() conn.close() # ------------------------------- # Application Tracking Functions # ------------------------------- def init_db(): """ Initializes the SQLite database and creates the applications table. """ 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 job 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() # ------------------------------- # Learning Path Generation # ------------------------------- @st.cache_data(ttl=86400) def generate_learning_path(career_goal, current_skills): """ Generates a personalized learning path using Groq. """ prompt = f""" Based on the following career goal and current skills, create a personalized learning path that includes recommended courses, projects, and milestones. **Career Goal:** {career_goal} **Current Skills:** {current_skills} **Learning Path:** """ try: response = llm.invoke(prompt) return response.content.strip() except Exception as e: st.error(f"Error generating learning path: {e}") return "" # ------------------------------- # YouTube Video Search and Embed Functions # ------------------------------- @st.cache_data(ttl=86400) def search_youtube_videos(query, max_results=2, video_duration="long"): """ Searches YouTube for videos matching the query. """ search_url = "https://www.googleapis.com/youtube/v3/search" params = { "part": "snippet", "q": query, "type": "video", "maxResults": max_results, "videoDuration": video_duration, "key": YOUTUBE_API_KEY } try: response = requests.get(search_url, params=params) response.raise_for_status() results = response.json().get("items", []) video_urls = [f"https://www.youtube.com/watch?v={item['id']['videoId']}" for item in results] return video_urls except requests.exceptions.RequestException as e: st.error(f"❌ Error fetching YouTube videos: {e}") return [] def embed_youtube_videos(video_urls, module_name): """ Embeds YouTube videos. """ for url in video_urls: st.video(url) # ------------------------------- # Application Modules (Pages) # ------------------------------- def email_generator_page(): st.header("📧 Automated Email Generator") st.write("Generate personalized cold emails based on job postings and your resume.") 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):", 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..."): job_description = extract_job_description(job_link) if not job_description: st.error("Failed to extract job description.") return requirements = extract_requirements(job_description) if not requirements: st.error("Failed to extract requirements.") return resume_text = extract_text_from_pdf(uploaded_file) if not resume_text: st.error("Failed to extract text from resume.") return email_text = generate_email(job_description, requirements, resume_text) if email_text: st.subheader("📨 Generated Email:") st.write(email_text) st.download_button("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.") 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):", 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..."): job_description = extract_job_description(job_link) if not job_description: st.error("Failed to extract job description.") return requirements = extract_requirements(job_description) if not requirements: st.error("Failed to extract requirements.") return resume_text = extract_text_from_pdf(uploaded_file) if not resume_text: st.error("Failed to extract text from resume.") return cover_letter = generate_cover_letter(job_description, requirements, resume_text) if cover_letter: st.subheader("📝 Generated Cover Letter:") st.write(cover_letter) st.download_button("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") st.write("Enhance your resume by extracting key information, suggestions, and visual analytics.") uploaded_file = st.file_uploader("📂 Upload your resume (PDF):", type="pdf") if uploaded_file: resume_text = extract_text_from_pdf(uploaded_file) if resume_text: st.success("✅ Resume uploaded successfully!") st.subheader("🔍 Extracted Information") tabs = st.tabs(["đŸ’ŧ Skills", "🔑 Suggested Keywords"]) with tabs[0]: skills = extract_skills(resume_text) if skills: st.markdown("**Identified Skills:**") cols = st.columns(4) for idx, skill in enumerate(skills, 1): cols[idx % 4].write(f"- {skill}") else: st.info("No skills extracted.") with tabs[1]: keywords = suggest_keywords(resume_text) if keywords: st.markdown("**Suggested Keywords for ATS Optimization:**") cols = st.columns(4) for idx, keyword in enumerate(keywords, 1): cols[idx % 4].write(f"- {keyword}") else: st.info("No keywords suggested.") st.subheader("đŸ› ī¸ Optimization Suggestions") st.markdown(""" - **Keyword Optimization:** Incorporate suggested keywords. - **Highlight Relevant Sections:** Emphasize skills that match job requirements. - **Consistent Formatting:** Ensure readability and structure. """) st.subheader("📊 Visual Resume Analytics") viz_col1, viz_col2 = st.columns(2) with viz_col1: if skills: st.markdown("**Skill Distribution:**") fig_skills = create_skill_distribution_chart(skills) st.plotly_chart(fig_skills, use_container_width=True) else: st.info("No skills to display.") with viz_col2: fig_experience = create_experience_timeline(resume_text) if fig_experience: st.markdown("**Experience Timeline:**") st.plotly_chart(fig_experience, use_container_width=True) else: st.info("Not enough data to generate an experience timeline.") st.subheader("💾 Save Resume Analysis") 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.error("❌ Failed to extract text from resume.") def application_tracking_dashboard(): st.header("📋 Application Tracking Dashboard") init_db() init_api_usage_db() 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: job_description = extract_text_from_pdf(uploaded_file) if uploaded_file else "" 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!") 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) csv = df.to_csv(index=False).encode('utf-8') st.download_button("💾 Download Applications as CSV", data=csv, file_name='applications.csv', mime='text/csv') 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) 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 _, row in imported_df.iterrows(): add_application( 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 [] ) st.success("✅ Applications imported successfully!") except Exception as e: st.error(f"❌ Error importing applications: {e}") 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']}") 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!") 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 job_recommendations_module(): st.header("🔍 Job Matching & Recommendations") st.write("Discover job opportunities tailored to your skills and preferences.") st.subheader("đŸŽ¯ Set Your Preferences") with st.form("preferences_form"): job_title = st.text_input("🔍 Desired Job Title", placeholder="e.g., Data Scientist") location = st.text_input("📍 Preferred Location", placeholder="e.g., New York, USA or Remote") category = st.selectbox("📂 Job Category", ["", "Engineering", "Marketing", "Design", "Sales", "Finance", "Healthcare", "Education", "Other"]) user_skills_input = st.text_input("💡 Your Skills (comma-separated)", placeholder="e.g., Python, Machine Learning, SQL") submitted = st.form_submit_button("🚀 Get Recommendations") if submitted: if not job_title or not user_skills_input: st.error("❌ Please enter both job title and your skills.") return user_skills = [skill.strip() for skill in user_skills_input.split(",") if skill.strip()] user_preferences = {"job_title": job_title, "location": location, "category": category} with st.spinner("🔄 Fetching job recommendations..."): recommended_jobs = recommend_jobs(user_skills, user_preferences) if recommended_jobs: st.subheader("đŸ’ŧ Recommended Jobs:") for idx, job in enumerate(recommended_jobs, 1): job_title_display = job.get("title") or job.get("name") or job.get("jobTitle") company_display = job.get("company", {}).get("name") or job.get("company_name") or job.get("employer", {}).get("name") location_display = job.get("candidate_required_location") or job.get("location") or job.get("country") job_url = job.get("url") or job.get("redirect_url") or job.get("url_standard") st.markdown(f"### {idx}. {job_title_display}") st.markdown(f"**đŸĸ Company:** {company_display}") st.markdown(f"**📍 Location:** {location_display}") st.markdown(f"**🔗 Job URL:** [Apply Here]({job_url})") st.write("---") else: st.info("â„šī¸ No job recommendations found based on your criteria.") def interview_preparation_module(): st.header("🎤 Interview Preparation") st.write("Prepare for your interviews with tailored mock questions and answers.") 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 50 interview questions along with their answers for the position of {job_title} at {company}. Each question should be followed by a concise and professional answer. """ try: qa_text = llm.invoke(prompt).content.strip() qa_pairs = qa_text.split('\n\n') st.subheader("đŸ—Ŗī¸ Mock Interview Questions and Answers:") for idx, qa in enumerate(qa_pairs, 1): if qa.strip(): parts = qa.split('\n', 1) if len(parts) == 2: question = parts[0].strip() answer = parts[1].strip() st.markdown(f"**Q{idx}: {question}**") st.markdown(f"**A:** {answer}") st.write("---") 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 achieve your career goals, complemented with curated video resources.") col1, col2 = st.columns(2) with col1: career_goal = st.text_input("đŸŽ¯ Enter your career goal (e.g., Data Scientist):") 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) modules = re.split(r'\d+\.\s+', learning_path) modules = [module.strip() for module in modules if module.strip()] st.subheader("📹 Recommended YouTube Videos for Each Module:") for module in modules: video_urls = search_youtube_videos(query=module, max_results=2, video_duration="long") if video_urls: st.markdown(f"### {module}") embed_youtube_videos(video_urls, module) else: st.write(f"No videos found for **{module}**.") 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 groups.") 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):") 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..."): 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 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: # You can store the feedback in a database or send via email st.success("✅ Thank you for your feedback!") 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": "A checklist to ensure you cover all steps.", "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: st.download_button("âŦ‡ī¸ 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 chatbot_support_page(): st.header("🤖 AI-Powered Chatbot Support") st.write("Have questions or need assistance? Chat with our AI-powered assistant!") if 'chat_history' not in st.session_state: st.session_state['chat_history'] = [] user_input = st.text_input("đŸ—¨ī¸ You:", key="user_input") if st.button("Send"): if user_input: 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: response = llm.invoke(prompt) assistant_message = response.content.strip() st.session_state['chat_history'].append({"message": assistant_message, "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}") 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 help_page(): st.header("❓ Help & FAQ") with st.expander("đŸ› ī¸ How do I generate a cover letter?"): st.write("Navigate to the **Cover Letter Generator** section, enter the job link, upload your resume, and click **Generate Cover Letter**.") with st.expander("📋 How do I track my applications?"): st.write("Use the **Application Tracking Dashboard** to add and manage your job applications.") 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 with the required columns.") with st.expander("đŸ—Ŗī¸ How do I provide feedback?"): st.write("Go to the **Feedback** section, fill out the form, and submit your feedback.") # ------------------------------- # Main Application # ------------------------------- def main_app(): st.markdown( """ """, unsafe_allow_html=True ) with st.sidebar: selected = option_menu( menu_title="📂 Main Menu", options=[ "Email Generator", "Cover Letter Generator", "Resume Analysis", "Application Tracking", "Job Recommendations", "Interview Preparation", "Personalized Learning Paths", "Networking Opportunities", "Feedback", "Resource Library", "Chatbot Support", "Help" ], icons=[ "envelope", "file-earmark-text", "file-person", "briefcase", "search", "microphone", "book", "people", "chat-left-text", "collection", "robot", "question-circle" ], menu_icon="cast", default_index=0, styles={ "container": {"padding": "5!important", "background-color": "#2e7bcf"}, "icon": {"color": "white", "font-size": "18px"}, "nav-link": {"font-size": "16px", "text-align": "left", "margin": "0px", "--hover-color": "#6b9eff"}, "nav-link-selected": {"background-color": "#1e5aab"}, } ) 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 == "Job Recommendations": job_recommendations_module() 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 == "Feedback": feedback_and_improvement_module() elif selected == "Resource Library": resource_library_page() elif selected == "Chatbot Support": chatbot_support_page() elif selected == "Help": help_page() if __name__ == "__main__": main_app()