Rails hash and arrays - ruby-on-rails

I am making a timesheet, but i have problems.
I want it to look like this:
projects |Monday Tuesday Wednesday Thursday Friday Saturday Sunday
development| 3:50 4:00 5:00 3:00 0:00 0:00 0:00
Testing | 0:00 0:00 0:00 2:00 4:00 0:00 0:00
Lanch | 0:00 0:00 0:00 0:00 1:00 6:00 0:00
Every project has_many logs with hours as a field.
I did this query:
#log_week_project = current_user.logs.where(:log_date => (Time.now.beginning_of_week + 1.second)..(Time.now.end_of_week - 1.second)).group("project_id").group("date(log_date)").sum(:hours)
And get this:
<OrderedHash {[2, "2011-05-24"]=>31800.0, [1, "2011-05-24"]=>10200.0, [1, "2011-05-23"]=>25200.0, [1, "2011-05-26"]=>19800.0}>
The key in the hash is an array with the project_id # [0] and log_date # [1]. The value is the hours.
I do this in my view:
<% for project in #projects do %>
<%= project.name %>
<% #log_week_project.map do |key,hours|%>
<% if key[0] == project.id %>
<%= time_diff(hours)%>
<% end %>
<% end %>
<% end %>
This gives me the project hours on each date, but if there is a date with no hours there is no output and the table gets wrong.
If there is no hours on wednesday the thursday hours ends up in the wednesday column.
Any tips on how to do this? Perhaps there is a better way?
SOLUTION:
Thanks for replies and comments. The solution was:
<% for project in #projects do %>
<%= project.name %>
<% #dates.each do |date| %>
<% hours = #log_week_project.detect { |key| key[0][0].to_i == project.id && key[0][1].to_time.strftime('%a') == date.to_time.strftime('%a')}%>
<%= hours ? time_diff(hours[1]) : "0:00"%>
<% end %>
<% end %>
Where #dates is an array of the dates in the timerange. Now refactoring... ; )

you can do this:
<% for project in #projects do %>
<%= project.name %>
<% (1..7).each do |day| %>
<% date = "2011-05-#{22 + day}" #please modify this, this is only to keep the example simple %>
<% hours = #log_week_project.detect{|key| key[0] == project && key[1] == date } %>
"<%= hours ? time_diff(hours) : "" %>
<% end %>
<% end %>
<% end %>
<% end %>
next, refactor this to put all the logic in a helper.
Hope, that helps

In your database you may have 0 as default for all hours, that way it can't be blank.
But if you storing hours in logs, then you can't do above. In that case, use
time_diff(hours).present? ? time_diff(hours) : 0
Or else you may use anything else than 0, that way tables will not disturbed.

Related

Rails: How to sum total price per day

