From 678fb66f1741c596e9886bbc2c463d7dcbd84c88 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 3 Jun 2025 15:51:37 -0500 Subject: [PATCH 01/50] Stub UI for independent settings for Considering and Doing --- app/assets/stylesheets/dividers.css | 19 +++++++++++++ .../collections/edit/_auto_close.html.erb | 27 ++++++++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 app/assets/stylesheets/dividers.css diff --git a/app/assets/stylesheets/dividers.css b/app/assets/stylesheets/dividers.css new file mode 100644 index 000000000..0a650ceb7 --- /dev/null +++ b/app/assets/stylesheets/dividers.css @@ -0,0 +1,19 @@ +@layer components { + .divider { + --divider-padding: var(--inline-space); + --divider-color: var(--color-ink-medium); + + align-items: center; + display: flex; + margin-inline: calc(var(--divider-padding) * -1); + + &:before, + &:after { + background: var(--divider-color); + block-size: var(--divider-size, 1px); + content: ""; + flex: 1; + margin: 0 var(--divider-padding); + } + } +} \ No newline at end of file diff --git a/app/views/collections/edit/_auto_close.html.erb b/app/views/collections/edit/_auto_close.html.erb index e0a05df2b..585627dd6 100644 --- a/app/views/collections/edit/_auto_close.html.erb +++ b/app/views/collections/edit/_auto_close.html.erb @@ -1,6 +1,21 @@ -Do or Die - - -<%= form_with model: collection, data: { controller: "form" } do |form| %> - <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-end", data: { action: "change->form#submit" } } %> -<% end %> +
+ Do or Die +
+
+ Cards in Considering +

Auto-close after…

+ <%= form_with model: collection, data: { controller: "form" } do |form| %> + <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> + <% end %> +
+
+
+ Cards inDoing +

Fall back to Considering after…

+ <%= form_with model: collection, data: { controller: "form" } do |form| %> + <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> + <% end %> +
+
+
+ \ No newline at end of file From 56b32e6f527b0a22c4acea335a509814be4d88e3 Mon Sep 17 00:00:00 2001 From: Jason Zimdars Date: Tue, 3 Jun 2025 15:57:11 -0500 Subject: [PATCH 02/50] No explicit stalled setting for now Stalled will be determined by the system --- app/views/collections/edit.html.erb | 3 +-- app/views/collections/edit/_stalled.html.erb | 6 ------ 2 files changed, 1 insertion(+), 8 deletions(-) delete mode 100644 app/views/collections/edit/_stalled.html.erb diff --git a/app/views/collections/edit.html.erb b/app/views/collections/edit.html.erb index dce927fcb..119e926d9 100644 --- a/app/views/collections/edit.html.erb +++ b/app/views/collections/edit.html.erb @@ -35,8 +35,7 @@ <%= render "collections/edit/auto_close", collection: @collection %> - <%= render "collections/edit/stalled", collection: @collection %> - + <%= form_with model: @collection, class: "txt-align-center", method: :delete do |form| %> <%= form.button class: "btn txt-negative borderless txt-small", data: { turbo_confirm: "Are you sure you want to permanently delete this Collection?" } do %> diff --git a/app/views/collections/edit/_stalled.html.erb b/app/views/collections/edit/_stalled.html.erb deleted file mode 100644 index 63447ea95..000000000 --- a/app/views/collections/edit/_stalled.html.erb +++ /dev/null @@ -1,6 +0,0 @@ -Stalled - - -<%= form_with model: collection, data: { controller: "form" } do |form| %> - <%= form.select :stalled_period, collection_stalled_options, {}, { class: "input input--select full-width margin-block-end", data: { action: "change->form#submit" } } %> -<% end %> From 91787a35237beb855f7d5e8a925edd3f21a71c53 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 10:41:08 +0200 Subject: [PATCH 03/50] Revamp entropy configuraiton - Move both settings to its dedicated model entropy configuration - A collection can have an entropy configuration, or will default to the account if not --- app/controllers/collections_controller.rb | 2 +- app/models/account.rb | 2 +- app/models/account/entropy.rb | 18 +++ app/models/card/entropy.rb | 4 +- app/models/collection.rb | 3 +- app/models/collection/auto_closing.rb | 4 +- app/models/collection/entropy.rb | 21 +++ app/models/entropy.rb | 5 + app/models/entropy/configuration.rb | 3 + .../collections/edit/_auto_close.html.erb | 5 +- ...604071718_create_entropy_configurations.rb | 15 ++ db/schema.rb | 16 ++- db/schema_cache.yml | 128 ++++++++++++++---- test/fixtures/collections.yml | 2 - test/fixtures/entropy/configurations.yml | 9 ++ test/models/card/entropy_test.rb | 8 +- 16 files changed, 199 insertions(+), 46 deletions(-) create mode 100644 app/models/account/entropy.rb create mode 100644 app/models/collection/entropy.rb create mode 100644 app/models/entropy.rb create mode 100644 app/models/entropy/configuration.rb create mode 100644 db/migrate/20250604071718_create_entropy_configurations.rb create mode 100644 test/fixtures/entropy/configurations.yml diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index f5eb8c9fc..aaf71e046 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -33,7 +33,7 @@ class CollectionsController < ApplicationController end def collection_params - params.expect(collection: [ :name, :all_access, :auto_close_period ]) + params.expect(collection: [ :name, :all_access, :auto_close_period, :auto_reconsider_period ]) end def grantees diff --git a/app/models/account.rb b/app/models/account.rb index f562ef665..b2a4d71b5 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,5 +1,5 @@ class Account < ApplicationRecord - include Joinable + include Entropy, Joinable has_many_attached :uploads end diff --git a/app/models/account/entropy.rb b/app/models/account/entropy.rb new file mode 100644 index 000000000..39c47f2b5 --- /dev/null +++ b/app/models/account/entropy.rb @@ -0,0 +1,18 @@ +module Account::Entropy + extend ActiveSupport::Concern + + included do + has_one :default_entropy_configuration, class_name: "Entropy::Configuration", as: :container, dependent: :destroy + + before_save :set_default_entropy_configuration + end + + private + DEFAULT_ENTROPY_PERIOD = 30.days + + def set_default_entropy_configuration + self.default_entropy_configuration ||= build_default_entropy_configuration \ + auto_close_period: DEFAULT_ENTROPY_PERIOD, + auto_reconsider_period: DEFAULT_ENTROPY_PERIOD + end +end diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 0b6f6e4aa..d5dd17268 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -8,9 +8,9 @@ module Card::Entropy scope :in_auto_closing_collection, -> { joins(:collection).merge(Collection.auto_closing) } scope :stagnated, -> { doing.where(last_active_at: ..AUTO_RECONSIDER_PERIOD.ago) } - scope :due_to_be_closed, -> { considering.in_auto_closing_collection.where("last_active_at <= DATETIME('now', '-' || auto_close_period || ' seconds')") } + scope :due_to_be_closed, -> { considering.in_auto_closing_collection.where("last_active_at <= DATETIME('now', '-' || entropy_configurations.auto_close_period || ' seconds')") } - delegate :auto_close_period, to: :collection + delegate :auto_close_period, :auto_reconsider_period, to: :collection end class_methods do diff --git a/app/models/collection.rb b/app/models/collection.rb index 9b74d8524..0fc1e912e 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -1,11 +1,12 @@ class Collection < ApplicationRecord - include AutoClosing, Accessible, Broadcastable, Filterable, Workflowing + include AutoClosing, Accessible, Broadcastable, Entropy, Filterable, Workflowing belongs_to :creator, class_name: "User", default: -> { Current.user } has_many :cards, dependent: :destroy has_many :tags, -> { distinct }, through: :cards has_many :events + has_one :entropy_configuration, class_name: "Entropy::Configuration", as: :container, dependent: :destroy scope :alphabetically, -> { order("lower(name)") } diff --git a/app/models/collection/auto_closing.rb b/app/models/collection/auto_closing.rb index e3a9c8990..c8e52cb12 100644 --- a/app/models/collection/auto_closing.rb +++ b/app/models/collection/auto_closing.rb @@ -2,12 +2,12 @@ module Collection::AutoClosing extend ActiveSupport::Concern included do - scope :auto_closing, -> { where.not(auto_close_period: nil) } + scope :auto_closing, -> { joins(:entropy_configuration).where.not("entropy_configurations.auto_close_period": nil) } before_create :set_default_auto_close_period end def auto_closing? - auto_close_period.present? + entropy_configuration.present? end private diff --git a/app/models/collection/entropy.rb b/app/models/collection/entropy.rb new file mode 100644 index 000000000..ddf0883f4 --- /dev/null +++ b/app/models/collection/entropy.rb @@ -0,0 +1,21 @@ +module Collection::Entropy + extend ActiveSupport::Concern + + included do + delegate :auto_close_period, :auto_reconsider_period, to: :entropy_configuration + end + + def entropy_configuration + super || Account.sole.default_entropy_configuration + end + + def auto_close_period=(new_value) + entropy_configuration ||= association(:entropy_configuration).reader || self.build_entropy_configuration + entropy_configuration.update auto_close_period: new_value + end + + def auto_reconsider_period=(new_value) + entropy_configuration ||= association(:entropy_configuration).reader || self.build_entropy_configuration + entropy_configuration.update auto_reconsider_period: new_value + end +end diff --git a/app/models/entropy.rb b/app/models/entropy.rb new file mode 100644 index 000000000..42e730a09 --- /dev/null +++ b/app/models/entropy.rb @@ -0,0 +1,5 @@ +module Entropy + def self.table_name_prefix + "entropy_" + end +end diff --git a/app/models/entropy/configuration.rb b/app/models/entropy/configuration.rb new file mode 100644 index 000000000..72370670f --- /dev/null +++ b/app/models/entropy/configuration.rb @@ -0,0 +1,3 @@ +class Entropy::Configuration < ApplicationRecord + belongs_to :container, polymorphic: true +end diff --git a/app/views/collections/edit/_auto_close.html.erb b/app/views/collections/edit/_auto_close.html.erb index 585627dd6..d4455e45b 100644 --- a/app/views/collections/edit/_auto_close.html.erb +++ b/app/views/collections/edit/_auto_close.html.erb @@ -5,12 +5,12 @@ Cards in Considering

