The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package WWW::SourceForge;
use strict;
use LWP::Simple qw(get);
use JSON::Parse;
use XML::Feed;
use File::HomeDir;

our $VERSION = '0.70'; # This is the overall version for the entire
# package, so should probably be updated even when the other modules are
# touched.

=head2 new

 Usage     : my $sfapi = new WWW::SourceForge;
 Returns   : WWW::SourceForge object

Optionally pass an 'api' argument to select one of the other APIs.

    my $download_api = WWW::SourceForge->new( api => 'download' );

See https://sourceforge.net/p/forge/documentation/Download%20Stats%20API/

=cut

sub new {
    my ( $class, %parameters ) = @_;

    my $api = $parameters{api} || 'data';
    my $api_url;

# TODO: This stuff made sense when I wrote it, but until there's a
# single unified API, this is just confusing. Need to nuke this bit
    if ( $api eq 'download' ) {
        $api_url = 'http://sourceforge.net/projects';
    } else {
        $api_url = 'http://sourceforge.net/api';
    }

    my $self = bless(
        {
            api_url => $api_url,
            api     => $api,
        },
        ref($class) || $class
    );

    return $self;
}

=head2 call

 Usage : my $json = $sfapi->call( 
                method => whatever, 
                arg1   => 'value', 
                arg2   => 'another value',
                format => 'rss',
                );
 Returns : Hashref, containing a bunch of data. Format defaults to
     'json', but in some cases, you'll want to force rss because that's
     how the return is available. Will try to make this smarter
     eventually.

Calls a particular method in the SourceForge API. Other args are passed
as args to that call.

=cut

sub call {
    my $self = shift;
    my %args = @_;

    my $r = {};
    my $url;
    my $format;

    if ( defined( $args{method} ) && ( $args{ method } eq 'proj_activity' ) ) {
        my $project = $args{ project };
        $url =
            'http://sourceforge.net/export/rss2_keepsake.php?group_id=' .
            $project->id();
        $format = 'rss'; 
    }

    # Download API, documented at
    # https://sourceforge.net/p/forge/documentation/Download%20Stats%20API/
    elsif ( $self->{api} eq 'download' ) {

        # TODO: Default start date, end date (last 7 days, perhaps?)

        # TODO: API allows specification of subdirs of the files
        # hierarchy, and we don't allow that yet here.

        $url =
            $self->{api_url} . '/'
          . $args{project}
          . '/files/stats/json?start_date=' . $args{start_date}
          . '&end_date=' . $args{end_date};

        $format = 'json';

    # Data API, documented at
    # https://sourceforge.net/p/forge/documentation/API/
    } else {

        # HACK
        # If a full URI is provided, use that
        if ( $args{uri} ) {
            $format = $args{format} || 'json';
            $url = $self->{api_url} . $args{uri};
        } else {

            my $method = $args{method} || return $r;
            delete( $args{method} );

            $format = $args{format} || 'json';
            delete( $args{format} );

            $url = $self->{api_url} . '/' . $method;
            # $url .= '/' . join('/',@args);
            foreach my $a ( keys %args ) {
                $url .= '/' . $a . '/' . $args{$a};
            }

            # Format defaults to 'json'
            $url .= '/' . $format;
        }
    }

    if ( $format eq 'rss' ) {
        $r = { entries => [] };

        my $feed;
        eval { $feed = XML::Feed->parse( URI->new($url) ) };
        if ($@) {
            warn $@;
            return $r;
        }
        {
            no warnings 'all';
            if ( $feed && $feed->entries ) {
                for my $entry ( $feed->entries ) {
                    push @{ $r->{entries} }, $entry;
                }
            } else {
                return { entries => [] };
            }
        }
    } else {
        my $json = get($url);
        eval { $r = JSON::Parse::json_to_perl($json); };
        if ( $@ ) {
            warn $@;
            return { entries => [] };
        }
    }
    return $r;
}

# Loads a config from ~/.sourceforge
sub get_config {
    my $conf = File::HomeDir->my_home() . "/.sourceforge";
    my %config = ();

    if ( -e $conf ) {
        open my $C, "<$conf" or die "Couldn't open $conf";
        my @conf = <$C>;
        close $C;

        foreach my $line (@conf) {
            chomp $line;
            next if $line =~ m/^#/;

            my ( $var, $val ) = split /\s+/, $line;
            next unless $val;
            $config{$var} = $val;
        }
    }
    return %config;
} 

=head1 NAME

WWW::SourceForge - Interface to SourceForge's APIs - http://sourceforge.net/p/forge/documentation/API/ and https://sourceforge.net/p/forge/documentation/Download%20Stats%20API/

=head1 SYNOPSIS

Usually you'll use this via WWW::SourceForge::Project and
WWW::SourceForge::User rather than using this directly.

=head1 DESCRIPTION

Implements a Perl interface to the SourceForge API, documented here:
http://sourceforge.net/p/forge/documentation/API/ and here:
https://sourceforge.net/p/forge/documentation/Download%20Stats%20API/

=head1 USAGE

    use WWW::SourceForge;
    my $sfapi = new WWW::SourceForge;

See WWW::SourceForge::User and WWW::SourceForge::Project for details.

=head1 BUGS

None

=head1 SUPPORT

http://sourceforge.net/p/sfprojecttools/tickets/

=head1 AUTHOR

    Rich Bowen
    CPAN ID: RBOW
    SourceForge
    rbowen@sourceforge.net
    http://sf.net

=head1 COPYRIGHT

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

The full text of the license can be found in the
LICENSE file included with this module.


=head1 SEE ALSO

perl(1).

=cut

#################### main pod documentation end ###################


1;