Compare commits

...

10 Commits

Author SHA1 Message Date
Renovate Bot
8b965c82d6 Update dependency jbuilder to v2.14.1
All checks were successful
Run unit tests / check-licenses (pull_request) Successful in 2m6s
Run unit tests / rubocop (pull_request) Successful in 2m7s
Run unit tests / copyright_notice (pull_request) Successful in 2m22s
Run unit tests / unit_tests (pull_request) Successful in 4m47s
Run unit tests / build-static-assets (pull_request) Successful in 21m5s
2025-09-16 22:16:53 +00:00
e28751521d Merge pull request 'Persist and expose via API the progress of the tables arrangement simulations' (#316) from arrangements-status into main
All checks were successful
Run unit tests / rubocop (push) Has been skipped
Run unit tests / check-licenses (push) Has been skipped
Run unit tests / copyright_notice (push) Has been skipped
Run unit tests / unit_tests (push) Successful in 9m29s
Run unit tests / build-static-assets (push) Successful in 22m38s
Reviewed-on: #316
2025-09-16 00:39:40 +00:00
0502bc4552
Disable a rubocop alert
All checks were successful
Run unit tests / rubocop (pull_request) Successful in 3m14s
Run unit tests / check-licenses (pull_request) Successful in 5m37s
Run unit tests / copyright_notice (pull_request) Successful in 7m13s
Run unit tests / unit_tests (pull_request) Successful in 30m24s
Run unit tests / build-static-assets (pull_request) Successful in 2h21m47s
2025-09-15 23:17:00 +02:00
7d8ecfd0e3
Refactor class to reduce complexity of #run method
Some checks failed
Run unit tests / rubocop (pull_request) Failing after 2m17s
Run unit tests / check-licenses (pull_request) Successful in 3m51s
Run unit tests / copyright_notice (pull_request) Successful in 4m4s
Run unit tests / unit_tests (pull_request) Successful in 10m37s
Run unit tests / build-static-assets (pull_request) Failing after 56s
2025-09-15 23:04:02 +02:00
78ab27a697
Fix specs
Some checks failed
Run unit tests / copyright_notice (pull_request) Successful in 1m50s
Run unit tests / rubocop (pull_request) Failing after 2m12s
Run unit tests / check-licenses (pull_request) Successful in 2m50s
Run unit tests / unit_tests (pull_request) Successful in 3m48s
Run unit tests / build-static-assets (pull_request) Successful in 1h45m6s
2025-09-15 22:52:41 +02:00
12174b6f20 Persist VNS calculation progress whenever an improvement has been made
Some checks failed
Run unit tests / check-licenses (pull_request) Failing after 1m44s
Run unit tests / rubocop (pull_request) Failing after 1m46s
Run unit tests / copyright_notice (pull_request) Successful in 2m8s
Run unit tests / unit_tests (pull_request) Failing after 3m30s
Run unit tests / build-static-assets (pull_request) Has been skipped
2025-09-08 22:44:54 +02:00
0d1b64256d Provide notification callbacks for progress and new solutions 2025-09-08 16:32:13 +02:00
ac659bef86 Update Tables::Distribution#save! to consider that the distribution may already be persisted 2025-09-08 15:51:43 +02:00
dd14a96e98 Expose and document the new status attribute in the tables arrangements controller 2025-08-01 12:29:13 +02:00
75a0191d40 Add a new status column to tables arrangements table 2025-08-01 12:25:43 +02:00
19 changed files with 164 additions and 45 deletions

View File

@ -145,9 +145,9 @@ GEM
pp (>= 0.6.0) pp (>= 0.6.0)
rdoc (>= 4.0.0) rdoc (>= 4.0.0)
reline (>= 0.4.2) reline (>= 0.4.2)
jbuilder (2.13.0) jbuilder (2.14.1)
actionview (>= 5.0.0) actionview (>= 7.0.0)
activesupport (>= 5.0.0) activesupport (>= 7.0.0)
json (2.13.2) json (2.13.2)
json-schema (5.0.1) json-schema (5.0.1)
addressable (~> 2.8) addressable (~> 2.8)
@ -211,18 +211,18 @@ GEM
net-smtp (0.5.1) net-smtp (0.5.1)
net-protocol net-protocol
nio4r (2.7.4) nio4r (2.7.4)
nokogiri (1.18.9) nokogiri (1.18.10)
mini_portile2 (~> 2.8.2) mini_portile2 (~> 2.8.2)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-aarch64-linux-gnu) nokogiri (1.18.10-aarch64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-arm-linux-gnu) nokogiri (1.18.10-arm-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-arm64-darwin) nokogiri (1.18.10-arm64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-x86_64-darwin) nokogiri (1.18.10-x86_64-darwin)
racc (~> 1.4) racc (~> 1.4)
nokogiri (1.18.9-x86_64-linux-gnu) nokogiri (1.18.10-x86_64-linux-gnu)
racc (~> 1.4) racc (~> 1.4)
orm_adapter (0.5.0) orm_adapter (0.5.0)
ostruct (0.6.2) ostruct (0.6.2)
@ -539,7 +539,7 @@ CHECKSUMS
importmap-rails (2.2.2) sha256=729f5b1092f832780829ade1d0b46c7e53d91c556f06da7254da2977e93fe614 importmap-rails (2.2.2) sha256=729f5b1092f832780829ade1d0b46c7e53d91c556f06da7254da2977e93fe614
io-console (0.8.1) sha256=1e15440a6b2f67b6ea496df7c474ed62c860ad11237f29b3bd187f054b925fcb io-console (0.8.1) sha256=1e15440a6b2f67b6ea496df7c474ed62c860ad11237f29b3bd187f054b925fcb
irb (1.15.2) sha256=222f32952e278da34b58ffe45e8634bf4afc2dc7aa9da23fed67e581aa50fdba irb (1.15.2) sha256=222f32952e278da34b58ffe45e8634bf4afc2dc7aa9da23fed67e581aa50fdba
jbuilder (2.13.0) sha256=7200a38a1c0081aa81b7a9757e7a299db75bc58cf1fd45ca7919a91627d227d6 jbuilder (2.14.1) sha256=4eb26376ff60ef100cb4fd6fd7533cd271f9998327e86adf20fd8c0e69fabb42
json (2.13.2) sha256=02e1f118d434c6b230a64ffa5c8dee07e3ec96244335c392eaed39e1199dbb68 json (2.13.2) sha256=02e1f118d434c6b230a64ffa5c8dee07e3ec96244335c392eaed39e1199dbb68
json-schema (5.0.1) sha256=bef71a82c600a42594911553522e143f7634affc198ed507ef3ded2f920a74a9 json-schema (5.0.1) sha256=bef71a82c600a42594911553522e143f7634affc198ed507ef3ded2f920a74a9
jsonapi-deserializable (0.2.0) sha256=5f0ca2d3f8404cce1584a314e8a3753be32a56054c942adfe997b87e92bce147 jsonapi-deserializable (0.2.0) sha256=5f0ca2d3f8404cce1584a314e8a3753be32a56054c942adfe997b87e92bce147
@ -570,12 +570,12 @@ CHECKSUMS
net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8 net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9 nio4r (2.7.4) sha256=d95dee68e0bb251b8ff90ac3423a511e3b784124e5db7ff5f4813a220ae73ca9
nokogiri (1.18.9) sha256=ac5a7d93fd0e3cef388800b037407890882413feccca79eb0272a2715a82fa33 nokogiri (1.18.10) sha256=d5cc0731008aa3b3a87b361203ea3d19b2069628cb55e46ac7d84a0445e69cc1
nokogiri (1.18.9-aarch64-linux-gnu) sha256=5bcfdf7aa8d1056a7ad5e52e1adffc64ef53d12d0724fbc6f458a3af1a4b9e32 nokogiri (1.18.10-aarch64-linux-gnu) sha256=7fb87235d729c74a2be635376d82b1d459230cc17c50300f8e4fcaabc6195344
nokogiri (1.18.9-arm-linux-gnu) sha256=fe611ae65880e445a9c0f650d52327db239f3488626df4173c05beafd161d46e nokogiri (1.18.10-arm-linux-gnu) sha256=51f4f25ab5d5ba1012d6b16aad96b840a10b067b93f35af6a55a2c104a7ee322
nokogiri (1.18.9-arm64-darwin) sha256=eea3f1f06463ff6309d3ff5b88033c4948d0da1ab3cc0a3a24f63c4d4a763979 nokogiri (1.18.10-arm64-darwin) sha256=c2b0de30770f50b92c9323fa34a4e1cf5a0af322afcacd239cd66ee1c1b22c85
nokogiri (1.18.9-x86_64-darwin) sha256=e0d2deb03d3d7af8016e8c9df5ff4a7d692159cefb135cbb6a4109f265652348 nokogiri (1.18.10-x86_64-darwin) sha256=536e74bed6db2b5076769cab5e5f5af0cd1dccbbd75f1b3e1fa69d1f5c2d79e2
nokogiri (1.18.9-x86_64-linux-gnu) sha256=b52f5defedc53d14f71eeaaf990da66b077e1918a2e13088b6a96d0230f44360 nokogiri (1.18.10-x86_64-linux-gnu) sha256=ff5ba26ba2dbce5c04b9ea200777fd225061d7a3930548806f31db907e500f72
orm_adapter (0.5.0) sha256=aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9 orm_adapter (0.5.0) sha256=aa5d0be5d540cbb46d3a93e88061f4ece6a25f6e97d6a47122beb84fe595e9b9
ostruct (0.6.2) sha256=6d7302a299e400a2c248d6ce0dad18fc3a5714e8096facc25ffd0c54ee57cfc0 ostruct (0.6.2) sha256=6d7302a299e400a2c248d6ce0dad18fc3a5714e8096facc25ffd0c54ee57cfc0
parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130

