Having problems building an Active Record query - ruby-on-rails

I have a small application which records all the minutes we spend on some projects at work.
I'm having troubles retrieving data from db.
My models:
WorkingDay
has_many :tasks
belongs_to :user
accepts_nested_attributes_for :tasks
Task
belongs_to :working_day
belongs_to :project
Project
belongs_to :user, required: false
belongs_to :company, required: false
has_many :tasks
Company
has_many :projects
User
belongs_to :role, required: false
has_many :projects
has_many :working_days
I'm exporting data into XLSX, following this tutorial and I would like to do two thing:
In the first sheet, I'd like to order the projects by name;
In the other sheets, I'd like to group projects by company.
This is my code snippet:
wb = xlsx_package.workbook
wb.add_worksheet(name: "All") do |sheet|
wd = WorkingDay.where(day_date: Date.strptime("2022-09-01", '%Y-%m-%d')..Date.strptime("2022-09-27", '%Y-%m-%d')).includes(:user).includes(tasks: :project)
ta = {}
wd.each do |wd|
wd.tasks.each do |t|
ms = 0
mw = 0
ta[t.project.id] ||= { brand: t.project.name, minutes_web: 0, minutes_social: 0 }
t.minutes_social ||= 0
ms += t.minutes_social
t.minutes_web ||= 0
mw += t.minutes_web
ta[t.project.id][:minutes_web] += mw
ta[t.project.id][:minutes_social] += ms
ta[t.project.id][:brand] = t.project.name
end
end
sheet.add_row ["Marchio", "Web", "Social"]
ta.each do |key, value|
sheet.add_row [ value[:brand], value[:minutes_web], value[:minutes_social]]
end
end
company = Company.all
company.each do |c|
wb.add_worksheet(name: "#{c.name}") do |sheet|
sheet.add_row ["Marchio", "Web", "Social", "TOTALE"]
end
end
The export does work and creates the right amount of sheets, but I don't know how to modify my query in order to get my data by company or ordered alphabetically.

Related

Querying in the controller- ROR

