reso 0.1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +149 -0
  6. data/Rakefile +1 -0
  7. data/app/controllers/syndication_controller.rb +5 -0
  8. data/app/models/.keep +0 -0
  9. data/app/models/address.rb +25 -0
  10. data/app/models/address_type.rb +3 -0
  11. data/app/models/alternate_price.rb +2 -0
  12. data/app/models/appliance.rb +3 -0
  13. data/app/models/architecture_style.rb +5 -0
  14. data/app/models/area_unit.rb +2 -0
  15. data/app/models/brokerage.rb +4 -0
  16. data/app/models/builder.rb +4 -0
  17. data/app/models/business.rb +8 -0
  18. data/app/models/community.rb +5 -0
  19. data/app/models/cooling_system.rb +3 -0
  20. data/app/models/county.rb +5 -0
  21. data/app/models/currency_period.rb +3 -0
  22. data/app/models/enumeration.rb +7 -0
  23. data/app/models/expense.rb +6 -0
  24. data/app/models/expense_category.rb +2 -0
  25. data/app/models/exterior_type.rb +3 -0
  26. data/app/models/floor_covering.rb +3 -0
  27. data/app/models/foreclosure_status.rb +5 -0
  28. data/app/models/franchise.rb +4 -0
  29. data/app/models/gender.rb +3 -0
  30. data/app/models/heating_fuel.rb +3 -0
  31. data/app/models/heating_system.rb +3 -0
  32. data/app/models/home_feature.rb +3 -0
  33. data/app/models/import.rb +16 -0
  34. data/app/models/import_format.rb +3 -0
  35. data/app/models/license_category.rb +2 -0
  36. data/app/models/listing.rb +64 -0
  37. data/app/models/listing_category.rb +5 -0
  38. data/app/models/listing_media.rb +6 -0
  39. data/app/models/listing_provider.rb +9 -0
  40. data/app/models/listing_status.rb +5 -0
  41. data/app/models/multiple_listing_service.rb +5 -0
  42. data/app/models/neighborhood.rb +3 -0
  43. data/app/models/office.rb +6 -0
  44. data/app/models/open_house.rb +4 -0
  45. data/app/models/parking.rb +3 -0
  46. data/app/models/participant.rb +5 -0
  47. data/app/models/participant_license.rb +8 -0
  48. data/app/models/participant_role.rb +3 -0
  49. data/app/models/person.rb +18 -0
  50. data/app/models/photo.rb +3 -0
  51. data/app/models/place.rb +3 -0
  52. data/app/models/price.rb +4 -0
  53. data/app/models/property_sub_type.rb +5 -0
  54. data/app/models/property_type.rb +5 -0
  55. data/app/models/queued_listing.rb +26 -0
  56. data/app/models/roof_type.rb +3 -0
  57. data/app/models/room.rb +4 -0
  58. data/app/models/room_category.rb +2 -0
  59. data/app/models/school.rb +3 -0
  60. data/app/models/school_category.rb +2 -0
  61. data/app/models/source_provider_category.rb +2 -0
  62. data/app/models/tax.rb +3 -0
  63. data/app/models/video.rb +3 -0
  64. data/app/models/view_type.rb +3 -0
  65. data/app/models/virtual_tour.rb +3 -0
  66. data/app/views/syndication/rets.xml.erb +325 -0
  67. data/lib/class_extensions.rb +53 -0
  68. data/lib/generators/reso/install_generator.rb +43 -0
  69. data/lib/generators/reso/setup.rb +36 -0
  70. data/lib/generators/reso/templates/create_addresses.rb +33 -0
  71. data/lib/generators/reso/templates/create_businesses.rb +19 -0
  72. data/lib/generators/reso/templates/create_enumerations.rb +11 -0
  73. data/lib/generators/reso/templates/create_expenses.rb +13 -0
  74. data/lib/generators/reso/templates/create_imports.rb +17 -0
  75. data/lib/generators/reso/templates/create_join_tables.rb +27 -0
  76. data/lib/generators/reso/templates/create_listing_media.rb +19 -0
  77. data/lib/generators/reso/templates/create_listing_providers.rb +12 -0
  78. data/lib/generators/reso/templates/create_listings.rb +154 -0
  79. data/lib/generators/reso/templates/create_multiple_listing_services.rb +11 -0
  80. data/lib/generators/reso/templates/create_offices.rb +29 -0
  81. data/lib/generators/reso/templates/create_open_houses.rb +18 -0
  82. data/lib/generators/reso/templates/create_participant_licenses.rb +13 -0
  83. data/lib/generators/reso/templates/create_participants.rb +26 -0
  84. data/lib/generators/reso/templates/create_people.rb +18 -0
  85. data/lib/generators/reso/templates/create_places.rb +20 -0
  86. data/lib/generators/reso/templates/create_prices.rb +19 -0
  87. data/lib/generators/reso/templates/create_queued_listings.rb +10 -0
  88. data/lib/generators/reso/templates/create_rooms.rb +10 -0
  89. data/lib/generators/reso/templates/create_schools.rb +13 -0
  90. data/lib/generators/reso/templates/create_taxes.rb +12 -0
  91. data/lib/generators/reso/templates/enumerations.csv +748 -0
  92. data/lib/mapper/reso.rb +658 -0
  93. data/lib/mapper.rb +114 -0
  94. data/lib/reso/engine.rb +13 -0
  95. data/lib/reso/railties/reso.rake +128 -0
  96. data/lib/reso/version.rb +3 -0
  97. data/lib/reso.rb +10 -0
  98. data/reso.gemspec +31 -0
  99. metadata +261 -0
