How to order("created_at desc") the months?
The articles within the month are in descending order, but not the months themselves.
controller
def archives
#posts = Post.order("created_at desc")
#posts_by_months = #posts.group_by { |t| t.created_at.beginning_of_month }
end
view
<% #posts_by_months.sort.each do |month, posts| %>
<%= month.strftime('%b') %>
<% for post in posts %>
<%= post.title %>
<% end %>
<% end %>
Using Enumerable#inject http://ruby-doc.org/core-2.3.1/Enumerable.html#method-i-inject:
#posts_by_months = #posts_by_months.inject({}) do |h,(k,v)|
h[k] = v.sort do |x,y|
y.created_at <=> x.created_at
end
h
end
For example:
irb(main):054:0> hash = #posts_by_months.inject({}) {|h,(k,v)| h[k] = v.sort {|x,y| y.created_at <=> x.created_at}; h}
#=> […]
irb(main):055:0> pp hash.first.map(&:created_at)
[Wed, 08 Jun 2016 22:26:34 UTC +00:00,
Wed, 08 Jun 2016 21:49:49 UTC +00:00,
Wed, 08 Jun 2016 18:30:44 UTC +00:00,
Wed, 08 Jun 2016 18:25:40 UTC +00:00]
UPDATE
Works for Rails view via controller.
# app/controllers/website_controller.rb
class WebsiteController < ApplicationController
def test
#posts = Post.order("created_at desc")
#posts_by_months = #posts.group_by {|t| t.created_at.beginning_of_month}
#posts_by_months = #posts_by_months.inject({}) do |h,(k,v)|
h[k] = v.sort do |x,y|
y.created_at <=> x.created_at
end
h
end
render(:layout => false, :template => 'website/test')
end
end
Using HAML (http://haml.info) template:
# app/views/website/test.html.haml
- #posts_by_months.sort.each do |month, posts|
= month.strftime('%b')
%br
%ul
- for post in posts
%li
= post.title
When sorting by month numbers you have to do some explicit conversions:
In controller:
#posts_by_months = #posts.group_by { |t| t.created_at.beginning_of_month }.
sort_by { |k, _| k.strftime('%-m').to_i }.reverse
#posts_by_months.each { |month, posts| puts month.strftime('%b') } ;
=> Dec
Nov
Oct
Sep
Aug
Jul
Jun
May
Apr
Mar
Feb
Jan
Here k.strftime('%-m') extracts month number without padding as a string and to_i converts that to a number. Without the conversion sort_by will apply lexical sorting which is not what's required.
Result of sort_by is not a hash but two-dimensional array. This does not affect the view code though.
Just in case you are using PostgreSQL:
#posts = Post.select('extract(month from created_at) as month, posts.*').order('month DESC, created_at DESC').group_by(&:month)
#posts.each do |month, posts|
puts "This is the month: #{Date::MONTHNAMES[month]}"
puts "And this is array of posts: #{posts}"
end
Related
I have a Call model with the following validations:
class Call < ActiveRecord::Base
validates_uniqueness_of :external_id, scope: :source
end
I generate new calls through a webhook that calls the following service:
class AircallWebhookService
include HubspotExtension
def initialize(params)
#event = params["event"]
#params = params["data"]
#call = nil
#aircall_number = nil
#employee_email = nil
end
def process
#call = Call.find_by(source: :aircall, external_id: #params["id"])
if #call.present?
p "Found existing call!"
else
p "Could not locate existing call."
#call = Call.new(source: :aircall, external_id: #params["id"])
end
#call.source = 1
#call.external_id = #params["id"]
#call.url = #params["direct_link"]
#call.direction = #params["direction"]
#call.status = #params["status"]
#call.missed_call_reason = #params["missed_call_reason"]
#call.started_at = Time.at(#params["started_at"]) if #params["started_at"].present?
#call.answered_at = Time.at(#params["answered_at"]) if #params["answered_at"].present?
#call.ended_at = Time.at(#params["ended_at"]) if #params["ended_at"].present?
#call.duration = #params["duration"]
#call.raw_digits = #params["raw_digits"]
#call.aircall_user_id = #params.dig("user", "id")
#call.contact_id = #params.dig("contact", "id")
#aircall_number = #params.dig("number", "digits").try{|n| n.gsub(/\s|-|\(|\)|\+/, "")}
#call.aircall_user_id = #params.dig("user", "id")
#employee_email = #params.dig("user", "email")
if !#params["tags"].empty?
mapTagToReferrer
end
#call.comments = mapComments
if #call.save
linkTagToCall
linkCallToEmployee
updateHubspotEngagement
end
end
...
end
For some reason, despite the uniqueness validation, I continue to see calls with the same external_id and source. For example these are 2 records in my DB:
[
[0] #<Call:0x000055d780f639b8> {
:id => 8149,
:location_id => nil,
:referrer => nil,
:consultation => nil,
:created_at => Tue, 07 Sep 2021 15:42:01 EDT -04:00,
:updated_at => Tue, 07 Sep 2021 15:42:01 EDT -04:00,
:worldwide => nil,
:external_id => 582402916,
:source => "aircall",
:direction => "inbound",
:started_at => Tue, 07 Sep 2021 15:41:03 EDT -04:00,
:answered_at => Tue, 07 Sep 2021 15:41:10 EDT -04:00,
:ended_at => Tue, 07 Sep 2021 15:41:57 EDT -04:00,
:duration => 54,
:status => "done",
:missed_call_reason => nil,
:aircall_user_id => 567754,
:contact_id => nil,
:comments => nil,
:lead_status => nil,
:call_type => "unknown"
},
[1] #<Call:0x000055d780f636e8> {
:id => 8150,
:location_id => nil,
:referrer => nil,
:consultation => nil,
:created_at => Tue, 07 Sep 2021 15:42:01 EDT -04:00,
:updated_at => Tue, 07 Sep 2021 15:42:01 EDT -04:00,
:worldwide => nil,
:external_id => 582402916,
:source => "aircall",
:direction => "inbound",
:started_at => Tue, 07 Sep 2021 15:41:03 EDT -04:00,
:answered_at => Tue, 07 Sep 2021 15:41:10 EDT -04:00,
:ended_at => Tue, 07 Sep 2021 15:41:57 EDT -04:00,
:duration => 54,
:status => "done",
:missed_call_reason => nil,
:aircall_user_id => 567754,
:contact_id => nil,
:comments => nil,
:lead_status => nil,
:call_type => "unknown"
}
]
They are identical and even the created_at is identical down to the millisecond. How is this possible?
Here's the controller in case it's necessary:
class API::WebhooksController < ApplicationController
def aircall_webhook
ac = AircallWebhookService.new(params)
ac.process
head :ok
end
end
validates_uniqueness_of doesn't actually guarantee that duplicate values cannot be inserted. It merely catches most of the cases where users input duplicated data and provides user feedback. Its very much prone to race conditions, and is foiled by stuff as simple as double clicking grannies.
If uniqueness is actually important you need to enforce it on the database layer with a unique index.
add_index :calls, [:external_id, :source], unique: true
I have a array suppose
[#<Data id: 1, date: "2016-01-06", value: "1">,
#<Data id: 2, date: "2015-12-31", value: "3">,
#<Data id: 3, date: "2016-01-06", value: "6">...]
and so on..
I want to sum the values having same date
i.e here first and third record are of same date so the result array will give
#<Data id: 1, date: "2016-01-06", value: "1">,
#<Data id: 3, date: "2016-01-06", value: "7">,
Hey you can use try this way if you have already fetch an array from database
arr.group_by{|a| a.date.to_date}.map{|k,v| {k => v.map(&:value).sum()}}
If you are are not fetch array/active record from database you can directly use database query as
If your database stores only date then you can use
Model.group("date").sum(:value)
If your database stores date with time here i have use DATE_FORMAT function for skipping Time part of date
Model.group("DATE_FORMAT(date, '%Y%m%d')").sum(:value)
You can use sql groupping on the model:
Data.where(date:(42.days.ago..Date.today)).group(:date).sum(:value)
This will return a hash of {date => sum}
On an array:
Hash[your_array.group_by(&:date).map{|k,v| [k, v.sum(&:value)]}]
sum = Hash.new(0)
array.each do |data|
<p>sum[data[:date]] += data[:value]</p>
end
# => {:id => 1, "Wed, 04 May 2011" => 300, "Tue, 03 May 2011" => 450...}
# => If you then want this in the same array format you started with:
new_array = sum.collect{ |key, value| {:date => key, :value => value} }
# => [{:id => 1,:date => "Wed, 04 May 2011", :value => 300}, {....}]
I'm using the calendar helper from Railscast: http://railscasts.com/episodes/213-calendars-revised but I'm running into a problem (calendar helper below):
module CalendarHelper
def calendar(date = Date.today, &block)
Calendar.new(self, date, start_date, end_date, scheduled, block).table
end
class Calendar < Struct.new(:view, :date, :start_date, :end_date, :scheduled, :callback)
HEADER = %w[S M T W T F S]
START_DAY = :sunday
delegate :content_tag, to: :view
def table
content_tag :table, class: "calendar" do
header + week_rows
end
end
def header
content_tag :tr do
HEADER.map { |day| content_tag :th, day }.join.html_safe
end
end
def week_rows
weeks.map do |week|
content_tag :tr do
week.map { |day| day_cell(day) }.join.html_safe
end
end.join.html_safe
end
def day_cell(day)
content_tag :td, view.capture(day, &callback), class: day_classes(day)
end
def day_classes(day)
classes = []
classes << "today" if day == Date.today
classes << "start_date" if day == start_date
classes << "end_date" if day == end_date
classes << "notmonth" if day.month != date.month
classes << "scheduled" if day == scheduled
classes.empty? ? nil : classes.join(" ")
end
def weeks
first = date.beginning_of_month.beginning_of_week(START_DAY)
last = date.end_of_month.end_of_week(START_DAY)
(first..last).to_a.in_groups_of(7)
end
end
end
My application spits out an array of scheduled dates (using the ice_cube gem). For each one of those dates I want to match them with dates from the calendar, assigning them with the class "scheduled". I can't figure out how to do this. This code is what I'm trying to make work:
classes << "scheduled" if day == scheduled
'scheduled' comes from the controller:
Application_Controller.rb
def scheduled
Schedule.find(params[:id]).itinerary.all_occurrences if params[:id]
end
helper_method :scheduled
Which returns the following array of dates:
=> [2014-05-16 00:00:00 -0400, 2014-05-19 00:00:00 -0400, 2014-05-20 00:00:00 -0400, 2014-05-21 00:00:00 -0400, 2014-05-22 00:00:00 -0400, 2014-05-23 00:00:00 -0400, 2014-05-26 00:00:00 -0400, 2014-05-27 00:00:00 -0400, 2014-05-28 00:00:00 -0400, 2014-05-29 00:00:00 -0400]
I've tried a number of scenarios but I can't figure it out.
As an example, this will work and show the "scheduled" class for these 3 days but I can't figure out how to loop through all the scheduled dates and still have an || operator in the block:
def day_classes(day)
...
classes << "scheduled" if Date.yesterday == day || Date.tomorrow == day || Date.today == day
...
end
Or maybe someone has a better idea?
After a bit of reading through the ice_cube gem I found a handy method that does exactly what I needed.
def day_classes(day)
...
classes << "scheduled" if scheduled.itinerary.occurs_on?(day)
...
end
I have a column with datetime in a table that has the format:
2012-10-30 08:00:00 UTC
How could I create an array or hash that would collect the times by date:
2012-10-30
8:00
9:00
13:00
2012-11-02
8:00
9:00
In the end I would like to have web output like this:
October 30, 2012
8:00 <reserve button>
9:00 <reserve button>
13:00 <reserve button>
November 2, 2012
8:00 <reserve button>
9:00 <reserve button>
I can figure out the html part if I can get the array correctly setup.
Get all your available timestamps from the table, for example:
available_times = SomeTable.select(:some_column).order(:some_column).all
Build a Hash by date:
#result = available_times.inject({}) do |m, timestamp|
date = timestamp.to_date
m[date] ||= []
m[date] << timestamp
m
end
#result will be a Hash by date with available times.
You can now do this in your view:
<% #result.each do |date, timestamps| -%>
<div><%= date.strftime("%B %-d, %Y") %></div>
<% timestamps.each do |timestamp| -%>
<%= timestamp.strftime("%k:%M") %> <input id='<%= timestamp.to_f%>' type='button' value='Reserve Button'/>
<br/>
<% end -%>
<% end -%>
sample = ["2012-10-28 08:30:00 UTC",
"2012-10-28 09:00:00 UTC",
"2012-11-30 09:15:00 UTC",
"2012-11-30 08:00:00 UTC"]
sample.each_with_object(Hash.new{ |h, k| h[k] = [] }) do |d, h|
date = DateTime.parse(d)
h[date.strftime("%B %-d, %Y")] << date.strftime("%k:%M")
end
# => {"October 28, 2012"=>["8:30", "9:00"], "November 30, 2012"=>["9:15", "8:00"]}
I'm getting a really strange error message in my app. In my controller I have:
require 'open-uri'
require 'json'
class NoticiasController < ApplicationController
def index
url = 'https://api.twitter.com/1/trends/daily.json'
buffer = open(url, "UserAgent" => "Ruby-Wget").read
# convert JSON data into a hash
#result = JSON.parse(buffer)
end
end
and in the view I have
<div>
<% for news in #result['trends'] %>
<p><%= news['name'] %></p>
<% end %>
</div>
but I get "TypeError: can't convert String into Integer".
What am I doing wrong?
results['trends'] is a map of timestamp => [trends].
You need to pick a trend date, then iterate over the array of trends.
ruby-1.9.2-p290 :011 > result['trends'].keys.each { |k| puts k }
2011-11-13 17:00
2011-11-13 19:00
2011-11-13 14:00
2011-11-13 16:00
2011-11-13 18:00
2011-11-13 15:00
# etc.
ruby-1.9.2-p290 :022 > result['trends']["2011-11-13 17:00"].each { |t| p t["name"] }; nil
"#myweddingsong"
"#mydivorcesong"
"#ThingsPeopleShouldntDo"
"GOOD LUCK 1D"
# etc.
For example, to get the names of the latest trends:
> ts = result['trends'].keys.sort.last
"2011-11-13 23:00"
> latest_trend_names = result['trends'][ts].collect { |t| t['name'] }
> latest_trend_names.each { |tn| p tn }
"#myweddingsong"
"#mydivorcesong"
"#ThingsPeopleShouldntDo"
"I'm a Celeb"
"HEADLESS GAGA"
"CHRIS BROWN IS A LEGEND"