Anystock not working with Anychart-NodeJS - node.js
I'm setting up a node.js server that renders static jpg/png images using Anychart.
It is possible for me to return the simple example pie charts in the examples but when I try to return the examples for AnyStock, I get some weird results.
The code should create and return a stock chart on the url: xx.xxx.xxx.xx:3000/insert.
Instead the code returns this chart without any graphs or candlesticks:
When I set the same graph up on a plain html site, I get following result:
The node.js code:
var fs = require('fs');
var express = require('express');
var app = express();
var path = require('path');
var router = express.Router();
app.get('/', function(req, res) {
var query = require('url').parse(req.url, true).query;
var stock_id = query.stock_id;
var type = query.type;
if (type == "insert") {
var JSDOM = require('jsdom').JSDOM;
var jsdom = new JSDOM('<head><script src="https://cdn.anychart.com/releases/8.9.0/js/anychart-core.min.js" type="text/javascript"></script><script src="https://cdn.anychart.com/releases/8.9.0/js/anychart-stock.min.js" type="text/javascript"></script></head><body><div id="container" style="width: 500px; height: 400px;"></div></body>', {
runScripts: 'dangerously'
});
var window = jsdom.window;
var anychart = require('anychart')(window);
var anychartExport = require('anychart-nodejs')(anychart);
var table, mapping, chart;
table = anychart.data.table();
table.addData([
['2015-12-24', 511.53, 514.98, 505.79, 506.40],
['2015-12-25', 512.53, 514.88, 505.69, 507.34],
['2015-12-26', 511.83, 514.98, 505.59, 506.23],
['2015-12-27', 511.22, 515.30, 505.49, 506.47],
['2015-12-28', 510.35, 515.72, 505.23, 505.80],
['2015-12-29', 510.53, 515.86, 505.38, 508.25],
['2015-12-30', 511.43, 515.98, 505.66, 507.45],
['2015-12-31', 511.50, 515.33, 505.99, 507.98],
['2016-01-01', 511.32, 514.29, 505.99, 506.37],
['2016-01-02', 511.70, 514.87, 506.18, 506.75],
['2016-01-03', 512.30, 514.78, 505.87, 508.67],
['2016-01-04', 512.50, 514.77, 505.83, 508.35],
['2016-01-05', 511.53, 516.18, 505.91, 509.42],
['2016-01-06', 511.13, 516.01, 506.00, 509.26],
['2016-01-07', 510.93, 516.07, 506.00, 510.99],
['2016-01-08', 510.88, 515.93, 505.22, 509.95],
['2016-01-09', 509.12, 515.97, 505.15, 510.12],
['2016-01-10', 508.53, 516.13, 505.66, 510.42],
['2016-01-11', 508.90, 516.24, 505.73, 510.40]
]);
mapping = table.mapAs();
mapping.addField('open', 1, 'first');
mapping.addField('high', 2, 'max');
mapping.addField('low', 3, 'min');
mapping.addField('close', 4, 'last');
mapping.addField('value', 4, 'last');
chart = anychart.stock();
chart.plot(0).ohlc(mapping).name('ACME Corp.');
chart.title('AnyStock Basic Sample');
chart.container('container');
chart.draw();
anychartExport.exportTo(chart, 'jpg').then(function(image) {
fs.writeFile('anychart.jpg', image, function(fsWriteError) {
if (fsWriteError) {
console.log(fsWriteError);
} else {
res.sendFile(path.join(__dirname + '/anychart.jpg'));
}
});
}, function(generationError) {
console.log(generationError);
});
} else if (type == "image") {
res.sendFile(path.join(__dirname + '/anychart.jpg'));
}
});
app.listen(3000);
I suspect there's something wrong with the way I includes the JS-files in the jsdom. If I exclude the two files in the jsdom, I get the same result..
Please let me know if you have any suggestions.
Can you please check and share the browser console messages? That tends to be the first troubleshooting step :)
So after a few days of waiting time, the AnyChart Support returned to me with the following answer for my question above:
we can't guarantee that this module will work as expected. It depends
on many other libraries that can't provide stable versions in
different OS.
Instead they recommend to use their Export Server solution which is different from what I was looking for.
Our setup is built on a LAMP-server, so we don't want to run another server just for a few images a day.
If any of you have a suggestion for a solution where I can export my AnyStock charts to JPG, PNG or GIFs please let me know.
Thanks :-)
Related
Organizing requires and moving them to document Top
I am organizing code in a app. The require statements are un-organized so I made this codemod to sort them and to add them on top of the page. The codemod works, almost perfect. I have some doubts: is this a ok approach, or is there a more correct way to use the API? how can I keep the empty line between the sourceStart (all the requires) and the rest of the source code? can a similar approach be used in ES6 imports? (ie to sort them with jscodeshift) My Initial code: var path = require('path'); var stylus = require('stylus'); var express = require('express'); var router = express.Router(); var async = require('async'); let restOfCode = 'foo'; My codemod: let requires = j(file.source).find(j.CallExpression, { "callee": { "name": "require" } }).closest(j.VariableDeclarator); let sortedNames = requires.__paths.map(node => node.node.id.name).sort(sort); // ["async", "express", "path", "stylus"] let sortedRequires = []; requires.forEach(r => { let index = sortedNames.indexOf(r.node.id.name); sortedRequires[index] = j(r).closest(j.VariableDeclaration).__paths[0]; // <- feels like a hack }); let sourceStart = j(sortedRequires).toSource(); let sourceRest = j(file.source).find(j.CallExpression, { "callee": { "name": "require" } }).closest(j.VariableDeclaration) .replaceWith((vD, i) => { // return nothing, it will be replaced on top of document }) .toSource(); return sourceStart.concat(sourceRest).join('\n'); // is there a better way than [].concat(string).join(newLine) ? And the result I got: var async = require('async'); var express = require('express'); var path = require('path'); var stylus = require('stylus'); var router = express.Router(); // <- I would expect a empty line before this one let restOfCode = 'foo';
is this a ok approach, or is there a more correct way to use the API? You shouldn't be accessing __paths directly. If you need to access all NodePaths, you can use the .paths() method. If you want to access the AST nodes, use .nodes(). E.g. the mapping would just be let sortedNames = requires.nodes()(node => node.id.name).sort(sort); how can I keep the empty line between the sourceStart (all the requires) and the rest of the source code? There isn't really a good way to do this. See this related recast issue. Hopefully this will become easier one day with CSTs. can a similar approach be used in ES6 imports? (ie to sort them with jscodeshift) Certainly. FWIW, here is my version (based on your first version): export default function transformer(file, api) { const j = api.jscodeshift; const sort = (a, b) => a.declarations[0].id.name.localeCompare( b.declarations[0].id.name ); const root = j(file.source); const requires = root .find(j.CallExpression, {"callee": {"name": "require"}}) .closest(j.VariableDeclaration); const sortedRequires = requires.nodes().sort(sort); requires.remove(); return root .find(j.Statement) .at(0) .insertBefore(sortedRequires) .toSource(); }; } https://astexplorer.net/#/i8v3GBENZ7
Cheerio scraping returning only two rows
So I tested my scraping on a static HTML file before adding it to my Node app. The problem is that it's not returning all the rows. On the site: $('#sport tr').length //Returns 13 In Cheerio: $('#sport tr').length //Returns 2 I'm stumped, here is the code I'm using. I've contained the URL as proof, so you can visit it yourself if you wish. I'm suspecting it's something to do with var $ = cheerio.load(html); however I'm not experienced in Cheerio to say outright that's the problem. var express = require('express'); var fs = require('fs'); var request = require('request'); var cheerio = require('cheerio'); var app = express(); app.get('/scrape', function(req, res){ var url = 'http://www.olbg.com/football.php'; var json = []; request(url, function(error, response, html){ if(!error){ var $ = cheerio.load(html); console.log($('#sport tr').length); var headers = []; $('#sport tr th').each(function(i, th) { var text = $(th).text(); if (text.trim() !== "") { headers[i] = text.replace(/[\t\n\r\s]/mgi, ''); } }); $('#sport tr').each(function(i, tr) { // skip if header if (!$(tr).is('th')) { var temp = {}; temp["Event"] = $(tr).find('td').eq(0).text().trim(); temp["TopSelection"] = $(tr).find('td').eq(1).text().trim(); temp["BookieOdds"] = $(tr).find('td').eq(2).text().trim(); temp["OLBGRating"] = $(tr).find('td').eq(3).find('img').length; if (temp["Event"] !== "" || temp["TopSelection"] !== ""){ json.push(temp); } } }); } // To write to the system we will use the built in 'fs' library. // In this example we will pass 3 parameters to the writeFile function // Parameter 1 : output.json - this is what the created filename will be called // Parameter 2 : JSON.stringify(json, null, 4) - the data to write, here we do an extra step by calling JSON.stringify to make our JSON easier to read // Parameter 3 : callback function - a callback function to let us know the status of our function fs.writeFile('output.json', JSON.stringify(json), function(err){ console.log('File successfully written!'); }) // Finally, we'll just send out a message to the browser reminding you that this app does not have a UI. res.send(json); }); }); app.listen("8081"); console.log("Magic happens on port 8081"); exports = module.exports = app;
The reason that you're not getting the expected result is because the (table) html on that page is mangled. If you look at the second <td> in the second <tr> of the table#sport, you'll see an "extra" </td>. This causes the <td> that the table#sport is inside to close (and an implicit closing of table#sport) on some parsers because that is the closest open <td>. So that is why the parser reports only 2 <tr>s instead of 13. The other <tr>s you're expecting are now outside of table#sport. Probably your best bet is to pass the html through an HTML tidying program/script (e.g. this one with the clean option enabled) first before passing it to cheerio. After that, your selector should return the elements you're probably expecting.
How ensure default data in NeDB?
I'm trying to use NeDB as storage for my data in node-webkit application. I have the single collection named config.db: var Datastore = require('nedb') , path = require('path') , db = new Datastore({ filename: path.join(require('nw.gui').App.dataPath, 'config.db') }); When user opens node-webkit application first time my config.db should have default data like: { color: "red", font: 'bold' ... } Does NeDB have option for providing default data if there are no yet? Or What it the best way to save it if config.db is empty (in case if user opens node-webkit application first time)?
As far as I know NeDB does not have an option to create initial data. I think the easiest way to achieve this is to simply query whether there is data. If counting documents returns 0, obviously the initial data have not yet been saved, so you should do this now. If you include this check in the startup code of your application, it will automatically initialize the data on first run, and afterwards simply do nothing.
I came across this question while looking for a similar solution. I thought I'd share what I ended up with (this is a module): var fs = require("fs"); module.exports = function (app) { var customizationService = app.service("customization"); fs.readFile("./db/customization", "utf8", function (err, data) { if (err) { return console.log(err); } if (data) { // Sweet, carry on } else { var customOptions = { SiteTitle: "VendoMarket", SiteTagline: "The freshest eCommerce platform around" }; // Save data to the locations service customizationService.create(customOptions); } }); }; And then in my app.js file: //-------------------------------------- // Initialize //-------------------------------------- var vendoInit = require("./src/init"); vendoInit(app); (My app.js file is at the base of my project, src is a folder next to it)
Node.JS - fs.exists not working?
I'm a beginner in Node.js, and was having trouble with this piece of code. var fs = require('fs'); Framework.Router = function() { this.run = function(req, res) { fs.exists(global.info.controller_file, function(exists) { if (exists) { // Here's the problem res.writeHead(200, {'Content-Type':'text/html'}); var cname = App.ucfirst(global.info.controller)+'Controller'; var c = require(global.info.controller_file); var c = new App[cname](); var action = global.info.action; c[action].apply(global.info.action, global.info.params); res.end(); } else { App.notFound(); return false; } }); } }; The problem lies in the part after checking if the 'global.info.controller_file' exists, I can't seem to get the code to work properly inside the: if (exists) { ... NOT WORKING } I tried logging out the values for all the variables in that section, and they have their expected values, however the line: c[action].apply(global.info.action, global.info.params); is not running as expected. It is supposed to call a function in the controller_file and is supposed to do a simple res.write('hello world');. I wasn't having this problem before I started checking for the file using fs.exists. Everything inside the if statement, worked perfectly fine before this check. Why is the code not running as expected? Why does the request just time out? Does it have something to do with the whole synchronous vs asynchronous thing? (Sorry, I'm a complete beginner) Thank you
Like others have commented, I would suggest you rewrite your code to bring it more in-line with the Node.js design patterns, then see if your problem still exists. In the meantime, here's something which may help: The advice about not using require dynamically at "run time" should be heeded, and calling fs.exists() on every request is tremendously wasteful. However, say you want to load all *.js files in a directory (perhaps a "controllers" directory). This is best accomplished using an index.js file. For example, save the following as app/controllers/index.js var fs = require('fs'); var files = fs.readdirSync(__dirname); var dotJs = /\.js$/; for (var i in files) { if (files[i] !== 'index.js' && dotJs.test(files[i])) exports[files[i].replace(dotJs, '')] = require('./' + files[i]); } Then, at the start of app/router.js, add: var controllers = require('./controllers'); Now you can access the app/controllers/test.js module by using controllers.test. So, instead of: fs.exists(controllerFile, function (exists) { if (exists) { ... } }); simply: if (controllers[controllerName]) { ... } This way you can retain the dynamic functionality you desire without unnecessary disk IO.
Synchronously load a dependency in node.js
I'm loading a node library in script and immediately after loading some customization that depends on that library: var somelib = require('somelib'); // some customizations made to // somelib's methods require('./somelib.custom'); somelib.SomeObject = ... ^ ReferenceError: somelib is not defined I keep getting an exception since the loading is done asynchronously and the second require happens before the first is namespaced correctly. What's a good way to resolve this? thanks. EDIT: My original code I'm trying to create a PNG image from json data using fabric.js node package (building on the article in package site). This is done by loading the server-side fabric canvas with JSON data that was originally generated on the client, then writing to a file stream: var path = require('path'); var fs = require('fs'); var fabric = require('fabric').fabric; require('./fabric.custom'); var canvas = fabric.createCanvasForNode(400, 400); var outfile = fs.createWriteStream("image.png"); var filepath = "/path/to/file.json"; fs.readFile(filepath, 'utf8', function(err, json) { canvas.loadFromJSON(json, function() { var stream = canvas.createPNGStream(); stream.on('data', function(chunk) { outfile.write(chunk); }); }); }); The "fabric.custom" file holds several custom fabric canvas objects that override some fabric prototype defaults. They work well on the client, and are needed to properly render the canvas. It looks something like this: fabric.TextBox = fabric.util.createClass(fabric.Text, { type: 'text-box', // more object specific stuff ... }); Thanks.
Rather than relying on side effects in a require to mutate your fabric object, how about having the fabric.custom.js file export the modified fabric, like so? fabric.custom.js: var fabric = require('fabric').fabric; fabric.myCustomMethod = function(){ ... } ... module.exports = fabric; // the modified fabric And in your main file: var path = require('path'); var fs = require('fs'); // var fabric = require('fabric').fabric; No need for this line anymore var modifiedFabric = require('./fabric.custom'); ... modifiedFabric.myCustomMethod( ... ); // should now be available