From cd9955d9387557e794bd5767b6e240a47eb0f02e Mon Sep 17 00:00:00 2001 From: Jorge Manrubia Date: Thu, 3 Jul 2025 14:28:33 +0200 Subject: [PATCH] Add "close by" filter --- app/models/card/closeable.rb | 1 + app/models/filter.rb | 8 ++- app/models/filter/params.rb | 2 + app/models/filter/resources.rb | 1 + ...00000_create_closers_filters_join_table.rb | 11 +++++ db/schema.rb | 9 +++- db/schema_cache.yml | 49 ++++++++++++++++++- test/models/filter_test.rb | 15 ++++++ 8 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20250626000000_create_closers_filters_join_table.rb diff --git a/app/models/card/closeable.rb b/app/models/card/closeable.rb index a8dfff9f6..f9b045239 100644 --- a/app/models/card/closeable.rb +++ b/app/models/card/closeable.rb @@ -9,6 +9,7 @@ module Card::Closeable scope :recently_closed_first, -> { closed.order("closures.created_at": :desc) } scope :closed_at_window, ->(window) { closed.where("closures.created_at": window) } + scope :closed_by, ->(users) { closed.where("closures.user_id": Array(users)) } end def closed? diff --git a/app/models/filter.rb b/app/models/filter.rb index 49c01265e..4784751ff 100644 --- a/app/models/filter.rb +++ b/app/models/filter.rb @@ -20,7 +20,7 @@ class Filter < ApplicationRecord @cards ||= begin result = creator.accessible_cards.indexed_by(indexed_by) result = result.where(id: card_ids) if card_ids.present? - result = result.open unless indexed_by.closed? || closure_window || 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? @@ -30,6 +30,7 @@ class Filter < ApplicationRecord 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 + result = result.closed_by(closers) if closers.present? result = terms.reduce(result) do |result, term| result.mentioning(term) end @@ -57,4 +58,9 @@ class Filter < ApplicationRecord def cache_key ActiveSupport::Cache.expand_cache_key collections.cache_key_with_version, super end + + private + def include_closed_cards? + indexed_by.closed? || closure_window || closers.present? || card_ids.present? + end end diff --git a/app/models/filter/params.rb b/app/models/filter/params.rb index e8cc9c220..21b0886bd 100644 --- a/app/models/filter/params.rb +++ b/app/models/filter/params.rb @@ -10,6 +10,7 @@ module Filter::Params card_ids: [], assignee_ids: [], creator_ids: [], + closer_ids: [], collection_ids: [], stage_ids: [], tag_ids: [], @@ -50,6 +51,7 @@ module Filter::Params params[:card_ids] = card_ids params[:assignee_ids] = assignees.ids params[:creator_ids] = creators.ids + params[:closer_ids] = closers.ids end.compact_blank.reject(&method(:default_value?)) end diff --git a/app/models/filter/resources.rb b/app/models/filter/resources.rb index 2a695e43d..e4d84ba99 100644 --- a/app/models/filter/resources.rb +++ b/app/models/filter/resources.rb @@ -7,6 +7,7 @@ module Filter::Resources has_and_belongs_to_many :stages, class_name: "Workflow::Stage", join_table: "filters_stages" has_and_belongs_to_many :assignees, class_name: "User", join_table: "assignees_filters", association_foreign_key: "assignee_id" has_and_belongs_to_many :creators, class_name: "User", join_table: "creators_filters", association_foreign_key: "creator_id" + has_and_belongs_to_many :closers, class_name: "User", join_table: "closers_filters", association_foreign_key: "closer_id" end def resource_removed(resource) diff --git a/db/migrate/20250626000000_create_closers_filters_join_table.rb b/db/migrate/20250626000000_create_closers_filters_join_table.rb new file mode 100644 index 000000000..4c6a53392 --- /dev/null +++ b/db/migrate/20250626000000_create_closers_filters_join_table.rb @@ -0,0 +1,11 @@ +class CreateClosersFiltersJoinTable < ActiveRecord::Migration[8.1] + def change + create_table :closers_filters, id: false do |t| + t.integer :filter_id, null: false + t.integer :closer_id, null: false + end + + add_index :closers_filters, :filter_id + add_index :closers_filters, :closer_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 3c4b155e6..01911c54c 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_25_111932) do +ActiveRecord::Schema[8.1].define(version: 2025_06_26_000000) do create_table "accesses", force: :cascade do |t| t.datetime "accessed_at" t.integer "collection_id", null: false @@ -133,6 +133,13 @@ ActiveRecord::Schema[8.1].define(version: 2025_06_25_111932) do t.index ["stage_id"], name: "index_cards_on_stage_id" end + create_table "closers_filters", id: false, force: :cascade do |t| + t.integer "closer_id", null: false + t.integer "filter_id", null: false + t.index ["closer_id"], name: "index_closers_filters_on_closer_id" + t.index ["filter_id"], name: "index_closers_filters_on_filter_id" + end + create_table "closure_reasons", force: :cascade do |t| t.datetime "created_at", null: false t.string "label" diff --git a/db/schema_cache.yml b/db/schema_cache.yml index 8aab5a0d5..c68ffad4a 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -462,6 +462,18 @@ columns: collation: comment: - *9 + closers_filters: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: closer_id + cast_type: *3 + sql_type_metadata: *4 + 'null': false + default: + default_function: + collation: + comment: + - *19 closure_reasons: - *5 - *6 @@ -1067,6 +1079,7 @@ primary_keys: card_engagements: id card_goldnesses: id cards: id + closers_filters: closure_reasons: id closures: id collection_publications: id @@ -1109,6 +1122,7 @@ data_sources: card_engagements: true card_goldnesses: true cards: true + closers_filters: true closure_reasons: true closures: true collection_publications: true @@ -1528,6 +1542,39 @@ indexes: nulls_not_distinct: comment: valid: true + closers_filters: + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: closers_filters + name: index_closers_filters_on_closer_id + unique: false + columns: + - closer_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: closers_filters + name: index_closers_filters_on_filter_id + unique: false + columns: + - filter_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true closure_reasons: [] closures: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition @@ -2391,4 +2438,4 @@ indexes: comment: valid: true workflows: [] -version: 20250625111932 +version: 20250626000000 diff --git a/test/models/filter_test.rb b/test/models/filter_test.rb index ae324d3b0..858f8c583 100644 --- a/test/models/filter_test.rb +++ b/test/models/filter_test.rb @@ -153,4 +153,19 @@ class FilterTest < ActiveSupport::TestCase cards(:shipping).closure.update_columns created_at: Time.current assert_includes filter.cards, cards(:shipping) end + + test "completed by" do + cards(:shipping).closure.update_columns user_id: users(:david).id + + filter = users(:david).filters.new closer_ids: [ users(:david).id ] + assert_includes filter.cards, cards(:shipping) + + filter = users(:david).filters.new closer_ids: [ users(:jz).id ] + assert_not_includes filter.cards, cards(:shipping) + + cards(:shipping).closure.update_columns user_id: users(:jz).id + + filter = users(:david).filters.new closer_ids: [ users(:jz).id ] + assert_includes filter.cards, cards(:shipping) + end end