Redo TablesArrangements#show to display arrangement ID and discomfort breakdown
This commit is contained in:
parent
bab5cd3161
commit
3bfe889747
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user