View File

@ -9,10 +9,10 @@ class TablesArrangementsController < ApplicationController
render json: TablesArrangement render json: TablesArrangement
.order(valid: :desc) .order(valid: :desc)
.order(discomfort: :asc) .order(discomfort: :asc)
.select(:id, :name, :discomfort) .select(:id, :name, :discomfort, :status, :progress)
.select("digest = '#{current_digest}'::uuid as valid") .select("digest = '#{current_digest}'::uuid OR discomfort IS NULL as valid")
.limit(20) .limit(20)
.as_json(only: %i[id name discomfort valid]) .as_json(only: %i[id name discomfort valid status progress])
end end
def show def show
@ -25,7 +25,10 @@ class TablesArrangementsController < ApplicationController
end end
def create def create
TableSimulatorJob.perform_later(current_tenant.id) ActiveRecord::Base.transaction do
tables_arrangement = TablesArrangement.create!(status: :not_started)
TableSimulatorJob.perform_later(current_tenant.id, tables_arrangement.id)
end
render json: {}, status: :created render json: {}, status: :created
end end

View File

@ -8,16 +8,35 @@ class TableSimulatorJob < ApplicationJob
MIN_PER_TABLE = 8 MIN_PER_TABLE = 8
MAX_PER_TABLE = 10 MAX_PER_TABLE = 10
def perform(wedding_id) def perform(wedding_id, tables_arrangement_id) # rubocop:disable Metrics/MethodLength
Rails.logger.info "Starting table simulation #{tables_arrangement_id} for wedding #{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
engine.add_optimization(Tables::Swap) engine.add_optimization(Tables::Swap)
engine.add_optimization(Tables::Shift) engine.add_optimization(Tables::Shift)
initial_solution = Tables::Distribution.new(min_per_table: MIN_PER_TABLE, max_per_table: MAX_PER_TABLE) tables_arrangement = TablesArrangement.find(tables_arrangement_id)
initial_solution = Tables::Distribution.new(
min_per_table: MIN_PER_TABLE,
max_per_table: MAX_PER_TABLE,
tables_arrangement_id:
)
initial_solution.random_distribution(Guest.potential.shuffle) initial_solution.random_distribution(Guest.potential.shuffle)
initial_solution.save!
engine.notify_progress do |current_progress|
tables_arrangement.update_columns(status: :in_progress, progress: current_progress)
end
engine.on_better_solution do |better_solution|
better_solution.save!
tables_arrangement.update_columns(discomfort: better_solution.discomfort) # TODO: remove?
end
engine.initial_solution = initial_solution engine.initial_solution = initial_solution
engine.target_function(&:discomfort) engine.target_function(&:discomfort)
@ -25,6 +44,8 @@ class TableSimulatorJob < ApplicationJob
best_solution = engine.run best_solution = engine.run
best_solution.save! best_solution.save!
tables_arrangement.update_columns(status: :completed)
end end
end end
end end

