dawarich/app/services/visitcalc.rb
2024-07-23 00:40:48 +02:00

127 lines
4.8 KiB
Ruby

# frozen_string_literal: true
class Visitcalc
class Visit
attr_accessor :start_time, :end_time, :points
def initialize(start_time)
@start_time = start_time
@end_time = start_time
@points = []
end
def add_point(point)
@points << point
@end_time = point.timestamp if point.timestamp > @end_time
end
def duration_in_minutes
(end_time - start_time) / 60.0
end
def valid?
@points.size > 1 && duration_in_minutes >= 10
end
end
def call
# Usage
area = Area.last
points = Point.near([area.latitude, area.longitude], (area.radius / 1000.0)).order(timestamp: :asc)
points_grouped_by_month = points.group_by { |point| Time.zone.at(point.timestamp).strftime('%Y-%m') }
visits_by_month = {}
points_grouped_by_month.each do |month, points_in_month|
visits_by_month[month] = group_points_into_visits(points_in_month, 30, 15)
end
# Debugging output to check the number of visits and some sample data
visits_by_month.each do |month, visits|
puts "Month: #{month}, Total visits: #{visits.size}"
visits.each do |time_range, visit_points|
puts "Visit from #{time_range}, Points: #{visit_points.size}"
end
end
visits_by_month.map { |d, v| v.keys }
end
def group_points_into_visits(points, time_threshold_minutes = 30, merge_threshold_minutes = 15)
# Ensure points are sorted by timestamp
sorted_points = points.sort_by(&:timestamp)
visits = []
current_visit = nil
sorted_points.each do |point|
point_time = point.timestamp
puts "Processing point at #{Time.zone.at(point_time).strftime('%Y-%m-%d %H:%M:%S')}"
if current_visit.nil?
puts "Starting new visit at #{Time.zone.at(point_time).strftime('%Y-%m-%d %H:%M:%S')}"
current_visit = Visit.new(point_time)
current_visit.add_point(point)
else
time_difference = (point_time - current_visit.end_time) / 60.0 # Convert to minutes
puts "Time difference: #{time_difference.round} minutes"
if time_difference <= time_threshold_minutes
current_visit.add_point(point)
else
if current_visit.valid?
puts "Ending visit from #{Time.zone.at(current_visit.start_time).strftime('%Y-%m-%d %H:%M:%S')} to #{Time.zone.at(current_visit.end_time).strftime('%Y-%m-%d %H:%M:%S')}, duration: #{current_visit.duration_in_minutes} minutes, points: #{current_visit.points.size}"
visits << current_visit
else
puts "Discarding visit from #{Time.zone.at(current_visit.start_time).strftime('%Y-%m-%d %H:%M:%S')} to #{Time.zone.at(current_visit.end_time).strftime('%Y-%m-%d %H:%M:%S')} (invalid, points: #{current_visit.points.size}, duration: #{current_visit.duration_in_minutes} minutes)"
end
current_visit = Visit.new(point_time)
current_visit.add_point(point)
puts "Starting new visit at #{Time.zone.at(point_time).strftime('%Y-%m-%d %H:%M:%S')}"
end
end
end
# Add the last visit to the list if it is valid
if current_visit&.valid?
puts "Ending visit from #{Time.zone.at(current_visit.start_time).strftime('%Y-%m-%d %H:%M:%S')} to #{Time.zone.at(current_visit.end_time).strftime('%Y-%m-%d %H:%M:%S')}, duration: #{current_visit.duration_in_minutes} minutes, points: #{current_visit.points.size}"
visits << current_visit
else
puts "Discarding last visit from #{Time.zone.at(current_visit.start_time).strftime('%Y-%m-%d %H:%M:%S')} to #{Time.zone.at(current_visit.end_time).strftime('%Y-%m-%d %H:%M:%S')} (invalid, points: #{current_visit.points.size}, duration: #{current_visit.duration_in_minutes} minutes)"
end
# Merge visits that are not more than merge_threshold_minutes apart
merged_visits = []
previous_visit = nil
visits.each do |visit|
if previous_visit.nil?
previous_visit = visit
else
time_difference = (visit.start_time - previous_visit.end_time) / 60.0 # Convert to minutes
if time_difference <= merge_threshold_minutes
previous_visit.points.concat(visit.points)
previous_visit.end_time = visit.end_time
else
merged_visits << previous_visit
previous_visit = visit
end
end
end
merged_visits << previous_visit if previous_visit
# Sort visits by start time
merged_visits.sort_by!(&:start_time)
# Convert visits to a hash with human-readable datetime ranges as keys and points as values
visits_hash = {}
merged_visits.each do |visit|
start_time_str = Time.zone.at(visit.start_time).strftime('%Y-%m-%d %H:%M:%S')
end_time_str = Time.zone.at(visit.end_time).strftime('%Y-%m-%d %H:%M:%S')
visits_hash["#{start_time_str} - #{end_time_str}"] = visit.points
end
visits_hash
end
end
# Run the Visitcalc class
# Visitcalc.new.call