544 lines
17 KiB
JavaScript
544 lines
17 KiB
JavaScript
/**
|
||
* Job Search Parser Demo
|
||
*
|
||
* Demonstrates the Job Search Parser's capabilities for job market intelligence,
|
||
* trend analysis, and competitive insights.
|
||
*
|
||
* This demo uses simulated data for demonstration purposes.
|
||
*/
|
||
|
||
const { logger } = require("../ai-analyzer");
|
||
const fs = require("fs");
|
||
const path = require("path");
|
||
|
||
// Terminal colors for demo output
|
||
const colors = {
|
||
reset: "\x1b[0m",
|
||
bright: "\x1b[1m",
|
||
cyan: "\x1b[36m",
|
||
green: "\x1b[32m",
|
||
yellow: "\x1b[33m",
|
||
blue: "\x1b[34m",
|
||
magenta: "\x1b[35m",
|
||
red: "\x1b[31m",
|
||
};
|
||
|
||
const demo = {
|
||
title: (text) =>
|
||
console.log(`\n${colors.bright}${colors.cyan}${text}${colors.reset}`),
|
||
section: (text) =>
|
||
console.log(`\n${colors.bright}${colors.magenta}${text}${colors.reset}`),
|
||
success: (text) => console.log(`${colors.green}✅ ${text}${colors.reset}`),
|
||
info: (text) => console.log(`${colors.blue}ℹ️ ${text}${colors.reset}`),
|
||
warning: (text) => console.log(`${colors.yellow}⚠️ ${text}${colors.reset}`),
|
||
error: (text) => console.log(`${colors.red}❌ ${text}${colors.reset}`),
|
||
code: (text) => console.log(`${colors.cyan}${text}${colors.reset}`),
|
||
};
|
||
|
||
// Mock job data for demonstration
|
||
const mockJobs = [
|
||
{
|
||
id: "job_1",
|
||
title: "Senior Software Engineer",
|
||
company: "TechCorp",
|
||
location: "Toronto, Ontario",
|
||
remote_type: "hybrid",
|
||
salary: { min: 100000, max: 140000, currency: "CAD" },
|
||
required_skills: ["React", "Node.js", "TypeScript", "AWS"],
|
||
preferred_skills: ["GraphQL", "Docker", "Kubernetes"],
|
||
experience_level: "senior",
|
||
job_url: "https://example.com/job/1",
|
||
posted_date: "2024-01-10T09:00:00Z",
|
||
scraped_at: "2024-01-15T10:30:00Z",
|
||
},
|
||
{
|
||
id: "job_2",
|
||
title: "Data Scientist",
|
||
company: "DataCorp",
|
||
location: "Vancouver, British Columbia",
|
||
remote_type: "remote",
|
||
salary: { min: 90000, max: 130000, currency: "CAD" },
|
||
required_skills: ["Python", "SQL", "Machine Learning", "Statistics"],
|
||
preferred_skills: ["TensorFlow", "PyTorch", "AWS"],
|
||
experience_level: "mid",
|
||
job_url: "https://example.com/job/2",
|
||
posted_date: "2024-01-09T14:30:00Z",
|
||
scraped_at: "2024-01-15T10:30:00Z",
|
||
},
|
||
{
|
||
id: "job_3",
|
||
title: "Frontend Developer",
|
||
company: "StartupXYZ",
|
||
location: "Calgary, Alberta",
|
||
remote_type: "onsite",
|
||
salary: { min: 70000, max: 95000, currency: "CAD" },
|
||
required_skills: ["React", "JavaScript", "CSS", "HTML"],
|
||
preferred_skills: ["Vue.js", "TypeScript", "Webpack"],
|
||
experience_level: "entry",
|
||
job_url: "https://example.com/job/3",
|
||
posted_date: "2024-01-08T11:15:00Z",
|
||
scraped_at: "2024-01-15T10:30:00Z",
|
||
},
|
||
{
|
||
id: "job_4",
|
||
title: "DevOps Engineer",
|
||
company: "CloudTech",
|
||
location: "Toronto, Ontario",
|
||
remote_type: "hybrid",
|
||
salary: { min: 95000, max: 125000, currency: "CAD" },
|
||
required_skills: ["Docker", "Kubernetes", "AWS", "Linux"],
|
||
preferred_skills: ["Terraform", "Jenkins", "Prometheus"],
|
||
experience_level: "senior",
|
||
job_url: "https://example.com/job/4",
|
||
posted_date: "2024-01-07T16:45:00Z",
|
||
scraped_at: "2024-01-15T10:30:00Z",
|
||
},
|
||
{
|
||
id: "job_5",
|
||
title: "Machine Learning Engineer",
|
||
company: "AI Solutions",
|
||
location: "Vancouver, British Columbia",
|
||
remote_type: "remote",
|
||
salary: { min: 110000, max: 150000, currency: "CAD" },
|
||
required_skills: ["Python", "TensorFlow", "PyTorch", "ML"],
|
||
preferred_skills: ["AWS", "Docker", "Kubernetes", "Spark"],
|
||
experience_level: "senior",
|
||
job_url: "https://example.com/job/5",
|
||
posted_date: "2024-01-06T10:20:00Z",
|
||
scraped_at: "2024-01-15T10:30:00Z",
|
||
},
|
||
];
|
||
|
||
async function runDemo() {
|
||
demo.title("=== Job Search Parser Demo ===");
|
||
demo.info(
|
||
"This demo showcases the Job Search Parser's capabilities for job market intelligence."
|
||
);
|
||
demo.info("All data shown is simulated for demonstration purposes.");
|
||
demo.info("Press Enter to continue through each section...\n");
|
||
|
||
await waitForEnter();
|
||
|
||
// 1. Configuration Demo
|
||
await demonstrateConfiguration();
|
||
|
||
// 2. Job Search Process Demo
|
||
await demonstrateJobSearch();
|
||
|
||
// 3. Market Analysis Demo
|
||
await demonstrateMarketAnalysis();
|
||
|
||
// 4. Trend Analysis Demo
|
||
await demonstrateTrendAnalysis();
|
||
|
||
// 5. Skill Analysis Demo
|
||
await demonstrateSkillAnalysis();
|
||
|
||
// 6. Competitive Intelligence Demo
|
||
await demonstrateCompetitiveIntelligence();
|
||
|
||
// 7. Output Generation Demo
|
||
await demonstrateOutputGeneration();
|
||
|
||
demo.title("=== Demo Complete ===");
|
||
demo.success("Job Search Parser demo completed successfully!");
|
||
demo.info("Check the README.md for detailed usage instructions.");
|
||
}
|
||
|
||
async function demonstrateConfiguration() {
|
||
demo.section("1. Configuration Setup");
|
||
demo.info(
|
||
"The Job Search Parser uses environment variables and command-line options for configuration."
|
||
);
|
||
|
||
demo.code("// Environment Variables (.env file)");
|
||
demo.info("SEARCH_SOURCES=linkedin,indeed,glassdoor");
|
||
demo.info("TARGET_ROLES=software engineer,data scientist,product manager");
|
||
demo.info("LOCATION_FILTER=Toronto,Vancouver,Calgary");
|
||
demo.info("EXPERIENCE_LEVELS=entry,mid,senior");
|
||
demo.info("REMOTE_PREFERENCE=remote,hybrid,onsite");
|
||
demo.info("ENABLE_SALARY_ANALYSIS=true");
|
||
demo.info("ENABLE_SKILL_ANALYSIS=true");
|
||
demo.info("ENABLE_TREND_ANALYSIS=true");
|
||
|
||
demo.code("// Command Line Options");
|
||
demo.info('node index.js --roles="frontend developer,backend developer"');
|
||
demo.info('node index.js --locations="Toronto,Vancouver"');
|
||
demo.info('node index.js --experience="senior" --salary-min=100000');
|
||
demo.info('node index.js --remote="remote" --trends --skills');
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
async function demonstrateJobSearch() {
|
||
demo.section("2. Job Search Process");
|
||
demo.info(
|
||
"The parser searches multiple job platforms for relevant positions."
|
||
);
|
||
|
||
const searchSources = ["LinkedIn", "Indeed", "Glassdoor"];
|
||
const targetRoles = [
|
||
"Software Engineer",
|
||
"Data Scientist",
|
||
"Frontend Developer",
|
||
];
|
||
|
||
demo.code("// Multi-source job search");
|
||
for (const source of searchSources) {
|
||
logger.search(`Searching ${source} for job postings...`);
|
||
await simulateSearch();
|
||
|
||
const jobsFound = Math.floor(Math.random() * 200) + 50;
|
||
logger.success(`Found ${jobsFound} jobs on ${source}`);
|
||
}
|
||
|
||
demo.code("// Role-specific filtering");
|
||
for (const role of targetRoles) {
|
||
logger.info(`Filtering for ${role} positions...`);
|
||
await simulateProcessing();
|
||
|
||
const roleJobs = Math.floor(Math.random() * 30) + 10;
|
||
logger.success(`Found ${roleJobs} ${role} positions`);
|
||
}
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
async function demonstrateMarketAnalysis() {
|
||
demo.section("3. Market Analysis");
|
||
demo.info(
|
||
"The parser analyzes market trends, salary ranges, and job distribution."
|
||
);
|
||
|
||
demo.code("// Market overview analysis");
|
||
logger.info("Analyzing market overview...");
|
||
await simulateProcessing();
|
||
|
||
const marketOverview = {
|
||
total_jobs: 1250,
|
||
average_salary: 95000,
|
||
salary_range: { min: 65000, max: 180000, median: 92000 },
|
||
remote_distribution: { remote: 45, hybrid: 35, onsite: 20 },
|
||
experience_distribution: { entry: 15, mid: 45, senior: 40 },
|
||
};
|
||
|
||
demo.success(`Total jobs found: ${marketOverview.total_jobs}`);
|
||
demo.info(
|
||
`Average salary: $${marketOverview.average_salary.toLocaleString()}`
|
||
);
|
||
demo.info(
|
||
`Salary range: $${marketOverview.salary_range.min.toLocaleString()} - $${marketOverview.salary_range.max.toLocaleString()}`
|
||
);
|
||
demo.info(
|
||
`Remote work: ${marketOverview.remote_distribution.remote}% remote, ${marketOverview.remote_distribution.hybrid}% hybrid`
|
||
);
|
||
|
||
demo.code("// Geographic distribution");
|
||
const locations = {
|
||
Toronto: 45,
|
||
Vancouver: 30,
|
||
Calgary: 15,
|
||
Other: 10,
|
||
};
|
||
|
||
Object.entries(locations).forEach(([location, percentage]) => {
|
||
demo.info(`${location}: ${percentage}% of jobs`);
|
||
});
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
async function demonstrateTrendAnalysis() {
|
||
demo.section("4. Trend Analysis");
|
||
demo.info(
|
||
"The parser identifies growing and declining skills and emerging roles."
|
||
);
|
||
|
||
demo.code("// Skill trend analysis");
|
||
logger.info("Analyzing skill trends...");
|
||
await simulateProcessing();
|
||
|
||
const growingSkills = [
|
||
{ skill: "React", growth_rate: 25 },
|
||
{ skill: "Python", growth_rate: 18 },
|
||
{ skill: "AWS", growth_rate: 22 },
|
||
{ skill: "TypeScript", growth_rate: 30 },
|
||
{ skill: "Docker", growth_rate: 15 },
|
||
];
|
||
|
||
const decliningSkills = [
|
||
{ skill: "jQuery", growth_rate: -12 },
|
||
{ skill: "PHP", growth_rate: -8 },
|
||
{ skill: "Angular", growth_rate: -5 },
|
||
];
|
||
|
||
demo.success("Growing skills:");
|
||
growingSkills.forEach((skill) => {
|
||
demo.info(` ${skill.skill}: +${skill.growth_rate}% growth`);
|
||
});
|
||
|
||
demo.warning("Declining skills:");
|
||
decliningSkills.forEach((skill) => {
|
||
demo.info(` ${skill.skill}: ${skill.growth_rate}% decline`);
|
||
});
|
||
|
||
demo.code("// Emerging roles");
|
||
const emergingRoles = [
|
||
"AI Engineer",
|
||
"DevSecOps Engineer",
|
||
"Data Engineer",
|
||
"Cloud Architect",
|
||
"Site Reliability Engineer",
|
||
];
|
||
|
||
demo.success("Emerging roles:");
|
||
emergingRoles.forEach((role) => {
|
||
demo.info(` ${role}`);
|
||
});
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
async function demonstrateSkillAnalysis() {
|
||
demo.section("5. Skill Analysis");
|
||
demo.info("The parser analyzes skill demand and salary correlation.");
|
||
|
||
demo.code("// Skill demand analysis");
|
||
logger.info("Analyzing skill demand...");
|
||
await simulateProcessing();
|
||
|
||
const skillDemand = {
|
||
React: { count: 45, avg_salary: 98000 },
|
||
Python: { count: 38, avg_salary: 102000 },
|
||
AWS: { count: 32, avg_salary: 105000 },
|
||
TypeScript: { count: 28, avg_salary: 95000 },
|
||
Docker: { count: 25, avg_salary: 103000 },
|
||
"Machine Learning": { count: 22, avg_salary: 115000 },
|
||
};
|
||
|
||
demo.success("Top in-demand skills:");
|
||
Object.entries(skillDemand)
|
||
.sort((a, b) => b[1].count - a[1].count)
|
||
.forEach(([skill, data]) => {
|
||
demo.info(
|
||
` ${skill}: ${
|
||
data.count
|
||
} jobs, avg salary $${data.avg_salary.toLocaleString()}`
|
||
);
|
||
});
|
||
|
||
demo.code("// Salary correlation analysis");
|
||
const salaryCorrelation = [
|
||
{ skill: "Machine Learning", correlation: 0.85 },
|
||
{ skill: "AWS", correlation: 0.78 },
|
||
{ skill: "Docker", correlation: 0.72 },
|
||
{ skill: "Python", correlation: 0.68 },
|
||
{ skill: "React", correlation: 0.65 },
|
||
];
|
||
|
||
demo.success("Skills with highest salary correlation:");
|
||
salaryCorrelation.forEach((item) => {
|
||
demo.info(
|
||
` ${item.skill}: ${(item.correlation * 100).toFixed(0)}% correlation`
|
||
);
|
||
});
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
async function demonstrateCompetitiveIntelligence() {
|
||
demo.section("6. Competitive Intelligence");
|
||
demo.info(
|
||
"The parser provides insights into company hiring patterns and salary competitiveness."
|
||
);
|
||
|
||
demo.code("// Company hiring analysis");
|
||
logger.info("Analyzing company hiring patterns...");
|
||
await simulateProcessing();
|
||
|
||
const topHirers = [
|
||
{ company: "TechCorp", jobs: 25, avg_salary: 105000 },
|
||
{ company: "StartupXYZ", jobs: 18, avg_salary: 95000 },
|
||
{ company: "DataCorp", jobs: 15, avg_salary: 110000 },
|
||
{ company: "CloudTech", jobs: 12, avg_salary: 115000 },
|
||
{ company: "AI Solutions", jobs: 10, avg_salary: 120000 },
|
||
];
|
||
|
||
demo.success("Top hiring companies:");
|
||
topHirers.forEach((company) => {
|
||
demo.info(
|
||
` ${company.company}: ${
|
||
company.jobs
|
||
} jobs, avg salary $${company.avg_salary.toLocaleString()}`
|
||
);
|
||
});
|
||
|
||
demo.code("// Salary competitiveness");
|
||
const salaryLeaders = [
|
||
{ company: "BigTech", avg_salary: 120000, market_position: "leader" },
|
||
{ company: "FinTech", avg_salary: 115000, market_position: "leader" },
|
||
{ company: "AI Solutions", avg_salary: 120000, market_position: "leader" },
|
||
{
|
||
company: "StartupXYZ",
|
||
avg_salary: 95000,
|
||
market_position: "competitive",
|
||
},
|
||
{ company: "TechCorp", avg_salary: 105000, market_position: "competitive" },
|
||
];
|
||
|
||
demo.success("Salary leaders:");
|
||
salaryLeaders.forEach((company) => {
|
||
const position = company.market_position === "leader" ? "🏆" : "📊";
|
||
demo.info(
|
||
` ${position} ${
|
||
company.company
|
||
}: $${company.avg_salary.toLocaleString()}`
|
||
);
|
||
});
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
async function demonstrateOutputGeneration() {
|
||
demo.section("7. Output Generation");
|
||
demo.info(
|
||
"Results are saved in multiple formats with comprehensive analysis."
|
||
);
|
||
|
||
demo.code("// Generating comprehensive report");
|
||
logger.file("Generating job market analysis report...");
|
||
|
||
const outputData = {
|
||
metadata: {
|
||
timestamp: new Date().toISOString(),
|
||
search_parameters: {
|
||
roles: ["software engineer", "data scientist", "frontend developer"],
|
||
locations: ["Toronto", "Vancouver", "Calgary"],
|
||
experience_levels: ["entry", "mid", "senior"],
|
||
remote_preference: ["remote", "hybrid", "onsite"],
|
||
},
|
||
total_jobs_found: 1250,
|
||
analysis_duration_seconds: 45,
|
||
},
|
||
market_overview: {
|
||
total_jobs: 1250,
|
||
average_salary: 95000,
|
||
salary_range: { min: 65000, max: 180000, median: 92000 },
|
||
remote_distribution: { remote: 45, hybrid: 35, onsite: 20 },
|
||
experience_distribution: { entry: 15, mid: 45, senior: 40 },
|
||
},
|
||
trends: {
|
||
growing_skills: [
|
||
{ skill: "React", growth_rate: 25 },
|
||
{ skill: "Python", growth_rate: 18 },
|
||
{ skill: "AWS", growth_rate: 22 },
|
||
],
|
||
declining_skills: [
|
||
{ skill: "jQuery", growth_rate: -12 },
|
||
{ skill: "PHP", growth_rate: -8 },
|
||
],
|
||
emerging_roles: ["AI Engineer", "DevSecOps Engineer", "Data Engineer"],
|
||
},
|
||
jobs: mockJobs,
|
||
analysis: {
|
||
skill_demand: {
|
||
React: { count: 45, avg_salary: 98000 },
|
||
Python: { count: 38, avg_salary: 102000 },
|
||
AWS: { count: 32, avg_salary: 105000 },
|
||
},
|
||
company_insights: {
|
||
top_hirers: [
|
||
{ company: "TechCorp", jobs: 25 },
|
||
{ company: "StartupXYZ", jobs: 18 },
|
||
],
|
||
salary_leaders: [
|
||
{ company: "BigTech", avg_salary: 120000 },
|
||
{ company: "FinTech", avg_salary: 115000 },
|
||
],
|
||
},
|
||
},
|
||
};
|
||
|
||
// Save to demo file
|
||
const outputPath = path.join(__dirname, "demo-job-analysis.json");
|
||
fs.writeFileSync(outputPath, JSON.stringify(outputData, null, 2));
|
||
|
||
demo.success(`Analysis report saved to: ${outputPath}`);
|
||
demo.info(`Total jobs analyzed: ${outputData.metadata.total_jobs_found}`);
|
||
demo.info(
|
||
`Analysis duration: ${outputData.metadata.analysis_duration_seconds} seconds`
|
||
);
|
||
|
||
demo.code("// Output formats available");
|
||
demo.info("📁 JSON: Comprehensive analysis with metadata");
|
||
demo.info("📊 CSV: Tabular data for spreadsheet analysis");
|
||
demo.info("📈 Charts: Visual trend analysis");
|
||
demo.info("📋 Summary: Executive summary report");
|
||
|
||
await waitForEnter();
|
||
}
|
||
|
||
// Helper functions
|
||
function waitForEnter() {
|
||
return new Promise((resolve) => {
|
||
const readline = require("readline");
|
||
const rl = readline.createInterface({
|
||
input: process.stdin,
|
||
output: process.stdout,
|
||
});
|
||
|
||
rl.question("\nPress Enter to continue...", () => {
|
||
rl.close();
|
||
resolve();
|
||
});
|
||
});
|
||
}
|
||
|
||
async function simulateSearch() {
|
||
return new Promise((resolve) => {
|
||
const steps = [
|
||
"Connecting to source",
|
||
"Searching jobs",
|
||
"Filtering results",
|
||
"Extracting data",
|
||
];
|
||
let i = 0;
|
||
const interval = setInterval(() => {
|
||
if (i < steps.length) {
|
||
logger.info(steps[i]);
|
||
i++;
|
||
} else {
|
||
clearInterval(interval);
|
||
resolve();
|
||
}
|
||
}, 600);
|
||
});
|
||
}
|
||
|
||
async function simulateProcessing() {
|
||
return new Promise((resolve) => {
|
||
const dots = [".", "..", "..."];
|
||
let i = 0;
|
||
const interval = setInterval(() => {
|
||
process.stdout.write(`\rProcessing${dots[i]}`);
|
||
i = (i + 1) % dots.length;
|
||
}, 500);
|
||
|
||
setTimeout(() => {
|
||
clearInterval(interval);
|
||
process.stdout.write("\r");
|
||
resolve();
|
||
}, 2000);
|
||
});
|
||
}
|
||
|
||
// Run the demo if this file is executed directly
|
||
if (require.main === module) {
|
||
runDemo().catch((error) => {
|
||
demo.error(`Demo failed: ${error.message}`);
|
||
process.exit(1);
|
||
});
|
||
}
|
||
|
||
module.exports = { runDemo };
|