diff --git a/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee b/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee index b8e6bb5c4..07a3614f5 100644 --- a/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee +++ b/app/assets/javascripts/angularjs/controllers/statistics_controller.js.coffee @@ -35,9 +35,9 @@ RosaABF.controller 'StatisticsController', ['$scope', '$http', '$filter', ($scop range_end = new Date($scope.range_end) if range_start > range_end - tpm = $scope.range_start + tmp = $scope.range_start $scope.range_start = $scope.range_end - $scope.range_end = tpm + $scope.range_end = tmp $scope.rangeChange = -> diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 9327f4fc5..3f16fa64e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -6,3 +6,4 @@ @import "design/custom"; @import "design/build_lists_monitoring"; @import "design/profile"; +@import "design/statistics"; diff --git a/app/assets/stylesheets/design/statistics.scss b/app/assets/stylesheets/design/statistics.scss new file mode 100644 index 000000000..fa0973116 --- /dev/null +++ b/app/assets/stylesheets/design/statistics.scss @@ -0,0 +1,14 @@ +#manage-statistics { + .graph-key-color1 { background-color: #38849e; } + .graph-key-color2 { background-color: #4da944; } + .graph-key-color3 { background-color: #f18049; } + .graph-key-color4 { background-color: #aec7e8; } + .graph-key-color5 { background-color: #ffbb78; } + + .graph-wrapper h3 span { + display: inline-block; + width: 10px; + height: 10px; + margin-left: 15px; + } +} \ No newline at end of file diff --git a/app/controllers/statistics_controller.rb b/app/controllers/statistics_controller.rb index 142a5b4f0..f4cddaa66 100644 --- a/app/controllers/statistics_controller.rb +++ b/app/controllers/statistics_controller.rb @@ -14,12 +14,10 @@ class StatisticsController < ApplicationController 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) + render json: StatisticPresenter.new(range_start: @range_start, range_end: @range_end, unit: @unit) end end end diff --git a/app/models/concerns/build_list_observer.rb b/app/models/concerns/build_list_observer.rb index fa4ddece2..2ff664d5b 100644 --- a/app/models/concerns/build_list_observer.rb +++ b/app/models/concerns/build_list_observer.rb @@ -3,10 +3,20 @@ module BuildListObserver included do before_update :update_average_build_time + before_update :update_statistic end private + def update_statistic + Statistic.statsd_increment( + activity_at: Time.now, + key: "build_list.#{status}", + project_id: project_id, + user_id: user_id, + ) if status_changed? + end + def update_average_build_time if status_changed? self.started_at = Time.now if status == self.class::BUILD_STARTED diff --git a/app/models/statistic.rb b/app/models/statistic.rb index 76c84a5a3..0d876831a 100644 --- a/app/models/statistic.rb +++ b/app/models/statistic.rb @@ -32,57 +32,14 @@ class Statistic < ActiveRecord::Base :counter, :activity_at - scope :for_period, -> (start_date, end_date) { where(activity_at: (start_date..end_date)) } + 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') + scope :build_lists_started, -> { where(key: "build_list.#{BuildList::BUILD_STARTED}") } + scope :build_lists_success, -> { where(key: "build_list.#{BuildList::SUCCESS}") } + scope :build_lists_error, -> { where(key: "build_list.#{BuildList::BUILD_ERROR}") } + scope :build_lists_published, -> { where(key: "build_list.#{BuildList::BUILD_PUBLISHED}") } - 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 @@ -106,8 +63,15 @@ class Statistic < ActiveRecord::Base ).update_all('counter = counter + 1') end - def self.fill_in + # TODO: remove later + def self.fill_in_build_lists BuildList.find_each do |bl| + Statistic.now_statsd_increment({ + activity_at: bl.created_at, + key: "build_list.#{BuildList::BUILD_STARTED}", + project_id: bl.project_id, + user_id: bl.user_id, + }) Statistic.now_statsd_increment({ activity_at: bl.updated_at, key: "build_list.#{bl.status}", diff --git a/app/presenters/statistic_presenter.rb b/app/presenters/statistic_presenter.rb new file mode 100644 index 000000000..142fba0f5 --- /dev/null +++ b/app/presenters/statistic_presenter.rb @@ -0,0 +1,72 @@ +class StatisticPresenter < ApplicationPresenter + + attr_accessor :range_start, :range_end, :unit + + def initialize(range_start: nil, range_end: nil, unit: nil) + @range_start = range_start + @range_end = range_end + @unit = unit + end + + def as_json(options = nil) + { + build_lists: { + build_started: prepare_collection(build_lists_started), + success: prepare_collection(build_lists_success), + build_error: prepare_collection(build_lists_error), + build_published: prepare_collection(build_lists_published), + + build_started_count: build_lists_started.sum(&:count), + success_count: build_lists_success.sum(&:count), + build_error_count: build_lists_error.sum(&:count), + build_published_count: build_lists_published.sum(&:count), + } + + } + end + + private + + def scope + @scope ||= Statistic.for_period(range_start, range_end) + end + + def build_lists + @build_lists ||= scope. + select("SUM(counter) as count, date_trunc('#{ unit }', activity_at) as activity_at"). + group("date_trunc('#{ unit }', activity_at)").order('activity_at') + end + + def build_lists_started + @build_lists_started ||= build_lists.build_lists_started.to_a + end + + def build_lists_success + @build_lists_success ||= build_lists.build_lists_success.to_a + end + + def build_lists_error + @build_lists_error ||= build_lists.build_lists_error.to_a + end + + def build_lists_published + @build_lists_published ||= build_lists.build_lists_published.to_a + end + + def prepare_collection(items) + data = [] + to = range_start + while to <= range_end + from = to - 1.send(unit) + y = items.find{ |i| i.activity_at > from && i.activity_at <= to }.try(:count) + data << { x: from.strftime(format), y: y || 0 } + to += 1.send(unit) + end + data + end + + def format + @format ||= unit == :hour ? '%F %H:00:00' : '%F' + end + +end diff --git a/app/views/statistics/index.json.jbuilder b/app/views/statistics/index.json.jbuilder deleted file mode 100644 index f348e186a..000000000 --- a/app/views/statistics/index.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.build_lists @build_lists diff --git a/config/locales/models/statistics.en.yml b/config/locales/models/statistics.en.yml index 55b4c60e7..0246ffe98 100644 --- a/config/locales/models/statistics.en.yml +++ b/config/locales/models/statistics.en.yml @@ -9,10 +9,6 @@ en: range_start_placeholder: "Select a start date" range_separator: " and " range_end_placeholder: "Select an end date" - page_views_title: " Page views" - unique_visitors_title: " Unique visitors" - total_page_views: "Total page views" - total_user_sessions: "Total user sessions" no_data: "No data available" build_lists: @@ -21,6 +17,11 @@ en: build_error_title: "Build error" build_published_title: "Build has been published" + total_build_started: "Total build started" + total_success: "Total build complete" + total_build_error: "Total build error" + total_build_published: "Total build has been published" + helper: period: twenty_four_hours: "the last 24 hours" diff --git a/config/locales/models/statistics.ru.yml b/config/locales/models/statistics.ru.yml new file mode 100644 index 000000000..53233f3c7 --- /dev/null +++ b/config/locales/models/statistics.ru.yml @@ -0,0 +1,34 @@ +ru: + statistics: + index: + header: "Статистика" + + filter: + header: "Статистика" + range_label: "Показать данные за" + range_start_placeholder: "Выберите начальную дату" + range_separator: " и " + range_end_placeholder: "Выберите конечную дату" + no_data: "Нет данных" + + build_lists: + build_started_title: "Cобирается" + success_title: "Cобрано" + build_error_title: "Ошибка сборки" + build_published_title: "Опубликовано" + + total_build_started: "Всего собирается" + total_success: "Всего собрано" + total_build_error: "Всего ошибок сборки" + total_build_published: "Всего опубликовано" + + helper: + period: + twenty_four_hours: "последние 24 часа" + last_7_days: "последние 7 дней" + last_30_days: "последние 30 дней" + last_60_days: "последние 60 дней" + last_90_days: "последние 90 дней" + last_180_days: "последние 6 месяцев" + last_year: "последний год" + custom: "интервал" \ No newline at end of file