Move distance unit settings to user settings

This commit is contained in:
Eugene Burmakin 2025-05-17 20:35:38 +02:00
parent 630c813f0b
commit 06aee05602
24 changed files with 46 additions and 55 deletions

View file

@ -28,7 +28,6 @@ services:
APPLICATION_HOSTS: localhost APPLICATION_HOSTS: localhost
TIME_ZONE: Europe/London TIME_ZONE: Europe/London
APPLICATION_PROTOCOL: http APPLICATION_PROTOCOL: http
DISTANCE_UNIT: km
PROMETHEUS_EXPORTER_ENABLED: false PROMETHEUS_EXPORTER_ENABLED: false
PROMETHEUS_EXPORTER_HOST: 0.0.0.0 PROMETHEUS_EXPORTER_HOST: 0.0.0.0
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394

View file

@ -4,4 +4,3 @@ DATABASE_PASSWORD=password
DATABASE_NAME=dawarich_development DATABASE_NAME=dawarich_development
DATABASE_PORT=5432 DATABASE_PORT=5432
REDIS_URL=redis://localhost:6379/1 REDIS_URL=redis://localhost:6379/1
DISTANCE_UNIT='km'

View file

@ -36,7 +36,9 @@ class MapController < ApplicationController
@distance ||= 0 @distance ||= 0
@coordinates.each_cons(2) do @coordinates.each_cons(2) do
@distance += Geocoder::Calculations.distance_between([_1[0], _1[1]], [_2[0], _2[1]], units: DISTANCE_UNIT) @distance += Geocoder::Calculations.distance_between(
[_1[0], _1[1]], [_2[0], _2[1]], units: current_user.safe_settings.distance_unit
)
end end
@distance.round(1) @distance.round(1)

View file

@ -51,7 +51,7 @@ module ApplicationHelper
end end
def year_distance_stat(year, user) def year_distance_stat(year, user)
# In km or miles, depending on the application settings (DISTANCE_UNIT) # In km or miles, depending on the user.safe_settings.distance_unit
Stat.year_distance(year, user).sum { _1[1] } Stat.year_distance(year, user).sum { _1[1] }
end end
@ -76,7 +76,7 @@ module ApplicationHelper
def sidebar_distance(distance) def sidebar_distance(distance)
return unless distance return unless distance
"#{distance} #{DISTANCE_UNIT}" "#{distance} #{current_user.safe_settings.distance_unit}"
end end
def sidebar_points(points) def sidebar_points(points)

View file

@ -3,14 +3,6 @@
module Distanceable module Distanceable
extend ActiveSupport::Concern extend ActiveSupport::Concern
DISTANCE_UNITS = {
km: 1000, # to meters
mi: 1609.34, # to meters
m: 1, # already in meters
ft: 0.3048, # to meters
yd: 0.9144 # to meters
}.freeze
module ClassMethods module ClassMethods
def total_distance(points = nil, unit = :km) def total_distance(points = nil, unit = :km)
# Handle method being called directly on relation vs with array # Handle method being called directly on relation vs with array

View file

@ -3,14 +3,6 @@
module Nearable module Nearable
extend ActiveSupport::Concern extend ActiveSupport::Concern
DISTANCE_UNITS = {
km: 1000, # to meters
mi: 1609.34, # to meters
m: 1, # already in meters
ft: 0.3048, # to meters
yd: 0.9144 # to meters
}.freeze
class_methods do class_methods do
# It accepts an array of coordinates [latitude, longitude] # It accepts an array of coordinates [latitude, longitude]
# and an optional radius and distance unit # and an optional radius and distance unit

View file

@ -37,7 +37,7 @@ class Stat < ApplicationRecord
def calculate_daily_distances(monthly_points) def calculate_daily_distances(monthly_points)
timespan.to_a.map.with_index(1) do |day, index| timespan.to_a.map.with_index(1) do |day, index|
daily_points = filter_points_for_day(monthly_points, day) daily_points = filter_points_for_day(monthly_points, day)
distance = Point.total_distance(daily_points, DISTANCE_UNIT) distance = Point.total_distance(daily_points, user.safe_settings.distance_unit)
[index, distance.round(2)] [index, distance.round(2)]
end end
end end

