Create endtime from collection_select - ruby-on-rails

Good Afternoon,
Feels like a newbie question but I have the following:
:start_datetime t.date
:end_datetime t.date
:length t.integer
On create my end_datetime is nil but I want to get it by adding the length to the start_datetime in order to generate the endtime.
Currently my integer is stored as '30 Mins', 30 and '1 Hour', 60.
Drawn a blank on where I should do this. I'm guessing I need to create it in the model when the booking is created.

If you're going to be using increments of minutes, I think you should start by redefining your datetimes as datetime rather than date.
then do something like this:
#controller
def create
...
end_datetime = params[:start_datetime] + params[:length].minutes
#save this
...
end

Related

Rails best practice for getting past 10 entries in model

I want to get the sales from the past 10 weeks. I can do that with this method:
def past_10_sales(yw)
past_sales = []
year_week = yw
10.times do
sales = Sales.where(year_week: year_week.previous)
past_sales << sales unless sales.empty?
year_week = year_week.previous
end
past_sales.flatten
end
But now I need this method somewhere else as well and wanted to put it in the sales.rb model, but i don't know what the best practice of this would be or if there is something in Rails that makes this better? It feels wrong calling Sales.where in the sales.rb model...
Edit:
year_week is a model with the current year and calendar week. So the current year_week would be 202244. Calling year_week.previous gives me 202243. I'm getting all the sales from a specific calendar week.
This works fine btw.
Edit 2:
I have a model sales.rb. in this model i save all the sales. it only matters in which calendar week the sales were made, the exact date does not matter so it looks something like this:
create_table "sales" do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "year_week_id"
etc...
end
i also have a model year_week which just saves the year and the calendarweek and the id is like this: yearweek (so 202244). there are methods like def previous that return the previous year_week.
What i now want is all the sales form the past 10 weeks. so i call this:
sales = Sales.where(year_week: year_week.previous)
past_sales << sales unless sales.empty?
year_week = year_week.previous
10 times because this way it gets all the sales in the past 10 weeks but it just doesn't feel right and i was wondering if/what better way there is for this.
If you really have to work with a bigint you can do something like:
starting_week = Time.current.advance(weeks: -10).strftime("%Y%V").to_i
Sale.where(
year_week: starting_week..
)
This uses ISO weeks. Otherwise use %U or %W. See DateTime#strftime. Of course using date_trunc('week', sales.created_at) or the equivilent database function would make this column reduntant unless the week isn't actually the same as the calender week its created.
you can do it in the below way:
def past_10_sales(yw)
year_week = yw.previous
past_sales = Sales.where(year_week: (year_week-9..year_week.to_a))
end

convert string to datetime when making activerecord db query

I have the following table in my DB:
class CreateGoogleRecords < ActiveRecord::Migration
def change
create_table :google_records do |t|
t.string :user_id
t.string :date
t.text :stats
t.string :account_name
t.integer :total_conversions
t.decimal :total_cost
t.timestamps
end
end
end
I'm looking to create a table inside a view that groups together records by month (I can't use "date created because sometimes they are scraped in bulk from an API).
There is a lot of legacy code involved so rather than convert the column to datetime I was hoping I could convert the date string to a datetime object when performing the query.
I've tried writing a scope like:
scope :stats_for_reports, ->(start_date, end_date, user_ids) { select('user_id, sum(total_cost) as total_cost, sum(total_conversions) as total_conversions')
.where('date >= ? and date <= ?', start_date, end_date)
.where(user_id: user_ids)
.group(DateTime.parse(:date).month.to_s)}
but I receive a TypeError: can't convert Symbol into String error.
In the console I've been trying things like:
GoogleRecord.where(date: date_start..date_end).group{ |m| DateTime.parse(m.date).month }
or
GoogleRecord.where(date: date_start..date_end).group(:date).to_date
Am I on the right track with any of these?
Have you considered using ActiveRecord before_save, after_save, after_initialize? You may be able to create a DateWrapper (very similar to the EncryptionWrapper below) and convert the string to a date transparent to the rest of the code.
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html

How to save datetime fields with validations? [Ruby / Rails 4 / Postgres]

