Build a grouped JSON object - ruby-on-rails

I am building a Rails/Backbone app and I will show users in a table.
I want to create a JSON tree which is grouped on the user ID/Name.
This is how it looks now
[{
"total_entries": 2
},
{
"entries": [{
"id": 21,
"status": "pending",
"starts_at_date": "2018-02-02",
"starts_at_time": "12:00",
"ends_at_date": "2018-02-02",
"ends_at_time": "12:00",
"description": "",
"ttype": "vacation",
"sum": 0,
"user": {
"id": 1,
"fullname": "Marcus Lurem"
},
"timetype": null,
"pause": 0,
"can_manage": true
},
{
"id": 22,
"status": "pending",
"starts_at_date": "2018-02-07",
"starts_at_time": "12:00",
"ends_at_date": "2018-02-07",
"ends_at_time": "12:00",
"description": "",
"ttype": "doctor",
"sum": 0,
"user": {
"id": 2,
"fullname": "Anna Palmgren"
},
"timetype": null,
"pause": 0,
"can_manage": true
}
]
}
]
I need it to be grouped on the name.
This is how I build the JSON object now.
json.array! [0,1] do |index|
if index == 0
json.total_entries #total
else
json.entries #events do |event|
json.extract! event, :id, :starts_at_date, :starts_at_time, :ends_at_date, :ends_at_time, :description, :ttype
json.sum event.sum
json.user event.user, :id, :fullname
json.can_manage true
end
end
end
Update
Should look like this more or less.
Marcus Lurem
Id
Status
starts_at_date
description
Anna Palmgren
Id
Status
starts_at_date
description

You can use something like this:
json.entries #events.group_by(&:user) do |user, events|
json.user :id, :fullname
json.events events do |event|
json.extract! event, :id, :starts_at_date, :starts_at_time, :ends_at_date, :ends_at_time, :description, :ttype
end
end

Related

How can I save external JSON data to DB(sqlite) with Rails

I have a form to add json url form, If user add json url and submit form, I want to save all json data to db.[form]1
Fetching json data from url using httparty gem works perfectly, now to store json data to db json_url
json data
{
"restaurant_name": "Restaurant 3",
"address": "xyz address",
"country": "United States",
"currency": "USD",
"client_key": "12345",
"client_name": "Client 3",
"client_email": "test3#mail.com",
"client_phone": "9876",
"product_tier": "tier1",
"brand_logo_large": {
"ID": 37,
"id": 37,
"title": "bait-al-bahar-logo-design",
"filename": "bait-al-bahar-logo-design.png",
"filesize": 105071,
"url": "http://codekyt.in/froodle-wp/wp-content/uploads/2019/01/bait-al-bahar-logo-design.png",
"link": "http://codekyt.in/froodle-wp/projects/res-1-client-1/bait-al-bahar-logo-design/",
"alt": "",
"author": "1",
"description": "",
"caption": "",
"name": "bait-al-bahar-logo-design",
"status": "inherit",
"uploaded_to": 35,
"date": "2019-01-04 11:11:48",
"modified": "2019-01-04 11:13:01",
"menu_order": 0,
"mime_type": "image/png",
"type": "image",
"subtype": "png",
"icon": "http://codekyt.in/froodle-wp/wp-includes/images/media/default.png",
"width": 600,
"height": 500,
"sizes": {
"thumbnail": "http://codekyt.in/froodle-wp/wp-content/uploads/2019/01/bait-al-bahar-logo-design-150x150.png",
"thumbnail-width": 150,
"thumbnail-height": 150,
"medium": "http://codekyt.in/froodle-wp/wp-content/uploads/2019/01/bait-al-bahar-logo-design-300x250.png",
"medium-width": 300,
"medium-height": 250,
"medium_large": "http://codekyt.in/froodle-wp/wp-content/uploads/2019/01/bait-al-bahar-logo-design.png",
"medium_large-width": 600,
"medium_large-height": 500,
"large": "http://codekyt.in/froodle-wp/wp-content/uploads/2019/01/bait-al-bahar-logo-design.png",
"large-width": 600,
"large-height": 500
}
}
}
I created a form to user & added json url,
<%= form_tag("/settings/json_url", method: "post") do %>
<%= label_tag :json_url %>
<%= text_field_tag :json_url %>
<%= submit_tag 'submit' %>
<% end %>
In db,
t.string :key
t.string :value
t.string :json_url
In controller,
def json_url
# I am getting confused here
end