View file

@ -39,7 +39,7 @@ class Trip < ApplicationRecord
end end
def calculate_distance def calculate_distance
distance = Point.total_distance(points, DISTANCE_UNIT) distance = Point.total_distance(points, user.safe_settings.distance_unit)
self.distance = distance.round self.distance = distance.round
end end

View file

@ -49,7 +49,7 @@ class User < ApplicationRecord
end end
def total_distance def total_distance
# In km or miles, depending on the application settings (DISTANCE_UNIT) # In km or miles, depending on user.safe_settings.distance_unit
stats.sum(:distance) stats.sum(:distance)
end end

View file

@ -31,14 +31,14 @@ class Areas::Visits::Create
def area_points(area) def area_points(area)
area_radius = area_radius =
if ::DISTANCE_UNIT == :km if user.safe_settings.distance_unit == :km
area.radius / 1000.0 area.radius / DISTANCE_UNITS[:km]
else else
area.radius / 1609.344 area.radius / DISTANCE_UNITS[user.safe_settings.distance_unit.to_sym]
end end
points = Point.where(user_id: user.id) points = Point.where(user_id: user.id)
.near([area.latitude, area.longitude], area_radius, DISTANCE_UNIT) .near([area.latitude, area.longitude], area_radius, user.safe_settings.distance_unit)
.order(timestamp: :asc) .order(timestamp: :asc)
# check if all points within the area are assigned to a visit # check if all points within the area are assigned to a visit

View file

@ -24,7 +24,8 @@ class Users::SafeSettings
immich_api_key: immich_api_key, immich_api_key: immich_api_key,
photoprism_url: photoprism_url, photoprism_url: photoprism_url,
photoprism_api_key: photoprism_api_key, photoprism_api_key: photoprism_api_key,
maps: maps maps: maps,
distance_unit: distance_unit
} }
end end
# rubocop:enable Metrics/MethodLength # rubocop:enable Metrics/MethodLength
@ -90,4 +91,8 @@ class Users::SafeSettings
def maps def maps
settings['maps'] || {} settings['maps'] || {}
end end
def distance_unit
settings['distance_unit'] || 'km'
end
end end

View file

@ -12,7 +12,7 @@
<%= link_to '🔄', update_year_month_stats_path(stat.year, stat.month), data: { turbo_method: :put }, class: 'text-sm text-gray-500 hover:underline' %> <%= link_to '🔄', update_year_month_stats_path(stat.year, stat.month), data: { turbo_method: :put }, class: 'text-sm text-gray-500 hover:underline' %>
</div> </div>
</div> </div>
<p><%= number_with_delimiter stat.distance %><%= DISTANCE_UNIT %></p> <p><%= number_with_delimiter stat.distance %><%= current_user.safe_settings.distance_unit %></p>
<% if DawarichSettings.reverse_geocoding_enabled? %> <% if DawarichSettings.reverse_geocoding_enabled? %>
<div class="card-actions justify-end"> <div class="card-actions justify-end">
<%= countries_and_cities_stat_for_month(stat) %> <%= countries_and_cities_stat_for_month(stat) %>
@ -22,7 +22,7 @@
<%= column_chart( <%= column_chart(
stat.daily_distance, stat.daily_distance,
height: '100px', height: '100px',
suffix: " #{DISTANCE_UNIT}", suffix: " #{current_user.safe_settings.distance_unit}",
xtitle: 'Days', xtitle: 'Days',
ytitle: 'Distance' ytitle: 'Distance'
) %> ) %>

View file

@ -6,7 +6,7 @@
<%= column_chart( <%= column_chart(
Stat.year_distance(year, current_user), Stat.year_distance(year, current_user),
height: '200px', height: '200px',
suffix: " #{DISTANCE_UNIT}", suffix: " #{current_user.safe_settings.distance_unit}",
xtitle: 'Days', xtitle: 'Days',
ytitle: 'Distance' ytitle: 'Distance'
) %> ) %>

