mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
133 lines
4 KiB
Ruby
133 lines
4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Processes individual time chunks in parallel for track generation
|
|
# Each job handles one time chunk independently using in-memory segmentation
|
|
class Tracks::TimeChunkProcessorJob < ApplicationJob
|
|
include Tracks::Segmentation
|
|
include Tracks::TrackBuilder
|
|
|
|
queue_as :tracks
|
|
|
|
def perform(user_id, session_id, chunk_data)
|
|
@user = User.find(user_id)
|
|
@session_manager = Tracks::SessionManager.new(user_id, session_id)
|
|
@chunk_data = chunk_data
|
|
|
|
return unless session_exists?
|
|
|
|
tracks_created = process_chunk
|
|
update_session_progress(tracks_created)
|
|
rescue StandardError => e
|
|
ExceptionReporter.call(e, "Failed to process time chunk for user #{user_id}")
|
|
|
|
mark_session_failed(e.message)
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :user, :session_manager, :chunk_data
|
|
|
|
def session_exists?
|
|
unless session_manager.session_exists?
|
|
Rails.logger.warn "Session #{session_manager.session_id} not found for user #{user.id}, skipping chunk"
|
|
return false
|
|
end
|
|
true
|
|
end
|
|
|
|
def process_chunk
|
|
# Load points for the buffer range
|
|
points = load_chunk_points
|
|
return 0 if points.empty?
|
|
|
|
# Segment points using Geocoder-based logic
|
|
segments = segment_chunk_points(points)
|
|
return 0 if segments.empty?
|
|
|
|
# Create tracks from segments
|
|
tracks_created = 0
|
|
segments.each do |segment_points|
|
|
tracks_created += 1 if create_track_from_points_array(segment_points)
|
|
end
|
|
|
|
tracks_created
|
|
end
|
|
|
|
def load_chunk_points
|
|
user.points
|
|
.where(timestamp: chunk_data[:buffer_start_timestamp]..chunk_data[:buffer_end_timestamp])
|
|
.order(:timestamp)
|
|
end
|
|
|
|
def segment_chunk_points(points)
|
|
# Convert relation to array for in-memory processing
|
|
points_array = points.to_a
|
|
|
|
# Use Geocoder-based segmentation
|
|
segments = split_points_into_segments_geocoder(points_array)
|
|
|
|
# Filter segments to only include those that overlap with the actual chunk range
|
|
# (not just the buffer range)
|
|
segments.select do |segment|
|
|
segment_overlaps_chunk_range?(segment)
|
|
end
|
|
end
|
|
|
|
def segment_overlaps_chunk_range?(segment)
|
|
return false if segment.empty?
|
|
|
|
segment_start = segment.first.timestamp
|
|
segment_end = segment.last.timestamp
|
|
chunk_start = chunk_data[:start_timestamp]
|
|
chunk_end = chunk_data[:end_timestamp]
|
|
|
|
# Check if segment overlaps with the actual chunk range (not buffer)
|
|
segment_start <= chunk_end && segment_end >= chunk_start
|
|
end
|
|
|
|
def create_track_from_points_array(points)
|
|
return nil if points.size < 2
|
|
|
|
begin
|
|
# Calculate distance using Geocoder with validation
|
|
distance = Point.calculate_distance_for_array_geocoder(points, :km)
|
|
|
|
# Additional validation for the distance result
|
|
if !distance.finite? || distance < 0
|
|
Rails.logger.error "Invalid distance calculated (#{distance}) for #{points.size} points in chunk #{chunk_data[:chunk_id]}"
|
|
Rails.logger.debug "Point coordinates: #{points.map { |p| [p.latitude, p.longitude] }.inspect}"
|
|
return nil
|
|
end
|
|
|
|
track = create_track_from_points(points, distance * 1000) # Convert km to meters
|
|
|
|
if track
|
|
Rails.logger.debug "Created track #{track.id} with #{points.size} points (#{distance.round(2)} km)"
|
|
else
|
|
Rails.logger.warn "Failed to create track from #{points.size} points with distance #{distance.round(2)} km"
|
|
end
|
|
|
|
track
|
|
rescue StandardError => e
|
|
nil
|
|
end
|
|
end
|
|
|
|
def update_session_progress(tracks_created)
|
|
session_manager.increment_completed_chunks
|
|
session_manager.increment_tracks_created(tracks_created) if tracks_created > 0
|
|
end
|
|
|
|
def mark_session_failed(error_message)
|
|
session_manager.mark_failed(error_message)
|
|
end
|
|
|
|
# Required by Tracks::Segmentation module
|
|
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
|
|
end
|
|
end
|