View File

@ -29,5 +29,5 @@
class Seat < ApplicationRecord class Seat < ApplicationRecord
acts_as_tenant :wedding acts_as_tenant :wedding
belongs_to :guest belongs_to :guest
belongs_to :table_arrangement belongs_to :tables_arrangement
end end

View File

@ -10,6 +10,8 @@
# digest :uuid not null # digest :uuid not null
# discomfort :integer # discomfort :integer
# name :string not null # name :string not null
# progress :float default(0.0), not null
# status :string default("complete"), not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# wedding_id :uuid not null # wedding_id :uuid not null

View File

@ -12,13 +12,14 @@ module Tables
end end
end end
attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy, :tables_arrangement_id
def initialize(min_per_table:, max_per_table:, hierarchy: AffinityGroupsHierarchy.new) def initialize(min_per_table:, max_per_table:, tables_arrangement_id:, hierarchy: AffinityGroupsHierarchy.new)
@min_per_table = min_per_table @min_per_table = min_per_table
@max_per_table = max_per_table @max_per_table = max_per_table
@hierarchy = hierarchy @hierarchy = hierarchy
@tables = [] @tables = []
@tables_arrangement_id = tables_arrangement_id
end end
def random_distribution(people, random: Random.new) def random_distribution(people, random: Random.new)
@ -42,15 +43,23 @@ module Tables
end end
def deep_dup def deep_dup
self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table, self.class.new(
hierarchy: @hierarchy).tap do |new_distribution| min_per_table: @min_per_table,
max_per_table: @max_per_table,
hierarchy: @hierarchy,
tables_arrangement_id: @tables_arrangement_id
).tap do |new_distribution|
new_distribution.tables = @tables.map(&:dup) new_distribution.tables = @tables.map(&:dup)
end end
end end
def save! def save!
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
arrangement = TablesArrangement.create! arrangement = TablesArrangement.find(tables_arrangement_id)
self.tables_arrangement_id = arrangement.id
arrangement.seats.delete_all
records_to_store = [] records_to_store = []