View file

@ -4,7 +4,7 @@
<div class="stats stats-vertical lg:stats-horizontal shadow w-full bg-base-200"> <div class="stats stats-vertical lg:stats-horizontal shadow w-full bg-base-200">
<div class="stat text-center"> <div class="stat text-center">
<div class="stat-value text-primary"> <div class="stat-value text-primary">
<%= number_with_delimiter(current_user.total_distance) %> <%= DISTANCE_UNIT %> <%= number_with_delimiter(current_user.total_distance) %> <%= current_user.safe_settings.distance_unit %>
</div> </div>
<div class="stat-title">Total distance</div> <div class="stat-title">Total distance</div>
</div> </div>
@ -39,7 +39,7 @@
</h2> </h2>
<p> <p>
<% cache [current_user, 'year_distance_stat', year], skip_digest: true do %> <% cache [current_user, 'year_distance_stat', year], skip_digest: true do %>
<%= number_with_delimiter year_distance_stat(year, current_user) %><%= DISTANCE_UNIT %> <%= number_with_delimiter year_distance_stat(year, current_user) %><%= current_user.safe_settings.distance_unit %>
<% end %> <% end %>
</p> </p>
<% if DawarichSettings.reverse_geocoding_enabled? %> <% if DawarichSettings.reverse_geocoding_enabled? %>
@ -50,7 +50,7 @@
<%= column_chart( <%= column_chart(
Stat.year_distance(year, current_user), Stat.year_distance(year, current_user),
height: '200px', height: '200px',
suffix: " #{DISTANCE_UNIT}", suffix: " #{current_user.safe_settings.distance_unit}",
xtitle: 'Days', xtitle: 'Days',
ytitle: 'Distance' ytitle: 'Distance'
) %> ) %>

View file

@ -1,10 +1,10 @@
<% if trip.countries.any? %> <% if trip.countries.any? %>
<p class="text-md text-base-content/60"> <p class="text-md text-base-content/60">
<%= "#{trip.countries.join(', ')} (#{trip.distance} #{DISTANCE_UNIT})" %> <%= "#{trip.countries.join(', ')} (#{trip.distance} #{current_user.safe_settings.distance_unit})" %>
</p> </p>
<% elsif trip.visited_countries.present? %> <% elsif trip.visited_countries.present? %>
<p class="text-md text-base-content/60"> <p class="text-md text-base-content/60">
<%= "#{trip.visited_countries.join(', ')} (#{trip.distance} #{DISTANCE_UNIT})" %> <%= "#{trip.visited_countries.join(', ')} (#{trip.distance} #{current_user.safe_settings.distance_unit})" %>
</p> </p>
<% else %> <% else %>
<p class="text-md text-base-content/60"> <p class="text-md text-base-content/60">

View file

@ -1,5 +1,5 @@
<% if trip.distance.present? %> <% if trip.distance.present? %>
<span class="text-md"><%= trip.distance %> <%= DISTANCE_UNIT %></span> <span class="text-md"><%= trip.distance %> <%= current_user.safe_settings.distance_unit %></span>
<% else %> <% else %>
<span class="text-md">Calculating...</span> <span class="text-md">Calculating...</span>
<span class="loading loading-dots loading-sm"></span> <span class="loading loading-dots loading-sm"></span>

View file

@ -5,7 +5,7 @@
<span class="hover:underline"><%= trip.name %></span> <span class="hover:underline"><%= trip.name %></span>
</h2> </h2>
<p class="text-sm text-gray-600 text-center"> <p class="text-sm text-gray-600 text-center">
<%= "#{human_date(trip.started_at)} #{human_date(trip.ended_at)}, #{trip.distance} #{DISTANCE_UNIT}" %> <%= "#{human_date(trip.started_at)} #{human_date(trip.ended_at)}, #{trip.distance} #{current_user.safe_settings.distance_unit}" %>
</p> </p>
<div style="width: 100%; aspect-ratio: 1/1;" <div style="width: 100%; aspect-ratio: 1/1;"

