Merge pull request 'Introduce endpoint to retrieve a summary of groups and invite attendance' (#122) from groups-index-stats into main
Reviewed-on: #122
This commit is contained in:
commit
d75b117c60
58
.annotaterb.yml
Normal file
58
.annotaterb.yml
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
:position: before
|
||||
:position_in_additional_file_patterns: before
|
||||
:position_in_class: before
|
||||
:position_in_factory: before
|
||||
:position_in_fixture: before
|
||||
:position_in_routes: before
|
||||
:position_in_serializer: before
|
||||
:position_in_test: before
|
||||
:classified_sort: true
|
||||
:exclude_controllers: true
|
||||
:exclude_factories: true
|
||||
:exclude_fixtures: false
|
||||
:exclude_helpers: true
|
||||
:exclude_scaffolds: true
|
||||
:exclude_serializers: false
|
||||
:exclude_sti_subclasses: false
|
||||
:exclude_tests: true
|
||||
:force: false
|
||||
:format_markdown: false
|
||||
:format_rdoc: false
|
||||
:format_yard: false
|
||||
:frozen: false
|
||||
:ignore_model_sub_dir: false
|
||||
:ignore_unknown_models: false
|
||||
:include_version: false
|
||||
:show_check_constraints: false
|
||||
:show_complete_foreign_keys: false
|
||||
:show_foreign_keys: true
|
||||
:show_indexes: true
|
||||
:simple_indexes: false
|
||||
:sort: false
|
||||
:timestamp: false
|
||||
:trace: false
|
||||
:with_comment: true
|
||||
:with_column_comments: true
|
||||
:with_table_comments: true
|
||||
:active_admin: false
|
||||
:command:
|
||||
:debug: false
|
||||
:hide_default_column_types: ''
|
||||
:hide_limit_column_types: ''
|
||||
:ignore_columns:
|
||||
:ignore_routes:
|
||||
:models: true
|
||||
:routes: false
|
||||
:skip_on_db_migrate: false
|
||||
:target_action: :do_annotations
|
||||
:wrapper:
|
||||
:wrapper_close:
|
||||
:wrapper_open:
|
||||
:classes_default_to_s: []
|
||||
:additional_file_patterns: []
|
||||
:model_dir:
|
||||
- app/models
|
||||
:require: []
|
||||
:root_dir:
|
||||
- ''
|
1
Gemfile
1
Gemfile
@ -22,6 +22,7 @@ gem 'react-rails'
|
||||
gem 'rubytree'
|
||||
|
||||
group :development, :test do
|
||||
gem 'annotaterb'
|
||||
gem 'debug', platforms: %i[mri windows]
|
||||
gem 'factory_bot_rails'
|
||||
gem 'license_finder'
|
||||
|
@ -72,6 +72,7 @@ GEM
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
uri (>= 0.13.1)
|
||||
annotaterb (4.13.0)
|
||||
ast (2.4.2)
|
||||
babel-source (5.8.35)
|
||||
babel-transpiler (0.7.0)
|
||||
@ -341,6 +342,7 @@ PLATFORMS
|
||||
x86_64-linux
|
||||
|
||||
DEPENDENCIES
|
||||
annotaterb
|
||||
bootsnap
|
||||
chroma
|
||||
csv
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
class GroupsController < ApplicationController
|
||||
def index
|
||||
roots = Group.where(parent_id: nil)
|
||||
render jsonapi: roots, include: [children: [children: [:children]]]
|
||||
render json: Groups::SummaryQuery.new.call.as_json
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,15 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: expenses
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# amount :decimal(, )
|
||||
# name :string
|
||||
# pricing_type :enum default("fixed"), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class Expense < ApplicationRecord
|
||||
end
|
||||
|
@ -1,5 +1,27 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: groups
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# color :string
|
||||
# icon :string
|
||||
# name :string not null
|
||||
# order :integer default(1), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# parent_id :uuid
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_groups_on_name (name) UNIQUE
|
||||
# index_groups_on_parent_id (parent_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (parent_id => groups.id)
|
||||
#
|
||||
class Group < ApplicationRecord
|
||||
validates :name, uniqueness: true
|
||||
validates :name, :order, presence: true
|
||||
@ -30,6 +52,8 @@ class Group < ApplicationRecord
|
||||
private
|
||||
|
||||
def set_color
|
||||
return if color.present?
|
||||
|
||||
new_color = "##{SecureRandom.hex(3)}".paint
|
||||
new_color = new_color.lighten(30) if new_color.dark?
|
||||
self.color = new_color
|
||||
|
@ -1,5 +1,25 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: guests
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# name :string
|
||||
# phone :string
|
||||
# status :integer default("considered")
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# group_id :uuid not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_guests_on_group_id (group_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (group_id => groups.id)
|
||||
#
|
||||
class Guest < ApplicationRecord
|
||||
belongs_to :group
|
||||
|
||||
|
@ -1,5 +1,26 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: seats
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# table_number :integer
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
# guest_id :uuid not null
|
||||
# tables_arrangement_id :uuid not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_seats_on_guest_id (guest_id)
|
||||
# index_seats_on_tables_arrangement_id (tables_arrangement_id)
|
||||
#
|
||||
# Foreign Keys
|
||||
#
|
||||
# fk_rails_... (guest_id => guests.id)
|
||||
# fk_rails_... (tables_arrangement_id => tables_arrangements.id) ON DELETE => cascade
|
||||
#
|
||||
class Seat < ApplicationRecord
|
||||
belongs_to :guest
|
||||
belongs_to :table_arrangement
|
||||
|
@ -1,5 +1,15 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: tables_arrangements
|
||||
#
|
||||
# id :uuid not null, primary key
|
||||
# discomfort :integer
|
||||
# name :string not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
class TablesArrangement < ApplicationRecord
|
||||
has_many :seats
|
||||
has_many :guests, through: :seats
|
||||
|
31
app/queries/groups/summary_query.rb
Normal file
31
app/queries/groups/summary_query.rb
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
module Groups
|
||||
class SummaryQuery
|
||||
def call
|
||||
ActiveRecord::Base.connection.execute(query).to_a
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def query
|
||||
<<~SQL.squish
|
||||
SELECT#{' '}
|
||||
groups.id,
|
||||
groups.name,
|
||||
groups.icon,
|
||||
groups.parent_id,
|
||||
groups.color,
|
||||
count(*) filter (where status IS NOT NULL) as total,
|
||||
count(*) filter (where status = 0) as considered,
|
||||
count(*) filter (where status = 10) as invited,
|
||||
count(*) filter (where status = 20) as confirmed,
|
||||
count(*) filter (where status = 30) as declined,
|
||||
count(*) filter (where status = 40) as tentative
|
||||
FROM groups
|
||||
LEFT JOIN guests on groups.id = guests.group_id
|
||||
GROUP BY groups.id
|
||||
SQL
|
||||
end
|
||||
end
|
||||
end
|
2
db/schema.rb
generated
2
db/schema.rb
generated
@ -1,5 +1,3 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
# This file is auto-generated from the current state of the database. Instead
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
|
8
lib/tasks/annotate_rb.rake
Normal file
8
lib/tasks/annotate_rb.rake
Normal file
@ -0,0 +1,8 @@
|
||||
# This rake task was added by annotate_rb gem.
|
||||
|
||||
# Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this
|
||||
if Rails.env.development? && ENV["ANNOTATERB_SKIP_ON_DB_TASKS"].nil?
|
||||
require "annotate_rb"
|
||||
|
||||
AnnotateRb::Core.load_rake_tasks
|
||||
end
|
98
spec/queries/groups/summary_query_spec.rb
Normal file
98
spec/queries/groups/summary_query_spec.rb
Normal file
@ -0,0 +1,98 @@
|
||||
# Copyright (C) 2024 Manuel Bustillo
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
module Groups
|
||||
RSpec.describe SummaryQuery do
|
||||
describe '#call' do
|
||||
subject { described_class.new.call }
|
||||
|
||||
context 'when there are no groups' do
|
||||
it { is_expected.to eq([]) }
|
||||
end
|
||||
|
||||
context 'when groups are defined' do
|
||||
let!(:parent) { create(:group, name: 'Friends', icon: 'icon-1', color: '#FF0000') }
|
||||
let!(:child) { create(:group, name: 'Family', icon: 'icon-2', color: '#00FF00', parent:) }
|
||||
|
||||
context 'when there are no guests' do
|
||||
it 'returns the summary of groups' do
|
||||
is_expected.to contain_exactly(
|
||||
{ 'id' => parent.id,
|
||||
'name' => 'Friends',
|
||||
'icon' => 'icon-1',
|
||||
'parent_id' => nil,
|
||||
'color' => '#FF0000',
|
||||
'total' => 0,
|
||||
'considered' => 0,
|
||||
'invited' => 0,
|
||||
'confirmed' => 0,
|
||||
'declined' => 0,
|
||||
'tentative' => 0 },
|
||||
{ 'id' => child.id,
|
||||
'name' => 'Family',
|
||||
'icon' => 'icon-2',
|
||||
'parent_id' => parent.id,
|
||||
'color' => '#00FF00',
|
||||
'total' => 0,
|
||||
'considered' => 0,
|
||||
'invited' => 0,
|
||||
'confirmed' => 0,
|
||||
'declined' => 0,
|
||||
'tentative' => 0 }
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are guests' do
|
||||
before do
|
||||
# Parent group
|
||||
create_list(:guest, 2, group: parent, status: :considered)
|
||||
create_list(:guest, 3, group: parent, status: :invited)
|
||||
create_list(:guest, 4, group: parent, status: :confirmed)
|
||||
create_list(:guest, 5, group: parent, status: :declined)
|
||||
create_list(:guest, 6, group: parent, status: :tentative)
|
||||
|
||||
# Child group
|
||||
create_list(:guest, 7, group: child, status: :considered)
|
||||
create_list(:guest, 8, group: child, status: :invited)
|
||||
create_list(:guest, 9, group: child, status: :confirmed)
|
||||
create_list(:guest, 10, group: child, status: :declined)
|
||||
create_list(:guest, 11, group: child, status: :tentative)
|
||||
end
|
||||
|
||||
it 'returns the summary of groups' do
|
||||
is_expected.to contain_exactly(
|
||||
{
|
||||
'id' => parent.id,
|
||||
'name' => 'Friends',
|
||||
'icon' => 'icon-1',
|
||||
'parent_id' => nil,
|
||||
'color' => '#FF0000',
|
||||
'total' => 20,
|
||||
'considered' => 2,
|
||||
'invited' => 3,
|
||||
'confirmed' => 4,
|
||||
'declined' => 5,
|
||||
'tentative' => 6
|
||||
},
|
||||
{
|
||||
'id' => child.id,
|
||||
'name' => 'Family',
|
||||
'icon' => 'icon-2',
|
||||
'parent_id' => parent.id,
|
||||
'color' => '#00FF00',
|
||||
'total' => 45,
|
||||
'considered' => 7,
|
||||
'invited' => 8,
|
||||
'confirmed' => 9,
|
||||
'declined' => 10,
|
||||
'tentative' => 11
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user