Auto-close after…

<%= form_with model: collection, data: { controller: "form" } do |form| %> - <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> + <%= form.select :auto_reconsider_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> <% end %>
- Cards inDoing + Cards in Doing

Fall back to Considering after…

<%= form_with model: collection, data: { controller: "form" } do |form| %> <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> @@ -18,4 +18,3 @@
- \ No newline at end of file diff --git a/db/migrate/20250604071718_create_entropy_configurations.rb b/db/migrate/20250604071718_create_entropy_configurations.rb new file mode 100644 index 000000000..702d4d9cd --- /dev/null +++ b/db/migrate/20250604071718_create_entropy_configurations.rb @@ -0,0 +1,15 @@ +class CreateEntropyConfigurations < ActiveRecord::Migration[8.1] + def change + create_table :entropy_configurations do |t| + t.references :container, polymorphic: true, null: false, index: { unique: true } + + t.bigint :auto_close_period, null: false, default: 30.days.to_i + t.bigint :auto_reconsider_period, null: false, default: 30.days.to_i + + t.timestamps + + t.index %i[ container_type container_id auto_close_period ] + t.index %i[ container_type container_id auto_reconsider_period ] + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 7acec01bd..8c1642ea4 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.1].define(version: 2025_06_01_161653) do +ActiveRecord::Schema[8.1].define(version: 2025_06_04_080022) do create_table "accesses", force: :cascade do |t| t.integer "collection_id", null: false t.datetime "created_at", null: false @@ -141,13 +141,11 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_01_161653) do create_table "collections", force: :cascade do |t| t.boolean "all_access", default: false, null: false - t.bigint "auto_close_period" t.datetime "created_at", null: false t.integer "creator_id", null: false t.string "name", null: false t.datetime "updated_at", null: false t.integer "workflow_id" - t.index ["auto_close_period"], name: "index_collections_on_auto_close_period" t.index ["creator_id"], name: "index_collections_on_creator_id" t.index ["workflow_id"], name: "index_collections_on_workflow_id" end @@ -188,6 +186,18 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_01_161653) do t.index ["filter_id"], name: "index_creators_filters_on_filter_id" end + create_table "entropy_configurations", force: :cascade do |t| + t.bigint "auto_close_period", default: 2592000, null: false + t.bigint "auto_reconsider_period", default: 2592000, null: false + t.integer "container_id", null: false + t.string "container_type", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["container_type", "container_id", "auto_close_period"], name: "idx_on_container_type_container_id_auto_close_perio_74dc880875" + t.index ["container_type", "container_id", "auto_reconsider_period"], name: "idx_on_container_type_container_id_auto_reconsider__583aaddbea" + t.index ["container_type", "container_id"], name: "index_entropy_configurations_on_container", unique: true + end + create_table "events", force: :cascade do |t| t.string "action", null: false t.integer "collection_id", null: false diff --git a/db/schema_cache.yml b/db/schema_cache.yml index e4a61d5f1..e29b9c902 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -495,16 +495,6 @@ columns: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: auto_close_period - cast_type: *11 - sql_type_metadata: *12 - 'null': true - default: - default_function: - collation: - comment: - *5 - *24 - *6 @@ -585,6 +575,50 @@ columns: creators_filters: - *24 - *19 + entropy_configurations: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: auto_close_period + cast_type: *11 + sql_type_metadata: *12 + 'null': false + default: 2592000 + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: auto_reconsider_period + cast_type: *11 + sql_type_metadata: *12 + 'null': false + default: 2592000 + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: container_id + cast_type: *1 + sql_type_metadata: *2 + 'null': false + default: + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: container_type + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - *5 + - *6 + - *9 events: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: @@ -997,6 +1031,7 @@ primary_keys: commands: id comments: id creators_filters: + entropy_configurations: id events: id filters: id filters_stages: @@ -1035,6 +1070,7 @@ data_sources: commands: true comments: true creators_filters: true + entropy_configurations: true events: true filters: true filters_stages: true @@ -1447,22 +1483,6 @@ indexes: comment: valid: true collections: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: collections - name: index_collections_on_auto_close_period - unique: false - columns: - - auto_close_period - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: collections name: index_collections_on_creator_id @@ -1646,6 +1666,60 @@ indexes: nulls_not_distinct: comment: valid: true + entropy_configurations: + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: entropy_configurations + name: idx_on_container_type_container_id_auto_close_perio_74dc880875 + unique: false + columns: + - container_type + - container_id + - auto_close_period + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: entropy_configurations + name: idx_on_container_type_container_id_auto_reconsider__583aaddbea + unique: false + columns: + - container_type + - container_id + - auto_reconsider_period + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: entropy_configurations + name: index_entropy_configurations_on_container + unique: true + columns: + - container_type + - container_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true events: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: events @@ -2137,4 +2211,4 @@ indexes: comment: valid: true workflows: [] -version: 20250601161653 +version: 20250604080022 diff --git a/test/fixtures/collections.yml b/test/fixtures/collections.yml index 2f00b7646..f08054e6c 100644 --- a/test/fixtures/collections.yml +++ b/test/fixtures/collections.yml @@ -2,11 +2,9 @@ writebook: name: Writebook creator: david all_access: true - auto_close_period: <%= 30.days.to_i %> workflow: qa private: name: Private collection creator: kevin all_access: false - auto_close_period: <%= 30.days.to_i %> diff --git a/test/fixtures/entropy/configurations.yml b/test/fixtures/entropy/configurations.yml new file mode 100644 index 000000000..3046b360f --- /dev/null +++ b/test/fixtures/entropy/configurations.yml @@ -0,0 +1,9 @@ +writebook_collection: + container: writebook (Collection) + auto_reconsider_period: <%= 30.days.to_i %> + auto_close_period: <%= 30.days.to_i %> + +private_collection: + container: private (Collection) + auto_reconsider_period: <%= 30.days.to_i %> + auto_close_period: <%= 30.days.to_i %> diff --git a/test/models/card/entropy_test.rb b/test/models/card/entropy_test.rb index 2c20f58a8..682c6dc0a 100644 --- a/test/models/card/entropy_test.rb +++ b/test/models/card/entropy_test.rb @@ -8,7 +8,7 @@ class Card::EntropyTest < ActiveSupport::TestCase test "auto_close_at infers the period from the collection" do freeze_time - collections(:writebook).update! auto_close_period: 123.days + entropy_configurations(:writebook_collection).update! auto_close_period: 123.days cards(:layout).update! last_active_at: 2.day.ago assert_equal (123-2).days.from_now, cards(:layout).auto_close_at end @@ -16,8 +16,8 @@ class Card::EntropyTest < ActiveSupport::TestCase test "auto close all due" do cards(:logo, :shipping).each(&:reconsider) - cards(:logo).update!(last_active_at: 1.day.ago - collections(:writebook).auto_close_period) - cards(:shipping).update!(last_active_at: 1.day.from_now - collections(:writebook).auto_close_period) + cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations(:writebook_collection).auto_close_period) + cards(:shipping).update!(last_active_at: 1.day.from_now - entropy_configurations(:writebook_collection).auto_close_period) assert_difference -> { Card.closed.count }, +1 do Card.auto_close_all_due @@ -30,7 +30,7 @@ class Card::EntropyTest < ActiveSupport::TestCase test "don't auto close those cards where the collection has no auto close period" do cards(:logo, :shipping).each(&:reconsider) - collections(:writebook).update auto_close_period: nil + collections(:writebook).entropy_configuration.destroy assert_no_difference -> { Card.closed.count } do Card.auto_close_all_due From 07424f6cb1c2b44c98299ce45f7b54a605c9458d Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 10:59:01 +0200 Subject: [PATCH 04/50] Remove old columns --- ...250604080022_remove_auto_close_period_from_collections.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 db/migrate/20250604080022_remove_auto_close_period_from_collections.rb diff --git a/db/migrate/20250604080022_remove_auto_close_period_from_collections.rb b/db/migrate/20250604080022_remove_auto_close_period_from_collections.rb new file mode 100644 index 000000000..226655f84 --- /dev/null +++ b/db/migrate/20250604080022_remove_auto_close_period_from_collections.rb @@ -0,0 +1,5 @@ +class RemoveAutoClosePeriodFromCollections < ActiveRecord::Migration[8.1] + def change + remove_column :collections, :auto_close_period + end +end From 3c61fc3cdfb94319992e639d7fb23fa831fe314f Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 11:25:10 +0200 Subject: [PATCH 05/50] Add screen to edit account settings to tweak default entropy configuration --- .../entropy_configurations_controller.rb | 12 ++++++++++ .../accounts/settings_controller.rb | 5 +++++ app/models/collection/accessible.rb | 4 ++++ app/views/accounts/settings/show.html.erb | 1 + .../collections/edit/_auto_close.html.erb | 19 +--------------- app/views/entropy/_auto_close.html.erb | 22 +++++++++++++++++++ config/routes.rb | 5 +++++ 7 files changed, 50 insertions(+), 18 deletions(-) create mode 100644 app/controllers/accounts/entropy_configurations_controller.rb create mode 100644 app/controllers/accounts/settings_controller.rb create mode 100644 app/views/accounts/settings/show.html.erb create mode 100644 app/views/entropy/_auto_close.html.erb diff --git a/app/controllers/accounts/entropy_configurations_controller.rb b/app/controllers/accounts/entropy_configurations_controller.rb new file mode 100644 index 000000000..7781512f6 --- /dev/null +++ b/app/controllers/accounts/entropy_configurations_controller.rb @@ -0,0 +1,12 @@ +class Accounts::EntropyConfigurationsController < ApplicationController + def update + Account.sole.default_entropy_configuration.update!(entropy_configuration_params) + + redirect_to account_settings_path, notice: "Account updated" + end + + private + def entropy_configuration_params + params.expect(entropy_configuration: [ :auto_close_period, :auto_reconsider_period ]) + end +end diff --git a/app/controllers/accounts/settings_controller.rb b/app/controllers/accounts/settings_controller.rb new file mode 100644 index 000000000..6727ead6a --- /dev/null +++ b/app/controllers/accounts/settings_controller.rb @@ -0,0 +1,5 @@ +class Accounts::SettingsController < ApplicationController + def show + @account = Account.sole + end +end diff --git a/app/models/collection/accessible.rb b/app/models/collection/accessible.rb index fc99cc217..bc591499d 100644 --- a/app/models/collection/accessible.rb +++ b/app/models/collection/accessible.rb @@ -32,6 +32,10 @@ module Collection::Accessible accesses.find_by(user: user) end + def accessible_by?(user) + access_for(user).present? + end + private def grant_access_to_everyone accesses.grant_to(User.all) if all_access_previously_changed?(to: true) diff --git a/app/views/accounts/settings/show.html.erb b/app/views/accounts/settings/show.html.erb new file mode 100644 index 000000000..98424b078 --- /dev/null +++ b/app/views/accounts/settings/show.html.erb @@ -0,0 +1 @@ +<%= render "entropy/auto_close", model: @account.default_entropy_configuration, url: account_entropy_configuration_path %> diff --git a/app/views/collections/edit/_auto_close.html.erb b/app/views/collections/edit/_auto_close.html.erb index d4455e45b..a5356b6d9 100644 --- a/app/views/collections/edit/_auto_close.html.erb +++ b/app/views/collections/edit/_auto_close.html.erb @@ -1,20 +1,3 @@
- Do or Die -
-
- Cards in Considering -

