How to determine the order from a controller - ruby-on-rails

I have a restaurant with many employees and each employee has many customer ratings.
I want to create a stats page that shows the employees ordered by their monthly ratings average.
In the employee model:
def avg_rating
date = Date.today
ratings_total = self.ratings.sum(:score, :conditions => {:created_at => (date.beginning_of_month..date.end_of_month)}).to_f
ratings_count = self.ratings.count(:conditions => {:created_at => (date.beginning_of_month..date.end_of_month)}).to_f
return (ratings_total/ ratings_count)
end
In the restaurant controller I have:
def restaurant_stats
#restaurant = Restaurant.find(params[:restaurant_id])
#employees = #restaurant.employees.all
end
In the restaurant stats view:
<table>
<% #employees.each do |employee| %>
<tr>
<td><%= employee.name %></td>
<td><%= employee.avg_rating %></td>
</tr>
<% end %>
</table>
I'm not sure how to get the employees in the correct order? I assume I would have to retrieve the values in the correct order in the restaurant_stats action instead of just #restaurant.employees.all but I'm not sure how to because of the functions used in the employees model

You could do, from the controller:
#employees = #restaurant.employees.all.sort_by {|employee| employee.avg_rating}
or more concisely
#employees = #restaurant.employees.all.sort_by(&:avg_rating)
Note that this will load all employees in memory for sorting.

Try in the restaurant controller:
#employees = #restaurant.employees.all.sort {|x,y| y.avg_rating <=> x.avg_rating }
or
#employees = #restaurant.employees.all.sort_by(:avg_rating)

you could create an array and sort it
I think below works but haven't checked it
#employees = #restaurant.employees.collect {|p| [ p.name, p.avg_rating ]}
#employees.sort!{ |a,b| (a[1] <=> b[1]) }

Related

How to show loop data from orders only having created_at != updated_at?

I want to show the response time, which I have set up but it currently shows the response time with all orders into account, so it's not accurate.
For example, there are 3 orders by buyers and a seller takes 2 days to respond to 1 of those orders, i want it to say 2 days. Instead of taking all 3 orders into account and saying .66 days.
Here's the controller for the page its displayed on:
#orders = Order.all.where(seller: current_user).paginate(:page => params[:page], :per_page => 15) || #orders = Order.all.where(seller: current_buyer).paginate(:page => params[:page], :per_page => 15)
#orders_a = Order.all.where(seller: current_user) || #orders = Order.all.where(seller: current_buyer)
#orders_month = #orders_a.all.group_by { |mon| mon.created_at.beginning_of_month }
#orders_day = #orders_a.all.group_by { |day| day.created_at.beginning_of_day }
#order = Order.new
The #orders_month and #orders_day are the ones displaying the information. (It is both charted and graphed in a table)
Here's the view:
<% #orders_month.each do |month, orders| %>
<tbody>
<tr>
<td><%= month.strftime("%B") %></td>
<td class="center"><%= orders.count %></td>
<td class="center"><%= (orders.sum { |order| order.updated_at.day - order.created_at.day } / orders.count ) %> Days</td>
</td>
<% end %>
I have tried adding attempts like and similar to the following with no avail:
if order.updated_at != order.created_at

loop thru dates on group_by

I am trying to display a table(data coming from my DB), which count the occurrence of the data on that date.
I want to group the data by date and then loop thru each date and run the query. The problem is on the server logs I am just getting is an infinite loop of my query.
CACHE (0.0ms) SELECT COUNT(*) FROM `data` WHERE (NOT (name = ''))
CACHE (0.0ms) SELECT COUNT(*) FROM `data` WHERE (NOT (age = ''))
CACHE (0.0ms) SELECT COUNT(*) FROM `data` WHERE (NOT (address = ''))
controller:
def sum
date = Data.all
#sub_date = date.group_by(&:Submit_Date).map
end
Model:
def name_null
Data.where.not("name = ?", '').count
end
def age_null
Data.where.not("age = ?",'').count
end
def address_null
Data.where.not("address = ?", '').count
end
Views:
<tr>
<% #sub_date.each do |l, date|%>
<td><%=l%></td>
<% date.each do |ds| %>
<td><%= ds.name_null%></td>
<td><%= ds.age_null%></td>
<td><%= ds.address_null%></td>
<% end %>
<% end %>
</tr>
Pls do tell me where I got it wrong, currently studying rails. :)
Try below code:
controller
def sum
date = Data.all
#sub_date = date.group_by(&:submit_date)
end
Model
def name_null
Data.where.not("name = ?", nil).count
end
def age_null
Data.where.not("age = ?",nil).count
end
def address_null
Data.where.not("address = ?", nil).count
end
view
<% #sub_date.each do |l, date|%>
<tr>
<td><%=l%></td>
<% date.each do |ds| %>
<td><%= ds.name_null%></td>
<td><%= ds.age_null%></td>
<td><%= ds.address_null%></td>
<% end %>
</tr>
<% end %>

