How do I group by day instead of date? - ruby-on-rails

ok so i have
>> list = Request.find_all_by_artist("someBand")
=> [#<Request id: 1, artist: "someBand", song: "someSong", venue: "Knebworth - Stevenage, United Kingdom", showdate: "2011-07-01", amount: nil, user_id: 2, created_at: "2011-01-01 18:14:08", updated_at: "2011-01-01 18:14:09".............
and then
list.group_by(&:created_at).map {|k,v| [k, v.length]}.sort
=> [[Sat, 01 Jan 2011 18:14:08 UTC +00:00, 10], [Sun, 09 Jan 2011 18:34:19 UTC +00:00, 1], [Sun, 09 Jan 2011 18:38:48 UTC +00:00, 1], [Sun, 09 Jan 2011 18:51:10 UTC +00:00, 1], [Sun, 09 Jan 2011 18:52:30 UTC +00:00, 1], [Thu, 10 Feb 2011 02:22:08 UTC +00:00, 1], [Thu, 10 Feb 2011 20:02:20 UTC +00:00, 1]]
the problem is I have a few Sun, 09 Jan and a couple for the 10th, instead of one like this
this is what i need
=> [[Sat, 01 Jan 2011 18:14:08 UTC +00:00, 10], [Sun, 09 Jan 2011 18:34:19 UTC +00:00, 4], [Thu, 10 Feb 2011 20:02:20 UTC +00:00, 2]]

I think this is a much more elegant and simple solution
list.group_by{|x| x.created_at.strftime("%Y-%m-%d")}

Time is a quite complex object to group by. Assuming you want to group by the creation date, instead of the full Time, start creating a custom method in your model to return the group criteria.
The method should return the creation date, possibly as string.
def group_by_criteria
created_at.to_date.to_s(:db)
end
Then, group by that method.
list.group_by(&:group_by_criteria).map {|k,v| [k, v.length]}.sort

There is a gem for that: groupdate.
Usage (from the docs):
User.group_by_day(:created_at).count
# {
# 2013-04-16 00:00:00 UTC => 50,
# 2013-04-17 00:00:00 UTC => 100,
# 2013-04-18 00:00:00 UTC => 34
# }

Ipsum's answer is actually good and probably the best:
In Arel:
requests = Arel::Table.new(:requests)
query = requests.project("COUNT(*), CAST(requests.created_at AS DATE) as created_at")
query = query.group("CAST (requests.created_at AS DATE)")
Request.find_by_sql(query.to_sql)

you can use GROUP BY DATE(created_at) in MySQL
On ruby code you can use like this
list.group('DATE(created_at)').map {|k,v| [k, v.length]}.sort

Group without extra gems:
def self.group_by_day items
data = items.group_by{|x| x.created_at.to_date}
chart_data = {}
data.each do |a,b|
chart_data.merge!({a => b.count})
end
return chart_data
end

Related

Rails query where dates are greater than or less than two dates

I have a model where I am trying to come up with a query where I wanna select data if the starts_at is greater than today and the ends_at is less than today. The other caveat is checking if either one of those dates a NULL and if they aren't then the dates need to be checked against todays date also.
my_model.where("? >= starts_at AND ? <= ends_at OR (starts_at IS NULL OR ends_at IS NULL)", Ti
me.now.beginning_of_day, Time.now.end_of_day)
That code seems to pull back data sometimes and other times it doesn't.
** UPDATE **
Here are a few code examples
=> Menu Load (0.9ms) SELECT "menus".* FROM "menus" WHERE "menus"."account_id" = $1 AND ('2021-09-24 00:00:00' >= starts_at AND '2021-09-24 23:59:59.999999' <= ends_at OR (starts_at IS NULL OR ends_at IS NULL)) [["account_id", 1]]
[#<Menu:0x00005635d5657028
id: 4,
menu_type: "standard",
name: "Promo Menu",
starts_at: Fri, 24 Sep 2021 00:00:00.000000000 UTC +00:00,
ends_at: Fri, 01 Oct 2021 15:19:32.075844000 UTC +00:00,
never_ends: false,
account_id: 1,
created_at: Fri, 24 Sep 2021 00:37:44.127698000 UTC +00:00,
updated_at: Fri, 24 Sep 2021 16:03:06.558332000 UTC +00:00,
available: true>]
The above loads menus correctly, but heres a menu with a starts_at and ends_at within the query time frame and doesn't return
=> #<Menu:0x00005635d56d6e40
id: 1,
menu_type: "standard",
name: "Main Menu",
starts_at: Fri, 24 Sep 2021 00:37:43.512403000 UTC +00:00,
ends_at: Sat, 25 Sep 2021 17:46:13.450433000 UTC +00:00,
never_ends: false,
account_id: 1,
created_at: Fri, 24 Sep 2021 00:37:43.532613000 UTC +00:00,
updated_at: Fri, 24 Sep 2021 17:46:13.455113000 UTC +00:00,
available: true>
I think your query is correct. I guess you couldn't get the records sometimes because of the time zone. What's the timezone of the Rails app?

Ruby How to iterate over an array of instances

I have this array from Order.all
[#<Order:0x00007f1d219f7028 id: 1, time: "01.00", amount: 21, created_at: Mon, 16 Apr 2018 17:44:41 UTC +00:00, updated_at: Mon, 16 Apr 2018 17:44:41 UTC +00:00>,
#<Order:0x00007f1d219f6ee8 id: 2, time: "02.00", amount: 23, created_at: Mon, 16 Apr 2018 17:44:41 UTC +00:00, updated_at: Mon, 16 Apr 2018 17:44:41 UTC +00:00>]
When I do Order.all.first[:time] I get "01.00" so that 'works'.
But when I do
a = []
Order.all.each do |e|
b = Array(e[:time])
b << e[:amount]
a << b
end
I just get the above array again???
How do I iterate over the array to get
[['01.00', 21], ['02.00', 23]]
You don't need that. Try this one
Order.all.pluck(:time, :amount)
A more verbose and expensive way to do it
Order.all.map { |order| [order.time, order.amount] }
Even more verbose, probably what you're trying to do
result = []
Order.all.each do |order|
result << [order.time, order.amount]
end
Your solution may be correct. You should keep in mind that #each return the enumerated collection. The collection of orders in your case.
The result you are expecting is the variable a

ActiveRecord Joins is not fetching data from multiple table. It is working for manual execution

I am building a RestAPI using Rails 5. I have 3 tables currently
Members
ContactSource (name)
ContactSourceMemberMap (member_id, contact_source_id, value)
Where,
class ContactSourceMemberMap < ApplicationRecord
belongs_to :member
belongs_to :contact_source
end
Now I want to fetch name from ContactSource and value from ContactSourceMemberMap.
I tried:
ContactSource.eager_load(:contact_source_member_maps).select("contact_sources.name", "contact_source_member_maps.value")
on ContactSourceMemberMap model.
The SQL which is getting generating is fetching data by performing INNER JOIN. But on the rails end its coming like
[#<ContactSource:0x007fa14f261950
id: 1,
name: "ContactSource1",
created_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00,
updated_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00>,
#<ContactSource:0x007fa14f200290
id: 2,
name: "ContactSource2",
created_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00,
updated_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00>,
#<ContactSource:0x007fa14fa8fb18
id: 3,
name: "ContactSource3",
created_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00,
updated_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00>,
#<ContactSource:0x007fa14fa8f2d0
id: 4,
name: "ContactSource4",
created_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00,
updated_at: Thu, 08 Mar 2018 16:33:00 UTC +00:00>]}]
It is not displaying the value field. Any thoughts why is it behaving like that?
contact_sources = ContactSource.eager_load(:contact_source_member_maps).select("contact_sources.name", "contact_source_member_maps.value")
and then you can get the other object with
contact_sources.first.contact_source_member_maps
if I am wrong you can check the following guide

