mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 17:51:39 -05:00
Merge pull request #1200 from Freika/feature/countries-and-cities-modal
Feature/countries and cities modal
This commit is contained in:
commit
51b9b0d4ae
11 changed files with 140 additions and 26 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
27
app/helpers/country_flag_helper.rb
Normal file
27
app/helpers/country_flag_helper.rb
Normal file
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}.*,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@
|
|||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Imported points</th>
|
||||
<th>Reverse geocoded points</th>
|
||||
<% if DawarichSettings.store_geodata? %>
|
||||
<th>Reverse geocoded points</th>
|
||||
<% end %>
|
||||
<th>Created at</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -65,9 +67,11 @@
|
|||
<td data-points-count>
|
||||
<%= number_with_delimiter import.processed %>
|
||||
</td>
|
||||
<td data-reverse-geocoded-points-count>
|
||||
<%= number_with_delimiter import.reverse_geocoded_points_count %>
|
||||
</td>
|
||||
<% if DawarichSettings.store_geodata? %>
|
||||
<td data-reverse-geocoded-points-count>
|
||||
<%= number_with_delimiter import.reverse_geocoded_points_count %>
|
||||
</td>
|
||||
<% end %>
|
||||
<td><%= human_datetime(import.created_at) %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,41 @@
|
|||
</p>
|
||||
<% if DawarichSettings.reverse_geocoding_enabled? %>
|
||||
<div class="card-actions justify-end">
|
||||
<%= 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" %>
|
||||
|
||||
<!-- Modal structure -->
|
||||
<div>
|
||||
<input type="checkbox" id="<%= location_data[:modal_id] %>" class="modal-toggle" />
|
||||
<div class="modal" role="dialog">
|
||||
<div class="modal-box max-w-3xl">
|
||||
<h3 class="text-lg font-bold mb-4">Countries and Cities visited in <%= location_data[:year] %></h3>
|
||||
<div class="max-h-96 overflow-y-auto">
|
||||
<% location_data[:grouped_by_country].each do |country, cities| %>
|
||||
<div class="mb-4">
|
||||
<h4 class="font-bold">
|
||||
<span class="mr-2"><%= country_flag(country) %></span>
|
||||
<%= country %>
|
||||
</h4>
|
||||
<% if cities.any? %>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-2 pl-4">
|
||||
<% cities.each do |city| %>
|
||||
<div class="text-sm"><%= city %></div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<p class="text-sm text-gray-500 italic pl-4">No specific cities recorded</p>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<label class="modal-backdrop" for="<%= location_data[:modal_id] %>"></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= column_chart(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue