Skip to content

Commit 7e26403

Browse files
committed
feat: replaced wopr with blessed stream implementation
1 parent 516db55 commit 7e26403

File tree

8 files changed

+261
-232
lines changed

8 files changed

+261
-232
lines changed

app.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const express = require('express'),
44
util = require('./bin/util'),
55
axios = require('axios'),
66
covid19 = require('./lib/cli'),
7-
covid19GFX = require ('./lib/cli/gfx'),
7+
covid19GFX = require('./lib/cli/gfx'),
88
pkg = require('./package.json'), // package.json info
99
apiBaseURL = "https://corona.lmao.ninja", // NovelCOVID API
1010
port = process.env.port || 7070; // set port
@@ -132,7 +132,7 @@ app.get('/history/:country/:chartType(cases|deaths)?', async (req, res, next) =>
132132

133133
// historical chart by country
134134
app.get('/history/charts/:country', async (req, res, next) => {
135-
const userAgent = req.headers['user-agent'],
135+
const userAgent = req.headers['user-agent'],
136136
countryData = req.params.country,
137137
chartType = req.params.chartType || 'cases',
138138
summary = await axios.get(`${apiBaseURL}/countries/${countryData}`),
@@ -148,7 +148,6 @@ app.get('/history/charts/:country', async (req, res, next) => {
148148
s.active, s.critical, s.casesPerOneMillion,
149149
s.updated, h, chartType, s.countryInfo
150150
)
151-
152151
return null;
153152
}
154153
return next();

lib/cli/gfx/index.js

Lines changed: 33 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
const
22
blessed = require('blessed'),
3-
fs = require('fs'),
4-
contrib = require('blessed-contrib'),
5-
present = require('./presenter'),
63
pkg = require('../../../package.json'),
7-
xml2js = require('xml2js'),
84
say = require('../../sayings/threads.json'),
5+
serverUtil = require('./server-util')
6+
template = require ('./template.js'),
97
ts = Date.now(),
108
date_ob = new Date(ts),
119
date = date_ob.getDate(),
@@ -24,10 +22,8 @@ const
2422
gcashNum = '+639176462753',
2523
ansiBMC = '`(Buy Me A Coffee)` '+bmcurl,
2624
ansiTwitter = twitterhandle+space+twitterhashtag,
27-
ansiGCash = '(GCash) '+gcashNum,
28-
XMLTemplate = '/template.xml';
25+
ansiGCash = '(GCash) '+gcashNum;
2926

30-
let cachedTemplate = null
3127

3228
// random sayings
3329
const randomSay = () => {
@@ -42,7 +38,6 @@ const patchBlessed = () => {
4238

4339
patchBlessed();
4440

45-
4641
exports.historyCountryTracker = (req, res, n, c, tC, d, tD, r, a, cl, cPOM, u, h, chartType, countryInfo) => {
4742
const name = n, cases = c, todayCases = tC,
4843
deaths = d, todayDeaths = tD, recovered = r,
@@ -52,101 +47,37 @@ exports.historyCountryTracker = (req, res, n, c, tC, d, tD, r, a, cl, cPOM, u, h
5247
dates = Object.keys(h.timeline[chartType]),
5348
from = dates[0],
5449
to = dates[dates.length - 1],
55-
parser = new xml2js.Parser(),
56-
builder = new xml2js.Builder(),
5750
tableFooter = randomSay(),
5851
defaultfooter = ansiBMC+ansiTwitter+br+br,
59-
specialfooter = ansiGCash+br+ansiBMC+ansiTwitter+br+br;
60-
61-
const parseXML = (data) => {
62-
parser.parseString(data, function (err, result) {
63-
64-
const casesDataTable =
65-
'\n' +
66-
` ,${cases},${deaths},${recovered},${active},${casesPerOneMillion}\n` +
67-
' ,Today Cases,Today Deaths,Critical,Mortality %,Recovery %\n' +
68-
` ,${todayCases},${todayDeaths},${critical},${mortalityPercentage},${recoveredPercentage}\n`;
69-
70-
// header
71-
result.document.page[0].item[0].markdown[0].markdown[0] = header+br+tagline
72-
73-
// map
74-
result.document.page[0].item[1].map[0].markers[0].m[0].$ = {
75-
lat: countryInfo.lat,
76-
lon: countryInfo.long,
77-
char: '\u24E7'+` ${name}`,
78-
color: 'magenta',
79-
};
80-
81-
// Doughnut/donut
82-
result.document.page[0].item[4].donut[0].data[0].m[0].$={
83-
color: 'red',
84-
percent: parseFloat(mortalityPercentage).toFixed(2),
85-
label: 'Mortality',
86-
};
87-
88-
result.document.page[0].item[4].donut[0].data[0].m[1].$={
89-
color: 'green',
90-
percent: parseFloat(recoveredPercentage).toFixed(2),
91-
label: 'Recovery',
92-
};
93-
94-
// bar graph
95-
result.document.page[0].item[3].bar[0].$['data-data'] = `${cases},${deaths},${recovered},${active}`;
96-
97-
// line graph
98-
result.document.page[0].item[5].line[0].label = `Cases from ${from} to ${to}`
99-
result.document.page[0].item[5].line[0].data[0].m[0].$ = {
100-
title: 'Cases',
101-
'style-line': 'blue',
102-
x:Object.keys(h.timeline.cases).join(','),
103-
y:Object.values(h.timeline.cases).join(','),
104-
};
105-
106-
result.document.page[0].item[5].line[0].data[0].m[1].$ = {
107-
title: 'Deaths',
108-
'style-line': 'red',
109-
x:Object.keys(h.timeline.deaths).join(','),
110-
y:Object.values(h.timeline.deaths).join(','),
111-
};
112-
113-
result.document.page[0].item[5].line[0].data[0].m[2].$ = {
114-
title: 'Recovered',
115-
'style-line': 'green',
116-
x:Object.keys(h.timeline.recovered).join(','),
117-
y:Object.values(h.timeline.recovered).join(','),
118-
};
119-
120-
// Historical data table
121-
result.document.page[0].item[2].table[0].label = 'Historical data as of '+asof.toLocaleString()+' [Date:'+currentdate+']'
122-
result.document.page[0].item[2].table[0]['data-data'][0] = casesDataTable;
123-
124-
// footer
125-
result.document.page[0].item[6].markdown[0].markdown[0] = (n.toLowerCase() == 'philippines') ? tableFooter+br+specialfooter+br+source+br+repo : tableFooter+br+defaultfooter+br+source+br+repo;
126-
127-
present(req, res, builder.buildObject(result), function(err) {
128-
if (err) console.log(new Error().stack);
129-
if (err) return contrib.serverError(req, res, err);
130-
});
131-
132-
});
133-
}
134-
135-
const readXML = () => {
136-
fs.readFile(__dirname + XMLTemplate, (err, data) => {
137-
cachedTemplate = data
138-
parseXML(data)
139-
});
140-
}
141-
142-
// read XML template from file or cache
143-
if(!cachedTemplate) return readXML ()
144-
145-
try {
146-
return parseXML(new Buffer.from(cachedTemplate.toString()))
147-
} catch (e) {
148-
return readXML ()
149-
}
150-
52+
specialfooter = ansiGCash+br+ansiBMC+ansiTwitter+br+br,
53+
defaultHeader = header+br+tagline,
54+
footer = (n.toLowerCase() == 'philippines') ? tableFooter+br+specialfooter+br+source+br+repo : tableFooter+br+defaultfooter+br+source+br+repo;
15155

56+
// load template with data
57+
// serverUtil.loadTemplate(template, {jsonData}, callback)
58+
serverUtil.loadTemplate(template, {
59+
name,
60+
cases,
61+
deaths,
62+
recovered,
63+
active,
64+
casesPerOneMillion,
65+
todayCases,
66+
todayDeaths,
67+
critical,
68+
mortalityPercentage,
69+
recoveredPercentage,
70+
countryInfo,
71+
from,
72+
to,
73+
currentdate,
74+
asof,
75+
h,
76+
defaultHeader,
77+
footer,
78+
}, (screen) => {
79+
res.send(screen+'\r\n'+'\033[?25h')
80+
})
81+
15282
};
83+

lib/cli/gfx/presenter.js

Lines changed: 0 additions & 57 deletions
This file was deleted.

lib/cli/gfx/server-util.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/** Modified script from blessed-contrib server-util */
2+
const blessed = require('blessed'),
3+
contrib = require('blessed-contrib'),
4+
Stream = require('stream');
5+
6+
function OutputBuffer(options) {
7+
this.isTTY = true;
8+
this.columns = options.cols;
9+
this.rows = options.rows;
10+
this.write = function(s) {
11+
s = s.replace('\x1b8', ''); //not clear from where in blessed this code comes from. It forces the terminal to clear and loose existing content.
12+
options.stream.write(s)
13+
};
14+
15+
this.on = function() {};
16+
}
17+
18+
function InputBuffer() {
19+
this.isTTY = true;
20+
this.isRaw = true;
21+
this.emit = function() {};
22+
this.setRawMode = function() {};
23+
this.resume = function() {};
24+
this.pause = function() {};
25+
this.on = function() {};
26+
}
27+
28+
29+
createScreen = (opt = {}) => {
30+
const output = new OutputBuffer({stream: opt.stream, cols: 250, rows: 50});
31+
const input = new InputBuffer(); //required to run under forever since it replaces stdin to non-tty
32+
const program = blessed.program({output: output, input: input});
33+
34+
let screen = blessed.screen({program: program});
35+
return screen
36+
}
37+
38+
39+
loadTemplate = (gridTemplateClass, json, callback) => {
40+
41+
blessed.Screen.global = null
42+
blessed.Program.global = null
43+
44+
const
45+
customStream = new Stream.Transform()
46+
screen = createScreen({stream: customStream}),
47+
grid = new contrib.grid({rows: 12, cols: 16, screen: screen});
48+
49+
let result = []
50+
51+
if(!gridTemplateClass) throw new Error('No template loaded')
52+
53+
// parse template
54+
gridTemplateClass.load(grid, json)
55+
56+
customStream._transform = function (chunk,encoding,done) {
57+
result.push(chunk.toString())
58+
done()
59+
}
60+
61+
customStream._final = () => {
62+
callback(result.join(''))
63+
}
64+
65+
customStream.on('error', (e) => {
66+
// do nothing here
67+
})
68+
69+
screen.render()
70+
71+
setTimeout(() => {
72+
customStream.end()
73+
}, 1000)
74+
}
75+
76+
module.exports = {
77+
createScreen,
78+
loadTemplate,
79+
}

0 commit comments

Comments
 (0)