Ruby: How should I remove duplicate lines from an Array?

I may not be using the correct terminology but here goes..
I'm displaying IPS alerts on a dashboard app and there are many duplicate lines. For example, if one script kiddie is trying to brute force an RDP server, I could get 150 Alerts but could be slimmed down to about 5 because that's how many hosts they are going after. So I'm trying to remove the duplicate alerts, and I'm looking to use the sid, src_addr, and dst_addr as my metrics to determine if they are duplicates.
Currently I display #filtered_snort_detail_query using this code:
This is my view
<% if #filtered_snort_detail_query.count > 0 %>
<table>
<tr>
<th>Timestamp</th>
<th>Tag Info</th>
<th>Message</th>
</tr>
<% #filtered_snort_detail_query.each do |d|
text_msg = d['_source']['message']
if d['_source']['message'].nil?
end
%>
<tr>
<td class='timestamp'><%= d['_source']['#timestamp'].to_time %></td>
<td class='tags'><%= d['_source']['tags'] %></td>
<td class='message'><%= text_msg %></td>
</tr>
<% end %>
</table>
<% else %>
<div> No Results Returned. </div>
<% end %>
Here is my controller
if #es_snort_detail_query.count > 0
#filtered_snort_detail_query = Array.new
#es_snort_detail_query.each do |ips_detail|
next if ips_detail['_source']['type'] != 'snort-ips'
next if ips_detail['_source']['#timestamp'] < #ts
#filtered_snort_detail_query.push(ips_detail)
end
end
Here is what I think I need to do to get the metrics I need to compare lines in my controller.
I'm just not sure the best way to look at each line of #filtered_snort_detail_query and build a new array to display in my view using these parameters:
show me all lines, but not if sid_data, src_ip_data, and dst_ip_data happen two or more times.
if #es_snort_detail_query.count > 0
#filtered_snort_detail_query = Array.new
#es_snort_detail_query.each do |ips_detail|
next if ips_detail['_source']['type'] != 'snort-ips'
next if ips_detail['_source']['#timestamp'] < #ts
#filtered_snort_detail_query.push(ips_detail)
end
if #filtered_snort_detail_query.count > 0
ip_src = Array.new
ip_dst = Array.new
sid = Array.new
#filtered_snort_detail_query.each do |find_ip, find_sid|
unless find_ip.nil?
sid_data = find_sid.scan(/\[\d+\:\d+\:\d+\]/)
src_ip_data = find_ip.scan(/(?:[0-9]{1,3}\.){3}[0-9]{1,3}/)
dst_ip_data = find_ip.scan(/(?:[0-9]{1,3}\.){3}[0-9]{1,3}/)
sid.push(sid_data[0]) unless sid_data[0].nil?
ip_src.push(src_ip_data[0]) unless src_ip_data[0].nil?
ip_dst.push(dst_ip_data[1]) unless dst_ip_data[1].nil?
end
end
end
end
Sorry if I misunderstood the question.
If you have a bunch of objects in an array, and you want to remove duplicates based on a subset of their properties, you can use the uniq method with a block:
queries.uniq do |query|
[query.sid_data, query.src_ip_data, query.dst_ip_data]
end
It will compare the queries based on the array created in the block, and remove duplicates.
Go to http://www.compileonline.com/execute_ruby_online.php, copy and paste the code below, and click execute script in the top left.
queries = [
{ :a => "ab", :b => "ba" },
{ :a => "ab", :b => "xy" },
{ :a => "xy", :b => "xy" }
]
unique_queries = queries.uniq do |q|
q[:a]
end
puts unique_queries
See? The comparaison was done only based on the value of the :a key. That's the same principle.

Sum Identical Keys in a Ruby Hash

