const fs = require('fs') const cosineSimilarity = require('compute-cosine-similarity') const mockOutputPath = './db/generated/_batch' const magic = 1000 // Insert here how many users you would like to generate: const total = 1000 const batchSize = 100 let batchCount = 1 // Counter to track how many things we've generated let extraProfilesToGenerate = 0 let extraProfileCount = 0 // Counter to track how many EXTRA profiles we've generated let generatedResponseCount = 0 // Amount of responses for a complete survey const questions = 13 // Seekers per 100 profiles const percentageOfSeekers = 90 const scoreVals = [100, 140, 180, 220, 260, 400] // Values for responsess as strings const possibleResponses = { not_important: null, some_what_important: null, important: null, very_important: null, extremely_important: null, mandatory: null, } for (let i = 0; i < Object.keys(possibleResponses).length; i++) { const key = Object.keys(possibleResponses)[i] possibleResponses[key] = scoreVals[i].toString() } /** * Our initial file setup */ const header = `/** * GENERATED MOCK SIIMEE DATA * Generated at: ${Date.now()} */ ` const write = async (batchNum, outputDataObject) => { await fs.writeFile(`${mockOutputPath}_${batchNum}.js`, '', () => {}) fs.appendFile( `${mockOutputPath}_${batchNum}.js`, header + 'module.exports = ' + JSON.stringify(outputDataObject), err => { if (err) { console.error(err) return } }, ) } /** * [100, 140, 180, 220, 260, 400] */ const preComputedScores = { 100: { 100: 0, 140: 0, 180: 0, 220: 0, 260: 0, 400: 0, }, 140: { 100: 0, 140: 0, 180: 0, 220: 0, 260: 0, 400: 0, }, 180: { 100: 0, 140: 0, 180: 0, 220: 0, 260: 0, 400: 0, }, 220: { 100: 0, 140: 0, 180: 0, 220: 0, 260: 0, 400: 0, }, 260: { 100: 0, 140: 0, 180: 0, 220: 0, 260: 0, 400: 0, }, 400: { 100: 0, 140: 0, 180: 0, 220: 0, 260: 0, 400: 0, }, } const score2d = (a, b) => { const aScorePlusBase = [100] const bScorePlusBase = [100] aScorePlusBase.push(a) bScorePlusBase.push(b) return Math.round( Math.pow(cosineSimilarity(aScorePlusBase, bScorePlusBase), 10) * magic, ) } scoreVals.forEach(val => { scoreVals.forEach(v => { preComputedScores[val][v] = score2d(val, v) }) }) const possibleZipcodes = [ '90065', // Glassel '90012', // Chinatown '90240', // Downey '91030', // South Pasadena '91201', // Glendale '91399', // Woodland Hills '91401', // Van Nuys '90840', // Long Beach '91001', // Altadena '91011', // La Canada Flintridge '97075', // Beaverton ] // Helper functions const randomNumber = max => { return Math.floor(Math.random() * max) < 1 ? 1 : Math.floor(Math.random() * max) } const randomValFrom = arr => arr[randomNumber(arr.length)] const randomEmail = (length = 5) => { let chars = 'abcdefghijklmnopqrstuvwxyz-_abcdefghijklmnopqrstuvwxyz0123456789' let str = '' for (let i = 0; i < length + randomNumber(9); i++) { str += chars.charAt(Math.floor(Math.random() * chars.length)) } const suffixs = [ '@gmail.com', '@aol.com', '@yahoo.com', '@apple.com', '@hotmail.com', '@rocket-mail.com', '@mail.com', ] return str + randomValFrom(suffixs) } const randomName = (length = 4) => { let chars = 'aeiouaeiouabcdefghijklmnoprstuvwyabcdefghijklmnopqrstuvwxyz' let str = '' for (let i = 0; i < length + randomNumber(9); i++) { str += chars.charAt(Math.floor(Math.random() * chars.length)) } return str } const generate = (classObj, amount, meta) => { const instances = [] for (let i = 0; i < amount; i++) { let startFrom = meta?.starting ? meta.starting - batchSize : 0 instances.push(new classObj(i + startFrom + 1, meta)) } return instances } class User { constructor(id) { this.user_id = id this.user_name = '' this.user_email = '' this.is_admin = false this.is_poster = false } } class Profile { constructor(id, override) { this.user_id = override?.user_id ? override.user_id : id this.profile_id = override?.profile_id ? override.profile_id + id : id } } class Response { constructor(id) { this.response_id = id this.profile_id = null this.response_key_id = null this.val = null } } console.log('\nStarting...\n---') for (let batch = batchSize; batch <= total; batch += batchSize) { /** * Generate Users */ let users = generate(User, batchSize, { starting: batchSize * batchCount, }) users.forEach(user => { user.is_poster = randomNumber(100) > percentageOfSeekers ? 1 : 0 if (user.is_poster) { extraProfilesToGenerate = extraProfilesToGenerate + randomNumber(2) } user.user_name = randomName() + ' ' + randomName() user.user_email = randomEmail() }) let jobPosterIds = users .filter(user => user.is_poster > 0) .map(user => user.user_id) console.log('COMPLETED: Generated Users...') // Guarentee ONE job poster if (!jobPosterIds.length) { randomValFrom(users).is_poster = 1 jobPosterIds = users .filter(user => user.is_poster > 0) .map(user => user.user_id) } /** * Generate Profiles */ let profiles = generate(Profile, batchSize, { starting: batchSize * batchCount, profile_id: extraProfileCount, }) // Generate extra job posting profiles // attributed to random user.is_poster === true // TODO: Clean this up. Hard to read... if (extraProfilesToGenerate > 0) { let extras = [] for (let l = 0; l < extraProfilesToGenerate; l++) { const generatedExtraProfiles = generate(Profile, 1, { user_id: jobPosterIds.length > 1 ? randomValFrom(jobPosterIds) : jobPosterIds[0], profile_id: batchSize * batchCount + extraProfileCount + l, }) extras = [...extras, ...generatedExtraProfiles] } extras.forEach(profile => { profiles.push(profile) extraProfileCount++ }) } console.log('COMPLETED: Generated Profiles...') /** * Generate Responses */ // Generate responses first, before filling in details let responses = generate( Response, (batchSize + extraProfilesToGenerate) * questions, { starting: generatedResponseCount + batchSize }, ) profiles.forEach((profile, i) => { const startingIndex = i * questions for (let k = 0; k < questions; k++) { const resToEdit = responses[startingIndex + k] resToEdit.response_key_id = k + 1 resToEdit.profile_id = profile.profile_id resToEdit.val = k + 1 == questions ? randomValFrom(possibleZipcodes) : randomValFrom(Object.values(possibleResponses)) } }) generatedResponseCount = generatedResponseCount + responses.length console.log('COMPLETED: Generated Responses...') /** * Our output format */ const outputDataObject = { users, profiles, responses } // const outputDataObject = { users, profiles, responses, match_queues } write(batchSize * batchCount, outputDataObject) batchCount++ } // /** // * Score all the profiles! // */ // const compareProfileResponses = (seeker, potentialMatch) => { // const checkValCb = res => { // const val = parseInt(res.val) // return isNaN(val) ? 0 : val // } // const filterBy = idToCheckFor => { // return responses // .filter( // response => // response.profile_id == idToCheckFor && // response.val.length < 4, // ) // .map(checkValCb) // } // const seekerResponses = filterBy(seeker.profile_id) // const potentialMatchResponses = filterBy(potentialMatch.profile_id) // const cachedScores = [] // seekerResponses.forEach(seekerResponse => { // potentialMatchResponses.forEach(potentialResponse => { // cachedScores.push( // preComputedScores[seekerResponse][potentialResponse], // ) // }) // }) // return Math.round( // cachedScores.reduce((a, b) => a + b) / cachedScores.length, // ) // } // const scoreProfile = (profile, potentialMatchList) => { // return potentialMatchList // .map(profileToCompare => { // return { // match_queue_id: null, // profile_id: profile.profile_id, // target_id: profileToCompare.profile_id, // is_deleted: false, // score: compareProfileResponses(profile, profileToCompare), // } // }) // .sort((a, b) => a.score - b.score) // } // const scoreAll = () => { // process.stdout.write('\nScoring Profiles') // let scores = [] // const posterProfiles = profiles.filter(profile => // jobPosterIds.includes(profile.user_id), // ) // const seekerProfiles = profiles.filter( // profile => !jobPosterIds.includes(profile.user_id), // ) // process.stdout.write('.') // for (let i = 0; i < seekerProfiles.length; i++) { // const scored = scoreProfile(seekerProfiles[i], posterProfiles) // scores.push(...scored) // } // process.stdout.write('.') // for (let j = 0; j < posterProfiles.length; j++) { // const scored = scoreProfile(posterProfiles[j], seekerProfiles) // scores.push(...scored) // } // process.stdout.write('.') // console.log('\n\nCOMPLETED: Scoring Profiles...') // return scores.reverse() // } // const match_queues = scoreAll().map((score, i) => { // score.match_queue_id = i + 1 // // Comment out next line to see the scores // delete score.score // return score // }) console.log('---\nFINISHED...\n===\n')