mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 09:41:40 -05:00
Compare commits
4 commits
c772d3a4c9
...
7d3cb4d394
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d3cb4d394 | ||
|
|
6ed6a4fd89 | ||
|
|
c181a2f9a2 | ||
|
|
06406591d0 |
9 changed files with 127 additions and 33 deletions
|
|
@ -1 +1 @@
|
||||||
0.37.0
|
0.37.1
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
# [0.37.1] - 2025-12-30
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- The db migration preventing the app from starting.
|
||||||
|
- Raw data archive verifier now allows having points deleted from the db after archiving.
|
||||||
|
|
||||||
# [0.37.0] - 2025-12-30
|
# [0.37.0] - 2025-12-30
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ class Api::V1::Owntracks::PointsController < ApiController
|
||||||
def create
|
def create
|
||||||
Owntracks::PointCreatingJob.perform_later(point_params, current_api_user.id)
|
Owntracks::PointCreatingJob.perform_later(point_params, current_api_user.id)
|
||||||
|
|
||||||
render json: {}, status: :ok
|
family_locations = OwnTracks::FamilyLocationsFormatter.new(current_api_user).call
|
||||||
|
|
||||||
|
render json: family_locations, status: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
||||||
51
app/services/own_tracks/family_locations_formatter.rb
Normal file
51
app/services/own_tracks/family_locations_formatter.rb
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class OwnTracks::FamilyLocationsFormatter
|
||||||
|
attr_reader :user
|
||||||
|
|
||||||
|
def initialize(user)
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
def call
|
||||||
|
return [] unless family_feature_enabled?
|
||||||
|
return [] unless user.in_family?
|
||||||
|
|
||||||
|
sharing_members = family_members_with_sharing_enabled
|
||||||
|
return [] unless sharing_members.any?
|
||||||
|
|
||||||
|
latest_points = sharing_members.map { |member| member.points.order(timestamp: :desc).first }.compact
|
||||||
|
latest_points.map { |point| build_owntracks_location(point) }.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def family_feature_enabled?
|
||||||
|
DawarichSettings.family_feature_enabled?
|
||||||
|
end
|
||||||
|
|
||||||
|
def family_members_with_sharing_enabled
|
||||||
|
user.family.members
|
||||||
|
.where.not(id: user.id)
|
||||||
|
.select(&:family_sharing_enabled?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_owntracks_location(point)
|
||||||
|
location = {
|
||||||
|
_type: 'location',
|
||||||
|
lat: point.lat.to_f,
|
||||||
|
lon: point.lon.to_f,
|
||||||
|
tst: point.timestamp.to_i,
|
||||||
|
tid: point.user.email,
|
||||||
|
acc: point.accuracy,
|
||||||
|
alt: point.altitude,
|
||||||
|
batt: point.battery,
|
||||||
|
bs: OwnTracks::Params.battery_status_to_numeric(point.battery_status),
|
||||||
|
vel: OwnTracks::Params.velocity_to_kmh(point.velocity),
|
||||||
|
conn: OwnTracks::Params.connection_to_string(point.connection),
|
||||||
|
}
|
||||||
|
|
||||||
|
location
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -90,4 +90,37 @@ class OwnTracks::Params
|
||||||
def valid_point?
|
def valid_point?
|
||||||
params[:lon].present? && params[:lat].present? && params[:tst].present?
|
params[:lon].present? && params[:lat].present? && params[:tst].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Reverse conversion methods: from internal format to Owntracks format
|
||||||
|
class << self
|
||||||
|
def battery_status_to_numeric(battery_status)
|
||||||
|
case battery_status
|
||||||
|
when 'unknown' then 0
|
||||||
|
when 'unplugged' then 1
|
||||||
|
when 'charging' then 2
|
||||||
|
when 'full' then 3
|
||||||
|
else 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def connection_to_string(connection)
|
||||||
|
case connection
|
||||||
|
when 'mobile' then 'm'
|
||||||
|
when 'wifi' then 'w'
|
||||||
|
when 'offline' then 'o'
|
||||||
|
else nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def velocity_to_kmh(velocity)
|
||||||
|
return nil if velocity.blank?
|
||||||
|
|
||||||
|
# Try to parse as float
|
||||||
|
velocity_float = velocity.to_f
|
||||||
|
return nil if velocity_float.zero?
|
||||||
|
|
||||||
|
# Reference: https://owntracks.org/booklet/tech/json/
|
||||||
|
(velocity_float * 3.6).round.to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -110,18 +110,24 @@ module Points
|
||||||
return { success: false, error: 'Point IDs checksum mismatch' }
|
return { success: false, error: 'Point IDs checksum mismatch' }
|
||||||
end
|
end
|
||||||
|
|
||||||
# 8. Verify all points still exist in database
|
# 8. Check which points still exist in database (informational only)
|
||||||
existing_count = Point.where(id: point_ids).count
|
existing_count = Point.where(id: point_ids).count
|
||||||
if existing_count != point_ids.count
|
if existing_count != point_ids.count
|
||||||
return {
|
Rails.logger.info(
|
||||||
success: false,
|
"Archive #{archive.id}: #{point_ids.count - existing_count} points no longer in database " \
|
||||||
error: "Missing points in database: expected #{point_ids.count}, found #{existing_count}"
|
"(#{existing_count}/#{point_ids.count} remaining). This is OK if user deleted their data."
|
||||||
}
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# 9. Verify archived raw_data matches current database raw_data
|
# 9. Verify archived raw_data matches current database raw_data (only for existing points)
|
||||||
verification_result = verify_raw_data_matches(archived_data)
|
if existing_count.positive?
|
||||||
return verification_result unless verification_result[:success]
|
verification_result = verify_raw_data_matches(archived_data)
|
||||||
|
return verification_result unless verification_result[:success]
|
||||||
|
else
|
||||||
|
Rails.logger.info(
|
||||||
|
"Archive #{archive.id}: Skipping raw_data verification - no points remain in database"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
{ success: true }
|
{ success: true }
|
||||||
end
|
end
|
||||||
|
|
@ -149,11 +155,18 @@ module Points
|
||||||
point_ids_to_check = archived_data.keys.sample(100)
|
point_ids_to_check = archived_data.keys.sample(100)
|
||||||
end
|
end
|
||||||
|
|
||||||
mismatches = []
|
# Filter to only check points that still exist in the database
|
||||||
found_points = 0
|
existing_point_ids = Point.where(id: point_ids_to_check).pluck(:id)
|
||||||
|
|
||||||
|
if existing_point_ids.empty?
|
||||||
|
# No points remain to verify, but that's OK
|
||||||
|
Rails.logger.info("No points remaining to verify raw_data matches")
|
||||||
|
return { success: true }
|
||||||
|
end
|
||||||
|
|
||||||
Point.where(id: point_ids_to_check).find_each do |point|
|
mismatches = []
|
||||||
found_points += 1
|
|
||||||
|
Point.where(id: existing_point_ids).find_each do |point|
|
||||||
archived_raw_data = archived_data[point.id]
|
archived_raw_data = archived_data[point.id]
|
||||||
current_raw_data = point.raw_data
|
current_raw_data = point.raw_data
|
||||||
|
|
||||||
|
|
@ -167,14 +180,6 @@ module Points
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check if we found all the points we were looking for
|
|
||||||
if found_points != point_ids_to_check.size
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: "Missing points during data verification: expected #{point_ids_to_check.size}, found #{found_points}"
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if mismatches.any?
|
if mismatches.any?
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
|
|
|
||||||
|
|
@ -2,10 +2,8 @@
|
||||||
|
|
||||||
class AddVisitedCountriesToTrips < ActiveRecord::Migration[8.0]
|
class AddVisitedCountriesToTrips < ActiveRecord::Migration[8.0]
|
||||||
def change
|
def change
|
||||||
# safety_assured do
|
execute <<-SQL
|
||||||
execute <<-SQL
|
|
||||||
ALTER TABLE trips ADD COLUMN visited_countries JSONB DEFAULT '{}'::jsonb NOT NULL;
|
ALTER TABLE trips ADD COLUMN visited_countries JSONB DEFAULT '{}'::jsonb NOT NULL;
|
||||||
SQL
|
SQL
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,8 @@ class AddH3HexIdsToStats < ActiveRecord::Migration[8.0]
|
||||||
|
|
||||||
def change
|
def change
|
||||||
add_column :stats, :h3_hex_ids, :jsonb, default: {}, if_not_exists: true
|
add_column :stats, :h3_hex_ids, :jsonb, default: {}, if_not_exists: true
|
||||||
# safety_assured do
|
add_index :stats, :h3_hex_ids, using: :gin,
|
||||||
add_index :stats, :h3_hex_ids, using: :gin,
|
where: "(h3_hex_ids IS NOT NULL AND h3_hex_ids != '{}'::jsonb)",
|
||||||
where: "(h3_hex_ids IS NOT NULL AND h3_hex_ids != '{}'::jsonb)",
|
algorithm: :concurrently, if_not_exists: true
|
||||||
algorithm: :concurrently, if_not_exists: true
|
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ class ChangeDigestsDistanceToBigint < ActiveRecord::Migration[8.0]
|
||||||
disable_ddl_transaction!
|
disable_ddl_transaction!
|
||||||
|
|
||||||
def up
|
def up
|
||||||
safety_assured { change_column :digests, :distance, :bigint, null: false, default: 0 }
|
change_column :digests, :distance, :bigint, null: false, default: 0
|
||||||
end
|
end
|
||||||
|
|
||||||
def down
|
def down
|
||||||
safety_assured { change_column :digests, :distance, :integer, null: false, default: 0 }
|
change_column :digests, :distance, :integer, null: false, default: 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue