Redo TablesArrangements#show to display arrangement ID and discomfort breakdown
All checks were successful
Check usage of free licenses / check-licenses (pull_request) Successful in 32s
Add copyright notice / copyright_notice (pull_request) Successful in 1m4s
Run unit tests / unit_tests (pull_request) Successful in 2m33s

This commit is contained in:
Manuel Bustillo 2024-12-16 18:52:34 +01:00
parent bab5cd3161
commit 3bfe889747
5 changed files with 96 additions and 47 deletions

View File

@ -6,18 +6,33 @@ class TablesArrangementsController < ApplicationController
end end
def show def show
Seat.joins(guest: :group) Guest.joins(:seats, :group)
.where(tables_arrangement_id: params[:id]) .where(seats: { tables_arrangement_id: params[:id] })
.order('guests.group_id') .select('guests.*', 'groups.color', 'seats.table_number')
.pluck( .group_by(&:table_number)
:table_number, .map { |number, guests| format(number:, guests:) }
'guests.name', .then { |result| render json: { id: params[:id], tables: result } }
'guests.id', end
'groups.color'
) private
.group_by(&:first)
.transform_values { |table| table.map { |(_, name, id, color)| { id:, name:, color: } } } def format(number:, guests:)
.map { |number, guests| { number:, guests: } } {
.then { |result| render json: result } number: number,
discomfort: discomfort(guests: guests),
guests: guests.as_json(only: %i[id name color])
}
end
def discomfort(guests:)
table = Tables::Table.new(guests)
table.min_per_table = TableSimulatorJob::MIN_PER_TABLE
table.max_per_table = TableSimulatorJob::MAX_PER_TABLE
calculator = Tables::DiscomfortCalculator.new(table:)
{
discomfort: calculator.calculate,
breakdown: calculator.breakdown
}
end end
end end

View File

@ -3,6 +3,9 @@
class TableSimulatorJob < ApplicationJob class TableSimulatorJob < ApplicationJob
queue_as :default queue_as :default
MIN_PER_TABLE = 8
MAX_PER_TABLE = 10
def perform(wedding_id) def perform(wedding_id)
ActsAsTenant.with_tenant(Wedding.find(wedding_id)) do ActsAsTenant.with_tenant(Wedding.find(wedding_id)) do
engine = VNS::Engine.new engine = VNS::Engine.new
@ -10,7 +13,7 @@ class TableSimulatorJob < ApplicationJob
engine.add_perturbation(Tables::Swap) engine.add_perturbation(Tables::Swap)
engine.add_perturbation(Tables::Shift) engine.add_perturbation(Tables::Shift)
initial_solution = Tables::Distribution.new(min_per_table: 8, max_per_table: 10) initial_solution = Tables::Distribution.new(min_per_table: MIN_PER_TABLE, max_per_table: MAX_PER_TABLE)
initial_solution.random_distribution(Guest.potential.shuffle) initial_solution.random_distribution(Guest.potential.shuffle)
engine.initial_solution = initial_solution engine.initial_solution = initial_solution

View File

@ -8,7 +8,11 @@ module Tables
end end
def calculate def calculate
table_size_penalty + 10 * (cohesion_penalty * 1.0 / table.size) breakdown.values.sum
end
def breakdown
@breakdown ||= { table_size_penalty:, cohesion_penalty: }
end end
private private
@ -28,6 +32,10 @@ module Tables
end end
end end
def cohesion_penalty
10 * (cohesion_discomfort * 1.0 / table.size)
end
# #
# Calculates the discomfort of the table based on the cohesion of the guests. The total discomfort # Calculates the discomfort of the table based on the cohesion of the guests. The total discomfort
# is calculated as the sum of the discomfort of each pair of guests. The discomfort of a pair of # is calculated as the sum of the discomfort of each pair of guests. The discomfort of a pair of
@ -35,7 +43,7 @@ module Tables
# #
# @return [Number] Total discomfort of the table. # @return [Number] Total discomfort of the table.
# #
def cohesion_penalty def cohesion_discomfort
table.map(&:group_id).tally.to_a.combination(2).sum do |(a, count_a), (b, count_b)| table.map(&:group_id).tally.to_a.combination(2).sum do |(a, count_a), (b, count_b)|
distance = AffinityGroupsHierarchy.instance.distance(a, b) distance = AffinityGroupsHierarchy.instance.distance(a, b)

View File

@ -3,7 +3,6 @@
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'tables_arrangements', type: :request do RSpec.describe 'tables_arrangements', type: :request do
path '/{slug}/tables_arrangements' do path '/{slug}/tables_arrangements' do
get('list tables arrangements') do get('list tables arrangements') do
tags 'Tables Arrangements' tags 'Tables Arrangements'
@ -33,21 +32,44 @@ RSpec.describe 'tables_arrangements', type: :request do
parameter Swagger::Schema::SLUG parameter Swagger::Schema::SLUG
parameter Swagger::Schema::ID parameter Swagger::Schema::ID
response(200, 'successful') do response(200, 'successful') do
schema type: :array, schema type: :object,
items: { required: %i[id tables],
type: :object, properties: {
required: %i[number guests], id: { type: :string, format: :uuid },
properties: { tables: {
number: { type: :integer },
guests: { type: :array,
type: :array, items: {
items: { type: :object,
type: :object, required: %i[number guests discomfort],
required: %i[id name color], properties: {
properties: { number: { type: :integer },
id: { type: :string, format: :uuid }, guests: {
name: { type: :string }, type: :array,
color: { type: :string } items: {
type: :object,
required: %i[id name color],
properties: {
id: { type: :string, format: :uuid },
name: { type: :string },
color: { type: :string }
}
}
},
discomfort: {
type: :object,
required: %i[discomfort breakdown],
properties: {
discomfort: { type: :number },
breakdown: {
type: :object,
required: %i[table_size_penalty cohesion_penalty],
properties: {
table_size_penalty: { type: :number },
cohesion_penalty: { type: :number }
}
}
}
} }
} }
} }

View File

@ -13,13 +13,14 @@ module Tables
describe '#calculate' do describe '#calculate' do
before do before do
allow(calculator).to receive(:table_size_penalty).and_return(2) allow(calculator).to receive(:table_size_penalty).and_return(2)
allow(calculator).to receive(:cohesion_penalty).and_return(3) allow(calculator).to receive(:cohesion_discomfort).and_return(3)
end end
let(:table) { Table.new(create_list(:guest, 6)) } let(:table) { Table.new(create_list(:guest, 6)) }
it 'returns the sum of the table size penalty and the average cohesion penalty' do it 'returns the sum of the table size penalty and the average cohesion penalty', :aggregate_failures do
expect(calculator.calculate).to eq(2 + 10 * 3 / 6.0) expect(calculator.calculate).to eq(7)
expect(calculator.breakdown).to eq(table_size_penalty: 2, cohesion_penalty: 5)
end end
end end
@ -71,7 +72,7 @@ module Tables
end end
end end
describe '#cohesion_penalty' do describe '#cohesion_discomfort' do
before do before do
# Overridden in each test except trivial cases # Overridden in each test except trivial cases
allow(AffinityGroupsHierarchy.instance).to receive(:distance).and_call_original allow(AffinityGroupsHierarchy.instance).to receive(:distance).and_call_original
@ -91,7 +92,7 @@ module Tables
context 'when they belong to the same group' do context 'when they belong to the same group' do
let(:table) { create_list(:guest, 2, group: family) } let(:table) { create_list(:guest, 2, group: family) }
it { expect(calculator.send(:cohesion_penalty)).to eq(0) } it { expect(calculator.send(:cohesion_discomfort)).to eq(0) }
end end
context 'when they belong to completely unrelated groups' do context 'when they belong to completely unrelated groups' do
@ -101,7 +102,7 @@ module Tables
create(:guest, group: friends) create(:guest, group: friends)
] ]
end end
it { expect(calculator.send(:cohesion_penalty)).to eq(1) } it { expect(calculator.send(:cohesion_discomfort)).to eq(1) }
end end
context 'when they belong to groups at a distance of 1' do context 'when they belong to groups at a distance of 1' do
@ -112,7 +113,7 @@ module Tables
] ]
end end
it { expect(calculator.send(:cohesion_penalty)).to eq(0.5) } it { expect(calculator.send(:cohesion_discomfort)).to eq(0.5) }
end end
context 'when they belong to groups at a distance of 2' do context 'when they belong to groups at a distance of 2' do
@ -123,7 +124,7 @@ module Tables
] ]
end end
it { expect(calculator.send(:cohesion_penalty)).to eq(Rational(2, 3)) } it { expect(calculator.send(:cohesion_discomfort)).to eq(Rational(2, 3)) }
end end
context 'when they belong to groups at a distance of 3' do context 'when they belong to groups at a distance of 3' do
@ -134,7 +135,7 @@ module Tables
] ]
end end
it { expect(calculator.send(:cohesion_penalty)).to eq(Rational(3, 4)) } it { expect(calculator.send(:cohesion_discomfort)).to eq(Rational(3, 4)) }
end end
end end
@ -148,7 +149,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_penalty)).to eq(1 + Rational(1, 2) + Rational(2, 3)) expect(calculator.send(:cohesion_discomfort)).to eq(1 + Rational(1, 2) + Rational(2, 3))
end end
end end
@ -163,7 +164,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_penalty)) expect(calculator.send(:cohesion_discomfort))
.to eq(1 + Rational(1, 2) + Rational(2, 3) + Rational(3, 4) + Rational(4, 5) + Rational(5, 6)) .to eq(1 + Rational(1, 2) + Rational(2, 3) + Rational(3, 4) + Rational(4, 5) + Rational(5, 6))
end end
end end
@ -177,7 +178,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_penalty)).to eq(4) expect(calculator.send(:cohesion_discomfort)).to eq(4)
end end
end end
@ -190,7 +191,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_penalty)).to eq(8) expect(calculator.send(:cohesion_discomfort)).to eq(8)
end end
end end
@ -204,7 +205,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_penalty)).to eq(4 * 1 + 4 * Rational(1, 2) + 4 * Rational(2, 3)) expect(calculator.send(:cohesion_discomfort)).to eq(4 * 1 + 4 * Rational(1, 2) + 4 * Rational(2, 3))
end end
end end
@ -218,7 +219,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_penalty)).to eq(6 * 1 + 2 * Rational(1, 2) + 3 * Rational(2, 3)) expect(calculator.send(:cohesion_discomfort)).to eq(6 * 1 + 2 * Rational(1, 2) + 3 * Rational(2, 3))
end end
end end
end end