Create "batch" request using ExtJS 4.1 REST Proxy - json

I've got two model/proxy/stores I'm concerned with Questions and Choices. Both get data from a REST server as JSON. My process currently goes like this:
// load numQuestions records from store.Questions
var qs = Ext.getStore('Question');
//... loadmask, etc.
qs.load({
scope : this,
params : {
limit : numQuestions
},
callback : function() {
this.createQuestionCards(numQuestions);
}
});
Once I have the Questions, I loop through and fetch the Choices that are relevant to each Question like:
for ( i = 0; i < numQuestions; i++) {
// ... misc ...
Assessor.questionChoices[i] = qs.getAt(i).choices();
// ...misc...
},
This works well, except that it makes an XMLHTTPRequest for every loop iteration. With minimum response times in the 0.15 sec area, that is fine for N < ~40. Once the numbers get to 200, which should be a common use case, the delay is nasty.
How do I get ExtJS to "batch" the requests and send them after the loop body? For example:
var choiceBatch = qs.createBatch();
for ( i = 0; i < numQuestions; i++) {
// ... misc ...
Assessor.questionChoices[i] = choiceBatch.getAt(i).choices();
// ...misc...
};
choiceBatch.execute();

The Ext.data.proxy.Rest has a config option batchActions and since it's basically an AjaxProxy with different methods it will probably work in the same way as the AjaxProxy.

Since I am not getting clear answer about restful batch with multipart...
testing on my own with batchActions=true in Ext.data.proxy.Rest v4.2.1 result that batch is only within the same store and HTTP method. (batchActions default to false for the REST)
That means if there is 200 post & 1 delete and you call store.sync(), it will batch into 2 request, the POST request body will be wrapped with an array of records instead of single record.
I am looking for if it can batch all stores with all GET, POST, PUT and DELETE by using multipart/mixed but the result is negative. (check out OData Batch Processing)
Regarding the OP, what you looking for is the model associations. Once you create Questions and Choices Ext model and let the server respond with nested json data (So the Questions contain the child Choices embedded in a request) Ext will create question record along with question.choices() child store automatically.

Related

CFML/JS Creating nested JSON/Array from plain SQL

