I have this output from a query in MongoDB using Ruby :
irb(main):087:0> data = col.find({}, :fields => ["result", "time"])
=> <Mongo::Cursor:0x14768b8 namespace='spark.light' #selector={} #cursor_id=>
irb(main):090:0> data.first
=> {"_id"=>BSON::ObjectId('537d961197c20960ad000001'), "result"=>2177, "time"=>2014-05-22 06:15:45 UTC}
Now I want to give this data to chartkick running on Ruby on Rails to draw a linechart
.The input shall looks like this :
"2014-05-22 06:15:45 UTC" => "2177"
Is there any clean way to do that ?
This would transform records into a hash of time => result:
Hash[data.map do |item|
[item['time'], item['result']]
end]
# => { 2014-05-22 06:15:45 UTC => 2177, 2014-05-22 06:20:00 UTC => 1000 }
Use Hash#values_at with Hash::[] method :
Hash[data.first.values_at('time', 'result')]
Related
I am generating a report from my ruby on rails app that would run everyday at 7 in the morning.
This report would collect the results from the previous day.
So basically I want query previous day's results to my Mongo collection. This is what I am tried:
logs = Log.where(:comp => comp, :created_at => (Date.today -1))
I got the following error:
BSON::InvalidDocument: Date is not currently supported; use a UTC Time instance instead.
What is the best approach to get the aboce result:
This is the the format of my date in my db:
"created_at" : ISODate("2015-12-02T23:12:38.076Z")
You can do something like this in mongoid:
logs = Log.where(:comp => comp, :created_at.gte => (Date.today -1 ).to_datetime)
Date.to_datetime will default to midnight as shown here:
(Date.today - 1).to_datetime
=> Wed, 30 Mar 2016 00:00:00 +0000
I've been over and over the documentation of the (otherwise) excellent Mysql2 gem.
I have a production Rails app hosted on a server running in UTC. The app's config.time_zone is set to "Eastern Time (US & Canada)."
We have an internal MS Access-headed application (which, thankfully, uses a MySQL DB) that the aforementioned Rails app connects to to sync product data, production schedules, etc. This MySQL server cannot be moved into the cloud due to the application's limitations, and -- needless to say -- we cannot change its timezone handling to UTC -- everything is in EST.
When I connect from our Rails app to our local database to retrieve data, Mysql2 (by default) casts all of the native data into digestible Ruby objects. However, the only way that I can get the Rails app to interpret dates correctly is to send :cast => false during the Mysql2 query command, and then, in the response handling, run Time.zone.parse( string ) on the results. This necessitates native Ruby object casting, which is far less efficient than the casting functions built in to the gem.
I tried passing the database_timezone and application_timezone options mentioned in the documentation, but the :local symbols do not have the intended effect of using the app's config.time_zone property.
thehost.query("SELECT NOW() AS n;", :symbolize_keys => true).each { |result| p result[:n] }
2015-05-19 16:28:41 +0000
thehost.query("SELECT NOW() AS n;", :symbolize_keys => true, :database_timezone => :local, :application_timezone => :local).each { |result| p result[:n] }
2015-05-19 16:30:11 +0000
thehost.query("SELECT NOW() AS n;", :symbolize_keys => true, :database_timezone => :utc, :application_timezone => :local).each { |result| p result[:n] }
2015-05-19 16:30:28 +0000
thehost.query("SELECT NOW() AS n;", :symbolize_keys => true, :database_timezone => :local, :application_timezone => :utc).each { |result| p result[:n] }
2015-05-19 16:30:57 UTC
thehost.query("SELECT NOW() AS n;", :symbolize_keys => true, :database_timezone => :utc, :application_timezone => :utc).each { |result| p result[:n] }
2015-05-19 16:31:19 UTC
I'm trying to parse a date like: 2011-05-21 04:20:46.011 into a Time object in Rails. So far I have the following:
Time.strptime("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%3N")
But it doesn't seem to like the "%3N" at the end. How do I parse this date?
Use parse:
x = Time.parse("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%3N")
# => 2011-05-21 04:20:46 -0700
x.usec
# => 11000
In many case you don't need to pass in the format either:
x = Time.parse("2011-05-21 04:20:46.011")
# => 2011-05-21 04:20:46 -0700
x.usec
# => 11000
Try Time.parse("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%3N")
With Ruby v 2.3.2 and Rails v 5.0.0.1 in rails console
2.3.2 :035 > Time.parse("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%3N")
NoMethodError: undefined method `getlocal' for "%Y-%m-%d %H:%M:%S.%3N":String
from /home/jignesh/.rvm/rubies/ruby-2.3.2/lib/ruby/2.3.0/time.rb:264:in `make_time'
from /home/jignesh/.rvm/rubies/ruby-2.3.2/lib/ruby/2.3.0/time.rb:366:in `parse'
from (irb):35
Note: In code below in the parse format, NOT prefixed %N with the number of fractional digits like %3N and instead simply specified %N and it works
2.3.2 :037 > Time.strptime("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%N")
=> 2011-05-21 04:20:46 +0530
2.3.2 :038 > tt = Time.strptime("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%N")
=> 2011-05-21 04:20:46 +0530
2.3.2 :039 > tt.usec
=> 11000
The above code didn't worked when using parse
2.3.2 :040 > tt = Time.parse("2011-05-21 04:20:46.011", "%Y-%m-%d %H:%M:%S.%N")
NoMethodError: undefined method `getlocal' for "%Y-%m-%d %H:%M:%S.%N":String
from /home/jignesh/.rvm/rubies/ruby-2.3.2/lib/ruby/2.3.0/time.rb:264:in `make_time'
from /home/jignesh/.rvm/rubies/ruby-2.3.2/lib/ruby/2.3.0/time.rb:366:in `parse'
from (irb):40
It looks strange because Time.strptime(date, format, now=self.now) official documentation does mention about %3N, %6N and %9N explicitly and using them in the parse format doesn't work!
Rails does provide a counterpart strptime(str, format, now=now()) as part of ActiveSupport::TimeZone API and it internally does the parsing using Ruby's standard DateTime._strptime as shown in its source-code:
# File activesupport/lib/active_support/values/time_zone.rb, line 382
def strptime(str, format, now=now())
parts_to_time(DateTime._strptime(str, format), now)
end
And I guess DateTime._strptime internally delegates to Ruby's standard Time.strptime
For those who are looking for leveraging Rails's Time.zone.parse counterpart for parsing in a specific format they can use below code:
tt = Time.zone.strptime('1999-12-31 14:00:00.011111', '%Y-%m-%d %H:%M:%S.%N')
=> Fri, 31 Dec 1999 14:00:00 UTC +00:00
2.3.2 :031 > tt.usec
=> 11111
Hope someone from the core-team can clarify about this behaviour observed and if the behaviour is normal then at-least a note should be made about in the documentation.
I'm building a json object in rails as follows:
#list = Array.new
#list << {
:created_at => item.created_at
}
end
#list.to_json
Problem is this gets received by the browser like so:
"created_at\":\"2000-01-01T01:31:35Z\"
Which is clearly not right, in the DB it has:
2011-06-17 01:31:35.057551
Why is this happening? Any way to make sure this gets to the browser correctly?
Thanks
You need to do some testing / debugging to see how that date is coming through.
For me, in Rails console (Rails 3.0.9, Ruby 1.9.2)
ruby-1.9.2-p180 :014 > d = Date.parse("2011-06-17 01:31:35.057551")
=> Fri, 17 Jun 2011
ruby-1.9.2-p180 :015 > #list = {:created_at => d}
=> {:created_at=>Fri, 17 Jun 2011}
ruby-1.9.2-p180 :016 > #list.to_json
=> "{\"created_at\":\"2011-06-17\"}"
i.e. it's just fine. Can you see whether the date is really ok?
The trouble lies with the way to_json escapes characters. There is a very good post on the subject here:
Rails to_json or as_json
You may need to look into overriding as_json.
How do I convert an XML body to a hash in Ruby?
I have an XML body which I'd like to parse into a hash
<soap:Body>
<TimesInMyDAY>
<TIME_DATA>
<StartTime>2010-11-10T09:00:00</StartTime>
<EndTime>2010-11-10T09:20:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T09:20:00</StartTime>
<EndTime>2010-11-10T09:40:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T09:40:00</StartTime>
<EndTime>2010-11-10T10:00:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T10:00:00</StartTime>
<EndTime>2010-11-10T10:20:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T10:40:00</StartTime>
<EndTime>2010-11-10T11:00:00</EndTime>
</TIME_DATA>
</TimesInMyDAY>
</soap:Body>
I'd like to convert it into a hash like this:
{ :times_in_my_day => {
:time_data = > [
{:start_time=>"2010-11-10T09:00:00", :end_time => "2010-11-10T09:20:00" },
{:start_time=>"2010-11-10T09:20:00", :end_time => "2010-11-10T09:40:00" },
{:start_time=>"2010-11-10T09:40:00", :end_time => "2010-11-10T10:00:00" },
{:start_time=>"2010-11-10T10:00:00", :end_time => "2010-11-10T10:20:00" },
{:start_time=>"2010-11-10T10:40:00", :end_time => "2010-11-10T11:00:00" }
]
}
}
Ideally, the tags would convert to snake_case symbols and become keys within the hash.
Also, the datetimes are missing their time zone offsets. They are in the local time zone (not UTC). So I'd like to parse it to show the local offset and then convert the xml datetime strings into Rails DateTime objects. The resulting array would be something like:
{ :times_in_my_day => {
:time_data = > [
{:start_time=>Wed Nov 10 09:00:00 -0800 2010, :end_time => Wed Nov 10 9:20:00 -0800 2010 },
{:start_time=>Wed Nov 10 09:20:00 -0800 2010, :end_time => Wed Nov 10 9:40:00 -0800 2010 },
{:start_time=>Wed Nov 10 09:40:00 -0800 2010, :end_time => Wed Nov 10 10:00:00 -0800 2010 },
{:start_time=>Wed Nov 10 10:00:00 -0800 2010, :end_time => Wed Nov 10 10:20:00 -0800 2010 },
{:start_time=>Wed Nov 10 10:40:00 -0800 2010, :end_time => Wed Nov 10 11:00:00 -0800 2010 }
]
}
}
I was able to convert a single datetime with the parse and in_time_zone methods this way:
Time.parse(xml_datetime).in_time_zone(current_user.time_zone)
But I'm not quite sure the best way to parse the times while converting the XML into a hash.
I'd appreciate any advice. Thanks!
Edit
The code for converting the datetime string into a Rails DateTime object is wrong. That will parse the xml datetime string to the system's timezone offset and then convert that time to the user's timezone. The correct code is:
Time.zone.parse(xml_datetime)
If the user has a different time zone other than the system, this will add the user's time zone offset to the original datetime string. There's a Railscast on how to enable user timezone preferences here: http://railscasts.com/episodes/106-time-zones-in-rails-2-1.
Hash.from_xml(xml) is simple way to solve this. Its activesupport method
I used to use XML::Simple in Perl because parsing XML using Perl was a PITA.
When I switched to Ruby I ended up using Nokogiri, and found it to be very easy to use for parsing HTML and XML. It's so easy that I think in terms of CSS or XPath selectors and don't miss a XML-to-hash converter.
require 'ap'
require 'date'
require 'time'
require 'nokogiri'
xml = %{
<soap:Body>
<TimesInMyDAY>
<TIME_DATA>
<StartTime>2010-11-10T09:00:00</StartTime>
<EndTime>2010-11-10T09:20:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T09:20:00</StartTime>
<EndTime>2010-11-10T09:40:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T09:40:00</StartTime>
<EndTime>2010-11-10T10:00:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T10:00:00</StartTime>
<EndTime>2010-11-10T10:20:00</EndTime>
</TIME_DATA>
<TIME_DATA>
<StartTime>2010-11-10T10:40:00</StartTime>
<EndTime>2010-11-10T11:00:00</EndTime>
</TIME_DATA>
</TimesInMyDAY>
</soap:Body>
}
time_data = []
doc = Nokogiri::XML(xml)
doc.search('//TIME_DATA').each do |t|
start_time = t.at('StartTime').inner_text
end_time = t.at('EndTime').inner_text
time_data << {
:start_time => DateTime.parse(start_time),
:end_time => Time.parse(end_time)
}
end
puts time_data.first[:start_time].class
puts time_data.first[:end_time].class
ap time_data[0, 2]
with the output looking like:
DateTime
Time
[
[0] {
:start_time => #<DateTime: 2010-11-10T09:00:00+00:00 (19644087/8,0/1,2299161)>,
:end_time => 2010-11-10 09:20:00 -0700
},
[1] {
:start_time => #<DateTime: 2010-11-10T09:20:00+00:00 (22099598/9,0/1,2299161)>,
:end_time => 2010-11-10 09:40:00 -0700
}
]
The time values are deliberately parsed into DateTime and Time objects to show that either could be used.
ActiveSupport adds a Hash.from_xml, which does the conversion in a single call. Described in another question: https://stackoverflow.com/a/7488299/937595
Example:
require 'open-uri'
remote_xml_file = "https://www.example.com/some_file.xml"
data = Hash.from_xml(open(remote_xml_file))
The original question was asked some time ago, but I found a simpler solution than using Nokogiri and searching for specific names in the XML.
Nori.parse(your_xml) will parse the XML into a hash and the keys will have the same names as your XML items.
If you don't mind using a gem, crack does a pretty good job at this.
Crack does the XML to hash processing, then you can loop over the resulting hash to normalize the datetimes.
edit
Using REXML, you could try the following (should be close to working, but I do not have access to a terminal so it may need some tweaking):
require 'rexml/document'
arr = []
doc = REXML::XPath.first(REXML::Document.new(xml), "//soap:Body/TimesInMyDAY").text
REXML::XPath.each(doc, "//TIME_DATA") do |el|
start = REXML::XPath.first(el, "//StartTime").text
end = REXML::XPath.first(el, "//EndTime").text
arr.push({:start_time => Time.parse(start).in_time_zone(current_user.time_zone), :end_time => Time.parse(end).in_time_zone(current_user.time_zone)})
end
hash = { :times_in_my_day => { :time_data => arr } }
Of course, this assumes the structure is ALWAYS the same, and that the example you posted was not contrived for simplicity sake (as examples often are).