module Eco module API class Session class Batch # @attr_reader count [Integer] the total number of requests # @attr_reader stats [Hash] plain `Hash` with the number of requests that include an attribute class RequestStats CORE_ATTRS = Eco::API::Common::People::PersonParser::CORE_ATTRS ACCOUNT_ATTRS = (Eco::API::Common::People::PersonParser::ACCOUNT_ATTRS + ["permissions_custom"]).uniq DETAILS_ATTRS = ["fields"] BLANKED_PREFIX = "blanked_" DETAILS_FIELDS = "details_fields" class << self def valid_type?(type) Eco::API::Session::Batch::Job.valid_type?(type.to_sym) end def core_attrs(stats: false, all: false) CORE_ATTRS.dup.tap do |attrs| if stats || all attrs.unshift("core") attrs.concat(blank_attrs(CORE_ATTRS)) end end end def account_attrs(stats: false, all: false) ACCOUNT_ATTRS.dup.tap do |attrs| if stats || all attrs.unshift("account_remove") attrs.unshift("account") if all attrs.concat(blank_attrs(ACCOUNT_ATTRS)) end end end def details_attrs(stats: false, all: false) DETAILS_ATTRS.dup.tap do |attrs| if stats || all attrs.unshift("details_remove") attrs.unshift("details") if all end end end def blanked_prefix(attr = nil) @blanked_prefix ||= BLANKED_PREFIX return @blanked_prefix unless attr "#{blanked_prefix}#{attr}" end def blanked_prefix=(value) @blanked_prefix = value || blanked_prefix end def blank_attrs(attrs) attrs.map {|attr| "#{blanked_prefix}#{attr}"} end end attr_reader :type, :count def initialize(type:, requests: []) raise "type should be one of #{Eco::API::Session::Batch::Job.types}. Given: #{type}" unless self.class.valid_type?(type.to_sym) @type = type.to_sym @count = requests && requests.length @stats = build(requests) end def to_h @stats end def message(percent: false) lines = [] lines << pairs_to_line(core_pairs(percent: percent)) lines << pairs_to_line(account_pairs(percent: percent)) lines << pairs_to_line(details_pairs(percent: percent)) lines.join("\n") end private def attr_value(attr, percent: false, total: count, details: false) target = details ? (@stats[DETAILS_FIELDS] || {}) : @stats i = target["#{attr}"] return i unless percent percentage(i, total: total) end def core(percent: false) attr_value("core", percent: percent) end def account(percent: false) attr_value("account", percent: percent) end def details(percent: false) attr_value("details", percent: percent) end def stats @stats ||= Hash.new(0) end def build(requests) stats.tap do |stats| stats[type] = count unless !requests || !requests.is_a?(Enumerable) || requests.empty? stats[DETAILS_FIELDS] = Hash.new(0) requests.each_with_index do |request| add_core_stats(stats, request || {}) add_account_stats(stats, request || {}) add_details_stats(stats, request || {}) end end end end def attrs_to_stat(stats, hash, attrs) stats.tap do |st| attrs.each do |attr| if hash.key?(attr) st[attr] += 1 st[blanked_prefix(attr)]+= 1 if blanked_value?(hash[attr]) end end end end def add_core_stats(stats, request) stats["core"] += 1 if (request.keys & core_attrs).length > 0 attrs_to_stat(stats, request, core_attrs) end def add_account_stats(stats, request) if request.key?("account") stats["account"] += 1 stats["account_remove"] += 1 if !request["account"] attrs_to_stat(stats, request["account"] || {}, account_attrs) end end def add_details_stats(stats, request) if request.key?("details") stats["details"] += 1 stats["details_remove"] += 1 if !request["details"] det_attrs = {} if fields = request.dig("details", "fields") stats["fields"] += fields.length det_attrs = fields.each_with_object(det_attrs) {|fld, hash| hash[fld["alt_id"]] = fld["value"]} end attrs_to_stat(stats[DETAILS_FIELDS], det_attrs, det_attrs.keys) end end def core_pairs(percent: false) cattrs = core_attrs + blank_attrs(core_attrs) [["core", core(percent: percent)]] + pairs(cattrs, percent: percent, total: core) end def account_pairs(percent: false) aattrs = ["account_remove"] + account_attrs + blank_attrs(account_attrs) [["account", account(percent: percent)]] + pairs(aattrs, percent: percent, total: account) end def details_pairs(percent: false) det_pairs = [["details", details(percent: percent)]] det_pairs += [["fields", fields_average]] if attr_value("fields") && fields_average det_pairs += pairs(["details_remove"], percent: percent, total: details) det_pairs += pairs(details_field_attrs, percent: percent, total: details, details: true) end def pairs(attrs, percent: false, total: count, details: false) pairs = attrs.map do |a| (v = attr_value(a, percent: percent, total: count, details: details)) > 0 ? [a, v] : nil end.compact end def pairs_to_line(pairs, percent: false) key_val_delimiter = ": "; attr_delimiter = " ++ " pairs.map do |p| [p.first.to_s, "#{p.last.to_s}" + (percent ? "%" : "")].join(key_val_delimiter) end.join(attr_delimiter) end def core_attrs @core_attrs ||= self.class.core_attrs end def account_attrs @account_attrs ||= self.class.account_attrs end def details_attrs @details_attrs ||= self.class.details_attrs end def details_field_attrs @stats[DETAILS_FIELDS].keys end def blank_attrs(attrs) self.class.blank_attrs(attrs) end def blanked_prefix(attr = nil) self.class.blanked_prefix(attr) end def blanked_value?(value) case value when nil true when false true when Numeric value == 0 when Array value.compact.empty? when String value.to_s.strip.empty? end end def fields_average if (fields_num = attr_value("fields")) && (total = details) > 0 (fields_num.to_f / total.to_f).round(2) end end def percentage(num, total: count) total ||= count if num (num.to_f / total * 100).round(2) end end end end end end end