Table in ERB with weekday names - ruby-on-rails

I am very new to Ruby/Rails and just I am trying to find the most elegant way to do a table in ERB where the columns are the weekday and the rows are each record for a given model. The concept itself is easy and I can probably do it through a bunch of manual code, but I would like to leverage as much rails/erb magic as possible. To be more specific, here is what I have defined.
A model that lists all users
A model that lists a single status for each user for a given date (each user can only have one status for a given date: say 'on' or 'off')
I want a view that lists all the users in a table with their status for that week. Eg:
Mon | Tue | ... | Fri
User1 on | off | ... |
User2 off | | ... |
A couple things to note:
- (as I already mentioned) A user can only have one status per day
- A user may not have any status for a given day
- No columns for Sat/Sun
- The table will always show the full current week, even if the day is on Tue (ie. in that case the Wed .. Fri entries will just be blank).
Now, again, I am sure I can write a bunch of erb code to do this all manually, but was hoping to use some rails magic. Any pointers or help would greatly be appreciated.

You could always do something like:
#monday = Date.today.at_beginning_of_week
7.times do |x|
#days << x.days.since(#monday)
end
Then loop through #days in your view.
You could print the date, or just the day in hour table rows. Useful in that you can also look up your users status using that date.

Related

Ruby on Rails: How to get all the records and sort it by the lowest days difference of two dates

I am using postgresql. I have one table the Shipments table and I have two date columns on the table called arrival_date and release_date, what I wanted to do is to get all the record and sort it according to the lowest days difference of arrival_date and release_date, for example:
Arrival Date, Released Date
2017-06-04, 2017-06-30
2017-05-02, 2017-05-05
So in this example the days difference of first record is 26 days and the second one is 3 days, so the second record should be the first one.
The easiest way would be to subtract the two dates using Postgres:
ordered_shipments = Shipment.order("(arrival_date - released_date) ASC")
This will subtract the Unix timestamps of the two dates and sort the difference in ascending order.
I got inspiration from this answer.
I think that you need improve your query in postgres
in example
Table
id | dateA | dateB
select dateA, dateB, age(timestamp dateA, timestamp dateB) diffdate from Table order by diffdate desc;
You may want to use sort_by! method:
Shipment.all.sort_by!{ |shipment| shipment.released_date - shipment.arrival_date }

Jira JQL decode Monday Tuesday etc

Has anyone managed to create a query that returns data based on Monday Tuesday Wednesday next week?
I can get a result with startofweek(1) and endofweek(1) but that doesn't break down to each day.
Ideally I'd get a result like this:
Monday | Tuesday | Wednesday | Thursday | Friday |
--------------------------------------------------
Card1 Card1
Card3 Card4
Card5
Any help would be appreciated.
I believe that is not possible. Columns on Jira boards are configurable by issue status, not via JQL. However, you could do the following:
1) Add a quickfilter for each weekday. JQL actually is more powerful as it looks on first sight: You can use expressions like "status changed ON ..." - enabling you to observe issues that were touched at these days. This would per filter show a single day only on the board, since filters are internally combined via AND.
2) Do the same JQL but with swim lanes. You could see each day on the board at the same time in a separate swimlane. But I consider this mind-twisting.
You should write a more elaborate JQL that fits what exactly you want to see, of course. There is another way, though you'd need (low) programming skills:
3) Use Jira REST API to fetch your sprint/week data and throw it in a grid that suits your needs yourself.

Store the day of the week and time?