How to merge a JSON-object in Jbuilder

I am creating a rails app with Backbone/Marionette frontend.
I want to take the JSON-object below and transform it.
This is how it looks now
[{
"total_entries": 4
},
{
"entries": [{
"user": {
"id": 1,
"fullname": "Paul Paulsen"
},
"reports": [{
"id": 1,
"name": "Anna Pearson",
"relation": "wife",
"phone": "2232",
"email": "adsas#sss.se"
}
]
},
{
"user": {
"id": 2,
"fullname": "Anna Palmgren"
},
"reports": [{
"id": 3,
"name": "Mika",
"relation": "Andersen",
"phone": "12312321",
"email": "aas#sss.se"
}]
}
]
}
]
I want to make it look like this
[{
"total_entries": 4
},
{
"entries": [{
"id": 1,
"fullname": "Paul Paulsen"
}, {
"id": 1,
"name": "Anna Pearson",
"relation": "wife",
"phone": "2232",
"email": "adsas#sss.se"
}, {
"id": 1,
"fullname": "Anna Palmgren"
}, {
"id": 3,
"name": "Mika",
"relation": "Andersen",
"phone": "12312321",
"email": "aas#sss.se"
}
]
}
]
This is the Rabl file/code I use
json.array! [0,1] do |index|
if index == 0
json.total_entries #total
else
json.entries #reports.group_by(&:user) do |user, reports|
json.user user, :id, :fullname
json.reports reports do |report|
json.extract! report, :id, :name, :relation, :phone, :email
end
end
end
end
Try this ruby snippet.
x= [{
"total_entries": 4
},
{
"entries": [{
"user": {
"id": 1,
"fullname": "Paul Paulsen"
},
"reports": [{
"id": 1,
"name": "Anna Pearson",
"relation": "wife",
"phone": "2232",
"email": "adsas#sss.se"
}
]
},
{
"user": {
"id": 2,
"fullname": "Anna Palmgren"
},
"reports": [{
"id": 3,
"name": "Mika",
"relation": "Andersen",
"phone": "12312321",
"email": "aas#sss.se"
}]
}
]
}
]
x[1][:entries] = x[1][:entries].map{|p| [p[:user], p[:reports]]}.flatten

json data stripe webhook

Im trying to implement stripe webhooks in my rails app. The webhook send this json data.
{ "created": 1326853478, "livemode": false, "id": "evt_00000000000000", "type": "invoice.payment_succeeded", "object": "event", "request": null, "pending_webhooks": 1, "api_version": "2017-06-05", "data": {
"object": {
"id": "in_00000000000000",
"object": "invoice",
"amount_due": 500,
"application_fee": null,
"attempt_count": 1,
"attempted": true,
"charge": "_00000000000000",
"closed": true,
"currency": "usd",
"customer": "cus_00000000000000",
"date": 1501950408,
"description": null,
"discount": null,
"ending_balance": 0,
"forgiven": false,
"lines": {
"data": [
{
"id": "sub_BLXcTSekdHflq5",
"object": "line_item",
"amount": 500,
"currency": "usd",
"description": null,
"discountable": true,
"livemode": true,
"metadata": {
},
"period": {
"start": 1507246341,
"end": 1509924741
},
"plan": {
"id": "Subscriber",
"object": "plan",
"amount": 500,
"created": 1501901993,
"currency": "usd",
"interval": "month",
"interval_count": 1,
"livemode": false,
"metadata": {
},
"name": "Subscriber",
"statement_descriptor": "WatchBuddie Stream sub",
"trial_period_days": null
},
"proration": false,
"quantity": 1,
"subscription": null,
"subscription_item": "si_1AyqWnFr5iCt1Tv7n23zDLOM",
"type": "subscription"
}
],
"total_count": 1,
"object": "list",
"url": "/v1/invoices/in_1AnV6yFr5iCt1Tv7PnqZ0EUA/lines"
},
"livemode": false,
"metadata": {
},
"next_payment_attempt": null,
"paid": true,
"period_end": 1501950408,
"period_start": 1501950408,
"receipt_number": null,
"starting_balance": 0,
"statement_descriptor": null,
"subscription": "sub_00000000000000",
"subtotal": 500,
"tax": null,
"tax_percent": null,
"total": 500,
"webhooks_delivered_at": 1501950409
} } }
webhook method
def webhooks
begin
event_json = JSON.parse(request.body.read)
event_object = event_json['data']['object']
#refer event types here https://stripe.com/docs/api#event_types
case event_json['type']
when 'invoice.payment_succeeded'
#Update the total subscription total
#Send in email to the user telling them that they resubbed
logger.debug event_object['lines']['id']
end
My question is how do I get the "id": "sub_BLXcTSekdHflq5" I tried
logger.debug event_object['lines']['id']
It doesn't seem to be working what am I doing wrong? I'm sure its just a syntax thing that I am not understanding.
Thanks for all the help!
Figured it out the lines data was in a hash so you had to iterate over it. This is what I did for anyone who comes across this.
event_object['lines']['data'].each{ |i|
sub_id = i['id']
logger.debug sub_id
}

