mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 17:21:38 -05:00
127 lines
4.8 KiB
Ruby
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
|