The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package WebService::Zoopla::API;
use Moose;
use Net::HTTP::Spore;
our $AUTOLOAD;
our $VERSION = '0.001';
use List::Util qw(first);

# ABSTRACT: Perl interface to the Zoopla API

has api_key => (is => 'ro', isa => 'Str', required => 1);
has specification => (
    is      => 'ro',
    isa     => 'Str',
    default => '{
                        "name" : "Zoopla",
                        "meta" : {
                            "documentation" : "http://developer.zoopla.com/docs"
                        },
                        "base_url" : "http://api.zoopla.co.uk/api/v1",
                        "version" : "1",
                        "methods" : {
                            "zed_index" : {
                                "required_params" : [
                                    "api_key",
                                    "output_type"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude"
                                ],
                                "path" : "/zed_index.js",
                                "method" : "GET"
                            },
                            "area_value_graphs" : {
                                "required_params" : [
                                    "api_key"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude",
                                    "size",
                                    "output_type"
                                ],
                                "path":"/area_value_graphs.js",
                                "method" : "GET"
                            },
                            "richlist" : {
                                "required_params" : [
                                    "api_key",
                                    "output_type",
                                    "area_type"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude"
                                ],
                                "path":"/richlist.js",
                                "method" : "GET"
                            },
                            "average_area_sold_price" : {
                                "required_params" : [
                                    "api_key",
                                    "output_type"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude"
                                ],
                                "path":"/average_area_sold_price.js",
                                "method" : "GET"
                            },
                            "zed_indices" : {
                                "required_params" : [
                                    "api_key",
                                    "output_type",
                                    "area_type"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude",
                                    "ordering",
                                    "page_number",
                                    "page_size"
                                ],
                                "path":"/zed_indices.js",
                                "method" : "GET"
                            },
                            "zoopla_estimates" : {
                                "required_params" : [
                                    "api_key"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude",
                                    "property_id",
                                    "order_by",
                                    "ordering",
                                    "page_number",
                                    "page_size",
                                    "output_type"
                                ],
                                "path":"/zoopla_estimates.js",
                                "method" : "GET"
                            },
                            "property_listings" : {
                                "required_params" : [
                                    "api_key"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude",
                                    "property_type",
                                    "radius",
                                    "order_by",
                                    "ordering",
                                    "page_number",
                                    "page_size",
                                    "output_type",
                                    "listing_status",
                                    "include_sold",
                                    "include_rented",
                                    "minimum_price",
                                    "maximum_price",
                                    "minimum_beds",
                                    "maximum_beds",
                                    "furnished",
                                    "keywords",
                                    "listing_id"
                                ],
                                "path":"/property_listings.js",
                                "method" : "GET"
                            },
                            "get_session_id" : {
                                "required_params" : [
                                    "api_key"
                                ],
                                "path":"/get_session_id.js",
                                "method" : "GET"
                            },
                            "refine_estimate" : {
                                "required_params" : [
                                    "api_key",
                                    "property_id",
                                    "property_type",
                                    "tenure",
                                    "num_bedrooms",
                                    "num_bathrooms",
                                    "num_receptions",
                                    "session_id"
                                ],
                                "optional_params" : [
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude",
                                    "output_type"
                                ],
                                "path":"/refine_estimate.js",
                                "method" : "GET"
                            },
                            "arrange_viewing" : {
                                "required_params" : [
                                    "api_key",
                                    "listing_id",
                                    "name",
                                    "email",
                                    "phone",
                                    "phone_type",
                                    "best_time_to_call",
                                    "message",
                                    "session_id"
                                ],
                                "path":"/arrange_viewing.js",
                                "method" : "GET"
                            },
                            "average_sold_prices" : {
                                "required_params" : [
                                    "api_key"
                                ],
                                "optional_params" : [
                                    "session_id",
                                    "area",
                                    "street",
                                    "town",
                                    "postcode",
                                    "county",
                                    "latitude",
                                    "longitude",
                                    "area_type",
                                    "page_number",
                                    "page_size",
                                    "ordering",
                                    "output_type"
                                ],
                                "path":"/zoopla_estimates.js",
                                "method" : "GET"
                            }
                        },
                        "formats" : [
                            "json",
                            "xml"
                        ]
                     }'
);

has api => (
    is      => 'ro',
    lazy    => 1,
    isa     => 'Object',
    default => sub {
        my $self = shift;
        Net::HTTP::Spore->new_from_strings($self->specification)
          ->enable('Format::JSON')->enable('Runtime');
    }
);

has session_id => (
    is      => 'ro',
    lazy    => 1,
    isa     => 'Str',
    default => sub {
        my $self = shift;
        $self->_call('get_session_id', @_)->{'session_id'};
    }
);

