Rails hash counting from a specific keys in hash - ruby-on-rails

Hi I am using this code bellow
Companion.where(companion_type: 1).joins(:tasks).where(tasks: {status: 3}).group_by_month(:created_at).size
and i am having a data looks like this
Jan, 2017 => 89
.
.
.
Aug, 2021 => 300
But i need data's above from Jan, 2019
Is there any nice ways to solve it out?

If you are only interested in Jan 2019, then you don't need to pull all the Companion table, and then group them by month. Instead, select only those in the month of interest...
Companion.
where(companion_type: 1).
joins(:tasks).
where(tasks: {status: 3}).
where("MONTH(tasks.created_at) = 1 AND DAY(tasks.created_at) = 19").
count
I assume that it's the created_at of tasks that you want to filter, vs. the created_at of companions, is that right?
Also, it's better to use count than size because that is directly inserted into the sql, as opposed to instantiating all the objects from Jan, 2019 and then applying .size to the result.

Related

Progression of records number by time

Let's say I have three users created on different dates. Now I want do have a graph of users progression. So I want to get something like:
{
Thu, 02 Nov 2017=>1,
Sat, 04 Feb 2017=>2,
Wed, 21 Mar 2018=>3
}
It's very similar to grouping by created_at::date, but I want to have number of all the records created before this date rather than number of items created exactly on this date.
How can I achieve this using group_by and aggregate functions in Postgresql? I need it for Ruby on Rails project, but I expect simple vanilla SQL, no maps and complex queries.
I am no rails expert and my solution requires loading a big relationship like:
#u = User.all
And then find the smallest creation date :
#start = #u.minimum("created_at")
Then you can calculate the number of days between creation and now:
#days = ((Time.now - #start)/1.day).to_i
Then you can calculate for each day the number of users already created:
#days.each do |day|
puts "day "+day.to_s
puts #u.where("created_at < ?", #start+day.day).count
end
Probably some easier solutions in SQL though. (Also you may take only a subset of users by choosing a range of dates not to exceed the server memory)

How to access previous updated_at?

I have model in which I keep track of the field updated_at.
Is there a way I can track the previous updated_at?
For example
updated_at = A (where A is an actual datetime stamp)
Some work is done then save is called
updated_at = B (where B is an actual datetime stamp)
Is there a way I can access the previous updated_at i.e. A?
If you have the object available, then you can call:
object.previous_changes
This would return you a hash as follows showing which attributes have been changed:
{"name"=>["foo", "bar"], "updated_at"=>[Tue, 19 Apr 2016 08:19:40 UTC +00:00, Mon, 25 Apr 2016 10:49:47 UTC +00:00]}
Refer:
previous_changes
Please have a look ActiveModel::Dirty module:
object.updated_at # returns current value
object.updated_at_changed? # returns true or false if value has changed
object.updated_at_was # return last value
object.updated_at_change
Or
If you wants to track all changed values you can use Paper Trail Gem.
It will be useful if you use paper trail gem.
It will persist the previous record on disk which may affect your app performance.
Better way to implement an in-memory solution to store the previous records.

Rails cant save correct date into database

I have a date format like this "18/12/15" as input (it is a string)
18 => day
12 => month
15 => year (2015)
I am trying to store it in my db in a date column but it doesnt save the proper date (for the previous exemple I get Sat, 15 Dec 2018 whereas I should get something like day, 18 Dec 2015.)
How can I fix that
You could use strptime:
require 'date'
Date.strptime('18/12/15', '%d/%m/%y')
#<Date: 2015-12-18 ((2457375j,0s,0n),+0s,2299161j)>
You want to define a date with the order of day, month, year, while, due to the locale your intention is interpreted as year, month, day.
You can do one of the following:
pass the parameters in inverted order
implement a method which gets the parameters in the correct order and inverts them
set the locale to the one you intend to use
use strptime, as spickermann described

Why are those two datetimes different?

Why isn't Time.current equal to its parsed equivalent?
current = Time.current
# Wed, 16 Sep 2015 17:10:56 CEST +02:00
parsed = Time.zone.parse('16 Sep 2015 17:10:56')
# Wed, 16 Sep 2015 17:10:56 CEST +02:00
current == parsed
# false <= What ?!
current.to_i == parsed.to_i
# true
Ticket.create(datetime: current)
# ...
Ticket.find_by_datetime(parsed)
# nil <= Why ?!
I'm actually having trouble with this in a Ruby on Rails application where I try to find a record based on a datetime attribute that has been parsed, like shown on the last lines.
I really don't get it. Time zones are the same, times are the same down to seconds. What's happening here?
Moreover, how should I proceed to find a record based on a parsed datetime?
They should not be the same:
current.to_f #=> 1442417032.6567826
parsed.to_f #=> 1442417032.0
When parsing, you miss milliseconds.
Thanks everyone for the help. I hope you don't mind but since pieces of the final answer are scattered across multiple answers I will answer my own question building on what you all said.
So as for why the dates are different, it's because of the milliseconds missing in the parsed datetime. As #dimakura mentioned.
current.to_f #=> 1442417032.6567826
parsed.to_f #=> 1442417032.0
Then the answer about how could we fetch the Ticket record based a the parsed datetime.
First it's important to know this will be relevant only for PostgreSQL (my case) or other databases that actually store milliseconds. Thanks to #sjagr for mentioning this.
So we have to query for a range from parsed to parsed + 1 second, like #Stefan explained:
Ticket.where(datetime: parsed...parsed+1).first
And if we have control on the Ticket creation, we could also remove the millisecond precision before saving the the database. Thanks to #sjagr for providing an easy way to do that.
current = Time.current
Ticket.create(datetime: current.change(usec: 0))
Thanks everyone !
It's because they are not equal, they differ by parts of the second. What you see in the console is the result of inspect method called on those dates, which by default, drop any sub-second parts.

Group users by created_at and insert into hash for MorrisJs

I want to use MorrisJs-Rails to build up a graphical statistic of user registered within the last 4 weeks.
For that I iterate through each day of the last month and check for users registered at this specific date. Here's the problem though.
admin_helper.rb:
def users_chart_date
(4.weeks.ago.to_date..Date.today).map do |date|
{
created_at: date,
count: User.where("created_at = ? ", date).count
}
end
end
Both dates are stored like so:
puts User.first.created_at # => Tue, 10 Mar 2015 20:08:18 UTC +00:00
puts date # => Thu, 23 Jul 2015
You can see there's quite the difference between those two stored dates thus I can not possibly get an actual count of users registered per day.
Is there a way to "cast" my SQL query somehow? I feel the solution to my problem is easy peasy, but I can't seem to get it.
Thanks for your help,
let me know if you need something.
As you've noticed, the created_at timestamp is of type DateTime (or Time), so you're correct in that its not directly comparable with a Date type. You could compare it with a range of times for each day:
(4.weeks.ago.to_date..Date.today).map do |date|
{
created_at: date,
count: User.where(created_at: date.to_time.all_day).count
}
end
(Note that this will use the times for your computer's timezone.)
However, this method can become very slow especially if the time range becomes longer (e.g., over the entire life of your app), in which case a SQL GROUP BY statement work much faster. Again, you need to deal with a comparison by dates on a time data field, which is database-dependent. With MySQL or PostgreSQL you can:
User.group("DATE(created_at)").count
If you want to restrict the time periods you get data for:
User.where("created_at >= ?", 4.weeks.ago.beginning_of_day).group("DATE(created_at)").count
These queries will return a hash of Date objects to the count of users with created_at times on that day.

Resources