I have simple app that is meant to pull Adwords account metrics (via the adwordsapi) and present it to the user in a table inside a Rails view. It is working properly pulling down all info for multiple campaigns except for one issue..
I am unable to get the totals of each field (total cost, total impressions, etc.). I am able to serve impressions for each campaign within the account, but am unable to get the totals for the account.
I apologize ahead of time for the noob code to follow :)
Here is the adwordscampaign_controller.rb
class AdwordscampaignController < ApplicationController
PAGE_SIZE = 50
def index()
#selected_account = selected_account
if #selected_account
response = request_campaigns_list()
if response
#campaigns = Adwordscampaign.get_campaigns_list(response)
#campaign_count = response[:total_num_entries]
#start = params[:start]
#end = params[:end]
#myhash = Adwordscampaign.get_campaigns_list(response)
end
end
end
private
def request_campaigns_list()
# Prepare start and end date for the last week.
if params[:start].nil?
start_date = DateTime.parse((Date.today - 7).to_s).strftime("%Y%m%d")
end_date = DateTime.parse((Date.today - 1).to_s).strftime("%Y%m%d")
else
start_date = params[:start]
end_date = params[:end]
end
api = get_adwords_api()
service = api.service(:CampaignService, get_api_version())
selector = {
:fields => ['Id', 'Name', 'Status', 'Cost', 'Impressions', 'Clicks', 'Ctr', 'Conversions', 'Amount'],
:ordering => [{:field => 'Id', :sort_order => 'ASCENDING'}],
:date_range => {:min => start_date, :max => end_date},
:paging => {:start_index => 0, :number_results => PAGE_SIZE}
}
result = nil
begin
result = service.get(selector)
rescue AdwordsApi::Errors::ApiException => e
logger.fatal("Exception occurred: %s\n%s" % [e.to_s, e.message])
flash.now[:alert] =
'API request failed with an error, see logs for details'
end
return result
end
end
the relevant model: adwordscampaign.rb
class Adwordscampaign
attr_reader :id
attr_reader :name
attr_reader :status
attr_reader :cost
attr_reader :impressions
attr_reader :clicks
attr_reader :ctr
attr_reader :costdecimal
attr_reader :costperconversiondecimal
def initialize(api_campaign)
#id = api_campaign[:id]
#name = api_campaign[:name]
#status = api_campaign[:status]
budget = api_campaign[:budget]
stats = api_campaign[:campaign_stats]
#cost = (stats[:cost][:micro_amount] / 10000)
#costdecimal = (#cost * 10000).round.to_f / 1000000
#impressions = stats[:impressions]
#clicks = stats[:clicks]
#ctr = (stats[:ctr] * 100)
end
def self.get_campaigns_list(response)
result = {}
if response[:entries]
response[:entries].each do |api_campaign|
campaign = Adwordscampaign.new(api_campaign)
result[campaign.id] = campaign
end
end
return result
end
end
The table from the views\adwordscampaign\index.html.erb
<table class="table table-striped table-bordered">
<tr>
<th>ID
<th>Name
<th>Status
<th>Impressions
<th>Clicks
<th>CTR
<th>Cost
<% #campaigns.each do |id, campaign| %>
<tr>
<td><%= campaign.id %></td>
<td><%= campaign.name %></td>
<td><%= campaign.status %></td>
<td><%= number_with_delimiter(campaign.impressions) %></td>
<td><%= number_with_delimiter(campaign.clicks) %></td>
<td><%= number_with_precision(campaign.ctr, precision: 2) %>%</td>
<td>$<%= number_with_delimiter(campaign.costdecimal) %></td>
<% end %>
<td><%= #campaigns %></td>
</table>
I threw #campaigns in the view (at the bottom) so I could see what the output of that was. The syntax is a little unfamiliar to me but it appears to be hashes nested in a hash (correct?)
output of #campaigns in the view
{109886905=>#<Adwordscampaign:0x4528ba0 #id=109879905, #name="Upholstery Cleaning", #status="ACTIVE", #cost=2702, #costdecimal=27.02, #impressions=824, #clicks=7, #ctr=0.8495145631067961>, 103480025=>#<Adwordscampaign:0x7028b28 #id=109880025, #name="Carpet Cleaning", #status="ACTIVE", #cost=16739, #costdecimal=167.39, #impressions=4457, #clicks=29, #ctr=0.6506618801884676>, 104560145=>#<Adwordscampaign:0x3e9ibac8 #id=109880145, #name="Competitors", #status="ACTIVE", #cost=1596, #costdecimal=15.96, #impressions=515, #clicks=5, #ctr=0.9708737864077669>
Finally to the question - How would I get the total #clicks (or #impressions, #cost, etc.) for each of the 3 campaigns found here?
I've been searching for things like "how to sum identical hash keys/values" or "how to merge nested hashes" to no avail.
Thanks in advance!
#campaigns looks like a simple hash of { id -> Adwordscampaign }. The #<ObjectName:data> notation is Ruby doing its best to give you something readable for that object.
To sum clicks, for example, is a simple map-reduce:
total_clicks = #campaigns.map { |id, campaign| campaign.clicks } .reduce(&:+)
This creates an array of all campaign.clicks values, then combines them all using the + operator.
This assumes there is a #clicks on those objects. If not, tweak accordingly.
If all you're doing is summing various attributes, you can simplify using it multiple times like so:
camp_list = #campaigns.map { |id, campaign| campaign }
total_clicks = camp_list.map(&:clicks).reduce(&:+)
total_cost = camp_list.map(&:cost).reduce(&:+)
total_impressions = camp_list.map(&:impressions).reduce(&:+)
Reducing it further is an exercise to the reader. :)