Why my DateTime format changed on Bamboo build

I have a User class that has_many Jobs. I map jobs with the following code, the start_at and end_at are datetime:
def ranges
user.jobs.map { |u| [u.start_at, u.end_at] }
end
I have a spec that compares two arrays:
my_array = [[start1, end1], [start2, end2]]
expect(ranges).to eq my_array
The test data are also datetime created from factory girl e.g.
create(:jobs, start_at:DateTime.parse('2017-03-26 00:00:00'), end_at: DateTime.parse('2017-03-27 00:00:00'))
Everything works fine, expect when Bamboo runs my spec, I get following error:
expect
[[2017-12-31 00:00:00.000000000 +0000, 2017-12-31 10:10:00.000000000 +0000], [2017-12-30 00:00:00.000000000 +0000, 2017-12-31 00:10:00.000000000 +0000], [2017-11-26 00:00:00.000000000 +0000, 2017-11-26 10:10:00.000000000 +0000], [2017-03-24 00:00:00.000000000 +0000, 2017-03-24 10:10:00.000000000 +0000], [2017-03-25 00:00:00.000000000 +0000, 2017-03-25 10:10:00.000000000 +0000], [2017-03-26 00:00:00.000000000 +0000, 2017-03-26 10:10:00.000000000 +0000]]
to match
[[Sun, 26 Mar 2017 00:00:00 UTC +00:00, Sun, 26 Mar 2017 10:10:00 UTC +00:00], [Sat, 25 Mar 2017 00:00:00 UTC +00:00, Sat, 25 Mar 2017 10:10:00 UTC +00:00], [Fri, 24 Mar 2017 00:00:00 UTC +00:00, Fri, 24 Mar 2017 10:10:00 UTC +00:00], [Sun, 26 Nov 2017 00:00:00 UTC +00:00, Sun, 26 Nov 2017 10:10:00 UTC +00:00], [Sat, 30 Dec 2017 00:00:00 UTC +00:00, Sun, 31 Dec 2017 00:10:00 UTC +00:00], [Sun, 31 Dec 2017 00:00:00 UTC +00:00, Sun, 31 Dec 2017 10:10:00 UTC +00:00]]
dose this mean I need to format all my datetime object byiso8601 all the time? what could cause this on Bamboo
The problem isn't your format. The problem is that the array elements are not in the same order. The dates in your expect array begin with the 2017-12-31 dates, while those in your match array begin with the 2017-03-26 dates.
RSpec's eq method returns true only if each element of the first array is identical to the element at the same index of the second array. But the match_array method returns true so long as the two arrays have the same elements, regardless of order.
Change your expectation line to:
expect(ranges).to match_array(my_array)
And you should be good to go.