View File

@ -5,6 +5,7 @@
module VNS module VNS
class Engine class Engine
PERTURBATION_SIZES = [1, 1, 1, 2, 2, 3].freeze PERTURBATION_SIZES = [1, 1, 1, 2, 2, 3].freeze
ITERATIONS = 50
class << self class << self
def sequence(elements) def sequence(elements)
elements = elements.to_a elements = elements.to_a
@ -12,6 +13,10 @@ module VNS
end end
end end
def initialize
@perturbations = Set.new
end
def target_function(&function) def target_function(&function)
@target_function = function @target_function = function
end end
@ -22,36 +27,45 @@ module VNS
end end
def add_perturbation(klass) def add_perturbation(klass)
@perturbations ||= Set.new
@perturbations << klass @perturbations << klass
end end
def notify_progress(&block)
@progress_notifier = block
end
def on_better_solution(&block)
@better_solution_notifier = block
end
attr_writer :initial_solution attr_writer :initial_solution
def run def run
raise 'No target function defined' unless @target_function check_preconditions!
raise 'No optimizations defined' unless @optimizations
raise 'No initial solution defined' unless @initial_solution
@perturbations ||= Set.new
@current_solution = @initial_solution @current_solution = @initial_solution
@best_score = @target_function.call(@current_solution) @best_score = @target_function.call(@current_solution)
run_all_optimizations run_all_optimizations
@progress_notifier&.call(Rational(1, ITERATIONS + 1))
best_solution = @current_solution best_solution = @current_solution
50.times do (1..ITERATIONS).each do |iteration|
@current_solution = Tables::WheelSwap.new(best_solution).call(PERTURBATION_SIZES.sample) @current_solution = Tables::WheelSwap.new(best_solution).call(PERTURBATION_SIZES.sample)
@best_score = @target_function.call(@current_solution) @best_score = @target_function.call(@current_solution)
Rails.logger.debug { "After perturbation: #{@best_score}" } Rails.logger.debug { "After perturbation: #{@best_score}" }
run_all_optimizations run_all_optimizations
@progress_notifier&.call(Rational(iteration + 1, ITERATIONS + 1))
next unless best_solution.discomfort > @current_solution.discomfort next unless best_solution.discomfort > @current_solution.discomfort
best_solution = @current_solution best_solution = @current_solution
@better_solution_notifier&.call(best_solution)
Rails.logger.debug do Rails.logger.debug do
"Found better solution after perturbation optimization: #{@current_solution.discomfort}" "Found better solution after perturbation optimization: #{@current_solution.discomfort}"
end end
@ -62,6 +76,12 @@ module VNS
private private
def check_preconditions!
raise 'No target function defined' unless @target_function
raise 'No optimizations defined' unless @optimizations
raise 'No initial solution defined' unless @initial_solution
end
def run_all_optimizations def run_all_optimizations
self.class.sequence(@optimizations).each do |optimization| self.class.sequence(@optimizations).each do |optimization|
optimize(optimization) optimize(optimization)

