From af7f9c6a15a07d2ad1b2ea6a2fb3e93fa8086295 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 9 Oct 2014 01:01:03 +0400 Subject: [PATCH] #435: fetch data for BuildLists graph --- app/controllers/statistics_controller.rb | 67 +++++++++++++ app/helpers/statistics_helper.rb | 12 +-- app/models/statistic.rb | 93 +++++++++++++++---- app/views/statistics/_build_lists.html.haml | 2 +- app/views/statistics/index.json.jbuilder | 1 + config/locales/models/statistics.en.yml | 5 + .../20141006182907_create_statistics.rb | 12 +-- db/schema.rb | 12 +-- 8 files changed, 164 insertions(+), 40 deletions(-) create mode 100644 app/views/statistics/index.json.jbuilder diff --git a/app/controllers/statistics_controller.rb b/app/controllers/statistics_controller.rb index 298cd670d..142a5b4f0 100644 --- a/app/controllers/statistics_controller.rb +++ b/app/controllers/statistics_controller.rb @@ -1,7 +1,74 @@ class StatisticsController < ApplicationController + RANGES = [ + RANGE_TWENTY_FOUR_HOURS = 'twenty_four_hours', + RANGE_LAST_7_DAYS = 'last_7_days', + RANGE_LAST_30_DAYS = 'last_30_days', + RANGE_LAST_60_DAYS = 'last_60_days', + RANGE_LAST_90_DAYS = 'last_90_days', + RANGE_LAST_180_DAYS = 'last_180_days', + RANGE_LAST_YEAR = 'last_year', + RANGE_CUSTOM = 'custom', + ] + + before_filter :init_variables + def index # TODO + respond_to do |format| + format.html + format.json do + scope = Statistic.for_period(@range_start, @range_end) + @build_lists = scope.build_lists(@range_start, @range_end, @unit) + end + end + end + + private + + def init_variables + case params[:range] + when RANGE_TWENTY_FOUR_HOURS + @range_end = Time.now.utc + @range_start = @range_end - 1.day + @unit = :hour + when RANGE_LAST_7_DAYS + @range_end = Date.today + @range_start = @range_end - 7.days + @unit = :day + when RANGE_LAST_30_DAYS + @range_end = Date.today + @range_start = @range_end - 30.days + @unit = :day + when RANGE_LAST_60_DAYS + @range_end = Date.today + @range_start = @range_end - 30.days + @unit = :day + when RANGE_LAST_90_DAYS + @range_end = Date.today + @range_start = @range_end - 90.days + @unit = :day + when RANGE_LAST_180_DAYS + @range_end = Date.today + @range_start = @range_end - 180.days + @unit = :month + when RANGE_LAST_YEAR + @range_end = Date.today + @range_start = @range_end - 1.year + @unit = :month + when RANGE_CUSTOM + @range_start = Time.parse(params[:range_start]).utc + @range_end = Time.parse(params[:range_end]).utc + diff = @range_end - @range_start + @unit = + if diff <= 24.hours + :hour + elsif diff <= 90.days + :day + else + :month + end + end end end \ No newline at end of file diff --git a/app/helpers/statistics_helper.rb b/app/helpers/statistics_helper.rb index ce0ea92c8..d4737a240 100644 --- a/app/helpers/statistics_helper.rb +++ b/app/helpers/statistics_helper.rb @@ -1,18 +1,8 @@ module StatisticsHelper - RANGES = %w( - twenty_four_hours - last_7_days - last_30_days - last_60_days - last_90_days - last_180_days - last_year - custom - ) def statistics_range_options options_for_select( - RANGES.map { |r| [I18n.t(r, scope: 'statistics.helper.period'), r] } + StatisticsController::RANGES.map { |r| [I18n.t(r, scope: 'statistics.helper.period'), r] } ) end diff --git a/app/models/statistic.rb b/app/models/statistic.rb index 0a334368b..76c84a5a3 100644 --- a/app/models/statistic.rb +++ b/app/models/statistic.rb @@ -1,11 +1,9 @@ class Statistic < ActiveRecord::Base - # TYPES = %w() - belongs_to :user belongs_to :project validates :user_id, - uniqueness: { scope: [:project_id, :type, :activity_at] }, + uniqueness: { scope: [:project_id, :key, :activity_at] }, presence: true validates :email, @@ -17,7 +15,7 @@ class Statistic < ActiveRecord::Base validates :project_name_with_owner, presence: true - validates :type, + validates :key, presence: true validates :counter, @@ -30,30 +28,93 @@ class Statistic < ActiveRecord::Base :email, :project_id, :project_name_with_owner, - :type, + :key, :counter, :activity_at - def self.now_statsd_increment(options = {}) + scope :for_period, -> (start_date, end_date) { where(activity_at: (start_date..end_date)) } + + def self.build_lists(range_start, range_end, unit) + items = select("SUM(counter) as count, date_trunc('#{ unit }', activity_at) as activity_at"). + group("date_trunc('#{ unit }', activity_at)").order('activity_at') + + + build_started = items.where(key: "build_list.#{BuildList::BUILD_STARTED}") + success = items.where(key: "build_list.#{BuildList::SUCCESS}") + build_error = items.where(key: "build_list.#{BuildList::BUILD_ERROR}") + build_published = items.where(key: "build_list.#{BuildList::BUILD_PUBLISHED}") + + { + build_started: prepare_collection(build_started, range_start, range_end, unit), + success: prepare_collection(success, range_start, range_end, unit), + build_error: prepare_collection(build_error, range_start, range_end, unit), + build_published: prepare_collection(build_published, range_start, range_end, unit), + } + end + + def self.prepare_collection(items, range_start, range_end, unit) + format = unit == :hour ? '%F %H:00:00' : '%F' + items = items.map do |item| + { x: item.activity_at.strftime(format), y: item.count } + end + if items.size == 0 + start = range_start + while start <= range_end + items.unshift({ x: start.strftime(format), y: 0 }) + start += 1.send(unit) + end + end + if items[0].try(:[], :x) != range_start.strftime(format) + items.unshift({ x: range_start.strftime(format), y: 0 }) + end + if items[-1].try(:[], :x) != range_end.strftime(format) + items << { x: range_end.strftime(format), y: 0 } + end + items + end + + protected + + def format + @format ||= case period.unit + when 'hour' + '%F %H:00:00' + else # 'day', 'month' + '%F' + end + end + + def self.now_statsd_increment(activity_at: nil, user_id: nil, project_id: nil, key: nil) # Truncates a DateTime to the minute - activity_at = options[:activity_at].utc.change(min: 0) - user = User.find options[:user_id] - project = Project.find options[:project_id] + activity_at = activity_at.utc.change(min: 0) + user = User.find user_id + project = Project.find project_id Statistic.create( - user: user, + user_id: user_id, email: user.email, - project: project, + project_id: project_id, project_name_with_owner: project.name_with_owner, - type: options[:type], + key: key, activity_at: activity_at ) ensure Statistic.where( - user_id: options[:user_id], - project_id: options[:project_id], - type: options[:type], + user_id: user_id, + project_id: project_id, + key: key, activity_at: activity_at - ).update_all('counter = counter + ?', options[:counter]) + ).update_all('counter = counter + 1') + end + + def self.fill_in + BuildList.find_each do |bl| + Statistic.now_statsd_increment({ + activity_at: bl.updated_at, + key: "build_list.#{bl.status}", + project_id: bl.project_id, + user_id: bl.user_id, + }) + end end def self.statsd_increment(options = {}) diff --git a/app/views/statistics/_build_lists.html.haml b/app/views/statistics/_build_lists.html.haml index 92eb1cfd0..789086f17 100644 --- a/app/views/statistics/_build_lists.html.haml +++ b/app/views/statistics/_build_lists.html.haml @@ -14,7 +14,7 @@ = image_tag 'loading-large.gif' .no-data{ ng_hide: 'loading || statistics.build_lists' } = t('.no_data') - %canvas#build_lists_chart ng-show='statistics.build_lists' + %canvas#build_lists_chart{ ng_show: 'statistics.build_lists' } .span3 .panel-wrapper diff --git a/app/views/statistics/index.json.jbuilder b/app/views/statistics/index.json.jbuilder new file mode 100644 index 000000000..f348e186a --- /dev/null +++ b/app/views/statistics/index.json.jbuilder @@ -0,0 +1 @@ +json.build_lists @build_lists diff --git a/config/locales/models/statistics.en.yml b/config/locales/models/statistics.en.yml index dca1e4f66..55b4c60e7 100644 --- a/config/locales/models/statistics.en.yml +++ b/config/locales/models/statistics.en.yml @@ -15,6 +15,11 @@ en: total_user_sessions: "Total user sessions" no_data: "No data available" + build_lists: + build_started_title: "Build started" + success_title: "Build complete" + build_error_title: "Build error" + build_published_title: "Build has been published" helper: period: diff --git a/db/migrate/20141006182907_create_statistics.rb b/db/migrate/20141006182907_create_statistics.rb index 4df439d71..cd134d78c 100644 --- a/db/migrate/20141006182907_create_statistics.rb +++ b/db/migrate/20141006182907_create_statistics.rb @@ -5,7 +5,7 @@ class CreateStatistics < ActiveRecord::Migration t.string :email, null: false t.integer :project_id, null: false t.string :project_name_with_owner, null: false - t.string :type, null: false + t.string :key, null: false t.integer :counter, null: false, default: 0 t.datetime :activity_at, null: false @@ -15,13 +15,13 @@ class CreateStatistics < ActiveRecord::Migration add_index :statistics, :user_id add_index :statistics, :project_id - add_index :statistics, :type - add_index :statistics, [:user_id, :type, :activity_at] - add_index :statistics, [:project_id, :type, :activity_at] - add_index :statistics, [:type, :activity_at] + add_index :statistics, :key + add_index :statistics, [:user_id, :key, :activity_at] + add_index :statistics, [:project_id, :key, :activity_at] + add_index :statistics, [:key, :activity_at] add_index :statistics, :activity_at - add_index :statistics, [:user_id, :project_id, :type, :activity_at], unique: true, + add_index :statistics, [:user_id, :project_id, :key, :activity_at], unique: true, name: 'index_statistics_on_all_keys' end diff --git a/db/schema.rb b/db/schema.rb index 78d7e3509..ad6657b54 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -586,18 +586,18 @@ ActiveRecord::Schema.define(version: 20141006182907) do t.string "email", null: false t.integer "project_id", null: false t.string "project_name_with_owner", null: false - t.string "type", null: false + t.string "key", null: false t.integer "counter", default: 0, null: false t.datetime "activity_at", null: false t.datetime "created_at" t.datetime "updated_at" t.index ["activity_at"], :name => "index_statistics_on_activity_at" - t.index ["project_id", "type", "activity_at"], :name => "index_statistics_on_project_id_and_type_and_activity_at" + t.index ["key", "activity_at"], :name => "index_statistics_on_key_and_activity_at" + t.index ["key"], :name => "index_statistics_on_key" + t.index ["project_id", "key", "activity_at"], :name => "index_statistics_on_project_id_and_key_and_activity_at" t.index ["project_id"], :name => "index_statistics_on_project_id" - t.index ["type", "activity_at"], :name => "index_statistics_on_type_and_activity_at" - t.index ["type"], :name => "index_statistics_on_type" - t.index ["user_id", "project_id", "type", "activity_at"], :name => "index_statistics_on_all_keys", :unique => true - t.index ["user_id", "type", "activity_at"], :name => "index_statistics_on_user_id_and_type_and_activity_at" + t.index ["user_id", "key", "activity_at"], :name => "index_statistics_on_user_id_and_key_and_activity_at" + t.index ["user_id", "project_id", "key", "activity_at"], :name => "index_statistics_on_all_keys", :unique => true t.index ["user_id"], :name => "index_statistics_on_user_id" end