diff --git a/admin/app/admin/score_export.rb b/admin/app/admin/score_export.rb index d593f2b58..5e4802a0d 100644 --- a/admin/app/admin/score_export.rb +++ b/admin/app/admin/score_export.rb @@ -11,46 +11,80 @@ ActiveAdmin.register_page "Score Exports" do if start_time.blank? start_time = '1900-01-01' end - start_time = "DATE '#{start_time}'" + start_time = "#{start_time}" if end_time.blank? - end_time = 'NOW()' + end_time = Time.now + 1.days else - end_time = "DATE '#{end_time}'" + end_time = "#{end_time}" end - @users = User.find(:all) - Fetching all user details and assigned on @users + puts "start_time #{start_time}, end_time #{end_time}" + + scores = ScoreHistory + .select("from_city, from_regions.regionname as from_region_name, from_countries.countryname as from_country_name, from_isp, + to_city, to_regions.regionname as to_region_name, to_countries.countryname as to_country_name, to_isp, + min(score_histories.score) as min_latency, max(score_histories.score) as max_latency, avg(score_histories.score) as mean_latency, median(CAST(score_histories.score AS NUMERIC)) as median_latency, count(score_histories.score) as score_count") + .joins('LEFT JOIN countries AS from_countries ON from_countries.countrycode = from_country') + .joins('LEFT JOIN countries AS to_countries ON to_countries.countrycode = to_country') + .joins('LEFT JOIN regions AS from_regions ON from_regions.region = from_region') + .joins('LEFT JOIN regions AS to_regions ON to_regions.region = to_region') + .where("score_dt BETWEEN DATE '#{start_time}' AND DATE '#{end_time}'") + .order('from_city, from_regions.regionname, from_countries.countryname, from_isp, to_city, to_regions.regionname, to_countries.countryname, to_isp') + .group('from_city, from_regions.regionname, from_countries.countryname, from_isp, to_city, to_regions.regionname, to_countries.countryname, to_isp') + .limit(1_000_000) + + csv_string = CSV.generate do |csv| - Using CSV class generate method to create csv file - csv << ["Id", "Name", "Email","Role"] - Creating header of CSV file + csv << ["From Country", "From Region", "From City", "From ISP", "To Country", "To Region", "To City", "To ISP", "Min Latency", "Max Latency", "Median Latency", "Mean Latency", 'Score Count'] - @users.each do |user| - csv << [user.id, user.name, user.name, user.role] + scores.each do |score| + puts score.inspect + csv << [score.from_country_name, score.from_region_name, score.from_city, score.from_isp, + score.to_country_name, score.to_region_name, score.to_city, score.to_isp, + score[:min_latency], score[:max_latency], score[:median_latency], score[:mean_latency], score[:score_count]] end - Retrieving each rows and assigning on csv_string variable. + end - send_data csv_string, - :type => 'text/csv; charset=iso-8859-1; header=present', - :disposition => "attachment; -filename=users.csv" - - render 'hi' - # redirect_to admin_bootstrap_path, :notice => "Server created. If you start a job worker (bundle exec rake all_jobs in /web), it should update your icecast config." + send_data csv_string, + :type => 'text/csv; charset=iso-8859-1; header=present', + :disposition => "attachment; filename=score_export-#{start_time}-#{end_time}.csv" end content :title => "Score Exports" do - - semantic_form_for :score_exports, :url => admin_score_exports_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f| - f.inputs do - f.input :start, :as => :datepicker - f.input :end, :as => :datepicker + columns do + column do + semantic_form_for :score_exports, :url => admin_score_exports_create_csv_path, :builder => ActiveAdmin::FormBuilder do |f| + f.inputs do + f.input :start, :as => :datepicker + f.input :end, :as => :datepicker + end + f.actions do + f.action :submit, :label => 'Download CSV' + end + end + end + column do + panel "Usage" do + span "Select a start day, and end day to generate a CSV with a score summary." + end + panel "Limitation 1" do + div do + span do "The system limits the number of rows exported to 1,000,000" end + end + end + panel "Limitation 2" do + div do + span do "This report uses the score_histories table, which can lag up to 1 hour behind data. You can force a score_history sweep by going to" end + span do link_to "Resque", "#{Gon.global.prefix}resque/schedule" end + span do " and then running ScoreHistorySweeper. When the job count goes from 1 to 0, the score_histories table is now completely up-to-date, and you can make a 'fresh' CSV." end + end + end end - f.buttons end + #panel "Upaid Registrations" do # table_for Registration.unpaid.limit(10).order('created_at desc') do # column "Registration" do |registration| diff --git a/db/manifest b/db/manifest index 65fbf8688..e3db60d29 100755 --- a/db/manifest +++ b/db/manifest @@ -195,4 +195,5 @@ max_mind_releases.sql score_histories.sql update_sms_index.sql connection_allow_null_locidispid.sql -track_user_in_scores.sql \ No newline at end of file +track_user_in_scores.sql +median_aggregate.sql \ No newline at end of file diff --git a/db/up/median_aggregate.sql b/db/up/median_aggregate.sql new file mode 100644 index 000000000..63bf77d96 --- /dev/null +++ b/db/up/median_aggregate.sql @@ -0,0 +1,23 @@ +-- from here: https://wiki.postgresql.org/wiki/Aggregate_Median +CREATE OR REPLACE FUNCTION _final_median(numeric[]) + RETURNS numeric +AS +$body$ + SELECT AVG(val) + FROM ( + SELECT val + FROM unnest($1) val + ORDER BY 1 + LIMIT 2 - MOD(array_upper($1, 1), 2) + OFFSET CEIL(array_upper($1, 1) / 2.0) - 1 + ) sub; +$body$ +LANGUAGE sql ; +-- IMMUTABLE not accepted by pg migrate + +CREATE AGGREGATE median(numeric) ( + SFUNC=array_append, + STYPE=numeric[], + FINALFUNC=_final_median, + INITCOND='{}' +);