Show content based on holidays - ruby-on-rails

ruby 1.9.2p290
rails 3.1.1
I have these models:
class Recipe < ActiveRecord::Base
belongs_to :festivity
end
class Festivity < ActiveRecord::Base
has_many :recipes
end
I have the following field in the "recipes" table:
festivity_id
And the following datetime fields in the "festivities" table:
starts_at
ends_at
How to display the content based on festivities dates?
I started think in this static way:
class PagesController < ApplicationController
def home
#recipes_by_festivities = Recipe.where(:festivity_id => 4).all(:order => 'RAND()', :limit => 8)
end
end
For example: In Xmas period (about all december month), I want to show a recipe list with the festivity_id = 1. In Thanksgiving period I wanna just a list of recipes with festivity_id = 4.
Am I clear? Let me know if I not.
SOLUTION
#current_festivity = Festivity.find(:last, :conditions => ["? between starts_at AND ends_at", Time.utc(0,Time.now.month,Time.now.day,0,0,0)])
#recipes_by_festivities = Recipe.where(:festivity_id => #current_festivity).all(:order => 'RAND()', :limit => 8)

What I would do is create a "name" field in the festivities table, then use the holidays gem to determine if there's a holiday on a specific date.
Holdays.on(Date.today)
Which will return a list of hashes: each hash has a name key. Then you could use that to look up the correct Festivity object current_festival = Festivies.where(:name => Holdays.on(Date.today)[0][:name]) and from there get the recipes: current_festival.recipes

This assumes starts_at and ends_at are saved with the same year (0), except if the period is in the middle of two years (Christmas time, for example): in that case, the ends_at year must be 1.
Festivity.create(name: 'Christmas time', starts_at: Time.utc(0,12,25,0,0,0), ends_at: Time.utc(1,1,6,23,59,59))
Festivity.create(name: 'Ferragosto', starts_at: Time.utc(0,8,15,0,0,0), ends_at: Time.utc(0,8,15,23,59,59))
Recipe.create(festivity: Festivity.find_by_name('Periodo natalizio'))
Recipe.create(festivity: Festivity.find_by_name('Ferragosto'))
# Searching for festivities today
Recipe.includes(:festivity).where(['? BETWEEN festivities.starts_at AND festivities.ends_at', Time.utc(0,Time.now.month,Time.now.day,12,0,0)]).all
# Searching for festivities on 15 August (in Italy there is a festivity that is called Ferragosto)
Recipe.includes(:festivity).where(['? BETWEEN festivities.starts_at AND festivities.ends_at', Time.utc(0,8,15,12,0,0)]).all
# Searching for festivities on 28 December
Recipe.includes(:festivity).where(['? BETWEEN festivities.starts_at AND festivities.ends_at', Time.utc(0,12,28,12,0,0)]).all
And this is a possible implementation of a function:
def recipes_on(day, month)
Recipe.includes(:festivity).where(['? BETWEEN festivities.starts_at AND festivities.ends_at', Time.utc(0,month,day,12,0,0)]).all
end

Since you want to find recipes by festivities, you can do this:
def home
festivity_today = some_custom_festivity_lookup(Time.now)
if !festivity_today.nil?
#recipes = Recipe.where(festivity_id: festivity_today.id).limit(8)
else
#recipes = Recipe.limit(8)
end
end

Related

Access JSON field of join table with Active Record

