# 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 return unless File.directory? destination # Recordar la última vez que se corrió la tarea stat = site.stats.create! name: STAT_NAME beginning = beginning_of_interval site.hostnames.each do |host| break if stop? uris.each do |uri| break if stop? rollup('host|uri', beginning, host: host, uri: uri) AccessLog.where(host: host, uri: uri).distinct(:http_referer).pluck(:http_referer).each do |http_referer| rollup('host|uri|referer', beginning, host: host, uri: uri, http_referer: http_referer) end end rollup('host', beginning, host: host) AccessLog.where(host: host).distinct(:http_referer).pluck(:http_referer).each do |http_referer| rollup('host|referer', beginning, host: host, http_referer: http_referer) end end stat.touch run_again! unless once end private # Generar un rollup a partir de unas dimensiones que también sirven de # filtro. # # @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) # Reducir las estadísticas calculadas aplicando un rollup sobre el # intervalo más amplio. 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 end def stat_name STAT_NAME end # @return [String] # # TODO: Cambiar al mergear origin-referer def destination @destination ||= site.deploys.find_by(type: 'DeployLocal').destination end # Recolecta todas las URIs menos imágenes # # @return [Array] def uris @uris ||= locales.map do |locale| uri = "/#{locale}/".squeeze('/') dir = File.join(destination, locale) files(dir).map do |f| uri + f end end.flatten(2) 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