Fix timezone issue with database in Rails on Heroku

My Rails app is hosted on Heroku with a Postgress database. For a certain model I'm asking the user to chose a day and a time, which I combine to a date before saving the model:
def create_timestamp
self.date = day.to_datetime + time.seconds_since_midnight.seconds
end
When I chose for instance today # 20:50:00 and store it in the database, my record looks like this:
<Report id: 1, account_id: 1, date: "2016-11-05 20:50:00", description: "test", created_at: "2016-11-05 19:50:57", updated_at: "2016-11-05 19:50:57", deleted_at: nil, user_id: 1, report_category_id: 2, time: "2000-01-01 20:50:00", day: "2016-11-05">
As you might notice, the created_at date is different, because it's in a different timezone. While the created_at is stored in UTC +0000, my custom date, which uses local timezone is CET +0100.
So when I type in console: Report.find(1).date, it returns 2016-11-05 21:50:00 +0100.
How can I store the correct date in the initial set, or make the database return the correct timezone when querying?
Thanks
What you're doing currently is basically this:
>> date = Date.new(2016, 11, 6)
=> Sun, 06 Nov 2016
>> time = Time.new(2000, 1, 1, 20, 50, 0)
=> 2000-01-01 20:50:00 +0100
>> date.to_datetime + time.seconds_since_midnight.seconds
=> Sun, 06 Nov 2016 20:50:00 +0000
to_datetime converts the time-zone-less Date into a DateTime representing midnight UTC on that date, and then you add 20 hours and 50 minutes.
Instead of midnight UTC, you want midnight in your local time zone as your starting point. So you could do this, for example:
>> date.in_time_zone + time.seconds_since_midnight.seconds
=> Sun, 06 Nov 2016 20:50:00 CET +01:00
Rails should then be smart enough to convert it to UTC when storing in the database and back to CET when retrieving from the database.
To be clear about the difference, compare:
>> date
=> Sun, 06 Nov 2016
>> date.to_datetime
=> Sun, 06 Nov 2016 00:00:00 +0000
>> date.in_time_zone
=> Sun, 06 Nov 2016 00:00:00 CET +01:00
You should use
Time.zone.now
# or
Time.current
Which will return the time in time zone

Resources