Find cards with today date when user receive mails in RoR - sql

Every day I need to send letters to users with today's tasks.
For do this I need to find all users who are allowed to send letters, and among these users to find all cards that have a deadline today. The result is three array elements with a nil value. How is this better done and right?
#users = User.all {|a| a.receive_emails true}
#user_cards = []
#users.each_with_index do |user, index|
#user_cards[index] = user.cards.where(start_date: Date.today).find_each do |card|
#user_cards[index] = card
end
end
My user model:
class Card < ApplicationRecord
belongs_to :user
# also has t.date "start_date"
end
My card model:
class User < ApplicationRecord
has_many :cards, dependent: :destroy
# also has t.boolean "receive_emails", default: false
end

Something like #cards_to_send = Card.joins(:users).where("users.receive_emails = true").where(start_date: Date.today)
Have a look at https://guides.rubyonrails.org/active_record_querying.html#specifying-conditions-on-the-joined-tables for the docs on how to query on a joined table.

You could do this with a SQL join like this
User.joins(:cards).where(receive_emails: true, cards: { start_date: Date.today })
https://guides.rubyonrails.org/active_record_querying.html#joining-tables

Related

Rails admin gem, polymorphic association

How to get the queried data having polymorphic association
I have an 3 models
class Picture < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Employee < ApplicationRecord
has_many :pictures, as: :imageable
end
class Product < ApplicationRecord
has_many :pictures, as: :imageable
end```
Employee and Product have column **is_active**.
In rails admin dropdown. I want to display the data where employee and product is **is_active = true.**
Have tried multiple ways to achieve this.
Please let me know if there is any solution?
You can write a custom scope in picture model as below
scope :list_active(imageable_type), -> {
where(imageable_type: imageable_type)
.joins("INNER JOIN #{imageable_type.pluralize} ON
{#imageable_type.pluralize}.id = imageable_id AND
imageable_type = '#{imageable_type}'")
.where('#{imageable_type.pluralize}.is_active = ?', true)
}
Then you can simply list and use the response.
E.g result = []
result << Image.list_active('Employee')
result << Image.list_active('Product')

Rails ActiveRecord querying

So I have the following three models: Assignment.rb, Submission.rb, User.rb
And here are the relationships:
class Assignment
has_many :submissions
end
class Submission
belongs_to :assignment
belongs_to :user
# submission has a boolean column called submitted with val true or false
end
class User
has_many submissions
end
I want to know how can I query the assignments that a user has not submitted (in a clean way)? If a user submits an assignment, a new submission for that assignment and user will be created.
Not sure if I provided enough info for anyone to answer, so please comment if anything else is needed.Thx!
The logic that #Norly Canarias is using is correct, but I would alter it to use methods on the User class, and I would also modify it to make it database-agnostic (for example, using 'submissions.submitted = true' will not work at all in Postgres).
class User < ApplicationRecord
has_many :submissions
has_many :assignments, through: :submissions
def submitted_assignments
assignments.where(submissions: {submitted: true})
end
def unsubmitted_assignments
Assignment.where.not(id: submitted_assignments)
end
end
I have tested this and it works as expected. For a user who has a Submission for Assignment 1 with submitted == true, and who has a Submission for Assignment 2 with submitted == false, and assuming there are two more Assignments (3 and 4) for which no Submission exists, you will get:
>> user.submitted_assignments.ids
#=>[1]
>> user.unsubmitted_assignments.ids
#=>[2, 3, 4]
I think something like this could work (I haven't tested though):
class Assignment
has_many :submissions
end
class Submission
belongs_to :assignment
belongs_to :user
end
class User
has_many :submissions
has_many :assignments, through: :submissions
end
user = User.first
submitted = user.assignments.where('submissions.submitted = true')
not_submitted = Assignment.where.not(id: submitted)
You can also make it a scope
class Assignment
has_many :submissions
scope :not_submitted_by_user, ->(user) do
where.not(id: user.assignments.where('submissions.submitted = true'))
end
end
user = User.first
not_submitted = Assignment.not_submitted_by_user(user)
To get all the Assignments that are not from a specific user
#assignments = Assignment.where.not(user_id: user_id)
A clean way to do it is to create a scope in the Assignment Model
class Assignment
has_many :submissions
scope :not_from_user, ->(user_id) {where.not(user_id: user_id) }
end
And then calling
#assignments = Assignment.not_from_user 1

How to rewrite Rails associations without separate queries (looping through them again)

Running into some performance issues with the following code (stripped out irrelevant parts).
This is the CardsController#index code:
def index
cards = cards.paginate(page: index_params[:page], per_page: limit)
# Assign bumped attribute
cards.each do |card|
if current_user
card.bumped = card.bump_by?(current_user)
card.bump = card.get_bump(current_user)
else
card.bumped = false
card.bump = nil
end
end
end
Card.rb:
class Card < ActiveRecord::Base
belongs_to :cardable, polymorphic: true, touch: true
belongs_to :user
has_many :card_comments, autosave: true
has_many :card_bumps
has_many :card_bumpers, through: :card_bumps, class_name: 'User', source: :user
def bump_by?(user)
self.card_bumpers.include? user
end
def get_bump(user)
CardBump.find_by(user_id: user.id, card_id: self.id)
end
end
How can I avoid and optimize the second loop on each card where I do the associations of card.bumped and card.bump ?
Thanks in advance
In the model level, since the method bump_by? equals to bump existence, so
card.bumped = !card.bump.empty?
So the whole includes check in method bump_by? can be avoided, which in turn avoid fetching all associated bumps.
First of all, I would optimize your controller code a little and would update all cards with a single query if current_user is not present:
def index
cards = cards.paginate(page: index_params[:page], per_page: limit)
# Assign bumped attribute
if current_user
cards.each do |card|
card.bump = card.get_bump(current_user)
card.bumped = card.bump_by?(current_user)
end
else
cards.update_all(bump: nil, bumped: false)
end
end
There is also a possibility to optimize model code:
class Card < ActiveRecord::Base
belongs_to :cardable, polymorphic: true, touch: true
belongs_to :user
has_many :card_comments, autosave: true
has_many :card_bumps
has_many :card_bumpers, through: :card_bumps, class_name: 'User', source: :user
def bump_by?(user)
# use #exists? to check whether a record is present in the database.
# This will make a `SELECT 1 as count...` query and,
# therefore, perform a lookup on database level.
# #include? in opposite will load ALL associated items from DB,
# turn them into a ruby objects array and perform a lookup in the
# obtained array which is much slower than simple lookup performed by #exists?
get_bump(user) == user || self.card_bumpers.exists?(user.id)
end
def get_bump(user)
#_bump ||= self.card_bumpers.find_by(user_id: user.id)
end
end
Since Card#get_bump is also looking in card_bumpers association we can memorize its result and later use memorized value in Card#bump_by? without hitting database again. If there is no memorized value then fast check for record existence will be performed by a database.
Notice, that I changed lines order in controller to get benefit of memorizing:
card.bump = card.get_bump(current_user)
card.bumped = card.bump_by?(current_user)

Many to many relationship with ability to set a state (active)

I have a fully working (for some time now) many-to-many relationship in my Rails application.
Instructors has many Schools (through SchoolsToInstructorsAssociations)
Schools has many Instructors (through SchoolsToInstructorsAssociations)
At this time, I would like the ability to have an "active state" in addition to simply adding or removing an Instructor from a School or a School from an Instructor.
I want an Instructor to be set as inactive before being removed completely at a later point (or reactivated).
My first thought was to add an 'active' boolean to the relationship model (SchoolsToInstructorsAssociations), but there's no simple way to access this attribute to update or query it).
My second thought was to simply create another relationship model with the 'active' attribute, but it's redundant and something extra I have to track.
Maybe a custom many-to-many module? Create a SchoolsToInstructorsAssociations controller?
class Instructor < ActiveRecord::Base
has_many :schools_to_instructors_association
has_many :schools, :through => :schools_to_instructors_association
end
class School < ActiveRecord::Base
has_many :schools_to_instructors_association
has_many :instructors, :through => :schools_to_instructors_association
end
class SchoolsToInstructorsAssociation < ActiveRecord::Base
belongs_to :user
belongs_to :school
end
I also plan to create a history record each time an instructors 'active' state changes or an instructor is removed or added to a school. Not asking how to do this, but wondering if it could be used to track an instructors 'active' state.
class SchoolsController < ApplicationController
def instructors_index
#school = School.find(params[:id])
instructors = find_instructors
#active_instructors = instructors[0]
#inactive_instructors = instructors[1]
respond_to do |format|
format.html # index.html.erb
format.json { render json: #schools }
end
end
private
def find_instructors
active = []; inactive = []
#school.instructors.each do |s|
if SchoolsToInstructorsAssociationRecord.where(user_id: s, school_id: #school)[0].active?
active << s
else
inactive << s
end
return [active, inactive]
end
end
end
class SchoolsToInstructorsAssociationRecord < ActiveRecord::Base
default_scope order('created_at DESC')
attr_accessor :user_id, :school_id, schools_to_instructors_association_id, :active
end
Sounds like you can accomplish what you're trying to do with scopes. Add a boolean column for 'active' as you described for the 'Instructor' class, then you can add scopes for it:
class Instructor < ActiveRecord::Base
...
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
...
end
Then for a given School, you can get the active (or inactive) instructors for that school:
#school.instructors.active
=> SELECT "instructors".* FROM "instructors" WHERE "instructors"."school_id" = <id> AND "instructors"."active" = 't'
If you wanted to do some operations on all the inactive instructors (like destroy them, as an example), you could do:
Instructor.inactive.map(&:destroy)
And you can of course write whatever custom methods you want for the Instructor or School classes.

ActiveRecord query all the first items of a unique has-many association

I am having problems to create a Rails ActiveRecord query that retrieves the first Item by unique Activity considering a creation time internal. I also need the values available in ItemStat that is why the includes.
The current method implementation is working, but it is poor and needs optimization.
This is my analogue model:
Activity:
class Activity < ActiveRecord::Base
has_many :items
end
Item:
class Item < ActiveRecord::Base
belongs_to :activity
has_one :item_stat
end
ItemStat:
class ItemStat < ActiveRecord::Base
belongs_to :item
end
Current working method (activities_id are all activities available by an user):
def self.first_items_by_unique_activity(activities_id, time_begin, time_end)
items = Item.includes(:item_stat).where(:activity_id => activities_id, :created_at => time_begin..time_end)
#make the first item unique by activity
uniques = {}
items.each do |item|
identifier = item.activity_id
uniques[identifier] = item if uniques[identifier].nil?
end
uniques.values
end
Thanks any help!