@@ -0,0 +1,658 @@
1
+ module Mapper
2
+ module Reso
3
+
4
+ def self.method_missing method_name, *args
5
+ args[1][method_name]
6
+ end
7
+
8
+ def self.address queued_listing, listing
9
+ if (result = get_value(queued_listing, %w(Address)))
10
+ listing.address ||= Address.new
11
+ listing.address.assign_attributes({
12
+ preference_order: result.drilldown('preference_order'),
13
+ address_preference_order: result.drilldown('address_preference_order'),
14
+ full_street_address: result.drilldown('FullStreetAddress'),
15
+ unit_number: result.drilldown('UnitNumber'),
16
+ city: result.drilldown('City'),
17
+ state_or_province: result.drilldown('StateOrProvince'),
18
+ postal_code: result.drilldown('PostalCode'),
19
+ country: result.drilldown('Country')
20
+ })
21
+ listing.address
22
+ end
23
+ end
24
+
25
+ def self.alternate_prices queued_listing, listing
26
+ if (result = get_repeaters(queued_listing, %w(AlternatePrices AlternatePrice)))
27
+ result.map{|item| listing.alternate_prices.find_or_initialize_by(
28
+ list_price: item.drilldown('AlternateListPrice'),
29
+ currency_code: item.drilldown('AlternateListPrice currencyCode'),
30
+ list_price_low: item.drilldown('AlternateListPriceLow'),
31
+ currency_code_low: item.drilldown('AlternateListPriceLow currencyCode')
32
+ )}
33
+ end
34
+ end
35
+
36
+ def self.appliances queued_listing, listing
37
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics Appliances Appliance))) ? result : nil
38
+ end
39
+
40
+ def self.architecture_style queued_listing, listing
41
+ (result = unwrap_attribute(get_value(queued_listing, %w(DetailedCharacteristics ArchitectureStyle)))) ? ArchitectureStyle.find_by_name(result) : nil
42
+ end
43
+
44
+ def self.architecture_style_description queued_listing, listing
45
+ (result = get_value(queued_listing, %w(DetailedCharacteristics ArchitectureStyle otherDescription))) ? result : nil
46
+ end
47
+
48
+ def self.bathrooms queued_listing, listing
49
+ (result = get_value(queued_listing, %w(Bathrooms))) ? result.to_i : nil
50
+ end
51
+
52
+ def self.bedrooms queued_listing, listing
53
+ (result = get_value(queued_listing, %w(Bedrooms))) ? result.to_i : nil
54
+ end
55
+
56
+ def self.brokerage queued_listing, listing
57
+ get_reso_business(queued_listing, 'Brokerage')
58
+ end
59
+
60
+ def self.builder queued_listing, listing
61
+ get_reso_business(queued_listing, 'Builder')
62
+ end
63
+
64
+ def self.building_unit_count queued_listing, listing
65
+ (result = get_value(queued_listing, %w(DetailedCharacteristics BuildingUnitCount))) ? result.to_i : nil
66
+ end
67
+
68
+ def self.condo_floor_num queued_listing, listing
69
+ (result = get_value(queued_listing, %w(DetailedCharacteristics CondoFloorNum))) ? result.to_i : nil
70
+ end
71
+
72
+ def self.cooling_systems queued_listing, listing
73
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics CoolingSystems CoolingSystem))) ? result : nil
74
+ end
75
+
76
+ def self.county queued_listing, listing
77
+ if (get_value(queued_listing, %w(Location County)))
78
+ County.find_or_initialize_by(
79
+ name: get_value(queued_listing, %w(Location County)),
80
+ state_or_province: get_value(queued_listing, %w(Address StateOrProvince)),
81
+ country: get_value(queued_listing, %w(Address Country))
82
+ )
83
+ else
84
+ return nil
85
+ end
86
+ end
87
+
88
+ def self.currency_code queued_listing, listing
89
+ (result = get_value(queued_listing, %w(ListPrice currencyCode))) ? result : nil
90
+ end
91
+
92
+ def self.directions queued_listing, listing
93
+ (result = get_value(queued_listing, %w(Location Directions))) ? result : nil
94
+ end
95
+
96
+ def self.disclaimer queued_listing, listing
97
+ (result = get_value(queued_listing, %w(Disclaimer))) ? result : nil
98
+ end
99
+
100
+ def self.disclose_address queued_listing, listing
101
+ (result = get_value(queued_listing, %w(DiscloseAddress))) ? result.to_s.to_bool : nil
102
+ end
103
+
104
+ def self.elevation queued_listing, listing
105
+ (result = get_value(queued_listing, %w(Location Elevation))) ? result : nil
106
+ end
107
+
108
+ def self.expenses queued_listing, listing
109
+ if (result = get_repeaters(queued_listing, %w(Expenses Expense)))
110
+ result.map{|item| listing.expenses.find_or_initialize_by(
111
+ expense_category: ExpenseCategory.find_by_name(item['ExpenseCategory']),
112
+ currency_period: CurrencyPeriod.find_by_name(item['ExpenseValue']['currencyPeriod']),
113
+ expense_value: unwrap_attribute(item['ExpenseValue'])
114
+ )}
115
+ end
116
+ end
117
+
118
+ def self.exterior_types queued_listing, listing
119
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics ExteriorTypes ExteriorType))) ? result : nil
120
+ end
121
+
122
+ def self.floor_coverings queued_listing, listing
123
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics FloorCoverings FloorCovering))) ? result : nil
124
+ end
125
+
126
+ def self.foreclosure_status queued_listing, listing
127
+ (result = get_value(queued_listing, %w(ForeclosureStatus))) ? ForeclosureStatus.find_by(name: result) : nil
128
+ end
129
+
130
+ def self.franchise queued_listing, listing
131
+ get_reso_business(queued_listing, 'Franchise')
132
+ end
133
+
134
+ def self.full_bathrooms queued_listing, listing
135
+ (result = get_value(queued_listing, %w(FullBathrooms))) ? result.to_i : nil
136
+ end
137
+
138
+ def self.geocode_options queued_listing, listing
139
+ (result = get_value(queued_listing, %w(Location GeocodeOptions))) ? result : nil
140
+ end
141
+
142
+ def self.half_bathrooms queued_listing, listing
143
+ (result = get_value(queued_listing, %w(HalfBathrooms))) ? result.to_i : nil
144
+ end
145
+
146
+ def self.has_attic queued_listing, listing
147
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasAttic))
148
+ end
149
+
150
+ def self.has_barbecue_area queued_listing, listing
151
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasBarbecueArea))
152
+ end
153
+
154
+ def self.has_basement queued_listing, listing
155
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasBasement))
156
+ end
157
+
158
+ def self.has_ceiling_fan queued_listing, listing
159
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasCeilingFan))
160
+ end
161
+
162
+ def self.has_deck queued_listing, listing
163
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasDeck))
164
+ end
165
+
166
+ def self.has_disabled_access queued_listing, listing
167
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasDisabledAccess))
168
+ end
169
+
170
+ def self.has_dock queued_listing, listing
171
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasDock))
172
+ end
173
+
174
+ def self.has_doorman queued_listing, listing
175
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasDoorman))
176
+ end
177
+
178
+ def self.has_double_pane_windows queued_listing, listing
179
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasDoublePaneWindows))
180
+ end
181
+
182
+ def self.has_elevator queued_listing, listing
183
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasElevator))
184
+ end
185
+
186
+ def self.has_fireplace queued_listing, listing
187
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasFireplace))
188
+ end
189
+
190
+ def self.has_garden queued_listing, listing
191
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasGarden))
192
+ end
193
+
194
+ def self.has_gated_entry queued_listing, listing
195
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasGatedEntry))
196
+ end
197
+
198
+ def self.has_greenhouse queued_listing, listing
199
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasGreenhouse))
200
+ end
201
+
202
+ def self.has_hot_tub_spa queued_listing, listing
203
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasHotTubSpa))
204
+ end
205
+
206
+ def self.has_intercom queued_listing, listing
207
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics Intercom))
208
+ end
209
+
210
+ def self.has_jetted_bath_tub queued_listing, listing
211
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasJettedBathTub))
212
+ end
213
+
214
+ def self.has_lawn queued_listing, listing
215
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasLawn))
216
+ end
217
+
218
+ def self.has_mother_in_law queued_listing, listing
219
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasMotherInLaw))
220
+ end
221
+
222
+ def self.has_patio queued_listing, listing
223
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasPatio))
224
+ end
225
+
226
+ def self.has_pond queued_listing, listing
227
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasPond))
228
+ end
229
+
230
+ def self.has_pool queued_listing, listing
231
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasPool))
232
+ end
233
+
234
+ def self.has_porch queued_listing, listing
235
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasPorch))
236
+ end
237
+
238
+ def self.has_rv_parking queued_listing, listing
239
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasRVParking))
240
+ end
241
+
242
+ def self.has_sauna queued_listing, listing
243
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasSauna))
244
+ end
245
+
246
+ def self.has_security_system queued_listing, listing
247
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasSecuritySystem))
248
+ end
249
+
250
+ def self.has_skylight queued_listing, listing
251
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasSkylight))
252
+ end
253
+
254
+ def self.has_sports_court queued_listing, listing
255
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasSportsCourt))
256
+ end
257
+
258
+ def self.has_sprinkler_system queued_listing, listing
259
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasSprinklerSystem))
260
+ end
261
+
262
+ def self.has_vaulted_ceiling queued_listing, listing
263
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasVaultedCeiling))
264
+ end
265
+
266
+ def self.has_wet_bar queued_listing, listing
267
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics HasWetBar))
268
+ end
269
+
270
+ def self.heating_fuels queued_listing, listing
271
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics HeatingFuels HeatingFuel))) ? result : nil
272
+ end
273
+
274
+ def self.heating_systems queued_listing, listing
275
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics HeatingSystems HeatingSystem))) ? result : nil
276
+ end
277
+
278
+ def self.is_cable_ready queued_listing, listing
279
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics IsCableReady))
280
+ end
281
+
282
+ def self.is_new_construction queued_listing, listing
283
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics IsNewConstruction))
284
+ end
285
+
286
+ def self.is_waterfront queued_listing, listing
287
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics IsWaterfront))
288
+ end
289
+
290
+ def self.is_wired queued_listing, listing
291
+ get_boolean_value(queued_listing, %w(DetailedCharacteristics IsWired))
292
+ end
293
+
294
+ def self.latitude queued_listing, listing
295
+ (result = get_value(queued_listing, %w(Location Latitude))) ? result.to_d : nil
296
+ end
297
+
298
+ def self.lead_routing_email queued_listing, listing
299
+ (result = get_value(queued_listing, %w(LeadRoutingEmail))) ? result : nil
300
+ end
301
+
302
+ def self.legal_description queued_listing, listing
303
+ (result = get_value(queued_listing, %w(DetailedCharacteristics LegalDescription))) ? result : nil
304
+ end
305
+
306
+ def self.list_price queued_listing, listing
307
+ (result = get_value(queued_listing, %w(ListPrice)).unwrap_attribute) ? result : nil
308
+ end
309
+
310
+ def self.list_price_low queued_listing, listing
311
+ (result = get_value(queued_listing, %w(ListPriceLow)).unwrap_attribute) ? result : nil
312
+ end
313
+
314
+ def self.listing_category queued_listing, listing
315
+ (result = get_value(queued_listing, %w(ListingCategory))) ? ListingCategory.find_by_name(result) : nil
316
+ end
317
+
318
+ def self.listing_date queued_listing, listing
319
+ (result = get_value(queued_listing, %w(ListingDate))) ? Chronic::parse(result).to_date : nil
320
+ end
321
+
322
+ def self.listing_description queued_listing, listing
323
+ (result = unwrap_attribute(get_value(queued_listing, %w(ListingDescription)))) ? result : nil
324
+ end
325
+
326
+ def self.listing_media queued_listing, listing, elements
327
+ if (result = get_repeaters(queued_listing, elements))
328
+ result.map do |item|
329
+ media = listing.send(elements.last.tableize).find_or_initialize_by(
330
+ media_url: item.drilldown('MediaURL'),
331
+ media_modification_timestamp: item.drilldown('MediaModificationTimestamp')
332
+ )
333
+ media.assign_attributes({
334
+ media_order_number: item.drilldown('MediaOrderNumber'),
335
+ media_caption: item.drilldown('MediaCaption'),
336
+ media_description: item.drilldown('MediaDescription')
337
+ })
338
+ media
339
+ end
340
+ end
341
+ end
342
+
343
+ # TODO: Make ListingProvider and SourceProviderCategory Provider and ProviderCategory
344
+ def self.listing_provider queued_listing, listing
345
+ (result = ListingProvider.find_or_initialize_by(
346
+ name: get_value(queued_listing, %w(ProviderName)),
347
+ url: get_value(queued_listing, %w(ProviderURL)),
348
+ source_provider_category: SourceProviderCategory.find_by_name(get_value(queued_listing, %w(ProviderCategory)))
349
+ )) ? result : nil
350
+ end
351
+
352
+ def self.listing_status queued_listing, listing
353
+ (result = get_value(queued_listing, %w(ListingStatus))) ? ListingStatus.find_by_name(result) : nil
354
+ end
355
+
356
+ def self.listing_title queued_listing, listing
357
+ (result = get_value(queued_listing, %w(ListingTitle))) ? result : nil
358
+ end
359
+
360
+ def self.listing_url queued_listing, listing
361
+ (result = get_value(queued_listing, %w(ListingURL))) ? result : nil
362
+ end
363
+
364
+ def self.living_area queued_listing, listing
365
+ (result = get_value(queued_listing, %w(LivingArea))) ? result.to_i : nil
366
+ end
367
+
368
+ def self.living_area_unit queued_listing, listing
369
+ (result = get_value(queued_listing, %w(LivingArea areaUnits))) ? result : nil
370
+ end
371
+
372
+ def self.longitude queued_listing, listing
373
+ (result = get_value(queued_listing, %w(Location Longitude))) ? result.to_d : nil
374
+ end
375
+
376
+ def self.lot_size queued_listing, listing
377
+ (result = get_value(queued_listing, %w(LotSize))) ? result : nil
378
+ end
379
+
380
+ def self.mls_number queued_listing, listing
381
+ (result = get_value(queued_listing, %w(MlsNumber))) ? result : nil
382
+ end
383
+
384
+ def self.modification_timestamp queued_listing, listing
385
+ # TODO: Change from string to datetime
386
+ (result = get_value(queued_listing, %w(ModificationTimestamp))) ? result : nil
387
+ end
388
+
389
+ def self.multiple_listing_service queued_listing, listing
390
+ if (get_value(queued_listing, %w(MlsId)) || get_value(queued_listing, %w(MlsName)))
391
+ MultipleListingService.find_or_initialize_by(
392
+ mls_id: get_value(queued_listing, %w(MlsId)),
393
+ mls_name: get_value(queued_listing, %w(MlsName))
394
+ )
395
+ end
396
+ end
397
+
398
+ def self.neighborhoods queued_listing, listing
399
+ if (result = get_value(queued_listing, %w(Location Neighborhoods Neighborhood)))
400
+ places = result.map do |item|
401
+ place = Neighborhood.find_or_initialize_by(
402
+ city: get_value(queued_listing, %w(Address City)),
403
+ name: item.drilldown('Name'),
404
+ state_or_province: get_value(queued_listing, %w(Address StateOrProvince))
405
+ )
406
+ place.description = item.drilldown('Description')
407
+ place
408
+ end
409
+ end
410
+ places ? places : []
411
+ end
412
+
413
+ def self.num_floors queued_listing, listing
414
+ (result = get_value(queued_listing, %w(DetailedCharacteristics NumFloors))) ? result.to_i : nil
415
+ end
416
+
417
+ def self.num_parking_spaces queued_listing, listing
418
+ (result = get_value(queued_listing, %w(DetailedCharacteristics NumParkingSpaces))) ? result.to_i : nil
419
+ end
420
+
421
+ def self.office queued_listing, listing
422
+ if (result = get_value(queued_listing, %w(Offices Office)))
423
+ office = Office.find_or_initialize_by(
424
+ office_identifier: result.drilldown('OfficeId'),
425
+ name: result.drilldown('Name')
426
+ )
427
+ office.assign_attributes({
428
+ office_key: result.drilldown('OfficeKey'),
429
+ level: result.drilldown('Level'),
430
+ office_code_identifier: result.drilldown('OfficeCode OfficeCodeId'),
431
+ corporate_name: result.drilldown('CorporateName'),
432
+ broker_identifier: result.drilldown('BrokerId'),
433
+ phone_number: result.drilldown('PhoneNumber'),
434
+ website: result.drilldown('Website'),
435
+ })
436
+ office.address ||= Address.new
437
+ office.address.assign_attributes({
438
+ preference_order: result.drilldown('Address preference_order'),
439
+ address_preference_order: result.drilldown('Address address_preference_order'),
440
+ full_street_address: result.drilldown('Address FullStreetAddress'),
441
+ unit_number: result.drilldown('Address UnitNumber'),
442
+ city: result.drilldown('Address City'),
443
+ state_or_province: result.drilldown('Address StateOrProvince'),
444
+ postal_code: result.drilldown('Address PostalCode'),
445
+ country: result.drilldown('Address Country')
446
+ })
447
+ office
448
+ end
449
+ end
450
+
451
+ def self.one_quarter_bathrooms queued_listing, listing
452
+ (result = get_value(queued_listing, %w(OneQuarterBathrooms))) ? result.to_i : nil
453
+ end
454
+
455
+ # TODO: Figure out how to provide time zone for StartTime and EndTime
456
+ def self.open_houses queued_listing, listing
457
+ if (result = get_repeaters(queued_listing, %w(OpenHouses OpenHouse)))
458
+ result.map{|open_house| listing.open_houses.find_or_initialize_by(
459
+ showing_date: Chronic::parse(open_house['Date']).to_date,
460
+ start_time: open_house['StartTime'],
461
+ end_time: open_house['EndTime'],
462
+ description: open_house['Description']
463
+ )}
464
+ end
465
+ end
466
+
467
+ def self.parcel_id queued_listing, listing
468
+ (result = get_value(queued_listing, %w(Location ParcelId))) ? result : nil
469
+ end
470
+
471
+ # TODO: Change participant_identifier to participant_id
472
+ def self.participants queued_listing, listing
473
+ if (result = get_repeaters(queued_listing, %w(ListingParticipants Participant)))
474
+ result.map do |item|
475
+ participant = Participant.find_or_initialize_by(
476
+ first_name: item['FirstName'],
477
+ last_name: item['LastName'],
478
+ email: item['Email']
479
+ )
480
+ participant.assign_attributes({
481
+ participant_key: item['ParticipantKey'],
482
+ participant_identifier: item['ParticipantId'],
483
+ participant_role: ParticipantRole.find_by(name: item['Role']),
484
+ primary_contact_phone: item['PrimaryContactPhone'],
485
+ office_phone: item['OfficePhone'],
486
+ fax: item['Fax'],
487
+ website_url: item['WebsiteURL']
488
+ })
489
+ participant
490
+ end
491
+ end
492
+ end
493
+
494
+ def self.permit_address_on_internet queued_listing, listing
495
+ (result = get_value(queued_listing, %w(MarketingInformation PermitAddressOnInternet))) ? result.to_s.to_bool : nil
496
+ end
497
+
498
+ def self.photos queued_listing, listing
499
+ (result = listing_media(queued_listing, listing, %w(Photos Photo))) ? result : nil
500
+ end
501
+
502
+ def self.property_sub_type queued_listing, listing
503
+ (result = unwrap_attribute(get_value(queued_listing, %w(PropertySubType)))) ? PropertySubType.find_by_name(result) : nil
504
+ end
505
+
506
+ def self.property_sub_type_description queued_listing, listing
507
+ (result = get_value(queued_listing, %w(PropertySubType otherDescription))) ? result : nil
508
+ end
509
+
510
+ def self.property_type queued_listing, listing
511
+ (result = unwrap_attribute(get_value(queued_listing, %w(PropertyType)))) ? PropertyType.find_by_name(result) : nil
512
+ end
513
+
514
+ def self.property_type_description queued_listing, listing
515
+ (result = get_value(queued_listing, %w(PropertyType otherDescription))) ? result : nil
516
+ end
517
+
518
+ def self.roof_types queued_listing, listing
519
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics RoofTypes RoofType))) ? result : nil
520
+ end
521
+
522
+ def self.room_count queued_listing, listing
523
+ (result = get_value(queued_listing, %w(DetailedCharacteristics RoomCount))) ? result.to_i : nil
524
+ end
525
+
526
+ def self.rooms queued_listing, listing
527
+ if (result = get_value(queued_listing, %w(DetailedCharacteristics Rooms Room)))
528
+ rooms = Array(result).map{|room_category| Room.new(listing: listing, room_category: RoomCategory.find_by(name: room_category))}
529
+ end
530
+ rooms ? rooms : []
531
+ end
532
+
533
+ def self.taxes queued_listing, listing
534
+ if (result = get_repeaters(queued_listing, %w(Taxes Tax)))
535
+ result.map{|item| Tax.find_or_initialize_by(
536
+ year: item['Year'],
537
+ amount: item['Amount'],
538
+ description: item['TaxDescription']
539
+ )}
540
+ end
541
+ end
542
+
543
+ def self.three_quarter_bathrooms queued_listing, listing
544
+ (result = get_value(queued_listing, %w(ThreeQuarterBathrooms))) ? result.to_i : nil
545
+ end
546
+
547
+ def self.videos queued_listing, listing
548
+ (result = listing_media(queued_listing, listing, %w(Videos Video))) ? result : nil
549
+ end
550
+
551
+ def self.view_types queued_listing, listing
552
+ (result = get_enums(queued_listing, %w(DetailedCharacteristics ViewTypes ViewType))) ? result : nil
553
+ end
554
+
555
+ def self.virtual_tours queued_listing, listing
556
+ (result = listing_media(queued_listing, listing, %w(VirtualTours VirtualTour))) ? result : nil
557
+ end
558
+
559
+ def self.vow_address_display queued_listing, listing
560
+ get_boolean_value(queued_listing, %w(MarketingInformation VOWAutomatedValuationDisplay'))
561
+ end
562
+
563
+ def self.vow_automated_valuation_display queued_listing, listing
564
+ get_boolean_value(queued_listing, %w(MarketingInformation VOWAddressDisplay'))
565
+ end
566
+
567
+ def self.vow_consumer_comment queued_listing, listing
568
+ get_boolean_value(queued_listing, %w(MarketingInformation VOWConsumerComment'))
569
+ end
570
+
571
+ def self.year_built queued_listing, listing
572
+ (result = get_value(queued_listing, %w(YearBuilt))) ? result.to_i : nil
573
+ end
574
+
575
+ def self.year_updated queued_listing, listing
576
+ (result = get_value(queued_listing, %w(DetailedCharacteristics YearUpdated))) ? result.to_s.to_i : nil
577
+ end
578
+
579
+ # Utilities
580
+
581
+ def self.get_reso_business queued_listing, business_type
582
+ if (result = get_value(queued_listing, [business_type]))
583
+ business = business_type.constantize.find_or_initialize_by(
584
+ name: result.drilldown('Name'),
585
+ phone: result.drilldown('Phone')
586
+ )
587
+ business.assign_attributes({
588
+ email: result.drilldown('Email'),
589
+ logo_url: result.drilldown('LogoURL'),
590
+ website_url: result.drilldown('WebsiteURL')
591
+ })
592
+ business.address ||= Address.new
593
+ business.address.assign_attributes({
594
+ preference_order: result.drilldown('Address preference_order'),
595
+ address_preference_order: result.drilldown('Address address_preference_order'),
596
+ full_street_address: result.drilldown('Address FullStreetAddress'),
597
+ unit_number: result.drilldown('Address UnitNumber'),
598
+ city: result.drilldown('Address City'),
599
+ state_or_province: result.drilldown('Address StateOrProvince'),
600
+ postal_code: result.drilldown('Address PostalCode'),
601
+ country: result.drilldown('Address Country')
602
+ })
603
+ business
604
+ end
605
+ end
606
+
607
+ def self.unique_identifier queued_listing
608
+ (result = get_value(queued_listing, queued_listing.import.unique_identifier.split(' ')))
609
+ end
610
+
611
+ def self.get_boolean_value queued_listing, elements
612
+ (result = get_value(queued_listing, elements)) ? result.to_s.to_bool : nil
613
+ end
614
+
615
+ def self.unwrap_attribute value
616
+ value.is_a?(Hash) ? (value['__content__'] ? value['__content__'] : value) : value
617
+ end
618
+
619
+ def self.get_repeaters queued_listing, elements
620
+ if (value = get_value(queued_listing, elements[0..-2]))
621
+ (result = value.drilldown(elements.last)) ? (result.is_a?(Array) ? result : [result]) : nil
622
+ else
623
+ return []
624
+ end
625
+ end
626
+
627
+ # TODO: Should be find_by_name not find_or_create_by_name. Remove after before_save validation.
628
+ def self.get_enums queued_listing, elements
629
+ if (result = get_repeaters(queued_listing, elements))
630
+ enums = result.map{|name| elements.last.constantize.find_or_create_by(name: name)}
631
+ end
632
+ enums ? enums : nil
633
+ end
634
+
635
+ def self.get_value queued_listing, elements
636
+ if elements.count.eql?(1)
637
+ get_simple_value(queued_listing, elements.first)
638
+ else
639
+ get_subvalue(queued_listing, elements.first, elements[1..-1])
640
+ end
641
+ end
642
+
643
+ def self.get_simple_value queued_listing, element
644
+ if (value = queued_listing.listing_data[element])
645
+ value.unwrap_attribute
646
+ end
647
+ end
648
+
649
+ def self.get_subvalue queued_listing, element, child_elements
650
+ if (value = get_simple_value(queued_listing, element))
651
+ if (subvalue = child_elements.inject(value){|v, e| v[e] ? v[e] : Hash.new })
652
+ subvalue.present? ? subvalue.unwrap_attribute : nil
653
+ end
654
+ end
655
+ end
656
+
657
+ end
658
+ end