WIP for stalled cards

This commit is contained in:
Jorge Manrubia
2025-06-04 14:13:12 +02:00
parent 5dc33c304c
commit b790da9617
9 changed files with 133 additions and 3 deletions
@@ -0,0 +1,5 @@
class Card::ActivitySpike::DetectionJob < ApplicationJob
def perform(card)
card.detect_activity_spikes
end
end
+1 -1
View File
@@ -1,7 +1,7 @@
class Card < ApplicationRecord
include Assignable, Colored, Engageable, Entropic, Eventable,
Golden, Mentions, Pinnable, Closeable, Readable, Searchable,
Staged, Statuses, Taggable, Watchable
Staged, Stallable, Statuses, Taggable, Watchable
belongs_to :collection, touch: true
belongs_to :creator, class_name: "User", default: -> { Current.user }
+3
View File
@@ -0,0 +1,3 @@
class Card::ActivitySpike < ApplicationRecord
belongs_to :card, touch: true
end
@@ -0,0 +1,51 @@
class Card::ActivitySpike::Detector
attr_reader :card
def initialize(card)
@card = card
end
def detect
if has_activity_spike?
activity_spike = card.activity_spike || card.build_activity_spike
activity_spike.touch
true
else
false
end
end
private
def has_activity_spike?
card.entropic? && (multiple_people_commented? || card_was_just_assigned?)
end
def multiple_people_commented?
card.comments
.where("created_at >= ?", recent_period.ago)
.group(:card_id)
.having("COUNT(*) >= ?", minimum_comments)
.having("COUNT(DISTINCT creator_id) >= ?", minimum_participants)
.exists?
end
def recent_period
card.entropy.auto_clean_period * 0.25
end
def minimum_participants
3
end
def minimum_comments
2
end
def card_was_just_assigned?
last_event&.action&.card_assigned? && card.assigned? && last_event.created_at > 1.minute.ago
end
def last_event
card.events.last
end
end
+4
View File
@@ -18,6 +18,10 @@ module Card::Assignable
assignments.exists? assignee: user
end
def assigned?
assignments.any?
end
private
def assign(user)
assignments.create! assignee: user, assigner: Current.user
+27
View File
@@ -0,0 +1,27 @@
module Card::Stallable
extend ActiveSupport::Concern
STALLED_AFTER_LAST_SPIKE_PERIOD = 14.days
included do
has_one :activity_spike, class_name: "Card::ActivitySpike", dependent: :destroy
scope :with_activity_spikes, -> { joins(:activity_spike) }
scope :stalled, -> { with_activity_spikes.where(updated_at: ..STALLED_AFTER_LAST_SPIKE_PERIOD.ago) }
after_update_commit :detect_activity_spikes_later, if: :saved_change_to_last_active_at?
end
def stalled?
activity_spike && activity_spike.updated_at < STALLED_AFTER_LAST_SPIKE_PERIOD.ago
end
def detect_activity_spikes
Card::ActivitySpike::Detector.new(self).detect
end
private
def detect_activity_spikes_later
Card::ActivitySpike::DetectionJob.perform_later(self)
end
end
@@ -0,0 +1,8 @@
class CreateActivitySpikes < ActiveRecord::Migration[8.1]
def change
create_table :card_activity_spikes do |t|
t.references :card, null: false, foreign_key: true, index: true
t.timestamps
end
end
end
Generated
+9 -1
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_06_04_080022) do
ActiveRecord::Schema[8.1].define(version: 2025_06_04_120033) do
create_table "accesses", force: :cascade do |t|
t.integer "collection_id", null: false
t.datetime "created_at", null: false
@@ -93,6 +93,13 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_04_080022) do
t.index ["card_id"], name: "index_assignments_on_card_id"
end
create_table "card_activity_spikes", force: :cascade do |t|
t.integer "card_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["card_id"], name: "index_card_activity_spikes_on_card_id"
end
create_table "card_engagements", force: :cascade do |t|
t.integer "card_id"
t.datetime "created_at", null: false
@@ -349,6 +356,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_04_080022) do
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "card_activity_spikes", "cards"
add_foreign_key "card_goldnesses", "cards"
add_foreign_key "cards", "workflow_stages", column: "stage_id"
add_foreign_key "closures", "cards"
+25 -1
View File
@@ -344,6 +344,11 @@ columns:
- *5
- *6
- *9
card_activity_spikes:
- *22
- *5
- *6
- *9
card_engagements:
- !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column
auto_increment:
@@ -1021,6 +1026,7 @@ primary_keys:
assignees_filters:
assigners_filters:
assignments: id
card_activity_spikes: id
card_engagements: id
card_goldnesses: id
cards: id
@@ -1060,6 +1066,7 @@ data_sources:
assignees_filters: true
assigners_filters: true
assignments: true
card_activity_spikes: true
card_engagements: true
card_goldnesses: true
cards: true
@@ -1347,6 +1354,23 @@ indexes:
nulls_not_distinct:
comment:
valid: true
card_activity_spikes:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: card_activity_spikes
name: index_card_activity_spikes_on_card_id
unique: false
columns:
- card_id
lengths: {}
orders: {}
opclasses: {}
where:
type:
using:
include:
nulls_not_distinct:
comment:
valid: true
card_engagements:
- !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition
table: card_engagements
@@ -2211,4 +2235,4 @@ indexes:
comment:
valid: true
workflows: []
version: 20250604080022
version: 20250604120033