Some frontend fixes

This commit is contained in:
Eugene Burmakin 2025-05-15 18:23:24 +02:00
parent 5fbc1fb884
commit a48cff098b
12 changed files with 76 additions and 72 deletions

View file

@ -5,24 +5,32 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/). and this project adheres to [Semantic Versioning](http://semver.org/).
# 0.26.1 - 2025-05-12 # 0.26.1 - 2025-05-15
Geodata on demand ## Geodata on demand
- [x] Introduce a `STORE_GEODATA` environment variable to control whether to store geodata in the database. This release introduces a new environment variable `STORE_GEODATA` to control whether to store geodata in the database.
- [ ] When `STORE_GEODATA` is disabled, each feature that uses geodata will now make a direct request to the geocoding service to calculate required data.
Geodata is being used: When `STORE_GEODATA` is disabled, each feature that uses geodata will now make a direct request to the geocoding service to calculate required data.
- [ ] Fetching places geodata
- [x] Fetching countries for a trip Geodata is being used:
- [x] Suggesting place name for a visit
- [x] When `STORE_GEODATA` is disabled, points are not being reverse geocoded on creation. - Fetching places geodata
- [x] When `STORE_GEODATA` is disabled, countries for a trip are being pulled from the geocoding service. - Fetching countries for a trip
- [x] When `STORE_GEODATA` is enabled, points are being reverse geocoded upon creation and stored in the database. - Suggesting place name for a visit
- [ ] Each feature that uses geodata will check if an entity (point, place, etc.) has geodata stored in the database and use it if available. If not, it will make a direct request to the geocoding service to calculate required data.
If you prefer to keep the old behavior, you can set `STORE_GEODATA` to `true`. By default, starting this release, it's set to `false`.
If you're running your own Photon instance, you can safely set `STORE_GEODATA` to `false`, otherwise it'd be better to keep it enabled, because that way Dawarich will be using existing geodata for its calculations.
## Added
- Map page now has a button to go to the previous and next day. #296 #631 #904
## Changed ## Changed
- Reverse geocoding is now working as on-demand job instead of storing the result in the database. - Reverse geocoding is now working as on-demand job instead of storing the result in the database.
- Stats cards now show the last update time. #733
## Fixed ## Fixed

File diff suppressed because one or more lines are too long

View file

@ -5,7 +5,7 @@ class StatsController < ApplicationController
before_action :authenticate_active_user!, only: %i[update update_all] before_action :authenticate_active_user!, only: %i[update update_all]
def index def index
@stats = current_user.stats.group_by(&:year).sort.reverse @stats = current_user.stats.group_by(&:year).transform_values { |stats| stats.sort_by(&:updated_at).reverse }.sort.reverse
@points_total = current_user.tracked_points.count @points_total = current_user.tracked_points.count
@points_reverse_geocoded = current_user.total_reverse_geocoded_points @points_reverse_geocoded = current_user.total_reverse_geocoded_points
@points_reverse_geocoded_without_data = current_user.total_reverse_geocoded_points_without_data @points_reverse_geocoded_without_data = current_user.total_reverse_geocoded_points_without_data

View file

@ -15,9 +15,9 @@ class Api::PlaceSerializer
country: place.country, country: place.country,
source: place.source, source: place.source,
geodata: place.geodata, geodata: place.geodata,
reverse_geocoded_at: place.reverse_geocoded_at,
created_at: place.created_at, created_at: place.created_at,
updated_at: place.updated_at updated_at: place.updated_at,
reverse_geocoded_at: place.reverse_geocoded_at
} }
end end

View file

