Calculate trip's visited countries from points

This commit is contained in:
Eugene Burmakin
2025-08-02 00:06:09 +02:00
parent b523a99391
commit 74112c0d04
5 changed files with 117 additions and 44 deletions

View File

@@ -10,6 +10,7 @@ module Calculateable
def calculate_distance
calculated_distance_meters = calculate_distance_from_coordinates
self.distance = convert_distance_for_storage(calculated_distance_meters)
end

View File

@@ -21,12 +21,6 @@ class Trip < ApplicationRecord
user.tracked_points.where(timestamp: started_at.to_i..ended_at.to_i).order(:timestamp)
end
def countries
return points.pluck(:country).uniq.compact if DawarichSettings.store_geodata?
visited_countries
end
def photo_previews
@photo_previews ||= select_dominant_orientation(photos).sample(12)
end
@@ -35,13 +29,8 @@ class Trip < ApplicationRecord
@photo_sources ||= photos.map { _1[:source] }.uniq
end
def calculate_countries
countries =
Country.where(id: points.pluck(:country_id).compact.uniq).pluck(:name)
self.visited_countries = countries
self.visited_countries = points.pluck(:country_name).uniq.compact
end
private

View File

@@ -15,12 +15,9 @@
<div class="card-body p-4">
<div class="stat-title text-xs">Countries</div>
<div class="stat-value text-lg">
<% if trip.countries.any? %>
<%= trip.countries.join(', ') %>
<% elsif trip.visited_countries.present? %>
<% if trip.visited_countries.any? %>
<%= trip.visited_countries.join(', ') %>
<% else %>
<span class="text-xs">Countries are being calculated...</span>
<span class="loading loading-dots loading-sm"></span>
<% end %>
</div>

View File

@@ -0,0 +1,114 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe Trips::CalculateCountriesJob, type: :job do
describe '#perform' do
let(:user) { create(:user) }
let(:trip) { create(:trip, user: user) }
let(:distance_unit) { 'km' }
let(:points) do
[
create(:point, user: user, country_name: 'Germany', timestamp: trip.started_at.to_i + 1.hour),
create(:point, user: user, country_name: 'France', timestamp: trip.started_at.to_i + 2.hours),
create(:point, user: user, country_name: 'Germany', timestamp: trip.started_at.to_i + 3.hours),
create(:point, user: user, country_name: 'Italy', timestamp: trip.started_at.to_i + 4.hours)
]
end
before do
points # Create the points
end
it 'finds the trip and calculates countries' do
expect(Trip).to receive(:find).with(trip.id).and_return(trip)
expect(trip).to receive(:calculate_countries)
expect(trip).to receive(:save!)
described_class.perform_now(trip.id, distance_unit)
end
it 'calculates unique countries from trip points' do
described_class.perform_now(trip.id, distance_unit)
trip.reload
expect(trip.visited_countries).to contain_exactly('Germany', 'France', 'Italy')
end
it 'broadcasts the update with correct parameters' do
expect(Turbo::StreamsChannel).to receive(:broadcast_update_to).with(
"trip_#{trip.id}",
target: "trip_countries",
partial: "trips/countries",
locals: { trip: trip, distance_unit: distance_unit }
)
described_class.perform_now(trip.id, distance_unit)
end
context 'when trip has no points' do
let(:trip_without_points) { create(:trip, user: user) }
it 'sets visited_countries to empty array' do
trip_without_points.points.destroy_all
described_class.perform_now(trip_without_points.id, distance_unit)
trip_without_points.reload
expect(trip_without_points.visited_countries).to eq([])
end
end
context 'when points have nil country names' do
let(:points_with_nil_countries) do
[
create(:point, user: user, country_name: 'Germany', timestamp: trip.started_at.to_i + 1.hour),
create(:point, user: user, country_name: nil, timestamp: trip.started_at.to_i + 2.hours),
create(:point, user: user, country_name: 'France', timestamp: trip.started_at.to_i + 3.hours)
]
end
before do
# Remove existing points and create new ones with nil countries
Point.where(user: user).destroy_all
points_with_nil_countries
end
it 'filters out nil country names' do
described_class.perform_now(trip.id, distance_unit)
trip.reload
expect(trip.visited_countries).to contain_exactly('Germany', 'France')
end
end
context 'when trip is not found' do
it 'raises ActiveRecord::RecordNotFound' do
expect {
described_class.perform_now(999999, distance_unit)
}.to raise_error(ActiveRecord::RecordNotFound)
end
end
context 'when distance_unit is different' do
let(:distance_unit) { 'mi' }
it 'passes the correct distance_unit to broadcast' do
expect(Turbo::StreamsChannel).to receive(:broadcast_update_to).with(
"trip_#{trip.id}",
target: "trip_countries",
partial: "trips/countries",
locals: { trip: trip, distance_unit: 'mi' }
)
described_class.perform_now(trip.id, distance_unit)
end
end
describe 'queue configuration' do
it 'uses the trips queue' do
expect(described_class.queue_name).to eq('trips')
end
end
end
end

View File

@@ -26,34 +26,6 @@ RSpec.describe Trip, type: :model do
trip.save
end
end
context 'when DawarichSettings.store_geodata? is enabled' do
before do
allow(DawarichSettings).to receive(:store_geodata?).and_return(true)
end
it 'sets the countries' do
expect(trip.countries).to eq(trip.points.pluck(:country).uniq.compact)
end
end
end
describe '#countries' do
let(:user) { create(:user) }
let(:trip) { create(:trip, user:) }
let(:points) do
create_list(
:point,
25,
:reverse_geocoded,
user:,
timestamp: (trip.started_at.to_i..trip.ended_at.to_i).to_a.sample
)
end
it 'returns the unique countries of the points' do
expect(trip.countries).to eq(trip.points.pluck(:country).uniq.compact)
end
end
describe '#photo_previews' do