allhomes_xml 0.1.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.
- data/.document +5 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +157 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/allhomes_xml.gemspec +79 -0
- data/lib/allhomes.xsd +2141 -0
- data/lib/allhomes_xml.rb +4 -0
- data/lib/allhomes_xml/agents.rb +17 -0
- data/lib/allhomes_xml/array.rb +0 -0
- data/lib/allhomes_xml/attributes.rb +7 -0
- data/lib/allhomes_xml/basic_listing.rb +31 -0
- data/lib/allhomes_xml/basic_price.rb +15 -0
- data/lib/allhomes_xml/exhibitions.rb +7 -0
- data/lib/allhomes_xml/generator.rb +416 -0
- data/lib/allhomes_xml/photos.rb +7 -0
- data/lib/allhomes_xml/price/auction.rb +11 -0
- data/lib/allhomes_xml/price/eoi.rb +11 -0
- data/lib/allhomes_xml/price/fixed.rb +11 -0
- data/lib/allhomes_xml/price/range.rb +11 -0
- data/lib/allhomes_xml/price/tender.rb +11 -0
- data/lib/allhomes_xml/rental_listing.rb +10 -0
- data/lib/allhomes_xml/sale_listing.rb +11 -0
- data/lib/allhomes_xml/validator.rb +36 -0
- data/test/helper.rb +18 -0
- data/test/test_allhomes_xml.rb +7 -0
- metadata +154 -0
data/lib/allhomes_xml.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module AllhomesXml
|
2
|
+
|
3
|
+
# Storage container for agents - primary and secondary(optional)
|
4
|
+
class Agents
|
5
|
+
|
6
|
+
attr_accessor :primary_agent, :secondary_agent
|
7
|
+
|
8
|
+
def primary hash
|
9
|
+
self.primary_agent = hash
|
10
|
+
end
|
11
|
+
def secondary hash
|
12
|
+
self.secondary_agent = hash
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module AllhomesXml
|
2
|
+
|
3
|
+
# Creates a template listing for use by higher level classes
|
4
|
+
class BasicListing
|
5
|
+
|
6
|
+
# All generic attributes common to all listings
|
7
|
+
attr_accessor :id, :property_type, :agency_id, :agents, :unit_number, :street_number, :street_name, :street_type, :complex, :suburb, :postcode, :state, :country, :hide_address, :title, :description, :eer, :photos, :price, :exhibitions, :bedrooms, :bathrooms, :ensuites, :carspaces, :garagespaces, :attributes
|
8
|
+
|
9
|
+
def required
|
10
|
+
[:id, :agents, :suburb, :property_type, :postcode]
|
11
|
+
end
|
12
|
+
|
13
|
+
# Creates a new basic listing - takes a hash full of attributes as an argument
|
14
|
+
def initialize(args)
|
15
|
+
|
16
|
+
args.each_pair do |attribute, value|
|
17
|
+
|
18
|
+
# Remove junk off the beginning/end of strings
|
19
|
+
value.strip! if value.instance_of? String and not attribute == :description
|
20
|
+
|
21
|
+
send "#{attribute}=", value
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
required.each { |field| raise "#{field} field cannot be blank - property #{id}!" if self.send(field).blank? }
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,416 @@
|
|
1
|
+
module AllhomesXml
|
2
|
+
|
3
|
+
class Generator
|
4
|
+
|
5
|
+
COMMERCIAL_PROPERTY_TYPES = %w(office hotel industrial motel retail warehouse)
|
6
|
+
|
7
|
+
# Creates a new XML class to house all data
|
8
|
+
# Takes an Allhomes username and password hash as it's argument
|
9
|
+
def initialize(args)
|
10
|
+
|
11
|
+
@username = args[:username]
|
12
|
+
@password = args[:password]
|
13
|
+
@properties = []
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
def << property
|
18
|
+
@properties << property
|
19
|
+
end
|
20
|
+
|
21
|
+
# Generates the XML
|
22
|
+
def generate
|
23
|
+
|
24
|
+
require 'builder'
|
25
|
+
|
26
|
+
xml = Builder::XmlMarkup.new :indent => 4
|
27
|
+
|
28
|
+
xml.instruct! :xml, :version => "1.0"
|
29
|
+
|
30
|
+
time = Time.now
|
31
|
+
xml.listings({:feedId => time.to_i,
|
32
|
+
:generated => time.xmlschema,
|
33
|
+
:username => @username,
|
34
|
+
:password => @password,
|
35
|
+
:xmlns => "http://www.allhomes.com.au/2008/v1/listingFeed",
|
36
|
+
:'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
|
37
|
+
:'xsi:schemaLocation' => "http://www.allhomes.com.au/2008/v1/listingFeed listingFeed.xsd "}) do
|
38
|
+
|
39
|
+
|
40
|
+
@properties.each do |property|
|
41
|
+
|
42
|
+
# Set up a quick variable for checking if this property is a sale listing
|
43
|
+
property.class.name == "AllhomesXml::SaleListing" ? sale_listing = true : sale_listing = false
|
44
|
+
|
45
|
+
if sale_listing
|
46
|
+
|
47
|
+
# Attributes for the initial tag
|
48
|
+
listingAttributes = { :saleStatus => get_sale_status(property),
|
49
|
+
:uniqueId => "S#{property.id}"}
|
50
|
+
|
51
|
+
# If this property is sold, add the attributes to the initial tag
|
52
|
+
if property.exchange_date and property.sale_price
|
53
|
+
listingAttributes.update({:saleDate => property.exchange_date.strftime("%Y-%m-%d"), :salePrice => property.sale_price})
|
54
|
+
end
|
55
|
+
|
56
|
+
else
|
57
|
+
|
58
|
+
# Attributes for the initial tag
|
59
|
+
listingAttributes = { :rentalStatus => get_rent_status(property),
|
60
|
+
:uniqueId => "R#{property.id}"}
|
61
|
+
|
62
|
+
# If the property is rented, add the rented date and price
|
63
|
+
if property.rented and property.rented_date and property.rented_price
|
64
|
+
listingAttributes.update({:rentedDate => property.rented_date.strftime("%Y-%m-%d"), :rentedPrice => property.rented_price})
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
# Generate initial tag
|
70
|
+
if COMMERCIAL_PROPERTY_TYPES.include? property.property_type
|
71
|
+
sale_listing ? opening_tag = "commercialSaleListing" : opening_tag = "commercialRentalListing"
|
72
|
+
else
|
73
|
+
sale_listing ? opening_tag = "residentialSaleListing" : opening_tag = "residentialRentalListing"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Opening Tag
|
77
|
+
######################################################
|
78
|
+
xml.tag!(opening_tag, listingAttributes) do
|
79
|
+
|
80
|
+
# Agent Details
|
81
|
+
######################################################
|
82
|
+
xml.agentDetails do
|
83
|
+
xml.agencyId property.agency_id
|
84
|
+
xml.primaryContact do
|
85
|
+
xml.firstName property.agents.primary_agent[:firstname]
|
86
|
+
xml.lastName property.agents.primary_agent[:surname]
|
87
|
+
end
|
88
|
+
|
89
|
+
if sale_listing
|
90
|
+
if property.agents.secondary_agent
|
91
|
+
xml.alternativeContact do
|
92
|
+
xml.firstName property.agents.secondary_agent[:firstname]
|
93
|
+
xml.lastName property.agents.secondary_agent[:surname]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
# Street Address
|
101
|
+
######################################################
|
102
|
+
xml.streetAddress do
|
103
|
+
|
104
|
+
xml.unitNumber property.unit_number unless property.unit_number.blank?
|
105
|
+
xml.streetNumber property.street_number unless (property.street_number =~ /^[&\w\- ]+$/).nil?
|
106
|
+
xml.streetName property.street_name
|
107
|
+
xml.streetType property.street_type.upcase
|
108
|
+
xml.propertyName property.complex unless property.complex.blank?
|
109
|
+
xml.suburb property.suburb
|
110
|
+
xml.postCode property.postcode
|
111
|
+
xml.state property.state
|
112
|
+
xml.country "Australia"
|
113
|
+
|
114
|
+
if property.hide_address
|
115
|
+
xml.addressVisibility "HIDE_STREET_NUMBER_AND_NAME"
|
116
|
+
else
|
117
|
+
xml.addressVisibility "VISIBLE"
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
# Formal Address - Sale Only
|
123
|
+
######################################################
|
124
|
+
if sale_listing and property.state == 'ACT'
|
125
|
+
|
126
|
+
xml.formalAddress do
|
127
|
+
|
128
|
+
xml.blockSection do
|
129
|
+
|
130
|
+
if (property.block =~ /[^0-9]/).nil?
|
131
|
+
xml.block property.block
|
132
|
+
else
|
133
|
+
xml.block
|
134
|
+
end
|
135
|
+
xml.section property.section
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
# end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
# Title and Description
|
146
|
+
######################################################
|
147
|
+
xml.title property.title
|
148
|
+
if property.description.blank?
|
149
|
+
xml.description 'No description available.'
|
150
|
+
else
|
151
|
+
xml.description property.description
|
152
|
+
end
|
153
|
+
|
154
|
+
# EER
|
155
|
+
######################################################
|
156
|
+
xml.eer property.eer if not property.eer.blank? and not property.eer == 'N/A'
|
157
|
+
|
158
|
+
# Street Directory Reference
|
159
|
+
######################################################
|
160
|
+
xml.streetDirectoryReference do
|
161
|
+
|
162
|
+
xml.type ""
|
163
|
+
xml.reference ""
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
# Listing Status
|
168
|
+
######################################################
|
169
|
+
xml.listingStatus "active"
|
170
|
+
|
171
|
+
# Photos
|
172
|
+
######################################################
|
173
|
+
unless property.photos.blank?
|
174
|
+
|
175
|
+
xml.images do
|
176
|
+
|
177
|
+
property.photos.each do |photo|
|
178
|
+
|
179
|
+
xml.image do
|
180
|
+
|
181
|
+
xml.imageReference photo[:url]
|
182
|
+
xml.caption photo[:caption] unless photo[:caption].blank?
|
183
|
+
xml.orderIndex photo[:order]
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
# Rental Availability - Rentals Only
|
194
|
+
######################################################
|
195
|
+
if not sale_listing and (property.available_from or property.available_to)
|
196
|
+
|
197
|
+
xml.availability do
|
198
|
+
|
199
|
+
xml.from property.available_from.strftime("%Y-%m-%d") if property.available_from
|
200
|
+
xml.to property.available_to.strftime("%Y-%m-%d") if property.available_to
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
# Sale Authority - Sale Only
|
207
|
+
######################################################
|
208
|
+
xml.saleAuthority "exclusive" if sale_listing
|
209
|
+
|
210
|
+
# Price
|
211
|
+
######################################################
|
212
|
+
if sale_listing
|
213
|
+
|
214
|
+
case property.price.class.name
|
215
|
+
|
216
|
+
# If the property is an auction
|
217
|
+
when "AllhomesXml::Price::Auction"
|
218
|
+
|
219
|
+
xml.auction do
|
220
|
+
|
221
|
+
xml.date property.price.date.strftime("%Y-%m-%d")
|
222
|
+
xml.startTime property.price.start.strftime("%H:%M:%S")
|
223
|
+
xml.unpublishedPriceEstimate property.price.estimate unless property.price.estimate.blank?
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
# If the property is by tender
|
228
|
+
when "AllhomesXml::Price::Tender"
|
229
|
+
|
230
|
+
xml.tender do
|
231
|
+
|
232
|
+
xml.closingDate property.price.date.xmlschema
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
# If the property is by Expressions of Interest
|
237
|
+
when "AllhomesXml::Price::EOI"
|
238
|
+
|
239
|
+
xml.expressionOfInterest do
|
240
|
+
|
241
|
+
xml.closingDate property.price.date.xmlschema
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
# If the property is sold by plain old price
|
246
|
+
when "AllhomesXml::Price::Range"
|
247
|
+
|
248
|
+
xml.price do
|
249
|
+
|
250
|
+
xml.lower property.price.low
|
251
|
+
xml.upper property.price.high
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
when "AllhomesXml::Price::Fixed"
|
256
|
+
|
257
|
+
xml.price do
|
258
|
+
|
259
|
+
xml.lower property.price.price
|
260
|
+
property.offers_over ? xml.offersAbove("true") : xml.offersAbove("false")
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
# If the property is by negotiation
|
265
|
+
else
|
266
|
+
|
267
|
+
xml.price ""
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
# If rental listing
|
272
|
+
else
|
273
|
+
|
274
|
+
xml.price do
|
275
|
+
|
276
|
+
xml.lower property.price unless property.price.blank?
|
277
|
+
xml.paymentPeriod "weekly" unless property.price.blank?
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
end
|
282
|
+
|
283
|
+
# Exhibitions
|
284
|
+
##################
|
285
|
+
unless property.exhibitions.blank?
|
286
|
+
|
287
|
+
xml.exhibitions do
|
288
|
+
|
289
|
+
property.exhibitions.each do |opentime|
|
290
|
+
|
291
|
+
xml.exhibition do
|
292
|
+
|
293
|
+
xml.date opentime[:date].strftime("%Y-%m-%d")
|
294
|
+
xml.startTime opentime[:start].strftime("%H:%M:%S")
|
295
|
+
xml.endTime opentime[:finish].strftime("%H:%M:%S")
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
# Rental Purpose
|
306
|
+
##################
|
307
|
+
xml.rentalPurpose 'residential' if not sale_listing and not COMMERCIAL_PROPERTY_TYPES.include?(property.property_type)
|
308
|
+
|
309
|
+
# Property Details
|
310
|
+
##################
|
311
|
+
xml.propertyDetails do
|
312
|
+
|
313
|
+
# House & Block Size
|
314
|
+
##################
|
315
|
+
xml.buildingDetails do
|
316
|
+
|
317
|
+
if sale_listing and property.floor_area and not property.floor_area.to_f == 0
|
318
|
+
|
319
|
+
xml.buildingSize property.floor_area.to_f
|
320
|
+
|
321
|
+
end
|
322
|
+
|
323
|
+
end
|
324
|
+
|
325
|
+
xml.landDetails do
|
326
|
+
|
327
|
+
xml.blockSize property.block_size if sale_listing and property.block_size and (property.block_size =~ /[^0-9.]/).nil?
|
328
|
+
|
329
|
+
end
|
330
|
+
|
331
|
+
# UV
|
332
|
+
##################
|
333
|
+
xml.unimprovedLandValue(property.uv) if sale_listing and not property.uv.blank?
|
334
|
+
|
335
|
+
# Property Type
|
336
|
+
##################
|
337
|
+
xml.propertyType property.property_type
|
338
|
+
|
339
|
+
# Features
|
340
|
+
##################
|
341
|
+
# If we're not dealing with a commercial property, output the features
|
342
|
+
unless COMMERCIAL_PROPERTY_TYPES.include? property.property_type
|
343
|
+
|
344
|
+
# Features unique to sales / rentals
|
345
|
+
features = {}
|
346
|
+
if sale_listing
|
347
|
+
features['coreArea'] = true if property.core_area
|
348
|
+
features['bodyCorporate'] = true if property.body_corporate
|
349
|
+
features['plansApproved'] = true if property.plans_approved
|
350
|
+
else
|
351
|
+
features['petsAllowed'] = true if property.pets_allowed
|
352
|
+
features['shortTermLease'] = true if property.short_term_lease
|
353
|
+
end
|
354
|
+
|
355
|
+
# Features common to both sales / rentals
|
356
|
+
features[:bedrooms] = property.bedrooms unless property.bedrooms.blank?
|
357
|
+
features[:bathrooms] = property.bathrooms unless property.bathrooms.blank?
|
358
|
+
features[:ensuites] = property.ensuites unless property.ensuites.blank?
|
359
|
+
features[:carportSpaces] = property.carspaces unless property.carspaces.blank?
|
360
|
+
features[:garageSpaces] = property.garagespaces unless property.garagespaces.blank?
|
361
|
+
|
362
|
+
# Merge extra attributes
|
363
|
+
features.update property.attributes
|
364
|
+
|
365
|
+
# Features tag
|
366
|
+
xml.features features
|
367
|
+
|
368
|
+
end
|
369
|
+
|
370
|
+
end
|
371
|
+
|
372
|
+
end
|
373
|
+
|
374
|
+
end
|
375
|
+
|
376
|
+
end
|
377
|
+
|
378
|
+
end
|
379
|
+
|
380
|
+
protected
|
381
|
+
|
382
|
+
def get_sale_status property
|
383
|
+
|
384
|
+
if property.sold
|
385
|
+
'sold'
|
386
|
+
else
|
387
|
+
if property.withdrawn
|
388
|
+
'withdrawn'
|
389
|
+
elsif property.under_offer
|
390
|
+
'underOffer'
|
391
|
+
else
|
392
|
+
'forSale'
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
end
|
397
|
+
|
398
|
+
def get_rent_status property
|
399
|
+
|
400
|
+
if property.rented
|
401
|
+
'rented'
|
402
|
+
else
|
403
|
+
if property.withdrawn
|
404
|
+
'withdrawn'
|
405
|
+
elsif property.under_application
|
406
|
+
'underApplication'
|
407
|
+
else
|
408
|
+
'forRent'
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
end
|
413
|
+
|
414
|
+
end
|
415
|
+
|
416
|
+
end
|