@ -21,10 +21,8 @@ class Trips::Countries
batches = split_into_batches(all_points, @batch_count) batches = split_into_batches(all_points, @batch_count)
threads_results = process_batches_in_threads(batches, total_points) threads_results = process_batches_in_threads(batches, total_points)
country_counts = merge_thread_results(threads_results)
log_results(country_counts, total_points) merge_thread_results(threads_results).uniq.compact
country_counts.sort_by { |_country, count| -count }.to_h
end end
private private
@ -39,10 +37,9 @@ class Trips::Countries
threads_results = [] threads_results = []
threads = [] threads = []
batches.each_with_index do |batch, batch_index| batches.each do |batch|
start_index = batch_index * batch.size + 1
threads << Thread.new do threads << Thread.new do
threads_results << process_batch(batch, start_index, total_points) threads_results << process_batch(batch)
end end
end end
@ -51,63 +48,32 @@ class Trips::Countries
end end
def merge_thread_results(threads_results) def merge_thread_results(threads_results)
country_counts = {} countries = []
threads_results.each do |result| threads_results.each do |result|
result.each do |country, count| countries.concat(result)
country_counts[country] ||= 0
country_counts[country] += count
end
end end
country_counts countries
end end
def log_results(country_counts, total_points) def process_batch(points)
total_counted = country_counts.values.sum points.map do |point|
Rails.logger.info("Processed #{total_points} points and found #{country_counts.size} countries") country_code = geocode_point(point)
Rails.logger.info("Points counted: #{total_counted} out of #{total_points}")
end
def process_batch(points, start_index, total_points)
country_counts = {}
points.each_with_index do |point, idx|
current_index = start_index + idx
country_code = geocode_point(point, current_index, total_points)
next unless country_code next unless country_code
country_counts[country_code] ||= 0 country_code
country_counts[country_code] += 1 end
end end
country_counts def geocode_point(point)
end
def geocode_point(point, current_index, total_points)
lonlat = point.lonlat lonlat = point.lonlat
return nil unless lonlat return nil unless lonlat
latitude = lonlat.y latitude = lonlat.y
longitude = lonlat.x longitude = lonlat.x
log_processing_point(current_index, total_points, latitude, longitude) fetch_country_code(latitude, longitude)
country_code = fetch_country_code(latitude, longitude)
log_found_country(country_code, latitude, longitude) if country_code
country_code
end
def log_processing_point(current_index, total_points, latitude, longitude)
thread_id = Thread.current.object_id
Rails.logger.info(
"Thread #{thread_id}: Processing point #{current_index} of #{total_points}: lat=#{latitude}, lon=#{longitude}"
)
end
def log_found_country(country_code, latitude, longitude)
thread_id = Thread.current.object_id
Rails.logger.info("Thread #{thread_id}: Found country: #{country_code} for point at #{latitude}, #{longitude}")
end end
def fetch_country_code(latitude, longitude) def fetch_country_code(latitude, longitude)

View file

@ -5,18 +5,36 @@
<div class="flex flex-col space-y-4 mb-4 w-full"> <div class="flex flex-col space-y-4 mb-4 w-full">
<%= form_with url: map_path(import_id: params[:import_id]), method: :get do |f| %> <%= form_with url: map_path(import_id: params[:import_id]), method: :get do |f| %>
<div class="flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4 sm:items-end"> <div class="flex flex-col space-y-4 sm:flex-row sm:space-y-0 sm:space-x-4 sm:items-end">
<div class="w-full sm:w-2/12 md:w-1/12 lg:w-3/12"> <div class="w-full sm:w-1/12 md:w-1/12 lg:w-1/12">
<div class="flex flex-col space-y-2">
<span class="tooltip" data-tip="<%= human_date(@start_at - 1.day) %>">
<%= link_to map_path(start_at: @start_at - 1.day, end_at: @end_at - 1.day, import_id: params[:import_id]), class: "btn btn-neutral hover:btn-ghost w-full" do %>
◀️
<% end %>
</span>
</div>
</div>
<div class="w-full sm:w-2/12 md:w-1/12 lg:w-2/12">
<div class="flex flex-col space-y-2"> <div class="flex flex-col space-y-2">
<%= f.label :start_at, class: "text-sm font-semibold" %> <%= f.label :start_at, class: "text-sm font-semibold" %>
<%= f.datetime_local_field :start_at, include_seconds: false, class: "input input-bordered hover:cursor-pointer hover:input-primary", value: @start_at %> <%= f.datetime_local_field :start_at, include_seconds: false, class: "input input-bordered hover:cursor-pointer hover:input-primary", value: @start_at %>
</div> </div>
</div> </div>
<div class="w-full sm:w-2/12 md:w-1/12 lg:w-3/12"> <div class="w-full sm:w-2/12 md:w-1/12 lg:w-2/12">
<div class="flex flex-col space-y-2"> <div class="flex flex-col space-y-2">
<%= f.label :end_at, class: "text-sm font-semibold" %> <%= f.label :end_at, class: "text-sm font-semibold" %>
<%= f.datetime_local_field :end_at, include_seconds: false, class: "input input-bordered hover:cursor-pointer hover:input-primary", value: @end_at %> <%= f.datetime_local_field :end_at, include_seconds: false, class: "input input-bordered hover:cursor-pointer hover:input-primary", value: @end_at %>
</div> </div>
</div> </div>
<div class="w-full sm:w-1/12 md:w-1/12 lg:w-1/12">
<div class="flex flex-col space-y-2">
<span class="tooltip" data-tip="<%= human_date(@start_at + 1.day) %>">
<%= link_to map_path(start_at: @start_at + 1.day, end_at: @end_at + 1.day, import_id: params[:import_id]), class: "btn btn-neutral hover:btn-ghost w-full" do %>
▶️
<% end %>
</span>
</div>
</div>
<div class="w-full sm:w-6/12 md:w-2/12 lg:w-1/12"> <div class="w-full sm:w-6/12 md:w-2/12 lg:w-1/12">
<div class="flex flex-col space-y-2"> <div class="flex flex-col space-y-2">
<%= f.submit "Search", class: "btn btn-primary hover:btn-info" %> <%= f.submit "Search", class: "btn btn-primary hover:btn-info" %>

