Record Expiration - ruby-on-rails

My Rails app allows administrators to give other players infractions, which are stored in a database table. These infractions have point values that add up to give the player a points value. However, these records will contain an expiry time stamp. Is there any way of automatically deleting the infraction record when the expiry date has been reached?

How about using a default scope that filters out expired records? Something like
class Infraction < ActiveRecord::Base
default_scope -> { where("expired_at IS NULL OR expired_at < ?", Time.now) }
before_create :set_expired_at
private
def set_expired_at
self.expired_at = Time.now + 1.week
end
end
This way, you can do Infraction.find(4), and if the infraction with id == 4 is expired, it won't be returned.

Related

ActiveRecord: Get all users who should be banned today

I have two models:
class User < ActiveRecord::Base
has_many :ban_messages, dependent: :destroy
end
class BanMessage < ActiveRecord::Base
belongs_to :user
end
Table BanMessage contains a field t.datetime :ban_date that stores the date, when user should be banned. User table contains a field status, that contains one of the status values (active, suspended, banned). When I send BanMessage to User, his status field changes from 'active' to 'suspended'. Also I can activate user back, so he would not be banned at ':ban_date', but his BanMessage wouldn't be destroyed.
So I need to create query for selecting all Users with status 'suspended', who have in their newest BanMessages records 'ban_date' field, with DateTime value, which is between 'DateTime.now.beginning_of_day' and 'DateTime.now.end_of_day'.
You can filter your users with where query followed by join method for associated ban_messages with where method to further filter with the date range.
User.where(status: 'suspended').joins(:ban_messages).where("ban_date >= ? AND ban_date <= ?", DateTime.now.beginning_of_day, DateTime.now.end_of_day )
Try something like this.
User.joins(:ban_messages).where(status: :suspended).where('ban_messages.ban_date BETWEEN :begin_time AND :end_date', begin_time: DateTime.now.beginning_of_day, end_time: DateTime.now.end_of_day)
Here's what I needed:
User.joins(:ban_messages)
.where('users.status = ?', :suspended)
.where('ban_messages.created_at = (SELECT MAX(ban_messages.created_at) FROM ban_messages WHERE ban_messages.user_id = users.id)')
.where('ban_messages.ban_date BETWEEN ? and ?', DateTime.now.beginning_of_day, DateTime.now.end_of_day)
.group('users.id')
UPDATE
MrYoshiji: The caveat in your code is that if you miss one day, then
the next day you won't see the people that should have been banned the
day before. You might want to get all User where the ban_date is
lesser than DateTime.now.end_of_day, either in the same view or
another one.
So final query might be something like this
User.joins(:ban_messages)
.where('users.status = ?', :suspended)
.where('ban_messages.created_at = (SELECT MAX(ban_messages.created_at) FROM ban_messages WHERE ban_messages.user_id = users.id)')
.where('ban_messages.ban_date < ?', DateTime.now.end_of_day)
.group('users.id')

How can I check if a new event (start time - endtime) doesn't overlap with previous startime - endtimes (Ruby)

I am making an appointment, scheduling API. I have many starttime and endtime pairings in DateTime format. I need to be sure that when I create a new appointment that times do not overlap with previous ones. What I mean is that if I have an appointment starting at 7/4/15 9:00 and ending at 7/4/15 13:00 I want to make a validation so that I can't make a new appintment starting at 7/4/15 10:00 ending at 7/4/15 12:00. I want to compare all the key value pairs to make sure the new one doesn't fall inside that range. Any ideas how I can do this?
An overlap happens when you have an a appointment that starts before this appointment ends, and ends after this one starts. If there are no appointments that fit that criteria, there are no overlaps.
Note that you need to also consider the special case that searching for appointments will find one overlap but it's the appointment you're currently editing, so you can ignore that one.
class Appointment < ActiveRecord::Base
validate :no_overlapping_appointments
def no_overlapping_appointments
overlaps = Appointment.where('start_time <= ? AND end_time >= ?', end_time, start_time)
return if overlaps.empty?
return if overlaps.count == 1 && overlaps.first.id == id
errors.add(:start_time, "This appointment overlaps others")
end
end
class Appointment < ActiveRecord::Base
validate :duration_not_overlap
private
def duration_not_overlap
verify_time(:starttime)
verify_time(:endtime)
end
# attr is :starttime | :endtime
def verify_time(attr)
errors[attr] << 'overlap' if Appointment.where(user_id: user_id, attr => (starttime..endtime)).exists?
end
end

