Compare commits

...

9 commits

Author SHA1 Message Date
Daniël
73c09a09fd
Merge 07ee46bce3 into b4c2def2be 2026-01-02 17:06:38 +01:00
Eugene Burmakin
b4c2def2be Merge remote-tracking branch 'origin' into dev 2025-12-30 19:05:30 +01:00
Eugene Burmakin
8a1e42a2e8 Update version 2025-12-30 19:04:15 +01:00
Eugene Burmakin
2f11003c29 Fix migrations and data verification to remove safety_assured blocks and handle missing points gracefully. 2025-12-30 19:02:38 +01:00
Evgenii Burmakin
8d2ade1bdc
0.37.0 (#2067)
* fix: move foreman to global gems to fix startup crash (#1971)

* Update exporting code to stream points data to file in batches to red… (#1980)

* Update exporting code to stream points data to file in batches to reduce memory usage

* Update changelog

* Update changelog

* Feature/maplibre frontend (#1953)

* Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet

* Implement phase 1

* Phases 1-3 + part of 4

* Fix e2e tests

* Phase 6

* Implement fog of war

* Phase 7

* Next step: fix specs, phase 7 done

* Use our own map tiles

* Extract v2 map logic to separate manager classes

* Update settings panel on v2 map

* Update v2 e2e tests structure

* Reimplement location search in maps v2

* Update speed routes

* Implement visits and places creation in v2

* Fix last failing test

* Implement visits merging

* Fix a routes e2e test and simplify the routes layer styling.

* Extract js to modules from maps_v2_controller.js

* Implement area creation

* Fix spec problem

* Fix some e2e tests

* Implement live mode in v2 map

* Update icons and panel

* Extract some styles

* Remove unused file

* Start adding dark theme to popups on MapLibre maps

* Make popups respect dark theme

* Move v2 maps to maplibre namespace

* Update v2 references to maplibre

* Put place, area and visit info into side panel

* Update API to use safe settings config method

* Fix specs

* Fix method name to config in SafeSettings and update usages accordingly

* Add missing public files

* Add handling for real time points

* Fix remembering enabled/disabled layers of the v2 map

* Fix lots of e2e tests

* Add settings to select map version

* Use maps/v2 as main path for MapLibre maps

* Update routing

* Update live mode

* Update maplibre controller

* Update changelog

* Remove some console.log statements

* Pull only necessary data for map v2 points

* Feature/raw data archive (#2009)

* 0.36.2 (#2007)

* fix: move foreman to global gems to fix startup crash (#1971)

* Update exporting code to stream points data to file in batches to red… (#1980)

* Update exporting code to stream points data to file in batches to reduce memory usage

* Update changelog

* Update changelog

* Feature/maplibre frontend (#1953)

* Add a plan to use MapLibre GL JS for the frontend map rendering, replacing Leaflet

* Implement phase 1

* Phases 1-3 + part of 4

* Fix e2e tests

* Phase 6

* Implement fog of war

* Phase 7

* Next step: fix specs, phase 7 done

* Use our own map tiles

* Extract v2 map logic to separate manager classes

* Update settings panel on v2 map

* Update v2 e2e tests structure

* Reimplement location search in maps v2

* Update speed routes

* Implement visits and places creation in v2

* Fix last failing test

* Implement visits merging

* Fix a routes e2e test and simplify the routes layer styling.

* Extract js to modules from maps_v2_controller.js

* Implement area creation

* Fix spec problem

* Fix some e2e tests

* Implement live mode in v2 map

* Update icons and panel

* Extract some styles

* Remove unused file

* Start adding dark theme to popups on MapLibre maps

* Make popups respect dark theme

* Move v2 maps to maplibre namespace

* Update v2 references to maplibre

* Put place, area and visit info into side panel

* Update API to use safe settings config method

* Fix specs

* Fix method name to config in SafeSettings and update usages accordingly

* Add missing public files

* Add handling for real time points

* Fix remembering enabled/disabled layers of the v2 map

* Fix lots of e2e tests

* Add settings to select map version

* Use maps/v2 as main path for MapLibre maps

* Update routing

* Update live mode

* Update maplibre controller

* Update changelog

* Remove some console.log statements

---------

Co-authored-by: Robin Tuszik <mail@robin.gg>

* Remove esbuild scripts from package.json

* Remove sideEffects field from package.json

* Raw data archivation

* Add tests

* Fix tests

* Fix tests

* Update ExceptionReporter

* Add schedule to run raw data archival job monthly

* Change file structure for raw data archival feature

* Update changelog and version for raw data archival feature

---------

Co-authored-by: Robin Tuszik <mail@robin.gg>

* Set raw_data to an empty hash instead of nil when archiving

* Fix storage configuration and file extraction

* Consider MIN_MINUTES_SPENT_IN_CITY during stats calculation (#2018)

* Consider MIN_MINUTES_SPENT_IN_CITY during stats calculation

* Remove raw data from visited cities api endpoint

* Use user timezone to show dates on maps (#2020)

* Fix/pre epoch time (#2019)

* Use user timezone to show dates on maps

* Limit timestamps to valid range to prevent database errors when users enter pre-epoch dates.

* Limit timestamps to valid range to prevent database errors when users enter pre-epoch dates.

* Fix tests failing due to new index on stats table

* Fix failing specs

* Update redis client configuration to support unix socket connection

* Update changelog

* Fix kml kmz import issues (#2023)

* Fix kml kmz import issues

* Refactor KML importer to improve readability and maintainability

* Implement moving points in map v2 and fix route rendering logic to ma… (#2027)

* Implement moving points in map v2 and fix route rendering logic to match map v1.

* Fix route spec

* fix(maplibre): update date format to ISO 8601 (#2029)

* Add verification step to raw data archival process (#2028)

* Add verification step to raw data archival process

* Add actual verification of raw data archives after creation, and only clear raw_data for verified archives.

* Fix failing specs

* Eliminate zip-bomb risk

* Fix potential memory leak in js

* Return .keep files

* Use Toast instead of alert for notifications

* Add help section to navbar dropdown

* Update changelog

* Remove raw_data_archival_job

* Ensure file is being closed properly after reading in Archivable concern

* Add composite index to stats table if not exists

* Update changelog

* Update entrypoint to always sync static assets (not only new ones)

* Add family layer to MapLibre maps (#2055)

* Add family layer to MapLibre maps

* Update migration

* Don't show family toggle if feature is disabled

* Update changelog

* Return changelog

* Update changelog

* Update tailwind file

* Bump sentry-rails from 6.0.0 to 6.1.0 (#1945)

Bumps [sentry-rails](https://github.com/getsentry/sentry-ruby) from 6.0.0 to 6.1.0.
- [Release notes](https://github.com/getsentry/sentry-ruby/releases)
- [Changelog](https://github.com/getsentry/sentry-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-ruby/compare/6.0.0...6.1.0)

---
updated-dependencies:
- dependency-name: sentry-rails
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump turbo-rails from 2.0.17 to 2.0.20 (#1944)

Bumps [turbo-rails](https://github.com/hotwired/turbo-rails) from 2.0.17 to 2.0.20.
- [Release notes](https://github.com/hotwired/turbo-rails/releases)
- [Commits](https://github.com/hotwired/turbo-rails/compare/v2.0.17...v2.0.20)

---
updated-dependencies:
- dependency-name: turbo-rails
  dependency-version: 2.0.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evgenii Burmakin <Freika@users.noreply.github.com>

* Bump webmock from 3.25.1 to 3.26.1 (#1943)

Bumps [webmock](https://github.com/bblimke/webmock) from 3.25.1 to 3.26.1.
- [Release notes](https://github.com/bblimke/webmock/releases)
- [Changelog](https://github.com/bblimke/webmock/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bblimke/webmock/compare/v3.25.1...v3.26.1)

---
updated-dependencies:
- dependency-name: webmock
  dependency-version: 3.26.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Evgenii Burmakin <Freika@users.noreply.github.com>

* Bump brakeman from 7.1.0 to 7.1.1 (#1942)

Bumps [brakeman](https://github.com/presidentbeef/brakeman) from 7.1.0 to 7.1.1.
- [Release notes](https://github.com/presidentbeef/brakeman/releases)
- [Changelog](https://github.com/presidentbeef/brakeman/blob/main/CHANGES.md)
- [Commits](https://github.com/presidentbeef/brakeman/compare/v7.1.0...v7.1.1)

---
updated-dependencies:
- dependency-name: brakeman
  dependency-version: 7.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump redis from 5.4.0 to 5.4.1 (#1941)

Bumps [redis](https://github.com/redis/redis-rb) from 5.4.0 to 5.4.1.
- [Changelog](https://github.com/redis/redis-rb/blob/master/CHANGELOG.md)
- [Commits](https://github.com/redis/redis-rb/compare/v5.4.0...v5.4.1)

---
updated-dependencies:
- dependency-name: redis
  dependency-version: 5.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Put import deletion into background job (#2045)

* Put import deletion into background job

* Update changelog

* fix null type error and update heatmap styling (#2037)

* fix: use constant weight for maplibre heatmap layer

* fix null type, update heatmap styling

* improve heatmap styling

* fix typo

* Fix stats calculation to recursively reduce H3 resolution when too ma… (#2065)

* Fix stats calculation to recursively reduce H3 resolution when too many hexagons are generated

* Update CHANGELOG.md

* Validate trip start and end dates (#2066)

* Validate trip start and end dates

* Update changelog

* Update migration to clean up duplicate stats before adding unique index

* Fix fog of war radius setting being ignored and applying settings causing errors (#2068)

* Update changelog

* Add Rack::Deflater middleware to config/application.rb to enable gzip compression for responses.

* Add composite index to points on user_id and timestamp

* Deduplicte points based on timestamp brought to unix time

* Fix/stats cache invalidation (#2072)

* Fix family layer toggle in Map v2 settings for non-selfhosted env

* Invalidate cache

* Remove comments

* Remove comment

* Add new indicies to improve performance and remove unused ones to opt… (#2078)

* Add new indicies to improve performance and remove unused ones to optimize database.

* Remove comments

* Update map search suggestions panel styling

* Add yearly digest (#2073)

* Add yearly digest

* Rename YearlyDigests to Users::Digests

* Minor changes

* Update yearly digest layout and styles

* Add flags and chart to email

* Update colors

* Fix layout of stats in yearly digest view

* Remove cron job for yearly digest scheduling

* Update CHANGELOG.md

* Update digest email setting handling

* Allow sharing digest for 1 week or 1 month

* Change Digests Distance to Bigint

* Fix settings page

* Update changelog

* Add RailsPulse (#2079)

* Add RailsPulse

* Add RailsPulse monitoring tool with basic HTTP authentication

* Bring points_count to integer

* Update migration and version

* Update rubocop issues

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Robin Tuszik <mail@robin.gg>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-30 17:33:56 +01:00
Eugene Burmakin
7f277612fc Update rubocop issues 2025-12-30 17:33:17 +01:00
Eugene Burmakin
2f5487cd35 Update migration and version 2025-12-30 16:57:17 +01:00
Eugene Burmakin
5455228b80 Bring points_count to integer 2025-12-30 16:57:17 +01:00
DanielB1990
07ee46bce3 Changing button (confirm, decline, map) alignment for suggested visit [Issue #1834; feature] 2025-11-27 20:55:33 +01:00
23 changed files with 79 additions and 119 deletions

View file

@ -1 +1 @@
0.36.5 0.37.1

View file

@ -4,7 +4,14 @@ 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.36.5] - Unreleased # [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
## Added ## Added

View file

@ -18,7 +18,7 @@ class BulkVisitsSuggestingJob < ApplicationJob
users.active.find_each do |user| users.active.find_each do |user|
next unless user.safe_settings.visits_suggestions_enabled? next unless user.safe_settings.visits_suggestions_enabled?
next unless user.points_count.positive? next unless user.points_count&.positive?
schedule_chunked_jobs(user, time_chunks) schedule_chunked_jobs(user, time_chunks)
end end

View file

@ -21,7 +21,7 @@ class Tracks::DailyGenerationJob < ApplicationJob
def perform def perform
User.active_or_trial.find_each do |user| User.active_or_trial.find_each do |user|
next if user.points_count.zero? next if user.points_count&.zero?
process_user_daily_tracks(user) process_user_daily_tracks(user)
rescue StandardError => e rescue StandardError => e

View file

@ -11,7 +11,7 @@ class StatsQuery
end end
{ {
total: user.points_count, total: user.points_count.to_i,
geocoded: cached_stats[:geocoded], geocoded: cached_stats[:geocoded],
without_data: cached_stats[:without_data] without_data: cached_stats[:without_data]
} }

View file

@ -9,7 +9,7 @@ class Imports::Destroy
end end
def call def call
points_count = @import.points_count points_count = @import.points_count.to_i
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
@import.points.destroy_all @import.points.destroy_all

View file

@ -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,

View file

@ -9,7 +9,7 @@ class PointsLimitExceeded
return false if DawarichSettings.self_hosted? return false if DawarichSettings.self_hosted?
Rails.cache.fetch(cache_key, expires_in: 1.day) do Rails.cache.fetch(cache_key, expires_in: 1.day) do
@user.points_count >= points_limit @user.points_count.to_i >= points_limit
end end
end end

View file

@ -323,7 +323,7 @@ class Users::ExportData
trips: user.trips.count, trips: user.trips.count,
stats: user.stats.count, stats: user.stats.count,
notifications: user.notifications.count, notifications: user.notifications.count,
points: user.points_count, points: user.points_count.to_i,
visits: user.visits.count, visits: user.visits.count,
places: user.visited_places.count places: user.visited_places.count
} }

View file

@ -219,9 +219,7 @@ class Users::ImportData::Points
country_key = [country_info['name'], country_info['iso_a2'], country_info['iso_a3']] country_key = [country_info['name'], country_info['iso_a2'], country_info['iso_a3']]
country = countries_lookup[country_key] country = countries_lookup[country_key]
if country.nil? && country_info['name'].present? country = countries_lookup[country_info['name']] if country.nil? && country_info['name'].present?
country = countries_lookup[country_info['name']]
end
if country if country
attributes['country_id'] = country.id attributes['country_id'] = country.id
@ -254,12 +252,12 @@ class Users::ImportData::Points
end end
def ensure_lonlat_field(attributes, point_data) def ensure_lonlat_field(attributes, point_data)
if attributes['lonlat'].blank? && point_data['longitude'].present? && point_data['latitude'].present? return unless attributes['lonlat'].blank? && point_data['longitude'].present? && point_data['latitude'].present?
longitude = point_data['longitude'].to_f
latitude = point_data['latitude'].to_f longitude = point_data['longitude'].to_f
attributes['lonlat'] = "POINT(#{longitude} #{latitude})" latitude = point_data['latitude'].to_f
logger.debug "Reconstructed lonlat: #{attributes['lonlat']}" attributes['lonlat'] = "POINT(#{longitude} #{latitude})"
end logger.debug "Reconstructed lonlat: #{attributes['lonlat']}"
end end
def normalize_timestamp_for_lookup(timestamp) def normalize_timestamp_for_lookup(timestamp)

View file

@ -1,6 +1,6 @@
<p class="py-6"> <p class="py-6">
<p class='py-2'> <p class='py-2'>
You have used <%= number_with_delimiter(current_user.points_count) %> points of <%= number_with_delimiter(DawarichSettings::BASIC_PAID_PLAN_LIMIT) %> available. You have used <%= number_with_delimiter(current_user.points_count.to_i) %> points of <%= number_with_delimiter(DawarichSettings::BASIC_PAID_PLAN_LIMIT) %> available.
</p> </p>
<progress class="progress progress-primary w-1/2 h-5" value="<%= current_user.points_count %>" max="<%= DawarichSettings::BASIC_PAID_PLAN_LIMIT %>"></progress> <progress class="progress progress-primary w-1/2 h-5" value="<%= current_user.points_count.to_i %>" max="<%= DawarichSettings::BASIC_PAID_PLAN_LIMIT %>"></progress>
</p> </p>

View file

@ -24,7 +24,7 @@
</div> </div>
</td> </td>
<td> <td>
<%= number_with_delimiter user.points_count %> <%= number_with_delimiter user.points_count.to_i %>
</td> </td>
<td> <td>
<%= human_datetime(user.created_at) %> <%= human_datetime(user.created_at) %>

View file

@ -4,7 +4,7 @@
<div class="hero-content text-center py-12"> <div class="hero-content text-center py-12">
<div class="max-w-lg"> <div class="max-w-lg">
<h1 class="text-4xl font-bold"><%= @digest.year %> Year in Review</h1> <h1 class="text-4xl font-bold"><%= @digest.year %> Year in Review</h1>
<p class="py-4">A journey, by the numbers</p> <p class="py-4">Your journey, by the numbers</p>
</div> </div>
</div> </div>
</div> </div>

View file

@ -168,7 +168,7 @@
<div class="content"> <div class="content">
<p> <p>
Hi, this is Evgenii from Dawarich! Pretty wild journey last yeah, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>. Hi, this is Evgenii from Dawarich! Pretty wild journey last year, huh? Let's take a look back at all the places you explored in <strong><%= @digest.year %></strong>.
</p> </p>
</div> </div>

View file

@ -1,15 +1,15 @@
<div class="group relative timeline-box"> <div class="group relative timeline-box">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div class="opacity-100 flex items-center mr-4">
<%= render 'visits/name', visit: visit %>
<div><%= "#{visit.started_at.strftime('%H:%M')} - #{visit.ended_at.strftime('%H:%M')}" %></div>
</div>
<div class="opacity-0 transition-opacity duration-200 group-hover:opacity-100 flex items-center ml-4">
<%= render 'visits/buttons', visit: visit %> <%= render 'visits/buttons', visit: visit %>
<!-- The button to open modal --> <!-- The button to open modal -->
<label for="visit_details_popup_<%= visit.id %>" class='btn btn-xs btn-info'>Map</label> <label for="visit_details_popup_<%= visit.id %>" class='btn btn-xs btn-info'>Map</label>
<%= render 'visits/modal', visit: visit %> <%= render 'visits/modal', visit: visit %>
</div> </div>
<div>
<%= render 'visits/name', visit: visit %>
<div class="lg:text-left"><%= "#{visit.started_at.strftime('%H:%M')} - #{visit.ended_at.strftime('%H:%M')}" %></div>
</div>
</div> </div>
</div> </div>

View file

@ -38,7 +38,5 @@ module Dawarich
config.active_job.queue_adapter = :sidekiq config.active_job.queue_adapter = :sidekiq
config.action_mailer.preview_paths << Rails.root.join('spec/mailers/previews').to_s config.action_mailer.preview_paths << Rails.root.join('spec/mailers/previews').to_s
config.middleware.use Rack::Deflater
end end
end end

View file

@ -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

View file

@ -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

View file

@ -16,7 +16,7 @@ class CreatePointsRawDataArchives < ActiveRecord::Migration[8.0]
end end
add_index :points_raw_data_archives, :user_id add_index :points_raw_data_archives, :user_id
add_index :points_raw_data_archives, [:user_id, :year, :month] add_index :points_raw_data_archives, %i[user_id year month]
add_index :points_raw_data_archives, :archived_at add_index :points_raw_data_archives, :archived_at
add_foreign_key :points_raw_data_archives, :users, validate: false add_foreign_key :points_raw_data_archives, :users, validate: false
end end

View file

@ -27,15 +27,19 @@ class AddCompositeIndexToStats < ActiveRecord::Migration[8.0]
deleted_count = 0 deleted_count = 0
loop do loop do
batch_deleted = execute(<<-SQL.squish).cmd_tuples batch_deleted = execute(<<-SQL.squish).cmd_tuples
DELETE FROM stats s1 DELETE FROM stats
WHERE EXISTS ( WHERE id IN (
SELECT 1 FROM stats s2 SELECT s1.id
WHERE s2.user_id = s1.user_id FROM stats s1
AND s2.year = s1.year WHERE EXISTS (
AND s2.month = s1.month SELECT 1 FROM stats s2
AND s2.id > s1.id WHERE s2.user_id = s1.user_id
AND s2.year = s1.year
AND s2.month = s1.month
AND s2.id > s1.id
)
LIMIT #{BATCH_SIZE}
) )
LIMIT #{BATCH_SIZE}
SQL SQL
break if batch_deleted.zero? break if batch_deleted.zero?

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class AddVerifiedAtToPointsRawDataArchives < ActiveRecord::Migration[8.0] class AddVerifiedAtToPointsRawDataArchives < ActiveRecord::Migration[8.0]
def change def change
add_column :points_raw_data_archives, :verified_at, :datetime add_column :points_raw_data_archives, :verified_at, :datetime

View file

@ -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

View file

@ -1,50 +0,0 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe 'devise/shared/_links.html.erb', type: :view do
let(:resource_name) { :user }
let(:devise_mapping) { Devise.mappings[:user] }
before do
def view.resource_name
:user
end
def view.devise_mapping
Devise.mappings[:user]
end
def view.resource_class
User
end
def view.signed_in?
false
end
end
context 'with OIDC provider' do
before do
stub_const('OMNIAUTH_PROVIDERS', [:openid_connect])
allow(User).to receive(:omniauth_providers).and_return([:openid_connect])
end
it 'displays custom OIDC provider name' do
stub_const('OIDC_PROVIDER_NAME', 'Authentik')
render
expect(rendered).to have_button('Sign in with Authentik')
end
it 'displays default name when OIDC_PROVIDER_NAME is not set' do
stub_const('OIDC_PROVIDER_NAME', 'Openid Connect')
render
expect(rendered).to have_button('Sign in with Openid Connect')
end
end
end