###################################################################### package Net::Amazon::Property; ###################################################################### use warnings; use strict; use base qw(Net::Amazon); use Net::Amazon::Property::Book; use Net::Amazon::Property::CE; use Net::Amazon::Property::DVD; use Net::Amazon::Property::Music; use Net::Amazon::Property::Software; use Net::Amazon::Property::VideoGames; use Net::Amazon::Attribute::ReviewSet; use Data::Dumper; use Log::Log4perl qw(:easy); # read: a poor man's XPath # NOTE: Igor Sutton Lopes has a module called Hash::Path that does exactly # what I am doing here. He beat me to the punch. :) our %DEFAULT_ATTRIBUTES_XPATH = ( Availability => [qw(Offers Offer OfferListing Availability)], Catalog => [qw(ItemAttributes ProductGroup)], Binding => [qw(ItemAttributes Binding)], CollectibleCount => [qw(OfferSummary TotalCollectible)], CollectiblePrice => [qw(OfferSummary LowestCollectiblePrice FormattedPrice)], ImageUrlLarge => [qw(LargeImage URL)], LargeImageUrl => [qw(LargeImage URL)], LargeImageWidth => [qw(LargeImage Width content)], LargeImageHeight => [qw(LargeImage Height content)], ListPrice => [qw(ItemAttributes ListPrice FormattedPrice)], Manufacturer => [qw(ItemAttributes Manufacturer)], MediumImageUrl => [qw(MediumImage URL)], ImageUrlMedium => [qw(MediumImage URL)], MediumImageWidth => [qw(MediumImage Width content)], MediumImageHeight => [qw(MediumImage Height content)], OurPrice => [qw(ItemAttributes ListPrice FormattedPrice)], ImageUrlSmall => [qw(SmallImage URL)], SmallImageUrl => [qw(SmallImage URL)], SmallImageWidth => [qw(SmallImage Width content)], SmallImageHeight => [qw(SmallImage Height content)], SuperSaverShipping => [qw(Offers Offer OfferListing IsEligibleForSuperSaverShipping)], Title => [qw(Title)], ThirdPartyNewCount => [qw(OfferSummary TotalNew)], ThirdPartyNewPrice => [qw(OfferSummary LowestNewPrice FormattedPrice)], TotalOffers => [qw(Offers TotalOffers)], UsedCount => [qw(OfferSummary TotalUsed)], UsedPrice => [qw(OfferSummary LowestUsedPrice FormattedPrice)], RawListPrice => [qw(ItemAttributes ListPrice Amount)], CurrencyCode => [qw(ItemAttributes ListPrice CurrencyCode)], ); our @DEFAULT_ATTRIBUTES = qw( SalesRank ASIN DetailPageURL ProductDescription NumMedia ReleaseDate NumberOfOfferings ); our %COMPATIBLE_ATTRIBUTES = ( 'Asin' => 'ASIN', 'url' => 'DetailPageURL', 'Media' => 'Binding', 'ProductName' => 'title', ); __PACKAGE__->make_accessor($_) for @DEFAULT_ATTRIBUTES; __PACKAGE__->make_accessor($_) for keys %DEFAULT_ATTRIBUTES_XPATH; __PACKAGE__->make_accessor($_) for qw(year review_set image_set offer_set); __PACKAGE__->make_array_accessor($_) for qw(browse_nodes similar_asins); __PACKAGE__->make_compatible_accessor($_, $COMPATIBLE_ATTRIBUTES{$_}) for keys %COMPATIBLE_ATTRIBUTES; ################################################## sub new { ################################################## my($class, %options) = @_; if(!$options{xmlref}) { die "Mandatory param xmlref missing"; } my $self = { %options, }; bless $self, $class; # Set default attributes for my $attr (@DEFAULT_ATTRIBUTES) { $self->$attr($options{xmlref}->{$attr}); } for my $attr (keys %DEFAULT_ATTRIBUTES_XPATH) { my $value = __PACKAGE__->walk_hash_ref($options{xmlref}, $DEFAULT_ATTRIBUTES_XPATH{$attr}); $self->$attr($value); } if (defined $options{xmlref}->{OfferSummary}) { $self->NumberOfOfferings($self->UsedCount() + $self->ThirdPartyNewCount() + $self->CollectibleCount()); } if (defined $options{xmlref}->{EditorialReviews}) { $self->ProductDescription($options{xmlref}->{EditorialReviews}->{EditorialReview}->[0]->{Content}); } my @browse_nodes; if (ref $options{xmlref}->{BrowseNodes}->{BrowseNode} eq 'ARRAY') { for my $bn (@{$options{xmlref}->{BrowseNodes}->{BrowseNode}}) { push @browse_nodes, $bn->{Name}; # Walk the BrowseNode Ancestors and collect the other BrowseNode Ids for (my $ref = $bn->{Ancestors}->{BrowseNode}; defined $ref; $ref = $ref->{Ancestors}->{BrowseNode}) { # The BrowseNodeId is also available... push @browse_nodes, $ref->{Name}; } } } $self->browse_nodes(\@browse_nodes); my @similars; for my $similar (@{$options{xmlref}->{SimilarProducts}->{SimilarProduct}}) { # You could also capture the Title as well... push @similars, $similar->{ASIN}; } $self->similar_asins(\@similars); return $self; } ################################################## sub as_string { ################################################## my($self) = @_; my $result = "\"" . $self->Title . "\", "; if($self->{xmlref}->{Manufacturer}) { $result .= "$self->{xmlref}->{Manufacturer}, "; } $result .= $self->year() . ", " if $self->year(); $result .= $self->OurPrice() . ", "; $result .= $self->ASIN(); return $result; } ################################################## sub factory { ################################################## my(%options) = @_; my $xmlref = $options{xmlref}; die "Called factory without xmlref" unless $xmlref; #DEBUG(sub {"factory xmlref=" . Data::Dumper::Dumper($xmlref)}); my $catalog = $xmlref->{ItemAttributes}->{ProductGroup}; my $obj; if(0) { } elsif($catalog eq "Book") { DEBUG("Creating new Book Property"); $obj = Net::Amazon::Property::Book->new(xmlref => $xmlref); } elsif($catalog eq "Music") { DEBUG("Creating new Music Property"); $obj = Net::Amazon::Property::Music->new(xmlref => $xmlref); } elsif($catalog eq "DVD") { DEBUG("Creating new DVD Property"); $obj = Net::Amazon::Property::DVD->new(xmlref => $xmlref); } elsif($catalog eq "Software") { DEBUG("Creating new Software Property"); $obj = Net::Amazon::Property::Software->new(xmlref => $xmlref); } elsif($catalog eq "Video Games") { DEBUG("Creating new Video Games Property"); $obj = Net::Amazon::Property::VideoGames->new(xmlref => $xmlref); } elsif($catalog eq "CE") { # Consumer Electronics? DEBUG("Creating new CE Property"); $obj = Net::Amazon::Property::CE->new(xmlref => $xmlref); } else { # print "UNKNOWN CATALOG: ", Data::Dumper::Dumper($xmlref), "\n"; # die "%Error: there is no property defined for type '$catalog'\n"; DEBUG("Creating new Default Property ($catalog)"); $obj = Net::Amazon::Property->new(xmlref => $xmlref); } return $obj; } ################################################## sub init_via_xmlref { ################################################## my($self, $xmlref) = @_; my $reviewset = Net::Amazon::Attribute::ReviewSet->new(); if(exists $xmlref->{CustomerReviews}) { $reviewset->init_via_xmlref($xmlref->{CustomerReviews}); } $self->review_set($reviewset); } ################################################## ################################################## ################################################## 1; __END__ =head1 NAME Net::Amazon::Property - Baseclass for products on amazon.com =head1 SYNOPSIS use Net::Amazon; # ... if($resp->is_success()) { for my $prop ($resp->properties) { print $_->ProductName(), " ", $_->Manufacturer(), " ", $_->OurPrice(), "\n"; =head1 DESCRIPTION C is the baseclass for results returned from Amazon web service queries. The term 'properties' is used as a generic description for an item on amazon.com. Typically, the C method of a C object will return one or more objects of class C or one of its subclasses, e.g. C or C. While C objects expose accessors for all fields returned in the XML response (like C, C, C, C, C, C, subclasses might define their own accessors to more class-specific fields (like the iC's C method returning a list of authors, while C's C method will return a reference to a sub-hash containing a C field, just like the response's XML contained it). =head2 METHODS Methods vary, depending on the item returned from a query. Here's the most common ones. They're all accessors, meaning they can be used like C to retrieve the value or like C to set the value of the field. =over 4 =item Asin() The item's ASIN number. This option is deprecated, please use ASIN. =item ASIN() The item's ASIN number. =item ProductName() Book title, CD album name or item name. This option is actually an alias for the method title, and is actually dependent upon the type of item returned. =item Availability() Text string describing if the item is available. Examples: C<"Usually ships within 24 hours"> or C<"Out of Print--Limited Availability">. =item Catalog() Shows the catalog the item was found in: C, C, C, C etc. =item Authors() Returns a sub-hash with a C key, which points to either a single $scalar or to a reference of an array containing author names as scalars. =item ReleaseDate() Item's release date, format is "NN Monthname, Year". =item Manufacturer() Music label, publishing company or manufacturer =item ImageUrlSmall() URL to a small (thumbnail) image of the item =item ImageUrlMedium() URL to a medium-size image of the item =item ImageUrlLarge() URL to a large image of the item =item ListPrice() List price of the item =item OurPrice() Amazon price of the item =item UsedPrice() Used price of the item =item RawListPrice() Unformatted list price as an integer, without currency symbol. =item CurrencyCode() The currency code for the L, e.g. C. =item SalesRank() Sales rank of the item (contains digits and commas, like 1,000,001) =item Media() Type of media (Paperback, etc.). =item NumMedia() Number of media the item carries (1,2 CDs etc.). =item ProductDescription() Lengthy textual description of the product. =item CollectiblePrice() Lowest price in "Collectible" category. =item CollectibleCount() Number of offerings in "Collectible" category. =item NumberOfOfferings() Total number of offerings in all categories. =item UsedCount() Number of offerings in "Used" category. =item TotalOffers() Number of offerings of the product. =item ThirdPartyNewPrice() Lowest price in "Third Party New" category. =item ThirdPartyNewCount() Number of offerings in "Third Party New" category. =item SmallImageWidth() Return the width of the small image in pixels. =item SmallImageHeight() Return the height of the small image in pixels. =item MediumImageWidth() Return the width of the medium image in pixels. =item MediumImageHeight() Return the height of the medium image in pixels. =item LargeImageWidth() Return the width of the large image in pixels. =item LargeImageHeight() Return the height of the large image in pixels. =item SuperSaverShipping() Boolean value that indicates if the product is eligible for super saver shipping. =item year() The release year extracted from ReleaseDate(). =item browse_nodes() Returns a list of browse nodes (text string categories) for this item. =item similar_asins() Returns a list of ASINs of similar items for this item. =back Please check the subclasses of C for specialized methods. =head1 SEE ALSO =head1 AUTHOR Mike Schilli, Em@perlmeister.comE =head1 COPYRIGHT AND LICENSE Copyright 2003 by Mike Schilli Em@perlmeister.comE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut