End of file expected. json(0) - json

I want to use this JSON file for pandas, but this JSON file has some problem.
I got an error from the time I downloaded it, but I couldn't find the answer.
{
"content": "http://com.dataturks.a96-i23.open.s3.amazonaws.com/2c9fafb064277d86016431e33e4e003d/8186c3d1-e9d4-4550-8ec1-a062a7628787___0-26.jpg.jpeg",
"annotation": [{
"label": ["Face"],
"notes": "",
"points": [{
"x": 0.08615384615384615,
"y": 0.3063063063063063
}, {
"x": 0.1723076923076923,
"y": 0.45345345345345345
}],
"imageWidth": 650,
"imageHeight": 333
}, {
"label": ["Face"],
"notes": "",
"points": [{
"x": 0.583076923076923,
"y": 0.2912912912912913
}, {
"x": 0.6584615384615384,
"y": 0.46846846846846846
}],
"imageWidth": 650,
"imageHeight": 333
}],
"extras": null
} {
"content": "http://com.dataturks.a96-i23.open.s3.amazonaws.com/2c9fafb064277d86016431e33e4e003d/d1c32c8e-8050-482d-a6c8-b101ccba5b65___0de0ee708a4a47039e441d488615ebb7.png",
"annotation": [{
"label": ["Face"],
"notes": "",
"points": [{
"x": 0.7053087757313109,
"y": 0.23260437375745527
}, {
"x": 0.7692307692307693,
"y": 0.36182902584493043
}],
"imageWidth": 1280,
"imageHeight": 697
}],
"extras": null
}
It shows this message.
Error: Parse error on line 29:
..., "extras": null} { "content": "http:
---------------------^
Expecting 'EOF', '}', ',', ']', got '{'
How can I solve this problem?

Try this: it should work!
There's a problem with the way you started your JSON file
Use a square bracket to start and end with a square bracket [ ]
[
{
"content": "http://com.dataturks.a96-i23.open.s3.amazonaws.com/2c9fafb064277d86016431e33e4e003d/8186c3d1-e9d4-4550-8ec1-a062a7628787___0-26.jpg.jpeg",
"annotation": [{
"label": ["Face"],
"notes": "",
"points": [{
"x": 0.08615384615384615,
"y": 0.3063063063063063
}, {
"x": 0.1723076923076923,
"y": 0.45345345345345345
}],
"imageWidth": 650,
"imageHeight": 333
}, {
"label": ["Face"],
"notes": "",
"points": [{
"x": 0.583076923076923,
"y": 0.2912912912912913
}, {
"x": 0.6584615384615384,
"y": 0.46846846846846846
}],
"imageWidth": 650,
"imageHeight": 333
}],
"extras": null
} {
"content": "http://com.dataturks.a96-i23.open.s3.amazonaws.com/2c9fafb064277d86016431e33e4e003d/d1c32c8e-8050-482d-a6c8-b101ccba5b65___0de0ee708a4a47039e441d488615ebb7.png",
"annotation": [{
"label": ["Face"],
"notes": "",
"points": [{
"x": 0.7053087757313109,
"y": 0.23260437375745527
}, {
"x": 0.7692307692307693,
"y": 0.36182902584493043
}],
"imageWidth": 1280,
"imageHeight": 697
}],
"extras": null
}
]

Your JSON structure is wrong. Probably you want get array inside :
[
{
"content": "http://com.dataturks.a96-i23.open.s3.amazonaws.com/2c9fafb064277d86016431e33e4e003d/8186c3d1-e9d4-4550-8ec1-a062a7628787___0-26.jpg.jpeg",
"annotation": [
{
"label": [
"Face"
],
"notes": "",
"points": [
{
"x": 0.08615384615384615,
"y": 0.3063063063063063
},
{
"x": 0.1723076923076923,
"y": 0.45345345345345345
}
],
"imageWidth": 650,
"imageHeight": 333
},
{
"label": [
"Face"
],
"notes": "",
"points": [
{
"x": 0.583076923076923,
"y": 0.2912912912912913
},
{
"x": 0.6584615384615384,
"y": 0.46846846846846846
}
],
"imageWidth": 650,
"imageHeight": 333
}
],
"extras": null
},
{
"content": "http://com.dataturks.a96-i23.open.s3.amazonaws.com/2c9fafb064277d86016431e33e4e003d/d1c32c8e-8050-482d-a6c8-b101ccba5b65___0de0ee708a4a47039e441d488615ebb7.png",
"annotation": [
{
"label": [
"Face"
],
"notes": "",
"points": [
{
"x": 0.7053087757313109,
"y": 0.23260437375745527
},
{
"x": 0.7692307692307693,
"y": 0.36182902584493043
}
],
"imageWidth": 1280,
"imageHeight": 697
}
],
"extras": null
}
]

Related

vega lite - persist null date/time in chart

How do I persist the null in the data on visualization with vega chart while rendering the date/time for data the timestamp. Right now, it looks like null data is getting passed as default time of 04:00
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"data": {
"values": [
{
"x": null,
"y": "recoPU8F3Asuc8t3n"
},
{
"x": "2017-06-02T07:00:00.000Z",
"y": "reco056sZzsAd7qVp"
},
{
"x": null,
"y": "reclQZbw7CiAegm1f"
},
{
"x": "2017-07-14T07:00:00.000Z",
"y": "recAzUhUdekIdkSVt"
},
{
"x": null,
"y": "rec0NHCpaRDP9W1pd"
},
{
"x": null,
"y": "recPYz3QNpXgrmBVy"
}
]
},
"config": {
"range": {
"category": [
"#46bfdd"
]
},
"legend": {
"disable": false,
"labelColor": "#333",
"labelFontSize": 11,
"symbolSize": 30,
"rowPadding": 4,
"symbolType": "circle",
"symbolStrokeWidth": 0,
"titleColor": "#666666",
"titleFontSize": 11,
"titlePadding": 10,
"titleFontWeight": 500,
"orient": "right"
},
"axisY": {
"tickMinStep": 1,
"format": "~s",
"formatType": "number"
},
"axis": {
"domainColor": "#E8E8E8",
"gridColor": "#E8E8E8",
"tickColor": "#E8E8E8",
"grid": true,
"gridWidth": 1,
"labelColor": "#666666",
"labelFontSize": 11,
"labelLimit": 180,
"titleColor": "#999999",
"tickSize": 16,
"titleFontSize": 13,
"titlePadding": 10,
"titleFontWeight": 400,
"labelPadding": 4,
"titleLimit": 180
},
"autosize": {
"type": "fit",
"contains": "padding"
},
"axisLeft": {
"labelFontSize": 11,
"labelColor": "#999999",
"labelLimit": 180,
"titleFontSize": 13,
"titleFontWeight": 400,
"titleLimit": 180
},
"style": {
"label": {
"align": "left",
"baseline": "middle",
"dx": 4
},
"cell": {
"stroke": "transparent"
}
}
},
"mark": {
"type": "bar",
"width": {
"band": 0.7
},
"tooltip": true,
"cornerRadiusEnd": 3,
"cursor": "pointer",
"color": "#4787cd"
},
"params": [
{
"name": "highlight",
"select": {
"type": "point",
"on": "mouseover"
}
},
{
"name": "select",
"select": "point"
}
],
"encoding": {
"x": {
"field": "x",
"type": "nominal",
"axis": {
"labelAngle": -90,
"format": "%I:%M",
"formatType": "time",
"title": "Reservation On"
},
"sort": {
"encoding": "x",
"order": "ascending"
},
"timeUnit": "hoursminutes"
},
"y": {
"field": "y",
"type": "quantitative",
"aggregate": "count",
"axis": {
"title": "Number of records"
}
},
"tooltip": [
{
"field": "x",
"title": "Reservation On",
"format": "%I:%M",
"formatType": "time",
"timeUnit": "hoursminutes"
},
{
"field": "y",
"type": "quantitative",
"aggregate": "count",
"title": "Number of records"
}
],
"fillOpacity": {
"condition": [
{
"param": "select",
"value": 1
}
],
"value": 0.3
},
"strokeWidth": {
"condition": [
{
"param": "select",
"empty": false,
"value": 2
},
{
"param": "highlight",
"empty": false,
"value": 1
}
],
"value": 0
}
},
"transform": [
],
"height": 372,
"width": 536
}
One way to do this is by replacing your "format" with an appropriate "labelExpr":
"axis": {
"labelAngle": -90,
"labelExpr": "isValid(datum.value) ? timeFormat(datum.value, '%I:%M') : 'null'",
"title": "Reservation On"
}

