I am trying to add and remove event handler for onSelectionChanged.
If I defined the functions like below with export async function
export async function EnableCellHighlight(): Promise<void> {
console.log(1);
let _this: any = this;
await Excel.run(async function(context){
let workbook = context.workbook;
workbook.onSelectionChanged.add(CellHighlightHandler);
await context.sync();
});
}
export async function CellHighlightHandler(event: Excel.SelectionChangedEventArgs): Promise<void>{
await Excel.run(async (context) => {
let workbook = context.workbook;
let sheets = workbook.worksheets;
sheets.load("items");
await context.sync();
// clear previous meekou conditional format
await clearMeekouFormat();
// add new conditional format
let selection = workbook.getSelectedRange();
selection.load("rowIndex,columnIndex");
await context.sync();
let rowConditionalFormat = selection.getEntireRow().conditionalFormats.add(Excel.ConditionalFormatType.custom);
rowConditionalFormat.custom.format.fill.color = "red";
rowConditionalFormat.custom.rule.formula = `=ROW()= + ${(selection.rowIndex + 1)} + N("${AppConsts.Meekou}")`;
let columnConditionalFormat = selection.getEntireColumn().conditionalFormats.add(Excel.ConditionalFormatType.custom);
columnConditionalFormat.custom.format.fill.color = "red";
columnConditionalFormat.custom.rule.formula = `=Column()= + ${(selection.columnIndex + 1)} + N("${AppConsts.Meekou}")`;
await context.sync();
});
}
export async function clearMeekouFormat(): Promise<void> {
await Excel.run(async function (context) {
let workbook = context.workbook;
let worksheets = workbook.worksheets;
worksheets.load("items/name");
await context.sync();
for (let i = 0; i < worksheets.items.length; i++) {
let worksheet = worksheets.items[i];
let conditionalFormats = worksheet.getRange().conditionalFormats;
conditionalFormats.load("items/type");
await context.sync();
let total = conditionalFormats.items.length;
for(let j = total-1; j >=0; j --){
let conditionalFormat = conditionalFormats.items[j];
if(conditionalFormat.type == Excel.ConditionalFormatType.custom){
conditionalFormat.load("custom/rule/formula");
await context.sync();
if (conditionalFormat.custom.rule.formula.includes(AppConsts.Meekou)) {
conditionalFormat.delete();
}
}
}
}
await context.sync();
});
}
It will works correctly.
But, if I move above code to a class like below:
export class ExcelService {
private cellHighlightHandler = this.CellHighlightHandler;
//#region excel events
async enableCellHighlight(): Promise<void> {
console.log(1);
let _this: any = this;
await Excel.run(async function(context){
let workbook = context.workbook;
workbook.onSelectionChanged.add(_this.cellHighlightHandler);
await context.sync();
});
}
async disableCellHightlight(): Promise<void> {
let _this: any = this;
await this.clearMeekouFormat();
await Excel.run(async function(context){
let workbook = context.workbook;
workbook.onSelectionChanged.remove(_this.cellHighlightHandler);
await context.sync();
});
}
async CellHighlightHandler(event: Excel.SelectionChangedEventArgs): Promise<void>{
let _this: ExcelService = this;
await Excel.run(async (context) => {
let workbook = context.workbook;
let sheets = workbook.worksheets;
sheets.load("items");
await context.sync().catch(e => console.log(e));
// clear previous meekou conditional format
await _this.clearMeekouFormat().catch(e => console.log(e));
// add new conditional format
let selection = workbook.getSelectedRange();
selection.load("rowIndex,columnIndex");
await context.sync();
let rowConditionalFormat = selection.getEntireRow().conditionalFormats.add(Excel.ConditionalFormatType.custom);
rowConditionalFormat.custom.format.fill.color = "red";
rowConditionalFormat.custom.rule.formula = `=ROW()= + ${(selection.rowIndex + 1)} + N("${AppConsts.Meekou}")`;
let columnConditionalFormat = selection.getEntireColumn().conditionalFormats.add(Excel.ConditionalFormatType.custom);
columnConditionalFormat.custom.format.fill.color = "red";
columnConditionalFormat.custom.rule.formula = `=Column()= + ${(selection.columnIndex + 1)} + N("${AppConsts.Meekou}")`;
await context.sync();
});
}
/**
* clear all meekou conditional format
*/
async clearMeekouFormat(): Promise<void> {
await Excel.run(async function (context) {
let workbook = context.workbook;
let worksheets = workbook.worksheets;
worksheets.load("items/name");
await context.sync();
for (let i = 0; i < worksheets.items.length; i++) {
let worksheet = worksheets.items[i];
let conditionalFormats = worksheet.getRange().conditionalFormats;
conditionalFormats.load("items/type");
await context.sync();
let total = conditionalFormats.items.length;
for(let j = total-1; j >=0; j --){
let conditionalFormat = conditionalFormats.items[j];
if(conditionalFormat.type == Excel.ConditionalFormatType.custom){
conditionalFormat.load("custom/rule/formula");
await context.sync();
if (conditionalFormat.custom.rule.formula.includes(AppConsts.Meekou)) {
conditionalFormat.delete();
}
}
}
}
await context.sync();
});
}
//#endregion
}
The code will run stop await _this.clearMeekouFormat().catch(e => console.log(e));, but it did not throw any error
Do you have tried to remove the "catch(...)", like what you called in the first sample code "await _this.clearMeekouFormat();", Function "clearMeekouFormat(...)" actually does not have return expected promise?
Related
I am trying to improve my skills with async, await. So I am trying to make an app that collects the prices of different flights in different periods and then it decides in which period the plane ticket is cheapest for personal use.
const puppeteerExtra = require("puppeteer-extra");
const pluginStealth = require("puppeteer-extra-plugin-stealth");
puppeteerExtra.use(pluginStealth());
const PCR = require("puppeteer-chromium-resolver");
const howLongStart = 7;
const howLongEnd = 8;
const fromDate = new Date("2023-07-15");
const toDate = new Date("2023-08-31");
const airport = "PDL";
let tickets = [];
for (let i = 0; i < howLongEnd - howLongStart; i++) {
let howLong = howLongStart + i;
let tempFromDate = new Date("2023-07-15");
let tempFromD = new Date("2023-07-15");
let tempToDate = addDays(tempFromD, howLong);
async function ticketFirstMethod() {
const ticketFirst = await searchFlight(airport, tempFromDate, tempToDate);
tickets.push(ticketFirst);
}
ticketFirstMethod();
while (addDays(tempToDate, 1) <= toDate) {
tempFromDate = addDays(tempFromDate, 1);
tempToDate = addDays(tempToDate, 1);
async function ticketMethod() {
let ticket = await searchFlight(airport, tempFromDate, tempToDate);
tickets.push(ticket);
}
ticketMethod();
}
}
let lowestTicket;
let lowest = Number.POSITIVE_INFINITY;
let highest = Number.NEGATIVE_INFINITY;
let tmp;
for (let i = tickets.length - 1; i >= 0; i--) {
tmp = tickets[i][0];
if (tmp < lowest) {
lowest = tmp;
lowestTicket = tickets[i];
}
if (tmp > highest) highest = tmp;
}
console.log(lowestTicket);
function addDays(date, days) {
date.setDate(date.getDate() + days);
return date;
}
async function searchFlight(airport, tempFromDate, tempToDate) {
const stats = await PCR();
const browser = await puppeteerExtra.launch({
executablePath: stats.executablePath,
headless: false,
});
const page = await browser.newPage();
await page.goto(
"https://www.pelikan.cz/cs/letenky/T:1,P:4000E_0_0,CDF:PRGMUCFRATXLVIE,CDT:C" +
airport +
",R:1,DD:" +
tempFromDate.getFullYear +
"_" +
tempFromDate.getMonth +
"_" +
tempFromDate.getDay +
",DR:" +
tempToDate.getFullYear +
"_" +
tempToDate.getMonth +
"_" +
tempToDate.getDay +
"/",
{ waitUntil: "networkidle2", timeout: 0 }
);
const cheapestPrice = await page.waitForSelector(
"#flight-10000 > div:nth-child(1) > flights-flight:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > div:nth-child(1) > div:nth-child(1) > div:nth-child(3)"
);
const price = await page.evaluate((el) => el.textContent, cheapestPrice);
const priceOnly = price.replace(/\D/g, "");
const ticket = [priceOnly, page.url()];
await browser.close();
return ticket;
}
I have tried to put here an example of the code.
Can anyone please help me?
EXPECTED
Firstly I choose a period from when to when it should be searching for the ticket. Then I call searchFlight with this period of time to search for the ticket. The main thread will wait for the function to be processed and then the ticket is pushed to tickets.
BEHAVIOUR
The main thread will not wait and it continous so there is undefined ticket pushed to tickets.
I was trying to use the then method on the line where I am calling searchFlight function. In then method I put tickets.push(ticket). But that didn't work.
I was trying to search for fix but because I dont understand await, async that much I could not fix my code.
First off, remove the (async () => { .... }() wrapper. That's superfluous and getting in the way. The parent function is already async so the wrapper is not needed.
Then, searchFlight is async so you need to await its result where you are calling it. And, you'll need to make it's parent function async so you can use that await.
const ticket = await searchFlight(airport, tempFromDate, tempToDate);
Then, you have to actually return a result from inside of searchFlight. Right now, you have no return result at the top level of that function.
I would suggest you do that by not mixing await and .then(). Just use await like this:
async function searchFlight(airport, tempFromDate, tempToDate){
const stats = await PCR();
const browser = await puppeteerExtra.launch({
executablePath: stats.executablePath,
headless: false
});
const page = await browser.newPage()
await page.goto("...", {waitUntil: "networkidle2", timeout: 0})
const cheapestPrice = await page.waitForSelector('...');
const price = await page.evaluate(el => el.textContent, cheapestPrice);
const priceOnly = price.replace(/\D/g, "");
const ticket = [priceOnly, page.url()];
await browser.close()
return ticket;
}
And, please eliminate any use of var. One should only be using const or let in modern Javascript.
When I apply the conditional formats, I store it in the conditionalFormatCols and conditionalFormatRows variables. I then use the delete() method on these variables, but this doesn't delete the conditional formatting on the cells. How do I delete conditional formatting so it no longer shows up in the workbook?
let eventResult;
let navAidIsOn = false;
let conditionalFormatCols;
let conditionalFormatRows;
async function navAid() {
await Excel.run(async (context) => {
if (!navAidIsOn) {
applyConditionalFormatting();
const sheet = context.workbook.worksheets.getActiveWorksheet();
eventResult = sheet.onSelectionChanged.add(navAidListener);
navAidIsOn = !navAidIsOn;
} else {
turnNavAidOff();
}
});
}
async function turnNavAidOff(){
await Excel.run(eventResult.context ,async (context) => {
eventResult.remove()
Here I try to use the delete() method
conditionalFormatCols.delete();
conditionalFormatRows.delete();
await context.sync();
navAidIsOn = !navAidIsOn;
});
}
async function navAidListener() {
applyConditionalFormatting();
}
async function applyConditionalFormatting() {
await Excel.run(async (context) => {
const sheet = context.workbook.worksheets.getActiveWorksheet();
let ranges;
let rangeArray = [];
await context.sync();
let columns;
let rows;
ranges = context.workbook.getSelectedRanges();
await context.sync();
ranges.load("address");
await context.sync();
let rangeStrs = ranges.address.split(",");
rangeStrs.forEach((rangeStr) => {
rangeArray.push(sheet.getRange(rangeStr).load("columnIndex, rowIndex, rowCount, columnCount"));
});
await context.sync();
rangeArray.forEach(async (range) => {
columns = sheet.getRangeByIndexes(0, range.columnIndex, 1048576, range.columnCount);
rows = sheet.getRangeByIndexes(range.rowIndex, 0, range.rowCount, 16384);
await context.sync();
conditionalFormatCols = columns.conditionalFormats.add(Excel.ConditionalFormatType.custom);
conditionalFormatCols.custom.rule.formula = "TRUE";
conditionalFormatCols.custom.format.fill.color = "#D2F0E0";
conditionalFormatRows = rows.conditionalFormats.add(Excel.ConditionalFormatType.custom);
conditionalFormatRows.custom.rule.formula = "TRUE";
conditionalFormatRows.custom.format.fill.color = "#D2F0E0";
await context.sync();
});
});
}
I am trying to web scrape with puppetter, currently the following code works but I think there can be optimizations made one of which I think is to only use one puppetter instance. But I don't really know how to do that. Can anyone help me?
This is the working but slow original:
const puppeteer = require('puppeteer');
async function scrapeProduct(url) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(url);
const [el] = await page.$x('xpath of element');
const txt = await el.getProperty('textContent');
const rawTxt = await txt.jsonValue();
browser.close();
return rawTxt;
}
async function something() {
var some_varible = the length of some array;
process.setMaxListeners(some_varible);
for (var i = 0; i < some_varible; i++) {
var target = some_array[i].Name
var url = 'somewebsite' + target;
console.log(target + ": " + await scrapeProduct(url));
}
}
something();
This is my pathetic attempt at not using multiple instances of puppeteer: (Does not work)
const puppeteer = require('puppeteer');
async function scrapeProduct(url, page) {
await page.goto(url);
const [el] = await page.$x('xpath of element');
const txt = await el.getProperty('textContent');
const rawTxt = await txt.jsonValue();
return rawTxt;
}
async function something() {
var some_varible = the length of some array;
process.setMaxListeners(some_varible);
const browser = await puppeteer.launch();
const page = await browser.newPage();
for (var i = 0; i < some_varible; i++) {
var target = some_array[i].Name
var url = 'somewebsite' + target;
console.log(target + ": " + await scrapeProduct(url, page));
}
browser.close();
}
something();
async function filterusers(users){
let usersfiltered=[]
for(var i = 0;i < users.length; i++){
let userref = db.collection('usernames').doc(users[i]);
let getDoc = userref.get()
.then(doc => {
if (doc.exists) {
usersfiltered.push(users[i])
}
})
}
return await usersfiltered;
}
filterusers(users).then(console.log);
i am looking to wait for the filtered result but it always prints blank array ie before the result is being returned.
async function filterusers(users){
let usersfiltered=[]
// You should use let or const instead of var.
for(let i = 0;i < users.length; i++){
// I believe getting doc needs await.
let userref = await db.collection('usernames').doc(users[i]);
await userref.get()
.then(doc => {
if (doc.exists) {
usersfiltered.push(users[i])
}
})
}
return usersfiltered;
}
filterusers(users).then(console.log);
First, you have to turn the array of users into an array of Promises ( asynchronous operations) by using Array.map:
const checkUserPromises = users.map((user) => {
const userref = db.collection("usernames").doc(user);
return userref.get().then((doc) => {
if (doc.exists) {
return user;
} else {
return null;
}
});
});
Then, you need to await these promises with Promises.all:
const checkedUsers = await Promise.all(checkUserPromises);
Lastly, you may want to filter out the users that are not existing:
const existingUsers = checkedUsers.filter((user) => user !== null);
await should be with Promise
async function filterusers(users) {
let usersfiltered = [];
for (let i = 0; i < users.length; i++) {
let userref = db.collection("usernames").doc(users[i]);
let getDoc = await userref.get();
if (getDoc.exists) {
usersfiltered.push(users[i]);
}
}
return usersfiltered;
}
let filterdUsers = filterusers(users);
console.log(filterdUsers);
Firstly, do not mix async/await with .then.
Secondly, use new ES6 for loops, to make the code work properly with async/await.
async function filterusers(users) {
let usersfiltered = [];
filterusers(users).then(console.log);
for (const user of users) {
let userref = db.collection('usernames').doc(user);
const doc = await userref.get();
if (doc.exists) {
usersfiltered.push(user);
}
}
return await usersfiltered;
}
Solved it myself by moving the await to before userref.get()
async function filterusers(users){
let usersfiltered=[]
for(var i = 0;i < users.length; i++){
let userref = db.collection('usernames').doc(users[i]);
let getDoc = await userref.get()
.then(doc => {
if (doc.exists) {
usersfiltered.push(users[i])
}
})
}
return usersfiltered;
}
filterusers(users).then(console.log);
Working on scraping TV episodes from IMDb (Breaking Bad in the example below). The problem is when implementing the for loop, only the first iteration of j is returned.
My assumption is the return statement is exiting the loop but I'm unsure how to fix the problem.
const fetch = require('node-fetch');
const cheerio = require('cheerio');
const searchUrl = 'https://www.imdb.com/find?s=tt&ttype=tv&ref_=fn_tv&q=';
const movieUrl = 'https://www.imdb.com/title/';
async function getEpisodes(searchTerm) {
//const imdbID = await getID(searchTerm);
//const numSeasons = await getSeasons(imdbID);
const imdbID = 'tt0903747';
const numSeasons = 5;
const episodes = [];
for (let j = 1; j <= numSeasons; j++) {
return fetch(`${movieUrl}${imdbID}/episodes?season=${j}`)
.then(response => response.text())
.then(body => {
const $ = cheerio.load(body);
$('div[itemProp="episodes"]').each(function (i, element) {
const airdate = $(element).find('.airdate').text().trim();
const episodeTitle = $(element).find('a[itemProp="name"]').text().trim();
const votes = $(element).find('.ipl-rating-star__total-votes').text().trim().match(/\(([^)]+)\)/)[1];
const rating = $(element).find('.ipl-rating-star ').find('.ipl-rating-star__rating').text().trim().slice(0, 3);
episode = {
season: j,
episodeTitle,
airdate,
votes,
rating
};
episodes.push(episode);
});
return episodes; //Only season 1 is returned.
});
}
}
Let's rewrite the function using async await style. This way we make sure we fire fetch numSeasons times, await all of them, and process them one by one.
async function processResponse(response, season) {
const body = await response.text();
const $ = cheerio.load(body);
let episodes = [];
$('div[itemProp="episodes"]').each(function (i, element) {
const airdate = $(element).find('.airdate').text().trim();
const episodeTitle = $(element).find('a[itemProp="name"]').text().trim();
const votes = $(element).find('.ipl-rating-star__total-votes').text().trim().match(/\(([^)]+)\)/)[1];
const rating = $(element).find('.ipl-rating-star ').find('.ipl-rating-star__rating').text().trim().slice(0, 3);
episode = {
season,
episodeTitle,
airdate,
votes,
rating
};
episodes.push(episode);
});
return episodes;
}
async function getEpisodes(searchTerm) {
//const imdbID = await getID(searchTerm);
//const numSeasons = await getSeasons(imdbID);
const imdbID = 'tt0903747';
const numSeasons = 5;
let promises = [];
for (let j = 1; j <= numSeasons; j++) {
promises.push(fetch(`${movieUrl}${imdbID}/episodes?season=${j}`));
}
const responses = await Promise.all(promises);
return responses.reduce((accumulator, response, index) => {
return accumulator.concat(await processResponse(response, index + 1));
}, []);
}