__PACKAGE__->meta->make_immutable;

sub _call {
    my $self   = shift;
    my $method = shift;
    my $attrs  = shift;
    $attrs->{api_key}    = $self->api_key;
    $attrs->{format}     = 'json';
    $attrs->{session_id} = $self->session_id
      if ($self->_need_session($method));
    return $self->api->$method(%{$attrs})->body;
}

sub _need_session {
    my $self   = shift;
    my $method = shift;
    return first { $_ eq 'session_id' } (
        @{  $self->api->meta->{methods}->{$method}
              ->get_original_method->meta->{optional_params}
          },
        @{  $self->api->meta->{methods}->{$method}
              ->get_original_method->meta->{required_params}
          }
    );
}

sub AUTOLOAD {
    my $self = shift;
    ref($self) || die "$self is not an object";
    my $method = $AUTOLOAD;
    $method =~ s/.*://;
    die "Can't call $method()"
      unless grep { $_ eq $method } @{$self->api->meta->local_spore_methods};
    return $self->_call($method, @_);
}

1;


=pod

=head1 NAME

WebService::Zoopla::API - Perl interface to the Zoopla API

=head1 VERSION

version 0.001

=head1 SYNOPSIS

use WebService::Zoopla::API;

my $zoopla = WebService::Zoopla::API->new(
            api_key         => 'xxxxxx');

my $result = $zoopla->zed_index({area=>'SE4', output_type=>"outcode"});

=head1 Constructor

=head2 new()

Creates and returns a new WebService::Zoopla::API object

    my $zoopla = WebService::Zoopla::API->new(
                api_key         => 'xxxxxx');

=over 4

=item * C<< api_key => 'xxxxx' >>

Set the api key. This can be set up at:
http://developer.zoopla.com

=back

=head1 METHODS

=head2 arrange_viewing

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=item * C<< listing_id >>

=item * C<< name >>

=item * C<< email >>

=item * C<< phone >>

=item * C<< phone_type >>

=item * C<< best_time_to_call >>

=item * C<< message >>

=item * C<< session_id >>

=back

=back

=head2 average_area_sold_price

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=item * C<< output_type >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=back

=back

=head2 richlist

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=item * C<< output_type >>

=item * C<< area_type >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=back

=back

=head2 property_listings

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=item * C<< property_type >>

=item * C<< radius >>

=item * C<< order_by >>

=item * C<< ordering >>

=item * C<< page_number >>

=item * C<< page_size >>

=item * C<< output_type >>

=item * C<< listing_status >>

=item * C<< include_sold >>

=item * C<< include_rented >>

=item * C<< minimum_price >>

=item * C<< maximum_price >>

=item * C<< minimum_beds >>

=item * C<< maximum_beds >>

=item * C<< furnished >>

=item * C<< keywords >>

=item * C<< listing_id >>

=back

=back

=head2 area_value_graphs

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=item * C<< size >>

=item * C<< output_type >>

=back

=back

=head2 zed_index

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=item * C<< output_type >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=back

=back

=head2 get_session_id

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=back

=back

=head2 zoopla_estimates

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=item * C<< property_id >>

=item * C<< order_by >>

=item * C<< ordering >>

=item * C<< page_number >>

=item * C<< page_size >>

=item * C<< output_type >>

=back

=back

=head2 average_sold_prices

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=item * C<< area_type >>

=item * C<< page_number >>

=item * C<< page_size >>

=item * C<< ordering >>

=item * C<< output_type >>

=back

=back

=head2 refine_estimate

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=item * C<< property_id >>

=item * C<< property_type >>

=item * C<< tenure >>

=item * C<< num_bedrooms >>

=item * C<< num_bathrooms >>

=item * C<< num_receptions >>

=item * C<< session_id >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=item * C<< output_type >>

=back

=back

=head2 zed_indices

=over 4

Required Parameters

=over 4

=item * C<< api_key >>

=item * C<< output_type >>

=item * C<< area_type >>

=back

=back

=over 4

Optional Parameters

=over 4

=item * C<< session_id >>

=item * C<< area >>

=item * C<< street >>

=item * C<< town >>

=item * C<< postcode >>

=item * C<< county >>

=item * C<< latitude >>

=item * C<< longitude >>

=item * C<< ordering >>

=item * C<< page_number >>

=item * C<< page_size >>

=back

=back

=head1 SEE ALSO

L<Net::HTTP::Spore>

=head1 INTERNAL METHODS

=head2 _call($args)

General method for calling the methods on the api
You don't need to call this directly

=head2 _need_session($method)

Internal method to check if a method needs a session id

=head1 AUTHOR

Willem Basson <willem.basson@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Willem Basson.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


__END__