|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +# app/builders/better_together/joatu_demo_builder.rb |
| 4 | + |
| 5 | +module BetterTogether |
| 6 | + # Seeds a realistic demo dataset for the Joatu exchange system |
| 7 | + class JoatuDemoBuilder < Builder # rubocop:todo Metrics/ClassLength |
| 8 | + DEMO_TAG = '[Demo]' |
| 9 | + |
| 10 | + class << self |
| 11 | + def seed_data |
| 12 | + I18n.with_locale(:en) do |
| 13 | + ensure_categories! |
| 14 | + |
| 15 | + people = build_people |
| 16 | + community = build_demo_community(creator: people.first) |
| 17 | + |
| 18 | + addresses = build_addresses |
| 19 | + |
| 20 | + requests = build_requests(people:, community:, addresses:) |
| 21 | + offers = build_offers(people:, community:, addresses:) |
| 22 | + |
| 23 | + build_agreements(requests:, offers:) |
| 24 | + end |
| 25 | + end |
| 26 | + |
| 27 | + # Cautious clean-up: only removes demo-tagged data created by this builder |
| 28 | + # rubocop:todo Metrics/MethodLength |
| 29 | + def clear_existing # rubocop:todo Metrics/AbcSize, Metrics/MethodLength |
| 30 | + # Agreements first due to FK |
| 31 | + ::BetterTogether::Joatu::Agreement |
| 32 | + .joins(:offer) |
| 33 | + .where("#{::BetterTogether::Joatu::Offer.table_name}.id IN (?)", |
| 34 | + demo_offers.select(:id)) |
| 35 | + .delete_all |
| 36 | + |
| 37 | + ::BetterTogether::Joatu::Agreement |
| 38 | + .joins(:request) |
| 39 | + .where("#{::BetterTogether::Joatu::Request.table_name}.id IN (?)", |
| 40 | + demo_requests.select(:id)) |
| 41 | + .delete_all |
| 42 | + |
| 43 | + demo_offers.delete_all |
| 44 | + demo_requests.delete_all |
| 45 | + |
| 46 | + demo_community&.destroy |
| 47 | + demo_people.delete_all |
| 48 | + end |
| 49 | + # rubocop:enable Metrics/MethodLength |
| 50 | + |
| 51 | + private |
| 52 | + |
| 53 | + # -- Core builders -- |
| 54 | + |
| 55 | + def ensure_categories! |
| 56 | + return if ::BetterTogether::Joatu::Category.exists? |
| 57 | + |
| 58 | + ::BetterTogether::CategoryBuilder.seed_data |
| 59 | + end |
| 60 | + |
| 61 | + def build_people |
| 62 | + names = [ |
| 63 | + 'Ava Patel', 'Liam Nguyen', 'Maya Chen', 'Noah Garcia', |
| 64 | + 'Sophia Ahmed', 'Ethan Rossi', 'Zoe Kim', 'Oliver Dubois' |
| 65 | + ] |
| 66 | + |
| 67 | + names.map do |name| |
| 68 | + ::BetterTogether::Person.find_or_create_by!(identifier: identifier_for(name)) do |p| |
| 69 | + p.name = name |
| 70 | + end |
| 71 | + end |
| 72 | + end |
| 73 | + |
| 74 | + def build_demo_community(creator:) |
| 75 | + ::BetterTogether::Community.find_or_create_by!(identifier: 'joatu-demo') do |c| |
| 76 | + c.name_en = 'Joatu Demo Community' |
| 77 | + c.description_en = 'A sample community to scope Joatu exchanges.' |
| 78 | + c.creator = creator |
| 79 | + c.privacy = 'public' |
| 80 | + end |
| 81 | + end |
| 82 | + |
| 83 | + def build_addresses |
| 84 | + [ |
| 85 | + { line1: '123 Harbour Rd', city_name: 'St. John\'s', state_province_name: 'NL', postal_code: 'A1A 1A1' }, |
| 86 | + { line1: '42 Elm Street', city_name: 'Halifax', state_province_name: 'NS', postal_code: 'B3H 2Y9' }, |
| 87 | + { line1: '77 Maple Avenue', city_name: 'Montreal', state_province_name: 'QC', postal_code: 'H2X 3V9' }, |
| 88 | + { line1: '900 King Street', city_name: 'Toronto', state_province_name: 'ON', postal_code: 'M5V 1G4' }, |
| 89 | + { line1: '12 Oak Crescent', city_name: 'Calgary', state_province_name: 'AB', postal_code: 'T2P 3N9' } |
| 90 | + ].map do |attrs| |
| 91 | + ::BetterTogether::Address.create!(attrs.merge(physical: true, postal: false, country_name: 'Canada')) |
| 92 | + end |
| 93 | + end |
| 94 | + |
| 95 | + # rubocop:todo Metrics/AbcSize |
| 96 | + def build_requests(people:, community:, addresses:) # rubocop:todo Metrics/MethodLength |
| 97 | + cat = ->(name) { find_category(name) } |
| 98 | + |
| 99 | + data = [ |
| 100 | + { |
| 101 | + name: 'Home-cooked meals for seniors', |
| 102 | + desc: 'Requesting daily dinners for two seniors recovering post-surgery.', |
| 103 | + categories: [cat.call('Food & Water')], |
| 104 | + urgency: 'high' |
| 105 | + }, |
| 106 | + { |
| 107 | + name: 'School pickup for two kids', |
| 108 | + desc: 'Help needed for weekday school pickup for 2 children.', |
| 109 | + categories: [cat.call('Childcare'), cat.call('Transportation')], |
| 110 | + urgency: 'normal' |
| 111 | + }, |
| 112 | + { |
| 113 | + name: 'Minor roof repair post-storm', |
| 114 | + desc: 'Shingles lifted in last storm; need minor roof patch.', |
| 115 | + categories: [cat.call('Cleanup & Repairs')], |
| 116 | + urgency: 'critical' |
| 117 | + }, |
| 118 | + { |
| 119 | + name: 'Spanish translation for clinic visit', |
| 120 | + desc: 'Looking for Spanish interpretation for a medical appointment.', |
| 121 | + categories: [cat.call('Translation'), cat.call('Medical Assistance')], |
| 122 | + urgency: 'high' |
| 123 | + }, |
| 124 | + { |
| 125 | + name: 'Temporary housing for 3 nights', |
| 126 | + desc: 'Family of 3 needs a place to stay after flooding.', |
| 127 | + categories: [cat.call('Evacuation Housing'), cat.call('Accommodation')], |
| 128 | + urgency: 'high' |
| 129 | + } |
| 130 | + ] |
| 131 | + |
| 132 | + data.map.with_index do |row, i| |
| 133 | + ::BetterTogether::Joatu::Request.create!( |
| 134 | + name_en: "#{DEMO_TAG} #{row[:name]}", |
| 135 | + description_en: row[:desc], |
| 136 | + creator: people.sample, |
| 137 | + categories: row[:categories].compact, |
| 138 | + status: 'open', |
| 139 | + urgency: row[:urgency], |
| 140 | + address: addresses[i % addresses.size], |
| 141 | + target: community |
| 142 | + ) |
| 143 | + end |
| 144 | + end |
| 145 | + # rubocop:enable Metrics/AbcSize |
| 146 | + |
| 147 | + # rubocop:todo Metrics/AbcSize |
| 148 | + def build_offers(people:, community:, addresses:) # rubocop:todo Metrics/MethodLength |
| 149 | + cat = ->(name) { find_category(name) } |
| 150 | + |
| 151 | + data = [ |
| 152 | + { |
| 153 | + name: 'Batch-cooked meals available', |
| 154 | + desc: 'Cooking extra dinners this week; can deliver locally.', |
| 155 | + categories: [cat.call('Food & Water')], |
| 156 | + urgency: 'normal' |
| 157 | + }, |
| 158 | + { |
| 159 | + name: 'Evening rides with minivan', |
| 160 | + desc: 'Offering weekday evening rides; car seats available.', |
| 161 | + categories: [cat.call('Transportation')], |
| 162 | + urgency: 'low' |
| 163 | + }, |
| 164 | + { |
| 165 | + name: 'Handyman for minor repairs', |
| 166 | + desc: 'Experienced with basic home repairs; evenings/weekends.', |
| 167 | + categories: [cat.call('Cleanup & Repairs')], |
| 168 | + urgency: 'normal' |
| 169 | + }, |
| 170 | + { |
| 171 | + name: 'Bilingual Spanish interpreter', |
| 172 | + desc: 'Fluent in Spanish and English; can accompany to appointments.', |
| 173 | + categories: [cat.call('Translation')], |
| 174 | + urgency: 'normal' |
| 175 | + }, |
| 176 | + { |
| 177 | + name: 'Guest room available', |
| 178 | + desc: 'Quiet room with private bath for short-term stays.', |
| 179 | + categories: [cat.call('Accommodation'), cat.call('Evacuation Housing')], |
| 180 | + urgency: 'high' |
| 181 | + } |
| 182 | + ] |
| 183 | + |
| 184 | + data.map.with_index do |row, i| |
| 185 | + ::BetterTogether::Joatu::Offer.create!( |
| 186 | + name_en: "#{DEMO_TAG} #{row[:name]}", |
| 187 | + description_en: row[:desc], |
| 188 | + creator: people.sample, |
| 189 | + categories: row[:categories].compact, |
| 190 | + status: 'open', |
| 191 | + urgency: row[:urgency], |
| 192 | + address: addresses[(i + 2) % addresses.size], |
| 193 | + target: community |
| 194 | + ) |
| 195 | + end |
| 196 | + end |
| 197 | + # rubocop:enable Metrics/AbcSize |
| 198 | + |
| 199 | + # rubocop:todo Metrics/MethodLength |
| 200 | + def build_agreements(requests:, offers:) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength |
| 201 | + # Try to pair similar categories for realism |
| 202 | + pair = lambda do |req_name_contains:, off_name_contains:, status: :pending, terms: nil, value: nil| |
| 203 | + req = requests.find { |r| r.name.include?(req_name_contains) } |
| 204 | + off = offers.find { |o| o.name.include?(off_name_contains) } |
| 205 | + return unless req && off |
| 206 | + |
| 207 | + agr = ::BetterTogether::Joatu::Agreement.create!( |
| 208 | + offer: off, |
| 209 | + request: req, |
| 210 | + terms: terms, |
| 211 | + value: value, |
| 212 | + status: 'pending' |
| 213 | + ) |
| 214 | + |
| 215 | + case status |
| 216 | + when :accepted |
| 217 | + agr.accept! |
| 218 | + when :rejected |
| 219 | + agr.reject! |
| 220 | + end |
| 221 | + end |
| 222 | + |
| 223 | + pair.call( |
| 224 | + req_name_contains: 'meals', |
| 225 | + off_name_contains: 'meals', |
| 226 | + status: :accepted, |
| 227 | + terms: 'Deliver dinners Mon–Fri for 1 week', |
| 228 | + value: '6 credits' |
| 229 | + ) |
| 230 | + |
| 231 | + pair.call( |
| 232 | + req_name_contains: 'School pickup', |
| 233 | + off_name_contains: 'rides', |
| 234 | + status: :pending, |
| 235 | + terms: 'Pickup at 3pm, Mon–Thu', |
| 236 | + value: 'fuel cost only' |
| 237 | + ) |
| 238 | + |
| 239 | + pair.call( |
| 240 | + req_name_contains: 'roof repair', |
| 241 | + off_name_contains: 'Handyman', |
| 242 | + status: :accepted, |
| 243 | + terms: 'Patch shingles and inspect attic', |
| 244 | + value: 'no cost' |
| 245 | + ) |
| 246 | + |
| 247 | + pair.call( |
| 248 | + req_name_contains: 'Temporary housing', |
| 249 | + off_name_contains: 'Guest room', |
| 250 | + status: :rejected, |
| 251 | + terms: '3 nights, no pets', |
| 252 | + value: 'n/a' |
| 253 | + ) |
| 254 | + end |
| 255 | + # rubocop:enable Metrics/MethodLength |
| 256 | + |
| 257 | + # -- Helpers -- |
| 258 | + |
| 259 | + def find_category(name) |
| 260 | + ::BetterTogether::Joatu::Category.i18n.find_or_create_by(name: name) |
| 261 | + end |
| 262 | + |
| 263 | + def identifier_for(name) |
| 264 | + "#{DEMO_TAG} #{name}".parameterize |
| 265 | + end |
| 266 | + |
| 267 | + def demo_people |
| 268 | + ::BetterTogether::Person.where('identifier LIKE ?', "#{DEMO_TAG.downcase.tr('[]', '')}%") |
| 269 | + end |
| 270 | + |
| 271 | + def demo_community |
| 272 | + ::BetterTogether::Community.find_by(identifier: 'joatu-demo') |
| 273 | + end |
| 274 | + |
| 275 | + def demo_offers |
| 276 | + ::BetterTogether::Joatu::Offer.i18n.where('mobility_string_translations.value LIKE ?', "%#{DEMO_TAG}%") |
| 277 | + end |
| 278 | + |
| 279 | + def demo_requests |
| 280 | + ::BetterTogether::Joatu::Request.i18n.where('mobility_string_translations.value LIKE ?', "%#{DEMO_TAG}%") |
| 281 | + end |
| 282 | + end |
| 283 | + end |
| 284 | +end |
0 commit comments