I have a two-part question about storing days of the week and time in a database. I'm using Rails 4.0, Ruby 2.0.0, and Postgres.
I have certain events, and those events have a schedule. For the event "Skydiving", for example, I might have Tuesday and Wednesday and 3 pm.
Is there a way for me to store the record for Tuesday and Wednesday in one row or should I have two records?
What is the best way to store the day and time? Is there a way to store day of week and time (not datetime) or should these be separate columns? If they should be separate, how would I store the day of the week? I was thinking of storing them as integer values, 0 for Sunday, 1 for Monday, since that's how the wday method for the Time class does it.
Any suggestions would be super helpful.
Is there a way for me to store the the record for Tuesday and
Wednesday in one row or do should I have two records?
There are several ways to store multiple time ranges in a single row. #bma already provided a couple of them. That might be useful to save disk space with very simple time patterns. The clean, flexible and "normalized" approach is to store one row per time range.
What is the best way to store the day and time?
Use a timestamp (or timestamptz if multiple time zones may be involved). Pick an arbitrary "staging" week and just ignore the date part while using the day and time aspect of the timestamp. Simplest and fastest in my experience, and all date and time related sanity-checks are built-in automatically. I use a range starting with 1996-01-01 00:00 for several similar applications for two reasons:
The first 7 days of the week coincide with the day of the month (for sun = 7).
It's the most recent leap year (providing Feb. 29 for yearly patterns) at the same time.
Range type
Since you are actually dealing with time ranges (not just "day and time") I suggest to use the built-in range type tsrange (or tstzrange). A major advantage: you can use the arsenal of built-in Range Functions and Operators. Requires Postgres 9.2 or later.
For instance, you can have an exclusion constraint building on that (implemented internally by way of a fully functional GiST index that may provide additional benefit), to rule out overlapping time ranges. Consider this related answer for details:
Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL
For this particular exclusion constraint (no overlapping ranges per event), you need to include the integer column event_id in the constraint, so you need to install the additional module btree_gist. Install once per database with:
CREATE EXTENSION btree_gist; -- once per db
Or you can have one simple CHECK constraint to restrict the allowed time period using the "range is contained by" operator <#.
Could look like this:
CREATE TABLE event (event_id serial PRIMARY KEY, ...);
CREATE TABLE schedule (
event_id integer NOT NULL REFERENCES event(event_id)
ON DELETE CASCADE ON UPDATE CASCADE
, t_range tsrange
, PRIMARY KEY (event_id, t_range)
, CHECK (t_range <# '[1996-01-01 00:00, 1996-01-09 00:00)') -- restrict period
, EXCLUDE USING gist (event_id WITH =, t_range WITH &&) -- disallow overlap
);
For a weekly schedule use the first seven days, Mon-Sun, or whatever suits you. Monthly or yearly schedules in a similar fashion.
How to extract day of week, time, etc?
#CDub provided a module to deal with it on the Ruby end. I can't comment on that, but you can do everything in Postgres as well, with impeccable performance.
SELECT ts::time AS t_time -- get the time (practically no cost)
SELECT EXTRACT(DOW FROM ts) AS dow -- get day of week (very cheap)
Or in similar fashion for range types:
SELECT EXTRACT(DOW FROM lower(t_range)) AS dow_from -- day of week lower bound
, EXTRACT(DOW FROM upper(t_range)) AS dow_to -- same for upper
, lower(t_range)::time AS time_from -- start time
, upper(t_range)::time AS time_to -- end time
FROM schedule;
db<>fiddle here
Old sqliddle
ISODOW instead of DOW for EXTRACT() returns 7 instead of 0 for sundays. There is a long list of what you can extract.
This related answer demonstrates how to use range type operator to compute a total duration for time ranges (last chapter):
Calculate working hours between 2 dates in PostgreSQL
Check out the ice_cube gem (link).
It can create a schedule object for you which you can persist to your database. You need not create two separate records. For the second part, you can create schedule based on any rule and you need not worry on how that will be saved in the database. You can use the methods provided by the gem to get whatever information you want from the persisted schedule object.
Depending how complex your scheduling needs are, you might want to have a look at RFC 5545, the iCalendar scheduling data format, for ideas on how to store the data.
If you needs are pretty simple, than that is probably overkill. Postgresql has many functions to convert date and time to whatever format you need.
For a simple way to store relative dates and times, you could store the day of week as an integer as you suggested, and the time as a TIME datatype. If you can have multiple days of the week that are valid, you might want to use an ARRAY.
Eg.
ARRAY[2,3]::INTEGER[] = Tues, Wed as Day of Week
'15:00:00'::TIME = 3pm
[EDIT: Add some simple examples]
/* Custom the time and timetz range types */
CREATE TYPE timerange AS RANGE (subtype = time);
--drop table if exists schedule;
create table schedule (
event_id integer not null, /* should be an FK to "events" table */
day_of_week integer[],
time_of_day time,
time_range timerange,
recurring text CHECK (recurring IN ('DAILY','WEEKLY','MONTHLY','YEARLY'))
);
insert into schedule (event_id, day_of_week, time_of_day, time_range, recurring)
values
(1, ARRAY[1,2,3,4,5]::INTEGER[], '15:00:00'::TIME, NULL, 'WEEKLY'),
(2, ARRAY[6,0]::INTEGER[], NULL, '(08:00:00,17:00:00]'::timerange, 'WEEKLY');
select * from schedule;
event_id | day_of_week | time_of_day | time_range | recurring
----------+-------------+-------------+---------------------+-----------
1 | {1,2,3,4,5} | 15:00:00 | | WEEKLY
2 | {6,0} | | (08:00:00,17:00:00] | WEEKLY
The first entry could be read as: the event is valid at 3pm Mon - Fri, with this schedule occurring every week.
The second entry could be read as: the event is valid Saturday and Sunday between 8am and 5pm, occurring every week.
The custom range type "timerange" is used to denote the lower and upper boundaries of your time range.
The '(' means "inclusive", and the trailing ']' means "exclusive", or in other words "greater than or equal to 8am and less than 5pm".
Why not just store the datestamp then use the built in functionality for Date to get the day of the week?
2.0.0p247 :139 > Date.today
=> Sun, 10 Nov 2013
2.0.0p247 :140 > Date.today.strftime("%A")
=> "Sunday"
strftime sounds like it can do everything for you. Here are the specific docs for it.
Specifically for what you're talking about, it sounds like you'd need an Event table that has_many :schedules, where a Schedule would have a start_date timestamp...

Rails UI date navigation control generation (ActiveRecord logic)

I have an app where users can view sporting events for today's date or a given date (if the user manually enters it in the URL). A user can navigate sporting events by date with a UI control, which crudely looks like this:
Tue Nov. 27 | Wed Nov. 28 | Thu Nov. 29
(The dates are links except the current active one in the middle.)
This date nav control works great for sporting events that occur nearly every day. The user is never staring at a blank page of events. However, for sporting events such as the NFL, events only occur on Monday, Thursday, Sunday (sometimes Saturday) and it becomes cumbersome for the user to use the date nav UI control.
What I'd like to do is to change the logic behind the creation of the date nav UI links to include only those dates that have events. The logic would be something like:
Given today's date or a date supplied by the user, return the nearest date which has events (current_date)
Given a current_date, find the nearest previous date which has events (previous_date)
Given a current_date, find the nearest next date which has events (next_date)
So, in the case of the NFL, the date nav UI control might look like, assuming today's date is Nov 27:
Mon Nov. 25 | Thu Nov. 29 | Sun Dec. 2
The way in which to tell if there are any events for a given date is by start_time field in the DB.
I don't like to post a question without some code, but I'm looking for some guidance as to how I should approach this without having to hit the DB a ton of times.
NOTE #1: When you click on a date for a sport, it returns all the events for that date. The date nav is not used for viewing individual events.
Create a scope method in your model that grabs the next two start dates for the given sport, and the most recent start date. Limit it to return only three records.
Use this scope in your controller to fetch only those three game objects. With those three game objects in the controller, pass them to a view partial as an instance variable (e.g. render #games), and in the partial, write the generic link code <%= link_to "#{#games.start_time}", your_path(#game) %>. Rails will automatically iterate this partial for the number of objects in the collection (the three games).
Source: http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

Rails / Postgresql: How can I quickly group records using a datetime field by date in a specific time zone?

I'm using postgresql with Ruby on Rails. Right now I'm using this to count the number of users who sign up over each day:
#users_signup_by_day = User.count(:order => 'DATE(created_at) DESC', :group => ["DATE(created_at)"])
This works, but the users are grouped according to UTC. How can I group the users according to EST?
Update
the users are grouped according to UTC. How can I group the users
according to EST?
To clarify: A certain date that begins and ends in UTC is a different span of time than the same date that begins and ends in EST. Right now the query generated by the above command considers the datetime that the user was created to be in UTC, and groups users by the day that they were created according to that span of time. How can I compensate for this considering I would like to group by dates according to EST and not UTC?
I have tried the following (thanks Erwin Brandstetter):
User.count(:order => "DATE(created_at AT TIME ZONE 'EST') DESC", :group => ["DATE(created_at AT TIME ZONE 'EST')"])
But this doesn't give the correct result because the first result is
["2011-12-02", 276]
and it's not 12/02/2011 in EST (UTC - 5 hours) yet. I'd appreciate any help.
You can use the expression timestamp_column AT TIME ZONE 'EST' before you cast to date. Consider this demo:
SELECT t AS ts_utc
,t::date AS date_utc
,t AT TIME ZONE 'EST' AS ts_est
,(t AT TIME ZONE 'EST')::date AS date_est
FROM (
VALUES
('2011-12-1 03:00'::timestamp) -- defaults to timestamp without time zone
,('2011-12-2 12:00')
,('2011-12-3 23:00')) t(t);
Result:
ts_utc | date_utc | ts_est | date_est
---------------------+------------+------------------------+------------
2011-12-01 03:00:00 | 2011-12-01 | 2011-12-01 09:00:00+01 | 2011-12-01
2011-12-02 12:00:00 | 2011-12-02 | 2011-12-02 18:00:00+01 | 2011-12-02
2011-12-03 23:00:00 | 2011-12-03 | 2011-12-04 05:00:00+01 | 2011-12-04
ts_est constitutes the literal given time at that time zone, which is automatically converted to the time zone of your current location for display, '+01' in my case, as my database server sits in Vienna, Austria and has matching locale settings.
It is not entirely clear from your question, what "according to EST" means. But one way or the other, you can achieve it with that construct.
I'm a little late to the party, but since I just ran into this issue myself I thought I would post something that appears to be working in my situation. I'm modifying the date field in the database by adding/subtracting the UTC offset based on the configured rails timezone. So for example:
offset = Time.zone.formatted_offset
interval = "#{offset[0]} time '#{offset[1..-1]}'"
#users_by_day = User.group("date(created_at #{interval})").select("count(id) as counter, date(created_at #{interval}) as created")
Not very pretty, but seems to get the job done.

Resources