What's the most efficient way to keep track of an accumulated lifetime sales figure?

So I have a Vendor model, and a Sale model. An entry is made in my Sale model whenever an order is placed via a vendor.
On my vendor model, I have 3 cache columns. sales_today, sales_this_week, and sales_lifetime.
For the first two, I calculated it something like this:
def update_sales_today
today = Date.today.beginning_of_day
sales_today = Sale.where("created_at >= ?", today).find_all_by_vendor_id(self.id)
self.sales_today = 0
sales_today.each do |s|
self.sales_today = self.sales_today + s.amount
end
self.save
end
So that resets that value everytime it is accessed and re-calculates it based on the most current records.
The weekly one is similar but I use a range of dates instead of today.
But...I am not quite sure how to do Lifetime data.
I don't want to clear out my value and have to sum all the Sale.amount for all the sales records for my vendor, every single time I update this record. That's why I am even implementing a cache in the first place.
What's the best way to approach this, from a performance perspective?
I might use ActiveRecord's sum method in this case (docs). All in one:
today = Date.today
vendor_sales = Sale.where(:vendor_id => self.id)
self.sales_today = vendor_sales.
where("created_at >= ?", today.beginning_of_day).
sum("amount")
self.sales_this_week = vendor_sales.
where("created_at >= ?", today.beginning_of_week).
sum("amount")
self.sales_lifetime = vendor_sales.sum("amount")
This would mean you wouldn't have to load lots of sales objects in memory to add the amounts.
You can use callbacks on the create and destroy events for your Sales model:
class SalesController < ApplicationController
after_save :increment_vendor_lifetime_sales
before_destroy :decrement_vendor_lifetime_sales
def increment_vendor_lifetime_sales
vendor.update_attribute :sales_lifetime, vendor.sales_lifetime + amount
end
def decrement_vendor_lifetime_sales
vendor.update_attribute :sales_lifetime, vendor.sales_lifetime - amount
end
end

Rails calculate and set a field value in the controller

I have an event table that contains start_at and ends_at. It also have a boolean for allday.
If the event is allday, then the user enters a field called hours. But, if they enter the start_at and ends_at, I'ld like to calculate and save in the hours field.
In English (not Ruby):
If event.allday = > true
hours = ends_at - starts_at
This way, I wouldn't have to calculate it during all the different display pages I have and it would be easier to sum.
OR - would this logic go into the model?
Thanks!
UPDATE-----------
I tried putting this into the model:
before_save :calculate_hours
def calculate_hours
if self.allday != true
self.hours = ((self.ends_at - self.starts_at)/ 3600).to_f.round(2)
end
end
Business logic always belongs in the model.
class Event < ActiveRecord::Base
before_validation :set_hours
private
def set_hours
if allday?
self.hours = ends_at - starts_at
end
end
end

Rails - How to check what the record count of a model 24 hours ago

I want to display the rate of change in the total number of records for a certain model. So I need to know, at any given time, how many there were 24 hours ago, and how many there are now.
It is very likely that a record will be deleted by users, so I can't just use the value of existing records that are older than 24 hours.
Is there a simple way to do this?
Thanks
class Item < ActiveRecord::Base
scope :since, lambda {|time| where("created_at > ?", time) }
scope :during_last, lambda {|time| where("created_at > ?", (Time.now - time)) }
end
Item.since(Time.now - 24.hours).count
Item.since(24.hours.ago).count
Item.during_last(24.hours).count
Well, you could do a count against the column created_at.
def count_rate(model, time=DateTime.now)
current_count = model.count(:conditions => ["created_at < ?", time])
last_24_hours_count = model.count(:conditions => ["created_at < ?", time-24.hours])
current_count - last_24_hours_count
end
This method count the record at a given compared with the last 24 hours.
Well, in case the user has deleted those records back, you would should create another table Rate(date, hour, count) and record every hour by using observer (only after_create).
def after_create
rate = Rate.find_or_initialize_by(Date.today.to_s, Time.now.hour)
rate.increment(:count)
rate.save!
end
In this observer, if it finds the existing record by date and hour, it simply increment the count column. You add the default value to zero on count column as well. Later on, you can query the data from that table by date and hour.

Resources