dawarich/app/services/tracks/generator.rb

163 lines
4.5 KiB
Ruby
Raw Normal View History

2025-07-07 15:48:07 -04:00
# frozen_string_literal: true
2025-07-16 16:22:33 -04:00
# Simplified track generation service that replaces the complex strategy pattern.
2025-07-07 17:12:02 -04:00
#
2025-07-16 16:22:33 -04:00
# This service handles both bulk and incremental track generation using a unified
# approach with different modes:
2025-07-07 17:12:02 -04:00
#
2025-07-16 16:22:33 -04:00
# - :bulk - Regenerates all tracks from scratch (replaces existing)
# - :incremental - Processes untracked points up to a specified end time
# - :daily - Processes tracks on a daily basis
2025-07-07 17:12:02 -04:00
#
2025-07-16 16:22:33 -04:00
# The service maintains the same core logic as the original system but simplifies
# the architecture by removing the multiple strategy classes in favor of
# mode-based configuration.
2025-07-07 17:12:02 -04:00
#
2025-07-16 16:22:33 -04:00
# Key features:
# - Deterministic results (same algorithm for all modes)
# - Simple incremental processing without buffering complexity
# - Configurable time and distance thresholds from user settings
# - Automatic track statistics calculation
# - Proper handling of edge cases (empty points, incomplete segments)
2025-07-07 17:12:02 -04:00
#
2025-07-16 16:22:33 -04:00
# Usage:
# # Bulk regeneration
# Tracks::Generator.new(user, mode: :bulk).call
2025-07-07 17:12:02 -04:00
#
2025-07-16 16:22:33 -04:00
# # Incremental processing
# Tracks::Generator.new(user, mode: :incremental).call
#
# # Daily processing
# Tracks::Generator.new(user, start_at: Date.current, mode: :daily).call
#
class Tracks::Generator
include Tracks::Segmentation
include Tracks::TrackBuilder
attr_reader :user, :start_at, :end_at, :mode
def initialize(user, start_at: nil, end_at: nil, mode: :bulk)
@user = user
@start_at = start_at
@end_at = end_at
@mode = mode.to_sym
end
def call
clean_existing_tracks if should_clean_tracks?
points = load_points
Rails.logger.debug "Generator: loaded #{points.size} points for user #{user.id} in #{mode} mode"
return if points.empty?
segments = split_points_into_segments(points)
Rails.logger.debug "Generator: created #{segments.size} segments"
segments.each { |segment| create_track_from_segment(segment) }
Rails.logger.info "Generated #{segments.size} tracks for user #{user.id} in #{mode} mode"
end
private
def should_clean_tracks?
case mode
when :bulk then true
when :daily then true
when :incremental then false
else false
2025-07-07 15:48:07 -04:00
end
2025-07-16 16:22:33 -04:00
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def load_points
case mode
when :bulk then load_bulk_points
when :incremental then load_incremental_points
when :daily then load_daily_points
else
raise ArgumentError, "Unknown mode: #{mode}"
end
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def load_bulk_points
scope = user.tracked_points.order(:timestamp)
scope = scope.where(timestamp: time_range) if time_range_defined?
scope
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def load_incremental_points
# For incremental mode, we process untracked points
# If end_at is specified, only process points up to that time
scope = user.tracked_points.where(track_id: nil).order(:timestamp)
scope = scope.where(timestamp: ..end_at.to_i) if end_at.present?
scope
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def load_daily_points
day_range = daily_time_range
user.tracked_points.where(timestamp: day_range).order(:timestamp)
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def create_track_from_segment(segment)
Rails.logger.debug "Generator: processing segment with #{segment.size} points"
return unless segment.size >= 2
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
track = create_track_from_points(segment)
Rails.logger.debug "Generator: created track #{track&.id}"
track
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def time_range_defined?
start_at.present? || end_at.present?
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def time_range
return nil unless time_range_defined?
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
Time.at(start_at&.to_i)..Time.at(end_at&.to_i)
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def daily_time_range
day = start_at&.to_date || Date.current
day.beginning_of_day.to_i..day.end_of_day.to_i
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def incremental_mode?
mode == :incremental
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def clean_existing_tracks
case mode
when :bulk
clean_bulk_tracks
when :daily
clean_daily_tracks
2025-07-07 15:48:07 -04:00
end
2025-07-16 16:22:33 -04:00
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def clean_bulk_tracks
scope = user.tracks
scope = scope.where(start_at: time_range) if time_range_defined?
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
deleted_count = scope.delete_all
Rails.logger.info "Deleted #{deleted_count} existing tracks for user #{user.id}"
end
2025-07-07 15:48:07 -04:00
2025-07-16 16:22:33 -04:00
def clean_daily_tracks
day_range_times = daily_time_range.map { |timestamp| Time.at(timestamp) }
range = Range.new(day_range_times.first, day_range_times.last)
deleted_count = user.tracks.where(start_at: range).delete_all
Rails.logger.info "Deleted #{deleted_count} daily tracks for user #{user.id}"
end
# Threshold methods from safe_settings
def distance_threshold_meters
@distance_threshold_meters ||= user.safe_settings.meters_between_routes.to_i
end
def time_threshold_minutes
@time_threshold_minutes ||= user.safe_settings.minutes_between_routes.to_i
2025-07-07 15:48:07 -04:00
end
end