I am creating an application in Ruby and I have two model classes
class Restaurant < ActiveRecord::Base
attr_accessible :title, :cuisine, :price_range, :environment
self.primary_key = 'title'
has_many :environments
end
end
class Environment < ActiveRecord::Base
attr_accessible :title, :type
belongs_to :restaurants,foreign_key: 'title'
end
I am trying to query two tables in the controller- to return restaurants with specific environments.
#restaurants = Restaurant.joins('INNER JOIN environments ON restaurants.title=environments.title').where('environments.env_type'=> #selected_environment)
NOTE: #selected_environment is a hash contained list of environments. Environments table has a list of environment and restaurant pairs(there can be more than one restaurant).
I know the query above is not correct to achieve my goal. Is there a way i can get this done?
controller method:
def index
# can later be moved to the Restaurant model
ordering = {:title => :asc}
#all_environments = Restaurant.all_environments
#selected_environments = params[:environments] || session[:environments] || {}
if #selected_environments == {}
#selected_environments = Hash[#all_environments.map {|environment| [environment, environment]}]
end
if params[:environments] != session[:environments]
session[:environments] = #selected_environments
redirect_to :environments => #selected_environments and return
end
#restaurants = Restaurant.joins(:environments).where(environments:{env_type: #selected_environments.keys })
end
For your models you want to do this:
class Restaurant < ActiveRecord::Base
attr_accessible :title, :cuisine, :price_range, :environment
self.primary_key = 'title'
has_many :environments, :foreign_key => 'title', :primary_key => 'title'
end
class Environment < ActiveRecord::Base
attr_accessible :title, :type
belongs_to :restaurants, foreign_key: 'title'
end
Try structuring your query like this:
Restaurant.joins(:environments).where(environments: { env_type: #selected_environments.values })

Ordering elasticsearch results, elasticsearch-rails gem, Rails

Im started to use Elasticsearh in my project, and have problem with result ordering.
In fact I need to sort my records by hstore record in connected (belongs_to) model.
More details:
So, I have a Model that I want to be searchable. This model have connections with another models, here the code:
class PropertyObject < ActiveRecord::Base
belongs_to :country, :counter_cache => true
belongs_to :region, :counter_cache => true
belongs_to :city, :counter_cache => true
belongs_to :property_object_type, :counter_cache => true
belongs_to :property_object_state, :counter_cache => true
has_one :property_object_parameter_pack, dependent: :destroy
has_one :property_object_feature_pack, dependent: :destroy
has_one :property_object_description, dependent: :destroy
has_one :property_object_seo_field, dependent: :destroy
end
I want to include to my search results next fields:
Model PropertyObject:
:code :string
Model Country
:title_translations :hstore
Model Region
:title_translations :hstore
Model City
:title_translations :hstore
Model PropertyObjectDescription
:title_translations :hstore
:main_text_translations :hstore
Model PropertyObjectParameterPack
:price :hstore (example: {min => 10, max=>100})
To make this work I had create concern Searchable and add it to my model PropertyObject.
Here the code of it:
module Searchable
extend ActiveSupport::Concern
included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks
mapping do
indexes :property_object_parameter_pack, type: 'nested' do
indexes :price do
indexes :min, type: :integer
end
end
end
# Customize the JSON serialization for Elasticsearch
def as_indexed_json(options={})
self.as_json(
include: {
country: {only: :title_translations},
region: {only: :title_translations},
city: {only: :title_translations},
property_object_description: {only: [:title_translations, :main_text_translations]},
property_object_parameter_pack: {only: [:price, :area, :rooms]}
})
end
end
end
Controller part where search is calling
def search
pagen = params[:page] || 1
#property_objects = PropertyObject.search(params[:q]).page(pagen).records
end
So now searching working and all seems good. But I need sort results of search by min price.
I had try order method that works in my another orders - but no luck.
As I understand I need to use Elasticsearch sorting , to get result already sorted - but spend a lot of hours trying to implement this and fail.
What you can suggest me?
UPDATE
Had try this code:
pagen = params[:page] || 1
query = params[:q]
params[:order] ||= 'asc'
property_objects = PropertyObject.search(query) do |s|
s.query do |q|
q.string query
end
s.sort { by :property_object_parameter_pack.price.min, params[:sort]}
end
#property_objects = property_objects.page(pagen).records
With different variants
s.sort by
by :price
by :price.min
by :price[:min]
by :property_object_parameter_pack.price.min
by :property_object_parameter_pack.price[:min]
and no luck with ordering.
In the end I decide to understand how Elasticsearch works, and start to read Elasticsearch: The Definitive Guide - where the answer was founded.
First off all I recommend to read this guide and install Marvel
. After this all becomes much more clearer then using CURL. In fact I discover my index structure with Marvel, and just implement it to search query of elasticsearch-rails gem.
Next thing that I did - I had rebuild price from hstore to separate integer columns : like price_min and price_max.
So in short the answer code is:
sq = {
"query": {
"multi_match": {
"query": "Prague",
"fields": [ "country.title_translations.en",
"region.title_translations.en",
"city.title_translations.en",
"property_object_description.main_text_translations.en",
"property_object_description.title_translations.en"
]
}
},
"track_scores": true,
"sort": {
"property_object_parameter_pack.price_min":
{ "order": "desc",
"missing" : "_last"
}
}} PropertyObject.search (sq)
In fact Im sure that it will work with hstore. Because I store translations in hstore and it indexing fine - just need to point right field (in this task Marvel helps with autocomplete).
Did you try this?
def search
options = { :page => (params[:page] || 1) }
#property_objects = PropertyObject.search, options do |f|
f.query { string params[:q] }
f.sort { by :price, 'desc' }
end
#property_objects.records
end

missing attribute error Rails

I cant seem to get the result of a scope to display in my view as i get this error message.I am trying to get all of the memberships amounts for the day added up and displayed as a total
missing attribute: membership_id
My Models and scope
class Member < ActiveRecord::Base
belongs_to :membership
accepts_nested_attributes_for :membership
attr_accessible :membership_id, :forename, :middlename, :surname, :house_no, :house_name, :street, :town, :postcode, :home_tel, :mobile_tel, :work_tel, :email, :start_date, :expiry_date
scope :new_memberships_cash_today, ->() {
joins(:membership).where(:start_date => Date.today).select('ROUND(SUM(memberships.cost), 2)')
}
end
class Membership < ActiveRecord::Base
has_many :members, :dependent => :destroy
attr_accessible :membership_type, :cost
end
And then my view
columns do
#Total amount in £ for New Memberships today
column do
panel "Cash Today", :class => 'NewAmountMemberships' do
table_for Member.new_memberships_cash_today do
column 'Total cash' do |c|
c.membership.map { |e| [e.cost, e.id] }
end
end
end
end
end
After some reading it would seem that there may be an issue with my select call in the scope, as i need to specify all of the models attributes to make a successful call with Active Record?
As i am performing a sum within the select i am unsure how to add more attributes, if this is even the case
Any help appreciated
i have run the scope in the the console and this is what is returned
Member Load (0.1ms) SELECT ROUND(SUM(memberships.cost), 2) FROM `members` INNER JOIN `memberships` ON `memberships`.`id` = `members`.`membership_id` WHERE `members`.`start_date` = '2013-12-13'
=> #<ActiveRecord::Relation [#<Member id: nil>]>
I wouldn't do this in a scope, but in a helper method. That way you can grab the associated records and just call your method to return the total.
Something along the lines of this:
def membership_sum(memberships = [])
sum = 0
memberships.each { |membership| sum += membership.cost }
sum.round
end
Now, store the associated records in a #memberships variable (from within your controller) and then, in your view, use <%= membership_sum(#memberships) %>

Rails export csv including join table attributes

I have 3 models in my app + associated join tables:
Rating Set:
class RatingSet < ActiveRecord::Base
has_many :product_rating_sets, :dependent => :destroy
has_many :products, :through => :product_rating_sets
end
Product:
class Product < ActiveRecord::Base
has_many :product_recommendations, :dependent => :destroy
has_many :recommendations, :through => :product_recommendations
has_many :product_rating_sets, :dependent => :destroy
has_many :rating_sets, :through => :product_rating_sets
end
Recommendations:
class Recommendation < ActiveRecord::Base
has_many :product_recommendations, :dependent => :destroy
has_many :products, :through => :product_recommendations
end
I am trying to add an export to csv function.
My method so far:
def self.as_csv
CSV.generate do |csv|
csv << ["set_id", "time", "item_id", "related_item_id", "rating"]
set_id = ['1','2','3']
time = ['10:40am', "11:30pm", "14:40am"]
item_id = ["123456","123456765", "123456543"]
related_item_id = ["1234565432345","109876532", "564783920"]
rating = ["12.3", "13.2", "132.2"]
all.each do |item|
csv << item.attributes.values_at(*column_names)#lost here
end
end
end
I am planning on adding scoped to find set_id, time, item_id, related_item_id and rating, but for testing, I am using the values above. How to I iterate through each of those arrays, and added the values to the exported csv file under their respective headers?
Added the method below to a lib file, and call it in each of my controllers. Basically had to iterate through one model at a time, and after getting all the attributes I needed, I appended to the csv object.
def self.as_csv(rating_id)
CSV.generate do |csv|
csv << ["set_id","item_id", "related_item_id", "rating", "time"]
product_ids = RatingSet.find(rating_id).products
product_ids.each do |product|
recommendation_ids = Recommendation.find(:all, :joins => :products, :conditions => ["product_id = ?", product.id])
recommendation_ids.each do |recommendation|
recommendation.ratings.each do |ratings|
time_updated = (ratings.updated_at)
csv << [rating_id, product.id, recommendation.id, ratings.rating, time_updated]
end
end
end
end
end

Polymorphic habtm relationships with Rails/ActiveRecord

How would I go about creating a polymorphic has_and_belongs_to_many relationship with Rails/ActiveRecord?
Most of the examples I see involve creating a belongs_to relationship which limits my polymorphic-side to being related to only one parent:
Table: Task
Table: Tasks_Targets
Table: CustomerStore
Table: SoftwareSystem
Both CustomerStore and SoftwareSystem would be of type "Targetable" in this circumstance. From what I understand, if I implement the polymorphic relationship as most examples show, I'd only be able to relate a Targetable to a Task once.
Some clarification might help as most searches online still leave some of the theory behind this relationship unexplained...
Thanks!
Given your explanation of your domain, I've whipped up a small test-driven example of how you might solve your problem. If you see any domain inconsistencies, please feel free to clarify further (I'm using my acts_as_fu gem to whip up test models on the fly).
require 'acts_as_fu'
# class Task < ActiveRecord::Base
build_model(:tasks) do
integer :task_target_id
has_many :task_targets
has_many :customer_stores, :through => :task_targets, :source => :targetable, :source_type => 'CustomerStore'
has_many :software_systems, :through => :task_targets, :source => :targetable, :source_type => 'SoftwareSystem'
end
# class TaskTarget < ActiveRecord::Base
build_model(:task_targets) do
string :targetable_type
integer :targetable_id
integer :task_id
belongs_to :targetable, :polymorphic => true
belongs_to :task
end
# class CustomerStore < ActiveRecord::Base
build_model(:customer_stores) do
has_many :task_targets, :as => :targetable
has_many :tasks, :through => :task_targets
end
# class SoftwareSystem < ActiveRecord::Base
build_model(:software_systems) do
has_many :task_targets, :as => :targetable
has_many :tasks, :through => :task_targets
end
require 'test/unit'
class PolymorphicDomainTest < Test::Unit::TestCase
# Test that customer stores can have multiple tasks
def test_customer_store_gets_task
task = Task.create!
customer_store = CustomerStore.create!
customer_store.task_targets.create! :task => task
assert customer_store.tasks.include?(task)
end
def test_many_customer_stores_get_task
task_a = Task.create!
task_b = Task.create!
customer_store = CustomerStore.create! :tasks => [task_a, task_b]
assert customer_store.tasks.include?(task_a)
assert customer_store.tasks.include?(task_b)
end
# Test that software systems can have multiple tasks
def test_software_system_gets_task
task = Task.create!
software_system = SoftwareSystem.create!
software_system.task_targets.create! :task => task
assert software_system.tasks.include?(task)
end
def test_many_software_systems_get_task
task_a = Task.create!
task_b = Task.create!
software_system = SoftwareSystem.create! :tasks => [task_a, task_b]
assert software_system.tasks.include?(task_a)
assert software_system.tasks.include?(task_b)
end
# Test that Tasks can have multiple customer stores
def test_task_has_many_customer_stores
task = Task.create!
customer_store_a = CustomerStore.create!
customer_store_b = CustomerStore.create!
task.customer_stores = [customer_store_a, customer_store_b]
task.save!
task.reload
assert task.customer_stores.include?(customer_store_a)
assert task.customer_stores.include?(customer_store_b)
end
# Test that Tasks can have multiple software systems
def test_task_has_many_software_systems
task = Task.create!
software_system_a = SoftwareSystem.create!
software_system_b = SoftwareSystem.create!
task.software_systems = [software_system_a, software_system_b]
task.save!
task.reload
assert task.software_systems.include?(software_system_a)
assert task.software_systems.include?(software_system_b)
end
end
To complement nakajima's answer with regards to your concern, here's how I would do it:
class Task < ActiveRecord::Base
def targets
# Get Array of all targetables
tt = TaskTarget.select_all("SELECT targetable_type, targetable_id FROM task_targerts WHERE task_id = #{self[:id]}")
# Build Hash of targetable_type => Array of targetable_ids
targetables = Hash.new { |hash, key| hash[key] = [] }
tt.each do |targetable|
targetables[targetable.targetable_type] << targetable.targetable_id
end
# Query each "targetable" table once and merge all results
targetables.keys.map{|key| (eval key).find(targetables[key])}.flatten
end
end
Make sure to index task_id in table task_targets.

Resources