Compare commits

..

No commits in common. "9c1fe130638ef4a2739784b1f22b8e19ff090583" and "998706da97f0491b5355fdeed3dbdf62b7bb70a4" have entirely different histories.

6 changed files with 49 additions and 98 deletions

View File

@ -327,7 +327,7 @@ GEM
rswag-ui (2.16.0) rswag-ui (2.16.0)
actionpack (>= 5.2, < 8.1) actionpack (>= 5.2, < 8.1)
railties (>= 5.2, < 8.1) railties (>= 5.2, < 8.1)
rubocop (1.69.2) rubocop (1.69.1)
json (~> 2.3) json (~> 2.3)
language_server-protocol (>= 3.17.0) language_server-protocol (>= 3.17.0)
parallel (~> 1.10) parallel (~> 1.10)
@ -343,7 +343,7 @@ GEM
rubytree (2.1.0) rubytree (2.1.0)
json (~> 2.0, > 2.3.1) json (~> 2.0, > 2.3.1)
rubyzip (2.3.2) rubyzip (2.3.2)
securerandom (0.4.1) securerandom (0.4.0)
shoulda-matchers (6.4.0) shoulda-matchers (6.4.0)
activesupport (>= 5.2.0) activesupport (>= 5.2.0)
solid_queue (1.1.0) solid_queue (1.1.0)

View File

@ -6,33 +6,18 @@ class TablesArrangementsController < ApplicationController
end end
def show def show
Guest.joins(:seats, :group) Seat.joins(guest: :group)
.where(seats: { tables_arrangement_id: params[:id] }) .where(tables_arrangement_id: params[:id])
.select('guests.*', 'groups.color', 'seats.table_number') .order('guests.group_id')
.group_by(&:table_number) .pluck(
.map { |number, guests| format(number:, guests:) } :table_number,
.then { |result| render json: { id: params[:id], tables: result } } 'guests.name',
end 'guests.id',
'groups.color'
private )
.group_by(&:first)
def format(number:, guests:) .transform_values { |table| table.map { |(_, name, id, color)| { id:, name:, color: } } }
{ .map { |number, guests| { number:, guests: } }
number: number, .then { |result| render json: result }
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,9 +3,6 @@
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
@ -13,7 +10,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: MIN_PER_TABLE, max_per_table: MAX_PER_TABLE) initial_solution = Tables::Distribution.new(min_per_table: 8, max_per_table: 10)
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,11 +8,7 @@ module Tables
end end
def calculate def calculate
breakdown.values.sum table_size_penalty + 10 * (cohesion_penalty * 1.0 / table.size)
end
def breakdown
@breakdown ||= { table_size_penalty:, cohesion_penalty: }
end end
private private
@ -32,10 +28,6 @@ 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
@ -43,7 +35,7 @@ module Tables
# #
# @return [Number] Total discomfort of the table. # @return [Number] Total discomfort of the table.
# #
def cohesion_discomfort def cohesion_penalty
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,6 +3,7 @@
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'
@ -32,44 +33,21 @@ 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: :object, schema type: :array,
required: %i[id tables], items: {
properties: { type: :object,
id: { type: :string, format: :uuid }, required: %i[number guests],
tables: { properties: {
number: { type: :integer },
type: :array, guests: {
items: { type: :array,
type: :object, items: {
required: %i[number guests discomfort], type: :object,
properties: { required: %i[id name color],
number: { type: :integer }, properties: {
guests: { id: { type: :string, format: :uuid },
type: :array, name: { type: :string },
items: { color: { type: :string }
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,14 +13,13 @@ 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_discomfort).and_return(3) allow(calculator).to receive(:cohesion_penalty).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', :aggregate_failures do it 'returns the sum of the table size penalty and the average cohesion penalty' do
expect(calculator.calculate).to eq(7) expect(calculator.calculate).to eq(2 + 10 * 3 / 6.0)
expect(calculator.breakdown).to eq(table_size_penalty: 2, cohesion_penalty: 5)
end end
end end
@ -72,7 +71,7 @@ module Tables
end end
end end
describe '#cohesion_discomfort' do describe '#cohesion_penalty' 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
@ -92,7 +91,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_discomfort)).to eq(0) } it { expect(calculator.send(:cohesion_penalty)).to eq(0) }
end end
context 'when they belong to completely unrelated groups' do context 'when they belong to completely unrelated groups' do
@ -102,7 +101,7 @@ module Tables
create(:guest, group: friends) create(:guest, group: friends)
] ]
end end
it { expect(calculator.send(:cohesion_discomfort)).to eq(1) } it { expect(calculator.send(:cohesion_penalty)).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
@ -113,7 +112,7 @@ module Tables
] ]
end end
it { expect(calculator.send(:cohesion_discomfort)).to eq(0.5) } it { expect(calculator.send(:cohesion_penalty)).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
@ -124,7 +123,7 @@ module Tables
] ]
end end
it { expect(calculator.send(:cohesion_discomfort)).to eq(Rational(2, 3)) } it { expect(calculator.send(:cohesion_penalty)).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
@ -135,7 +134,7 @@ module Tables
] ]
end end
it { expect(calculator.send(:cohesion_discomfort)).to eq(Rational(3, 4)) } it { expect(calculator.send(:cohesion_penalty)).to eq(Rational(3, 4)) }
end end
end end
@ -149,7 +148,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_discomfort)).to eq(1 + Rational(1, 2) + Rational(2, 3)) expect(calculator.send(:cohesion_penalty)).to eq(1 + Rational(1, 2) + Rational(2, 3))
end end
end end
@ -164,7 +163,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_discomfort)) expect(calculator.send(:cohesion_penalty))
.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
@ -178,7 +177,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_discomfort)).to eq(4) expect(calculator.send(:cohesion_penalty)).to eq(4)
end end
end end
@ -191,7 +190,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_discomfort)).to eq(8) expect(calculator.send(:cohesion_penalty)).to eq(8)
end end
end end
@ -205,7 +204,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_discomfort)).to eq(4 * 1 + 4 * Rational(1, 2) + 4 * Rational(2, 3)) expect(calculator.send(:cohesion_penalty)).to eq(4 * 1 + 4 * Rational(1, 2) + 4 * Rational(2, 3))
end end
end end
@ -219,7 +218,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_discomfort)).to eq(6 * 1 + 2 * Rational(1, 2) + 3 * Rational(2, 3)) expect(calculator.send(:cohesion_penalty)).to eq(6 * 1 + 2 * Rational(1, 2) + 3 * Rational(2, 3))
end end
end end
end end