cartodb-4.42/lib/cartodb/stats/api_calls.rb
2024-04-06 05:25:13 +00:00

162 lines
6.2 KiB
Ruby

module CartoDB
module Stats
class APICalls
# REDIS_SOURCES
# mapviews -> mapviews sent from tiler to redis
# mapviews_es -> mapviews sent from logs to redis
REDIS_SOURCES = ['mapviews', 'mapviews_es']
# This method will return an array without dates
def get_api_calls_without_dates(username, options = {})
calls = get_api_calls(username, options).map {|day, value| value}
if options[:old_api_calls]
raise "Cannot request old api calls with custom dates" if options[:to] or options[:from]
# Add old api calls
old_calls = get_old_api_calls(username) rescue []
calls = calls.zip(old_calls).map { |pair|
pair[0].to_i + pair[1].to_i
} unless old_calls.blank?
end
return calls
end
# This method will never include old api calls
# It will return a hash of dates with map views values
def get_api_calls_with_dates(username, options = {})
get_api_calls(username, options)
end
# Wrapper to get a total of api calls of a user or visualization
# It doesn't include old api calls
def get_total_api_calls(username, visualization_id = nil)
get_total_api_calls_from_redis(username, visualization_id)
end
def get_api_calls(username, options = {})
get_api_calls_from_redis(username, options)
end
# DEPRECATED
# This method returns a 30 days ordered array
# This array is populated from other tasks. It's not possible
# to pass a days parameter to this method
def get_old_api_calls(username)
user_redis_key = ::User.where(:username => username).first.key
calls = $users_metadata.HMGET(user_redis_key, 'api_calls').first
if calls.nil? || calls['per_day'].nil?
return []
else
JSON.parse(calls['per_day']).to_a.reverse
end
end
# Iterate through all api calls redis sources and returns total
# api calls of a user or a visualization
def get_total_api_calls_from_redis(username, visualization_id = nil)
calls = 0
REDIS_SOURCES.each do |source|
source_calls = get_total_api_calls_from_redis_source(username, source, visualization_id)
if !source_calls.nil? and source_calls != 0
calls = calls + source_calls
end
end
return calls
end
# Iterate through all api calls redis sources and returns total
# api calls per day
def get_api_calls_from_redis(username, options = {})
calls = {}
REDIS_SOURCES.each do |source|
source_calls = get_api_calls_from_redis_source(username, source, options)
if calls.blank?
calls = source_calls
else
if !source_calls.blank?
source_calls.each do |day, value|
if !value.nil?
calls[day] = (calls[day].nil? ? 0 : calls[day]) + value.to_i
end
end
end
end
end
return calls
end
# Get redis key based on username and visualization id
def redis_api_call_key(username, api_call_type, visualization_id = nil)
redis_base_key = "user:#{username}:#{api_call_type}"
if visualization_id.nil?
return "#{redis_base_key}:global"
else
return "#{redis_base_key}:stat_tag:#{visualization_id}"
end
end
# Returns api calls from a redis key in a hash with dates
def get_api_calls_from_redis_source(username, api_call_type, options = {})
redis_key = redis_api_call_key(username, api_call_type, options[:stat_tag])
date_to = (options[:to] ? options[:to].to_date : Date.today)
date_from = (options[:from] ? options[:from].to_date : date_to - 29.days)
# Retrieve matching months from Redis with ZSCAN
# TODO: move to single request if Redis gem ever supports multiple matches
calls = []
matching_months_date = date_from.beginning_of_month
while matching_months_date <= date_to
$users_metadata.zscan_each(redis_key, match: "#{matching_months_date.strftime('%Y%m')}*") do |key|
calls.push(key)
end
matching_months_date += 1.month
end
# Take only the date, score tuples (arrays), flatten (to remove array levels), group again date,score pairs and sort it by date desc
calls_in_reverse_order = calls.select { |a| a.kind_of? Array }.flatten.each_slice(2).to_a.sort { |a1, a2| a1[0] <=> a2[0]}.reverse
# Crop requested window
last_requested_day = date_to.strftime("%Y%m%d")
first_requested_day = date_from.strftime("%Y%m%d")
if check_available_values(calls_in_reverse_order, first_requested_day, last_requested_day)
first_requested_index = calls_in_reverse_order.index { |day_and_count|
day_and_count[0] <= last_requested_day
}
calls_in_reverse_order = calls_in_reverse_order.drop(first_requested_index) if first_requested_index
last_requested_index = calls_in_reverse_order.index { |day_and_count|
day_and_count[0] <= first_requested_day
}
calls_in_reverse_order = calls_in_reverse_order.take(last_requested_index + 1) if last_requested_index
else
calls_in_reverse_order = {}
end
# Fill gaps
whole_range_zero = {}
date_to.downto(date_from) do |date|
stat_date = date.strftime("%Y%m%d")
whole_range_zero[stat_date] = 0
end
whole_range_zero.merge(Hash[*calls_in_reverse_order.flatten])
end
def check_available_values(values, date_from, date_to)
values.select { |value| value[0] >= date_from && value[0] <= date_to }.length > 0
end
# Returns total api calls from a redis key
def get_total_api_calls_from_redis_source(username, api_call_type, visualization_id = nil)
raise "Total api calls per user is not supported yet" if visualization_id.nil?
redis_key = redis_api_call_key(username, api_call_type, visualization_id)
return $users_metadata.ZSCORE(redis_key, 'total').to_i
end
end
end
end