View file

@ -7,11 +7,12 @@
<% end %> <% end %>
</h2> </h2>
<div class="flex items-center gap-2"> <div class="gap-2">
<%= link_to '[Update]', update_year_month_stats_path(stat.year, stat.month), data: { turbo_method: :put }, class: 'text-sm text-gray-500 hover:underline' %> <span class='text-xs text-gray-500'>Last update <%= human_date(stat.updated_at) %></span>
<%= 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><%= stat.distance %><%= DISTANCE_UNIT %></p> <p><%= number_with_delimiter stat.distance %><%= 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) %>

View file

@ -32,7 +32,10 @@
<%= link_to year, "/stats/#{year}", class: 'underline hover:no-underline' %> <%= link_to year, "/stats/#{year}", class: 'underline hover:no-underline' %>
<%= link_to '[Map]', map_url(year_timespan(year)), class: 'underline hover:no-underline' %> <%= link_to '[Map]', map_url(year_timespan(year)), class: 'underline hover:no-underline' %>
</div> </div>
<%= link_to '[Update]', update_year_month_stats_path(year, :all), data: { turbo_method: :put }, class: 'text-sm text-gray-500 hover:underline' %> <div class="gap-2">
<span class='text-xs text-gray-500'>Last updated: <%= human_date(stats.first.updated_at) %></span>
<%= link_to '🔄', update_year_month_stats_path(year, :all), data: { turbo_method: :put }, class: 'text-sm text-gray-500 hover:underline' %>
</div>
</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 %>

View file

@ -70,6 +70,7 @@ services:
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394
SECRET_KEY_BASE: 1234567890 SECRET_KEY_BASE: 1234567890
RAILS_LOG_TO_STDOUT: "true" RAILS_LOG_TO_STDOUT: "true"
STORE_GEODATA: "false"
logging: logging:
driver: "json-file" driver: "json-file"
options: options:
@ -124,6 +125,7 @@ services:
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394
SECRET_KEY_BASE: 1234567890 SECRET_KEY_BASE: 1234567890
RAILS_LOG_TO_STDOUT: "true" RAILS_LOG_TO_STDOUT: "true"
STORE_GEODATA: "false"
logging: logging:
driver: "json-file" driver: "json-file"
options: options:

View file

@ -70,6 +70,7 @@ services:
PROMETHEUS_EXPORTER_HOST: 0.0.0.0 PROMETHEUS_EXPORTER_HOST: 0.0.0.0
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394
SELF_HOSTED: "true" SELF_HOSTED: "true"
STORE_GEODATA: "false"
logging: logging:
driver: "json-file" driver: "json-file"
options: options:
@ -122,6 +123,7 @@ services:
PROMETHEUS_EXPORTER_HOST: dawarich_app PROMETHEUS_EXPORTER_HOST: dawarich_app
PROMETHEUS_EXPORTER_PORT: 9394 PROMETHEUS_EXPORTER_PORT: 9394
SELF_HOSTED: "true" SELF_HOSTED: "true"
STORE_GEODATA: "false"
logging: logging:
driver: "json-file" driver: "json-file"
options: options:

View file

@ -7,6 +7,7 @@ MIN_MINUTES_SPENT_IN_CITY=60
APPLICATION_HOSTS=dawarich.example.synology.me APPLICATION_HOSTS=dawarich.example.synology.me
TIME_ZONE=Europe/Berlin TIME_ZONE=Europe/Berlin
BACKGROUND_PROCESSING_CONCURRENCY=10 BACKGROUND_PROCESSING_CONCURRENCY=10
STORE_GEODATA=false
################################################################################### ###################################################################################
# Database # Database

View file

@ -9,8 +9,11 @@ RSpec.describe ReverseGeocoding::Points::FetchData do
context 'when Geocoder returns city and country' do context 'when Geocoder returns city and country' do
before do before do
allow(Geocoder).to receive(:search).and_return([double(city: 'City', country: 'Country', allow(Geocoder).to receive(:search).and_return(
data: { 'address' => 'Address' })]) [
double(city: 'City', country: 'Country',data: { 'address' => 'Address' })
]
)
end end
context 'when point does not have city and country' do context 'when point does not have city and country' do