View File

@ -3,4 +3,5 @@
require_relative "../config/environment" require_relative "../config/environment"
require "solid_queue/cli" require "solid_queue/cli"
SolidQueue.logger = ActiveSupport::Logger.new($stdout)
SolidQueue::Cli.start(ARGV) SolidQueue::Cli.start(ARGV)

View File

@ -0,0 +1,5 @@
class AddStatusColumnToTablesArrangements < ActiveRecord::Migration[8.0]
def change
add_column :tables_arrangements, :status, :string, default: :complete, null: false
end
end

View File

@ -0,0 +1,5 @@
class AddProgressToTablesArrangements < ActiveRecord::Migration[8.0]
def change
add_column :tables_arrangements, :progress, :float, default: 0, null: false
end
end

4
db/schema.rb generated
View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_06_08_181054) do ActiveRecord::Schema[8.0].define(version: 2025_09_08_145119) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql" enable_extension "pg_catalog.plpgsql"
@ -216,6 +216,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_08_181054) do
t.string "name", null: false t.string "name", null: false
t.uuid "wedding_id", null: false t.uuid "wedding_id", null: false
t.uuid "digest", default: -> { "gen_random_uuid()" }, null: false t.uuid "digest", default: -> { "gen_random_uuid()" }, null: false
t.string "status", default: "complete", null: false
t.float "progress", default: 0.0, null: false
t.index ["wedding_id"], name: "index_tables_arrangements_on_wedding_id" t.index ["wedding_id"], name: "index_tables_arrangements_on_wedding_id"
end end

View File

@ -86,7 +86,9 @@ ActsAsTenant.with_tenant(wedding) do
# TODO: Clean up invitations with no guests # TODO: Clean up invitations with no guests
ActiveJob.perform_all_later(3.times.map { TableSimulatorJob.new(wedding.id) }) 3.times { TablesArrangement.create! }
.map { |arrangement| TableSimulatorJob.new(wedding.id, arrangement.id) }
.then { |jobs| ActiveJob.perform_all_later }
"red".dup.paint.palette.triad(as: :hex).zip(Group.roots).each { |(color, group)| group.update!(color: color.paint.desaturate(40)) } "red".dup.paint.palette.triad(as: :hex).zip(Group.roots).each { |(color, group)| group.update!(color: color.paint.desaturate(40)) }

View File

@ -21,6 +21,14 @@ namespace :vns do
engine.target_function(&:discomfort) engine.target_function(&:discomfort)
engine.notify_progress do |current_progress|
Rails.logger.info "Progress: #{(current_progress * 100.0).round(2)}%"
end
engine.on_better_solution do |better_solution|
Rails.logger.info "New best solution found with discomfort: #{better_solution.discomfort}"
end
solution = Rails.benchmark('VNS Benchmarking') { engine.run } solution = Rails.benchmark('VNS Benchmarking') { engine.run }
Rails.logger.info "Best solution found with discomfort: #{solution.discomfort}" Rails.logger.info "Best solution found with discomfort: #{solution.discomfort}"

View File

@ -12,6 +12,11 @@ server {
proxy_set_header Host $http_host; proxy_set_header Host $http_host;
} }
location /jobs/ {
proxy_pass http://backend:3000/jobs/;
proxy_set_header Host $http_host;
}
location /captcha/v2/media/ { location /captcha/v2/media/ {
proxy_pass http://libre-captcha:8888/v2/media/; proxy_pass http://libre-captcha:8888/v2/media/;
proxy_set_header Host $http_host; proxy_set_header Host $http_host;

View File

@ -19,7 +19,8 @@ RSpec.describe 'tables_arrangements' do
id: { type: :string, format: :uuid }, id: { type: :string, format: :uuid },
name: { type: :string }, name: { type: :string },
discomfort: { type: :integer }, discomfort: { type: :integer },
valid: { type: :boolean } valid: { type: :boolean },
status: { type: :string, enum: %w[complete in_progress] }
} }
} }
xit xit

View File

