WIP for stalled cards
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
class Card::ActivitySpike::DetectionJob < ApplicationJob
|
||||
def perform(card)
|
||||
card.detect_activity_spikes
|
||||
end
|
||||
end
|
||||
+1
-1
@@ -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 }
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user