Seeding a Dashing list from an External JSON file - ruby

I'm new to Dashing (and relatively new to Ruby) so I apologize in advance if this is a dumb question. Basically I'm trying to get Dashing to read a json file that I update every 10 seconds. But I can't seem to get my job to read the file or post it out to my List widget.
Here's my job code:
require 'rubygems'
require 'json'
require 'pp'
name_list = Hash.new(0)
SCHEDULER.every '10s' do
json = File.read('list.json')
response = JSON.parse(json)
name_list[people] = {label: response.keys, value: response.keys[]}
send_event('whosHere', { items: response.values })
end
and my JSON:
{
"Mike": "Here",
"Jon": "Out",
}
And in case you need it my dashboard code:
<% content_for(:title) { "My super sweet dashboard" } %>
<div class="gridster">
<ul>
<li data-row="1" data-col="1" data-sizex="1" data-sizey="1">
<div data-id="whosHere" data-view="List" data-title="Who's Home" style="background-color:#96bf48;"></div>
</li>
</ul>
</div>

I see two issues:
1) You're sending the wrong object:
response = JSON.parse(json)
name_list[people] = {label: response.keys, value: response.keys[]}
send_event('whosHere', { items: response.values })
name_list[people] is your assumed result from processing your JSON, but you're sending response.values instead.
response is your parsed-JSON object returned. You must iterate over response to build an array of JSON objects to send to your List:
# new empty array of persons each time job is run
persons = []
# since your JSON file is single JSON object, using names for keys
person_names = response.keys
# for each name, do this
person_names.each do |name|
# use the 'name' key to get the value (their status)
person_status = response[name]
# create new Hash/JSON object for each person
# use "label" and "value" keys for Dashing List widget
person = Hash.new("label",name,"value",person_status)
# add this new person object to your persons array
persons.push(person)
end
Updated Job, which sends the persons array to Dashing instead:
require 'rubygems'
require 'json'
require 'pp'
name_list = Hash.new(0)
SCHEDULER.every '10s' do
json = File.read('list.json')
response = JSON.parse(json)
persons = []
person_names = response.keys
person_names.each do |name|
person_status = response[name]
person = Hash.new("label", name, "value", person_status)
persons.push(person)
end
send_event('whosHere', { items: persons })
end
2) Your JSON isn't formatted properly. Dashing's List widget is listening for an array of objects with "label" and "value" keys. Each person would be an individual JSON object, the person's name would be your "label" value, and their status would be their "value" value.
Here is the list-item HTML from the List widget:
<li data-foreach-item="items">
<span class="label" data-bind="item.label"></span>
<span class="value" data-bind="item.value"></span>
</li>
Each list item will display a single JSON object:
{
"label":"Mike",
"value":"Here"
}
So to display Mike and Jon's statuses in your list, you will need an array containing two JSON objects:
[
{
"label": "Mike",
"value": "Here"
},
{
"label": "Jon",
"value": "Out"
}
]
This array will be the "value" for your "items" key in your data object being sent to your List widget:
{
"items": [
{
"label": "Mike",
"value": "Here"
},
{
"label": "Jon",
"value": "Out"
}
]
}

Related

How to query key values from a hash of arrays of hashes

