Prepare entropy code for the new auto postpone approach

This commit is contained in:
Jorge Manrubia
2025-09-24 15:11:50 +02:00
parent b63a6b3e97
commit 2f6a639682
42 changed files with 125 additions and 395 deletions
@@ -7,6 +7,6 @@ class Account::EntropyConfigurationsController < ApplicationController
private
def entropy_configuration_params
params.expect(entropy_configuration: [ :auto_close_period, :auto_reconsider_period ])
params.expect(entropy_configuration: [ :auto_postpone_period ])
end
end
+1 -2
View File
@@ -11,9 +11,8 @@ class CardsController < ApplicationController
PAGE_SIZE = 25
def index
@columns = Cards::OldColumns.new(user_filtering: @user_filtering, page_size: PAGE_SIZE)
fresh_when etag: @columns
head :ok
end
def create
@@ -12,6 +12,6 @@ class Collections::EntropyConfigurationsController < ApplicationController
private
def entropy_configuration_params
params.expect(collection: [ :auto_close_period, :auto_reconsider_period ])
params.expect(collection: [ :auto_postpone_period ])
end
end
+1 -1
View File
@@ -38,7 +38,7 @@ class CollectionsController < ApplicationController
end
def collection_params
params.expect(collection: [ :name, :all_access, :auto_close_period, :auto_reconsider_period, :public_description ])
params.expect(collection: [ :name, :all_access, :auto_postpone_period, :public_description ])
end
def grantees
@@ -6,10 +6,10 @@ class Public::CollectionsController < ApplicationController
layout "public"
def show
@considering = current_page_from @collection.cards.considering.latest, per_page: CardsController::PAGE_SIZE
@on_deck = current_page_from @collection.cards.on_deck.latest, per_page: CardsController::PAGE_SIZE
@doing = current_page_from @collection.cards.doing.latest, per_page: CardsController::PAGE_SIZE
@closed = current_page_from @collection.cards.closed.recently_closed_first, per_page: CardsController::PAGE_SIZE
# @considering = current_page_from @collection.cards.considering.latest, per_page: CardsController::PAGE_SIZE
# @on_deck = current_page_from @collection.cards.on_deck.latest, per_page: CardsController::PAGE_SIZE
# @doing = current_page_from @collection.cards.doing.latest, per_page: CardsController::PAGE_SIZE
# @closed = current_page_from @collection.cards.closed.recently_closed_first, per_page: CardsController::PAGE_SIZE
# To enable caching at intermediate proxies during traffic spikes
expires_in 5.seconds, public: true
+2 -2
View File
@@ -35,8 +35,8 @@ module CardsHelper
classes = [
options.delete(:class),
("golden-effect" if card.golden?),
("card--considering" if card.considering?),
("card--doing" if card.doing?),
("card--postponed" if card.postponed?),
("card--active" if card.active?),
("card--drafted" if card.drafted?)
].compact.join(" ")
+1 -9
View File
@@ -7,7 +7,7 @@ module EntropyHelper
{
daysBeforeReminder: card.entropy.days_before_reminder,
closesAt: card.entropy.auto_clean_at.iso8601,
action: card_entropy_action(card)
action: "Postpones"
}
end
@@ -20,12 +20,4 @@ module EntropyHelper
}
end
end
def card_entropy_action(card)
if card.stagnated?
"Falls Back"
elsif card.considering?
"Closes"
end
end
end
-7
View File
@@ -1,7 +0,0 @@
class Card::AutoCloseAllDueJob < ApplicationJob
def perform
ApplicationRecord.with_each_tenant do |tenant|
Card.auto_close_all_due
end
end
end
@@ -0,0 +1,7 @@
class Card::AutoPostponeAllDueJob < ApplicationJob
def perform
ApplicationRecord.with_each_tenant do |tenant|
Card.auto_postpone_all_due
end
end
end
+1 -2
View File
@@ -12,7 +12,6 @@ module Account::Entropic
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
auto_postpone_period: DEFAULT_ENTROPY_PERIOD
end
end
+2 -2
View File
@@ -1,5 +1,5 @@
class Card < ApplicationRecord
include Assignable, Attachments, Cacheable, Closeable, Colored, Engageable, Entropic, Eventable,
include Assignable, Attachments, Cacheable, Closeable, Colored, Entropic, Eventable,
Golden, Mentions, Multistep, Pinnable, Postponable, Promptable, Readable, Searchable,
Staged, Stallable, Statuses, Taggable, Watchable
@@ -22,7 +22,7 @@ class Card < ApplicationRecord
scope :indexed_by, ->(index) do
case index
when "stalled" then stalled
when "closing_soon" then closing_soon
when "postponing_soon" then postponing_soon
when "falling_back_soon" then falling_back_soon
when "closed" then closed.recently_closed_first
when "golden" then golden
+2
View File
@@ -31,6 +31,7 @@ module Card::Closeable
def close(user: Current.user, reason: Closure::Reason.default)
unless closed?
transaction do
resume
create_closure! user: user, reason: reason
track_event :closed, creator: user
end
@@ -40,6 +41,7 @@ module Card::Closeable
def reopen(user: Current.user)
if closed?
transaction do
resume
closure&.destroy
track_event :reopened, creator: user
end
+3 -3
View File
@@ -14,11 +14,11 @@ module Card::Colored
DEFAULT_COLOR = "var(--color-card-default)"
def color
color_from_stage || DEFAULT_COLOR
color_from_column || DEFAULT_COLOR
end
private
def color_from_stage
stage&.color&.presence if doing?
def color_from_column
column&.color&.presence
end
end
+12 -30
View File
@@ -2,43 +2,29 @@ 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.or(on_deck).entropic_by(:auto_reconsider_period) }
scope :due_to_be_closed, -> { considering.entropic_by(:auto_close_period) }
scope :closing_soon, -> do
considering
scope :due_to_be_postponed, -> do
active
.left_outer_joins(collection: :entropy_configuration)
.where("last_active_at > DATETIME('now', '-' || COALESCE(entropy_configurations.auto_close_period, ?) || ' seconds')", Entropy::Configuration.default.auto_close_period)
.where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropy_configurations.auto_close_period, ?) * 0.75 AS INTEGER) || ' seconds')", Entropy::Configuration.default.auto_close_period)
.where("last_active_at <= DATETIME('now', '-' || COALESCE(entropy_configurations.auto_postpone_period, ?) || ' seconds')",
Entropy::Configuration.default.auto_postpone_period)
end
scope :falling_back_soon, -> do
doing.or(on_deck)
scope :postponing_soon, -> do
active
.left_outer_joins(collection: :entropy_configuration)
.where("last_active_at > DATETIME('now', '-' || COALESCE(entropy_configurations.auto_reconsider_period, ?) || ' seconds')", Entropy::Configuration.default.auto_reconsider_period)
.where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropy_configurations.auto_reconsider_period, ?) * 0.75 AS INTEGER) || ' seconds')", Entropy::Configuration.default.auto_reconsider_period)
.where("last_active_at > DATETIME('now', '-' || COALESCE(entropy_configurations.auto_postpone_period, ?) || ' seconds')", Entropy::Configuration.default.auto_postpone_period)
.where("last_active_at <= DATETIME('now', '-' || CAST(COALESCE(entropy_configurations.auto_postpone_period, ?) * 0.75 AS INTEGER) || ' seconds')", Entropy::Configuration.default.auto_postpone_period)
end
delegate :auto_close_period, :auto_reconsider_period, to: :collection
delegate :auto_postpone_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")
def auto_postpone_all_due
due_to_be_postponed.find_each do |card|
card.postpone
end
end
def auto_reconsider_all_stagnated
stagnated.find_each(&:reconsider)
end
end
def entropy
@@ -48,8 +34,4 @@ module Card::Entropic
def entropic?
entropy.present?
end
def stagnated?
card.doing? || card.on_deck?
end
end
+1 -5
View File
@@ -5,11 +5,7 @@ class Card::Entropy
def for(card)
return unless card.last_active_at
if card.considering?
new(card, card.auto_close_period)
elsif card.stagnated?
new(card, card.auto_reconsider_period)
end
new(card, card.auto_postpone_period)
end
end
+5 -1
View File
@@ -18,7 +18,11 @@ module Card::Postponable
def postpone
unless postponed?
create_not_now!
transaction do
reopen
activity_spike&.destroy
create_not_now!
end
end
end
+1 -1
View File
@@ -7,7 +7,7 @@ module Card::Stallable
has_one :activity_spike, class_name: "Card::ActivitySpike", dependent: :destroy
scope :with_activity_spikes, -> { joins(:activity_spike) }
scope :stalled, -> { open.with_activity_spikes.where("card_activity_spikes.updated_at": ..STALLED_AFTER_LAST_SPIKE_PERIOD.ago) }
scope :stalled, -> { open.active.with_activity_spikes.where("card_activity_spikes.updated_at": ..STALLED_AFTER_LAST_SPIKE_PERIOD.ago) }
before_update :remember_to_detect_activity_spikes
after_update_commit :detect_activity_spikes_later, if: :should_detect_activity_spikes?
-17
View File
@@ -1,17 +0,0 @@
class Cards::OldColumns::Column
attr_reader :page, :filter, :user_filtering
def initialize(page:, filter:, user_filtering:)
@page = page
@filter = filter
@user_filtering = user_filtering
end
def cards
page.records
end
def cache_key
ActiveSupport::Cache.expand_cache_key([ cards ])
end
end
-45
View File
@@ -1,45 +0,0 @@
class Cards::OldColumns
attr_reader :user_filtering, :page_size
delegate :filter, to: :user_filtering
def initialize(user_filtering:, page_size:)
@user_filtering = user_filtering
@page_size = page_size
end
def considering
@considering ||= build_column_for "considering"
end
def on_deck
@on_deck ||= build_column_for "on_deck"
end
def doing
@doing ||= build_column_for "doing"
end
def closed
@closed ||= if filter.indexed_by.stalled?
build_column(filter) { |cards| cards.recently_closed_first }
else
build_column(filter.with(indexed_by: "closed")) { |cards| cards.recently_closed_first }
end
end
def cache_key
ActiveSupport::Cache.expand_cache_key([ considering, on_deck, doing, closed, Workflow.all, user_filtering ])
end
private
def build_column_for(engagement_status)
build_column(filter.with(engagement_status: engagement_status))
end
def build_column(filter, &block)
cards = block ? yield(filter.cards) : filter.cards
Column.new(page: GearedPagination::Recordset.new(cards, per_page: page_size).page(1), filter: filter, user_filtering: user_filtering)
end
end
+4 -4
View File
@@ -2,13 +2,13 @@ module Collection::AutoClosing
extend ActiveSupport::Concern
included do
before_create :set_default_auto_close_period
before_create :set_default_auto_postpone_period
end
private
DEFAULT_AUTO_CLOSE_PERIOD = 30.days
DEFAULT_auto_postpone_period = 30.days
def set_default_auto_close_period
self.auto_close_period ||= DEFAULT_AUTO_CLOSE_PERIOD unless attribute_present?(:auto_close_period)
def set_default_auto_postpone_period
self.auto_postpone_period ||= DEFAULT_auto_postpone_period unless attribute_present?(:auto_postpone_period)
end
end
+3 -8
View File
@@ -2,20 +2,15 @@ module Collection::Entropic
extend ActiveSupport::Concern
included do
delegate :auto_close_period, :auto_reconsider_period, to: :entropy_configuration
delegate :auto_postpone_period, to: :entropy_configuration
end
def entropy_configuration
super || Entropy::Configuration.default
end
def auto_close_period=(new_value)
def auto_postpone_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
entropy_configuration.update auto_postpone_period: new_value
end
end
+1 -2
View File
@@ -23,12 +23,11 @@ class Filter < ApplicationRecord
result = result.sorted_by(sorted_by)
result = result.where(id: card_ids) if card_ids.present?
result = result.open unless include_closed_cards?
result = result.by_engagement_status(engagement_status) if engagement_status.present?
result = result.unassigned if assignment_status.unassigned?
result = result.assigned_to(assignees.ids) if assignees.present?
result = result.where(creator_id: creators.ids) if creators.present?
result = result.where(collection: collections.ids) if collections.present?
result = result.in_stage(stages.ids) if stages.present? && engagement_status&.doing?
result = result.in_stage(stages.ids) if stages.present?
result = result.tagged_with(tags.ids) if tags.present?
result = result.where("cards.created_at": creation_window) if creation_window
result = result.closed_at_window(closure_window) if closure_window
+2 -6
View File
@@ -1,7 +1,7 @@
module Filter::Fields
extend ActiveSupport::Concern
INDEXES = %w[ all stalled closing_soon falling_back_soon golden draft ]
INDEXES = %w[ all stalled postponing_soon falling_back_soon golden draft ]
SORTED_BY = %w[ newest oldest latest ]
delegate :default_value?, to: :class
@@ -18,7 +18,7 @@ module Filter::Fields
included do
store_accessor :fields, :assignment_status, :indexed_by, :sorted_by, :terms,
:engagement_status, :card_ids, :creation, :closure
:card_ids, :creation, :closure
def assignment_status
super.to_s.inquiry
@@ -32,10 +32,6 @@ module Filter::Fields
(super || default_sorted_by).inquiry
end
def engagement_status
super&.inquiry
end
def creation_window
TimeWindowParser.parse(creation)
end
-2
View File
@@ -5,7 +5,6 @@ module Filter::Params
:assignment_status,
:indexed_by,
:sorted_by,
:engagement_status,
:creation,
:closure,
card_ids: [],
@@ -54,7 +53,6 @@ module Filter::Params
@as_params ||= {}.tap do |params|
params[:indexed_by] = indexed_by
params[:sorted_by] = sorted_by
params[:engagement_status] = engagement_status
params[:creation] = creation
params[:closure] = closure
params[:assignment_status] = assignment_status
+1 -1
View File
@@ -22,7 +22,7 @@
</h3>
</div>
<%= render "cards/display/preview/stages", card: card if card.doing? %>
<%= render "cards/display/preview/stages", card: card %>
</div>
</div>
@@ -1,4 +1,4 @@
<%= tag.div hidden: true, class: "bubble bubble--#{card.engagement_status}",
<%= tag.div hidden: true, class: "bubble",
data: {
controller: "bubble",
action: "turbo:morph-element->bubble#update",
+1 -1
View File
@@ -7,7 +7,7 @@
<%= form_with model: model, url: url, data: { controller: "form" } do |form| %>
<%= render "entropy/knob",
form: form,
name: :auto_close_period,
name: :auto_postpone_period,
knob_options: entropy_auto_close_options,
label: "Days until auto-close" %>
<% end %>
+1 -1
View File
@@ -32,7 +32,7 @@
<div class="card__body justify-space-between">
<%= render "public/cards/show/title", card: @card %>
<%= render "cards/display/public_preview/stages", card: @card if @card.doing? %>
<%= render "cards/display/public_preview/stages", card: @card %>
</div>
<%= render "public/cards/show/steps", card: @card %>
+2 -2
View File
@@ -1,6 +1,6 @@
production: &production
auto_close_all_due:
class: Card::AutoCloseAllDueJob
auto_postpone_all_due:
class: Card::AutoPostponeAllDueJob
schedule: every hour
auto_reconsider_all_inactive:
class: Card::AutoReconsiderAllStagnatedJob
-1
View File
@@ -35,7 +35,6 @@ Rails.application.routes.draw do
resources :cards, only: %i[ index show edit update destroy ] do
scope module: :cards do
resource :engagement
resource :goldness
resource :image
resource :pin
@@ -0,0 +1,17 @@
class AddAutoPostponePeriod < ActiveRecord::Migration[8.1]
def change
add_column :entropy_configurations, :auto_postpone_period, :bigint, default: 30.days.to_i, null: false
add_index :entropy_configurations, [:container_type, :container_id, :auto_postpone_period]
execute <<-SQL
UPDATE entropy_configurations
SET auto_postpone_period = auto_close_period
SQL
remove_index :entropy_configurations, name: "idx_on_container_type_container_id_auto_close_perio_74dc880875"
remove_index :entropy_configurations, name: "idx_on_container_type_container_id_auto_reconsider__583aaddbea"
remove_column :entropy_configurations, :auto_close_period, :bigint
remove_column :entropy_configurations, :auto_reconsider_period, :bigint
end
end
Generated
+3 -5
View File
@@ -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_09_24_123001) do
ActiveRecord::Schema[8.1].define(version: 2025_09_24_124737) do
create_table "accesses", force: :cascade do |t|
t.datetime "accessed_at"
t.integer "collection_id", null: false
@@ -255,14 +255,12 @@ ActiveRecord::Schema[8.1].define(version: 2025_09_24_123001) do
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.bigint "auto_postpone_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", "auto_postpone_period"], name: "idx_on_container_type_container_id_auto_postpone_pe_47f82c5b73"
t.index ["container_type", "container_id"], name: "index_entropy_configurations_on_container", unique: true
end
+4 -32
View File
@@ -772,17 +772,7 @@ columns:
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
name: auto_postpone_period
cast_type: *11
sql_type_metadata: *12
'null': false
@@ -2377,30 +2367,12 @@ indexes:
entropy_configurations:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: entropy_configurations
name: idx_on_container_type_container_id_auto_close_perio_74dc880875
name: idx_on_container_type_container_id_auto_postpone_pe_47f82c5b73
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
- auto_postpone_period
lengths: {}
orders: {}
opclasses: {}
@@ -3276,4 +3248,4 @@ indexes:
comment:
valid: true
workflows: []
version: 20250924123001
version: 20250924124737
@@ -6,10 +6,9 @@ class Account::EntropyConfigurationsControllerTest < ActionDispatch::Integration
end
test "update" do
put account_entropy_configuration_path, params: { entropy_configuration: { auto_close_period: 1.day, auto_reconsider_period: 2.days } }
put account_entropy_configuration_path, params: { entropy_configuration: { auto_postpone_period: 1.day } }
assert_equal 1.day, entropy_configurations("37s_account").auto_close_period
assert_equal 2.days, entropy_configurations("37s_account").auto_reconsider_period
assert_equal 1.day, entropy_configurations("37s_account").auto_postpone_period
assert_redirected_to account_settings_path
end
@@ -1,39 +0,0 @@
require "test_helper"
class Cards::EngagementsControllerTest < ActionDispatch::IntegrationTest
setup do
sign_in_as :kevin
end
test "create" do
card = cards(:text)
assert_changes -> { card.reload.doing? }, from: false, to: true do
post card_engagement_path(card), params: { engagement: "doing" }
assert_card_container_rerendered(card)
end
end
test "create on_deck" do
card = cards(:text)
assert_changes -> { card.reload.on_deck? }, from: false, to: true do
post card_engagement_path(card), params: { engagement: "on_deck" }
assert_card_container_rerendered(card)
end
end
test "destroy" do
card = cards(:logo)
assert_changes -> { card.reload.doing? }, from: true, to: false do
delete card_engagement_path(card)
assert_card_container_rerendered(card)
end
end
private
def assert_card_container_rerendered(card)
assert_turbo_stream action: :replace, target: dom_id(card, :card_container)
end
end
@@ -7,10 +7,9 @@ class Collections::EntropyConfigurationsControllerTest < ActionDispatch::Integra
end
test "update" do
put collection_entropy_configuration_path(@collection), params: { collection: { auto_close_period: 1.day, auto_reconsider_period: 2.days } }
put collection_entropy_configuration_path(@collection), params: { collection: { auto_postpone_period: 1.day } }
assert_equal 1.day, @collection.entropy_configuration.reload.auto_close_period
assert_equal 2.days, @collection.entropy_configuration.reload.auto_reconsider_period
assert_equal 1.day, @collection.entropy_configuration.reload.auto_postpone_period
assert_turbo_stream action: :replace, target: dom_id(@collection, :entropy_configuration)
end
@@ -31,8 +31,7 @@ class CollectionsControllerTest < ActionDispatch::IntegrationTest
collection: {
name: "Writebook bugs",
all_access: false,
auto_close_period: 1.day,
auto_reconsider_period: 2.days
auto_postpone_period: 1.day
},
user_ids: users(:kevin, :jz).pluck(:id)
}
@@ -40,8 +39,7 @@ class CollectionsControllerTest < ActionDispatch::IntegrationTest
assert_redirected_to edit_collection_path(collections(:writebook))
assert_equal "Writebook bugs", collections(:writebook).reload.name
assert_equal users(:kevin, :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_equal 1.day, entropy_configurations(:writebook_collection).auto_postpone_period
assert_not collections(:writebook).all_access?
end
@@ -7,38 +7,4 @@ class Public::Collections::CardPreviewsControllerTest < ActionDispatch::Integrat
collections(:writebook).publish
end
test "render considering cards" do
assert cards(:text).considering?
assert_not cards(:logo).considering?
get public_collection_card_previews_path(collections(:writebook).publication.key, target: "considering-cards", format: :turbo_stream)
assert_select ".card", text: /#{cards(:text).title}/
assert_select ".card", text: cards(:logo).title, count: 0
end
test "render doing cards" do
assert cards(:logo).doing?
assert_not cards(:text).doing?
get public_collection_card_previews_path(collections(:writebook).publication.key, target: "doing-cards", format: :turbo_stream)
assert_select ".card", text: /#{cards(:logo).title}/
assert_select ".card", text: cards(:text).title, count: 0
end
test "render closed cards" do
assert cards(:shipping).closed?
assert_not cards(:text).doing?
get public_collection_card_previews_path(collections(:writebook).publication.key, target: "closed-cards", format: :turbo_stream)
assert_select ".card", text: /#{cards(:shipping).title}/
assert_select ".card", text: cards(:text).title, count: 0
end
test "bad response for unknown target" do
get public_collection_card_previews_path(collections(:writebook).publication.key, format: :turbo_stream)
assert_response :bad_request
end
end
+3 -6
View File
@@ -1,14 +1,11 @@
37s_account:
container: 37s (Account)
auto_reconsider_period: <%= 30.days.to_i %>
auto_close_period: <%= 30.days.to_i %>
auto_postpone_period: <%= 30.days.to_i %>
writebook_collection:
container: writebook (Collection)
auto_reconsider_period: <%= 90.days.to_i %>
auto_close_period: <%= 90.days.to_i %>
auto_postpone_period: <%= 90.days.to_i %>
private_collection:
container: private (Collection)
auto_reconsider_period: <%= 30.days.to_i %>
auto_close_period: <%= 30.days.to_i %>
auto_postpone_period: <%= 30.days.to_i %>
+4 -8
View File
@@ -1,16 +1,12 @@
require "test_helper"
class Card::ColoredTest < ActiveSupport::TestCase
test "use default color no stage" do
test "use default color if no column" do
cards(:logo).update! column: nil
assert_equal Card::DEFAULT_COLOR, cards(:logo).color
end
test "use default color not in doing stage" do
assert_equal Card::DEFAULT_COLOR, cards(:text).color
end
test "infer color from stage when in doing stage" do
cards(:text).engage
assert_equal cards(:text).stage.color, cards(:text).color
test "infer color from column" do
assert_equal cards(:layout).column.color, cards(:layout).color
end
end
+24 -93
View File
@@ -5,125 +5,56 @@ class Card::EntropicTest < ActiveSupport::TestCase
Current.session = sessions(:david)
end
test "auto_close_at uses the period defined in the account by default" do
test "auto_postpone_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
entropy_configurations("37s_account").reload.update! auto_postpone_period: 456.days
cards(:layout).update! last_active_at: 2.day.ago
assert_equal (456 - 2).days.from_now, cards(:layout).entropy.auto_clean_at
end
test "auto_close_at infers the period from the collection when present" do
test "auto_postpone_at infers the period from the collection when present" do
freeze_time
entropy_configurations(:writebook_collection).update! auto_close_period: 123.days
entropy_configurations(:writebook_collection).update! auto_postpone_period: 123.days
cards(:layout).update! last_active_at: 2.day.ago
assert_equal (123 - 2).days.from_now, cards(:layout).entropy.auto_clean_at
end
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).entropy.auto_clean_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).entropy.auto_clean_at
end
test "auto close all due using the default account entropy configuration" do
cards(:logo, :shipping).each(&:reconsider)
test "auto postpone all due using the default account entropy configuration" do
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)
cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations("37s_account").auto_postpone_period)
cards(:shipping).update!(last_active_at: 1.day.from_now - entropy_configurations("37s_account").auto_postpone_period)
assert_difference -> { Card.closed.count }, +1 do
Card.auto_close_all_due
assert_difference -> { Card.not_now.count }, +1 do
Card.auto_postpone_all_due
end
assert cards(:logo).reload.closed?
assert_not cards(:shipping).reload.closed?
assert cards(:logo).reload.postponed?
assert_not cards(:shipping).reload.postponed?
end
test "auto close all due using entropy configuration defined at the collection level" do
cards(:logo, :shipping).each(&:reconsider)
test "auto postpone all due using entropy configuration defined at the collection level" do
cards(:logo).update!(last_active_at: 1.day.ago - entropy_configurations(:writebook_collection).auto_postpone_period)
cards(:shipping).update!(last_active_at: 1.day.from_now - entropy_configurations(:writebook_collection).auto_postpone_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
assert_difference -> { Card.not_now.count }, +1 do
Card.auto_postpone_all_due
end
assert cards(:logo).reload.closed?
assert_not cards(:shipping).reload.closed?
assert cards(:logo).reload.postponed?
assert_not cards(:shipping).reload.postponed?
end
test "closing soon scope" do
cards(:logo, :shipping).each(&:published!).each(&:reconsider)
test "postponing soon scope" do
cards(:logo, :shipping).each(&:published!)
cards(:logo).update!(last_active_at: entropy_configurations(:writebook_collection).auto_close_period.seconds.ago + 2.days)
cards(:shipping).update!(last_active_at: entropy_configurations(:writebook_collection).auto_close_period.seconds.ago - 2.days)
cards(:logo).update!(last_active_at: entropy_configurations(:writebook_collection).auto_postpone_period.seconds.ago + 2.days)
cards(:shipping).update!(last_active_at: entropy_configurations(:writebook_collection).auto_postpone_period.seconds.ago - 2.days)
assert_includes Card.closing_soon, cards(:logo)
assert_not_includes Card.closing_soon, cards(:shipping)
end
test "auto consider all stagnated using the default account entropy configuration" do
travel_to Time.current
cards(:logo, :shipping).each(&:engage)
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.considering.count }, +1 do
Card.auto_reconsider_all_stagnated
end
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 includes cards in doing and on_deck" do
travel_to Time.current
cards(:logo, :shipping).each(&:engage)
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
end
assert cards(:shipping).reload.doing?
assert cards(:logo).reload.considering?
assert_equal Time.current, cards(:logo).last_active_at
end
test "falling back scope" do
travel_to Time.current
cards(:logo, :shipping).each(&:engage)
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_includes Card.falling_back_soon, cards(:shipping)
assert_not_includes Card.falling_back_soon, cards(:logo)
assert_includes Card.postponing_soon, cards(:logo)
assert_not_includes Card.postponing_soon, cards(:shipping)
end
end
+3 -6
View File
@@ -26,18 +26,15 @@ class Card::StallableTest < ActiveSupport::TestCase
assert_includes Card.stalled, cards(:logo)
end
test "a card with an old activity spike is not stalled after falling back to considering" do
test "a card with an old activity spike is not stalled after being postponed" do
card = cards(:logo)
card.engage
card.update!(last_active_at: 1.day.ago - card.collection.entropy_configuration.auto_reconsider_period)
card.update!(last_active_at: 1.day.ago - card.collection.entropy_configuration.auto_postpone_period)
card.create_activity_spike!(updated_at: 3.months.ago)
assert card.stalled?
assert_includes Card.stalled, card
assert_difference -> { Card.considering.count }, +1 do
Card.auto_reconsider_all_stagnated
end
Card.auto_postpone_all_due
assert_not card.reload.stalled?
end