# frozen_string_literal: true # Procesar una lista de URIs para una lista de dominios. Esto nos # permite procesar estadísticas a demanda. # # Hay varias cosas acá que van a convertirse en métodos propios, como la # detección de URIs de un sitio (aunque la versión actual detecta todas # las páginas y no solo las de posts como tenemos planeado, hay que # resolver eso). # # Los hostnames de un sitio van a poder obtenerse a partir de # Site#hostnames con la garantía de que son únicos. class UriCollectionJob < PeriodicJob include RecursiveRollup # Ignoramos imágenes porque suelen ser demasiadas y no aportan a las # estadísticas. IMAGES = %w[.png .jpg .jpeg .gif .webp .jfif].freeze STAT_NAME = 'uri_collection_job' def perform(site_id:, once: true) @site = Site.find site_id # Recordar la última vez que se corrió la tarea stat = site.stats.create! name: STAT_NAME beginning = beginning_of_interval # Recorremos todos los hostnames y uris posibles y luego agrupamos # recursivamente para no tener que recalcular, asumiendo que es más # rápido buscar en los rollups indexados que en la tabla en bruto. # # Los referers solo se agrupan por host. site.hostnames.each do |host| break if stop? dimensions = { host: host } uris.each do |uri| break if stop? dimensions[:uri] = uri rollup('host|uri', beginning, **dimensions) reduce_rollup('host|uri', beginning, **dimensions) end dimensions.delete(:uri) # Reducir todas las visitas a cantidad de visitas por host recursive_rollup(name: 'host|uri', new_name: 'host', interval_previous: starting_interval, interval: starting_interval, dimensions: dimensions, beginning: beginning) # Acumular por mes y año reduce_rollup('host', beginning, **dimensions) columns = %i[http_referer geoip2_data_country_name] columns.each do |column| # Obtener orígenes de visitas por host AccessLog.where(host: host).distinct(column).pluck(column).each do |value| dimensions.delete_if do |k, _| columns.include? k end name = "host|#{column}" dimensions[column] = value rollup(name, beginning, **dimensions) reduce_rollup(name, beginning, **dimensions) end end end stat.touch run_again! unless once end private # Generar un rollup de access logs # # @param :name [String] # @param :beginning [Time] # @param :dimensions [Hash] # @return [nil] def rollup(name, beginning, **dimensions) AccessLog.where(**dimensions) .where('created_at >= ?', beginning) .completed_requests .non_robots .group(*dimensions.keys) .rollup(name, interval: starting_interval, update: true) end # Reducir las estadísticas calculadas aplicando un rollup sobre el # intervalo más amplio. # # @param :name [String] # @param :beginning [Time] # @param :dimensions [Hash] # @return [nil] def reduce_rollup(name, beginning, **dimensions) Stat::INTERVALS.reduce do |previous, current| recursive_rollup(name: name, interval_previous: previous, interval: current, dimensions: dimensions, beginning: beginning) # Devolver el intervalo actual current end nil end def stat_name STAT_NAME end # Obtiene todas las ubicaciones de archivos # # @return [String] # # TODO: Cambiar al mergear origin-referer def destinations @destinations ||= site.deploys.map(&:destination).select do |d| File.directory?(d) end.map do |d| File.realpath(d) end.uniq end # Recolecta todas las URIs menos imágenes # # @return [Array] def uris @uris ||= destinations.map do |destination| locales.map do |locale| uri = "/#{locale}/".squeeze('/') dir = File.join(destination, locale) files(dir).map do |f| uri + f end end end.flatten(3) end # @return [Array] def locales @locales ||= ['', site.locales.map(&:to_s)].flatten(1) end # @param :dir [String] # @return [Array] def files(dir) Dir.chdir(dir) do pages = Dir.glob('**/*.html') files = Dir.glob('public/**/*') files = remove_directories files files = remove_images files [pages, files].flatten(1) end end # @param :files [Array] # @return [Array] def remove_directories(files) files.reject do |f| File.directory? f end end def remove_images(files) files.reject do |f| IMAGES.include? File.extname(f).downcase end end end