One shop has_many products and a product belongs_to a shop.
The shop has opening_hours and closing_hours columns. Each column stores a hash which looks like the following:
opening_hours = {'Monday' => '12:00', 'Tuesday' => '13:00'}
and so on
I am working with current date and time, and I would like to retrieve all products which stores have opening_hours smaller than current time which is smaller than closing_hours.
Basically all the products that are available when the shop is open.
I did something like that in my controller:
day_today = Date.today.strftime('%A')
time_now = Time.new.strftime('%H:%M')
#products = Product.joins(shop: [{opening_hours[day_today] < time_now,
{closing_hours[day_today] > time_now }])
Edit
This is the code that stores the opening times in the DB
hours_table = shop.search('table')
opening_hours = {}
closing_hours = {}
hours_table.first.search('tr').each do |tr|
cells = tr.search('td')
day = cells.first.text.strip
hours = cells.last.text.strip.tr('-', '').split
opening_hours[day] = hours[0].to_time.strftime('%H:%M')
closing_hours[day] = hours[1].to_time.strftime('%H:%M')
end
shop = Shop.new(
name: shop_name,
opening_hours: opening_hours,
closing_hours: closing_hours
)
shop.save
new_product = Product.new(
name: foo,
description: foo,
price: foo,
company: 'foo',
)
new_product.shop = shop
new_product.save
This is the migration to add opening hours to Shop. The same is done for closing hours.
class AddOpeningHoursToShop < ActiveRecord::Migration[5.1]
def change
add_column :shops, :opening_hours, :json, default: {}
end
end
This is the migration to add the shop ID to products
class AddShopToProducts < ActiveRecord::Migration[5.1]
def change
add_reference :products, :shop, foreign_key: true
end
end
Any guess?
You can use the ->> operator to access a specific JSON field as text:
SELECT opening_hours->>'Monday' AS monday_opening_hour FROM shops;
Same for closing_hours, it just changes the name of the column and field.
So the AR representation in your case would be like:
Product
.joins(:shop)
.where(
"shops.opening_hours->>:day <= :now AND shops.closing_hours->>:day >= :now",
day: day_today,
now: time_now
)
You can easily bind the key name and the time you're going to ask for, here as day and now.
Notice, it's not needed to explicitly prepend the table name, but I'm doing so just for the sake of clarity.
This can also be done with the BETWEEN operator:
Product
.joins(:shop)
.where(
":now BETWEEN shops.opening_hours->>:day AND shops.closing_hours->>:day",
day: day_today,
now: time_now
)
You can use the one you think is more clear.
In any case, you can get rid of storing the time_now variable, by using the NOW function of PostgreSQL. It'll handle the time format difference if you cast it to text (NOW()::text):
Product
.joins(:shop)
.where(
"NOW()::text BETWEEN shops.opening_hours->>:day AND shops.closing_hours->>:day",
day: day_today
)
Similarly, the day can also be obtained from NOW. You can use to_char(NOW(), 'Day') for that
SELECT to_char(date, 'Day') AS day
FROM generate_series (NOW() - '6 days'::interval, NOW(), '1 day') date;
day
-----------
Thursday
Friday
Saturday
Sunday
Monday
Tuesday
Wednesday
So:
Product
.joins(:shop)
.where("NOW()::text BETWEEN shops.opening_hours->>(to_char(NOW(), 'Day'))
AND shops.closing_hours->>(to_char(NOW(), 'Day'))")

Rails: Fetch feed entries from certain date with Feedjira

How's it going SO'ers?
I've appended a Recent News section to the top of Users Dashboard, where it fetches news stories through Feedjira, saves the NewsEntry to the database (postgres) and then picks three random NewsEntries to display in the Dashboard#index view.
I've also created a rake task NewsEntry.update_from_feed(feed_url) to update the table with new NewsEntries scheduled to run once every 24 hours.
My problem, because it's a Recent News section I would like to fetch the NewsEntries that were published today (or yesterday; this could be a time as well). Eventually I'll add to the rake task to remove old articles and update the feed with newer articles. Right now I am getting an error trying to fetch entries on a certain date:
NoMethodError: undefined method `where' for #<Array:0x00007fcab2910d00>
NewsEntry
class NewsEntry < ApplicationRecord
def self.update_from_feed(feed_url)
feed = Feedjira::Feed.fetch_and_parse(feed_url)
add_entries(feed.entries)
end
private
def self.add_entries(entries)
today = Date.today
entries.where("published_at = ?", today) do |entry|
unless exists? :guid => entry.id
create!(
:title => entry.title,
:url => entry.url,
:published_at => entry.published,
:guid => entry.id
)
end
end
end
end
Thanks in advance for any help!

ruby check if current date is within date records

Let's say I have a model called Absence that looks something like this
:id => :integer,
:employee_id => :integer,
:start_date => :date,
:end_date => :date
I need to check if an Employee is away today, and return true if they are. Someone is away if they have an absence record that
Has a start date is today or before today,
Has an end date that is either null, or today or ahead of today.
so I need a method on the Employee that is something like
def is_away
?????
end
please help!
I would do something like this:
# add this to absence.rb
def covers_today?
(start_date..end_date).cover?(Date.today)
end
# add this to employee.rb
def away?
absences.any?(&:covers_today?)
end
After doing this just call away? on an employee:
#employee.away?
# => true, if employee has an absense that covers the current date
Assuming that Employee has_many :absences, this should work:
def away?(date = Date.today)
absences.where('start_date <= :date AND (end_date >= :date OR end_date IS NULL)', date: date).exists?
end
You can try this too.
def is_away?
(start_date <= Date.today) and (end_date.nil? or end_date <= Date.today) ? true : false
end

How to check if value unique for a certain period

I have a Ratings model with a phone_id attribute. Before creating a new ratings object I want to check if the phone_id is unique for the past week.
In my model I want to do something like this in the before_save callback:
self.all(:conditions => {:created_at => (1.week.ago..Date.today)}).include? self.phone_id
I would do it in clean sql (performance)
select count(phone_id) from ratings where created_at < DATE_SUB(NOW(), INTERVAL 1 MONTH)
You can use ActiveRecord validations with a constraint.
class Rating < ActiveRecord::Base
validates_uniqueness_of :phone_id, conditions: -> { where(:created_at => (1.week.ago..Date.today)) }
end
You can use validation on create:
class Rating < ActiveRecord::Base
validate :unique_phone_within_last_week?, on: :create
private
def unique_phone_within_last_week?
self.class.where("created_at > ?", 1.week.ago.to_date)
.where(phone_id: phone_id).empty? ||
errors.add(:phone_id, 'is not unique within last week') && false
end
end
In Rails 4 you can use validates_uniqueness_of with conditions proc:
class Rating < ActiveRecord::Base
validates_uniqueness_of :phone_id,
conditions: -> { where('created_at > ?', 1.week.ago.to_date) }
end
Read more about validates_uniqueness_of.
Checking SQL will be optimal:
SELECT COUNT(*) FROM "ratings"
WHERE "ratings"."phone_id" = 2 AND ("created_at" > '2014-01-14');
Note also that
Using interval 1.week.ago..Date.today is seems to be bad idea,
because records that were created today (in day of checking) are out of scope.
'2014-01-21 09:10:21' BETWEEN '2014-01-14 11:23:30' AND '2014-01-21' is false

How to scope last week by Date object [duplicate]

This question already has an answer here:
Closed 11 years ago.
Possible Duplicate:
Scoping date attribute for this week?
I am trying to scope all of my Products for this week, so it should show all the products leading up to whichever day of the week.
class Product < ActiveRecord::Base
attr_accessible :purchase_date
def self.last_week # All prices of last week.
where(:purchase_date => 1.week.ago)
end
create_table :products do |t|
t.date :purchase_date
end
end
This code renders nothing in the view though so what do I need to correct?
ANSWER
For some reason I had to add advance(:days => -1) to in order to also retrieve Monday as well. You may not have to do this though.
def self.last_week
where(:purchase_date => 1.week.ago.beginning_of_week.advance(:days => -1)..1.week.ago.end_of_week).order("purchase_date desc")
end
UPDATED ANSWER
I had to do the advance(:days => -1) because of the Time zone I am in. I got rid of this by making sure I'm in my own Time zone. So now it can be normal as it should be:
def self.last_week
where(:purchase_date => 1.week.ago.beginning_of_week..1.week.ago.end_of_week)
end
And it should work correctly ONLY if you go by the default Rails Time zone or you config your own:
app/config/environment/development.rb
config.time_zone = "Eastern Time (US & Canada)"
Good luck.
This should do the trick:
scope :last_week, lambda { where("purchase_date >= :date", :date => 1.week.ago) }
scope :past_week, lambda { where("purchase_date >= :start_date AND purchase_date <= :end_date", {:start_date => 1.week.ago, :end_date => 1.day.ago }) }

Resources