Function Score attribute to rank searches based on clicks not working with elastic search and rails

I have implemented the function score attribute in my document model which contains a click field that keeps tracks of a number of view per document. Now I want the search results to get more priority and appear at the top based on the clicks per search
My document.rb code
require 'elasticsearch/model'
def self.search(query)
__elasticsearch__.search(
{
query: {
function_score: {
query: {
multi_match: {
query: query,
fields: ['name', 'service'],
fuzziness: "AUTO"
}
},
field_value_factor: {
field: 'clicks',
modifier: 'log1p',
factor: 2
}
}
}
}
)
end
settings index: { "number_of_shards": 1,
analysis: {
analyzer: {
edge_ngram_analyzer: { type: "custom", tokenizer: "standard", filter:
["lowercase", "edge_ngram_filter", "stop", "kstem" ] },
}
},
filter: { ascii_folding: { type: 'asciifolding', preserve_original: true
},
edge_ngram_filter: { type: "edgeNGram", min_gram: "3", max_gram:
"20" }
}
} do
mapping do
indexes :name, type: "string", analyzer: "edge_ngram_analyzer",
term_vector: "with_positions"
indexes :service, type: "string", analyzer: "edge_ngram_analyzer",
term_vector: "with_positions"
end
end
end
Search View is here
<h1>Document Search</h1>
<%= form_for search_path, method: :get do |f| %>
<p>
<%= f.label "Search for" %>
<%= text_field_tag :query, params[:query] %>
<%= submit_tag "Go", name: nil %>
</p>
<% end %>
<% if #documents %>
<ul class="search_results">
<% #documents.each do |document| %>
<li>
<h3>
<%= link_to document.name, controller: "documents", action: "show",
id: document._id %>
</h3>
</li>
<% end %>
</ul>
<% else %>
<p>Your search did not match any documents.</p>
<% end %>
<br/>
When I search for Estamp, I get the results follow in the following order:
Franking and Estamp # clicks 5
Notary and Estamp #clicks 8
So clearly when the Notary and Estamp had more clicks it does not come to the top of the search.How can I achieve this?
This is what I get when I run it on the console.
POST _search
"hits": {
"total": 2,
"max_score": 1.322861,
"hits": [
{
"_index": "documents",
"_type": "document",
"_id": "13",
"_score": 1.322861,
"_source": {
"id": 13,
"name": "Franking and Estamp",
"service": "Estamp",
"user_id": 1,
"clicks": 7
},
{
"_index": "documents",
"_type": "document",
"_id": "14",
"_score": 0.29015404,
"_source": {
"id": 14,
"name": "Notary and Estamp",
"service": "Notary",
"user_id": 1,
"clicks": 12
}
}
]
Here the score of the documents is not getting updated based on the clicks
Without seeing your indexed data it's not easy to answer. But looking at the query one thing comes to my mind, I'll show it with short example:
Example 1:
I've indexed following documents:
{"name":"Franking and Estampy", "service" :"text", "clicks": 5}
{"name":"Notary and Estamp", "service" :"text", "clicks": 8}
Running the same query you provided gave this result:
"hits": {
"total": 2,
"max_score": 4.333119,
"hits": [
{
"_index": "script",
"_type": "test",
"_id": "AV2iwkems7jEvHyvnccV",
"_score": 4.333119,
"_source": {
"name": "Notary and Estamp",
"service": "text",
"clicks": 8
}
},
{
"_index": "script",
"_type": "test",
"_id": "AV2iwo6ds7jEvHyvnccW",
"_score": 3.6673431,
"_source": {
"name": "Franking and Estampy",
"service": "text",
"clicks": 5
}
}
]
}
So everything is fine - document with 8 clicks got higher scoring (_score field value) and the order is correct.
Example 2:
I noticed in your query that name field is boosted with high factor. So what would happen if I had following data indexed?
{"name":"Franking and Estampy", "service" :"text", "clicks": 5}
{"name":"text", "service" :"Notary and Estamp", "clicks": 8}
And result:
"hits": {
"total": 2,
"max_score": 13.647502,
"hits": [
{
"_index": "script",
"_type": "test",
"_id": "AV2iwo6ds7jEvHyvnccW",
"_score": 13.647502,
"_source": {
"name": "Franking and Estampy",
"service": "text",
"clicks": 5
}
},
{
"_index": "script",
"_type": "test",
"_id": "AV2iwkems7jEvHyvnccV",
"_score": 1.5597181,
"_source": {
"name": "text",
"service": "Notary and Estamp",
"clicks": 8
}
}
]
}
Although Franking and Estampy has only 5 clicks, it has much much higher scoring than the second document with greater number of clicks.
So the point is that in your query, the number of clicks is not the only factor that has an impact on scoring and final order of documents. Without the real data it's only a guess from my side. You can run the query yourself with some REST client and check scoring/field/matching phrases.
Update
Based on your search result - you can see that document with id=13 has Estamp term in both fields (name and service). That is the reason why this document got higer scoring (it means that in the algorithm of calculating scoring it is more important to have the term in both fields than have higher number of clicks). If you want clicks field to have bigger impact on the scoring, try to experiment with factor (probably should be higher) and modifier ("modifier": "square" could work in your case). You can check possible values here.
Try for example this combination:
{
"query": {
"function_score": {
... // same as before
},
"field_value_factor": {
"field": "clicks" ,
"modifier": "square",
"factor": 3
}
}
}
}
Update 2 - scoring based only on number of clicks
If the only parameter that should have an impact on scoring should be the value in clicks field, you can try to use "boost_mode": "replace" - in this case only function score is used, the query score is ignored. So the frequency of Estamp term in name and service fields will have no impact on the scoring. Try this query:
{
"query": {
"function_score": {
"query": {
"multi_match": {
"query": "Estamp",
"fields": [ "name", "service"],
"fuzziness": "AUTO"
}
},
"field_value_factor": {
"field": "clicks",
"factor": 1
},
"boost_mode": "replace"
}
}
}
It gave me:
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 5,
"hits": [
{
"_index": "script",
"_type": "test",
"_id": "AV2nI0HkJPYn0YKQxRvd",
"_score": 5,
"_source": {
"name": "Notary and Estamp",
"service": "Notary",
"clicks": 5
}
},
{
"_index": "script",
"_type": "test",
"_id": "AV2nIwKvJPYn0YKQxRvc",
"_score": 4,
"_source": {
"name": "Franking and Estamp",
"service": "Estamp",
"clicks": 4
}
}
]
}
}
This may be the one you are looking for (note the values "_score": 5 and "_score": 4 are matching the number of clicks).