AWS Cloudformation YML File with Embedded JSON Strong for Dashboard Body

Trying to make our dashboard as code while using CloudFormation. Getting an error while validating template with the embedded JSON String inside the YML File. It is having problems with the "SEARCH('{AWS/EC2,InstanceId) fields saying those are invalid strings. I have ran it through a JSON Linter and works fine. I have also deployed the CloudWatch Dashboard console just fine with the JSON.
Question:
Is there anyway to just wrap this section as a string for CloudFormation in the YML file so it doesn't get tripped up on the JSON string contents?
Example:
DetailedDashboard:
Type: "AWS::CloudWatch::Dashboard"
Properties:
DashboardName: "DetailedDashboard"
DashboardBody: !Sub
...,
{
"type": "metric",
"x": 6,
"y": 18,
"width": 6,
"height": 6,
"properties": {
"metrics": [
[ { "expression": "SEARCH('{AWS/EC2,InstanceId} NetworkIn', 'Average', 300)", "id": "network_in", "period": 300, "region": "us-east-1", "label": "net_in" } ],
[ { "expression": "SEARCH('{AWS/EC2,InstanceId} NetworkOut', 'Average', 300)", "id": "network_out", "period": 300, "region": "us-east-1", "label": "net_out" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"title": "Network in/out by Host",
"stat": "Average",
"period": 300,
"yAxis": {
"left": {
"label": "bytes"
}
}
}
},
DetailedDashboard:
Type: 'AWS::CloudWatch::Dashboard'
Properties:
DashboardName: 'DetailedDashboard'
DashboardBody: !Sub |
{
"type": "metric",
"x": 6,
"y": 18,
"width": 6,
"height": 6,
"properties": {
"metrics": [
[ { "expression": "SEARCH('{AWS/EC2,InstanceId} NetworkIn', 'Average', 300)", "id": "network_in", "period": 300, "region": "us-east-1", "label": "net_in" } ],
[ { "expression": "SEARCH('{AWS/EC2,InstanceId} NetworkOut', 'Average', 300)", "id": "network_out", "period": 300, "region": "us-east-1", "label": "net_out" } ]
],
"view": "timeSeries",
"stacked": false,
"region": "us-east-1",
"title": "Network in/out by Host",
"stat": "Average",
"period": 300,
"yAxis": {
"left": {
"label": "bytes"
}
}
}
},

How do I insert an ip address into mysql

good morning
im new to node red and javascript so this may be a simple issue im having
im trying to insert an ip address into a mysql table
in mysql i set the field to varchar(20)
9/28/2019, 9:08:38 AMnode: 7bfc4d89.726dbc
msg : error
"Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '[object Object])' at line 1"
the basic flows are as follows:
[
{
"id": "df624ad9.0db0f8",
"type": "function",
"z": "661acf87.d0306",
"name": "Defining inside ip",
"func": "global.set(\"inside_ip\",msg.payload);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 530,
"y": 120,
"wires": [
[]
]
},
{
"id": "400e4f07.42965",
"type": "inject",
"z": "661acf87.d0306",
"name": "",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"x": 190,
"y": 120,
"wires": [
[
"f3f24f92.29582"
]
]
},
{
"id": "f3f24f92.29582",
"type": "ip",
"z": "661acf87.d0306",
"name": "ip",
"https": false,
"timeout": "5000",
"internalIPv4": true,
"internalIPv6": false,
"publicIPv4": false,
"publicIPv6": false,
"x": 330,
"y": 120,
"wires": [
[
"df624ad9.0db0f8"
]
]
},
{
"id": "47202c3d.71ebfc",
"type": "inject",
"z": "661acf87.d0306",
"name": "",
"topic": "",
"payload": "1",
"payloadType": "num",
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": "5",
"x": 230,
"y": 180,
"wires": [
[
"20c745e0.57fb2a"
]
]
},
{
"id": "7bfc4d89.726dbc",
"type": "mysql",
"z": "661acf87.d0306",
"mydb": "ca2479e3.1dda58",
"name": "",
"x": 510,
"y": 180,
"wires": [
[]
]
},
{
"id": "20c745e0.57fb2a",
"type": "function",
"z": "661acf87.d0306",
"name": "",
"func": "msg.topic =\"INSERT INTO`node`(`inside_ip`) VALUES(\" + global.get(\"inside_ip\") + \")\";\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"x": 350,
"y": 180,
"wires": [
[
"7bfc4d89.726dbc"
]
]
},
{
"id": "ca2479e3.1dda58",
"type": "MySQLdatabase",
"z": "",
"host": "72.90.182.40",
"port": "3306",
"db": "mysql_test",
"tz": "eastern"
}
]
However
if i set the "ip" like this it works:
[
{
"id": "1f5e06b6.76e931",
"type": "function",
"z": "87908bbe.239128",
"name": "Defining inside ip",
"func": "global.set(\"inside_ip\",msg.payload);\nreturn msg;",
"outputs": 1,
"noerr": 0,
"x": 645,
"y": 228,
"wires": [
[]
]
},
{
"id": "4dd624b5.e58f34",
"type": "inject",
"z": "87908bbe.239128",
"name": "",
"topic": "",
"payload": "",
"payloadType": "date",
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"x": 305,
"y": 228,
"wires": [
[
"b9528c26.ceca2"
]
]
},
{
"id": "b9528c26.ceca2",
"type": "ip",
"z": "87908bbe.239128",
"name": "ip",
"https": false,
"timeout": "5000",
"internalIPv4": true,
"internalIPv6": false,
"publicIPv4": false,
"publicIPv6": false,
"x": 445,
"y": 228,
"wires": [
[]
]
},
{
"id": "215d3000.15a8b8",
"type": "inject",
"z": "87908bbe.239128",
"name": "",
"topic": "",
"payload": "1",
"payloadType": "num",
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": "5",
"x": 345,
"y": 288,
"wires": [
[
"fdcd98f2.a35838"
]
]
},
{
"id": "17946cb4.862c23",
"type": "mysql",
"z": "87908bbe.239128",
"mydb": "59bd1ef5.938c2",
"name": "",
"x": 625,
"y": 288,
"wires": [
[]
]
},
{
"id": "fdcd98f2.a35838",
"type": "function",
"z": "87908bbe.239128",
"name": "",
"func": "msg.topic =\"INSERT INTO`node`(`inside_ip`) VALUES(\" + global.get(\"inside_ip\") + \")\";\nreturn msg;\n",
"outputs": 1,
"noerr": 0,
"x": 465,
"y": 288,
"wires": [
[
"17946cb4.862c23"
]
]
},
{
"id": "a248bafe.b02ac",
"type": "inject",
"z": "87908bbe.239128",
"name": "",
"topic": "",
"payload": "\"192.168.1.1\"",
"payloadType": "str",
"repeat": "",
"crontab": "",
"once": true,
"onceDelay": 0.1,
"x": 341,
"y": 145,
"wires": [
[
"1f5e06b6.76e931"
]
]
},
{
"id": "59bd1ef5.938c2",
"type": "MySQLdatabase",
"z": "",
"host": "72.90.182.40",
"port": "3306",
"db": "mysql_test",
"tz": "eastern"
}
]
any advise would be great
regards mike
Okay so the IP node you are using returns the following in msg.payload
{"internalIPv4":"192.168.1.107"}
For you to be able to save it to the database you need to crop the:
({"internalIPv4":" and "}
there are a couple of ways to do this by using the function node.
The first, one is to replace the unnecessary string with nothing (this is my way to go!)
var str = JSON.stringify(msg.payload)
var ip = str.replace('{"internalIPv4":"', '');
ip = ip.replace('"}', '')
msg.payload = ip
return msg;
The second method is to crop by character number
var str = JSON.stringify(msg.payload)
var ip = str.substring(17,30 );
msg.payload = ip
return msg;
The problem with this code is if the length of your IP changes, possibly one number from IP will be missing.
Both of these functions return me the same message (first is before function, the second is after)
{"internalIPv4":"192.168.1.107"}
"192.168.1.107"
Four more hints on your small project:
1. No need to send the SQL query every second.
Better use some unique identifier (if running on raspberry pi use its serial number to identify which IP is for what device)
Add UNIQUE key on MYSQL for the IP addresses
Where you cant use", use ' (especially in fitting data in a MySQL query)
Hope I helped!

Customise vega-lite Line Graph Axis Labels

What I want to do is really simple, but I just can't seem to get it right. I have a feeling I'm going to be embarrassed by the answer!
I have a line graph with "attempt" along the x-axis and "grade" along the y-axis, with grade being a number between 0 and 100. I simply want to change the y-axis so that, instead of seeing the raw number, a grade is show, say with 0 - 20 representing "E", 20-40 being "D" etc up to "A" (80-100). How can I do that? I don't want to use discrete values because I want to visually show where within a grade boundary a grade falls. I'm not sure whether I yet want to simply display the grade bands on the line or put them in the middle of their ticks, but just getting somewhere with this would help a lot!
Here is what I've been working with in the vega-lite editor:
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"data": {
"values": [
{
"attempt": 1,
"score": 30
},
{
"attempt": 2,
"score": 60
},
{
"attempt": 3,
"score": 75
},
{
"attempt": 4,
"score": 58
},
{
"attempt": 5,
"score": 67
}
]
},
"mark": {
"type": "line",
"color": "#22bc9a",
"point": {
"filled": false
}
},
"encoding": {
"x": {
"field": "attempt",
"type": "quantitative",
"axis": {
"grid": false,
"tickCount": 5,
"title": "Attempt"
}
},
"y": {
"field": "score",
"scale": {"domain": [0, 100]},
"type": "quantitative",
"axis": {
"tickCount": 5,
"title": "Grade"
}
},
"opacity": {"value": 0.3}
},
"config": {
"autosize": "fit",
"axis": {
"labelColor": "#bebec8",
"tickColor": "#bebec8",
"titleColor": "black",
"titleFontWeight": "normal",
"titleFontSize": 11,
"labelFont": "Helvetica",
"titleFont": "Helvetica",
"gridOpacity": 0.4,
"gridWidth": 1.5,
"domain": false
},
"view": {
"strokeWidth": 0
}
}
}
Thanks in advance.
What about something like this: I add a dataframe with the grade category, and use this to layer some text. I remove the labels of the axis and so the text acts as if it were the labels of the axis.
The chart looks like this, and here is a link to the editor:
Schema (Note that I did it with Python's Altair, so it may not be canonical Vega-lite, and I did not use your settings either, sorry about that):
{
"$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json",
"config": {
"view": {
"height": 300,
"width": 400
}
},
"datasets": {
"data-1acee7c5d817865a529b53e022474ce8": [
{
"label": "E",
"x_min": 1,
"y_med": 10
},
{
"label": "D",
"x_min": 1,
"y_med": 30
},
{
"label": "C",
"x_min": 1,
"y_med": 50
},
{
"label": "B",
"x_min": 1,
"y_med": 70
},
{
"label": "A",
"x_min": 1,
"y_med": 90
}
],
"data-8e6359ea3034b8410708361bb10fafd5": [
{
"attempt": 1,
"score": 30
},
{
"attempt": 2,
"score": 60
},
{
"attempt": 3,
"score": 75
},
{
"attempt": 4,
"score": 58
},
{
"attempt": 5,
"score": 67
}
]
},
"layer": [
{
"data": {
"name": "data-1acee7c5d817865a529b53e022474ce8"
},
"encoding": {
"text": {
"field": "label",
"type": "ordinal"
},
"x": {
"field": "x_min",
"scale": {
"zero": false
},
"type": "quantitative"
},
"y": {
"axis": {
"labels": false,
"tickCount": 5,
"ticks": false
},
"field": "y_med",
"type": "quantitative"
}
},
"mark": {
"dx": -15,
"dy": 8,
"size": 15,
"type": "text"
}
},
{
"data": {
"name": "data-8e6359ea3034b8410708361bb10fafd5"
},
"encoding": {
"x": {
"axis": {
"tickCount": 5
},
"field": "attempt",
"title": "Attempt",
"type": "quantitative"
},
"y": {
"field": "score",
"scale": {
"domain": [
0,
100
]
},
"title": "Grade",
"type": "quantitative"
}
},
"mark": {
"point": true,
"type": "line"
}
}
]
}
Using a slighly modified dataframe for the categories (with x_max, y_min and y_max added), you can add another layer with colored rectangles, that can help read the values:
Here is a link to the editor
And here is the schema
{
"$schema": "https://vega.github.io/schema/vega-lite/v2.6.0.json",
"config": {
"view": {
"height": 300,
"width": 400
}
},
"datasets": {
"data-1acee7c5d817865a529b53e022474ce8": [
{
"label": "E",
"x_min": 1,
"y_med": 10
},
{
"label": "D",
"x_min": 1,
"y_med": 30
},
{
"label": "C",
"x_min": 1,
"y_med": 50
},
{
"label": "B",
"x_min": 1,
"y_med": 70
},
{
"label": "A",
"x_min": 1,
"y_med": 90
}
],
"data-39ffbda2b5d5fe96de84d9e308d920ff": [
{
"label": "E",
"x_max": 5,
"x_min": 1,
"y_max": 20,
"y_med": 10,
"y_min": 0
},
{
"label": "D",
"x_max": 5,
"x_min": 1,
"y_max": 40,
"y_med": 30,
"y_min": 20
},
{
"label": "C",
"x_max": 5,
"x_min": 1,
"y_max": 60,
"y_med": 50,
"y_min": 40
},
{
"label": "B",
"x_max": 5,
"x_min": 1,
"y_max": 80,
"y_med": 70,
"y_min": 60
},
{
"label": "A",
"x_max": 5,
"x_min": 1,
"y_max": 100,
"y_med": 90,
"y_min": 80
}
],
"data-8e6359ea3034b8410708361bb10fafd5": [
{
"attempt": 1,
"score": 30
},
{
"attempt": 2,
"score": 60
},
{
"attempt": 3,
"score": 75
},
{
"attempt": 4,
"score": 58
},
{
"attempt": 5,
"score": 67
}
]
},
"layer": [
{
"data": {
"name": "data-39ffbda2b5d5fe96de84d9e308d920ff"
},
"encoding": {
"color": {
"field": "label",
"scale": {
"scheme": "greenblue"
},
"type": "ordinal"
},
"x": {
"field": "x_min",
"scale": {
"zero": false
},
"title": "Attempt",
"type": "quantitative"
},
"x2": {
"field": "x_max",
"type": "quantitative"
},
"y": {
"axis": null,
"field": "y_min",
"type": "quantitative"
},
"y2": {
"field": "y_max",
"type": "quantitative"
}
},
"mark": "rect"
},
{
"data": {
"name": "data-1acee7c5d817865a529b53e022474ce8"
},
"encoding": {
"text": {
"field": "label",
"type": "ordinal"
},
"x": {
"field": "x_min",
"scale": {
"zero": false
},
"type": "quantitative"
},
"y": {
"axis": {
"labels": false,
"tickCount": 5,
"ticks": false
},
"field": "y_med",
"type": "quantitative"
}
},
"mark": {
"dx": -15,
"dy": 8,
"size": 15,
"type": "text"
}
},
{
"data": {
"name": "data-8e6359ea3034b8410708361bb10fafd5"
},
"encoding": {
"x": {
"axis": {
"tickCount": 5
},
"field": "attempt",
"title": "Attempt",
"type": "quantitative"
},
"y": {
"field": "score",
"scale": {
"domain": [
0,
100
]
},
"title": "Grade",
"type": "quantitative"
}
},
"mark": {
"point": true,
"type": "line"
}
}
]
}
To get it working, I first had to change the encoding of the x and y axis to be ordinal. Then I mapped your input data values to letter grades before creating the schema:
//replace every score value with correct letter grade
values.forEach(datapoint => {
if(datapoint.score > 90) {
datapoint.score = "A";
} else if(datapoint.score > 80) {
datapoint.score = "B";
} else if (datapoint.score > 70) {
//so on...
}
});
Here is a working example in the vega-lite editor:
Line Chart with Ordinal Values
Here is the schema:
{
"$schema": "https://vega.github.io/schema/vega-lite/v3.json",
"data": {
"values": [
{
"attempt": 1,
"score": "F"
},
{
"attempt": 2,
"score": "D"
},
{
"attempt": 3,
"score": "C"
},
{
"attempt": 4,
"score": "F"
},
{
"attempt": 5,
"score": "D"
}
]
},
"mark": {
"type": "line",
"color": "#22bc9a",
"point": {
"filled": false
}
},
"encoding": {
"x": {
"field": "attempt",
"type": "ordinal",
"axis": {
"title": "Attempt"
}
},
"y": {
"field": "score",
"type": "ordinal",
"axis": {
"title": "Grade"
}
},
"opacity": {"value": 0.3}
},
"config": {
"autosize": "fit",
"axis": {
"labelColor": "#bebec8",
"tickColor": "#bebec8",
"titleColor": "black",
"titleFontWeight": "normal",
"titleFontSize": 11,
"labelFont": "Helvetica",
"titleFont": "Helvetica",
"gridOpacity": 0.4,
"gridWidth": 1.5
},
"view": {
"strokeWidth": 0
}
}
}

Node-RED: Interpreting REST service URL

I am currently using the IBM Bluemix IoT Quickstart for some experiments. For those of you who don't know this, it is an IoT Sensor simulator that can be used to control temperature and humidity values.
I am looking to use a web service connector to look for the temperature reading. How would I go about interpreting the Service URL for this?
Also (but not as important!) I see that the website /test/ fails to display the temperature reading. Any ideas as to why?
Below is the json to be used by web service to display temperature
[
{
"id": "e42c6f27.46cca",
"type": "function",
"z": "3ed49b1f.4c1164",
"name": "temp",
"func": "return {payload:msg.payload.d.temp};",
"outputs": 1,
"noerr": 0,
"x": 422.5,
"y": 775,
"wires": [
[
"c76cd036.bc2ba"
]
]
},
{
"id": "f47256cd.a18b78",
"type": "template",
"z": "3ed49b1f.4c1164",
"name": "",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "The temperature is\n{{payload}}\n",
"output": "str",
"x": 775,
"y": 953,
"wires": [
[
"22b54d74.028ae2"
]
]
},
{
"id": "22b54d74.028ae2",
"type": "http response",
"z": "3ed49b1f.4c1164",
"name": "",
"statusCode": "",
"headers": {
},
"x": 1064,
"y": 955,
"wires": [
]
},
{
"id": "243d218a.fe14ae",
"type": "http in",
"z": "3ed49b1f.4c1164",
"name": "",
"url": "test",
"method": "get",
"upload": false,
"swaggerDoc": "",
"x": 432,
"y": 993,
"wires": [
[
"f47256cd.a18b78"
]
]
},
{
"id": "429ffb9e.7ed404",
"type": "ibmiot in",
"z": "3ed49b1f.4c1164",
"authentication": "quickstart",
"apiKey": "",
"inputType": "evt",
"deviceId": "3bd1c8df5ed2",
"applicationId": "",
"deviceType": "iotqs-sensor",
"eventType": "+",
"commandType": "",
"format": "json",
"name": "IBM IoT App In",
"service": "quickstart",
"allDevices": false,
"allApplications": false,
"allDeviceTypes": true,
"allEvents": true,
"allCommands": false,
"allFormats": false,
"x": 269,
"y": 550,
"wires": [
[
"55064d15.005804",
"c34674a2.7ea0d8",
"2356b867.1c3d68",
"c7ee16c4.493fc8",
"e42c6f27.46cca"
]
]
},
{
"id": "55064d15.005804",
"type": "debug",
"z": "3ed49b1f.4c1164",
"name": "name",
"active": true,
"complete": "payload.d.name",
"x": 717.5,
"y": 551,
"wires": [
]
},
{
"id": "c34674a2.7ea0d8",
"type": "debug",
"z": "3ed49b1f.4c1164",
"name": "Temperature",
"active": true,
"complete": "payload.d.temp",
"x": 717.5,
"y": 651,
"wires": [
]
},
{
"id": "2356b867.1c3d68",
"type": "debug",
"z": "3ed49b1f.4c1164",
"name": "Humidity",
"active": true,
"complete": "payload.d.humidity",
"x": 717.5,
"y": 751,
"wires": [
]
},
{
"id": "c7ee16c4.493fc8",
"type": "debug",
"z": "3ed49b1f.4c1164",
"name": "Object Temperature",
"active": true,
"complete": "payload.d.objectTemp",
"x": 717.5,
"y": 851,
"wires": [
]
},
{
"id": "c76cd036.bc2ba",
"type": "json",
"z": "3ed49b1f.4c1164",
"name": "",
"pretty": true,
"x": 531,
"y": 894,
"wires": [
[
"f47256cd.a18b78"
]
]
}
]
There are a few issues with this flow:
the JSON node is not necessary, since the msg.payload is already json
the output readings from the iot node were being sent to the http response node, which is only needed when the flow is returning results from the API call
the output from the http in node contains no msg.payload, so the template node has nothing to format
Here is a different approach, that I believe gives you what you need:
store the output object msg.payload.d into a flow context variable called "lastReadings"
use the http in node with url /last/:type as the api endpoint to retrieve the last value for the given sensor type (if you have node-red-node-swagger installed, you can test the endpoint directly from the right-hand sidebar)
use a switch node to route the api call, based on the sensor type, to retrieve the last reading and return the output string through the http response node
A couple things to note: if your settings.js includes either an httpRoot or httpNodeRoot url prefix, this will preceed your api url -- this is probably why you got no results when trying to access the url "http://localhost:1880/test/". Check the notes on the http in node for something like this:
The url will be relative to /red.
in which case you would need to use the api url "http://localhost:1880/red/test/"
Here is the flow I created, along with an inject node for testing your values (since I don't have the ibmiot input node available):
[
{
"id": "5ebe7dff.780724",
"type": "template",
"z": "7299298e.a9f058",
"name": "output value",
"field": "payload",
"fieldType": "msg",
"format": "handlebars",
"syntax": "mustache",
"template": "Last {{sensor}} reading:\n{{payload}}\n",
"output": "str",
"x": 630,
"y": 720,
"wires": [
[
"cb13c651.0ef458",
"efabaec0.9bd7a"
]
]
},
{
"id": "cb13c651.0ef458",
"type": "http response",
"z": "7299298e.a9f058",
"name": "",
"statusCode": "",
"headers": {},
"x": 790,
"y": 760,
"wires": []
},
{
"id": "361c51b4.f519ae",
"type": "http in",
"z": "7299298e.a9f058",
"name": "",
"url": "/last/:type",
"method": "get",
"upload": false,
"swaggerDoc": "59b72c5b.bc0fb4",
"x": 150,
"y": 640,
"wires": [
[
"c2fb1169.b76e8",
"61234f00.7faf1"
]
]
},
{
"id": "884f3eb9.e22be",
"type": "debug",
"z": "7299298e.a9f058",
"name": "payload.d values",
"active": true,
"console": "false",
"complete": "payload.d",
"x": 470,
"y": 480,
"wires": []
},
{
"id": "fef6db49.919728",
"type": "comment",
"z": "7299298e.a9f058",
"name": "Save the last readings to flow context",
"info": "",
"x": 210,
"y": 440,
"wires": []
},
{
"id": "81bfb514.a63348",
"type": "change",
"z": "7299298e.a9f058",
"name": "save readings",
"rules": [
{
"t": "set",
"p": "lastReadings",
"pt": "flow",
"to": "payload.d",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 460,
"y": 520,
"wires": [
[]
]
},
{
"id": "9573eab5.84e548",
"type": "comment",
"z": "7299298e.a9f058",
"name": "API endpoints to retrieve sensor values",
"info": "",
"x": 210,
"y": 600,
"wires": []
},
{
"id": "c2fb1169.b76e8",
"type": "switch",
"z": "7299298e.a9f058",
"name": "sensor type",
"property": "req.params.type",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "temp",
"vt": "str"
},
{
"t": "eq",
"v": "humidity",
"vt": "str"
},
{
"t": "eq",
"v": "objectTemp",
"vt": "str"
},
{
"t": "else"
}
],
"checkall": "true",
"outputs": 4,
"x": 250,
"y": 700,
"wires": [
[
"4eb4657b.cd0b0c"
],
[
"801c0b28.320308"
],
[
"a7833d28.f58f"
],
[
"e30f26ce.a084b8"
]
]
},
{
"id": "4eb4657b.cd0b0c",
"type": "change",
"z": "7299298e.a9f058",
"name": "Temperature",
"rules": [
{
"t": "set",
"p": "sensor",
"pt": "msg",
"to": "Temperature",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "lastReadings.temp",
"tot": "flow"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 450,
"y": 680,
"wires": [
[
"5ebe7dff.780724"
]
]
},
{
"id": "801c0b28.320308",
"type": "change",
"z": "7299298e.a9f058",
"name": "Humidity",
"rules": [
{
"t": "set",
"p": "sensor",
"pt": "msg",
"to": "Humidity",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "lastReadings.humidity",
"tot": "flow"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 440,
"y": 720,
"wires": [
[
"5ebe7dff.780724"
]
]
},
{
"id": "a7833d28.f58f",
"type": "change",
"z": "7299298e.a9f058",
"name": "Object Temp",
"rules": [
{
"t": "set",
"p": "sensor",
"pt": "msg",
"to": "Object Temp",
"tot": "str"
},
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "lastReadings.objectTemp",
"tot": "flow"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 450,
"y": 760,
"wires": [
[
"5ebe7dff.780724"
]
]
},
{
"id": "61234f00.7faf1",
"type": "debug",
"z": "7299298e.a9f058",
"name": "API params",
"active": false,
"console": "false",
"complete": "true",
"x": 450,
"y": 640,
"wires": []
},
{
"id": "efabaec0.9bd7a",
"type": "debug",
"z": "7299298e.a9f058",
"name": "API out",
"active": true,
"console": "false",
"complete": "payload",
"x": 800,
"y": 720,
"wires": []
},
{
"id": "b019dd18.1806e",
"type": "inject",
"z": "7299298e.a9f058",
"name": "test values",
"topic": "",
"payload": "{\"d\":{\"temp\":21,\"humidity\":69,\"objectTemp\":\"NA\"}}",
"payloadType": "json",
"repeat": "",
"crontab": "",
"once": false,
"x": 150,
"y": 520,
"wires": [
[
"81bfb514.a63348",
"884f3eb9.e22be"
]
]
},
{
"id": "e30f26ce.a084b8",
"type": "change",
"z": "7299298e.a9f058",
"name": "invalid type",
"rules": [
{
"t": "set",
"p": "payload",
"pt": "msg",
"to": "Error! Unknown sensor type...",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 450,
"y": 800,
"wires": [
[
"cb13c651.0ef458"
]
]
},
{
"id": "e02d2f38.842d8",
"type": "ibmiot in",
"z": "7299298e.a9f058",
"authentication": "quickstart",
"apiKey": "",
"inputType": "evt",
"deviceId": "3bd1c8df5ed2",
"applicationId": "",
"deviceType": "iotqs-sensor",
"eventType": "+",
"commandType": "",
"format": "json",
"name": "IBM IoT App In",
"service": "quickstart",
"allDevices": false,
"allApplications": false,
"allDeviceTypes": true,
"allEvents": true,
"allCommands": false,
"allFormats": false,
"x": 150,
"y": 480,
"wires": [
[
"884f3eb9.e22be",
"81bfb514.a63348"
]
]
},
{
"id": "59b72c5b.bc0fb4",
"type": "swagger-doc",
"z": "",
"summary": "Retrieve last sensor reading",
"description": "",
"tags": "",
"consumes": "",
"produces": "",
"parameters": [
{
"name": "type",
"in": "path",
"description": "Sensor type",
"required": true,
"type": "string"
}
],
"responses": {},
"deprecated": false
}
]
Please note that I changed the api url to use /last/{type} syntax... where {type} is replaced by the name of the field in your msg.payload.d object.