@ -6,8 +6,43 @@ require 'rails_helper'
module Tables module Tables
RSpec.describe Distribution do RSpec.describe Distribution do
let(:tables_arrangement) { TablesArrangement.create! }
around do |example|
ActsAsTenant.with_tenant(create(:wedding)) do
example.run
end
end
describe '#save!' do
let(:people) { create_list(:guest, 2, status: :invited) }
let(:distribution) do
described_class.new(min_per_table: 5, max_per_table: 10, tables_arrangement_id: tables_arrangement.id)
.tap { |d| d.random_distribution(people) }
end
context 'when tables_arrangement_id is nil' do
it { expect { distribution.save! }.to change(TablesArrangement, :count).by(1) }
it { expect { distribution.save! }.to change(Seat, :count).by(2) }
end
context 'when tables_arrangement_id is set' do
before do
existing_arrangement = TablesArrangement.create!
existing_arrangement.seats.create!(guest: people.first, table_number: 1)
distribution.tables_arrangement_id = existing_arrangement.id
end
it { expect { distribution.save! }.not_to(change(TablesArrangement, :count)) }
it { expect { distribution.save! }.to change(Seat, :count).by(1) }
end
end
describe '#random_distribution' do describe '#random_distribution' do
subject(:distribution) { described_class.new(min_per_table: 5, max_per_table: 10) } subject(:distribution) do
described_class.new(min_per_table: 5, max_per_table: 10, tables_arrangement_id: tables_arrangement.id)
end
context 'when there are fewer people than the minimum per table' do context 'when there are fewer people than the minimum per table' do
it 'creates one table' do it 'creates one table' do

View File

@ -17,7 +17,7 @@ module Tables
context 'when there are two tables with two people each' do context 'when there are two tables with two people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| Distribution.new(min_per_table: 2, max_per_table: 2, tables_arrangement_id: nil).tap do |distribution|
distribution.tables << Set[:a, :b].to_table distribution.tables << Set[:a, :b].to_table
distribution.tables << Set[:c, :d].to_table distribution.tables << Set[:c, :d].to_table
end end
@ -35,7 +35,7 @@ module Tables
context 'when there are two tables with three people each' do context 'when there are two tables with three people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution| Distribution.new(min_per_table: 3, max_per_table: 3, tables_arrangement_id: nil).tap do |distribution|
distribution.tables << Set[:a, :b, :c].to_table distribution.tables << Set[:a, :b, :c].to_table
distribution.tables << Set[:d, :e, :f].to_table distribution.tables << Set[:d, :e, :f].to_table
end end

View File

@ -17,7 +17,7 @@ module Tables
context 'when there are two tables with two people each' do context 'when there are two tables with two people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| Distribution.new(min_per_table: 2, max_per_table: 2, tables_arrangement_id: nil).tap do |distribution|
distribution.tables << Set[:a, :b].to_table distribution.tables << Set[:a, :b].to_table
distribution.tables << Set[:c, :d].to_table distribution.tables << Set[:c, :d].to_table
end end
@ -35,7 +35,7 @@ module Tables
context 'when there are two tables with three people each' do context 'when there are two tables with three people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution| Distribution.new(min_per_table: 3, max_per_table: 3, tables_arrangement_id: nil).tap do |distribution|
distribution.tables << Set[:a, :b, :c].to_table distribution.tables << Set[:a, :b, :c].to_table
distribution.tables << Set[:d, :e, :f].to_table distribution.tables << Set[:d, :e, :f].to_table
end end
@ -58,7 +58,7 @@ module Tables
context 'when there are three tables with two people each' do context 'when there are three tables with two people each' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution| Distribution.new(min_per_table: 2, max_per_table: 2, tables_arrangement_id: nil).tap do |distribution|
distribution.tables << Set[:a, :b].to_table distribution.tables << Set[:a, :b].to_table
distribution.tables << Set[:c, :d].to_table distribution.tables << Set[:c, :d].to_table
distribution.tables << Set[:e, :f].to_table distribution.tables << Set[:e, :f].to_table

View File

@ -8,7 +8,7 @@ module Tables
RSpec.describe WheelSwap do RSpec.describe WheelSwap do
context 'when the solution has three tables' do context 'when the solution has three tables' do
let(:initial_solution) do let(:initial_solution) do
Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution| Distribution.new(min_per_table: 3, max_per_table: 3, tables_arrangement_id: nil).tap do |distribution|
distribution.tables << Set[:a, :b, :c].to_table distribution.tables << Set[:a, :b, :c].to_table
distribution.tables << Set[:d, :e, :f].to_table distribution.tables << Set[:d, :e, :f].to_table
distribution.tables << Set[:g, :h, :i].to_table distribution.tables << Set[:g, :h, :i].to_table