Rails 4 + Grape Gem + Formatted date and date in JSON response - json

Rails 4 + Grape Gem + Formatted date and date in JSON response
In my grape response - I am trying to find formatted date in JSON response, but unable to find formatted date.
Model - Item
class Item < ActiveRecord::Base
def self.list_view
select("items.id as id, items.availability_date as date, items.name as name, items.description")
.order("id ASC").as_json( except: [ :id, :created_at, :updated_at ])
end
end
API Call
lib/api/v1/item.rb
module API
module V1
class Items < Grape::API
version 'v1'
format :json
resource :item_data do
get do
Item.list_view
end
end
end
end
end
Request
http://localhost:3000/api/v1/item_data
Response
[
{
"description": "Product One Description",
"date": "2016-03-19",
"name": "Product One"
},
{
"description": "Product 2 Desc",
"date": "2016-03-20",
"name": "Product 2"
}
]
Here I am using date_format gem to show formatted date, but unable to produce the output.
Trying to use -
DateFormat.change_to(date, "ONLY_DATE")

require 'date_format'
Try include it on lib/api/v1/item.rb

Related

Databricks - explode JSON from SQL column with PySpark

New to Databricks. Have a SQL database table that I am creating a dataframe from. One of the columns is a JSON string. I need to explode the nested JSON into multiple columns. Have used this post and this post to get me to where I am at now.
Example JSON:
{
"Module": {
"PCBA Serial Number": "G7456789",
"Manufacturing Designator": "DISNEY",
"Firmware Version": "0.0.0",
"Hardware Revision": "46858",
"Manufacturing Date": "10/17/2018 4:04:25 PM",
"Test Result": "Fail",
"Test Start Time": "10/22/2018 6:14:14 AM",
"Test End Time": "10/22/2018 6:16:11 AM"
}
Code so far:
#define schema
schema = StructType(
[
StructField('Module',ArrayType(StructType(Seq
StructField('PCBA Serial Number',StringType,True),
StructField('Manufacturing Designator',StringType,True),
StructField('Firmware Version',StringType,True),
StructField('Hardware Revision',StringType,True),
StructField('Test Result',StringType,True),
StructField('Test Start Time',StringType,True),
StructField('Test End Time',StringType,True))), True) ,True),
StructField('Test Results',StringType(),True),
StructField('HVM Code Errors',StringType(),True)
]
#use from_json to explode json by applying it to column
df.withColumn("ActivityName", from_json("ActivityName", schema))\
.select(col('ActivityName'))\
.show()
Error:
SyntaxError: invalid syntax
File "<command-1632344621139040>", line 10
StructField('PCBA Serial Number',StringType,True),
^
SyntaxError: invalid syntax
As you are using pyspark then types should be StringType() instead of StringType and remove Seq replace it with []
schema = StructType([StructField('Module',ArrayType(StructType([
StructField('PCBA Serial Number',StringType(),True),
StructField('Manufacturing Designator',StringType(),True),
StructField('Firmware Version',StringType(),True),
StructField('Hardware Revision',StringType(),True),
StructField('Test Result',StringType(),True),
StructField('Test Start Time',StringType(),True),
StructField('Test End Time',StringType(),True)])), True),
StructField('Test Results',StringType(),True),
StructField('HVM Code Errors',StringType(),True)])

Ruby Parsing json array with OpenStruct

I'm trying to parse a json file with OpenStruct. Json file has an array for Skills. When I parse it I get some extra "garbage" returned. How do I get rid of it?
json
{
"Job": "My Job 1",
"Skills": [{ "Name": "Name 1", "ClusterName": "Cluster Name 1 Skills"},{ "Name": "Name 2", "ClusterName": "Cluster Name 2 Skills"}]
}
require 'ostruct'
require 'json'
json = File.read('1.json')
job = JSON.parse(json, object_class: OpenStruct)
puts job.Skills
#<OpenStruct Name="Name 1", ClusterName="Cluster Name 1 Skills">
#<OpenStruct Name="Name 2", ClusterName="Cluster Name 2 Skills">
If by garbage, you mean #<OpenStruct and ">, it is just the way Ruby represents objects when called with puts. It is useful for development and debugging, and it makes it easier to understand the difference between a String, an Array, an Hash and an OpenStruct.
If you just want to display the name and cluster name, and nothing else :
puts job.Job
job.Skills.each do |skill|
puts skill.Name
puts skill.ClusterName
end
It returns :
My Job 1
Name 1
Cluster Name 1 Skills
Name 2
Cluster Name 2 Skills
EDIT:
When you use job = JSON.parse(json, object_class: OpenStruct), your job variable becomes an OpenStruct Ruby object, which has been created from a json file.
It doesn't have anything to do with json though: it is not a json object anymore, so you cannot just write it back to a .json file and expect it to have the correct syntax.
OpenStruct doesn't seem to work well with to_json, so it might be better to remove object_class: OpenStruct, and just work with hashes and arrays.
This code reads 1.json, convert it to a Ruby object, adds a skill, modifies the job name, writes the object to 2.json, and reads it again as JSON to check that everything worked fine.
require 'json'
json = File.read('1.json')
job = JSON.parse(json)
job["Skills"] << {"Name" => "Name 3", "ClusterName" => "Cluster Name 3 Skills"}
job["Job"] += " (modified version)"
# job[:Fa] = 'blah'
File.open('2.json', 'w'){|out|
out.puts job.to_json
}
require 'pp'
pp JSON.parse(File.read('2.json'))
# {"Job"=>"My Job 1 (modified version)",
# "Skills"=>
# [{"Name"=>"Name 1", "ClusterName"=>"Cluster Name 1 Skills"},
# {"Name"=>"Name 2", "ClusterName"=>"Cluster Name 2 Skills"},
# {"Name"=>"Name 3", "ClusterName"=>"Cluster Name 3 Skills"}]}

transform JSON to hashmap in Ruby

I am trying to convert a json file which contain object and array to a JSON file.
Below is the JSON file
{
"localbusiness":{
"name": "toto",
"phone": "+11234567890"
},
"date":"05/02/2016",
"time":"5:00pm",
"count":"4",
"userInfo":{
"name": "John Doe",
"phone": "+10987654321",
"email":"john.doe#unknown.com",
"userId":"user1234333"
}
}
my goal is to save this is a database such as MongoId. I would like to use map to get something like:
localbusiness_name => "toto",
localbusiness_phone => "+11234567890",
date => "05/02/2016",
...
userInfo_name => "John Doe"
...
I have tried map but it's not splitting the array of local business or userInfo
def format_entry
ps = #params.map do | h |
ps.merge!(h)
##logger.info("entry #{h}")
end
##logger.info("formatting the data #{ps}")
ps
end
I do not really how to parse each entry and rebuild the name
It looks like to me you are trying to "flatten" the inner hashes into one big hash. Flatten being incorrect because you want to prepend the hash's key to the sub-hash's key. This will require looping through the hash, and then looping again through each sub hash. This code example will only work if you have 1 layer deep. if you have multiple layers, then I would suggest making two methods, or a recursive method.
#business = { # This is a hash, not a json blob, but you can take json and call JSON.parse(blob) to turn it into a hash.
"localbusiness":{
"name": "toto",
"phone": "+11234567890"
},
"date":"05/02/2016",
"time":"5:00pm",
"count":"4",
"userInfo":{
"name": "John Doe",
"phone": "+10987654321",
"email":"john.doe#unknown.com",
"userId":"user1234333"
}
}
#squashed_business = Hash.new
#business.each do |k, v|
if v.is_a? Hash
v.each do |key, value|
#squashed_business.merge! (k.to_s + "_" + key.to_s) => value
end
else
#squashed_business.merge! k => v
end
end
I noticed that you are getting "unexpected" outcomes when enumerating over a hash #params.each { |h| ... } because it gives you both a key and a value. Instead you want to do #params.each { |key, value| ... } as I did in the above code example.

