Prepare entropy code for the new auto postpone approach
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(" ")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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?
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
@@ -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 %>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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 %>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user