View file

@ -5,6 +5,14 @@ SELF_HOSTED = ENV.fetch('SELF_HOSTED', 'true') == 'true'
MIN_MINUTES_SPENT_IN_CITY = ENV.fetch('MIN_MINUTES_SPENT_IN_CITY', 60).to_i MIN_MINUTES_SPENT_IN_CITY = ENV.fetch('MIN_MINUTES_SPENT_IN_CITY', 60).to_i
DISTANCE_UNIT = ENV.fetch('DISTANCE_UNIT', 'km').to_sym DISTANCE_UNIT = ENV.fetch('DISTANCE_UNIT', 'km').to_sym
DISTANCE_UNITS = {
km: 1000, # to meters
mi: 1609.34, # to meters
m: 1, # already in meters
ft: 0.3048, # to meters
yd: 0.9144 # to meters
}.freeze
APP_VERSION = File.read('.app_version').strip APP_VERSION = File.read('.app_version').strip
# Reverse geocoding settings # Reverse geocoding settings

View file

@ -2,7 +2,7 @@
settings = { settings = {
timeout: 5, timeout: 5,
units: DISTANCE_UNIT, units: :km,
cache: Redis.new, cache: Redis.new,
always_raise: :all, always_raise: :all,
use_https: PHOTON_API_USE_HTTPS, use_https: PHOTON_API_USE_HTTPS,

View file

@ -64,7 +64,6 @@ services:
APPLICATION_HOSTS: localhost,::1,127.0.0.1 APPLICATION_HOSTS: localhost,::1,127.0.0.1
TIME_ZONE: Europe/London TIME_ZONE: Europe/London
APPLICATION_PROTOCOL: http APPLICATION_PROTOCOL: http
DISTANCE_UNIT: km
PROMETHEUS_EXPORTER_ENABLED: false PROMETHEUS_EXPORTER_ENABLED: false
PROMETHEUS_EXPORTER_HOST: 0.0.0.0 PROMETHEUS_EXPORTER_HOST: 0.0.0.0
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394
@ -119,7 +118,6 @@ services:
APPLICATION_HOSTS: localhost,::1,127.0.0.1 APPLICATION_HOSTS: localhost,::1,127.0.0.1
BACKGROUND_PROCESSING_CONCURRENCY: 10 BACKGROUND_PROCESSING_CONCURRENCY: 10
APPLICATION_PROTOCOL: http APPLICATION_PROTOCOL: http
DISTANCE_UNIT: km
PROMETHEUS_EXPORTER_ENABLED: false PROMETHEUS_EXPORTER_ENABLED: false
PROMETHEUS_EXPORTER_HOST: dawarich_app PROMETHEUS_EXPORTER_HOST: dawarich_app
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394

View file

@ -65,7 +65,6 @@ services:
APPLICATION_HOSTS: localhost APPLICATION_HOSTS: localhost
TIME_ZONE: Europe/London TIME_ZONE: Europe/London
APPLICATION_PROTOCOL: http APPLICATION_PROTOCOL: http
DISTANCE_UNIT: km
PROMETHEUS_EXPORTER_ENABLED: false PROMETHEUS_EXPORTER_ENABLED: false
PROMETHEUS_EXPORTER_HOST: 0.0.0.0 PROMETHEUS_EXPORTER_HOST: 0.0.0.0
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394
@ -118,7 +117,6 @@ services:
APPLICATION_HOSTS: localhost APPLICATION_HOSTS: localhost
BACKGROUND_PROCESSING_CONCURRENCY: 10 BACKGROUND_PROCESSING_CONCURRENCY: 10
APPLICATION_PROTOCOL: http APPLICATION_PROTOCOL: http
DISTANCE_UNIT: km
PROMETHEUS_EXPORTER_ENABLED: false PROMETHEUS_EXPORTER_ENABLED: false
PROMETHEUS_EXPORTER_HOST: dawarich_app PROMETHEUS_EXPORTER_HOST: dawarich_app
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394

View file

@ -100,8 +100,6 @@ spec:
value: "dawarich.example.com, localhost" value: "dawarich.example.com, localhost"
- name: APPLICATION_PROTOCOL - name: APPLICATION_PROTOCOL
value: http value: http
- name: DISTANCE_UNIT
value: km
- name: PHOTON_API_HOST - name: PHOTON_API_HOST
value: photon.komoot.io value: photon.komoot.io
- name: PHOTON_API_USE_HTTPS - name: PHOTON_API_USE_HTTPS
@ -109,7 +107,7 @@ spec:
- name: RAILS_MIN_THREADS - name: RAILS_MIN_THREADS
value: "5" value: "5"
- name: RAILS_MAX_THREADS - name: RAILS_MAX_THREADS
value: "10" value: "10"
image: freikin/dawarich:0.16.4 image: freikin/dawarich:0.16.4
imagePullPolicy: Always imagePullPolicy: Always
volumeMounts: volumeMounts:
@ -149,7 +147,7 @@ spec:
- name: RAILS_MIN_THREADS - name: RAILS_MIN_THREADS
value: "5" value: "5"
- name: RAILS_MAX_THREADS - name: RAILS_MAX_THREADS
value: "10" value: "10"
- name: BACKGROUND_PROCESSING_CONCURRENCY - name: BACKGROUND_PROCESSING_CONCURRENCY
value: "20" value: "20"
- name: APPLICATION_HOST - name: APPLICATION_HOST
@ -158,8 +156,6 @@ spec:
value: "dawarich.example.com, localhost" value: "dawarich.example.com, localhost"
- name: APPLICATION_PROTOCOL - name: APPLICATION_PROTOCOL
value: http value: http
- name: DISTANCE_UNIT
value: km
- name: PHOTON_API_HOST - name: PHOTON_API_HOST
value: photon.komoot.io value: photon.komoot.io
- name: PHOTON_API_USE_HTTPS - name: PHOTON_API_USE_HTTPS

View file

@ -44,8 +44,6 @@ RSpec.describe Stats::CalculateMonth do
end end
context 'when units are kilometers' do context 'when units are kilometers' do
before { stub_const('DISTANCE_UNIT', :km) }
it 'creates stats' do it 'creates stats' do
expect { calculate_stats }.to change { Stat.count }.by(1) expect { calculate_stats }.to change { Stat.count }.by(1)
end end
@ -72,7 +70,9 @@ RSpec.describe Stats::CalculateMonth do
end end
context 'when units are miles' do context 'when units are miles' do
before { stub_const('DISTANCE_UNIT', :mi) } before do
user.update(settings: { distance_unit: 'mi' })
end
it 'creates stats' do it 'creates stats' do
expect { calculate_stats }.to change { Stat.count }.by(1) expect { calculate_stats }.to change { Stat.count }.by(1)

View file

@ -23,7 +23,8 @@ RSpec.describe Users::SafeSettings do
immich_api_key: nil, immich_api_key: nil,
photoprism_url: nil, photoprism_url: nil,
photoprism_api_key: nil, photoprism_api_key: nil,
maps: {} maps: {},
distance_unit: 'km'
} }
) )
end end
@ -68,7 +69,8 @@ RSpec.describe Users::SafeSettings do
immich_api_key: 'immich-key', immich_api_key: 'immich-key',
photoprism_url: 'https://photoprism.example.com', photoprism_url: 'https://photoprism.example.com',
photoprism_api_key: 'photoprism-key', photoprism_api_key: 'photoprism-key',
maps: { 'name' => 'custom', 'url' => 'https://custom.example.com' } maps: { 'name' => 'custom', 'url' => 'https://custom.example.com' },
distance_unit: 'km'
} }
) )
end end