Limit fields of JSON response

I'm using the PhoenixFramework and the library Poison.
Current I'm working on an REST API. Now I need to encode the model Book in two different ways.
In a list of all books with only base informations (GET /books)
In a detailed view with all informations (GET /book/1)
GET /books
{
"books": [
{
"id": 1,
"name": "Book one"
},
{
"id": 2,
"name": "Book two"
},
// ...
]
}
GET /books/1
{
"book": {
"id": 1,
"name": "Book one",
"description": "This is book one.",
"author": "Max Mustermann",
// ...
}
}
Encoder of Book
defimpl Poison.Encoder, for: MyProject.Book do
#attributes [:id, :name, :description, :author]
def encode(project, _options) do
project
|> Map.take(#attributes)
|> Poison.encode!
end
end
Snippet controller
def index(conn, _params) do
books = Repo.all(Book)
json(conn, %{books: books})
end
How to limit the fields? I search for a option like :only or :exclude.
Has anyone experience with this problem?
Thanks for help!
You can use render_many/4 and render_one/4:
defmodule MyApp.BookView do
def render("index.json", %{books: books}) do
render_many(books, __MODULE__, "book.json")
end
def render("show.json", %{book: book}) do
render_one(book, __MODULE__, "full_book.json")
end
def render("book.json", %{book: book}) do
%{
id: book.id,
name: book.name
}
end
def render("full_book.json", %{book: book}) do
%{
id: book.id,
name: book.name,
description: description,
author: book.author
...
}
end
end
Please note that the name in assigns (2nd argument of render) is determined by the module. See Render many to many relationship JSON in Phoenix Framework for an example of using a different name.
You can then call this from your controller using:
render(conn, "index.json", books: Repo.all(Book))
render(conn, "show.json", book: Repo.get(Book, 1))

Building a JSON map for a self-referencing Ecto model

I have an Ecto model as such:
defmodule Project.Category do
use Project.Web, :model
schema "categories" do
field :name, :string
field :list_order, :integer
field :parent_id, :integer
belongs_to :menu, Project.Menu
has_many :subcategories, Project.Category, foreign_key: :parent_id
timestamps
end
#required_fields ~w(name list_order)
#optional_fields ~w(menu_id parent_id)
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
end
end
As you can see the Category model can reference itself via the subcategories atom.
Here is the view associated with this model:
defmodule Project.CategoryView do
use Project.Web, :view
def render("show.json", %{category: category}) do
json = %{
id: category.id,
name: category.name,
list_order: category.list_order
parent_id: category.parent_id
}
if is_list(category.subcategories) do
children = render_many(category.subcategories, Project.CategoryView, "show.json")
Map.put(json, :subcategories, children)
else
json
end
end
end
I have an if condition on subcategories so that I can play nice with Poison when they are not preloaded.
Finally, here are my 2 controller functions that invoke this view:
defmodule Project.CategoryController do
use Project.Web, :controller
alias Project.Category
def show(conn, %{"id" => id}) do
category = Repo.get!(Category, id)
render conn, "show.json", category: category
end
def showWithChildren(conn, %{"id" => id}) do
category = Repo.get!(Category, id)
|> Repo.preload [:subcategories, subcategories: :subcategories]
render conn, "show.json", category: category
end
end
The show function works fine:
{
"parent_id": null,
"name": "a",
"list_order": 4,
"id": 7
}
However, my showWithChildren function is limited to 2 levels of nesting because of how I use preloading:
{
"subcategories": [
{
"subcategories": [
{
"parent_id": 10,
"name": "d",
"list_order": 4,
"id": 11
}
],
"parent_id": 7,
"name": "c",
"list_order": 4,
"id": 10
},
{
"subcategories": [],
"parent_id": 7,
"name": "b",
"list_order": 9,
"id": 13
}
],
"parent_id": null,
"name": "a",
"list_order": 4,
"id": 7
}
For example, the category item 11 above also has subcategories but I am unable to reach them. Those subcategories can also have subcategories themselves, so the potential depth of the hierarchy is n.
I am aware that I need some recursive magic but since I'm new to both functional programming and Elixir, I cannot wrap my head around it. Any help is greatly appreciated.
You can consider doing the preloading in the view, so it works recursively:
def render("show.json", %{category: category}) do
%{id: category.id,
name: category.name,
list_order: category.list_order
parent_id: category.parent_id}
|> add_subcategories(category)
end
defp add_subcategories(json, %{subcategories: subcategories}) when is_list(subcategories) do
children =
subcategories
|> Repo.preload(:subcategories)
|> render_many(Project.CategoryView, "show.json")
Map.put(json, :subcategories, children)
end
defp add_subcategories(json, _category) do
json
end
Keep in mind this is not ideal for two reasons:
Ideally you don't want to do queries in views (but this is is recursive, so it is easier to piggyback in the view rendering)
You are going to emit multiple queries for the second level of subcategories
There is a book called SQL Antipatterns and, if I am not mistaken, it covers how to write tree structures. Your example is exposed as an antipattern in one of the free chapters. It is an excellent book and they explore solutions for all antipatterns.
PS: you want show_with_children and not showWithChildren.