I have a JSONB payload in my database. This payload is from a GraphQL query of the shopify_api.
For the shop_order below, I am trying to query for the name of the fourth order in the node.
shop_order = {"data":{"orders":{"edges":[{"node":{"id":"gid://shopify/Order/2228134674512","name":"#1001","createdAt":"2020-05-01T18:46:04Z","shippingAddress":{"address1":"1234 Long Avenue, 2N","address2":"","city":"Chicago","province":"Illinois","provinceCode":"IL","zip":"55555"}}},{"node":{"id":"gid://shopify/Order/2239643451472","name":"#1002","createdAt":"2020-05-05T14:40:36Z","shippingAddress":{"address1":"1234 Long Avenue","address2":"2N","city":"Chicago","province":"Illinois","provinceCode":"IL","zip":"55555"}}},{"node":{"id":"gid://shopify/Order/2239950323792","name":"#1003","createdAt":"2020-05-05T16:35:38Z","shippingAddress":{"address1":"1234 Long Avenue","address2":"2N","city":"Chicago","province":"Illinois","provinceCode":"IL","zip":"55555"}}},{"node":{"id":"gid://shopify/Order/2239959105616","name":"#1004","createdAt":"2020-05-05T16:38:27Z","shippingAddress":{"address1":"1234 Long Avenue","address2":"2N","city":"Chicago","province":"Illinois","provinceCode":"IL","zip":"55555"}}}]}},"casted_data":{},"errors":[]}
order = shop_order[:data][:orders][:edges][3]
puts order
response > {:node=>{:id=>"gid://shopify/Order/2239959105616", :name=>"#1004", :createdAt=>"2020-05-05T16:38:27Z", :shippingAddress=>{:address1=>"1234 Long Avenue", :address2=>"2N", :city=>"Chicago", :province=>"Illinois", :provinceCode=>"IL", :zip=>"55555"}}}
order_to_a = shop_order[:data][:orders][:edges][3].to_a
puts order_to_a
response > node
{:id=>"gid://shopify/Order/2239959105616", :name=>"#1004", :createdAt=>"2020-05-05T16:38:27Z", :shippingAddress=>{:address1=>"1234 Long Avenue", :address2=>"2N", :city=>"Chicago", :province=>"Illinois", :provinceCode=>"IL", :zip=>"55555"}}
How do I query and display a specific value from a key that is inside a node?
It's not entirely clear what your intent is, but your access of elements in a hash can be streamlined using dig:
shop_order = {
"data": {
"orders": {
"edges": [
{}, {}, {}, {
"node": {
"name": '#1004',
"shippingAddress": {
"zip": '55555'
}
}
}
]
}
}
}
Access data using:
order = shop_order.dig(:data, :orders, :edges)[3]
# => {:node=>{:name=>"#1004", :shippingAddress=>{:zip=>"55555"}}}
or:
order = shop_order.dig(:data, :orders, :edges, 3)
# => {:node=>{:name=>"#1004", :shippingAddress=>{:zip=>"55555"}}}
How do I query and display a specific value from a key that is inside a node?
Huh? If you want information inside order, do the same sort of thing:
order.dig(:node, :name) # => "#1004"
order.dig(:node, :shippingAddress, :zip) # => "55555"
or:
shop_order.dig(:data, :orders, :edges, 3, :node, :name) # => "#1004"
shop_order.dig(:data, :orders, :edges, 3, :node, :shippingAddress, :zip) # => "55555"
Many times when we're walking through a complex hash of arrays we point to the array in a variable and then work from that point. It's similar to putting your finger on a page in a recipe, so we can go back to it quickly. We do the same when parsing HTML/XML, parsed JSON and YAML, etc.

Find attributes with prefix attribute name

I have html look likes :
<div in-prop in-alias="" in-type="teacher"><div in-name="Alice">Hello <i in-name="Wonderland">World</i></div></div>
I want to collect custom attribute names based on prefix in- attribute name, I have working code and returns ["prop", "alias", "type", "name", "name"]
require 'nokogiri'
class PartB
def get_scheme(html)
get_doc(html).map { |elm|
elm.attributes.map{|k, v| k.gsub("in-", "") if !k.nil? && k.include?("in-")}
}.flatten.compact
end
def get_doc(html)
Nokogiri::HTML(html).search('*')
end
end
but I think that is redundant, because I use search('*') and it will get all html tags. Is there any way to do that?
doc = Nokogiri::HTML('<div in-prop in-alias="" in-type="teacher"><div in-name="Alice">Hello <i in-name="Wonderland">World</i></div></div>')
a = doc.xpath("//#*[starts-with(name(), 'in')]")
a.map { |i| i.name[3..-1] } # => ["prop", "alias", "type", "name", "name"]

Strong params: How to process nested json code?

I'm trying to write an update method that processes JSON. The JSON looks like this:
{
"organization": {
"id": 1,
"nodes": [
{
"id": 1,
"title": "Hello",
"description": "My description."
},
{
"id": 101,
"title": "fdhgh",
"description": "My description."
}
]
}
}
My update method is as follows:
def update
organization = Organization.find(params[:id])
nodes = params[:organization][:nodes]
nodes.each do |node|
n = Node.find(node[:id])
unless n.update_attributes(node_params)
render json: organization, status: :failed
end
end
render json: diagram, status: :ok
end
private
def node_params
params.require(:organization).permit(nodes: [:title, :description])
end
Unfortunately, n.update_attributes(node_params) generates:
Unpermitted parameter: id
Unpermitted parameter: id
Unpermitted parameter: id
(0.2ms) BEGIN
(0.3ms) ROLLBACK
*** ActiveRecord::UnknownAttributeError Exception: unknown attribute 'nodes' for Node.
Does anyone see what I'm doing wrong and to write this update method?
On the unless n.update_attributes(node_params) line, you're trying to update Node n with nodes_params, which are all of the nodes from your JSON minus the ids:
{"nodes"=>[{"title"=>"Hello", "description"=>"My description."}, {"title"=>"fdhgh", "description"=>"My description."}]}
You could just add :id as a permitted node parameter, cut out the nodes assignment step, iterate over node_params instead, and just omit the :id when updating Node n. E.g.,
def update
organization = Organization.find(params[:id])
node_params.each do |node|
n = Node.find(node[:id])
unless n.update_attributes(node.except(:id))
render json: organization, status: :failed
end
end
render json: diagram, status: :ok
end
private
def node_params
params.require(:organization).permit(nodes: [:id, :title, :description])
end

Iterate and search a JSON array for the element in the array

I have a JSON array that looks like this:
response = {
"items"=>[
{
"tags"=>[
"random"
],
"timestamp"=>12345,
"storage"=>{
"url"=>"https://example.com/example",
"key"=>"mykeys"
},
"envelope"=>{
},
"log-level"=>"info",
"id"=>"random_id_test_1",
"campaigns"=>[
],
"user-variables"=>{
},
"flags"=>{
"is-test-mode"=>false
},
"message"=>{
"headers"=>{
"to"=>"random#example.com",
"message-id"=>"foobar#example.com",
"from"=>"noreply#example.com",
"subject"=>"new subject"
},
"attachments"=>[
],
"recipients"=>[
"result#example.com"
],
"size"=>4444
},
"event"=>"stored"
},
{
"tags"=>[
"flowerPower"
],
"timestamp"=>567890,
"storage"=>{
"url"=>"https://yahoo.com",
"key"=>"some_really_cool_keys_go_here"
},
"envelope"=>{
},
"log-level"=>"info",
"id"=>"some_really_cool_ids_go_here",
"campaigns"=>[
],
"user-variables"=>{
},
"flags"=>{
"is-test-mode"=>false
},
"message"=>{
"headers"=>{
"to"=>"another_great#example.com",
"message-id"=>"email_id#example.com",
"from"=>"from#example.com",
"subject"=>"email_looks_good"
},
"attachments"=>[
],
"recipients"=>[
"example#example.com"
],
"size"=>2222
},
"event"=>"stored"
}]
}
I am trying to obtain the "storage" "url" based on the "to" email.
How do I iterate through this array where x is just the element in the array
response['items'][x]["message"]["headers"]["to"]
Once I find the specific email that I need, it will stop and return the value of x which is the element number.
I was going to use that value for x and call response['items'][x]['storage']['url']
which will return the string for the URL.
I thought about doing this but there's gotta be a better way:
x = 0
user_email = another_great#example.com
while user_email != response['items'][x]["message"]["headers"]["to"] do
x+=1
value = x
puts value
end
target =
response['items'].detect do |i|
i['message']['headers']['to'] == 'another_great#example.com'
end
then
target['storage']['url']
This is another option by creating Hash with key of to's email. And on basis of it fetch required information like this:
email_hash = Hash.new
response["items"].each do |i|
email_hash[i["message"]["headers"]["to"]] = i
end
Now if you want to fetch "storage" "url" then simply do:
user_email = "another_great#example.com"
puts email_hash[user_email]["storage"]["url"] if email_hash[user_email]
#=> "https://yahoo.com"
You can use it as #Satoru suggested. As a suggestion, if you use case involves complex queries on json data (more complex than this), then you can store your data in mongodb, and can elegantly query anything.

Iterating through JSON array for a List in Dashing

I'm trying to create a List in Dashing and I've managed to bring in my external JSON, but I'm having problems iterating through it to create the new JSON array to post it out to my List widget. Here's the current code:
require 'rubygems'
require 'json'
require 'pp'
name_list = Hash.new({ value: 0 })
SCHEDULER.every '10s' do
json = File.read('/Users/research/inoutdash/sweet_dashboard_project/jobs/list.json')
response = JSON.parse(json)
name_list[response] = {label: response.keys, value: response.values}
send_event('whosHere', { items: name_list.values })
puts response.keys
puts response.values
end
Here's my JSON file it's reading from:
{
"Mike":"Here",
"Jon": "Out"
}
The output to the widget currently looks like this:
MikeJon Here,Out
How to I properly iterate through the parsed json response and pass it to the send_event?
Just in reply to Matt's question:
So what I need is one 'whosHere' event with all the values from the JSON file formatted for the List widget - which needs JSON formatted as label: X, value: Y.
So what I need it to do is format it as a json array with:
label: "Mike", value: "Here"
label: "Jon", value: "Out"
where now it's storing as:
label: Mike,Jon, value:Here,out
You can use collect to accomplish this. It iterates over a collection and returns an Array where each element is the result of a block.
name_list = response.collect { |(name, status)| { :label => name, :value => status } }
# [{:label=>"Mike", :value=>"Here"}, {:label=>"Jon", :value=>"Out"}]

Resources