app/lib/stats/views/usage.rb
module Stats module Views module Usage extend ActiveSupport::Concern included do include System::UrlHelpers.system_url_helpers end GRANULARITIES = {:year => :month, :month => :day, :week => 6.hours, :day => :hour}.with_indifferent_access ALLOWED_GRANULARITIES = GRANULARITIES.values ALLOWED_TIME_RANGES = {:month => 10.years, :day => 1.year, 6.hours => 6.months, # 6.hours looks unused, but since it's a valid granularity we need to check it as well :hour => 90.days}.with_indifferent_access Method `usage` has 43 lines of code (exceeds 25 allowed). Consider refactoring.
Stats::Views::Usage#usage has approx 15 statements def usage(options) range, granularity, metric = extract_range_and_granularity_and_metric(options) data = usage_values_in_range(range, granularity, metric) # metric can be a response_code total = data.sum result = { metric.class.name.underscore.to_sym => detail(metric), :period => period_detail(options), :total => total, :values => data } Stats::Views::Usage#usage performs a nil-check unless @cinstance.nil? application_id = @cinstance.idStats::Views::Usage#usage calls '@cinstance.user_account' 2 times account_id = @cinstance.user_account.id result[:application] = { :id => application_id, :name => @cinstance.name, :state => @cinstance.state, :link => provider_admin_application_path(application_id), :description => @cinstance.description, :plan => {Stats::Views::Usage#usage calls '@cinstance.plan' 2 times :id => @cinstance.plan.id, :name => @cinstance.plan.name }, :account => { :id => account_id, :name => @cinstance.user_account.org_name, :link => admin_buyers_account_path(account_id) }, service: { id: @cinstance.service_id } } end return result if options.fetch(:skip_change, true) if granularity.to_s == 'day'Stats::Views::Usage#usage calls 'range.previous' 3 times previous_range = range.class.new(range.previous.begin.midnight, range.previous.end.midnight) # this is to keep us from breaking in DST....... previous_data = usage_values_in_range(previous_range, granularity, metric) else previous_data = usage_values_in_range(range.previous, granularity, metric) end previous_total = previous_data.sum result[:previous_total] = previous_total result[:change] = total.percentage_change_from(previous_total) result end Stats::Views::Usage#usage_progress has approx 10 statements def usage_progress(options) range, granularity, metric = extract_range_and_granularity_and_metric(options) current_data = usage_values_in_range(range, granularity, metric) # can be Metric or ResponseCodeStats::Views::Usage#usage_progress calls 'range.previous' 2 times previous_range = range.class.new(range.previous.begin.midnight, range.previous.end.midnight - 1) # this is to keep us from breaking in DST.......Stats::Views::Usage#usage_progress calls 'options[:skip_change]' 2 times previous_data = usage_values_in_range(previous_range, granularity, metric) unless options[:skip_change] # previous_data = usage_values_in_range(range.previous, granularity, metric) unless options[:skip_change] total = current_data.sum previous_total = previous_data.sum rslt = { data: { total: total, values: current_data, previous_total: previous_total } } rslt.merge!(detail(metric)) rslt.deep_merge!(data: { change: total.percentage_change_from(previous_total) }) unless options[:skip_change] rslt end def usage_progress_for_buyer_methods(options) #source.first => service, source.last => app methods = source.first.method_metrics.select do |method|Stats::Views::Usage#usage_progress_for_buyer_methods calls 'source.last.plan' 2 times
Stats::Views::Usage#usage_progress_for_buyer_methods calls 'source.last' 2 times method.enabled_for_plan?(source.last.plan) && method.visible_in_plan?(source.last.plan) end usage_for_all(methods, options) end def usage_progress_for_all_methods(options) usage_for_all( source.first.method_metrics, options) end def usage_progress_for_all_metrics(options) usage_for_all( source.first.metrics.top_level, options) end private def usage_for_all(items, options) metrics = items.inject([]) do |memo, item| memo << usage_progress(options.merge(:metric => item)) end { :period => period_detail(options), :metrics => metrics } end def usage_values_in_range(range, granularity, metric) storage.values_in_range(range, granularity, [:stats, source_key, metric]) end def extract_range_and_granularity_and_metric(options) options = options.to_h.symbolize_keys range, granularity = extract_range_and_granularity(options) validate_time_range(range, granularity) metric = extract_metric(options) [range, granularity, metric] end Method `extract_range_and_granularity` has a Cognitive Complexity of 20 (exceeds 5 allowed). Consider refactoring.
Stats::Views::Usage#extract_range_and_granularity has approx 16 statements def extract_range_and_granularity(options)Stats::Views::Usage#extract_range_and_granularity refers to 'options' more than self (maybe move it to another class?)
Stats::Views::Usage#extract_range_and_granularity calls 'options[:period]' 2 times if options[:period] period = sanitize_period(options[:period])Stats::Views::Usage#extract_range_and_granularity calls 'options[:granularity]' 6 times granularity = options[:granularity] || GRANULARITIES[period] length = 1.send(period) Stats::Views::Usage#extract_range_and_granularity calls 'extract_timezone(options)' 2 times timezone = extract_timezone(options)Stats::Views::Usage#extract_range_and_granularity calls 'options[:since]' 3 times range_since = to_time(options[:since].presence || timezone.now - length, timezone) range_until = (range_since + length - 1.second).end_of_minute # taking a second away means excluding the extra day in case of a month, etc sanitize_range_and_granularity(range_since..range_until, granularity) else raise InvalidParameterError, "Missing parameter :granularity" unless options.key?(:granularity) # due to the unfortunate use of 21600 as a valid granularity the parameter is required to a symbol or fixnum raise InvalidParameterError, "Granularity must be one of #{ALLOWED_GRANULARITIES.inspect}, not #{options[:granularity]}" unless ALLOWED_GRANULARITIES.include?(options[:granularity]) || ALLOWED_GRANULARITIES.include?(options[:granularity].to_sym) Stats::Views::Usage#extract_range_and_granularity calls 'options[:until]' 2 times if options[:since].present? && options[:until].present? timezone = extract_timezone(options) range = to_time(options[:since], timezone)..to_time(options[:until], timezone) sanitize_range_and_granularity(range, options[:granularity])Stats::Views::Usage#extract_range_and_granularity calls 'options[:range]' 2 times elsif options[:range].present? sanitize_range_and_granularity(options[:range], options[:granularity]) else raise InvalidParameterError, "You need to specify either 'range' or 'since' and 'until'" end endStats::Views::Usage#extract_range_and_granularity has the variable name 'e' rescue ThreeScale::HashHacks::MissingKeyError => e raise InvalidParameterError, e.to_s end protected def sanitize_period(period) if GRANULARITIES.has_key?(period) return period else raise InvalidParameterError, "Period must be one of #{GRANULARITIES.keys.inspect} not #{period.inspect}" end end def sanitize_range_and_granularity(range, granularity) granularity = Stats::Aggregation.normalize_granularity(granularity) range = range.to_time_range.round(granularity) [range, granularity] end def validate_time_range(range, granularity) allowed_time = ALLOWED_TIME_RANGES[granularity] return unless allowed_time raise InvalidParameterError, "Time range for the granularity must be less than #{allowed_time.inspect}" if range.length > allowed_time end end endend