From 63b92f695f14365e97a41197b0baa35daea70ba5 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Thu, 23 Jan 2025 15:53:32 +0100 Subject: [PATCH 01/17] Enable Postgis extension for PostgreSQL --- db/migrate/20250123145155_enable_postgis_extension.rb | 7 +++++++ db/schema.rb | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20250123145155_enable_postgis_extension.rb diff --git a/db/migrate/20250123145155_enable_postgis_extension.rb b/db/migrate/20250123145155_enable_postgis_extension.rb new file mode 100644 index 00000000..e9d816dd --- /dev/null +++ b/db/migrate/20250123145155_enable_postgis_extension.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class EnablePostgisExtension < ActiveRecord::Migration[8.0] + def change + enable_extension 'postgis' + end +end diff --git a/db/schema.rb b/db/schema.rb index ebf1007e..f865b48d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,10 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_01_20_154555) do +ActiveRecord::Schema[8.0].define(version: 2025_01_23_145155) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" + enable_extension "postgis" create_table "action_text_rich_texts", force: :cascade do |t| t.string "name", null: false @@ -177,6 +178,14 @@ t.index ["visit_id"], name: "index_points_on_visit_id" end + create_table "spatial_ref_sys", primary_key: "srid", id: :integer, default: nil, force: :cascade do |t| + t.string "auth_name", limit: 256 + t.integer "auth_srid" + t.string "srtext", limit: 2048 + t.string "proj4text", limit: 2048 + t.check_constraint "srid > 0 AND srid <= 998999", name: "spatial_ref_sys_srid_check" + end + create_table "stats", force: :cascade do |t| t.integer "year", null: false t.integer "month", null: false From 774de9991b71b099a75d0bb5b55170ad0342fd48 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Thu, 23 Jan 2025 16:03:21 +0100 Subject: [PATCH 02/17] Add tracks model --- app/models/track.rb | 7 +++++++ app/models/user.rb | 3 ++- config/database.yml | 2 +- db/migrate/20250123145954_create_tracks.rb | 14 ++++++++++++++ db/schema.rb | 21 ++++++++++++--------- spec/factories/tracks.rb | 10 ++++++++++ spec/models/track_spec.rb | 15 +++++++++++++++ spec/models/user_spec.rb | 1 + 8 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 app/models/track.rb create mode 100644 db/migrate/20250123145954_create_tracks.rb create mode 100644 spec/factories/tracks.rb create mode 100644 spec/models/track_spec.rb diff --git a/app/models/track.rb b/app/models/track.rb new file mode 100644 index 00000000..41e673b4 --- /dev/null +++ b/app/models/track.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Track < ApplicationRecord + belongs_to :user + + validates :path, :started_at, :ended_at, presence: true +end diff --git a/app/models/user.rb b/app/models/user.rb index b3112130..90ff2fb0 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -13,7 +13,8 @@ class User < ApplicationRecord has_many :visits, dependent: :destroy has_many :points, through: :imports has_many :places, through: :visits - has_many :trips, dependent: :destroy + has_many :trips, dependent: :destroy + has_many :tracks, dependent: :destroy after_create :create_api_key before_save :strip_trailing_slashes diff --git a/config/database.yml b/config/database.yml index fca7a51c..79ad2b3b 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,5 +1,5 @@ default: &default - adapter: postgresql + adapter: postgis encoding: unicode database: <%= ENV['DATABASE_NAME'] %> username: <%= ENV['DATABASE_USERNAME'] %> diff --git a/db/migrate/20250123145954_create_tracks.rb b/db/migrate/20250123145954_create_tracks.rb new file mode 100644 index 00000000..168d2c12 --- /dev/null +++ b/db/migrate/20250123145954_create_tracks.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateTracks < ActiveRecord::Migration[8.0] + def change + create_table :tracks do |t| + t.datetime :started_at, null: false + t.datetime :ended_at, null: false + t.references :user, null: false, foreign_key: true + t.line_string :path, srid: 3785, null: false + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f865b48d..a85c60bd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_01_23_145155) do +ActiveRecord::Schema[8.0].define(version: 2025_01_23_145954) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" enable_extension "postgis" @@ -178,14 +178,6 @@ t.index ["visit_id"], name: "index_points_on_visit_id" end - create_table "spatial_ref_sys", primary_key: "srid", id: :integer, default: nil, force: :cascade do |t| - t.string "auth_name", limit: 256 - t.integer "auth_srid" - t.string "srtext", limit: 2048 - t.string "proj4text", limit: 2048 - t.check_constraint "srid > 0 AND srid <= 998999", name: "spatial_ref_sys_srid_check" - end - create_table "stats", force: :cascade do |t| t.integer "year", null: false t.integer "month", null: false @@ -201,6 +193,16 @@ t.index ["year"], name: "index_stats_on_year" end + create_table "tracks", force: :cascade do |t| + t.datetime "started_at", null: false + t.datetime "ended_at", null: false + t.bigint "user_id", null: false + t.geometry "path", limit: {:srid=>3785, :type=>"line_string"}, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_tracks_on_user_id" + end + create_table "trips", force: :cascade do |t| t.string "name", null: false t.datetime "started_at", null: false @@ -261,6 +263,7 @@ add_foreign_key "points", "users" add_foreign_key "points", "visits" add_foreign_key "stats", "users" + add_foreign_key "tracks", "users" add_foreign_key "trips", "users" add_foreign_key "visits", "areas" add_foreign_key "visits", "places" diff --git a/spec/factories/tracks.rb b/spec/factories/tracks.rb new file mode 100644 index 00000000..32603460 --- /dev/null +++ b/spec/factories/tracks.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :track do + started_at { DateTime.new(2025, 1, 23, 15, 59, 55) } + ended_at { DateTime.new(2025, 1, 23, 16, 0, 0) } + user + path { 'LINESTRING(0 0, 1 1, 2 2)' } + end +end diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb new file mode 100644 index 00000000..051b8ae8 --- /dev/null +++ b/spec/models/track_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Track, type: :model do + describe 'validations' do + it { is_expected.to validate_presence_of(:path) } + it { is_expected.to validate_presence_of(:started_at) } + it { is_expected.to validate_presence_of(:ended_at) } + end + + describe 'associations' do + it { is_expected.to belong_to(:user) } + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 398e436f..a9ce1d1e 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -14,6 +14,7 @@ it { is_expected.to have_many(:visits).dependent(:destroy) } it { is_expected.to have_many(:places).through(:visits) } it { is_expected.to have_many(:trips).dependent(:destroy) } + it { is_expected.to have_many(:tracks).dependent(:destroy) } end describe 'callbacks' do From 7db7fb17d55d490ab1ad6b3b8849b80c91c3b11c Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 12:01:54 +0100 Subject: [PATCH 03/17] Add path to trips --- Gemfile | 1 + Gemfile.lock | 1 + .../controllers/trips_controller.js | 26 +++++++++++-------- app/jobs/trips/create_path_job.rb | 10 +++++++ app/models/trip.rb | 6 +++++ app/services/tracks/build_path.rb | 21 +++++++++++++++ app/views/trips/show.html.erb | 1 + config/initializers/03_dawarich_settings.rb | 8 ++++++ .../20250123151849_create_paths_for_trips.rb | 10 +++++++ db/migrate/20250123145954_create_tracks.rb | 2 +- .../20250123151657_add_path_to_trips.rb | 7 +++++ db/schema.rb | 5 ++-- spec/jobs/trips/create_path_job_spec.rb | 5 ++++ 13 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 app/jobs/trips/create_path_job.rb create mode 100644 app/services/tracks/build_path.rb create mode 100644 db/data/20250123151849_create_paths_for_trips.rb create mode 100644 db/migrate/20250123151657_add_path_to_trips.rb create mode 100644 spec/jobs/trips/create_path_job_spec.rb diff --git a/Gemfile b/Gemfile index 92c6d14f..592c2fd3 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,7 @@ gem 'activerecord-postgis-adapter', github: 'StoneGod/activerecord-postgis-adapt gem 'puma' gem 'pundit' gem 'rails', '~> 8.0' +gem 'rgeo' gem 'rswag-api' gem 'rswag-ui' gem 'shrine', '~> 3.6' diff --git a/Gemfile.lock b/Gemfile.lock index 5460cf07..8407dd3a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -484,6 +484,7 @@ DEPENDENCIES pundit rails (~> 8.0) redis + rgeo rspec-rails rswag-api rswag-specs diff --git a/app/javascript/controllers/trips_controller.js b/app/javascript/controllers/trips_controller.js index 602c04be..f512a208 100644 --- a/app/javascript/controllers/trips_controller.js +++ b/app/javascript/controllers/trips_controller.js @@ -1,17 +1,21 @@ import { Controller } from "@hotwired/stimulus" import L from "leaflet" -import { osmMapLayer } from "../maps/layers" +import { + osmMapLayer, + osmHotMapLayer, + OPNVMapLayer, + openTopoMapLayer, + cyclOsmMapLayer, + esriWorldStreetMapLayer, + esriWorldTopoMapLayer, + esriWorldImageryMapLayer, + esriWorldGrayCanvasMapLayer +} from "../maps/layers" import { createPopupContent } from "../maps/popups" -import { osmHotMapLayer } from "../maps/layers" -import { OPNVMapLayer } from "../maps/layers" -import { openTopoMapLayer } from "../maps/layers" -import { cyclOsmMapLayer } from "../maps/layers" -import { esriWorldStreetMapLayer } from "../maps/layers" -import { esriWorldTopoMapLayer } from "../maps/layers" -import { esriWorldImageryMapLayer } from "../maps/layers" -import { esriWorldGrayCanvasMapLayer } from "../maps/layers" -import { fetchAndDisplayPhotos } from '../maps/helpers'; -import { showFlashMessage } from "../maps/helpers"; +import { + fetchAndDisplayPhotos, + showFlashMessage +} from '../maps/helpers'; export default class extends Controller { static targets = ["container", "startedAt", "endedAt"] diff --git a/app/jobs/trips/create_path_job.rb b/app/jobs/trips/create_path_job.rb new file mode 100644 index 00000000..f36fa7cd --- /dev/null +++ b/app/jobs/trips/create_path_job.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class Trips::CreatePathJob < ApplicationJob + queue_as :default + + def perform(trip_id) + trip = Trip.find(trip_id) + trip.create_path! + end +end diff --git a/app/models/trip.rb b/app/models/trip.rb index 4a2b0302..00c2774a 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -9,6 +9,12 @@ class Trip < ApplicationRecord before_save :calculate_distance + def create_path! + self.path = Tracks::BuildPath.new(points.pluck(:latitude, :longitude)).call + + save! + end + def points user.tracked_points.where(timestamp: started_at.to_i..ended_at.to_i).order(:timestamp) end diff --git a/app/services/tracks/build_path.rb b/app/services/tracks/build_path.rb new file mode 100644 index 00000000..4feaf49c --- /dev/null +++ b/app/services/tracks/build_path.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class Tracks::BuildPath + def initialize(coordinates) + @coordinates = coordinates + end + + def call + factory.line_string( + coordinates.map { |point| factory.point(point[1].to_f.round(5), point[0].to_f.round(5)) } + ) + end + + private + + attr_reader :coordinates + + def factory + @factory ||= RGeo::Geographic.spherical_factory(srid: 3857) + end +end diff --git a/app/views/trips/show.html.erb b/app/views/trips/show.html.erb index f399eb3f..9d6dc4ee 100644 --- a/app/views/trips/show.html.erb +++ b/app/views/trips/show.html.erb @@ -25,6 +25,7 @@ data-api_key="<%= current_user.api_key %>" data-user_settings="<%= current_user.settings.to_json %>" data-coordinates="<%= @coordinates.to_json %>" + data-path="<%= @trip.path.to_json %>" data-timezone="<%= Rails.configuration.time_zone %>">
diff --git a/config/initializers/03_dawarich_settings.rb b/config/initializers/03_dawarich_settings.rb index 87cf4817..451ed716 100644 --- a/config/initializers/03_dawarich_settings.rb +++ b/config/initializers/03_dawarich_settings.rb @@ -17,5 +17,13 @@ def photon_uses_komoot_io? def geoapify_enabled? @geoapify_enabled ||= GEOAPIFY_API_KEY.present? end + + def meters_between_tracks + @meters_between_tracks ||= 300 + end + + def minutes_between_tracks + @minutes_between_tracks ||= 20 + end end end diff --git a/db/data/20250123151849_create_paths_for_trips.rb b/db/data/20250123151849_create_paths_for_trips.rb new file mode 100644 index 00000000..6abcfff4 --- /dev/null +++ b/db/data/20250123151849_create_paths_for_trips.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class CreatePathsForTrips < ActiveRecord::Migration[8.0] + def up + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/migrate/20250123145954_create_tracks.rb b/db/migrate/20250123145954_create_tracks.rb index 168d2c12..35c6afa1 100644 --- a/db/migrate/20250123145954_create_tracks.rb +++ b/db/migrate/20250123145954_create_tracks.rb @@ -6,7 +6,7 @@ def change t.datetime :started_at, null: false t.datetime :ended_at, null: false t.references :user, null: false, foreign_key: true - t.line_string :path, srid: 3785, null: false + t.line_string :path, srid: 3857, null: false t.timestamps end diff --git a/db/migrate/20250123151657_add_path_to_trips.rb b/db/migrate/20250123151657_add_path_to_trips.rb new file mode 100644 index 00000000..a5f121e7 --- /dev/null +++ b/db/migrate/20250123151657_add_path_to_trips.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddPathToTrips < ActiveRecord::Migration[8.0] + def change + add_column :trips, :path, :line_string, srid: 3857 + end +end diff --git a/db/schema.rb b/db/schema.rb index a85c60bd..7e9cca52 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_01_23_145954) do +ActiveRecord::Schema[8.0].define(version: 2025_01_23_151657) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" enable_extension "postgis" @@ -197,7 +197,7 @@ t.datetime "started_at", null: false t.datetime "ended_at", null: false t.bigint "user_id", null: false - t.geometry "path", limit: {:srid=>3785, :type=>"line_string"}, null: false + t.geometry "path", limit: {:srid=>3857, :type=>"line_string"}, null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["user_id"], name: "index_tracks_on_user_id" @@ -211,6 +211,7 @@ t.bigint "user_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.geometry "path", limit: {:srid=>3857, :type=>"line_string"} t.index ["user_id"], name: "index_trips_on_user_id" end diff --git a/spec/jobs/trips/create_path_job_spec.rb b/spec/jobs/trips/create_path_job_spec.rb new file mode 100644 index 00000000..1dd711ef --- /dev/null +++ b/spec/jobs/trips/create_path_job_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Trips::CreatePathJob, type: :job do + pending "add some examples to (or delete) #{__FILE__}" +end From 1e7efbc9afbb8ed89cdd252a802f24386f6884a7 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 14:54:10 +0100 Subject: [PATCH 04/17] Render trips using precalculated paths instead of list of coordinates --- CHANGELOG.md | 2 + app/controllers/trips_controller.rb | 5 -- .../controllers/trips_controller.js | 51 ++++++++++++++----- app/views/trips/show.html.erb | 1 - 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b70a9eb..e66d12ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - Enabled Postgis extension for PostgreSQL. +- Trips are now store their paths in the database independently of the points. +- Trips are now being rendered on the map using their precalculated paths instead of list of coordinates. # 0.23.5 - 2025-01-22 diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb index 2a9a26d2..038d4842 100644 --- a/app/controllers/trips_controller.rb +++ b/app/controllers/trips_controller.rb @@ -10,11 +10,6 @@ def index end def show - @coordinates = @trip.points.pluck( - :latitude, :longitude, :battery, :altitude, :timestamp, :velocity, :id, - :country - ).map { [_1.to_f, _2.to_f, _3.to_s, _4.to_s, _5.to_s, _6.to_s, _7.to_s, _8.to_s] } - @photo_previews = Rails.cache.fetch("trip_photos_#{@trip.id}", expires_in: 1.day) do @trip.photo_previews end diff --git a/app/javascript/controllers/trips_controller.js b/app/javascript/controllers/trips_controller.js index f512a208..c40b1426 100644 --- a/app/javascript/controllers/trips_controller.js +++ b/app/javascript/controllers/trips_controller.js @@ -27,9 +27,9 @@ export default class extends Controller { } console.log("Trips controller connected") - this.coordinates = JSON.parse(this.containerTarget.dataset.coordinates) + this.apiKey = this.containerTarget.dataset.api_key - this.userSettings = JSON.parse(this.containerTarget.dataset.user_settings) + this.userSettings = JSON.parse(this.containerTarget.dataset.user_settings || '{}') this.timezone = this.containerTarget.dataset.timezone this.distanceUnit = this.containerTarget.dataset.distance_unit @@ -46,16 +46,12 @@ export default class extends Controller { // Move map initialization to separate method initializeMap() { // Initialize layer groups - this.markersLayer = L.layerGroup() this.polylinesLayer = L.layerGroup() this.photoMarkers = L.layerGroup() // Set default center and zoom for world view - const hasValidCoordinates = this.coordinates && Array.isArray(this.coordinates) && this.coordinates.length > 0 - const center = hasValidCoordinates - ? [this.coordinates[0][0], this.coordinates[0][1]] - : [20, 0] // Roughly centers the world map - const zoom = hasValidCoordinates ? 14 : 2 + const center = [20, 0] // Roughly centers the world map + const zoom = 2 // Initialize map this.map = L.map(this.containerTarget).setView(center, zoom) @@ -72,7 +68,6 @@ export default class extends Controller { }).addTo(this.map) const overlayMaps = { - "Points": this.markersLayer, "Route": this.polylinesLayer, "Photos": this.photoMarkers } @@ -116,6 +111,27 @@ export default class extends Controller { this.addPolyline() this.fitMapToBounds() } + + // After map initialization, add the path if it exists + if (this.containerTarget.dataset.path) { + const pathData = this.containerTarget.dataset.path.replace(/^"|"$/g, ''); // Remove surrounding quotes + const coordinates = this.parseLineString(pathData); + + const polyline = L.polyline(coordinates, { + color: 'blue', + opacity: 0.8, + weight: 3, + zIndexOffset: 400 + }); + + polyline.addTo(this.polylinesLayer); + this.polylinesLayer.addTo(this.map); + + // Fit the map to the polyline bounds + if (coordinates.length > 0) { + this.map.fitBounds(polyline.getBounds(), { padding: [50, 50] }); + } + } } disconnect() { @@ -153,9 +169,6 @@ export default class extends Controller { const popupContent = createPopupContent(coord, this.timezone, this.distanceUnit) marker.bindPopup(popupContent) - - // Add to markers layer instead of directly to map - marker.addTo(this.markersLayer) }) } @@ -191,7 +204,6 @@ export default class extends Controller { ]).sort((a, b) => a[4] - b[4]); // Clear existing layers - this.markersLayer.clearLayers() this.polylinesLayer.clearLayers() this.photoMarkers.clearLayers() @@ -202,4 +214,17 @@ export default class extends Controller { this.fitMapToBounds() } } + + // Add this method to parse the LineString format + parseLineString(lineString) { + // Remove LINESTRING and parentheses, then split into coordinate pairs + const coordsString = lineString.replace('LINESTRING (', '').replace(')', ''); + const coords = coordsString.split(', '); + + // Convert each coordinate pair to [lat, lng] format + return coords.map(coord => { + const [lng, lat] = coord.split(' ').map(Number); + return [lat, lng]; // Swap to lat, lng for Leaflet + }); + } } diff --git a/app/views/trips/show.html.erb b/app/views/trips/show.html.erb index 9d6dc4ee..44a4ce53 100644 --- a/app/views/trips/show.html.erb +++ b/app/views/trips/show.html.erb @@ -24,7 +24,6 @@ data-distance_unit="<%= DISTANCE_UNIT %>" data-api_key="<%= current_user.api_key %>" data-user_settings="<%= current_user.settings.to_json %>" - data-coordinates="<%= @coordinates.to_json %>" data-path="<%= @trip.path.to_json %>" data-timezone="<%= Rails.configuration.time_zone %>">
From 380dd9235d711db77ae61d3fa4964f8e244dd486 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 15:03:57 +0100 Subject: [PATCH 05/17] Calculate path and distance before saving trip --- app/assets/stylesheets/actiontext.css | 3 ++- app/javascript/controllers/datetime_controller.js | 4 ++++ app/javascript/controllers/trips_controller.js | 7 ++++++- app/models/trip.rb | 7 ++++--- app/views/trips/_form.html.erb | 2 +- 5 files changed, 17 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/actiontext.css b/app/assets/stylesheets/actiontext.css index b849676e..ae5522ab 100644 --- a/app/assets/stylesheets/actiontext.css +++ b/app/assets/stylesheets/actiontext.css @@ -40,6 +40,7 @@ background-color: white !important; } -.trix-content { +.trix-content-editor { min-height: 10rem; + width: 100%; } diff --git a/app/javascript/controllers/datetime_controller.js b/app/javascript/controllers/datetime_controller.js index 04c9061b..b56f07e3 100644 --- a/app/javascript/controllers/datetime_controller.js +++ b/app/javascript/controllers/datetime_controller.js @@ -1,3 +1,7 @@ +// This controller is being used on: +// - trips/new +// - trips/edit + import { Controller } from "@hotwired/stimulus" export default class extends Controller { diff --git a/app/javascript/controllers/trips_controller.js b/app/javascript/controllers/trips_controller.js index c40b1426..1d2e8c7e 100644 --- a/app/javascript/controllers/trips_controller.js +++ b/app/javascript/controllers/trips_controller.js @@ -1,3 +1,8 @@ +// This controller is being used on: +// - trips/show +// - trips/edit +// - trips/new + import { Controller } from "@hotwired/stimulus" import L from "leaflet" import { @@ -192,7 +197,7 @@ export default class extends Controller { this.map.fitBounds(bounds, { padding: [50, 50] }) } - // Add this new method to update coordinates and refresh the map + // Update coordinates and refresh the map updateMapWithCoordinates(newCoordinates) { // Transform the coordinates to match the expected format this.coordinates = newCoordinates.map(point => [ diff --git a/app/models/trip.rb b/app/models/trip.rb index 00c2774a..4043e8c7 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -7,12 +7,13 @@ class Trip < ApplicationRecord validates :name, :started_at, :ended_at, presence: true - before_save :calculate_distance + before_save :create_path! def create_path! - self.path = Tracks::BuildPath.new(points.pluck(:latitude, :longitude)).call + trip_path = Tracks::BuildPath.new(points.pluck(:latitude, :longitude)).call + distance = calculate_distance - save! + update_columns(path: trip_path, distance: distance) # Avoids recursion with `after_save` end def points diff --git a/app/views/trips/_form.html.erb b/app/views/trips/_form.html.erb index cf5518ff..40dc57e3 100644 --- a/app/views/trips/_form.html.erb +++ b/app/views/trips/_form.html.erb @@ -62,7 +62,7 @@
<%= form.label :notes %> - <%= form.rich_text_area :notes %> + <%= form.rich_text_area :notes, class: 'trix-content-editor' %>
From 401ac8ca31e7acc677b9c38cd4510fac276e4a22 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 15:19:05 +0100 Subject: [PATCH 06/17] Use path instead of coordinates on trips#index --- .../controllers/trip_map_controller.js | 76 +++++++++++++++---- app/views/trips/_trip.html.erb | 2 +- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/app/javascript/controllers/trip_map_controller.js b/app/javascript/controllers/trip_map_controller.js index b2a18bfb..1bbdc207 100644 --- a/app/javascript/controllers/trip_map_controller.js +++ b/app/javascript/controllers/trip_map_controller.js @@ -1,10 +1,13 @@ +// This controller is being used on: +// - trips/index + import { Controller } from "@hotwired/stimulus" import L from "leaflet" export default class extends Controller { static values = { tripId: Number, - coordinates: Array, + path: String, apiKey: String, userSettings: Object, timezone: String, @@ -12,6 +15,8 @@ export default class extends Controller { } connect() { + console.log("TripMap controller connected") + setTimeout(() => { this.initializeMap() }, 100) @@ -23,7 +28,7 @@ export default class extends Controller { zoomControl: false, dragging: false, scrollWheelZoom: false, - attributionControl: true // Disable default attribution control + attributionControl: true }) // Add the tile layer @@ -33,24 +38,69 @@ export default class extends Controller { }).addTo(this.map) // If we have coordinates, show the route - if (this.hasCoordinatesValue && this.coordinatesValue.length > 0) { + if (this.hasPathValue && this.pathValue) { this.showRoute() + } else { + console.log("No path value available") } } showRoute() { - const points = this.coordinatesValue.map(coord => [coord[0], coord[1]]) + const points = this.parseLineString(this.pathValue) - const polyline = L.polyline(points, { - color: 'blue', - opacity: 0.8, - weight: 3, - zIndexOffset: 400 - }).addTo(this.map) + // Only create polyline if we have points + if (points.length > 0) { + const polyline = L.polyline(points, { + color: 'blue', + opacity: 0.8, + weight: 3, + zIndexOffset: 400 + }) - this.map.fitBounds(polyline.getBounds(), { - padding: [20, 20] - }) + // Add the polyline to the map + polyline.addTo(this.map) + + // Fit the map bounds + this.map.fitBounds(polyline.getBounds(), { + padding: [20, 20] + }) + } else { + console.error("No valid points to create polyline") + } + } + + parseLineString(linestring) { + try { + // Remove 'LINESTRING (' from start and ')' from end + const coordsString = linestring + .replace(/LINESTRING\s*\(/, '') // Remove LINESTRING and opening parenthesis + .replace(/\)$/, '') // Remove closing parenthesis + .trim() // Remove any leading/trailing whitespace + + // Split into coordinate pairs and parse + const points = coordsString.split(',').map(pair => { + // Clean up any extra whitespace and remove any special characters + const cleanPair = pair.trim().replace(/[()"\s]+/g, ' ') + const [lng, lat] = cleanPair.split(' ').filter(Boolean).map(Number) + + // Validate the coordinates + if (isNaN(lat) || isNaN(lng) || !lat || !lng) { + console.error("Invalid coordinates:", cleanPair) + return null + } + + return [lat, lng] // Leaflet uses [lat, lng] order + }).filter(point => point !== null) // Remove any invalid points + + // Validate we have points before returning + if (points.length === 0) { + return [] + } + + return points + } catch (error) { + return [] + } } disconnect() { diff --git a/app/views/trips/_trip.html.erb b/app/views/trips/_trip.html.erb index e0b14ba8..f7a198b6 100644 --- a/app/views/trips/_trip.html.erb +++ b/app/views/trips/_trip.html.erb @@ -13,7 +13,7 @@ class="rounded-lg z-0" data-controller="trip-map" data-trip-map-trip-id-value="<%= trip.id %>" - data-trip-map-coordinates-value="<%= trip.points.pluck(:latitude, :longitude, :battery, :altitude, :timestamp, :velocity, :id, :country).to_json %>" + data-trip-map-path-value="<%= trip.path.to_json %>" data-trip-map-api-key-value="<%= current_user.api_key %>" data-trip-map-user-settings-value="<%= current_user.settings.to_json %>" data-trip-map-timezone-value="<%= Rails.configuration.time_zone %>" From 9c102c1de8ccc6278e9e63341c2b581cd27f6777 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 15:22:46 +0100 Subject: [PATCH 07/17] Fix rendering polyline on trip editing page --- app/views/trips/_form.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/trips/_form.html.erb b/app/views/trips/_form.html.erb index 40dc57e3..b70c5704 100644 --- a/app/views/trips/_form.html.erb +++ b/app/views/trips/_form.html.erb @@ -20,7 +20,7 @@ data-distance_unit="<%= DISTANCE_UNIT %>" data-api_key="<%= current_user.api_key %>" data-user_settings="<%= current_user.settings.to_json %>" - data-coordinates="<%= @coordinates.to_json %>" + data-path="<%= trip.path.to_json %>" data-timezone="<%= Rails.configuration.time_zone %>">
From 6e9c981329794a0486a9afedc5868bd9409f5812 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 15:35:35 +0100 Subject: [PATCH 08/17] Fix photos fetching with trip dates --- .../controllers/trips_controller.js | 38 +++++++++++++++---- app/views/trips/_form.html.erb | 2 + app/views/trips/show.html.erb | 2 + 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/app/javascript/controllers/trips_controller.js b/app/javascript/controllers/trips_controller.js index 1d2e8c7e..974feb30 100644 --- a/app/javascript/controllers/trips_controller.js +++ b/app/javascript/controllers/trips_controller.js @@ -43,7 +43,6 @@ export default class extends Controller { // Add event listener for coordinates updates this.element.addEventListener('coordinates-updated', (event) => { - console.log("Coordinates updated:", event.detail.coordinates) this.updateMapWithCoordinates(event.detail.coordinates) }) } @@ -84,6 +83,15 @@ export default class extends Controller { this.map.on('overlayadd', (e) => { if (e.name !== 'Photos') return; + const startedAt = this.element.dataset.started_at; + const endedAt = this.element.dataset.ended_at; + + console.log('Dataset values:', { + startedAt, + endedAt, + path: this.element.dataset.path + }); + if ((!this.userSettings.immich_url || !this.userSettings.immich_api_key) && (!this.userSettings.photoprism_url || !this.userSettings.photoprism_api_key)) { showFlashMessage( 'error', @@ -92,13 +100,26 @@ export default class extends Controller { return; } - if (!this.coordinates?.length) return; - - const firstCoord = this.coordinates[0]; - const lastCoord = this.coordinates[this.coordinates.length - 1]; - - const startDate = new Date(firstCoord[4] * 1000).toISOString().split('T')[0]; - const endDate = new Date(lastCoord[4] * 1000).toISOString().split('T')[0]; + // Try to get dates from coordinates first, then fall back to path data + let startDate, endDate; + + if (this.coordinates?.length) { + const firstCoord = this.coordinates[0]; + const lastCoord = this.coordinates[this.coordinates.length - 1]; + startDate = new Date(firstCoord[4] * 1000).toISOString().split('T')[0]; + endDate = new Date(lastCoord[4] * 1000).toISOString().split('T')[0]; + } else if (startedAt && endedAt) { + // Parse the dates and format them correctly + startDate = new Date(startedAt).toISOString().split('T')[0]; + endDate = new Date(endedAt).toISOString().split('T')[0]; + } else { + console.log('No date range available for photos'); + showFlashMessage( + 'error', + 'No date range available for photos. Please ensure the trip has start and end dates.' + ); + return; + } fetchAndDisplayPhotos({ map: this.map, @@ -174,6 +195,7 @@ export default class extends Controller { const popupContent = createPopupContent(coord, this.timezone, this.distanceUnit) marker.bindPopup(popupContent) + marker.addTo(this.polylinesLayer) }) } diff --git a/app/views/trips/_form.html.erb b/app/views/trips/_form.html.erb index b70c5704..847c2df2 100644 --- a/app/views/trips/_form.html.erb +++ b/app/views/trips/_form.html.erb @@ -21,6 +21,8 @@ data-api_key="<%= current_user.api_key %>" data-user_settings="<%= current_user.settings.to_json %>" data-path="<%= trip.path.to_json %>" + data-started_at="<%= trip.started_at %>" + data-ended_at="<%= trip.ended_at %>" data-timezone="<%= Rails.configuration.time_zone %>"> diff --git a/app/views/trips/show.html.erb b/app/views/trips/show.html.erb index 44a4ce53..f4709aa5 100644 --- a/app/views/trips/show.html.erb +++ b/app/views/trips/show.html.erb @@ -25,6 +25,8 @@ data-api_key="<%= current_user.api_key %>" data-user_settings="<%= current_user.settings.to_json %>" data-path="<%= @trip.path.to_json %>" + data-started_at="<%= @trip.started_at %>" + data-ended_at="<%= @trip.ended_at %>" data-timezone="<%= Rails.configuration.time_zone %>">
From 01275d0d2e39490f452bd02876ea19589b474065 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 15:58:44 +0100 Subject: [PATCH 09/17] Add some tests --- app/models/trip.rb | 4 +-- .../20250123151849_create_paths_for_trips.rb | 3 ++ spec/factories/trips.rb | 2 ++ spec/jobs/trips/create_path_job_spec.rb | 20 ++++++++++- spec/models/trip_spec.rb | 4 +++ spec/services/tracks/build_path_spec.rb | 35 +++++++++++++++++++ 6 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 spec/services/tracks/build_path_spec.rb diff --git a/app/models/trip.rb b/app/models/trip.rb index 4043e8c7..cd4f7225 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -11,9 +11,9 @@ class Trip < ApplicationRecord def create_path! trip_path = Tracks::BuildPath.new(points.pluck(:latitude, :longitude)).call - distance = calculate_distance - update_columns(path: trip_path, distance: distance) # Avoids recursion with `after_save` + self.distance = calculate_distance + self.path = trip_path end def points diff --git a/db/data/20250123151849_create_paths_for_trips.rb b/db/data/20250123151849_create_paths_for_trips.rb index 6abcfff4..c78cffff 100644 --- a/db/data/20250123151849_create_paths_for_trips.rb +++ b/db/data/20250123151849_create_paths_for_trips.rb @@ -2,6 +2,9 @@ class CreatePathsForTrips < ActiveRecord::Migration[8.0] def up + Trip.find_each do |trip| + Trips::CreatePathJob.perform_later(trip.id) + end end def down diff --git a/spec/factories/trips.rb b/spec/factories/trips.rb index 4ef4041a..5986e882 100644 --- a/spec/factories/trips.rb +++ b/spec/factories/trips.rb @@ -7,6 +7,8 @@ started_at { DateTime.new(2024, 11, 27, 17, 16, 21) } ended_at { DateTime.new(2024, 11, 29, 17, 16, 21) } notes { FFaker::Lorem.sentence } + distance { 100 } + path { 'LINESTRING(1 1, 2 2, 3 3)' } trait :with_points do after(:build) do |trip| diff --git a/spec/jobs/trips/create_path_job_spec.rb b/spec/jobs/trips/create_path_job_spec.rb index 1dd711ef..60d288e3 100644 --- a/spec/jobs/trips/create_path_job_spec.rb +++ b/spec/jobs/trips/create_path_job_spec.rb @@ -1,5 +1,23 @@ +# frozen_string_literal: true + require 'rails_helper' RSpec.describe Trips::CreatePathJob, type: :job do - pending "add some examples to (or delete) #{__FILE__}" + let!(:trip) { create(:trip, :with_points) } + let(:points) { trip.points } + let(:trip_path) do + "LINESTRING (#{points.map do |point| + "#{point.longitude.to_f.round(5)} #{point.latitude.to_f.round(5)}" + end.join(', ')})" + end + + before do + trip.update(path: nil, distance: nil) + end + + it 'creates a path for a trip' do + described_class.perform_now(trip.id) + + expect(trip.reload.path.to_s).to eq(trip_path) + end end diff --git a/spec/models/trip_spec.rb b/spec/models/trip_spec.rb index 032185bd..f56daf20 100644 --- a/spec/models/trip_spec.rb +++ b/spec/models/trip_spec.rb @@ -21,6 +21,10 @@ it 'sets the distance' do expect(trip.distance).to eq(calculated_distance) end + + it 'sets the path' do + expect(trip.path).to be_present + end end describe '#countries' do diff --git a/spec/services/tracks/build_path_spec.rb b/spec/services/tracks/build_path_spec.rb new file mode 100644 index 00000000..1d2db10a --- /dev/null +++ b/spec/services/tracks/build_path_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Tracks::BuildPath do + describe '#call' do + let(:coordinates) do + [ + [45.123456, -122.654321], # [lat, lng] + [45.234567, -122.765432], + [45.345678, -122.876543] + ] + end + + let(:service) { described_class.new(coordinates) } + let(:result) { service.call } + + it 'returns an RGeo::Geographic::SphericalLineString' do + expect(result).to be_a(RGeo::Geographic::SphericalLineStringImpl) + end + + it 'creates a line string with the correct number of points' do + expect(result.num_points).to eq(coordinates.length) + end + + it 'correctly converts coordinates to points with rounded values' do + points = result.points + + coordinates.each_with_index do |(lat, lng), index| + expect(points[index].x).to eq(lng.to_f.round(5)) + expect(points[index].y).to eq(lat.to_f.round(5)) + end + end + end +end From 5bd6a6c072935d6392e270ff5283b660307f8392 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Fri, 24 Jan 2025 16:37:28 +0100 Subject: [PATCH 10/17] Don't trim time from start and end dates --- CHANGELOG.md | 4 ++++ app/javascript/controllers/maps_controller.js | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e66d12ac..95de7bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Trips are now store their paths in the database independently of the points. - Trips are now being rendered on the map using their precalculated paths instead of list of coordinates. +### Changed + +- Requesting photos on the Map page now uses the start and end dates from the URL params. #589 + # 0.23.5 - 2025-01-22 ### Added diff --git a/app/javascript/controllers/maps_controller.js b/app/javascript/controllers/maps_controller.js index 313b477d..997821da 100644 --- a/app/javascript/controllers/maps_controller.js +++ b/app/javascript/controllers/maps_controller.js @@ -218,8 +218,8 @@ export default class extends Controller { } const urlParams = new URLSearchParams(window.location.search); - const startDate = urlParams.get('start_at')?.split('T')[0] || new Date().toISOString().split('T')[0]; - const endDate = urlParams.get('end_at')?.split('T')[0] || new Date().toISOString().split('T')[0]; + const startDate = urlParams.get('start_at') || new Date().toISOString(); + const endDate = urlParams.get('end_at')|| new Date().toISOString(); await fetchAndDisplayPhotos({ map: this.map, photoMarkers: this.photoMarkers, From fd47bf7d5dca6f84e0c1f6850df14f67d7a470c8 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 11:43:02 +0100 Subject: [PATCH 11/17] Update trip path calculation --- CONTRIBUTING.md | 2 +- README.md | 2 +- app/jobs/trips/create_path_job.rb | 5 ++++- app/models/trip.rb | 18 ++++++++++++------ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 00214a48..d1470f1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,7 +8,7 @@ #### **Did you write a patch that fixes a bug?** -* Open a new GitHub pull request with the patch. +* Open a new GitHub pull request with the patch against the `dev` branch. * Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable. diff --git a/README.md b/README.md index a087aab4..0d21ed03 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Donate using crypto: [0x6bAd13667692632f1bF926cA9B421bEe7EaEB8D4](https://ethers - Explore statistics like the number of countries and cities visited, total distance traveled, and more! 📄 **Changelog**: Find the latest updates [here](CHANGELOG.md). - +👩‍💻 **Contribute**: See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute to Dawarich. --- ## ⚠️ Disclaimer diff --git a/app/jobs/trips/create_path_job.rb b/app/jobs/trips/create_path_job.rb index f36fa7cd..d64a39ec 100644 --- a/app/jobs/trips/create_path_job.rb +++ b/app/jobs/trips/create_path_job.rb @@ -5,6 +5,9 @@ class Trips::CreatePathJob < ApplicationJob def perform(trip_id) trip = Trip.find(trip_id) - trip.create_path! + + trip.calculate_path_and_distance + + trip.save! end end diff --git a/app/models/trip.rb b/app/models/trip.rb index cd4f7225..5e094078 100644 --- a/app/models/trip.rb +++ b/app/models/trip.rb @@ -7,15 +7,14 @@ class Trip < ApplicationRecord validates :name, :started_at, :ended_at, presence: true - before_save :create_path! + before_save :calculate_path_and_distance - def create_path! - trip_path = Tracks::BuildPath.new(points.pluck(:latitude, :longitude)).call - - self.distance = calculate_distance - self.path = trip_path + def calculate_path_and_distance + calculate_path + calculate_distance end + def points user.tracked_points.where(timestamp: started_at.to_i..ended_at.to_i).order(:timestamp) end @@ -47,6 +46,13 @@ def select_dominant_orientation(photos) vertical_photos.count > horizontal_photos.count ? vertical_photos : horizontal_photos end + def calculate_path + trip_path = Tracks::BuildPath.new(points.pluck(:latitude, :longitude)).call + + self.path = trip_path + end + + def calculate_distance distance = 0 From 5913b65ca84770437359f92500c3e288647e9aea Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 11:46:41 +0100 Subject: [PATCH 12/17] Update CircleCI config --- .circleci/config.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d0055f31..df09558f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,11 +10,15 @@ jobs: - image: cimg/ruby:3.3.4 environment: RAILS_ENV: test - - image: cimg/postgres:13.3 + - image: cimg/postgres:14.0 environment: POSTGRES_USER: postgres POSTGRES_DB: test_database POSTGRES_PASSWORD: mysecretpassword + POSTGRES_INITDB_ARGS: --enable-debug --data-checksums --encoding=UTF8 --lc-collate=C --lc-ctype=C + command: | + apt-get update + apt-get install -y postgis postgresql-14-postgis-3 - image: redis:7.0 steps: From 20d38625488f0c84295081507637a026f903971e Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 11:49:15 +0100 Subject: [PATCH 13/17] Update database config for CI --- config/database.ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/database.ci.yml b/config/database.ci.yml index c5ee5c9d..d5e13575 100644 --- a/config/database.ci.yml +++ b/config/database.ci.yml @@ -1,8 +1,9 @@ # config/database.ci.yml test: - adapter: postgresql + adapter: postgis encoding: unicode pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + host: localhost database: <%= ENV["POSTGRES_DB"] %> username: <%= ENV['POSTGRES_USER'] %> password: <%= ENV["POSTGRES_PASSWORD"] %> From cb9e11c18a6d1ba3cde685fab1c80936b02e555e Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 11:53:02 +0100 Subject: [PATCH 14/17] Update CircleCI config --- .circleci/config.yml | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index df09558f..210c03cb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,6 +10,10 @@ jobs: - image: cimg/ruby:3.3.4 environment: RAILS_ENV: test + POSTGRES_HOST: localhost + POSTGRES_USER: postgres + POSTGRES_PASSWORD: mysecretpassword + POSTGRES_DB: test_database - image: cimg/postgres:14.0 environment: POSTGRES_USER: postgres @@ -23,17 +27,28 @@ jobs: steps: - checkout + - run: + name: Install System Dependencies + command: | + sudo apt-get update + sudo apt-get install -y postgresql-client + wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz + sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz + rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Install Bundler command: gem install bundler - run: name: Bundle Install command: bundle install --jobs=4 --retry=3 + - run: + name: Wait for PostgreSQL + command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: name: Database Setup command: | - bundle exec rails db:create - bundle exec rails db:schema:load + cp config/database.ci.yml config/database.yml + bundle exec rails db:create db:schema:load - run: name: Run RSpec tests command: bundle exec rspec From 3139d2897130439de19fca890f5dacc70e006896 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 11:55:33 +0100 Subject: [PATCH 15/17] Update CircleCI config --- .circleci/config.yml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 210c03cb..bd38b09c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,11 +10,7 @@ jobs: - image: cimg/ruby:3.3.4 environment: RAILS_ENV: test - POSTGRES_HOST: localhost - POSTGRES_USER: postgres - POSTGRES_PASSWORD: mysecretpassword - POSTGRES_DB: test_database - - image: cimg/postgres:14.0 + - image: cimg/postgres:14.0-postgis environment: POSTGRES_USER: postgres POSTGRES_DB: test_database @@ -27,28 +23,17 @@ jobs: steps: - checkout - - run: - name: Install System Dependencies - command: | - sudo apt-get update - sudo apt-get install -y postgresql-client - wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz - sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz - rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Install Bundler command: gem install bundler - run: name: Bundle Install command: bundle install --jobs=4 --retry=3 - - run: - name: Wait for PostgreSQL - command: dockerize -wait tcp://localhost:5432 -timeout 1m - run: name: Database Setup command: | - cp config/database.ci.yml config/database.yml - bundle exec rails db:create db:schema:load + bundle exec rails db:create + bundle exec rails db:schema:load - run: name: Run RSpec tests command: bundle exec rspec From e99e105ab8f95fb47e77e2309e734762ba5d5a32 Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 11:57:53 +0100 Subject: [PATCH 16/17] Update CircleCI config --- .circleci/config.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bd38b09c..460be1ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,15 +10,11 @@ jobs: - image: cimg/ruby:3.3.4 environment: RAILS_ENV: test - - image: cimg/postgres:14.0-postgis + - image: cimg/postgres:13.3-postgis environment: POSTGRES_USER: postgres POSTGRES_DB: test_database POSTGRES_PASSWORD: mysecretpassword - POSTGRES_INITDB_ARGS: --enable-debug --data-checksums --encoding=UTF8 --lc-collate=C --lc-ctype=C - command: | - apt-get update - apt-get install -y postgis postgresql-14-postgis-3 - image: redis:7.0 steps: From 8a309a2186185580a280689672726b11cb0d889f Mon Sep 17 00:00:00 2001 From: Eugene Burmakin Date: Wed, 29 Jan 2025 12:18:03 +0100 Subject: [PATCH 17/17] Remove tracks --- CHANGELOG.md | 2 +- app/models/track.rb | 7 ------- app/models/user.rb | 1 - db/migrate/20250123145954_create_tracks.rb | 14 -------------- db/schema.rb | 11 ----------- spec/factories/tracks.rb | 10 ---------- spec/models/track_spec.rb | 15 --------------- spec/models/user_spec.rb | 1 - 8 files changed, 1 insertion(+), 60 deletions(-) delete mode 100644 app/models/track.rb delete mode 100644 db/migrate/20250123145954_create_tracks.rb delete mode 100644 spec/factories/tracks.rb delete mode 100644 spec/models/track_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 95de7bbf..1dd4398c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,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/). -# 0.23.6 - 2025-01-23 +# 0.23.6 - 2025-01-29 ### Added diff --git a/app/models/track.rb b/app/models/track.rb deleted file mode 100644 index 41e673b4..00000000 --- a/app/models/track.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -class Track < ApplicationRecord - belongs_to :user - - validates :path, :started_at, :ended_at, presence: true -end diff --git a/app/models/user.rb b/app/models/user.rb index 90ff2fb0..b8d27f17 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,7 +14,6 @@ class User < ApplicationRecord has_many :points, through: :imports has_many :places, through: :visits has_many :trips, dependent: :destroy - has_many :tracks, dependent: :destroy after_create :create_api_key before_save :strip_trailing_slashes diff --git a/db/migrate/20250123145954_create_tracks.rb b/db/migrate/20250123145954_create_tracks.rb deleted file mode 100644 index 35c6afa1..00000000 --- a/db/migrate/20250123145954_create_tracks.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -class CreateTracks < ActiveRecord::Migration[8.0] - def change - create_table :tracks do |t| - t.datetime :started_at, null: false - t.datetime :ended_at, null: false - t.references :user, null: false, foreign_key: true - t.line_string :path, srid: 3857, null: false - - t.timestamps - end - end -end diff --git a/db/schema.rb b/db/schema.rb index 7e9cca52..b431351f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -193,16 +193,6 @@ t.index ["year"], name: "index_stats_on_year" end - create_table "tracks", force: :cascade do |t| - t.datetime "started_at", null: false - t.datetime "ended_at", null: false - t.bigint "user_id", null: false - t.geometry "path", limit: {:srid=>3857, :type=>"line_string"}, null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["user_id"], name: "index_tracks_on_user_id" - end - create_table "trips", force: :cascade do |t| t.string "name", null: false t.datetime "started_at", null: false @@ -264,7 +254,6 @@ add_foreign_key "points", "users" add_foreign_key "points", "visits" add_foreign_key "stats", "users" - add_foreign_key "tracks", "users" add_foreign_key "trips", "users" add_foreign_key "visits", "areas" add_foreign_key "visits", "places" diff --git a/spec/factories/tracks.rb b/spec/factories/tracks.rb deleted file mode 100644 index 32603460..00000000 --- a/spec/factories/tracks.rb +++ /dev/null @@ -1,10 +0,0 @@ -# frozen_string_literal: true - -FactoryBot.define do - factory :track do - started_at { DateTime.new(2025, 1, 23, 15, 59, 55) } - ended_at { DateTime.new(2025, 1, 23, 16, 0, 0) } - user - path { 'LINESTRING(0 0, 1 1, 2 2)' } - end -end diff --git a/spec/models/track_spec.rb b/spec/models/track_spec.rb deleted file mode 100644 index 051b8ae8..00000000 --- a/spec/models/track_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Track, type: :model do - describe 'validations' do - it { is_expected.to validate_presence_of(:path) } - it { is_expected.to validate_presence_of(:started_at) } - it { is_expected.to validate_presence_of(:ended_at) } - end - - describe 'associations' do - it { is_expected.to belong_to(:user) } - end -end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a9ce1d1e..398e436f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -14,7 +14,6 @@ it { is_expected.to have_many(:visits).dependent(:destroy) } it { is_expected.to have_many(:places).through(:visits) } it { is_expected.to have_many(:trips).dependent(:destroy) } - it { is_expected.to have_many(:tracks).dependent(:destroy) } end describe 'callbacks' do