diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a549e1c..bf1babf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,13 +37,14 @@ Also, after updating to this version, Dawarich will start a huge background job ## Added - Map page now has a button to go to the previous and next day. #296 #631 #904 +- Clicking on number of countries and cities in stats cards now opens a modal with a list of countries and cities visited in that year. ## 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. #619 - Stats cards now show the last update time. #733 - Visit card now shows buttons to confirm or decline a visit only if it's not confirmed or declined yet. -- Distance unit is now being stored in the user settings. You can choose between kilometers and miles, default is kilometers. The setting is accessible in the user settings -> Maps -> Distance Unit. You might want to recalculate your stats after changing the unit. +- Distance unit is now being stored in the user settings. You can choose between kilometers and miles, default is kilometers. The setting is accessible in the user settings -> Maps -> Distance Unit. You might want to recalculate your stats after changing the unit. #1126 - Fog of war is now being displayed as lines instead of dots. Thanks to @MeijiRestored! ## Fixed @@ -53,6 +54,7 @@ Also, after updating to this version, Dawarich will start a huge background job - `rake points:migrate_to_lonlat` should work properly now. #1083 #1161 - PostGIS extension is now being enabled only if it's not already enabled. #1186 - Fixed a bug where visits were returning into Suggested state after being confirmed or declined. #848 +- If no points are found for a month during stats calculation, stats are now being deleted instead of being left empty. #1066 #406 ## Removed diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 94c4a8a7..47d40698 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -40,7 +40,32 @@ module ApplicationHelper data[:cities].flatten!.uniq! data[:countries].flatten!.uniq! - "#{data[:countries].count} countries, #{data[:cities].count} cities" + grouped_by_country = {} + stats.select { _1.year == year }.each do |stat| + stat.toponyms.flatten.each do |toponym| + country = toponym['country'] + next unless country.present? + + grouped_by_country[country] ||= [] + + if toponym['cities'].present? + toponym['cities'].each do |city_data| + city = city_data['city'] + grouped_by_country[country] << city if city.present? + end + end + end + end + + grouped_by_country.transform_values!(&:uniq) + + { + countries_count: data[:countries].count, + cities_count: data[:cities].count, + grouped_by_country: grouped_by_country.transform_values(&:sort).sort.to_h, + year: year, + modal_id: "countries_cities_modal_#{year}" + } end def countries_and_cities_stat_for_month(stat) diff --git a/app/helpers/country_flag_helper.rb b/app/helpers/country_flag_helper.rb new file mode 100644 index 00000000..cfa711f0 --- /dev/null +++ b/app/helpers/country_flag_helper.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module CountryFlagHelper + def country_flag(country_name) + country_code = country_to_code(country_name) + return "" unless country_code + + # Convert country code to regional indicator symbols (flag emoji) + country_code.upcase.each_char.map { |c| (c.ord + 127397).chr(Encoding::UTF_8) }.join + end + + + private + + def country_to_code(country_name) + mapping = Country.names_to_iso_a2 + + return mapping[country_name] if mapping[country_name] + + mapping.each do |name, code| + return code if country_name.downcase == name.downcase + return code if country_name.downcase.include?(name.downcase) || name.downcase.include?(country_name.downcase) + end + + nil + end +end diff --git a/app/models/concerns/distanceable.rb b/app/models/concerns/distanceable.rb index a9aad852..7ddc190d 100644 --- a/app/models/concerns/distanceable.rb +++ b/app/models/concerns/distanceable.rb @@ -16,8 +16,8 @@ module Distanceable private def calculate_distance_for_relation(unit) - unless DISTANCE_UNITS.key?(unit.to_sym) - raise ArgumentError, "Invalid unit. Supported units are: #{DISTANCE_UNITS.keys.join(', ')}" + unless ::DISTANCE_UNITS.key?(unit.to_sym) + raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" end distance_in_meters = connection.select_value(<<-SQL.squish) @@ -40,12 +40,12 @@ module Distanceable WHERE prev_lonlat IS NOT NULL SQL - distance_in_meters.to_f / DISTANCE_UNITS[unit.to_sym] + distance_in_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] end def calculate_distance_for_array(points, unit = :km) - unless DISTANCE_UNITS.key?(unit.to_sym) - raise ArgumentError, "Invalid unit. Supported units are: #{DISTANCE_UNITS.keys.join(', ')}" + unless ::DISTANCE_UNITS.key?(unit.to_sym) + raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" end return 0 if points.length < 2 @@ -58,13 +58,13 @@ module Distanceable ) end - total_meters.to_f / DISTANCE_UNITS[unit.to_sym] + total_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] end end def distance_to(other_point, unit = :km) - unless DISTANCE_UNITS.key?(unit.to_sym) - raise ArgumentError, "Invalid unit. Supported units are: #{DISTANCE_UNITS.keys.join(', ')}" + unless ::DISTANCE_UNITS.key?(unit.to_sym) + raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" end # Extract coordinates based on what type other_point is @@ -80,7 +80,7 @@ module Distanceable SQL # Convert to requested unit - distance_in_meters.to_f / DISTANCE_UNITS[unit.to_sym] + distance_in_meters.to_f / ::DISTANCE_UNITS[unit.to_sym] end private diff --git a/app/models/concerns/nearable.rb b/app/models/concerns/nearable.rb index b217ac12..66ff990a 100644 --- a/app/models/concerns/nearable.rb +++ b/app/models/concerns/nearable.rb @@ -11,12 +11,12 @@ module Nearable def near(*args) latitude, longitude, radius, unit = extract_coordinates_and_options(*args) - unless DISTANCE_UNITS.key?(unit.to_sym) - raise ArgumentError, "Invalid unit. Supported units are: #{DISTANCE_UNITS.keys.join(', ')}" + unless ::DISTANCE_UNITS.key?(unit.to_sym) + raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" end # Convert radius to meters for ST_DWithin - radius_in_meters = radius * DISTANCE_UNITS[unit.to_sym] + radius_in_meters = radius * ::DISTANCE_UNITS[unit.to_sym] # Create a point from the given coordinates point = "SRID=4326;POINT(#{longitude} #{latitude})" @@ -33,12 +33,12 @@ module Nearable def with_distance(*args) latitude, longitude, unit = extract_coordinates_and_options(*args) - unless DISTANCE_UNITS.key?(unit.to_sym) - raise ArgumentError, "Invalid unit. Supported units are: #{DISTANCE_UNITS.keys.join(', ')}" + unless ::DISTANCE_UNITS.key?(unit.to_sym) + raise ArgumentError, "Invalid unit. Supported units are: #{::DISTANCE_UNITS.keys.join(', ')}" end point = "SRID=4326;POINT(#{longitude} #{latitude})" - conversion_factor = 1.0 / DISTANCE_UNITS[unit.to_sym] + conversion_factor = 1.0 / ::DISTANCE_UNITS[unit.to_sym] select(<<-SQL.squish) #{table_name}.*, diff --git a/app/models/country.rb b/app/models/country.rb index fe649360..2cc5d4b7 100644 --- a/app/models/country.rb +++ b/app/models/country.rb @@ -8,4 +8,8 @@ class Country < ApplicationRecord .select(:id, :name, :iso_a2, :iso_a3) .first end + + def self.names_to_iso_a2 + pluck(:name, :iso_a2).to_h + end end diff --git a/app/services/areas/visits/create.rb b/app/services/areas/visits/create.rb index 76eb2575..6c5faf63 100644 --- a/app/services/areas/visits/create.rb +++ b/app/services/areas/visits/create.rb @@ -32,9 +32,9 @@ class Areas::Visits::Create def area_points(area) area_radius = if user.safe_settings.distance_unit == :km - area.radius / DISTANCE_UNITS[:km] + area.radius / ::DISTANCE_UNITS[:km] else - area.radius / DISTANCE_UNITS[user.safe_settings.distance_unit.to_sym] + area.radius / ::DISTANCE_UNITS[user.safe_settings.distance_unit.to_sym] end points = Point.where(user_id: user.id) diff --git a/app/services/stats/calculate_month.rb b/app/services/stats/calculate_month.rb index b303d39f..e9d6d64d 100644 --- a/app/services/stats/calculate_month.rb +++ b/app/services/stats/calculate_month.rb @@ -8,7 +8,11 @@ class Stats::CalculateMonth end def call - return if points.empty? + if points.empty? + destroy_month_stats(year, month) + + return + end update_month_stats(year, month) rescue StandardError => e @@ -66,4 +70,8 @@ class Stats::CalculateMonth content: "#{error.message}, stacktrace: #{error.backtrace.join("\n")}" ).call end + + def destroy_month_stats(year, month) + Stat.where(year:, month:, user:).destroy_all + end end diff --git a/app/views/imports/index.html.erb b/app/views/imports/index.html.erb index 0d2f480e..3fde0ef3 100644 --- a/app/views/imports/index.html.erb +++ b/app/views/imports/index.html.erb @@ -41,7 +41,9 @@ Name Imported points - Reverse geocoded points + <% if DawarichSettings.store_geodata? %> + Reverse geocoded points + <% end %> Created at @@ -65,9 +67,11 @@ <%= number_with_delimiter import.processed %> - - <%= number_with_delimiter import.reverse_geocoded_points_count %> - + <% if DawarichSettings.store_geodata? %> + + <%= number_with_delimiter import.reverse_geocoded_points_count %> + + <% end %> <%= human_datetime(import.created_at) %> <% end %> diff --git a/app/views/stats/index.html.erb b/app/views/stats/index.html.erb index 4eea46ed..bac6e0bd 100644 --- a/app/views/stats/index.html.erb +++ b/app/views/stats/index.html.erb @@ -44,7 +44,41 @@

<% if DawarichSettings.reverse_geocoding_enabled? %>
- <%= countries_and_cities_stat_for_year(year, stats) %> + <% location_data = countries_and_cities_stat_for_year(year, stats) %> + <%= link_to "#{location_data[:countries_count]} countries, #{location_data[:cities_count]} cities", + "##{location_data[:modal_id]}", + class: "link link-primary", + onclick: "document.getElementById('#{location_data[:modal_id]}').checked = true" %> + + +
+ + +
<% end %> <%= column_chart( diff --git a/spec/services/stats/calculate_month_spec.rb b/spec/services/stats/calculate_month_spec.rb index 7f24adc3..83069d08 100644 --- a/spec/services/stats/calculate_month_spec.rb +++ b/spec/services/stats/calculate_month_spec.rb @@ -14,6 +14,16 @@ RSpec.describe Stats::CalculateMonth do it 'does not create stats' do expect { calculate_stats }.not_to(change { Stat.count }) end + + context 'when stats already exist for the month' do + before do + create(:stat, user: user, year: year, month: month) + end + + it 'deletes existing stats for that month' do + expect { calculate_stats }.to change { Stat.count }.by(-1) + end + end end context 'when there are points' do