From 605ceee820494ea53eecbccc85ccc19018e8828c Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 18 May 2025 00:15:25 +0200 Subject: [PATCH 1/4] Add modal to show countries and cities visited in a year --- CHANGELOG.md | 4 +- app/helpers/application_helper.rb | 30 ++- app/helpers/country_flag_helper.rb | 235 ++++++++++++++++++++ app/models/concerns/distanceable.rb | 18 +- app/models/concerns/nearable.rb | 12 +- app/services/areas/visits/create.rb | 4 +- app/services/stats/calculate_month.rb | 10 +- app/views/imports/index.html.erb | 12 +- app/views/stats/index.html.erb | 36 ++- config/initializers/01_constants.rb | 2 +- spec/services/stats/calculate_month_spec.rb | 10 + 11 files changed, 347 insertions(+), 26 deletions(-) create mode 100644 app/helpers/country_flag_helper.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a549e1c..35610bf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,10 +37,11 @@ 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. @@ -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..760509a6 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -40,7 +40,35 @@ module ApplicationHelper data[:cities].flatten!.uniq! data[:countries].flatten!.uniq! - "#{data[:countries].count} countries, #{data[:cities].count} cities" + # Group cities by country + 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 + + # Deduplicate cities for each country + grouped_by_country.transform_values!(&:uniq) + + # Return data for the template to use + { + 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..d5e4f5b5 --- /dev/null +++ b/app/helpers/country_flag_helper.rb @@ -0,0 +1,235 @@ +# 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 of country names to ISO 3166-1 alpha-2 codes + # This is a basic mapping - you might want to expand it based on your data + mapping = { + "Afghanistan" => "AF", + "Albania" => "AL", + "Algeria" => "DZ", + "Andorra" => "AD", + "Angola" => "AO", + "Antigua and Barbuda" => "AG", + "Argentina" => "AR", + "Armenia" => "AM", + "Australia" => "AU", + "Austria" => "AT", + "Azerbaijan" => "AZ", + "Bahamas" => "BS", + "Bahrain" => "BH", + "Bangladesh" => "BD", + "Barbados" => "BB", + "Belarus" => "BY", + "Belgium" => "BE", + "Belize" => "BZ", + "Benin" => "BJ", + "Bhutan" => "BT", + "Bolivia" => "BO", + "Bosnia and Herzegovina" => "BA", + "Botswana" => "BW", + "Brazil" => "BR", + "Brunei" => "BN", + "Bulgaria" => "BG", + "Burkina Faso" => "BF", + "Burundi" => "BI", + "Cabo Verde" => "CV", + "Cambodia" => "KH", + "Cameroon" => "CM", + "Canada" => "CA", + "Central African Republic" => "CF", + "Chad" => "TD", + "Chile" => "CL", + "China" => "CN", + "Colombia" => "CO", + "Comoros" => "KM", + "Congo (Brazzaville)" => "CG", + "Congo (Kinshasa)" => "CD", + "Costa Rica" => "CR", + "Croatia" => "HR", + "Cuba" => "CU", + "Cyprus" => "CY", + "Czechia" => "CZ", + "Czech Republic" => "CZ", + "Denmark" => "DK", + "Djibouti" => "DJ", + "Dominica" => "DM", + "Dominican Republic" => "DO", + "Ecuador" => "EC", + "Egypt" => "EG", + "El Salvador" => "SV", + "Equatorial Guinea" => "GQ", + "Eritrea" => "ER", + "Estonia" => "EE", + "Eswatini" => "SZ", + "Swaziland" => "SZ", + "Ethiopia" => "ET", + "Fiji" => "FJ", + "Finland" => "FI", + "France" => "FR", + "Gabon" => "GA", + "Gambia" => "GM", + "Georgia" => "GE", + "Germany" => "DE", + "Ghana" => "GH", + "Greece" => "GR", + "Grenada" => "GD", + "Guatemala" => "GT", + "Guinea" => "GN", + "Guinea-Bissau" => "GW", + "Guyana" => "GY", + "Haiti" => "HT", + "Honduras" => "HN", + "Hungary" => "HU", + "Iceland" => "IS", + "India" => "IN", + "Indonesia" => "ID", + "Iran" => "IR", + "Iraq" => "IQ", + "Ireland" => "IE", + "Israel" => "IL", + "Italy" => "IT", + "Jamaica" => "JM", + "Japan" => "JP", + "Jordan" => "JO", + "Kazakhstan" => "KZ", + "Kenya" => "KE", + "Kiribati" => "KI", + "Kuwait" => "KW", + "Kyrgyzstan" => "KG", + "Laos" => "LA", + "Latvia" => "LV", + "Lebanon" => "LB", + "Lesotho" => "LS", + "Liberia" => "LR", + "Libya" => "LY", + "Liechtenstein" => "LI", + "Lithuania" => "LT", + "Luxembourg" => "LU", + "Madagascar" => "MG", + "Malawi" => "MW", + "Malaysia" => "MY", + "Maldives" => "MV", + "Mali" => "ML", + "Malta" => "MT", + "Marshall Islands" => "MH", + "Mauritania" => "MR", + "Mauritius" => "MU", + "Mexico" => "MX", + "Micronesia" => "FM", + "Moldova" => "MD", + "Monaco" => "MC", + "Mongolia" => "MN", + "Montenegro" => "ME", + "Morocco" => "MA", + "Mozambique" => "MZ", + "Myanmar" => "MM", + "Burma" => "MM", + "Namibia" => "NA", + "Nauru" => "NR", + "Nepal" => "NP", + "Netherlands" => "NL", + "New Zealand" => "NZ", + "Nicaragua" => "NI", + "Niger" => "NE", + "Nigeria" => "NG", + "North Korea" => "KP", + "North Macedonia" => "MK", + "Norway" => "NO", + "Oman" => "OM", + "Pakistan" => "PK", + "Palau" => "PW", + "Palestine" => "PS", + "Panama" => "PA", + "Papua New Guinea" => "PG", + "Paraguay" => "PY", + "Peru" => "PE", + "Philippines" => "PH", + "Poland" => "PL", + "Portugal" => "PT", + "Qatar" => "QA", + "Romania" => "RO", + "Russia" => "RU", + "Russian Federation" => "RU", + "Rwanda" => "RW", + "Saint Kitts and Nevis" => "KN", + "Saint Lucia" => "LC", + "Saint Vincent and the Grenadines" => "VC", + "Samoa" => "WS", + "San Marino" => "SM", + "Sao Tome and Principe" => "ST", + "Saudi Arabia" => "SA", + "Senegal" => "SN", + "Serbia" => "RS", + "Seychelles" => "SC", + "Sierra Leone" => "SL", + "Singapore" => "SG", + "Slovakia" => "SK", + "Slovenia" => "SI", + "Solomon Islands" => "SB", + "Somalia" => "SO", + "South Africa" => "ZA", + "South Korea" => "KR", + "South Sudan" => "SS", + "Spain" => "ES", + "Sri Lanka" => "LK", + "Sudan" => "SD", + "Suriname" => "SR", + "Sweden" => "SE", + "Switzerland" => "CH", + "Syria" => "SY", + "Taiwan" => "TW", + "Tajikistan" => "TJ", + "Tanzania" => "TZ", + "Thailand" => "TH", + "Timor-Leste" => "TL", + "Togo" => "TG", + "Tonga" => "TO", + "Trinidad and Tobago" => "TT", + "Tunisia" => "TN", + "Turkey" => "TR", + "Turkmenistan" => "TM", + "Tuvalu" => "TV", + "Uganda" => "UG", + "Ukraine" => "UA", + "United Arab Emirates" => "AE", + "United Kingdom" => "GB", + "UK" => "GB", + "Great Britain" => "GB", + "United States" => "US", + "USA" => "US", + "Uruguay" => "UY", + "Uzbekistan" => "UZ", + "Vanuatu" => "VU", + "Vatican City" => "VA", + "Venezuela" => "VE", + "Vietnam" => "VN", + "Yemen" => "YE", + "Zambia" => "ZM", + "Zimbabwe" => "ZW" + } + + + # Try direct match first + return mapping[country_name] if mapping[country_name] + + # Try case-insensitive match or partial match + 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/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/config/initializers/01_constants.rb b/config/initializers/01_constants.rb index 30584e9d..7f51ffca 100644 --- a/config/initializers/01_constants.rb +++ b/config/initializers/01_constants.rb @@ -4,7 +4,7 @@ SELF_HOSTED = ENV.fetch('SELF_HOSTED', 'true') == 'true' MIN_MINUTES_SPENT_IN_CITY = ENV.fetch('MIN_MINUTES_SPENT_IN_CITY', 60).to_i -DISTANCE_UNITS = { +::DISTANCE_UNITS = { km: 1000, # to meters mi: 1609.34, # to meters m: 1, # already in meters 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 From 168e33dedda813a5b4b7e09644d917fb89745639 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 18 May 2025 11:17:25 +0200 Subject: [PATCH 2/4] Use iso_a2 from the countries table --- CHANGELOG.md | 2 +- app/helpers/country_flag_helper.rb | 210 +---------------------------- app/models/country.rb | 4 + 3 files changed, 6 insertions(+), 210 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35610bf7..bf1babf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,7 +44,7 @@ Also, after updating to this version, Dawarich will start a huge background job - 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 diff --git a/app/helpers/country_flag_helper.rb b/app/helpers/country_flag_helper.rb index d5e4f5b5..cfa711f0 100644 --- a/app/helpers/country_flag_helper.rb +++ b/app/helpers/country_flag_helper.rb @@ -13,218 +13,10 @@ module CountryFlagHelper private def country_to_code(country_name) - # Mapping of country names to ISO 3166-1 alpha-2 codes - # This is a basic mapping - you might want to expand it based on your data - mapping = { - "Afghanistan" => "AF", - "Albania" => "AL", - "Algeria" => "DZ", - "Andorra" => "AD", - "Angola" => "AO", - "Antigua and Barbuda" => "AG", - "Argentina" => "AR", - "Armenia" => "AM", - "Australia" => "AU", - "Austria" => "AT", - "Azerbaijan" => "AZ", - "Bahamas" => "BS", - "Bahrain" => "BH", - "Bangladesh" => "BD", - "Barbados" => "BB", - "Belarus" => "BY", - "Belgium" => "BE", - "Belize" => "BZ", - "Benin" => "BJ", - "Bhutan" => "BT", - "Bolivia" => "BO", - "Bosnia and Herzegovina" => "BA", - "Botswana" => "BW", - "Brazil" => "BR", - "Brunei" => "BN", - "Bulgaria" => "BG", - "Burkina Faso" => "BF", - "Burundi" => "BI", - "Cabo Verde" => "CV", - "Cambodia" => "KH", - "Cameroon" => "CM", - "Canada" => "CA", - "Central African Republic" => "CF", - "Chad" => "TD", - "Chile" => "CL", - "China" => "CN", - "Colombia" => "CO", - "Comoros" => "KM", - "Congo (Brazzaville)" => "CG", - "Congo (Kinshasa)" => "CD", - "Costa Rica" => "CR", - "Croatia" => "HR", - "Cuba" => "CU", - "Cyprus" => "CY", - "Czechia" => "CZ", - "Czech Republic" => "CZ", - "Denmark" => "DK", - "Djibouti" => "DJ", - "Dominica" => "DM", - "Dominican Republic" => "DO", - "Ecuador" => "EC", - "Egypt" => "EG", - "El Salvador" => "SV", - "Equatorial Guinea" => "GQ", - "Eritrea" => "ER", - "Estonia" => "EE", - "Eswatini" => "SZ", - "Swaziland" => "SZ", - "Ethiopia" => "ET", - "Fiji" => "FJ", - "Finland" => "FI", - "France" => "FR", - "Gabon" => "GA", - "Gambia" => "GM", - "Georgia" => "GE", - "Germany" => "DE", - "Ghana" => "GH", - "Greece" => "GR", - "Grenada" => "GD", - "Guatemala" => "GT", - "Guinea" => "GN", - "Guinea-Bissau" => "GW", - "Guyana" => "GY", - "Haiti" => "HT", - "Honduras" => "HN", - "Hungary" => "HU", - "Iceland" => "IS", - "India" => "IN", - "Indonesia" => "ID", - "Iran" => "IR", - "Iraq" => "IQ", - "Ireland" => "IE", - "Israel" => "IL", - "Italy" => "IT", - "Jamaica" => "JM", - "Japan" => "JP", - "Jordan" => "JO", - "Kazakhstan" => "KZ", - "Kenya" => "KE", - "Kiribati" => "KI", - "Kuwait" => "KW", - "Kyrgyzstan" => "KG", - "Laos" => "LA", - "Latvia" => "LV", - "Lebanon" => "LB", - "Lesotho" => "LS", - "Liberia" => "LR", - "Libya" => "LY", - "Liechtenstein" => "LI", - "Lithuania" => "LT", - "Luxembourg" => "LU", - "Madagascar" => "MG", - "Malawi" => "MW", - "Malaysia" => "MY", - "Maldives" => "MV", - "Mali" => "ML", - "Malta" => "MT", - "Marshall Islands" => "MH", - "Mauritania" => "MR", - "Mauritius" => "MU", - "Mexico" => "MX", - "Micronesia" => "FM", - "Moldova" => "MD", - "Monaco" => "MC", - "Mongolia" => "MN", - "Montenegro" => "ME", - "Morocco" => "MA", - "Mozambique" => "MZ", - "Myanmar" => "MM", - "Burma" => "MM", - "Namibia" => "NA", - "Nauru" => "NR", - "Nepal" => "NP", - "Netherlands" => "NL", - "New Zealand" => "NZ", - "Nicaragua" => "NI", - "Niger" => "NE", - "Nigeria" => "NG", - "North Korea" => "KP", - "North Macedonia" => "MK", - "Norway" => "NO", - "Oman" => "OM", - "Pakistan" => "PK", - "Palau" => "PW", - "Palestine" => "PS", - "Panama" => "PA", - "Papua New Guinea" => "PG", - "Paraguay" => "PY", - "Peru" => "PE", - "Philippines" => "PH", - "Poland" => "PL", - "Portugal" => "PT", - "Qatar" => "QA", - "Romania" => "RO", - "Russia" => "RU", - "Russian Federation" => "RU", - "Rwanda" => "RW", - "Saint Kitts and Nevis" => "KN", - "Saint Lucia" => "LC", - "Saint Vincent and the Grenadines" => "VC", - "Samoa" => "WS", - "San Marino" => "SM", - "Sao Tome and Principe" => "ST", - "Saudi Arabia" => "SA", - "Senegal" => "SN", - "Serbia" => "RS", - "Seychelles" => "SC", - "Sierra Leone" => "SL", - "Singapore" => "SG", - "Slovakia" => "SK", - "Slovenia" => "SI", - "Solomon Islands" => "SB", - "Somalia" => "SO", - "South Africa" => "ZA", - "South Korea" => "KR", - "South Sudan" => "SS", - "Spain" => "ES", - "Sri Lanka" => "LK", - "Sudan" => "SD", - "Suriname" => "SR", - "Sweden" => "SE", - "Switzerland" => "CH", - "Syria" => "SY", - "Taiwan" => "TW", - "Tajikistan" => "TJ", - "Tanzania" => "TZ", - "Thailand" => "TH", - "Timor-Leste" => "TL", - "Togo" => "TG", - "Tonga" => "TO", - "Trinidad and Tobago" => "TT", - "Tunisia" => "TN", - "Turkey" => "TR", - "Turkmenistan" => "TM", - "Tuvalu" => "TV", - "Uganda" => "UG", - "Ukraine" => "UA", - "United Arab Emirates" => "AE", - "United Kingdom" => "GB", - "UK" => "GB", - "Great Britain" => "GB", - "United States" => "US", - "USA" => "US", - "Uruguay" => "UY", - "Uzbekistan" => "UZ", - "Vanuatu" => "VU", - "Vatican City" => "VA", - "Venezuela" => "VE", - "Vietnam" => "VN", - "Yemen" => "YE", - "Zambia" => "ZM", - "Zimbabwe" => "ZW" - } + mapping = Country.names_to_iso_a2 - - # Try direct match first return mapping[country_name] if mapping[country_name] - # Try case-insensitive match or partial match 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) 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 From ce0c38e6e8b48708dedca90db05cf29527445de4 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 18 May 2025 11:19:01 +0200 Subject: [PATCH 3/4] Remove comments --- app/helpers/application_helper.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 760509a6..47d40698 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -40,7 +40,6 @@ module ApplicationHelper data[:cities].flatten!.uniq! data[:countries].flatten!.uniq! - # Group cities by country grouped_by_country = {} stats.select { _1.year == year }.each do |stat| stat.toponyms.flatten.each do |toponym| @@ -58,10 +57,8 @@ module ApplicationHelper end end - # Deduplicate cities for each country grouped_by_country.transform_values!(&:uniq) - # Return data for the template to use { countries_count: data[:countries].count, cities_count: data[:cities].count, From 2b453dc967713feac20df109584e436e00b64474 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Sun, 18 May 2025 11:19:49 +0200 Subject: [PATCH 4/4] Fix constant --- config/initializers/01_constants.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/initializers/01_constants.rb b/config/initializers/01_constants.rb index 7f51ffca..30584e9d 100644 --- a/config/initializers/01_constants.rb +++ b/config/initializers/01_constants.rb @@ -4,7 +4,7 @@ SELF_HOSTED = ENV.fetch('SELF_HOSTED', 'true') == 'true' MIN_MINUTES_SPENT_IN_CITY = ENV.fetch('MIN_MINUTES_SPENT_IN_CITY', 60).to_i -::DISTANCE_UNITS = { +DISTANCE_UNITS = { km: 1000, # to meters mi: 1609.34, # to meters m: 1, # already in meters