this query isn't efficient

i have a single search field that is querying three different columns from two different tables: "companies" and "industries" from a positions table and "schools" from an educations table. it is successfully returning all users that meet ALL fields entered into the search field (using select_tag). this is from my view:
<%= form_tag(search_path, :method => :get, :id => "people_search") do %>
<div class="row-fluid">
<div class="span4">
<table>
<tr>
<td>
<div class="search-table" style="padding-left:55px">
<%= select_tag "all", options_for_select((#visible_companies + #visible_industries + #visible_schools).uniq, params[:all]), { :placeholder => "Search by companies, industries and schools...", :multiple => true, :js => "if (evt.keyCode == 13) {form.submit();}" } %>
</div>
</td>
<td>
<%= submit_tag "Add", id: "send-button", style:"width:175px;" %>
</td>
</tr>
</table>
</div>
<% end %>
</div>
and controller:
#visible_positions = Position.where{ is_visible.eq('true') }
#visible_educations = Education.where{ is_visible.eq('true') }
#visible_companies = #visible_positions.order("LOWER(company)").map(&:company).uniq
#visible_industries = #visible_positions.order("LOWER(industry)").map(&:industry).uniq
#visible_schools = #visible_educations.order("LOWER(school)").map(&:school).uniq
#c = #visible_positions.where{company.in(my{params[:all]})}.map(&:user_id)
#i = #visible_positions.where{industry.in(my{params[:all]})}.map(&:user_id)
#s = #visible_educations.where{school.in(my{params[:all]})}.map(&:user_id)
#C = #visible_positions.where{company.in(my{params[:all]})}.map(&:company)
#I = #visible_positions.where{industry.in(my{params[:all]})}.map(&:industry)
#S = #visible_educations.where{school.in(my{params[:all]})}.map(&:school)
#blacklist = []
#cis = #c + #i + #s
#experiences = ([#C,#I,#S].reject(&:empty?).reduce(:&))
#cis.uniq.each do |user_id|
unless #C.empty?
#C.uniq.each do |company|
unless Position.find_all_by_company(company).map(&:user_id).include?(user_id) || Position.find_all_by_industry(company).map(&:user_id).include?(user_id) || Education.find_all_by_school(company).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
unless #I.empty?
#I.uniq.each do |industry|
unless Position.find_all_by_industry(industry).map(&:user_id).include?(user_id) || Position.find_all_by_company(industry).map(&:user_id).include?(user_id) || Education.find_all_by_school(industry).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
unless #S.empty?
#S.each do |school|
unless Education.find_all_by_school(school).map(&:user_id).include?(user_id) || Position.find_all_by_company(school).map(&:user_id).include?(user_id) || Position.find_all_by_industry(school).map(&:user_id).include?(user_id)
#blacklist << user_id
end
end
end
end
unless #c.empty? && #i.empty? && #s.empty?
#users = User.find(#cis - #blacklist)
end
the search looks like this (notice the single field), with a sample query included (notice the AND filter...i'm the only user in the database that fits all search terms ['dartmouth college' for school, 'world health organization' for company, 'internet' for industry]):
i realize this is not an efficient query and am thinking of ways to speed it up, but could use some ideas at this point.
happy turkey day :)
Based on your description rather then on understanding your code I figured out something like this
User.joins(:positions, :educations).where("lower(positions.company) like lower(?) and lower(positions.industry) like lower(?) and lower(educations.school) like lower(?) and positions.is_visible and educations.is_visible", "%#{company}%", "%#{industry}%", "%#{school}%")
or if there is only one company or industry in column
User.joins(:positions, :educations).where("(lower(positions.company) = lower(?) or lower(positions.industry) = lower(?)) and lower(educations.school) = lower(?) and positions.is_visible and educations.is_visible", company,industry, school)
But to put many industries, companies, schools as params will be more complicated
and create indexes
create index positions_lower_company on positions (lower(company));
create index positions_lower_industry on positions (lower(industry));
create index educations_lower_school on educations (lower(school));
I hope it will help somehow.

Resources