diff --git a/app/controllers/repp/v1/stats_controller.rb b/app/controllers/repp/v1/stats_controller.rb new file mode 100644 index 0000000000..480c81a6b1 --- /dev/null +++ b/app/controllers/repp/v1/stats_controller.rb @@ -0,0 +1,100 @@ +module Repp + module V1 + class StatsController < BaseController + api :get, '/repp/v1/stats/market_share_distribution' + desc 'Get market share and distribution of registrars' + param :q, Hash, required: true, desc: 'Period parameters for data' do + param :end_date, String, required: true, desc: 'Period end date' + end + def market_share_distribution + registrars = ::Registrar.where(test_registrar: false).joins(:domains) + .where(from_condition).where(to_condition) + grouped = registrars.group(:name).count + + result = grouped.map do |key, value| + hash = { name: key.strip, y: value } + hash.merge!({ sliced: true, selected: true }) if current_user.registrar.name == key + hash + end + render_success(data: result) + end + + # rubocop:disable Metrics/MethodLength + api :get, '/repp/v1/stats/market_share_growth_rate' + desc 'Get market share and growth rate of registrars' + param :q, Hash, required: true, desc: 'Period parameters for data' do + param :end_date, String, required: true, desc: 'Period end date' + param :compare_to_date, String, required: true, desc: 'Comparison date' + end + def market_share_growth_rate + registrars = ::Registrar.where(test_registrar: false).joins(:domains) + + domains_by_rar = registrars.where(from_condition).where(to_condition).group(:name).count + prev_domains_by_rar = registrars.where(compare_to_condition).group(:name).count + + set_zero_values!(domains_by_rar, prev_domains_by_rar) + + market_share_by_rar = calculate_market_share(domains_by_rar) + prev_market_share_by_rar = calculate_market_share(prev_domains_by_rar) + + result = { prev_data: { name: search_params[:compare_to_date], + domains: serialize(prev_domains_by_rar), + market_share: serialize(prev_market_share_by_rar) }, + data: { name: search_params[:end_date], + domains: serialize(domains_by_rar), + market_share: serialize(market_share_by_rar) } } + render_success(data: result) + end + # rubocop:enable Metrics/MethodLength + + private + + def search_params + params.permit(:q, q: %i[start_date end_date compare_to_date]) + .fetch(:q, {}) || {} + end + + def from_condition + return unless search_params[:start_date] + + "domains.created_at >= '#{to_date(search_params[:start_date])}'" + end + + def to_condition + return unless search_params[:end_date] + + "domains.created_at <= '#{to_date(search_params[:end_date]).end_of_month}'" + end + + def compare_to_condition + return unless search_params[:compare_to_date] + + "domains.created_at <= '#{to_date(search_params[:compare_to_date]).end_of_month}'" + end + + def to_date(date_param) + Date.strptime(date_param, '%m.%y') + end + + def serialize(rars) + rars.map { |key, value| [key, value] } + end + + def set_zero_values!(cur, prev) + cur_dup = cur.dup + cur_dup.each_key do |k| + cur_dup[k] = prev[k] || 0 + end + prev.clear.merge!(cur_dup) + end + + def calculate_market_share(domains_by_rar) + sum = domains_by_rar.values.sum + domains_by_rar.transform_values do |v| + value = v.to_f / sum * 100.0 + value < 0.1 ? value.round(3) : value.round(1) + end + end + end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 9021eff60c..f15a8a55da 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -8,8 +8,11 @@ en: date_long: "%d. %B %Y" filename: "%Y-%m-%d_%H.%M" date: + month_names: + [~, January, February, March, April, May, June, July, August, September, October, November, December] formats: default: "%Y-%m-%d" + month_year: "%B, %Y" activerecord: errors: diff --git a/config/locales/et.yml b/config/locales/et.yml index 84672ed3c7..6da5860fff 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -3,8 +3,11 @@ et: password: 'Parool' date: - # Don't forget the nil at the beginning; there's no such thing as a 0th month - month_names: [~, Jaanuar, Veebruar, Märts, Aprill, Mai, Juuni, Juuli, August, September, Oktoober, November, Detsember] + month_names: + [~, Jaanuar, Veebruar, Märts, April, Mai, Juuni, Juuli, August, September, Oktoober, November, Detsember] + formats: + default: "%Y-%m-%d" + month_year: "%B, %Y" emails: "Meillaadressid" invoice: title: 'Arve' diff --git a/config/routes.rb b/config/routes.rb index 180a4687dd..7ef8474f0d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,6 +99,12 @@ end resources :auctions, only: %i[index] resources :retained_domains, only: %i[index] + resources :stats do + collection do + get '/market_share_distribution', to: 'stats#market_share_distribution' + get '/market_share_growth_rate', to: 'stats#market_share_growth_rate' + end + end namespace :registrar do resources :notifications, only: [:index, :show, :update] do collection do diff --git a/test/fixtures/domains.yml b/test/fixtures/domains.yml index c8ea1bfce4..01c43d644d 100644 --- a/test/fixtures/domains.yml +++ b/test/fixtures/domains.yml @@ -12,6 +12,7 @@ shop: period: 1 period_unit: m uuid: 1b3ee442-e8fe-4922-9492-8fcb9dccc69c + created_at: <%= 2.days.ago.to_s :db %> airport: name: airport.test @@ -24,6 +25,7 @@ airport: period: 1 period_unit: m uuid: 2df2c1a1-8f6a-490a-81be-8bdf29866880 + created_at: <%= 2.days.ago.to_s :db %> library: name: library.test @@ -36,6 +38,7 @@ library: period: 1 period_unit: m uuid: 647bcc48-8d5e-4a04-8ce5-2a3cd17b6eab + created_at: <%= 2.days.ago.to_s :db %> metro: name: metro.test @@ -48,6 +51,7 @@ metro: period: 1 period_unit: m uuid: ef97cb80-333b-4893-b9df-163f2b452798 + created_at: <%= 2.days.ago.to_s :db %> hospital: name: hospital.test diff --git a/test/integration/repp/v1/stats/market_share_test.rb b/test/integration/repp/v1/stats/market_share_test.rb new file mode 100644 index 0000000000..614b5868dc --- /dev/null +++ b/test/integration/repp/v1/stats/market_share_test.rb @@ -0,0 +1,45 @@ +require 'test_helper' + +class ReppV1StatsMarketShareTest < ActionDispatch::IntegrationTest + def setup + @user = users(:api_bestnames) + token = Base64.encode64("#{@user.username}:#{@user.plain_text_password}") + token = "Basic #{token}" + + @auth_headers = { 'Authorization' => token } + @today = Time.zone.today.strftime('%m.%y') + end + + def test_shows_market_share_distribution_data + get '/repp/v1/stats/market_share_distribution', headers: @auth_headers, + params: { q: { end_date: @today } } + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + assert json[:data].is_a? Array + assert json[:data][0].is_a? Hash + assert_equal json[:data][0][:name], 'Best Names' + assert json[:data][0][:selected] + end + + def test_shows_market_share_growth_rate_data + prev_date = Time.zone.today.last_month.strftime('%m.%y') + get '/repp/v1/stats/market_share_growth_rate', headers: @auth_headers, + params: { q: { end_date: @today, + compare_to_date: prev_date } } + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + data = json[:data] + assert data[:data].is_a? Hash + assert data[:prev_data].is_a? Hash + assert_equal data[:data][:name], @today + assert data[:data][:domains].is_a? Array + end +end \ No newline at end of file