diff --git a/.rubocop.yml b/.rubocop.yml index ea725c1b..aaa6befd 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1 +1,4 @@ require: rubocop-rails + +Style/Documentation: + Enabled: false diff --git a/Gemfile b/Gemfile index 92b6a822..cb1e7c09 100644 --- a/Gemfile +++ b/Gemfile @@ -15,10 +15,11 @@ gem 'stimulus-rails' gem 'tailwindcss-rails' gem 'turbo-rails' gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby] -gem "importmap-rails" -gem "chartkick" +gem 'importmap-rails' +gem 'chartkick' gem 'geocoder' gem 'sidekiq' +gem 'sidekiq-cron' group :development, :test do @@ -42,4 +43,4 @@ group :development do end # Use Redis for Action Cable -gem "redis" +gem 'redis' diff --git a/Gemfile.lock b/Gemfile.lock index 3a497dd8..a1d19b80 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,8 @@ GEM railties (>= 6.1) drb (2.2.1) erubi (1.12.0) + et-orbi (1.2.11) + tzinfo factory_bot (6.4.6) activesupport (>= 5.0.0) factory_bot_rails (6.4.3) @@ -113,6 +115,9 @@ GEM railties (>= 5.0.0) ffaker (2.23.0) foreman (0.87.2) + fugit (1.10.1) + et-orbi (~> 1, >= 1.2.7) + raabro (~> 1.4) geocoder (1.8.2) globalid (1.2.1) activesupport (>= 6.1) @@ -178,6 +183,7 @@ GEM nio4r (~> 2.0) pundit (2.3.1) activesupport (>= 3.0.0) + raabro (1.4.0) racc (1.7.3) rack (3.0.10) rack-session (2.0.0) @@ -274,6 +280,10 @@ GEM connection_pool (>= 2.3.0) rack (>= 2.2.4) redis-client (>= 0.19.0) + sidekiq-cron (1.12.0) + fugit (~> 1.8) + globalid (>= 1.0.1) + sidekiq (>= 6) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) @@ -343,6 +353,7 @@ DEPENDENCIES rubocop-rails shoulda-matchers sidekiq + sidekiq-cron simplecov sprockets-rails stimulus-rails diff --git a/Procfile.dev b/Procfile.dev index adb58e23..67106a1e 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,3 @@ web: bin/rails server -p 3000 -b 0.0.0.0 css: bin/rails tailwindcss:watch -worker: bundle exec sidekiq +worker: bundle exec sidekiq -C config/sidekiq.yml diff --git a/app/controllers/api/v1/points_controller.rb b/app/controllers/api/v1/points_controller.rb index aa1e280b..98aeea70 100644 --- a/app/controllers/api/v1/points_controller.rb +++ b/app/controllers/api/v1/points_controller.rb @@ -2,15 +2,9 @@ class Api::V1::PointsController < ApplicationController skip_forgery_protection def create - parsed_params = OwnTracks::Params.new(point_params).call + PointCreatingJob.perform_later(point_params) - @point = Point.create(parsed_params) - - if @point.valid? - render json: @point, status: :ok - else - render json: @point.errors, status: :unprocessable_entity - end + render json: {}, status: :ok end def destroy diff --git a/app/controllers/points_controller.rb b/app/controllers/points_controller.rb index 9c57fac5..ba1c3fc8 100644 --- a/app/controllers/points_controller.rb +++ b/app/controllers/points_controller.rb @@ -11,6 +11,7 @@ class PointsController < ApplicationController @distance = distance @start_at = Time.zone.at(start_at) @end_at = Time.zone.at(end_at) + @years = (@start_at.year..@end_at.year).to_a end private @@ -18,13 +19,13 @@ class PointsController < ApplicationController def start_at return 1.month.ago.beginning_of_day.to_i if params[:start_at].nil? - params[:start_at].to_datetime.to_i + Time.parse(params[:start_at]).to_i end def end_at return Time.zone.today.end_of_day.to_i if params[:end_at].nil? - params[:end_at].to_datetime.to_i + Time.parse(params[:end_at]).to_i end def distance diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index f7a13f81..f871dd6a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -17,10 +17,18 @@ module ApplicationHelper end def year_timespan(year) - start_at = DateTime.new(year).beginning_of_year.to_time.strftime('%Y-%m-%dT%H:%M') - end_at = DateTime.new(year).end_of_year.to_time.strftime('%Y-%m-%dT%H:%M') + start_at = Time.utc(year).in_time_zone('Europe/Berlin').beginning_of_year.strftime('%Y-%m-%dT%H:%M') + end_at = Time.utc(year).in_time_zone('Europe/Berlin').end_of_year.strftime('%Y-%m-%dT%H:%M') - { start_at: start_at, end_at: end_at } + { start_at:, end_at: } + end + + def timespan(month, year) + month = DateTime.new(year, month).in_time_zone(Time.zone) + start_at = month.beginning_of_month.to_time.strftime('%Y-%m-%dT%H:%M') + end_at = month.end_of_month.to_time.strftime('%Y-%m-%dT%H:%M') + + { start_at:, end_at: } end def header_colors @@ -38,4 +46,14 @@ module ApplicationHelper def year_distance_stat_in_km(year) Stat.year_distance(year).sum { _1[1] } end + + def is_past?(year, month) + DateTime.new(year, month).past? + end + + def points_exist?(year, month) + Point.where( + timestamp: DateTime.new(year, month).beginning_of_month..DateTime.new(year, month).end_of_month + ).exists? + end end diff --git a/app/jobs/point_creating_job.rb b/app/jobs/point_creating_job.rb new file mode 100644 index 00000000..c7899d04 --- /dev/null +++ b/app/jobs/point_creating_job.rb @@ -0,0 +1,9 @@ +class PointCreatingJob < ApplicationJob + queue_as :default + + def perform(point_params) + parsed_params = OwnTracks::Params.new(point_params).call + + point = Point.create(parsed_params) + end +end diff --git a/app/jobs/stat_creating_job.rb b/app/jobs/stat_creating_job.rb index 6ca0f9fe..610ad38c 100644 --- a/app/jobs/stat_creating_job.rb +++ b/app/jobs/stat_creating_job.rb @@ -1,7 +1,9 @@ +# frozen_string_literal: true + class StatCreatingJob < ApplicationJob queue_as :default - def perform(user_id) - CreateStats.new(user_id).call + def perform(user_ids = nil) + CreateStats.new(user_ids).call end end diff --git a/app/models/stat.rb b/app/models/stat.rb index 45ec29dd..5250255a 100644 --- a/app/models/stat.rb +++ b/app/models/stat.rb @@ -51,4 +51,10 @@ class Stat < ApplicationRecord { countries: data.count, cities: data.sum { |country| country[:cities].count } } end + + def self.years + starting_year = pluck(:year).uniq.min || Time.current.year + + (starting_year..Time.current.year).to_a.reverse + end end diff --git a/app/services/countries_and_cities.rb b/app/services/countries_and_cities.rb index fa952448..873a3edd 100644 --- a/app/services/countries_and_cities.rb +++ b/app/services/countries_and_cities.rb @@ -37,8 +37,10 @@ class CountriesAndCities end end - def filter_cities(mapped_with_cities) + # In future, we would want to remove cities where user spent less than + # 1 hour per day + # Remove cities with less than MINIMUM_POINTS_IN_CITY mapped_with_cities.transform_values do |cities| cities.reject { |_, data| data[:points] < MINIMUM_POINTS_IN_CITY } @@ -48,8 +50,8 @@ class CountriesAndCities def normalize_result(hash) hash.map do |country, cities| { - country: country, - cities: cities.map { |city, data| { city: city, points: data[:points], timestamp: data[:timestamp] } } + country:, + cities: cities.map { |city, data| { city:, points: data[:points], timestamp: data[:timestamp] } } } end end diff --git a/app/services/create_stats.rb b/app/services/create_stats.rb index 04125574..b99a4fa3 100644 --- a/app/services/create_stats.rb +++ b/app/services/create_stats.rb @@ -1,32 +1,34 @@ # frozen_string_literal: true class CreateStats - attr_reader :years, :months, :user + attr_reader :years, :months, :users - def initialize(user_id) - @user = User.find(user_id) + def initialize(user_ids) + @users = User.where(id: user_ids) @years = (1970..Time.current.year).to_a @months = (1..12).to_a end def call - years.flat_map do |year| - months.map do |month| - beginning_of_month_timestamp = DateTime.new(year, month).beginning_of_month.to_i - end_of_month_timestamp = DateTime.new(year, month).end_of_month.to_i + users.each do |user| + years.each do |year| + months.each do |month| + beginning_of_month_timestamp = DateTime.new(year, month).beginning_of_month.to_i + end_of_month_timestamp = DateTime.new(year, month).end_of_month.to_i - points = points(beginning_of_month_timestamp, end_of_month_timestamp) - next if points.empty? + points = points(beginning_of_month_timestamp, end_of_month_timestamp) + next if points.empty? - stat = Stat.find_or_initialize_by(year: year, month: month, user: user) - stat.distance = distance(points) - stat.toponyms = toponyms(points) - stat.daily_distance = stat.distance_by_day - stat.save + stat = Stat.find_or_initialize_by(year: year, month: month, user: user) + stat.distance = distance(points) + stat.toponyms = toponyms(points) + stat.daily_distance = stat.distance_by_day + stat.save - stat + stat + end end - end.compact + end end private diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d5b6db57..dbd7a696 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,7 +6,7 @@ <%= csrf_meta_tags %> <%= csp_meta_tag %> - + <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %> diff --git a/app/views/points/index.html.erb b/app/views/points/index.html.erb index 544c5f0a..62637846 100644 --- a/app/views/points/index.html.erb +++ b/app/views/points/index.html.erb @@ -1,4 +1,4 @@ -
<%= number_with_delimiter year_distance_stat_in_km(year) %>km
++ <% cache [current_user, 'year_distance_stat_in_km', year], skip_digest: true do %> + <%= number_with_delimiter year_distance_stat_in_km(year) %>km + <% end %> +
<% if REVERSE_GEOCODING_ENABLED %>