private static DateTime? ExtractAuction(XElement document) { document.ShouldNotBe(null); var auction = document.ValueOrDefault("auction"); // NOTE: The REA documentation is vague as to the 100% specification on this. // So i'm going to assume the following (in order) // 1. <auction>date-time-in-here</auction> // 2. <auction date="date-time-in-here"></auction> // ** YET ANOTHER FRICKING EXAMPLE OF WHY THIS SCHEMA AND XML ARE F'ING CRAP ** if (string.IsNullOrWhiteSpace(auction)) { auction = document.ValueOrDefault("auction", "date"); } return (!string.IsNullOrWhiteSpace(auction)) ? ToDateTime(auction) : null; }
private static IList<Media> ExtractFloorPlans(XElement document) { document.ShouldNotBe(null); var objectElement = document.Element("objects"); if (objectElement == null) { return null; } var floorPlanElements = objectElement.Elements("floorplan").ToArray(); if (!floorPlanElements.Any()) { return null; } var floorPlans = (from x in floorPlanElements let url = x.AttributeValueOrDefault("url") let file = x.AttributeValueOrDefault("file") let order = x.AttributeValueOrDefault("id") where !string.IsNullOrWhiteSpace(url) || !string.IsNullOrWhiteSpace(file) select new Media { Url = string.IsNullOrWhiteSpace(url) ? string.IsNullOrWhiteSpace(file) ? null : file : url, Order = Convert.ToInt32(order) }).ToList(); return floorPlans.Any() ? floorPlans : null; }
private static SalePricing ExtractSalePricing(XElement document, CultureInfo cultureInfo) { document.ShouldNotBe(null); var salePricing = new SalePricing { SalePrice = document.MoneyValueOrDefault(cultureInfo, "price") }; // Selling data. var salePriceText = document.ValueOrDefault("priceView"); var displayAttributeValue = document.ValueOrDefault("price", "display"); var isDisplay = string.IsNullOrWhiteSpace(displayAttributeValue) || displayAttributeValue.ParseOneYesZeroNoToBool(); // NOTE: If Display="no" then we do not display anything for the price, regardless // of any other data provided. Otherwise, make a decision. salePricing.SalePriceText = isDisplay ? string.IsNullOrWhiteSpace(salePriceText) ? salePricing.SalePrice.ToString("C0") : salePriceText : null; var isUnderOffer = document.ValueOrDefault("underOffer", "value"); salePricing.IsUnderOffer = !string.IsNullOrWhiteSpace(isUnderOffer) && isUnderOffer.ParseOneYesZeroNoToBool(); // Sold data. var soldDetails = document.Element("soldDetails"); if (soldDetails != null) { // SoldPrice could be price or soldPrice. Thanks REA for such a great schema. var soldPrice = soldDetails.Element("price") ?? soldDetails.Element("soldPrice"); if (soldPrice != null) { ExtractSoldPrice(soldPrice, salePricing); } var soldDate = soldDetails.Element("date") ?? soldDetails.Element("soldDate"); if (soldDate != null) { ExtractSoldOn(soldDate, salePricing); } } return salePricing; }
private static void ExtractFeatureWithTextValues(XElement document, string elementName, string[] validValues, ISet<string> tags, string delimeter = "-") { document.ShouldNotBe(null); elementName.ShouldNotBeNullOrEmpty(); validValues.ShouldNotBe(null); tags.ShouldNotBe(null); var type = document.ValueOrDefault(elementName, ("type")); if (string.IsNullOrWhiteSpace(type)) { return; } if (validValues.Contains(type, StringComparer.InvariantCultureIgnoreCase)) { tags.Add(string.Format("{0}{1}{2}", elementName, string.IsNullOrWhiteSpace(delimeter) ? string.Empty : delimeter, type)); } }
private static IList<Inspection> ExtractInspectionTimes(XElement document) { document.ShouldNotBe(null); var inspectionTimes = document.Element("inspectionTimes"); if (inspectionTimes == null) { return null; } var inspectionElements = inspectionTimes.Elements("inspection").ToList(); if (!inspectionElements.Any()) { return null; } var inspections = new List<Inspection>(); foreach (var inspectionElement in inspectionElements) { // -Some- xml data only contains empty inspects. (eg. RentalExpress). if (inspectionElement == null || inspectionElement.IsEmpty || string.IsNullOrWhiteSpace(inspectionElement.Value)) { continue; } // Only the following format is accepted as valid: DD-MON-YYYY hh:mm[am|pm] to hh:mm[am|pm] // REF: http://reaxml.realestate.com.au/docs/reaxml1-xml-format.html#inspection var data = inspectionElement.Value.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); if (data.Length < 4) { throw new Exception("Inspection element has an invald Date/Time value. Element: " + inspectionElement); } DateTime inspectionStartsOn, inspectionEndsOn; DateTime.TryParse(string.Format("{0} {1}", data[0], data[1]), out inspectionStartsOn); DateTime.TryParse(string.Format("{0} {1}", data[0], data[3]), out inspectionEndsOn); if (inspectionStartsOn == DateTime.MinValue || inspectionEndsOn == DateTime.MinValue) { throw new Exception("Inspection element has an invalid Date/Time value. Element: " + inspectionElement); } var newInspection = new Inspection { OpensOn = inspectionStartsOn, ClosesOn = inspectionEndsOn == inspectionStartsOn ? (DateTime?) null : inspectionEndsOn }; // Just to be safe, lets make sure do get dupes. if (!inspections.Contains(newInspection)) { inspections.Add(newInspection); } } return inspections.Any() ? inspections.OrderBy(x => x.OpensOn).ToList() : null; }
private static Address ExtractAddress(XElement document, string addressDelimeter) { document.ShouldNotBe(null); var addressElement = document.Element("address"); if (addressElement == null) { return null; } var address = new Address(); // Land and CommericalLand should only provide lot numbers. var lotNumber = addressElement.ValueOrDefault("lotNumber"); var subNumber = addressElement.ValueOrDefault("subNumber"); var streetNumber = addressElement.ValueOrDefault("streetNumber"); // LOGIC: // So, we're trying to create a streetnumber value that contains the rea values // Sub Number // Lot Number // Street Number // into a single value. URGH. // This is because REA have over fricking complicated shiz (again). So lets just // keep this simple, eh? :) // FORMAT: subnumber lotnumber streetnumber // eg. 23a/135 smith street // 6/23a 135 smith street // 23a lot 33 smith street // 23a lot 33/135 smith street // Lot number logic: If the value contains the word LOT in it, then we don't // need to do anything. Otherwise, we should have a value the starts with 'LOT'. // eg. LOT 123abc var lotNumberResult = string.IsNullOrWhiteSpace(lotNumber) ? string.Empty : lotNumber.IndexOf("lot", StringComparison.InvariantCultureIgnoreCase) > 0 ? lotNumber : string.Format("LOT {0}", lotNumber); // Sub number and Street number logic: A sub number can exist -before- the street number. // A street number might NOT exist, so a sub number is all by itself. // When we want to show a sub number, we probably want to show it, like this: // 'subNumber`delimeter`streetNumber` // eg. 12a/432 // But .. sometimes, the sub number -already- contains a delimeter! so then we want this: // eg. 45f/231 15 // So we don't put a delimeter in there, but a space. Urgh! confusing, so sowwy. var subNumberLotNumber = string.Format("{0} {1}", subNumber, lotNumberResult).Trim(); // We only have a delimeter if we have a sub-or-lot number **and** // a street number. // Also, we use the default delimeter if we don't have one already in the // sub-or-lot number. var delimeter = string.IsNullOrWhiteSpace(subNumberLotNumber) ? string.Empty : subNumberLotNumber.IndexOfAny(new[] {'/', '\\', '-'}) > 0 ? " " : string.IsNullOrWhiteSpace(streetNumber) ? string.Empty : addressDelimeter; address.StreetNumber = string.Format("{0}{1}{2}", subNumberLotNumber, delimeter, streetNumber).Trim(); address.Street = addressElement.ValueOrDefault("street"); address.Suburb = addressElement.ValueOrDefault("suburb"); address.State = addressElement.ValueOrDefault("state"); // REA Xml Rule: Country is ommited == default to Australia. // Reference: http://reaxml.realestate.com.au/docs/reaxml1-xml-format.html#country var country = addressElement.ValueOrDefault("country"); address.CountryIsoCode = !string.IsNullOrEmpty(country) ? ConvertCountryToIsoCode(country) : "AU"; address.Postcode = addressElement.ValueOrDefault("postcode"); var isStreetDisplayedText = addressElement.AttributeValueOrDefault("display"); address.IsStreetDisplayed = string.IsNullOrWhiteSpace(isStreetDisplayedText) || addressElement.AttributeBoolValueOrDefault("display"); // Technically, the <municipality/> element is not a child of the <address/> element. // But I feel that it's sensible to still parse for it, in here. address.Municipality = document.ValueOrDefault("municipality"); // Finally - Lat/Longs. These are -not- part of the REA XML standard. // ~BUT~ some multi-loaders are sticking this data into some xml! ExtractLatitudeLongitudes(document, address); return address; }
private static IList<ListingAgent> ExtractAgent(XElement document) { document.ShouldNotBe(null); var agentElements = document.Elements("listingAgent").ToArray(); if (!agentElements.Any()) { return null; } var agents = new List<ListingAgent>(); foreach (var agentElement in agentElements) { var agent = new ListingAgent { Name = agentElement.ValueOrDefault("name") }; var orderValue = agentElement.AttributeValueOrDefault("id"); int order = 0; if (!string.IsNullOrWhiteSpace(orderValue) && int.TryParse(orderValue, out order)) { agent.Order = order; } var email = agentElement.ValueOrDefault("email"); agent.Communications = new List<Communication>(); if (!string.IsNullOrWhiteSpace(email)) { agent.Communications.Add(new Communication { CommunicationType = CommunicationType.Email, Details = email }); } var phoneMobile = agentElement.ValueOrDefault("telephone", "type", "mobile"); if (!string.IsNullOrWhiteSpace(phoneMobile)) { agent.Communications.Add(new Communication { CommunicationType = CommunicationType.Mobile, Details = phoneMobile }); } var phoneOffice = agentElement.ValueOrDefault("telephone", "type", "BH"); if (!string.IsNullOrWhiteSpace(phoneOffice)) { agent.Communications.Add(new Communication { CommunicationType = CommunicationType.Landline, Details = phoneOffice }); } // Some listings have this element but no data provided. :( // So we don't add 'emtpy' agents. if (!string.IsNullOrWhiteSpace(agent.Name)) { agents.Add(agent); } } var counter = 0; return agents.Any() ? agents .OrderBy(x => x.Order) .Select(x => new ListingAgent { Name = x.Name, Order = ++counter, Communications = x.Communications }) .ToList() : null; }
private static void ExtractRentalData(RentalListing rentalListing, XElement document, CultureInfo cultureInfo) { rentalListing.ShouldNotBe(null); document.ShouldNotBe(null); var dateAvailble = document.ValueOrDefault("dateAvailable"); if (!string.IsNullOrWhiteSpace(dateAvailble)) { rentalListing.AvailableOn = ToDateTime(dateAvailble); } rentalListing.PropertyType = ExtractResidentialAndRentalPropertyType(document); rentalListing.Pricing = ExtractRentalPricing(document, cultureInfo); rentalListing.Features = ExtractFeatures(document); rentalListing.BuildingDetails = ExtractBuildingDetails(document); ExtractRentalNewConstruction(document, rentalListing); }
// REF: http://reaxml.realestate.com.au/docs/reaxml1-xml-format.html#rent private static RentalPricing ExtractRentalPricing(XElement xElement, CultureInfo cultureInfo) { xElement.ShouldNotBe(null); // Quote: There can be multiple rent elements if you wish to specify a price for both monthly and weekly. // However, at least one of the rent elements must be for a weekly period. // Result: FML :( var rentElements = xElement.Elements("rent").ToArray(); if (!rentElements.Any()) { return null; } // We will only use the WEEKLY one. var rentalPricing = new RentalPricing(); foreach (var rentElement in rentElements) { // Have to have a period. var frequency = rentElement.AttributeValueOrDefault("period"); if (string.IsNullOrWhiteSpace(frequency)) { continue; } if (frequency.Equals("week", StringComparison.InvariantCultureIgnoreCase) || frequency.Equals("weekly", StringComparison.InvariantCultureIgnoreCase)) { rentalPricing.PaymentFrequencyType = PaymentFrequencyType.Weekly; } else if (frequency.Equals("month", StringComparison.InvariantCultureIgnoreCase) || frequency.Equals("monthly", StringComparison.InvariantCultureIgnoreCase)) { rentalPricing.PaymentFrequencyType = PaymentFrequencyType.Monthly; } rentalPricing.RentalPrice = rentElement.MoneyValueOrDefault(cultureInfo); var displayAttributeValue = rentElement.AttributeValueOrDefault("display"); var isDisplay = string.IsNullOrWhiteSpace(displayAttributeValue) || displayAttributeValue.ParseOneYesZeroNoToBool(); rentalPricing.RentalPriceText = isDisplay ? rentalPricing.RentalPrice.ToString("C0") : null; // NOTE: We only parse the first one. You have more than one? Pffftttt!!! Die! break; } // NOTE: Even though we have set the rental price text to be the last // rental period's value ... this can now be overwritten by // whatever value they might have in here ... if they have a value. var priceView = xElement.ValueOrDefault("priceView"); if (!string.IsNullOrWhiteSpace(priceView)) { rentalPricing.RentalPriceText = priceView; } rentalPricing.Bond = xElement.MoneyValueOrDefault(cultureInfo, "bond"); return rentalPricing; }
private static void ExtractResidentialData(ResidentialListing residentialListing, XElement document, CultureInfo cultureInfo) { residentialListing.ShouldNotBe(null); document.ShouldNotBe(null); residentialListing.PropertyType = ExtractResidentialAndRentalPropertyType(document); residentialListing.Pricing = ExtractSalePricing(document, cultureInfo); residentialListing.AuctionOn = ExtractAuction(document); residentialListing.BuildingDetails = ExtractBuildingDetails(document); residentialListing.CouncilRates = document.ValueOrDefault("councilRates"); ExtractHomeAndLandPackage(document, residentialListing); ExtractResidentialNewConstruction(document, residentialListing); }
private static void ExtractHomeAndLandPackage(XElement document, ResidentialListing residentialListing) { document.ShouldNotBe(null); residentialListing.ShouldNotBe(null); var homeAndLandPackageElement = document.Element("isHomeLandPackage"); if (homeAndLandPackageElement == null) { return; } if (homeAndLandPackageElement.AttributeBoolValueOrDefault("value")) { if (residentialListing.Features == null) { residentialListing.Features = new Features(); } if (residentialListing.Features.Tags == null) { residentialListing.Features.Tags = new HashSet<string>(); } residentialListing.Features.Tags.Add("houseAndLandPackage"); }; }
private static IList<string> ExtractExternalLinks(XElement document) { document.ShouldNotBe(null); var elements = document.Elements("externalLink").ToArray(); if (!elements.Any()) { return null; } return (from e in elements let externalLink = e.AttributeValueOrDefault("href") where !string.IsNullOrWhiteSpace(externalLink) select Uri.UnescapeDataString(externalLink.Trim())) .ToList(); }
private static LandDetails ExtractLandDetails(XElement document) { document.ShouldNotBe(null); var landDetailsElement = document.Element("landDetails"); if (landDetailsElement == null) { return null; } var details = new LandDetails { Area = landDetailsElement.UnitOfMeasureOrDefault("area", "unit"), Frontage = landDetailsElement.UnitOfMeasureOrDefault("frontage", "unit"), CrossOver = landDetailsElement.ValueOrDefault("crossOver", "value") }; var depthElements = landDetailsElement.Elements("depth").ToArray(); if (depthElements.Any()) { foreach (var depthElement in depthElements) { var depthValue = depthElement.DecimalValueOrDefault(); var depthType = depthElement.AttributeValueOrDefault("unit"); var depthSide = depthElement.AttributeValueOrDefault("side"); if (depthValue > 0) { var depth = new Depth { Value = depthValue, Type = string.IsNullOrWhiteSpace(depthType) ? "Total" : depthType, Side = depthSide }; if (details.Depths == null) { details.Depths = new List<Depth>(); } details.Depths.Add(depth); } } } return details; }
private static BuildingDetails ExtractBuildingDetails(XElement document) { document.ShouldNotBe(null); var buildingDetailsElement = document.Element("buildingDetails"); if (buildingDetailsElement == null) { return null; } var energyRating = buildingDetailsElement.DecimalValueOrDefault("energyRating"); return new BuildingDetails { Area = buildingDetailsElement.UnitOfMeasureOrDefault("area", "unit"), EnergyRating = energyRating <= 0 ? (decimal?) null : energyRating, }; }
private static Listing ConvertFromReaXml(XElement document, CultureInfo cultureInfo, string addressDelimeter, bool isClearAllIsModified) { document.ShouldNotBe(null); // Determine the category, so we know why type of listing we need to create. var categoryType = document.Name.ToCategoryType(); // We can only handle a subset of all the category types. var listing = CreateListing(categoryType); if (listing == null) { // TODO: Add logging message. return null; } // Extract common data. ExtractCommonData(listing, document, addressDelimeter); // Extract specific data. if (listing is ResidentialListing) { ExtractResidentialData(listing as ResidentialListing, document, cultureInfo); } if (listing is RentalListing) { ExtractRentalData(listing as RentalListing, document, cultureInfo); } if (listing is LandListing) { ExtractLandData(listing as LandListing, document, cultureInfo); } if (listing is RuralListing) { ExtractRuralData(listing as RuralListing, document, cultureInfo); } if (isClearAllIsModified) { listing.ClearAllIsModified(); } return listing; }
private static void ExtractLandData(LandListing landListing, XElement document, CultureInfo cultureInfo) { landListing.ShouldNotBe(null); document.ShouldNotBe(null); landListing.CategoryType = ExtractLandCategoryType(document); landListing.Pricing = ExtractSalePricing(document, cultureInfo); landListing.AuctionOn = ExtractAuction(document); landListing.Estate = ExtractLandEstate(document); landListing.AuctionOn = ExtractAuction(document); landListing.CouncilRates = document.ValueOrDefault("councilRates"); }
private static void ExtractCommonData(Listing listing, XElement document, string addressDelimeter) { listing.ShouldNotBe(null); document.ShouldNotBe(null); listing.UpdatedOn = ParseReaDateTime(document.AttributeValue("modTime")); // We have no idea if this was created before this date, but we need to set a date // so we'll default it to this. listing.CreatedOn = listing.UpdatedOn; listing.AgencyId = document.ValueOrDefault("agentID"); listing.Id = document.ValueOrDefault("uniqueID"); var status = document.AttributeValueOrDefault("status"); if (!string.IsNullOrWhiteSpace(status)) { listing.StatusType = StatusTypeHelpers.ToStatusType(status); } listing.Title = document.ValueOrDefault("headline"); listing.Description = document.ValueOrDefault("description"); listing.Address = ExtractAddress(document, addressDelimeter); listing.Agents = ExtractAgent(document); listing.Inspections = ExtractInspectionTimes(document); listing.Images = ExtractImages(document); listing.FloorPlans = ExtractFloorPlans(document); listing.Videos = ExtractVideos(document); listing.Features = ExtractFeatures(document); listing.LandDetails = ExtractLandDetails(document); listing.Links = ExtractExternalLinks(document); }
private static LandEstate ExtractLandEstate(XElement xElement) { xElement.ShouldNotBe(null); var estateElement = xElement.Element("estate"); if (estateElement == null) { return null; } return new LandEstate { Name = estateElement.ValueOrDefault("name"), Stage = estateElement.ValueOrDefault("stage") }; }
private static void ExtractLatitudeLongitudes(XElement document, Address address) { document.ShouldNotBe(null); address.ShouldNotBe(null); var latitudeElement = document.Descendants("Latitude").FirstOrDefault() ?? document.Descendants("latitude").FirstOrDefault(); if (latitudeElement != null) { address.Latitude = latitudeElement.DecimalValueOrDefault(); } var longitudeElement = document.Descendants("Longitude").FirstOrDefault() ?? document.Descendants("longitude").FirstOrDefault(); if (latitudeElement != null) { address.Longitude = longitudeElement.DecimalValueOrDefault(); } }
private static void ExtractRuralData(RuralListing ruralListing, XElement document, CultureInfo cultureInfo) { document.ShouldNotBe(null); ruralListing.CategoryType = ExtractRuralCategoryType(document); ruralListing.Pricing = ExtractSalePricing(document, cultureInfo); ruralListing.AuctionOn = ExtractAuction(document); ruralListing.RuralFeatures = ExtractRuralFeatures(document); ruralListing.CouncilRates = ExtractRuralCouncilRates(document); ruralListing.BuildingDetails = ExtractBuildingDetails(document); ExtractRuralNewConstruction(document, ruralListing); }
private static Features ExtractFeatures(XElement document) { document.ShouldNotBe(null); var featuresElement = document.Element("features"); if (featuresElement == null) { return null; } var tags = new HashSet<string>(StringComparer.OrdinalIgnoreCase); // NOTE: Bedrooms can be a number -or- the value 'STUDIO'. // YES - where a number is the logical value, they can now have a string. :cry: // So, if the value is a string, like STUDIO (or anything else), then the // value will be returned as ZERO. // If it's a STUDIO, we'll add that to the feature's tag hash set. var bedroomsValue = featuresElement.ValueOrDefault("bedrooms"); var bedrooms = 0; if (!string.IsNullOrWhiteSpace(bedroomsValue)) { if (bedroomsValue.Equals("studio", StringComparison.OrdinalIgnoreCase)) { // *epic le sigh - yes, we have a text value for (what looks like) a number value. tags.Add("bedroom-studio"); } else { bedrooms = featuresElement.ByteValueOrDefault("bedrooms"); } } ExtractFeatureWithTextValues(featuresElement, "heating", new[] {"gas", "electric", "GDH", "solid", "other"}, tags); ExtractFeatureWithTextValues(featuresElement, "hotWaterService", new[] {"gas", "electric", "solar"}, tags); ExtractFeatureWithTextValues(featuresElement, "pool", new[] { "inground", "aboveground" }, tags, null); ExtractFeatureWithTextValues(featuresElement, "spa", new[] { "inground", "aboveground" }, tags, null); ExtractOtherFeatures(featuresElement, tags); // Now for the final, tricky part - extracting all the boolean stuff into tags. foreach (var feature in new[] {"features", "allowances", "ecoFriendly"} .Select(node => document.Element(node)) .Where(element => element != null).Select(ExtractBooleanFeatures) .Where(features => features.Any()).SelectMany(features => features)) { tags.Add(feature); } var finalFeatures = new Features { Bedrooms = bedrooms, Bathrooms = featuresElement.ByteValueOrDefault("bathrooms"), CarParking = new CarParking { Garages = featuresElement.BoolOrByteValueOrDefault("garages"), Carports = featuresElement.BoolOrByteValueOrDefault("carports"), OpenSpaces = featuresElement.BoolOrByteValueOrDefault("openSpaces") }, Ensuites = featuresElement.BoolOrByteValueOrDefault("ensuite"), Toilets = featuresElement.BoolOrByteValueOrDefault("toilets"), LivingAreas = featuresElement.BoolOrByteValueOrDefault("livingAreas"), Tags = tags }; return finalFeatures; }
private static Core.Models.Rural.CategoryType ExtractRuralCategoryType(XElement document) { document.ShouldNotBe(null); var categoryElement = document.Element("ruralCategory"); if (categoryElement == null) { return Core.Models.Rural.CategoryType.Unknown; } var categoryValue = categoryElement.AttributeValueOrDefault("name"); return string.IsNullOrWhiteSpace(categoryValue) ? Core.Models.Rural.CategoryType.Unknown : Core.Models.Rural.CategoryTypeHelpers.ToCategoryType(categoryValue); }
private static void ExtractOtherFeatures(XElement features, ISet<string> tags) { features.ShouldNotBe(null); tags.ShouldNotBe(null); var value = features.ValueOrDefault("otherFeatures"); if (string.IsNullOrWhiteSpace(value)) { return; } // Split the value up into comma delimeted parts. var parts = value.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); foreach (var part in parts) { tags.Add(part.Trim()); } }
private static RuralFeatures ExtractRuralFeatures(XElement document) { document.ShouldNotBe(null); var ruralFeaturesElement = document.Element("ruralFeatures"); if (ruralFeaturesElement == null) { return null; } return new RuralFeatures { AnnualRainfall = ruralFeaturesElement.ValueOrDefault("annualRainfall"), CarryingCapacity = ruralFeaturesElement.ValueOrDefault("carryingCapacity"), Fencing = ruralFeaturesElement.ValueOrDefault("fencing"), Improvements = ruralFeaturesElement.ValueOrDefault("improvements"), Irrigation = ruralFeaturesElement.ValueOrDefault("irrigation"), Services = ruralFeaturesElement.ValueOrDefault("services"), SoilTypes = ruralFeaturesElement.ValueOrDefault("soilTypes") }; }
private static IList<Media> ExtractImages(XElement document) { document.ShouldNotBe(null); var imageElement = document.Element("images") ?? document.Element("objects"); if (imageElement == null) { return null; } var imagesElements = imageElement.Elements("img").ToArray(); if (!imagesElements.Any()) { return null; } // Note: Image 'urls' can either be via a Uri (yay!) or // a file name because the xml was provided in a zip file with // the images (booooo! hiss!!!) var images = (from x in imagesElements let url = x.AttributeValueOrDefault("url") let file = x.AttributeValueOrDefault("file") let order = x.AttributeValueOrDefault("id") where (!string.IsNullOrWhiteSpace(url) || !string.IsNullOrWhiteSpace(file)) && !string.IsNullOrWhiteSpace(order) select new Media { Url = string.IsNullOrWhiteSpace(url) ? string.IsNullOrWhiteSpace(file) ? null : file : url, Order = ConvertImageOrderToNumber(order) }).ToList(); return images.Any() ? images : null; }
private static string ExtractRuralCouncilRates(XElement document) { document.ShouldNotBe(null); var ruralFeaturesElement = document.Element("ruralFeatures"); return ruralFeaturesElement == null ? null : ruralFeaturesElement.ValueOrDefault("councilRates"); }
private static IList<Media> ExtractVideos(XElement document) { document.ShouldNotBe(null); var videoUrl = document.ValueOrDefault("videoLink", "href"); return string.IsNullOrWhiteSpace(videoUrl) ? null : new List<Media> { new Media { Order = 1, Url = videoUrl } }; }
private static void ExtractRuralNewConstruction(XElement document, RuralListing listing) { document.ShouldNotBe(null); listing.ShouldNotBe(null); if (!document.BoolValueOrDefault("newConstruction")) { return; } if (listing.Features == null) { listing.Features = new Features(); } if (listing.Features.Tags == null) { listing.Features.Tags = new HashSet<string>(); } listing.Features.Tags.Add("isANewConstruction"); }
private static void ExtractSoldPrice(XElement element, SalePricing salePricing) { element.ShouldNotBe(null); salePricing.SoldPrice = element.DecimalValueOrDefault(); var soldDisplayAttribute = element.ValueOrDefault(null, "display"); // NOTE 1: no display price assumes a 'YES' and that the price -is- to be displayed. // NOTE 2: A _display attribute_ value of 'range' can only valid for commerical properties ... // and .. we don't handle commerical. So it will end up throwing an exception // which is legit in this case. var isDisplay = string.IsNullOrWhiteSpace(soldDisplayAttribute) || soldDisplayAttribute.ParseOneYesZeroNoToBool(); salePricing.SoldPriceText = isDisplay ? salePricing.SoldPrice.Value.ToString("C0") : null; }
private static void ExtractSoldOn(XElement element, SalePricing salePricing) { element.ShouldNotBe(null); // SoldOn could be date or soldData. Thanks REA for such a great schema. var soldOnText = element.ValueOrDefault(); if (!string.IsNullOrWhiteSpace(soldOnText)) { salePricing.SoldOn = ToDateTime(soldOnText); } }