mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-10 01:01:39 -05:00
Enable visit suggesting job
This commit is contained in:
parent
9a4a6481d0
commit
b8e6b1a372
8 changed files with 131 additions and 40 deletions
|
|
@ -1 +1 @@
|
|||
0.24.2
|
||||
0.25.0
|
||||
|
|
|
|||
37
CHANGELOG.md
37
CHANGELOG.md
|
|
@ -4,15 +4,7 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
TODO:
|
||||
|
||||
- Specs for app/services/visits/merge_service.rb and rename it probably
|
||||
- Remove Stimulus controllers for visits on the Visits page
|
||||
- Revert changes to Visits page
|
||||
- Decide on how to suggest visits for the past
|
||||
- Should visits be disabled for non-reverse-geocoded instances?
|
||||
|
||||
# 0.25.0 - 2025-03-08
|
||||
# 0.25.0 - 2025-03-09
|
||||
|
||||
This release is focused on improving the visits experience.
|
||||
|
||||
|
|
@ -26,29 +18,12 @@ This release is focused on improving the visits experience.
|
|||
- User can select click on the "Select area" button in the top right corner of the map to select an area on the map. Once area is selected, visits for all times in that area will be shown on the map, regardless of whether they are in the selected time range or not.
|
||||
- User can now select two or more visits in the visits drawer and merge them into a single visit. This operation is not reversible.
|
||||
- User can now select two or more visits in the visits drawer and confirm or decline them at once. This operation is not reversible.
|
||||
- Status field to the User model. Inactive users are now being restricted from accessing some of the functionality, which is mostly about writing data to the database. Reading is remaining unrestricted.
|
||||
|
||||
|
||||
## Changed
|
||||
|
||||
- Links to Points, Visits & Places, Imports and Exports were moved under "My data" section in the navbar.
|
||||
|
||||
## Fixed
|
||||
|
||||
|
||||
|
||||
# 0.24.2 - 2025-02-24
|
||||
|
||||
## Added
|
||||
|
||||
- Status field to the User model. Inactive users are now being restricted from accessing some of the functionality, which is mostly about writing data to the database. Reading is remaining unrestricted.
|
||||
|
||||
## Fixed
|
||||
|
||||
- Fixed a bug where non-admin users could not import Immich and Photoprism geolocation data.
|
||||
- Fixed a bug where upon point deletion it was not being removed from the map, while it was actually deleted from the database. #883
|
||||
- Fixed a bug where upon import deletion stats were not being recalculated. #824
|
||||
|
||||
### Changed
|
||||
|
||||
- Restrict access to Sidekiq in non self-hosted mode.
|
||||
- Restrict access to background jobs in non self-hosted mode.
|
||||
- Restrict access to users management in non self-hosted mode.
|
||||
|
|
@ -57,6 +32,12 @@ This release is focused on improving the visits experience.
|
|||
- GPX files are now being imported much faster.
|
||||
- Distance calculation are now using Postgis functions and expected to be more accurate.
|
||||
|
||||
## Fixed
|
||||
|
||||
- Fixed a bug where non-admin users could not import Immich and Photoprism geolocation data.
|
||||
- Fixed a bug where upon point deletion it was not being removed from the map, while it was actually deleted from the database. #883
|
||||
- Fixed a bug where upon import deletion stats were not being recalculated. #824
|
||||
|
||||
# 0.24.1 - 2025-02-13
|
||||
|
||||
## Custom map tiles
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This job is being run on daily basis at 00:05 to suggest visits for all users
|
||||
# with the default timespan of 1 day.
|
||||
class BulkVisitsSuggestingJob < ApplicationJob
|
||||
queue_as :default
|
||||
queue_as :visit_suggesting
|
||||
sidekiq_options retry: false
|
||||
|
||||
# Passing timespan of more than 3 years somehow results in duplicated Places
|
||||
def perform(start_at:, end_at:, user_ids: [])
|
||||
users = user_ids.any? ? User.where(id: user_ids) : User.all
|
||||
def perform(start_at: 1.day.ago.beginning_of_day, end_at: 1.day.ago.end_of_day, user_ids: [])
|
||||
return unless DawarichSettings.reverse_geocoding_enabled?
|
||||
|
||||
users = user_ids.any? ? User.active.where(id: user_ids) : User.active
|
||||
start_at = start_at.to_datetime
|
||||
end_at = end_at.to_datetime
|
||||
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class Point < ApplicationRecord
|
|||
end
|
||||
|
||||
def recorded_at
|
||||
Time.zone.at(timestamp)
|
||||
@recorded_at ||= Time.zone.at(timestamp)
|
||||
end
|
||||
|
||||
def async_reverse_geocode
|
||||
|
|
|
|||
|
|
@ -39,9 +39,9 @@
|
|||
<tr>
|
||||
<td><%= place.name %></td>
|
||||
<td><%= place.created_at.strftime('%Y-%m-%d %H:%M:%S') %></td>
|
||||
<td><%= place.to_coordinates.map(&:to_f) %></td>
|
||||
<td><%= "#{place.lat}, #{place.lon}" %></td>
|
||||
<td>
|
||||
<%= link_to 'Delete', place, data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?", turbo_method: :delete }, method: :delete, class: "px-4 py-2 bg-red-500 text-white rounded-md" %>
|
||||
<%= link_to 'Delete', place, data: { confirm: "Are you sure? Deleting a place will result in deleting all visits for this place.", turbo_confirm: "Are you sure? Deleting a place will result in deleting all visits for this place.", turbo_method: :delete }, method: :delete, class: "px-4 py-2 bg-red-500 text-white rounded-md" %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -10,11 +10,10 @@ area_visits_calculation_scheduling_job:
|
|||
class: "AreaVisitsCalculationSchedulingJob"
|
||||
queue: visit_suggesting
|
||||
|
||||
# Disabled until fixed
|
||||
# visit_suggesting_job:
|
||||
# cron: "0 1 * * *" # every day at 1:00
|
||||
# class: "VisitSuggestingJob"
|
||||
# queue: visit_suggesting
|
||||
visit_suggesting_job:
|
||||
cron: "5 0 * * *" # every day at 00:05
|
||||
class: "BulkVisitsSuggestingJob"
|
||||
queue: visit_suggesting
|
||||
|
||||
watcher_job:
|
||||
cron: "0 */1 * * *" # every 1 hour
|
||||
|
|
|
|||
1
db/schema.rb
generated
1
db/schema.rb
generated
|
|
@ -126,6 +126,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_03_03_194043) do
|
|||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.geography "lonlat", limit: {srid: 4326, type: "st_point", geographic: true}
|
||||
t.index "name, st_astext(lonlat)", name: "index_places_on_name_and_lonlat", unique: true
|
||||
t.index ["lonlat"], name: "index_places_on_lonlat", using: :gist
|
||||
end
|
||||
|
||||
|
|
|
|||
106
spec/jobs/bulk_visits_suggesting_job_spec.rb
Normal file
106
spec/jobs/bulk_visits_suggesting_job_spec.rb
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe BulkVisitsSuggestingJob, type: :job do
|
||||
describe '#perform' do
|
||||
let(:start_at) { 1.day.ago.beginning_of_day }
|
||||
let(:end_at) { 1.day.ago.end_of_day }
|
||||
let(:user) { create(:user) }
|
||||
let(:inactive_user) { create(:user, status: :inactive) }
|
||||
let(:user_with_points) { create(:user) }
|
||||
let(:time_chunks) { [[start_at, end_at]] }
|
||||
|
||||
before do
|
||||
allow(DawarichSettings).to receive(:reverse_geocoding_enabled?).and_return(true)
|
||||
allow_any_instance_of(Visits::TimeChunks).to receive(:call).and_return(time_chunks)
|
||||
create(:point, user: user_with_points)
|
||||
end
|
||||
|
||||
it 'does nothing if reverse geocoding is disabled' do
|
||||
allow(DawarichSettings).to receive(:reverse_geocoding_enabled?).and_return(false)
|
||||
|
||||
expect(VisitSuggestingJob).not_to receive(:perform_later)
|
||||
|
||||
described_class.perform_now
|
||||
end
|
||||
|
||||
it 'schedules jobs only for active users with tracked points' do
|
||||
expect(VisitSuggestingJob).to receive(:perform_later).with(
|
||||
user_id: user_with_points.id,
|
||||
start_at: time_chunks.first.first,
|
||||
end_at: time_chunks.first.last
|
||||
)
|
||||
|
||||
expect(VisitSuggestingJob).not_to receive(:perform_later).with(
|
||||
user_id: user.id,
|
||||
start_at: anything,
|
||||
end_at: anything
|
||||
)
|
||||
|
||||
expect(VisitSuggestingJob).not_to receive(:perform_later).with(
|
||||
user_id: inactive_user.id,
|
||||
start_at: anything,
|
||||
end_at: anything
|
||||
)
|
||||
|
||||
described_class.perform_now
|
||||
end
|
||||
|
||||
it 'handles multiple time chunks' do
|
||||
chunks = [
|
||||
[start_at, start_at + 12.hours],
|
||||
[start_at + 12.hours, end_at]
|
||||
]
|
||||
allow_any_instance_of(Visits::TimeChunks).to receive(:call).and_return(chunks)
|
||||
|
||||
chunks.each do |chunk|
|
||||
expect(VisitSuggestingJob).to receive(:perform_later).with(
|
||||
user_id: user_with_points.id,
|
||||
start_at: chunk.first,
|
||||
end_at: chunk.last
|
||||
)
|
||||
end
|
||||
|
||||
described_class.perform_now
|
||||
end
|
||||
|
||||
it 'only processes specified users when user_ids is provided' do
|
||||
create(:point, user: user)
|
||||
|
||||
expect(VisitSuggestingJob).to receive(:perform_later).with(
|
||||
user_id: user.id,
|
||||
start_at: time_chunks.first.first,
|
||||
end_at: time_chunks.first.last
|
||||
)
|
||||
|
||||
expect(VisitSuggestingJob).not_to receive(:perform_later).with(
|
||||
user_id: user_with_points.id,
|
||||
start_at: anything,
|
||||
end_at: anything
|
||||
)
|
||||
|
||||
described_class.perform_now(user_ids: [user.id])
|
||||
end
|
||||
|
||||
it 'uses custom time range when provided' do
|
||||
custom_start = 2.days.ago.beginning_of_day
|
||||
custom_end = 2.days.ago.end_of_day
|
||||
custom_chunks = [[custom_start, custom_end]]
|
||||
|
||||
time_chunks_instance = instance_double(Visits::TimeChunks)
|
||||
allow(Visits::TimeChunks).to receive(:new)
|
||||
.with(start_at: custom_start, end_at: custom_end)
|
||||
.and_return(time_chunks_instance)
|
||||
allow(time_chunks_instance).to receive(:call).and_return(custom_chunks)
|
||||
|
||||
expect(VisitSuggestingJob).to receive(:perform_later).with(
|
||||
user_id: user_with_points.id,
|
||||
start_at: custom_chunks.first.first,
|
||||
end_at: custom_chunks.first.last
|
||||
)
|
||||
|
||||
described_class.perform_now(start_at: custom_start, end_at: custom_end)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue