Ruby on Rails 3 Диапазоны заезда / выезда по часам - PullRequest
1 голос
/ 10 октября 2011

Я использую Ruby on Rails 3, и у меня есть модель "посещения", которая хранит дату и время check_in и check_out, и мне нужно искать посещения в общем диапазоне дат и подсчитывать количество "присутствующих посетителей", сгруппированных по всем часы дня.

... т.е. Мне нужно что-то вроде:

8:00am - 8:59am : 12 visitors
9:00am - 9:59am : 5 visitors
10:00am - 10:59am : 4 visitors

... с учетом таблицы посещений с сохранением времени заезда и выезда.

Идея состоит в том, чтобы взять время заезда и выезда для «посещений», а затем определить, сколько посетителей (при условии, что каждое посещение регистрирует одного посетителя, что и происходит в соответствии с политикой), посещенных в течение любого данного часа дня Чтобы узнать время пиковых посещений.

Я пытался настроить запросы вроде:

eight_am_visits = Visit.where("EXTRACT(HOUR_MINUTE FROM check_in) <= 859").where("EXTRACT(HOUR_MINUTE FROM check_out) >= 800")

... и не совсем ударил, потому что Rails хранит даты таким странным образом (в UTC, который он конвертирует по запросу к базе данных), и, похоже, он не выполняет это преобразование, когда я что-то использую как EXTRACT в SQL ...

... есть идеи, как мне это сделать?

Ответы [ 3 ]

1 голос
/ 10 октября 2011

Похоже, вы вообще не интересуетесь объектами Визита.Если вам просто нужна простая сводка, то уберите AR с пути и позвольте базе данных выполнить работу:

# In visit.rb
def self.check_in_summary(date)
    connection.select_rows(%Q{
        select extract(hour from check_in), count(*)
        from visits
        where cast(check_in as date) = '#{date.iso8601}'
        group by extract(hour from check_in)
    }).inject([ ]) do |a, r|
        a << { :hour => r[0].to_i, :n => r[1].to_i }
    end
end

Тогда a = Visit.check_in_summary(Date.today - 1) даст вам сводку за вчерашний день без дополнительной работы.Эта демонстрационная реализация, конечно, будет иметь дыры в массиве в течение нескольких часов без каких-либо проверок, но это легко решить (при желании):

def self.check_in_summary(date)
    connection.select_rows(%Q{
        select extract(hour from check_in), count(*)
        from visits
        where cast(check_in as date) = '#{date.iso8601}'
        group by extract(hour from check_in)
    }).each_with_object([0]*24) do |r, a| # Don't forget the arg order change!
        a[r[0].to_i] = r[1].to_i
    end
end

Эта версия возвращает массив с 24 элементами (по одному на каждыйотсчитываемый от нуля), значения которого представляют собой число проверок за этот час.

Не бойтесь переходить на SQL, когда это удобно, AREL - это всего лишь один инструмент, и у вас должно быть несколько инструментовв вашем наборе инструментов.Кроме того, не бойтесь добавлять в свои модели дополнительные методы обработки и суммирования данных, в ваших моделях должен быть интерфейс, позволяющий вам четко выражать свои намерения в остальной части кода.

1 голос
/ 10 октября 2011

Может быть что-то подобное?!

t = Time.now
eight_am_visits = Visit.all(:conditions => ['check_in > ? and check_in < ?', Time.utc(t.year, t.month, t.day, 8), Time.utc(t.year, t.month, t.day, 8, 59)])

РЕДАКТИРОВАТЬ: Или вы можете захватить все посещения за день и отфильтровать его в Rails:

t = Time.now
visits = Visit.all(:conditions => ['created_at > ? and created_at < ?', Time.utc(t.year, t.month, t.day - 1), Time.utc(t.year, t.month, t.day + 1)])
visits_by_hour = []
(0..23).each do |h|
  visits_by_hour << visits.map {|e| e if e.created_at > Time.utc(t.year, t.month, t.day, h) && e.created_at < Time.utc(t.year, t.month, t.day, h, 59)}.count
end

Ив поле зрения:

<% visits_by_hour.each_with_index do |h, v| %>
  <%= "#{h}:00 - #{h}:59: #{v} visitors" %>
<% end %>
0 голосов
/ 12 октября 2011

Спасибо за вашу помощь, Александр и мю, мне удалось кое-что выяснить благодаря тому пониманию, которое вы мне дали.

Я придумал это, и, кажется, работает:

#grab the data here, this is nice because 
#I can get other stats out of it (which I don't show here)
@visits = Visit.where(:check_in => @start_date..@end_date, :check_out => @start_date..@end_date).where("check_out IS NOT NULL");

#Here we go
@visitors_present_by_hour = {}
(0..23).each do |h|
  # o.o Ooooooh.... o_o Hee-hee! ^_^
  @visitors_present_by_hour[h] = @visits.collect{|v| v.id if v.check_in.hour <= h and v.check_out.hour >= h}.compact.count
end

Тогда я могу просто выбросить этот хэш на мой взгляд.

Кажется, решение оказалось немного проще, чем я думал, и если сделать это таким образом, то на самом деле рельсы будут выполнять преобразование времени из UTC.

Итак, я мог бы просто собрать все посещения, у которых есть часы в часовом диапазоне, затем сжать нули и посчитать, что осталось. Я был удивлен, когда ударил по нему. Мне вообще не требовался какой-либо пользовательский SQL, как я думал (если это не совсем неправильно, но, похоже, он работает с некоторыми тестовыми данными).

Спасибо, ребята!

...