[Flow] Corona Virus - Map

I've been tracking the data in Excel for a few days since I wanted to track confirmed cases by country population (e.g. cases as a % of population). I also wanted charts that used log10 Y axis rather than linear. These are quite revealing.

Started to convert that to Node-RED just as an interesting exercise but then discovered https://www.worldometers.info/ which has all sorts of interesting information including world population and lots of interesting COVID-19 stats.

Just for fun, if you want to extract the world population stats from their website, here is an example flow. Note that this renames some of the countries so that they fit with the WHO data from the Johns Hopkins GitHub repo that is used in the above example.

[{"id":"2507da13.824146","type":"inject","z":"769ea95b.9e7518","name":"","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":135,"y":280,"wires":[["3755e11c.d6bb5e"]],"l":false},{"id":"3755e11c.d6bb5e","type":"http request","z":"769ea95b.9e7518","name":"GET Population by Country","method":"GET","ret":"txt","paytoqs":false,"url":"https://www.worldometers.info/world-population/population-by-country/","tls":"","persist":false,"proxy":"","authType":"","x":300,"y":280,"wires":[["3d0f890f.c5d366"]]},{"id":"4067a8a5.2acd68","type":"debug","z":"769ea95b.9e7518","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":810,"y":280,"wires":[]},{"id":"3d0f890f.c5d366","type":"cheerio-function","z":"769ea95b.9e7518","name":"","func":"// Get table title\nconst tblTitle = msg.payload=$('.content-inner > div > h1').text()\nconst tblDesc = msg.payload=$('.content-inner > div > p').html()\nconst tblSrc = msg.payload=$('.source-table').html()\n\nconst tableSelector = '#example2'\n\n// msg.payload=$('#example2').text();\n// return msg;\n\nconst options = {\n    rowForHeadings: 0,  // extract th cells from this row for column headings (zero-based)\n    ignoreHeadingRow: true, // Don't tread the heading row as data\n    ignoreRows: [],\n}\nconst jsonReponse = []\nconst jsonReponseObj = {}\nconst columnHeadings = []\n\n$(tableSelector).each(function(i, table) {\n    var trs = $(table).find('tr')\n\n    // Set up the column heading names\n    getColHeadings( $(trs[options.rowForHeadings]) )\n\n    // Process rows for data\n    $(table).find('tr').each(processRow)\n})\n\nmsg.payload = {\n    title: tblTitle,\n    description: tblDesc,\n    source: tblSrc,\n    columnHeadings: columnHeadings,\n    rows: jsonReponse,\n    countries: jsonReponseObj,\n}\n\nreturn msg\n\nfunction getColHeadings(headingRow) {\n    const alreadySeen = {}\n\n    $(headingRow).find('th').each(function(j, cell) {\n        let tr = $(cell).text().trim()\n\n        if ( alreadySeen[tr] ) {\n            let suffix = ++alreadySeen[tr]\n            tr = `${tr}_${suffix}`\n        } else {\n            alreadySeen[tr] = 1\n        }\n\n        columnHeadings.push(tr)\n    })\n}\n\nfunction processRow(i, row) {\n    const rowJson = {}\n\n    if ( options.ignoreHeadingRow && i === options.rowForHeadings ) return\n    // TODO: Process options.ignoreRows\n\n    $(row).find('td').each(function(j, cell) {\n        let mycell = $(cell).text().trim()\n        if ( mycell === 'United States' ) mycell = 'US'\n        else if ( mycell === 'South Korea' ) mycell = 'Korea, South'\n        else if ( mycell === 'Czech Republic (Czechia)' ) mycell = 'Czechia'\n        else if ( mycell === 'Taiwan' ) mycell = 'Taiwan*'\n        else if ( mycell === 'Congo' ) mycell = 'Congo (Kinshasa)'\n        else if ( mycell === 'Côte d\\'Ivoire' ) mycell = 'Cote d\\'Ivoire'\n        else if ( mycell === 'Réunion' ) mycell = 'Reunion'\n        rowJson[ columnHeadings[j] ] = mycell\n    })\n    \n    // Skip blank rows\n    if (JSON.stringify(rowJson) !== '{}') {\n        jsonReponse.push(rowJson)\n        jsonReponseObj[rowJson['Country (or dependency)']] = (rowJson)\n    }\n}\n\n//EOF","outputs":1,"noerr":0,"x":455,"y":280,"wires":[["9bda43ae.f39a1"]],"l":false},{"id":"9bda43ae.f39a1","type":"change","z":"769ea95b.9e7518","name":"flow.populations (file)","rules":[{"t":"set","p":"#:(file)::populations","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":600,"y":280,"wires":[["4067a8a5.2acd68"]]}]

Sorry, should have said that the function node actually uses the cheerio-function node which you would need to install. This gives you a special function node with the Cheerio library fully exposed for advanced extract and manipulation of HTML.

2 Likes