I want to calculate total price of ticket sales on the day it sold.
But it shows total price of the last day (Today's total price).
What i got now :
October 20
Ticket 1 USD 1.11
Ticket 2 USD 2.22
Ticket 3 USD 3.33
Total For Today
USD 7.77 (HERE'S THE PROBLEM)
-------------------------------
October 21
Ticket 1 USD 1.11
Ticket 2 USD 2.22
Ticket 3 USD 4.44
Today's total
USD 7.77
What i exactly want is :
October 20
Ticket 1 USD 1.11
Ticket 2 USD 2.22
Ticket 3 USD 3.33
Today's total
USD 6.66
-------------------------------
October 21
Ticket 1 USD 1.11
Ticket 2 USD 2.22
Ticket 3 USD 4.44
Total For Today USD 7.77
What i want is how to calculate total price of each day (total price on the day those tickets has been sold)?
In ticket model
scope :daily_total_price, ->(date_time = Time.now) { where('created_at BETWEEN ? AND ?',date_time.beginning_of_day, date_time.end_of_day).sum(:price) }
In Ticket controller
def index
#tickets = Ticket.all.order("created_at DESC")
#users = User.all
end
In Ticket index
<% tickets.each do |ticket| %>
<%= ticket.created_at.strftime("%d %B") %>
<%= ticket.user.username %>
<%= ticket.price %>
<% end %>
<%= Ticket.daily_total_price) %>
Thank for helping :)
So to do what you want, I'd imagine the following code...
<% date_for_group = nil %>
<% #tickets.each do |ticket| %>
<% if ticket.created_at.to_date != date_for_group %>
<%# show total of previous group %>
<% if date_for_group %>
<br/>
Total for <%=date_for_group.strftime("%d %B") %></br>
<%= Ticket.daily_total_price(date_for_group) %>
<br/>
<% end %>
<%= ticket.created_at.strftime("%d %B") %>
<br/>
<% date_for_group = ticket.created_at.to_date %>
<% end %>
<%= ticket.user.username %>
<%= ticket.price %>
<% end %>
<% if date_for_group %>
<br/>
Total for <%=date_for_group.strftime("%d %B") %></br>
<%= Ticket.daily_total_price(date_for_group) %>
<br/>
<% end %>
You're doing
<%= Ticket.daily_total_price %>
But that always defaults the first argument to today's date (you wrote ->(date_time = Time.now) What you want to do is to pass the appropriate date in... put the date into a '#date' instance variable in your controller and then do...
<%= Ticket.daily_total_price(#date) %>

Time Difference of One Item, Two Fields, Grouped by Days - Rails

Controller
#clock_events = ClockEvent.where(user_id: current_user.id)
#clock_event_days = #clock_events.group_by { |t| t.clock_in.beginning_of_day }
#clock_out = ClockEvent.where(user_id: current_user.id, clock_out: nil).last
View
<% if #clock_events.exists? %>
<div class="row">
<% #clock_event_days.each do |day, clock_events| %>
<div class="col-md-6">
<div class="list-group">
<h4 class="list-group-item-heading"><%= day.strftime("%A, %B #{day.day.ordinalize}") %></h4>
<% clock_events.each do |clock_event| %>
<p class="list-group-item-text time-group">
<%= clock_event.clock_in.strftime("%l:%M%p") %>
<% if clock_event.clock_out.present? %>
-
<%= clock_event.clock_out.strftime("%l:%M%p") %> -
<%= sprintf "%.2f", ((clock_event.clock_out - clock_event.clock_in) / 1.hour) %> hrs
<% end %>
</p>
<% end %>
</div>
<div class="daily-total">
<strong>Total: **need total here**</strong><br>
</div>
</div>
<% end %>
</div>
<% end %>
Result
Friday, February 17th
5:15PM - 6:15PM - 1.00 hrs
5:29PM - 5:41PM - 0.21 hrs
5:49PM - 6:45PM - 0.94 hrs
6:49PM - 6:49PM - 0.00 hrs
6:50PM - 6:57PM - 0.11 hrs
Total:
Saturday, February 18th
4:45PM - 4:52PM - 0.11 hrs
Total:
Can some one help me get the total hours for each day and then for the entire week (Friday-Thursday)?
I am guessing I should be using the model for getting the time difference instead of using the view...but I can't seem to figure it out.
Putting a variable to track this in the view is the most straightforward solution, even if it's maybe not the most elegant one.
<% total_hours = 0 %>
<% clock_events.each do |clock_event| %>
<% total_hours += (clock_event.clock_out - clock_event.clock_in) %>
<% end %>
and then output it
<strong>Total: <%= total_hours / 1.hour %></strong><br>
Edit 1:
You could also clean things up a bit by adding this in the model, something like
class ClockEvent < ApplicationRecord
# ..
def duration
clock_out - clock_in
end
end
and use that in the view
<% total_hours += clock_event.duration %>
...
<%= sprintf "%.2f hrs", (clock_event.duration / 1.hour) %>
Edit 2:
You can clean it up even more by skipping the total_hours variable all together and do this.
<%= clock_events.sum(&:duration) / 1.hour %>

How to sort group_by month and year in view?

pages_controller.rb
#past_challenges_by_years = #past_challenges.group_by { |t| [t.deadline.year, t.deadline.month] }
How can I break it down in the view with year and then subdivide it with month like this:
2014 # Years
01 # Months
Challenge
Challenge
02
Challenge
08
Challenge
2016
03
Challenge
08
Challenge
view.html.erb
<% #past_challenges_by_years.sort.each do |year, challenges| %>
<%= year %>
<%= month %> # I don't know how to define this.
<% for challenge in challenges %>
etc...
<% end %>
<% end %>
I would start by sorting the keys and then look up the challenges while iterating through those keys.
<% #past_challenges_by_years.keys.sort.each do |(year, month)| %>
<%= year %>
<br />
<%= month %>
<br />
<% #past_challenges_by_years[[year, month]].each do |challenge| %>
<%= challenge %>
<br />
Edit: Here is a new solution that only displays years and months once.
#past_challenges_by_years.keys.map { |a| a[0] }.uniq.sort.each do |year|
puts year
#past_challenges_by_years.keys.select { |a| a[0] == year }.map { |a| a[1] }.uniq.sort.each do |month|
puts month
#past_challenges_by_years[[year, month]].each do |challenge|
puts challenge
end
end
end
I think it should be simple enough to convert to ERB.

Ruby on Rails only show data value once if there are more than one of the same

In my ruby on rails application I am currently displaying the date and time each film is shown through:
<% if not #film.showings.blank? %>
To book click on a time below:</br>
<% #film.showings.each do |showing| %>
<%= showing.show_date.strftime("%A %e %B %Y") %># <%= showing.show_time.strftime("%H:%M") %><br>
<% end %>
<% else %>
<p>There are currently no showings for this film.</p>
<% end %>
And this displays data like the following:
Sunday 25 January 2015 # 12:00
Sunday 25 January 2015 # 16:00
Monday 26 January 2015 # 11:00
Monday 26 January 2015 # 22:00
Tuesday 27 January 2015 # 22:00
Wednesday 28 January 2015 # 11:00
Wednesday 28 January 2015 # 12:00
Wednesday 28 January 2015 # 16:00
Wednesday 28 January 2015 # 19:30
But what I want to be able to do is if the date is repeated then it only shows it once and repeats the time, so for example the date Sunday 25 January 2015 would be shown as:
Sunday 25 January 2015 # 12:00 16:00
You should group records by date and join hours.
<% if not #film.showings.blank? %>
To book click on a time below:</br>
<% #film.showings.group_by{|showing| showing.show_date.strftime("%A %e %B %Y") }.to_a.each do |showing| %>
<%= showing.first %># <%= showing.last.map{|s| s.show_time.strftime("%H:%M")}.join(' ') %><br>
<% end %>
<% else %>
<p>There are currently no showings for this film.</p>
<% end %>
You could place a function like this in helpers or a decorator if you are using them. I'm assuming you pass in the showings from #file.showings. This will return an array that is how you want that you could loop over in the view and display.
def show_times(showings)
showings.each_with_object({}) do |showing, hash|
key = showing.show_date.strftime("%A %e %B %Y")
hash[key] ||= []
hash[key] << showing.show_time.strftime("%H:%M")
end.map do |date, times|
"#{date} # #{times.join(' ')}"
end
end
Given an array of datetime objects, you can use the following code to extract a nested hash of date and time showings. It probably best lives inside your Film model.
def showings_hash
showings.pluck(:show_time).map {|datetime| datetime.to_date}.uniq.map{|date| {date => dates.select{|datetime| date == datetime.to_date}}}
end
This returns:
- 2015-01-12:
- !ruby/object:DateTime 2015-01-12 18:00:00.000000000 Z
- !ruby/object:DateTime 2015-01-12 19:00:00.000000000 Z
- !ruby/object:DateTime 2015-01-12 20:00:00.000000000 Z
- 2015-01-13:
- !ruby/object:DateTime 2015-01-13 20:00:00.000000000 Z
You can then iterate through each showing date in the view like so:
<% #film.showings_hash.each do |showing_date| %>
<li>
<%= showing_date %>
<% showing_date.each do |showtime| %>
<%= showtime.strftime("%H:%M") %>
<% end %>
</li>
<% end %>

Grouping by Days Created_At

I am trying to get a loop to post videos grouped by each day by created_at.
For example:
December 5, 2012 -
Video 9
Video 8
Video 7
December 4, 2012 -
Video 6
Video 5
December 3, 2012 -
Video 4
Video 3
Video 2
Video 1
videos_controller:
def index
#title = 'Hip Hop Videos, Breaking News, Videos, And Funny Shxt | HOTDROPHIPHOP'
#description = ''
#videos = Video.all
#days = Video.where(:created_at == Time.today )
end
View file:
<% #days.each do |day| %>
<div class="video-date">December 4, 2012</div>
<% #videos.each do |video| %>
<% end %>
<% end %>
I also need to get that div to show that day's date as well.
I searched around and couldn't find a solution and tried the group_by (which seemed the cleanest) but couldn't get it to work. I am a bit rusty on my Rails as I haven't touched it for months.
You can do this:
#videos = Video.where(Video.arel_table[:created_at].gteq(some_date_value))
#video_days = #videos.group_by {|video| video.created_at.to_date }
Where #video_days will be a hash in the form of {some_date_value: [{video1}, {video2}, etc], next_date_value: [{video3}, {video4}, etc], etc...}.
Since you are calling .to_date on the created_at field, it will drop all of the time information, effectively grouping everything by day.
You can loop through it like:
<% #video_days.each do |day, videos| %>
<%= day.strftime("some format") %>
<% videos.each do |video| %>
<%= #output videos how you see fit %>
<% end %>
<% end %>
The best way is to use the gem Groupdate
e.g User.group_by_day(:created_at).count
Allow you to order by day, week, hour
First of all it makes no sense to call only videos from today and then run through all videos.
I would try it this way:
Controller
#videos = Video.all(:conditions => ["created_at >= ?", Date.today.at_beginning_of_month])
View
<% Date.today.at_beginning_of_month.upto(Date.today).each do |date| %>
<%= date %>: <%= #videos.select{|u| u.created_at == date }.title %>
<% end %>
It should give you a Video list with "Date: Video Title" for the actual month.
Assumed you are using mysql and video has attribute title
I am posting the following code to help you to grasp the logic ( Surely it need re-factoring )
Controller
#videos = {}
videos = Video.order("DATE(created_at) DESC, title ASC").select("DATE(created_at) as created_at, title, id" )
videos.collect{|x| #videos[x.created_at.to_s] = #videos[x.created_at.to_s] ? #videos[x.created_at.to_s] << x.title : [x.title] }
view
<% #videos.each do |posted_date, videos| %>
<div class="video-date"><%= Date.parse(posted_date.to_s).strftime("%b %d, %Y") %> </div>
<%= videos.join(", ") %>
<% end %>

Resources