I would like to build a tree structre from a plain json array.
The regular depth is approx. 6/7 (max 10) and has about 5,000 records.
My input json looks like this
[3,"01","GruppenAnfangHook",1,0,1,0,"Installationsmaterial",1.0,"",null,null,0.0,-1.0,null,803.0300,803.0300,0.00000,1,1]
[5,"01.001","JumboAnfangHook",3,0,3,0,"MBS Wandler 1.000",6.0,"St",null,null,0.0,-6.0,0.0000,336.7800,56.1300,0.00000,2,2],
[38,"","ArtikelHook",3,5,3,0,"ASK 61.4 1000/5A 5VA Kl.1 Preis lt. Hr. K am 16.05.17",6.0,"stk",6.0,6.0,0.0,-6.0,null,21.5000,21.5000,0.00000,3,3]
But I need it structured with childrens like that
{"0":34,1":"02.003",2":"JumboBegin","3":26,"4":0, "5":26,"6":0, "children":[
{ "0":36,"1":"", "2":"Article","3":26,"4":34,"5":26,6:"0", 7: "Artikel"},
{ "0":35,"1":"", "2":"JumboEnd",3":26,"4":34, "5":26, 6:"0",7:"Stunde"}
]}
My best approach so far was to build the child-structure with the following JS function in the frontend
function nest(data, parentId = 0) {
return data.reduce((r, e) => {
let obj = Object.assign({}, e)
if (parentId == e[4]) {
let children = nest(data, e[0])
if (children.length) obj.children = children
r.push(obj)
}
return r;
}, [])}
It works well and fast (< 1s) with a small (<500) amount of records but my browser begins to freeze at 2,000 and above.
My thought was it is too much data and so I tried to solve it in the CFML backend.
Due to I'm new with recursion, Ben Nadels Blog helped me alot, so I used his post about recursion and created a working example with sample data.
q = queryNew("id,grpCol,jumCol,leiCol,name,typ,order");
The grpCol is level 0, up to 5 groups can be placed in each other, in those groups can be placed two kinds of containers (jumCol and leiCol), they can be placed in each other to, but not in themselfs.
But now I am failing to convert it to a array of structures with child members. The structure of the HTML tree generated as output in the example is exactly what I want for my frontend JSON.
Because of the recursion I don't get, how to store it in an array outside of the function.
My goal is a final return as serzializeJson(array).

Extract Single Data From JSON URL for Dashing Dashboard

I am trying to show the "sgv" value on a Dashing / Smashing dashboard widget. Ultimately I would also like to show the "direction" value as well. I am running into problems pulling that precise value down which changes every 3 to 5 minutes. I have already been able to mirror the exact string using the following:
require 'net/http'
require 'rest-client'
require 'json'
url = "https://dnarnianbg.herokuapp.com/api/v1/entries/current.json"
response = RestClient.get(url)
JSON.parse(response)
# :first_in sets how long it takes before the job is first run. In this case, it is run immediately
current_nightscout = 0
SCHEDULER.every '5m' do
last_nightscout = current_nightscout
current_nightscout = response
send_event('nightscout', { current: current_nightscout, last: last_nightscout })
end
I have also searched the archives several times. I don't wish to write this to a file like this one shows and the duplicate question has been deleted or moved.
I realize that the JSON.parse(response) is just going to parse out whatever I tell it the response equals, but I don't know how to get that response to equal SGV. Maybe the solution isn't in the RestClient, but that is where I am lost.
Here is the JSON URL: http://dnarnianbg.herokuapp.com/api/v1/entries/current.json
EDIT: The output of that link is something like this:
[{"_id":"5ba295ddb8a1ee0aede71822","sgv":87,"date":1537381813000,"dateString":"2018-09-19T18:30:13.000Z","trend":4,"direction":"Flat","device":"share2","type":"sgv"}]
You need something like response[0]["sgv"] which should return 52 if you end up with many items in the list you will need to iterate over them.
The best thing you can do is to break your problem down into easier parts to debug. As you are having problems accessing some JSON via an API you should make a simple script which only does the function you want in order to test it and see where the problem is.
Here is a short example you can put into a .rb file and run;
#!/usr/bin/ruby
require 'open-uri'
require 'json'
test = JSON.parse(open("https://dnarnianbg.herokuapp.com/api/v1/entries/current.json", :read_timeout => 4).read)
puts test[0]["sgv"]
That should return the value from sgv
I realise that short sweet example may be little use as a learner so here is a more verbose version with some comments;
#!/usr/bin/ruby
require 'open-uri'
require 'json'
# Open the URL and read the result. Time out if this takes longer then 4 sec.
get_data = open("https://dnarnianbg.herokuapp.com/api/v1/entries/current.json", :read_timeout => 4).read
# Parse the response (get_data) to JSON and put in variable output
output = JSON.parse(get_data)
# Put the output to get the 'sgv figure'
p output[0]["sgv"]
It always pays to manually examine the data you get back, in your case the data looks like this (when make pretty)
[
{
"_id": "5ba41a0fb8a1ee0aedf6eb2c",
"sgv": 144,
"date": 1537481109000,
"dateString": "2018-09-20T22:05:09.000Z",
"trend": 4,
"direction": "Flat",
"device": "share2",
"type": "sgv"
}
]
What you actually have is an Array. Your server returns only 1 result, numbered '0' hence you need [0] in your p statement. Once you have accessed the array id then you can simply use the object you need as [sgv]
If your app ever returns more than one record then you will need to change your code to read all of the results and iterate over them in order to get all the values you need.
Here is the final code that made it work
require 'net/http'
require 'json'
require 'rest-client'
# :first_in sets how long it takes before the job is first run. In this case, it is run immediately
current_nightscout = 0
SCHEDULER.every '1m' do
test = JSON.parse(open("https://dnarnianbg.herokuapp.com/api/v1/entries/current.json", :read_timeout => 4).read)
last_nightscout = current_nightscout
current_nightscout = p test[0]["sgv"]
send_event('nightscout', { current: current_nightscout, last: last_nightscout })
end
I can probably eliminate require 'rest-client' since that is no longer being used, but it works right now and that is all that matters.

Select/isolate in multimodel approach

In API reference described methods to select/isolate objects (in condition that only one model is loaded in viewer):
- select(dbids,selectionType)
- isolate(node)/isolateById(dbids) // that is the difference?
I know select analog for multimodel:
viewer.impl.selector.setSelection([objectIds], model);
Questions are:
Is isolate analog for multimodel mode exists?
How can I select/isolate two objects from diffrenent models at once?
In the recent version of the API the viewer.impl.visibilityManager is returning a MultiModelVisibilityManager, so you can pass a model as second argument:
MultiModelVisibilityManager.prototype.isolate = function (node, model)
Take a look in viewer3D.js (L#17825) to see available methods on that object.
As far as I know there is no way to select two objects from different models in a single call, you would just issue one select call for each model passing respective ids. I don't see a problem with that.
Hope that helps.
For the isolate, you can do something like this(borrowed from the Viewer3D.js):
// Get selected elements from each loaded models
var selection = this.viewer.getAggregateSelection();
var allModels = this.viewer.impl.modelQueue().getModels().concat(); // shallow copy
// Isolate selected nodes.
selection.forEach(function(singleRes){
singleRes.model.visibilityManager.isolate(singleRes.selection);
var indx = allModels.indexOf(singleRes.model);
if (indx >= 0) {
allModels.splice(indx, 1);
}
});
// Hide nodes from all other models
while (allModels.length) {
allModels.pop().visibilityManager.setAllVisibility(false);
}
this.viewer.clearSelection();
For the select, you need to pass corresponding model and dbIds to the viewer.impl.selector.setSelection([dbIds], model); and call setSelection for each set, such as below. It cannot be archived at once.
var selSet = [
{
selection: [1234, 5621],
model: model1
},
{
selection: [12, 758],
model: model2
},
];
selSet.forEach(funciton(sel) {
viewer.impl.selector.setSelection(sel.selection, sel.model);
});

Node-red SQL output object / Array conversion

I'm doing a SQL query in Node-Red to output a load of time/value data. This data is then passed to a web page for display in a graph.
Previously I've used php to do the SQL query, which I'm trying to replace. However SQL queries in php are delivered in a different format.
With Node-Red, I get:
[
{
"Watts": 1018,
"Time": 1453825454
},
{
"Watts": 1018,
"Time": 1453825448
},
{
"Watts": 1010,
"Time": 1453825442
}]
With PHP, I get:
[
[1453819620000,962],
[1453819614000,950],
[1453819608000,967],
[1453819602000,947]
]
I think I'm getting an array from php and an array of JSON objects from Node-Red. How do I convert the Node-Red object to be output from Node-Red in the same format as the PHP is? (Ie: I want to handle the processing at the server, rather than the client.)
A function node can be used to generate something in the same format.
var array = msg.payload;
var phpFormat = "[";
for (var i=0; i<array.length; i++) {
phpFormat += "[" +
// time format differ, NodeJS is in seconds
// php is in milliseconds
(array[i].Time * 1000 ) +
"," +
array[i].Watts + "],";
}
//take the last "," off
phpFormat = phpFormat.substring(0,phpFormat.lenght - 1);
phpFormat += "]";
msg.payload = phpFormat;
return msg;
I've had a bit of help from a chap at work and here is what he's come up with, modified for node-red by me:
var outputArray = [];
for(var i in msg.payload){
var entryData = [msg.payload[i]['Time']];
for(var attr in msg.payload[i]) {
if(attr!='Time') {
entryData.push(msg.payload[i][attr])}
};
outputArray.push(entryData); }
var returnMsg={"payload":outputArray};
return returnMsg;
I know, I know, this question is over 2 years old... however, for the next 500 people seeking an answer to a similar problem, I'd like to highlight the new JSONata expression feature built-in to the change node. Using this simple expression:
payload.[Time, Watts]
transforms your JS objects into the requested output of an array of arrays. In fact, much of my old repetitive looping through arrays has been replaced with some simpler (to me) expressions like this.
The magic of the lambda syntax evaluator is documented on the JSONata site. There you will also find the online exerciser where you can build an expression against your own data and immediately see the resulting structure.
Note: in order to use a jsonata expression in your change node, be sure to select the J: pulldown next to the input field (not the {} JSON option)... two totally different things!

Make dynamic name text field in Postman

I'm using Postman to make REST API calls to a server. I want to make the name field dynamic so I can run the request with a unique name every time.
{
"location":
{
"name": "Testuser2", // this should be unique, eg. Testuser3, Testuser4, etc
"branding_domain_id": "52f9f8e2-72b7-0029-2dfa-84729e59dfee",
"parent_id": "52f9f8e2-731f-b2e1-2dfa-e901218d03d9"
}
}
In Postman you want to use Dynamic Variables.
The JSON you post would look like this:
{
"location":
{
"name": "{{$guid}}",
"branding_domain_id": "52f9f8e2-72b7-0029-2dfa-84729e59dfee",
"parent_id": "52f9f8e2-731f-b2e1-2dfa-e901218d03d9"
}
}
Note that this will give you a GUID (you also have the option to use ints or timestamps) and I'm not currently aware of a way to inject strings (say, from a test file or a data generation utility).
In Postman you can pass random integer which ranges from 0 to 1000, in your data you can use it as
{
"location":
{
"name": "Testuser{{$randomInt}}",
"branding_domain_id": "52f9f8e2-72b7-0029-2dfa-84729e59dfee",
"parent_id": "52f9f8e2-731f-b2e1-2dfa-e901218d03d9"
}
}
Just my 5 cents to this matter. When using randomInt there is always a chance that the number might eventually be present in the DB which can cause issues.
Solution (for me at least) is to use $timestamp instead.
Example:
{
"username": "test{{$timestamp}}",
"password": "test"
}
For anyone who's about to downvote me this post was made before the discussion in comments with the OP (see below). I'm leaving it in place so the comment from the OP which eventually described what he needs isn't removed from the question.
From what I understand you're looking for, here's a basic solution. It's assuming that:
you're developing some kind of script where you need test data
the name field should be unique each time it's run
If your question was more specific then I'd be able to give you a more specific answer, but this is the best I can do from what's there right now.
var counter = location.hash ? parseInt(location.hash.slice(1)) : 1; // get a unique counter from the URL
var unique_name = 'Testuser' + counter; // create a unique name
location.hash = ++counter; // increase the counter by 1
You can forcibly change the counter by looking in the address bar and changing the URL from ending in #1 to #5, etc.
You can then use the variable name when you build your object:
var location = {
name: unique_name,
branding_domain_id: 'however-you-currently-get-it',
parent_id: 'however-you-currently-get-it'
};
Add the below text in pre-req:
var myUUID = require('uuid').v4();
pm.environment.set('myUUID', myUUID);
and use the myUUID wherever you want
like
name: "{{myUUID}}"
It will generate a random unique GUID for every request
var uuid = require('uuid');
pm.globals.set('unique_name', 'testuser' + uuid.v4());
add above code to the pre-request tab.
this was you can reuse the unique name for subsequent api calls.
Dynamic variable like randomInt, or guid is dynamic ie : you donot know what was send in the request. there is no way to refer it again, unless it is send back in response. even if you store it in a variable,it will still be dynamic
another way is :
var allowed = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
var shuffled_unique_str = allowed.split('').sort(function(){return 0.5-Math.random()}).join('');
courtsey refer this link for more options