Auto-close after…

- <%= form_with model: collection, data: { controller: "form" } do |form| %> - <%= form.select :auto_reconsider_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> - <% end %> -
-
-
- Cards in Doing -

Fall back to Considering after…

- <%= form_with model: collection, data: { controller: "form" } do |form| %> - <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> - <% end %> -
-
+ <%= render "entropy/auto_close", model: collection %>
diff --git a/app/views/entropy/_auto_close.html.erb b/app/views/entropy/_auto_close.html.erb new file mode 100644 index 000000000..b804a06bf --- /dev/null +++ b/app/views/entropy/_auto_close.html.erb @@ -0,0 +1,22 @@ +<% url = local_assigns[:url] %> + +
+ Do or Die +
+
+ Cards in Considering +

Auto-close after…

+ <%= form_with model: model, url: url, data: { controller: "form" } do |form| %> + <%= form.select :auto_reconsider_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> + <% end %> +
+
+
+ Cards in Doing +

Fall back to Considering after…

+ <%= form_with model: model, url: url, data: { controller: "form" } do |form| %> + <%= form.select :auto_close_period, collection_auto_close_options, {}, { class: "input input--select full-width margin-block-half txt-small fill-white txt-align-center", data: { action: "change->form#submit" } } %> + <% end %> +
+
+
diff --git a/config/routes.rb b/config/routes.rb index 49dcad7fe..e1efdc834 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,11 @@ Rails.application.routes.draw do resource :account do resource :join_code, module: :accounts + + scope module: :accounts do + resource :settings + resource :entropy_configuration + end end resources :users do From 2ee857a4d2698df4509fb431f80ac64d1efaa64d Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 11:36:47 +0200 Subject: [PATCH 06/50] Add fixture for account entropy configurations --- test/fixtures/entropy/configurations.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/fixtures/entropy/configurations.yml b/test/fixtures/entropy/configurations.yml index 3046b360f..cab952c69 100644 --- a/test/fixtures/entropy/configurations.yml +++ b/test/fixtures/entropy/configurations.yml @@ -1,3 +1,8 @@ +37s_account: + container: 37s (Account) + auto_reconsider_period: <%= 30.days.to_i %> + auto_close_period: <%= 30.days.to_i %> + writebook_collection: container: writebook (Collection) auto_reconsider_period: <%= 30.days.to_i %> From 6501604a5f9bdf82f2be98b594414dda0f8643ed Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 11:44:18 +0200 Subject: [PATCH 07/50] All the collections are "auto closing now" --- app/models/card/entropy.rb | 9 ++++----- app/models/collection/auto_closing.rb | 5 ----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index d5dd17268..2bec8d0ec 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -5,10 +5,9 @@ module Card::Entropy ENTROPY_REMINDER_BEFORE = 7.days included do - scope :in_auto_closing_collection, -> { joins(:collection).merge(Collection.auto_closing) } - - scope :stagnated, -> { doing.where(last_active_at: ..AUTO_RECONSIDER_PERIOD.ago) } - scope :due_to_be_closed, -> { considering.in_auto_closing_collection.where("last_active_at <= DATETIME('now', '-' || entropy_configurations.auto_close_period || ' seconds')") } + scope :with_entropy_configuration, -> { joins(collection: :entropy_configuration) } + scope :stagnated, -> { doing.with_entropy_configuration.where(last_active_at: ..AUTO_RECONSIDER_PERIOD.ago) } + scope :due_to_be_closed, -> { considering.with_entropy_configuration.where("last_active_at <= DATETIME('now', '-' || entropy_configurations.auto_close_period || ' seconds')") } delegate :auto_close_period, :auto_reconsider_period, to: :collection end @@ -34,7 +33,7 @@ module Card::Entropy end def auto_closing? - considering? && collection.auto_closing? && last_active_at + considering? && last_active_at end def auto_close_at diff --git a/app/models/collection/auto_closing.rb b/app/models/collection/auto_closing.rb index c8e52cb12..5f000c48b 100644 --- a/app/models/collection/auto_closing.rb +++ b/app/models/collection/auto_closing.rb @@ -2,14 +2,9 @@ module Collection::AutoClosing extend ActiveSupport::Concern included do - scope :auto_closing, -> { joins(:entropy_configuration).where.not("entropy_configurations.auto_close_period": nil) } before_create :set_default_auto_close_period end - def auto_closing? - entropy_configuration.present? - end - private DEFAULT_AUTO_CLOSE_PERIOD = 30.days From 741a6faaf53046991c8c42dc49878360b73c7226 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:01:09 +0200 Subject: [PATCH 08/50] Consider the new recording period for stagnated cards --- app/models/card/entropy.rb | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 2bec8d0ec..b4a2fbc5a 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -5,9 +5,19 @@ module Card::Entropy ENTROPY_REMINDER_BEFORE = 7.days included do - scope :with_entropy_configuration, -> { joins(collection: :entropy_configuration) } - scope :stagnated, -> { doing.with_entropy_configuration.where(last_active_at: ..AUTO_RECONSIDER_PERIOD.ago) } - scope :due_to_be_closed, -> { considering.with_entropy_configuration.where("last_active_at <= DATETIME('now', '-' || entropy_configurations.auto_close_period || ' seconds')") } + scope :entropic_by, ->(period_name) do + left_outer_joins(collection: :entropy_configuration) + .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", + Entropy::Configuration.select(period_name).where(container_type: "Account").limit(1).to_sql) + end + + scope :stagnated, -> do + doing.entropic_by(:auto_reconsider_period) + end + + scope :due_to_be_closed, -> do + considering.entropic_by(:auto_close_period) + end delegate :auto_close_period, :auto_reconsider_period, to: :collection end From a39ad0cae321f6a9bccabd65a6cb95a99ddc00b1 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:02:19 +0200 Subject: [PATCH 09/50] Not needed --- app/models/collection/accessible.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/models/collection/accessible.rb b/app/models/collection/accessible.rb index bc591499d..fc99cc217 100644 --- a/app/models/collection/accessible.rb +++ b/app/models/collection/accessible.rb @@ -32,10 +32,6 @@ module Collection::Accessible accesses.find_by(user: user) end - def accessible_by?(user) - access_for(user).present? - end - private def grant_access_to_everyone accesses.grant_to(User.all) if all_access_previously_changed?(to: true) From ae1ad9c5098c43d052c9fbed425d500ceb313304 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:04:11 +0200 Subject: [PATCH 10/50] Extract scope to clarify semantics --- app/models/card/entropy.rb | 2 +- app/models/entropy/configuration.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index b4a2fbc5a..874c87ddd 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -8,7 +8,7 @@ module Card::Entropy scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", - Entropy::Configuration.select(period_name).where(container_type: "Account").limit(1).to_sql) + Entropy::Configuration.default.to_sql) end scope :stagnated, -> do diff --git a/app/models/entropy/configuration.rb b/app/models/entropy/configuration.rb index 72370670f..995f49840 100644 --- a/app/models/entropy/configuration.rb +++ b/app/models/entropy/configuration.rb @@ -1,3 +1,5 @@ class Entropy::Configuration < ApplicationRecord belongs_to :container, polymorphic: true + + scope :default, -> { where(container_type: "Account").limit(1) } end From f3e6f1e5e01bdf962425cae2b81c2fba0fca4576 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:12:06 +0200 Subject: [PATCH 11/50] This is clearer imo --- app/models/card/entropy.rb | 2 +- app/models/entropy/configuration.rb | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 874c87ddd..3b988f00d 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -8,7 +8,7 @@ module Card::Entropy scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", - Entropy::Configuration.default.to_sql) + Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).limit(1).to_sql) end scope :stagnated, -> do diff --git a/app/models/entropy/configuration.rb b/app/models/entropy/configuration.rb index 995f49840..72370670f 100644 --- a/app/models/entropy/configuration.rb +++ b/app/models/entropy/configuration.rb @@ -1,5 +1,3 @@ class Entropy::Configuration < ApplicationRecord belongs_to :container, polymorphic: true - - scope :default, -> { where(container_type: "Account").limit(1) } end From f4bbab1485470481fa68c30cecb0d10c08510c17 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:13:36 +0200 Subject: [PATCH 12/50] More concise --- app/models/card/entropy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 3b988f00d..f91b8e814 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -8,7 +8,7 @@ module Card::Entropy scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", - Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).limit(1).to_sql) + Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).select("id").limit(1)) end scope :stagnated, -> do From 23f3c421143100e7759b01bebb546ed8924ff874 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:34:17 +0200 Subject: [PATCH 13/50] Use the new setting to calculate the auto_reconsider_period --- app/models/card/entropy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index f91b8e814..50a624a45 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -55,7 +55,7 @@ module Card::Entropy end def auto_reconsider_at - last_active_at + AUTO_RECONSIDER_PERIOD if auto_reconsidering? + last_active_at + auto_reconsider_period if auto_reconsidering? end def days_until_reconsider From 75ce06733a90d62e1d3de47b4f07c5a0e6a6fd78 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:44:50 +0200 Subject: [PATCH 14/50] Review entropy tests to include the new behavior and settings --- app/models/card/entropy.rb | 2 +- test/fixtures/entropy/configurations.yml | 4 +- test/models/card/entropy_test.rb | 71 ++++++++++++++++++++---- 3 files changed, 63 insertions(+), 14 deletions(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 50a624a45..4bfdba611 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -8,7 +8,7 @@ module Card::Entropy scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", - Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).select("id").limit(1)) + Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).select(period_name).limit(1)) end scope :stagnated, -> do diff --git a/test/fixtures/entropy/configurations.yml b/test/fixtures/entropy/configurations.yml index cab952c69..2ead35c67 100644 --- a/test/fixtures/entropy/configurations.yml +++ b/test/fixtures/entropy/configurations.yml @@ -5,8 +5,8 @@ writebook_collection: container: writebook (Collection) - auto_reconsider_period: <%= 30.days.to_i %> - auto_close_period: <%= 30.days.to_i %> + auto_reconsider_period: <%= 90.days.to_i %> + auto_close_period: <%= 90.days.to_i %> private_collection: container: private (Collection) diff --git a/test/models/card/entropy_test.rb b/test/models/card/entropy_test.rb index 682c6dc0a..d9c569515 100644 --- a/test/models/card/entropy_test.rb +++ b/test/models/card/entropy_test.rb @@ -5,15 +5,58 @@ class Card::EntropyTest < ActiveSupport::TestCase Current.session = sessions(:david) end - test "auto_close_at infers the period from the collection" do + test "auto_close_at uses the period defined in the account by default" do + freeze_time + + entropy_configurations(:writebook_collection).destroy + entropy_configurations("37s_account").reload.update! auto_close_period: 456.days + cards(:layout).update! last_active_at: 2.day.ago + assert_equal (456 - 2).days.from_now, cards(:layout).auto_close_at + end + + test "auto_close_at infers the period from the collection when present" do freeze_time entropy_configurations(:writebook_collection).update! auto_close_period: 123.days cards(:layout).update! last_active_at: 2.day.ago - assert_equal (123-2).days.from_now, cards(:layout).auto_close_at + assert_equal (123 - 2).days.from_now, cards(:layout).auto_close_at end - test "auto close all due" do + test "auto_reconsider_at uses the period defined in the account by default" do + freeze_time + + cards(:layout).engage + entropy_configurations(:writebook_collection).destroy + entropy_configurations("37s_account").reload.update! auto_reconsider_period: 456.days + cards(:layout).update! last_active_at: 2.day.ago + assert_equal (456 - 2).days.from_now, cards(:layout).auto_reconsider_at + end + + test "auto_reconsider_at infers the period from the collection when present" do + freeze_time + + cards(:layout).engage + entropy_configurations(:writebook_collection).update! auto_reconsider_period: 123.days + cards(:layout).update! last_active_at: 2.day.ago + assert_equal (123 - 2).days.from_now, cards(:layout).auto_reconsider_at + end + + test "auto close all due using the default account entropy configuration" do + cards(:logo, :shipping).each(&:reconsider) + entropy_configurations(:writebook_collection).destroy + + cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations("37s_account").auto_close_period) + cards(:shipping).update!(last_active_at: 1.day.from_now - entropy_configurations("37s_account").auto_close_period) + + assert_difference -> { Card.closed.count }, +1 do + Card.auto_close_all_due + end + + assert cards(:logo).reload.closed? + assert_not cards(:shipping).reload.closed? + end + + test "auto close all due using entropy configuration defined at the collection level" do cards(:logo, :shipping).each(&:reconsider) cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations(:writebook_collection).auto_close_period) @@ -27,16 +70,22 @@ class Card::EntropyTest < ActiveSupport::TestCase assert_not cards(:shipping).reload.closed? end - test "don't auto close those cards where the collection has no auto close period" do - cards(:logo, :shipping).each(&:reconsider) + test "auto consider all stagnated using the default account entropy configuration" do + travel_to Time.current - collections(:writebook).entropy_configuration.destroy + cards(:logo, :shipping).each(&:engage) + entropy_configurations(:writebook_collection).destroy - assert_no_difference -> { Card.closed.count } do - Card.auto_close_all_due + cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations("37s_account").auto_close_period) + cards(:shipping).update!(last_active_at: 1.day.from_now - entropy_configurations("37s_account").auto_close_period) + + assert_difference -> { Card.considering.count }, +1 do + Card.auto_reconsider_all_stagnated end - assert_not cards(:logo).reload.closed? + assert cards(:shipping).reload.doing? + assert cards(:logo).reload.considering? + assert_equal Time.current, cards(:logo).last_active_at end test "auto_reconsider_all_stagnated" do @@ -44,8 +93,8 @@ class Card::EntropyTest < ActiveSupport::TestCase cards(:logo, :shipping).each(&:engage) - cards(:logo).update!(last_active_at: 1.day.ago - Card::AUTO_RECONSIDER_PERIOD) - cards(:shipping).update!(last_active_at: 1.day.from_now - Card::AUTO_RECONSIDER_PERIOD) + cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations("writebook_collection").auto_close_period) + cards(:shipping).update!(last_active_at: 1.day.from_now - entropy_configurations("writebook_collection").auto_close_period) assert_difference -> { Card.considering.count }, +1 do Card.auto_reconsider_all_stagnated From f721bc2c0a7e3e8a7c09f30fb453abbaf5db7cc1 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:52:11 +0200 Subject: [PATCH 15/50] Format --- app/models/card/entropy.rb | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 4bfdba611..7aec14ac3 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -11,14 +11,8 @@ module Card::Entropy Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).select(period_name).limit(1)) end - scope :stagnated, -> do - doing.entropic_by(:auto_reconsider_period) - end - - scope :due_to_be_closed, -> do - considering.entropic_by(:auto_close_period) - end - + scope :stagnated, -> { doing.entropic_by(:auto_reconsider_period) } + scope :due_to_be_closed, -> { considering.entropic_by(:auto_close_period) } delegate :auto_close_period, :auto_reconsider_period, to: :collection end From dd78ec2db2cd68119df3d114f1b544bc33c4b553 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:56:11 +0200 Subject: [PATCH 16/50] Shortcut to grab the default entropy configuration --- .../accounts/entropy_configurations_controller.rb | 2 +- app/models/card/entropy.rb | 2 +- app/models/collection/entropy.rb | 2 +- app/models/entropy/configuration.rb | 6 ++++++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/controllers/accounts/entropy_configurations_controller.rb b/app/controllers/accounts/entropy_configurations_controller.rb index 7781512f6..07e828242 100644 --- a/app/controllers/accounts/entropy_configurations_controller.rb +++ b/app/controllers/accounts/entropy_configurations_controller.rb @@ -1,6 +1,6 @@ class Accounts::EntropyConfigurationsController < ApplicationController def update - Account.sole.default_entropy_configuration.update!(entropy_configuration_params) + Entropy::Configuration.default.update!(entropy_configuration_params) redirect_to account_settings_path, notice: "Account updated" end diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 7aec14ac3..41fcd4cfd 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -8,7 +8,7 @@ module Card::Entropy scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", - Entropy::Configuration.where(id: Account.sole.default_entropy_configuration).select(period_name).limit(1)) + Entropy::Configuration.where(id: Entropy::Configuration.default).select(period_name).limit(1)) end scope :stagnated, -> { doing.entropic_by(:auto_reconsider_period) } diff --git a/app/models/collection/entropy.rb b/app/models/collection/entropy.rb index ddf0883f4..b7b55133b 100644 --- a/app/models/collection/entropy.rb +++ b/app/models/collection/entropy.rb @@ -6,7 +6,7 @@ module Collection::Entropy end def entropy_configuration - super || Account.sole.default_entropy_configuration + super || Entropy::Configuration.default end def auto_close_period=(new_value) diff --git a/app/models/entropy/configuration.rb b/app/models/entropy/configuration.rb index 72370670f..4ed13d597 100644 --- a/app/models/entropy/configuration.rb +++ b/app/models/entropy/configuration.rb @@ -1,3 +1,9 @@ class Entropy::Configuration < ApplicationRecord belongs_to :container, polymorphic: true + + class << self + def default + Account.sole.default_entropy_configuration + end + end end From b8b8a2fa0369af59183a00085b402d6652b40df6 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:58:17 +0200 Subject: [PATCH 17/50] Simpler --- app/models/card/entropy.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 41fcd4cfd..0a4e19c79 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -7,8 +7,7 @@ module Card::Entropy included do scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) - .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", - Entropy::Configuration.where(id: Entropy::Configuration.default).select(period_name).limit(1)) + .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", Entropy::Configuration.default.public_send(period_name)) end scope :stagnated, -> { doing.entropic_by(:auto_reconsider_period) } From b35aefd07b1b33f53cd9b1a84a70fdbfa0b10452 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:58:33 +0200 Subject: [PATCH 18/50] Format --- app/models/card/entropy.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 0a4e19c79..643107e22 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -7,7 +7,8 @@ module Card::Entropy included do scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) - .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", Entropy::Configuration.default.public_send(period_name)) + .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", + Entropy::Configuration.default.public_send(period_name)) end scope :stagnated, -> { doing.entropic_by(:auto_reconsider_period) } From 653186a8b909b16e6e1fc86f57f0a32048141d0b Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 12:58:55 +0200 Subject: [PATCH 19/50] Remove unneeded parenthesis --- app/models/card/entropy.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index 643107e22..df7f7aa4c 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -7,7 +7,7 @@ module Card::Entropy included do scope :entropic_by, ->(period_name) do left_outer_joins(collection: :entropy_configuration) - .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, (?)) || ' seconds')", + .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, ?) || ' seconds')", Entropy::Configuration.default.public_send(period_name)) end From e8249d2405436b870aaebae22c0aa5697f3ff871 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 13:13:45 +0200 Subject: [PATCH 20/50] Consider the new default settings for previews --- app/helpers/cards_helper.rb | 4 ++++ app/views/cards/display/_preview.html.erb | 2 +- app/views/cards/index/engagement/_doing.html.erb | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/helpers/cards_helper.rb b/app/helpers/cards_helper.rb index e34a51d3d..0ab9a2b8b 100644 --- a/app/helpers/cards_helper.rb +++ b/app/helpers/cards_helper.rb @@ -56,4 +56,8 @@ module CardsHelper "Closes" end end + + def cacheable_preview_parts_for(card) + [card, card.collection.workflow, Entroy::Configuration::Default] + end end diff --git a/app/views/cards/display/_preview.html.erb b/app/views/cards/display/_preview.html.erb index 767d54a82..761df138d 100644 --- a/app/views/cards/display/_preview.html.erb +++ b/app/views/cards/display/_preview.html.erb @@ -1,4 +1,4 @@ -<% cache [ card, card.collection.workflow ] do %> +<% cache cacheable_preview_parts_for(card) do %> <%= card_article_tag card, class: "card" do %>
diff --git a/app/views/cards/index/engagement/_doing.html.erb b/app/views/cards/index/engagement/_doing.html.erb index fe652ac8c..3b6db623b 100644 --- a/app/views/cards/index/engagement/_doing.html.erb +++ b/app/views/cards/index/engagement/_doing.html.erb @@ -8,7 +8,7 @@ <% end %> <% if page.used? %> - <%= render partial: "cards/display/preview", collection: page.records, as: :card, cached: ->(record) { [ record, record.collection.workflow ] } %> + <%= render partial: "cards/display/preview", collection: page.records, as: :card, cached: ->(card) { cacheable_preview_parts_for(card) } %> <% unless page.last? %> <%= cards_next_page_link "doing-cards", page: page, filter: filter %> From 2870924ca70ac6b252696a6b7f556d57ec772e9e Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 13:14:46 +0200 Subject: [PATCH 21/50] Format --- app/helpers/cards_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/cards_helper.rb b/app/helpers/cards_helper.rb index 0ab9a2b8b..821e9a145 100644 --- a/app/helpers/cards_helper.rb +++ b/app/helpers/cards_helper.rb @@ -58,6 +58,6 @@ module CardsHelper end def cacheable_preview_parts_for(card) - [card, card.collection.workflow, Entroy::Configuration::Default] + [ card, card.collection.workflow, Entroy::Configuration::Default ] end end From 6cb1fef909addb8aed766ae81d21d041315e9930 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 13:26:10 +0200 Subject: [PATCH 22/50] Add controller tests --- .../entropy_configurations_controller_test.rb | 16 ++++++++++++++++ .../accounts/settings_controller_test.rb | 12 ++++++++++++ test/controllers/collections_controller_test.rb | 6 +++++- 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/controllers/accounts/entropy_configurations_controller_test.rb create mode 100644 test/controllers/accounts/settings_controller_test.rb diff --git a/test/controllers/accounts/entropy_configurations_controller_test.rb b/test/controllers/accounts/entropy_configurations_controller_test.rb new file mode 100644 index 000000000..b888b8deb --- /dev/null +++ b/test/controllers/accounts/entropy_configurations_controller_test.rb @@ -0,0 +1,16 @@ +require "test_helper" + +class Accounts::EntropyConfigurationsControllerTest < ActionDispatch::IntegrationTest + setup do + sign_in_as :kevin + end + + test "update" do + put account_entropy_configuration_path, params: { entropy_configuration: { auto_close_period: 1.day, auto_reconsider_period: 2.days } } + + assert_equal 1.day, entropy_configurations("37s_account").auto_close_period + assert_equal 2.days, entropy_configurations("37s_account").auto_reconsider_period + + assert_redirected_to account_settings_path + end +end diff --git a/test/controllers/accounts/settings_controller_test.rb b/test/controllers/accounts/settings_controller_test.rb new file mode 100644 index 000000000..606245612 --- /dev/null +++ b/test/controllers/accounts/settings_controller_test.rb @@ -0,0 +1,12 @@ +require "test_helper" + +class Accounts::SettingsControllerTest < ActionDispatch::IntegrationTest + setup do + sign_in_as :kevin + end + + test "show" do + get account_settings_path + assert_response :success + end +end diff --git a/test/controllers/collections_controller_test.rb b/test/controllers/collections_controller_test.rb index 9d3275078..02b8683e0 100644 --- a/test/controllers/collections_controller_test.rb +++ b/test/controllers/collections_controller_test.rb @@ -30,7 +30,9 @@ class CollectionsControllerTest < ActionDispatch::IntegrationTest patch collection_path(collections(:writebook)), params: { collection: { name: "Writebook bugs", - all_access: false + all_access: false, + auto_close_period: 1.day, + auto_reconsider_period: 2.days, }, user_ids: users(:david, :jz).pluck(:id) } @@ -38,6 +40,8 @@ class CollectionsControllerTest < ActionDispatch::IntegrationTest assert_redirected_to edit_collection_path(collections(:writebook)) assert_equal "Writebook bugs", collections(:writebook).reload.name assert_equal users(:david, :jz).sort, collections(:writebook).users.sort + assert_equal 1.day, entropy_configurations(:writebook_collection).auto_close_period + assert_equal 2.days, entropy_configurations(:writebook_collection).auto_reconsider_period assert_not collections(:writebook).all_access? end From 407141f37610e69315577964823234745ac28af4 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 13:31:23 +0200 Subject: [PATCH 23/50] Fix typo --- app/helpers/cards_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/cards_helper.rb b/app/helpers/cards_helper.rb index 821e9a145..ff760794f 100644 --- a/app/helpers/cards_helper.rb +++ b/app/helpers/cards_helper.rb @@ -58,6 +58,6 @@ module CardsHelper end def cacheable_preview_parts_for(card) - [ card, card.collection.workflow, Entroy::Configuration::Default ] + [ card, card.collection.workflow, Entropy::Configuration.default ] end end From db86d17880867cd4c5a9194d3f6dab617fd5c862 Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Wed, 4 Jun 2025 14:38:22 +0200 Subject: [PATCH 24/50] Refactor to introduce an entropy model Instead of keeping two tracks of similar logic (auto-reconsider / auto-close). --- app/models/account.rb | 2 +- .../account/{entropy.rb => entropic.rb} | 2 +- app/models/card.rb | 2 +- app/models/card/entropic.rb | 35 ++++++++++ app/models/card/entropy.rb | 66 ++++--------------- app/models/collection.rb | 2 +- .../collection/{entropy.rb => entropic.rb} | 2 +- app/views/cards/container/_closure.html.erb | 10 +-- app/views/cards/display/_preview.html.erb | 2 +- .../cards/display/preview/_bubble.html.erb | 4 +- .../{entropy_test.rb => entropic_test.rb} | 20 +++--- 11 files changed, 73 insertions(+), 74 deletions(-) rename app/models/account/{entropy.rb => entropic.rb} (95%) create mode 100644 app/models/card/entropic.rb rename app/models/collection/{entropy.rb => entropic.rb} (95%) rename test/models/card/{entropy_test.rb => entropic_test.rb} (83%) diff --git a/app/models/account.rb b/app/models/account.rb index b2a4d71b5..afd022441 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -1,5 +1,5 @@ class Account < ApplicationRecord - include Entropy, Joinable + include Entropic, Joinable has_many_attached :uploads end diff --git a/app/models/account/entropy.rb b/app/models/account/entropic.rb similarity index 95% rename from app/models/account/entropy.rb rename to app/models/account/entropic.rb index 39c47f2b5..a60af2b4b 100644 --- a/app/models/account/entropy.rb +++ b/app/models/account/entropic.rb @@ -1,4 +1,4 @@ -module Account::Entropy +module Account::Entropic extend ActiveSupport::Concern included do diff --git a/app/models/card.rb b/app/models/card.rb index 656a51b12..0a109e5ba 100644 --- a/app/models/card.rb +++ b/app/models/card.rb @@ -1,5 +1,5 @@ class Card < ApplicationRecord - include Assignable, Colored, Engageable, Entropy, Eventable, + include Assignable, Colored, Engageable, Entropic, Eventable, Golden, Mentions, Pinnable, Closeable, Readable, Searchable, Staged, Statuses, Taggable, Watchable diff --git a/app/models/card/entropic.rb b/app/models/card/entropic.rb new file mode 100644 index 000000000..92725454b --- /dev/null +++ b/app/models/card/entropic.rb @@ -0,0 +1,35 @@ +module Card::Entropic + extend ActiveSupport::Concern + + included do + scope :entropic_by, ->(period_name) do + left_outer_joins(collection: :entropy_configuration) + .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, ?) || ' seconds')", + Entropy::Configuration.default.public_send(period_name)) + end + + scope :stagnated, -> { doing.entropic_by(:auto_reconsider_period) } + scope :due_to_be_closed, -> { considering.entropic_by(:auto_close_period) } + delegate :auto_close_period, :auto_reconsider_period, to: :collection + end + + class_methods do + def auto_close_all_due + due_to_be_closed.find_each do |card| + card.close(user: User.system, reason: "Closed") + end + end + + def auto_reconsider_all_stagnated + stagnated.find_each(&:reconsider) + end + end + + def entropy + Card::Entropy.for(self) + end + + def has_entropy? + entropy.present? + end +end diff --git a/app/models/card/entropy.rb b/app/models/card/entropy.rb index df7f7aa4c..c2cc45e10 100644 --- a/app/models/card/entropy.rb +++ b/app/models/card/entropy.rb @@ -1,62 +1,24 @@ -module Card::Entropy - extend ActiveSupport::Concern +class Card::Entropy + attr_reader :card, :auto_clean_period - AUTO_RECONSIDER_PERIOD = 30.days - ENTROPY_REMINDER_BEFORE = 7.days + class << self + def for(card) + return unless card.last_active_at - included do - scope :entropic_by, ->(period_name) do - left_outer_joins(collection: :entropy_configuration) - .where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.#{period_name}, ?) || ' seconds')", - Entropy::Configuration.default.public_send(period_name)) - end - - scope :stagnated, -> { doing.entropic_by(:auto_reconsider_period) } - scope :due_to_be_closed, -> { considering.entropic_by(:auto_close_period) } - delegate :auto_close_period, :auto_reconsider_period, to: :collection - end - - class_methods do - def auto_close_all_due - due_to_be_closed.find_each do |card| - card.close(user: User.system, reason: "Closed") + if card.considering? + new(card, card.auto_close_period) + elsif card.doing? + new(card, card.auto_reconsider_period) end end - - def auto_reconsider_all_stagnated - stagnated.find_each(&:reconsider) - end end - def subject_to_entropy? - auto_reconsidering? || auto_closing? + def initialize(card, auto_clean_period) + @card = card + @auto_clean_period = auto_clean_period end - def auto_reconsidering? - doing? && last_active_at - end - - def auto_closing? - considering? && last_active_at - end - - def auto_close_at - last_active_at + auto_close_period if auto_closing? - end - - def days_until_close - (auto_close_at.to_date - Date.current).to_i if auto_close_at - end - - def auto_reconsider_at - last_active_at + auto_reconsider_period if auto_reconsidering? - end - - def days_until_reconsider - (auto_reconsider_at.to_date - Date.current).to_i if auto_reconsider_at - end - - def entropy_cleaned_at - auto_close_at || auto_reconsider_at + def auto_clean_at + card.last_active_at + auto_clean_period end end diff --git a/app/models/collection.rb b/app/models/collection.rb index 0fc1e912e..105cd6eed 100644 --- a/app/models/collection.rb +++ b/app/models/collection.rb @@ -1,5 +1,5 @@ class Collection < ApplicationRecord - include AutoClosing, Accessible, Broadcastable, Entropy, Filterable, Workflowing + include AutoClosing, Accessible, Broadcastable, Entropic, Filterable, Workflowing belongs_to :creator, class_name: "User", default: -> { Current.user } diff --git a/app/models/collection/entropy.rb b/app/models/collection/entropic.rb similarity index 95% rename from app/models/collection/entropy.rb rename to app/models/collection/entropic.rb index b7b55133b..9a4a6b362 100644 --- a/app/models/collection/entropy.rb +++ b/app/models/collection/entropic.rb @@ -1,4 +1,4 @@ -module Collection::Entropy +module Collection::Entropic extend ActiveSupport::Concern included do diff --git a/app/views/cards/container/_closure.html.erb b/app/views/cards/container/_closure.html.erb index 24931b77c..c260fde03 100644 --- a/app/views/cards/container/_closure.html.erb +++ b/app/views/cards/container/_closure.html.erb @@ -22,10 +22,12 @@ - <% if card.doing? %> - Falls back to Considering if no activity <%= local_datetime_tag(card.auto_reconsider_at, style: :indays) -%>. - <% elsif card.auto_closing? %> - Auto-closes if no activity <%= local_datetime_tag(card.auto_close_at, style: :indays) -%>. + <% if card.has_entropy? %> + <% if card.doing? %> + Falls back to Considering if no activity <%= local_datetime_tag(card.entropy.auto_clean_at, style: :indays) -%>. + <% else %> + Auto-closes if no activity <%= local_datetime_tag(card.entropy.auto_clean_at, style: :indays) -%>. + <% end %> <% end %> <% end %> diff --git a/app/views/cards/display/_preview.html.erb b/app/views/cards/display/_preview.html.erb index 761df138d..be5de2077 100644 --- a/app/views/cards/display/_preview.html.erb +++ b/app/views/cards/display/_preview.html.erb @@ -29,7 +29,7 @@ <%= render "cards/display/common/background", card: card %> - <% if card.subject_to_entropy? %> + <% if card.has_entropy? %> <%= render "cards/display/preview/bubble", card: card %> <% end %> <% end %> diff --git a/app/views/cards/display/preview/_bubble.html.erb b/app/views/cards/display/preview/_bubble.html.erb index f0e3df352..676bef94c 100644 --- a/app/views/cards/display/preview/_bubble.html.erb +++ b/app/views/cards/display/preview/_bubble.html.erb @@ -1,7 +1,7 @@