Ok, so I need help with datetime database fields.
Let's say my table is called "events" and has a datetime field named "starts_at". I have confirmed this in my schema.rb file (technically I am using Postgres):
create_table "events", force: true do |t|
t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.datetime "starts_at
...
end
In my event.rb model, I have a validation to make sure the starts_at datetime is set:
attr_accessor :starts_at
validates :starts_at, presence: true
I'm testing this via the rails console and I can't get it to save any value, let alone create any validation errors. What gives? For example:
e = Event.new
e.name = 'Post 1'
e.starts_at = DateTime.now.utc
e.save
It appears to save, but there is no validation error, no mention of "starts_at" in the displayed query. Starts_at is nil in the database. My schema defines it as a "datetime" field but it ignores my DateTime variable. I thought, ok, maybe it is technically a string field and rails doesn't auto-convert for me:
e = Event.new
e.name = 'Post 2'
e.starts_at = DateTime.now.utc.to_s
e.save
Same thing. Thinking that the resulting format is being rejected in Postgres, I try this:
e = Event.new
e.name = 'Post 3'
e.starts_at = DateTime.now.utc.strftime('%Y-%m-%d %H:%M:%S')
e.save
I thought it might work with:
e.starts_at = Time.now.utc
Or, for Unix timestamp integer style:
e.starts_at = DateTime.now.utc.to_i
Nope, nothing works. What am I doing wrong?
Some questions:
How do I get a datetime field to accept my input -- any input! -- and actually save it to the database? Do I need to know which time format is ultimately being used by the database type (Postgres, MySQL, etc) and adjust accordingly? i.e. How does database agnosticism apply?
How can I update my validation to check if the database actually accepted my input? It's counterintuitive that I am validating the presence of my starts_at variable, but it will in fact allow it to be saved as nil.
Remove this line:
attr_accessor :starts_at
It masks the original setter that comes with Rails and sets a instance variable instead.
If there is a column (like the starts_at column here) in the database then there is no need to define a getter oder setter method on your own.
Probable issue is that Rails is protecting you from mass assignment of variables.
You need to remove attr_accessor on that columns that are saved as NULL in the database.
Explained really well here - What is attr_accessor in Ruby? and Difference between attr_accessor and attr_accessible

Rails cannot write to database field

I am trying to do a very simple update on a field that does not have any validation whatsoever. However, the update always fails. Here is what the code looks like:
# model
class Event < ActiveRecord::Base
attr_accessible :start_time
..
end
# migration
class CreateEvents < ActiveRecord::Migration
def change
t.datetime :start_time
end
end
# console
Event.first.update_attribute(:start_time, "02:00")
The query that was run in the Rails log does not even include the start_time attribute!
(0.2ms) BEGIN
(4.5ms) UPDATE events SET updated_at =
'2012-07-24 19:51:33', repeat_days = '--- \n- wed\n- sat\n- sun\n',
event_date_list = '--- []\n\n' WHERE events.id = 3763
(5.5ms) COMMIT
I cannot begin to make sense of this. Can anyone help me understand the root cause of this problem?
You are a passing it a string, not a Date, Time, or Datetime object.
It looks like you just want to store the time, not the date attached. But maybe you meant to attach a date as well. If you want to store the date as well, look up the Datetime class.
If you want to store just the time (hours, minutes, and seconds), then I would suggest you change your start_time field to be an integer, and store the seconds: 2.hours or 2.hours + 4.minutes + 6.seconds.
You can convert that easily in to time again.

Looking for best way to retrieve business hours from database

I'm using Ruby on Rails and I'm storing business hours like this:
CREATE TABLE "business_hours" (
"id" integer NOT NULL PRIMARY KEY,
"business_id" integer NOT NULL FOREIGN KEY REFERENCES "businesses",
"day" integer NOT NULL,
"open_time" time,
"close_time" time)
(which came from the thread at:
Storing Business Hours in a Database )
Now I want to pull the hours out for each day of the week and display them, and I'm trying to find the best (or at least a good) way.
Should I just have a helper method that loops through getting the days (from 0..6) for a given business_id and assign it to a variable for the associated day? I feel like there must be a better way -- with an array, or something, but it's hurting my head thinking about it, because I also have a form of 'select's where any of the hours for a given business can be updated at once.
Thanks for any guidance!
Use the enum column plugin to declare the day field as a enum field.
class BusinessHours < ActiveRecord::Migration
def self.up
create_table :business_hours do |t|
t.integer :business_id, :null => false
t.enum :day, :limit =>[:sun, :mon, :tue, :wed, :thu, :fri, :sat], :nill => false
t.time :open_time, :null => false
t.time :close_time, :null => false
end
end
def self.down
drop_table :business_hours
end
end
Now when you do find on the BusinessHour model you will get the day as a string.
b = BusinessHour.find_by_business_id(2).first
p b.day.to_s.camelize #prints Sun/Mon/Tue etc.
You can use the enum_select and enum_radio form helpers to create list box/radio button group for the enum group:
Since the number of days in a week really is fixed, you can join the table 6 times (plus the original) and do a query for a single row. I'd probably just do a single query and loop through the rows though.
Have you considered serializing the business hours? Using serialization you are essentially storing objects in the database.
class BusinessHour < ActiveRecord::Base
serialize :hours
...
end
BusinessHour.create :business => #business, :hours =>
{:mon => [mon_start_time, mon_end_time], :wed => [wed_start_time, wed_end_time],
...}
Personally I would go with the bitwise approach described in linked question. All you really need to do to make it work is write new accessor methods.
It would be easier to find the business and use the associations to retrieve the business_hours rows.
Try this in your view
<% #business.business_hours.each do |hrs| %>
<%= hrs.day_name %>: Open-<%= hrs.open_time %> Close-<%= hrs.close_time %>
<%- end -%>
In your business_hour.rb model file, create a default scope to make sure the days are always listed in order. You can also create the day_name method to make it easier to display the day.
default_scope :order => 'day ASC'
def day_name
case self.day
when 0 then "Sun"
when 1 then "Mon"
...
end
end

Resources