UNPKG

@padrocha/uam-scraping

Version:

Scraping of teachers

387 lines (327 loc) 13.4 kB
import readline from 'readline'; import puppeteer from 'puppeteer'; import { config } from './config'; declare global { interface String { capitalize(): string; } } String.prototype.capitalize = function (this: string) { return this.toLowerCase().replace(/(?:^|\s|["'([{])+\S/g, match => match.toUpperCase()); }; export const week = new Array<day>('monday', 'tuesday', 'wednesday', 'thursday', 'friday'); export function log(param: string, pad = config.CONSOLESIZE): void { param = ` ${param} `; const start = (pad - param.length) / 2 + param.length; console.log(param.padStart(Math.round(start), '-').padEnd(pad, '-')); } export function confirm(message: string) { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise<boolean>(response => { rl.question(`\x1b[34m?\x1b[0m ${message} (Y/N)? `, (answer) => { process.stdout.moveCursor(0, -1); process.stdout.clearScreenDown(); response(new Array('y', 'Y', 's', 'S', '').includes(answer.trim())); rl.close(); }); }); } export function askUser() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); return new Promise<string>(resolve => { rl.question('User: ', user => { resolve(user); rl.close(); }) }); } export function password() { const rl: any = readline.createInterface({ input: process.stdin, output: process.stdout }); const stdoutMuted = true; const query = "Password : "; rl._writeToOutput = function _writeToOutput(stringToWrite: string) { const animation = (rl.line.length % 2 == 1) ? "=-" : "-="; if (stdoutMuted) process.stdout.write(`\x1B[2K\x1B[200D${query}[${animation}]`); else process.stdout.write(stringToWrite); }; return new Promise<string>(resolve => { rl.question(query, function (password: string) { process.stdout.write('\n'); resolve(password); rl.close(); }); }); } export function progressBar(length = 0, count = 0): void { const percent = count / length; const percent_handle = isNaN(percent) ? 0 : percent; const percent_fixed = Math.ceil(percent_handle * 100); const size = config.CONSOLESIZE - 4 - percent_fixed.toString().length; const bar = '.'.repeat(size * percent_handle).padEnd(size, " "); if (percent_handle === 1) { process.stdout.write(`\r[${bar}] ${percent_fixed}%`); process.stdout.write(`\r`); } else { process.stdout.write(`\r[${bar}] ${percent_fixed}%`); } } export function selectUEAS(ueas_options: uea[]) { const ueas = ueas_options.map(option => { return { ...option, choosed: false } as uea & { choosed?: boolean }; }); let index = 0; let toogle = false; const menu = () => { if (!!toogle) { process.stdout.moveCursor(0, -(ueas.length - 1)); process.stdout.cursorTo(0); process.stdout.clearScreenDown(); } else { toogle = true; } ueas.forEach(({ name, choosed }, i) => { let option = (i === index) ? `\x1b[35m> ${name}` : name; if (!!choosed) { option = `\x1b[4m${option}`; } process.stdout.write(`${option}\x1b[0m${i !== ueas.length - 1 ? '\n' : ''}`); }); } return new Promise<uea[]>((resolve, rejected) => { console.log('\x1b[34m?\x1b[0m %s', 'Choose what subjects are taken'); console.log('\x1b[31m!\x1b[0m %s', 'Press [Esc] to resume process'); readline.emitKeypressEvents(process.stdin); if (process.stdin.isTTY) { process.stdin.setRawMode(true); process.stdin.resume(); process.stdin.on('keypress', (_, { name, ctrl }) => { if (name === 'down' && index < ueas.length - 1) { ++index; } else if (name === 'up' && index > 0) { --index; } if (name === 'return') { ueas[index].choosed = !ueas[index].choosed; } if (name === 'escape' || (name === 'c' && ctrl)) { process.stdin.setRawMode(false); process.stdin.pause(); if (name === 'c' && ctrl) process.exit(0); process.stdout.moveCursor(0, -(ueas.length + 1)); process.stdout.cursorTo(0); process.stdout.clearScreenDown(); resolve(ueas.filter(option => { const choosed = option.choosed; delete option.choosed; return choosed; })); process.stdin.removeAllListeners('keypress'); } else { menu(); } }); menu(); } else { rejected('Idk man, you r ugly'); } }); } export async function tryDOM<T>(callback: () => Promise<T extends void ? never : T | null | undefined>, DOM: puppeteer.Page) { let response: T | null | undefined; let response_tries = 0; do { response = await callback(); if (response_tries === config.TRIES) { throw new Error("Element from DOM couldn´t load property"); } else if (!response) { await DOM.waitForTimeout(config.TIMEOUT); ++response_tries; } if (response instanceof Array && response.length < 1) { response = undefined; } } while (!response); return response; } export function timeParse(time: string): time | null { const [starts, ends] = time.slice(0, 13).trim().split(' - '); return time.trim().length > 1 ? { starts, ends } : null; } export function timeStringify(time: number): string { const timeDate = new Date(time); const hours = timeDate.getHours().toString(); const minutes = timeDate.getMinutes().toString(); return (hours.length < 2 ? 0 + hours : hours) + ':' + (minutes.length < 2 ? 0 + minutes : minutes); } export function selectTeachers(teachers_options: Set<string>) { let teachers = Array.from(teachers_options).map(name => { return { name, choosed: false } as { name: string; choosed?: boolean; }; }).sort(({ name: a }, { name: b }) => (a < b) ? -1 : ((a > b) ? 1 : 0)); let index = 0; let toogle = false; const menu = () => { if (!!toogle) { process.stdout.moveCursor(0, -(teachers.length - 1)); process.stdout.cursorTo(0); process.stdout.clearScreenDown(); } else { toogle = true; } teachers.forEach(({ name, choosed }, i) => { let option = (i === index) ? `\x1b[35m> ${name.capitalize()}` : name.capitalize(); if (!!choosed) { option = `\x1b[4m${option}`; } process.stdout.write(`${option}\x1b[0m${i !== teachers.length - 1 ? '\n' : ''}`); }); } return new Promise<string[]>((resolve, rejected) => { console.log('\x1b[34m?\x1b[0m %s', 'Choose what subjects are taken'); console.log('\x1b[31m!\x1b[0m %s', 'Press [Esc] to resume process'); readline.emitKeypressEvents(process.stdin); if (process.stdin.isTTY) { process.stdin.setRawMode(true); process.stdin.resume(); process.stdin.on('keypress', (_, { name, ctrl }) => { if (name === 'down' && index < teachers.length - 1) { ++index; } else if (name === 'up' && index > 0) { --index; } if (name === 'return') { teachers[index].choosed = !teachers[index].choosed; } if (name === 'escape' || (name === 'c' && ctrl)) { process.stdin.setRawMode(false); process.stdin.pause(); if (name === 'c' && ctrl) process.exit(0); process.stdout.moveCursor(0, -(teachers.length + 1)); process.stdout.cursorTo(0); process.stdout.clearScreenDown(); resolve(teachers.filter(({ choosed }) => choosed).map(({ name }) => name)); process.stdin.removeAllListeners('keypress'); } else { menu(); } }); menu(); } else { rejected('Idk man, you r ugly'); } }); } export function selectSchedule(schedules: [any, Map<string, ueaData>][]) { let index = 0; let toogle = false; let clear_area = 4; let byDay: weekByDay; let schedule_subjects: schedule['subjects_info']; const display = () => { if (!!toogle) { process.stdout.moveCursor(0, -clear_area); process.stdout.cursorTo(0); process.stdout.clearScreenDown(); clear_area = 4; } else { toogle = true; } const [, schedule] = schedules[index]; const min_date = Math.min.apply(Math, week.map(day => Math.min.apply(Math, Array.from(schedule) .filter(([, { [day]: _ }]) => _) .map(([, { [day]: _ }]) => Date.parse('01/01/1970 ' + _?.starts))))); const max_date = Math.max.apply(Math, week.map(day => Math.max.apply(Math, Array.from(schedule) .filter(([, { [day]: _ }]) => _) .map(([, { [day]: _ }]) => Date.parse('01/01/1970 ' + _?.ends))))); clear_area += schedule.size; schedule_subjects = Array.from(schedule).map(([subject, schedule_info]) => { console.log(`${schedule_info.key} | ${schedule_info.teacher.name.capitalize()} | ${subject}`); return { key: schedule_info.key, teacher: schedule_info.teacher.name.capitalize(), subject }; }); byDay = {} as weekByDay; for (let i = min_date; i < max_date; i += 1_800_000) { const curr_time = timeStringify(i); const week_time: perDay = { monday: null, tuesday: null, wednesday: null, thursday: null, friday: null, }; for (const day of week) { for (const [, { key, [day]: _ }] of schedule) { if (_) { if (curr_time >= _.starts && curr_time < _.ends) { week_time[day] = !week_time[day] ? key : key + '/' + week_time[day]; } } } } byDay[curr_time] = week_time ++clear_area; } console.table(byDay); const left = index !== 0 ? '<' : ''; const right = index !== schedules.length - 1 ? '>' : ''; process.stdout.write(`\x1b[34m?\x1b[0m Index ${left}[${index + 1}/${schedules.length}]${right}`); } return new Promise<schedule>((resolve, reject) => { console.log('\x1b[34m?\x1b[0m %s', 'Browse the best schedules posible'); console.log('\x1b[31m!\x1b[0m %s', 'Press [Enter] to choose schedule'); readline.emitKeypressEvents(process.stdin); if (process.stdin.isTTY) { process.stdin.setRawMode(true); process.stdin.resume(); process.stdin.on('keypress', (_, { name, ctrl }) => { if (name === 'right' && index < schedules.length - 1) { ++index; } else if (name === 'left' && index > 0) { --index; } if (name === 'return' || (name === 'c' && ctrl)) { process.stdin.setRawMode(false); process.stdin.pause(); if (name === 'c' && ctrl) process.exit(0); process.stdout.moveCursor(0, -(clear_area + 2)); process.stdout.cursorTo(0); process.stdout.clearScreenDown(); resolve({ subjects_info: schedule_subjects, hours: byDay }); process.stdin.removeAllListeners('keypress'); } else { display(); } }); display(); } else { reject('I still dunno man, fkg creep'); } }); }