Manuel Bustillo 2147d7ad5e
All checks were successful
Run unit tests / rubocop (push) Successful in 27s
Run unit tests / check-licenses (push) Successful in 32s
Run unit tests / copyright_notice (push) Successful in 36s
Run unit tests / unit_tests (push) Successful in 1m22s
Run unit tests / build-static-assets (push) Successful in 10m9s
Redo simulations lifecycle (#222)
## Why

The current way of creating and deleting simulations doesn't scale for big instances. We cannot generate 50 simulations every time a guest confirms attendance, and we should not delete existing valuable simulations. For example, if a guest confirms attendance and declines right after, previously generated simulations should still be valid.

## What

In this PR we are introducing a series of changes that make simulations management easier:

1. We're removing the automatic creation of simulations.
2. Simulations are not removed anymore, neither manually nor automatically.
3. A new endpoint has been defined to create simulations on demand.
4. A digest property has been defined to determine whether a simulation is still valid (meaning there have not been any change in the list of guests involved).

Reviewed-on: #222
Co-authored-by: Manuel Bustillo <bustikiller@bustikiller.com>
Co-committed-by: Manuel Bustillo <bustikiller@bustikiller.com>
2025-01-26 12:53:21 +00:00

77 lines
2.1 KiB
Ruby

# Copyright (C) 2024-2025 LibreWeddingPlanner contributors
# frozen_string_literal: true
require_relative '../../extensions/tree_node_extension'
module Tables
class Distribution
class << self
def digest(wedding)
Digest::UUID.uuid_v5(wedding.id, wedding.guests.potential.order(:id).pluck(:id).join)
end
end
attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy
def initialize(min_per_table:, max_per_table:)
@min_per_table = min_per_table
@max_per_table = max_per_table
@hierarchy = AffinityGroupsHierarchy.new
@tables = []
end
def random_distribution(people)
min_tables = (people.count * 1.0 / @max_per_table).ceil
max_tables = (people.count * 1.0 / @min_per_table).ceil
@tables = people.in_groups(rand(min_tables..max_tables), false)
.map { |group| Table.new(group) }
.each { |table| table.min_per_table = @min_per_table }
.each { |table| table.max_per_table = @max_per_table }
end
def discomfort
@tables.map do |table|
local_discomfort(table)
end.sum
end
def inspect
"#{@tables.count} tables, discomfort: #{discomfort}"
end
def deep_dup
self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table).tap do |new_distribution|
new_distribution.tables = @tables.map(&:dup)
end
end
def save!
ActiveRecord::Base.transaction do
arrangement = TablesArrangement.create!
records_to_store = []
tables.each_with_index do |table, table_number|
table.each do |person|
records_to_store << { guest_id: person.id, tables_arrangement_id: arrangement.id, table_number: }
end
end
Seat.insert_all!(records_to_store)
arrangement.update!(
discomfort:,
digest: self.class.digest(tables.first.first.wedding)
)
end
end
private
def local_discomfort(table)
table.discomfort ||= DiscomfortCalculator.new(table:, hierarchy:).calculate
end
end
end