import reader from 'xlsx'; import { PrismaClient } from '@prisma/client'; import crypto from 'crypto'; import { baseCurrencyString } from '../src/utils/offers/currency'; import { convert } from '../src/utils/offers/currency/currencyExchange'; import { generateAnalysis } from '../src/utils/offers/analysis/analysisGeneration'; import { generateRandomName, generateRandomStringForToken, } from '../src/utils/offers/randomGenerator'; const prisma = new PrismaClient(); // Reading our test file const file = reader.readFile('prisma/salaries.xlsx'); let data: Array = []; type ExcelData = { Timestamp: Date; Type: string; Company: string; Role: string; Income?: number | string; Stocks?: number | string; SignOn?: number | string; TC?: number | string; Bonus?: number | string; Comments?: string; }; const sheets = file.SheetNames; for (let i = 0; i < 1; i++) { const temp = reader.utils.sheet_to_json(file.Sheets[file.SheetNames[i]]); temp.forEach((res: ExcelData) => { data.push(res); }); } function xlSerialToJsDate(xlSerial) { return new Date(Date.UTC(0, 0, xlSerial - 1)); } const getJobTitle = (role: string) => { const processedRole = role.toUpperCase().trim(); if (processedRole.includes('ML ENGINEER')) { return 'machine-learning-engineer'; } else if (processedRole.includes('BACKEND')) { return 'back-end-engineer'; } else if (processedRole.includes('DATA')) { return 'data-engineer'; } else if (processedRole.includes('DEVOPS')) { return 'devops-engineer'; } else if (processedRole.includes('ENTERPRISE')) { return 'enterprise-engineer'; } else if (processedRole.includes('RESEARCH')) { return 'research-engineer'; } else if ( processedRole.includes('CYBER') || processedRole.includes('SECURITY') ) { return 'security-engineer'; } else if (processedRole.includes('QA')) { return 'test-engineer'; } else if (processedRole.includes('SYSTEM')) { return 'systems-engineer'; } else { return 'software-engineer'; // Assume default SWE } }; const getYoe = (type: string) => { const processedType = type.toUpperCase().trim(); if ( processedType.includes('FRESH GRAD') || processedType.includes('JUNIOR') ) { return Math.floor(Math.random() * 3); } else if (processedType.includes('MID')) { return Math.floor(Math.random() * 3) + 3; } else if (processedType.includes('SENIOR')) { return Math.floor(Math.random() * 5) + 6; } else { return 0; // INTERNSHIP OR ERROR -> 0 YOE } }; const getLevel = (type: string) => { const processedType = type.toUpperCase().trim(); if ( processedType.includes('FRESH GRAD') || processedType.includes('JUNIOR') ) { return 'Junior'; } else if (processedType.includes('MID')) { return 'Mid'; } else if (processedType.includes('SENIOR')) { return 'Senior'; } else { return 'N/A'; } }; const createdProfileIds: Array = []; const seedSalaries = async () => { console.log('Seeding from salaries sheet...'); const companyIdMappings = {}; (await prisma.company.findMany()).forEach((company) => { companyIdMappings[company.slug] = company.id; }); // get countryId of Singapore const singapore = await prisma.city.findFirst({ where: { name: 'Singapore', }, }); console.log('Singapore ID: ' + singapore?.id); // break; // seed here if (singapore) { return await Promise.all( data.map(async (data: ExcelData) => { if (data.TC && typeof data.TC === 'number') { // Generate random name until unique let uniqueName: string = await generateRandomName(); const jobTitle = getJobTitle(data.Role); const yoe = getYoe(data.Type); const level = getLevel(data.Type); // check if we have company id if (companyIdMappings[data.Company]) { const token = crypto .createHash('sha256') .update( xlSerialToJsDate(data.Timestamp).toString() + generateRandomStringForToken(), ) .digest('hex'); if (data.Type.toUpperCase() === 'INTERNSHIP') { // create profile const dataAdded = await prisma.offersProfile.create({ data: { profileName: uniqueName, createdAt: xlSerialToJsDate(data.Timestamp), editToken: token, background: { create: { totalYoe: yoe, }, }, offers: { create: { comments: data.Comments ?? '', company: { connect: { id: companyIdMappings[data.Company], }, }, jobType: 'INTERN', location: { connect: { id: singapore.id, }, }, // TODO: DEFAULT AS SG monthYearReceived: xlSerialToJsDate(data.Timestamp), negotiationStrategy: '', offersIntern: { create: { internshipCycle: 'Summer', monthlySalary: { create: { baseCurrency: baseCurrencyString, baseValue: await convert( data.Income ? typeof data.Income === 'number' ? data.Income : 0 : 0, 'SGD', // assume sgd baseCurrencyString, ), currency: 'SGD', // assume sgd value: data.Income ? typeof data.Income === 'number' ? data.Income : 0 : 0, }, }, startYear: xlSerialToJsDate( data.Timestamp, ).getFullYear(), title: jobTitle, }, }, }, }, }, }); console.log('Profile created:', dataAdded.id); createdProfileIds.push(dataAdded.id); } else { // assume rest full time const dataAdded = await prisma.offersProfile.create({ data: { profileName: uniqueName, createdAt: xlSerialToJsDate(data.Timestamp), editToken: token, background: { create: { totalYoe: yoe, }, }, offers: { create: { comments: data.Comments ?? '', company: { connect: { id: companyIdMappings[data.Company], }, }, jobType: 'FULLTIME', location: { connect: { id: singapore.id, }, }, // TODO: DEFAULT AS SG monthYearReceived: xlSerialToJsDate(data.Timestamp), negotiationStrategy: '', offersFullTime: { create: { baseSalary: { create: { baseCurrency: baseCurrencyString, baseValue: await convert( data.Income ? typeof data.Income === 'number' ? data.Income : 0 : 0, 'SGD', // assume sgd baseCurrencyString, ), currency: 'SGD', // assume sgd value: data.Income ? typeof data.Income === 'number' ? data.Income : 0 : 0, }, }, bonus: { create: { baseCurrency: baseCurrencyString, baseValue: await convert( data.Bonus ? typeof data.Bonus === 'number' ? data.Bonus : 0 : 0, 'SGD', baseCurrencyString, ), currency: 'SGD', value: data.Bonus ? typeof data.Bonus === 'number' ? data.Bonus : 0 : 0, }, }, level: level, stocks: { create: { baseCurrency: baseCurrencyString, baseValue: await convert( data.Stocks ? typeof data.Stocks === 'number' ? data.Stocks : 0 : 0, 'SGD', baseCurrencyString, ), currency: 'SGD', value: data.Stocks ? typeof data.Stocks === 'number' ? data.Stocks : 0 : 0, }, }, title: jobTitle, totalCompensation: { create: { baseCurrency: baseCurrencyString, baseValue: await convert( data.TC ? typeof data.TC === 'number' ? data.TC : 0 : 0, 'SGD', baseCurrencyString, ), currency: 'SGD', value: data.TC ? typeof data.TC === 'number' ? data.TC : 0 : 0, }, }, }, }, }, }, }, }); console.log('Profile created:', dataAdded.id); createdProfileIds.push(dataAdded.id); } } else { console.log('Invalid Company: ' + data.Company); } } else { console.log('Invalid TC not a number: ' + data.TC); } }), ); } }; const generateAllAnalysis = async () => { return await Promise.all( createdProfileIds.map(async (profileId) => { await generateAnalysis({ ctx: { prisma, session: null }, input: { profileId }, }); console.log('Analysis generated for profile with id:', profileId); }), ); }; Promise.all([seedSalaries()]) .then(() => { console.log(createdProfileIds.length + ' profiles created'); console.log('Busy crunching analysis.....'); }) .then(() => generateAllAnalysis()) .then((_data) => { console.log('Seeding from salaries sheet complete'); }) .then(async () => { await prisma.$disconnect(); }) .catch(async (e) => { console.error(e); await prisma.$disconnect(); process.exit(1); }); export {};