diff --git a/.app_version b/.app_version index 04a373ef..2a0970ca 100644 --- a/.app_version +++ b/.app_version @@ -1 +1 @@ -0.16.0 +0.16.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index a1d93f62..e9ddbae9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,17 @@ 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/). +# 0.16.1 - 2024-11-08 + +### Fixed + +- Speed is now being recorded into points when a GPX file is being imported. Previously, the speed was not being recorded. +- GeoJSON file from GPSLogger now can be imported to Dawarich. Previously, the import was failing due to incorrect parsing of the file. + +### Changed + +- The Vists suggestion job is disabled. It will be re-enabled in the future with a new approach to the visit suggestion process. + # 0.16.0 - 2024-11-07 ## The Websockets release diff --git a/app/services/geojson/params.rb b/app/services/geojson/params.rb index f8838951..21faf941 100644 --- a/app/services/geojson/params.rb +++ b/app/services/geojson/params.rb @@ -39,10 +39,10 @@ class Geojson::Params battery: battery_level(feature[:properties][:battery_level]), timestamp: timestamp(feature), altitude: altitude(feature), - velocity: feature[:properties][:speed], + velocity: speed(feature), tracker_id: feature[:properties][:device_id], ssid: feature[:properties][:wifi], - accuracy: feature[:properties][:horizontal_accuracy], + accuracy: accuracy(feature), vertical_accuracy: feature[:properties][:vertical_accuracy], raw_data: feature } @@ -50,19 +50,19 @@ class Geojson::Params def build_line(feature) feature[:geometry][:coordinates].map do |point| - build_line_point(feature, point) + build_line_point(point) end end def build_multi_line(feature) feature[:geometry][:coordinates].map do |line| line.map do |point| - build_line_point(feature, point) + build_line_point(point) end end end - def build_line_point(feature, point) + def build_line_point(point) { latitude: point[1], longitude: point[0], @@ -84,7 +84,23 @@ class Geojson::Params def timestamp(feature) return Time.zone.at(feature[3]) if feature.is_a?(Array) - value = feature.dig(:properties, :timestamp) || feature.dig(:geometry, :coordinates, 3) - Time.zone.at(value) + value = feature.dig(:properties, :timestamp) || + feature.dig(:geometry, :coordinates, 3) + + return Time.zone.at(value.to_i) if value.is_a?(Numeric) + + ### GPSLogger for Android case ### + time = feature.dig(:properties, :time) + + Time.zone.parse(time).to_i if time.present? + ### /GPSLogger for Android case ### + end + + def speed(feature) + feature.dig(:properties, :speed).to_f.round(1) + end + + def accuracy(feature) + feature.dig(:properties, :accuracy) || feature.dig(:properties, :horizontal_accuracy) end end diff --git a/app/services/gpx/track_parser.rb b/app/services/gpx/track_parser.rb index 27cf1ae2..65cbc3be 100644 --- a/app/services/gpx/track_parser.rb +++ b/app/services/gpx/track_parser.rb @@ -39,6 +39,7 @@ class Gpx::TrackParser altitude: point['ele'].to_i, timestamp: Time.parse(point['time']).to_i, import_id: import.id, + velocity: speed(point), raw_data: point, user_id: ) @@ -54,4 +55,12 @@ class Gpx::TrackParser user_id: ) end + + def speed(point) + return if point['extensions'].blank? + + ( + point.dig('extensions', 'speed') || point.dig('extensions', 'TrackPointExtension', 'speed') + ).to_f.round(1) + end end diff --git a/config/schedule.yml b/config/schedule.yml index 94151c3a..1b9a4f59 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -10,10 +10,11 @@ area_visits_calculation_scheduling_job: class: "AreaVisitsCalculationSchedulingJob" queue: visit_suggesting -visit_suggesting_job: - cron: "0 1 * * *" # every day at 1:00 - class: "VisitSuggestingJob" - queue: visit_suggesting +# Disabled until fixed +# visit_suggesting_job: +# cron: "0 1 * * *" # every day at 1:00 +# class: "VisitSuggestingJob" +# queue: visit_suggesting watcher_job: cron: "0 */1 * * *" # every 1 hour diff --git a/spec/fixtures/files/geojson/gpslogger_example.json b/spec/fixtures/files/geojson/gpslogger_example.json new file mode 100644 index 00000000..12a0af1b --- /dev/null +++ b/spec/fixtures/files/geojson/gpslogger_example.json @@ -0,0 +1,23 @@ +{ + "features": [ + { + "geometry": { + "coordinates": [ + 106.64234449272531, + 10.758321212464024 + ], + "type": "Point" + }, + "properties": { + "accuracy": 4.7551565, + "altitude": 17.634344400269068, + "provider": "gps", + "speed": 1.2, + "time": "2024-11-03T16:30:11.331+07:00", + "time_long": 1730626211331 + }, + "type": "Feature" + } + ], + "type": "FeatureCollection" +} diff --git a/spec/fixtures/files/gpx/garmin_example.gpx b/spec/fixtures/files/gpx/garmin_example.gpx new file mode 100644 index 00000000..04c7a6dd --- /dev/null +++ b/spec/fixtures/files/gpx/garmin_example.gpx @@ -0,0 +1,31 @@ + + + + + + + 20241103 + + + 17.634344400269068 + + + + 2.8 + + + -1.6 + gps + 3 + 1.9 + 8.6 + 8.8 + + + + diff --git a/spec/services/geojson/params_spec.rb b/spec/services/geojson/params_spec.rb new file mode 100644 index 00000000..ed0e245b --- /dev/null +++ b/spec/services/geojson/params_spec.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Geojson::Params do + describe '#call' do + let(:file_path) { Rails.root.join('spec/fixtures/files/geojson/export.json') } + let(:file) { File.read(file_path) } + let(:json) { JSON.parse(file) } + let(:params) { described_class.new(json) } + + subject { params.call } + + it 'returns an array of points' do + expect(subject).to be_an_instance_of(Array) + expect(subject.first).to be_an_instance_of(Hash) + end + + it 'returns the correct data for each point' do + expect(subject.first).to eq( + latitude: '0.0', + longitude: '0.0', + battery_status: nil, + battery: nil, + timestamp: Time.zone.at(1_609_459_201), + altitude: 1, + velocity: 0, + tracker_id: nil, + ssid: nil, + accuracy: 1, + vertical_accuracy: 1, + raw_data: { + 'type' => 'Feature', + 'geometry' => { + 'type' => 'Point', + 'coordinates' => [ + '0.0', + '0.0' + ] + }, + 'properties' => { + 'battery_status' => 'unplugged', + 'ping' => 'MyString', + 'battery' => 1, + 'tracker_id' => 'MyString', + 'topic' => 'MyString', + 'altitude' => 1, + 'longitude' => '0.1', + 'velocity' => 'MyString', + 'trigger' => 'background_event', + 'bssid' => 'MyString', + 'ssid' => 'MyString', + 'connection' => 'wifi', + 'vertical_accuracy' => 1, + 'accuracy' => 1, + 'timestamp' => 1_609_459_201, + 'latitude' => '0.1', + 'mode' => 1, + 'inrids' => [], + 'in_regions' => [], + 'raw_data' => '', + 'city' => nil, + 'country' => nil, + 'geodata' => {} + } + } + ) + end + + context 'when the json is exported from GPSLogger' do + let(:file_path) { Rails.root.join('spec/fixtures/files/geojson/gpslogger_example.json') } + + it 'returns the correct data for each point' do + expect(subject.first).to eq( + latitude: 10.758321212464024, + longitude: 106.64234449272531, + battery_status: nil, + battery: nil, + timestamp: Time.parse('2024-11-03T16:30:11.331+07:00').to_i, + altitude: 17.634344400269068, + velocity: 1.2, + tracker_id: nil, + ssid: nil, + accuracy: 4.7551565, + vertical_accuracy: nil, + raw_data: { + 'geometry' => { + 'coordinates' => [ + 106.64234449272531, + 10.758321212464024 + ], + 'type' => 'Point' + }, + 'properties' => { + 'accuracy' => 4.7551565, + 'altitude' => 17.634344400269068, + 'provider' => 'gps', + 'speed' => 1.2, + 'time' => '2024-11-03T16:30:11.331+07:00', + 'time_long' => 1_730_626_211_331 + }, + 'type' => 'Feature' + } + ) + end + end + end +end diff --git a/spec/services/gpx/track_parser_spec.rb b/spec/services/gpx/track_parser_spec.rb index 9e9a47c1..b1026143 100644 --- a/spec/services/gpx/track_parser_spec.rb +++ b/spec/services/gpx/track_parser_spec.rb @@ -11,31 +11,29 @@ RSpec.describe Gpx::TrackParser do let(:raw_data) { Hash.from_xml(File.read(file_path)) } let(:import) { create(:import, user:, name: 'gpx_track.gpx', raw_data:) } - context 'when file exists' do - context 'when file has a single segment' do - it 'creates points' do - expect { parser }.to change { Point.count }.by(301) - end - - it 'broadcasts importing progress' do - expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(301).times - - parser - end + context 'when file has a single segment' do + it 'creates points' do + expect { parser }.to change { Point.count }.by(301) end - context 'when file has multiple segments' do - let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/gpx_track_multiple_segments.gpx') } + it 'broadcasts importing progress' do + expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(301).times - it 'creates points' do - expect { parser }.to change { Point.count }.by(558) - end + parser + end + end - it 'broadcasts importing progress' do - expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(558).times + context 'when file has multiple segments' do + let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/gpx_track_multiple_segments.gpx') } - parser - end + it 'creates points' do + expect { parser }.to change { Point.count }.by(558) + end + + it 'broadcasts importing progress' do + expect_any_instance_of(Imports::Broadcaster).to receive(:broadcast_import_progress).exactly(558).times + + parser end end @@ -51,6 +49,30 @@ RSpec.describe Gpx::TrackParser do parser end + + it 'creates points with correct data' do + parser + + expect(Point.first.latitude).to eq(37.17221.to_d) + expect(Point.first.longitude).to eq(-3.55468.to_d) + expect(Point.first.altitude).to eq(1066) + expect(Point.first.timestamp).to eq(Time.zone.parse('2024-04-21T10:19:55Z').to_i) + expect(Point.first.velocity).to eq('2.9') + end + end + + context 'when file exported from Garmin' do + let(:file_path) { Rails.root.join('spec/fixtures/files/gpx/garmin_example.gpx') } + + it 'creates points with correct data' do + parser + + expect(Point.first.latitude).to eq(10.758321.to_d) + expect(Point.first.longitude).to eq(106.642344.to_d) + expect(Point.first.altitude).to eq(17) + expect(Point.first.timestamp).to eq(1_730_626_211) + expect(Point.first.velocity).to eq('2.8') + end end end end