how to make this method more efficient Rails 3?

i want to make my method more efficient. my method take so much time in response. so please edit my code to get more efficient response.i am very thank full to you if you make this method more more efficient. and you are master in ruby on rails then if you can make this method with joins then what a wounder full method is it for me.
thanks
def all_shows_with_videos
#arr = []
tvs = Tv.all
tvs.each do |tv|
tv_tmp = {:name => tv.name, :id => tv.id}
tv_tmp[:videos] = tv.videos
tv_tmp[:seasons] = []
season_tmp = {}
tv.seasons.each do |season|
season_tmp = {:name => season.name, :id => season.id}
season_tmp[:videos] = season.videos
season_tmp[:episodes] = []
season.episodes.each do |episode|
season_tmp[:episodes] << {:name => episode.name, :id => episode.id} if episode.videos?
end
tv_tmp[:seasons].push(season_tmp) if !season_tmp[:videos].blank? or !season_tmp[:episodes].blank?
end
#arr.push(tv_tmp) if !tv_tmp[:videos].blank? or !tv_tmp[:seasons].blank?
end
#arr = Kaminari.paginate_array(#arr).page(params[:page]).per(5)
respond_to do |format|
format.json {render :json => #arr}
end
end
and output is
[
{
"name": "Iron Man",
"id": 95,
"videos": [
{
"id": 1,
"name": "Trailer 1",
"site": "Youtube.com",
"link": "Google.com",
"quality": null,
"video_type": null,
"videoable_id": 95,
"videoable_type": "Tv",
"created_at": "2014-05-26T07:05:39+05:00",
"video_source": null,
"video_source_cd": null
}
],
"seasons": []
},
{
"name": "How I Met Your Mother",
"id": 100,
"videos": [
{
"id": 13,
"name": "Trailer 1",
"site": null,
"link": "google.com",
"quality": "1020",
"video_type": "Trailer",
"videoable_id": 100,
"videoable_type": "Tv",
"created_at": "2014-06-09T10:05:03+05:00",
"video_source": null,
"video_source_cd": null
}
],
"seasons": []
},
{
"name": "my tv",
"id": 124,
"videos": [
{
"id": 59,
"name": "Trailer 1",
"site": null,
"link": "google.com",
"quality": "1020",
"video_type": "Trailer",
"videoable_id": 124,
"videoable_type": "Tv",
"created_at": "2014-06-20T06:59:32+05:00",
"video_source": null,
"video_source_cd": null
}
],
"seasons": []
},
{
"name": "Game of Thrones",
"id": 151,
"videos": [
{
"id": 129,
"name": "",
"site": null,
"link": null,
"quality": null,
"video_type": "Season",
"videoable_id": 151,
"videoable_type": "Tv",
"created_at": "2014-09-02T11:13:40+05:00",
"video_source": null,
"video_source_cd": null
},
{
"id": 130,
"name": "",
"site": null,
"link": "",
"quality": null,
"video_type": null,
"videoable_id": 151,
"videoable_type": "Tv",
"created_at": "2014-09-02T11:13:40+05:00",
"video_source": null,
"video_source_cd": null
},
{
"id": 131,
"name": "",
"site": null,
"link": "",
"quality": null,
"video_type": null,
"videoable_id": 151,
"videoable_type": "Tv",
"created_at": "2014-09-02T11:13:40+05:00",
"video_source": null,
"video_source_cd": null
}
],
"seasons": []
},
{
"name": "Under the Dome",
"id": 160,
"videos": [],
"seasons": [
{
"name": "Season Specials",
"id": 267,
"videos": [],
"episodes": [
{
"name": "Inside Chester's Mill",
"id": 1112
}
]
}
]
}
]
First step is to move your logic to model, that way your controller will get more skinnier
in your Tv model
def self.all_with_seasons_and_episodes
tvs = includes(:videos, :seasons => [:videos, :episodes]).all
# loads all tvs and all its videos and seasons, seasons videos and episodes
tvs.map do |tv|
{
name: tv.name,
id: tv.id,
videos: tv.videos,
seasons: tv.map_seasons
}
end
end
private
def map_seasons
seasons.map do |s|
{
id: s.id,
name: s.name,
videos: s.videos,
episode: s.episodes.map {|e| {name: e.name, id: e.id} }
}
end
end
now you can use in like
def all_shows_with_videos
arr = Tv.all_with_seasons_and_episodes
#arr = Kaminari.paginate_array(arr).page(params[:page]).per(5)
respond_to do |format|
format.json {render :json => #arr}
end
end

Resources