Cultured Perl: Flickr, a business's bst frnd

Create charts and upload them to Flickr using CPAN modules

Flickr isn't just for photo sharing and social networking; it's a legitimate business tool. Learn how Perl programmers can use the CPAN Chart modules to create charts and graphs, and the Flickr::Upload module to upload the charts to Flickr.


Teodor Zlatanov, Programmer, Gold Software Systems

photo- teodor zlatanovTeodor Zlatanov emerged with an M.S. in computer engineering from Boston University in 1999. He has worked as a programmer since 1992, using Perl, Java, C, and C++. His interests are in open source work on text parsing, database architectures, user interfaces, and UNIX system administration.

24 November 2009

Also available in Japanese Portuguese

Connect with Ted

Ted is one of our most popular and prolific authors. Browse all of Ted's articles on developerWorks. Check out Ted's profile and connect with him, other authors, and fellow readers in My developerWorks.

Ah, Flickr. Absolutely the best place on Earth for pictures of cute puppies, slightly out-of-focus sunsets, and wedding cakes. It's hardly famous as a business hub, but this article shows some ways to use Flickr through Perl that are more business oriented.

A common business use for Flickr has been to show a product. This works well enough for companies with a physical product to sell, especially one that benefits from visual exposure. There isn't much need to dig into that here—dozens of available tools will handle uploading a product portfolio to Flickr just fine.

Instead, I show you how to build a custom chart using the CPAN Chart module (originally by Dave Bonner, now maintained by the Chart group) for business use. We'll look at the performance of stock gains over a year (using fake data), and then we'll post the chart in a Flickr album that shows past performance charts as well. We'll use only the Flickr features that make sense for this application, so this won't be a tour of the whole Flickr API. For that, you should go to the API site and read through the documentation. It's easy to read and very thorough; using it from Perl is simple.

Where's the Chart::???? module?

There's really no Chart::[type] module. All of the different chart types—Points, Lines, Bars, LinesPoints, Composite, StackedBars, Pie, Pareto, HorizontalBars, Split, ErrorBars, Direction, and Mountain—are classes by themselves that each inherit tons of methods from the Chart::Base class. To use one, you simply replace [type] with the type of chart you want, like so:

use Chart::Lines;

You could use the Google API for charts, but at the time of this writing, the Chart module can do more from Perl than the Google Charts API from Javascript. This may change, however, so you should definitely keep an eye on Google's tools.

What's the goal?

First, let's generate the data for the chart randomly and place it directly in a list. Normally, the data would come from a file or a database, but we'll skip that step because it's not terribly interesting.

I'll use the list to create a chart as an image. The chart can then be uploaded to Flickr, tagged correctly, and described appropriately. Simple, right?

When you install the Chart module, remember that you can't just install "Chart," you need to install "Chart::Lines," for example.

Generating random data

It's not hard to generate random data, so let's just start with the number 100 and make every successive data point a random offset from the previous one, for 200 data points, with a random gain or loss of at most 0.5. There's only so far we need to go in a fictitious example, but using composite charts to mark the top and bottom of the curves, calculating MACD, etc., would be a fun project. If you feel adventurous, go for it.

Listing 1. Generating random data
my $max = 200;         # the maximum data points

# generate random data and labels for it
my @data = ([100], [100]);
my @labels = (0);

foreach (1..$max)
 # I know there are 20 options for skipping ticks, but this works better for me
 push @labels, every(25) ? $_ : undef ;

 # the delta is from [ -.5 up to .5 )
 foreach my $array (@data)
  push @$array, $array->[-1];
  $array->[-1] += (0.5 - rand 1);

Here's the approach: start with @data containing two arrays of one element each. This is the starting value for the two data sets. The labels also have just the starting zero. Now, do this 200 times: add a label equal to the current loop iteration to the @labels list (every 25th time, you add 200/25 = 8 labels total); then copy the last value for each data set into the same data set and apply a random up or down adjustment to the new last value. No need for temporary variables.

I should mention that this uses the CPAN Every module to insert either nothing (an undef value) or the current loop iteration in the @labels list. I know there are many Chart options for skipping labels, but this was a simpler solution. I also wanted to show the every() function, which, given a number, will return true just once when it's called that many times (so every(2) will return true on the second, fourth, sixth, etc. time it's called). I wrote the Every module, and I find it very handy for this kind of use and for debugging.

The adjustment is between -0.5 and 0.5 (up to but not including 0.5 exactly). With a starting value of 100, this seemed reasonable.

Generate the chart

The CPAN Chart modules are nice and make life easy. They use the GD library. Use the installation instructions and search the Web if you have trouble. The Chart modules come with a Documentation.pdf file that has all the documentation for all the modules with examples of each chart type.

Once everything is set up, generating a PNG chart is easy:

Listing 2. Creating the chart
my $save_name = "performance.png";   # file name to use

my $title = "Performance of MoneyCo funds over 200 days";
my $desc = "Total value at EOD starting " . 
           strftime("%Y-%m-%d", localtime);

my $chart = Chart::Lines->new(800,600);
$chart->set(title => $title,
            sub_title => $desc,
            x_label => "days",
            y_label => "millions USD",
            legend => "bottom",
            legend_labels => [ "Aggressive Fund", "Passive Fund" ],
            y_grid_lines => "true",

$chart->png($save_name, [ \@labels, @data ]);

die "Couldn't create $save_name: $!" unless -f $save_name && -r $save_name;

We've set up the chart with a title, a descriptive subtitle, X and Y labels, placed the chart legend at the bottom, and labeled the two data sets. Finally, we asked for Y grid lines (they go from left to right). This way, the value of the funds can be easily seen.

The title should include the current date so it will make sense when multiples are uploaded in the Flickr photostream. Whatever your desired date format is, make sure you use the strftime function from the POSIX module to generate it. Without a doubt, the most annoying messes of Perl code I've had to untangle over the last 15 years have come from the author's ignorance of the POSIX::strftime() function.

With the png() method call, the chart is saved as a PNG file to the $save_name variable.

Flickr authentication and authorization

The application must be run with two parameters and takes an optional third parameter:

Listing 3. Command-line parameters
my $api_key    = shift @ARGV;
my $secret     = shift @ARGV;
my $auth_token = shift @ARGV;

die "Syntax: $0 API_KEY SECRET [AUTH_TOKEN]" unless defined $secret;

This is a really simple way to do command-line options. You may want to use Getopt::Long instead. It requires two parameters and dies with a usage summary if they are not given.

The API key and secret can be retrieved from Flickr. Ask for a non-commercial key unless you know you need a commercial one, and you'll get it immediately. Those two parameters will authenticate your application (

Now, if you have not provided the $auth_token (the third command-line parameter), the script will get it with your help (I copied this approach from the flickr_upload script that comes with Flickr::Upload, FIXMEs and all).

You can't do an upload without the token. The token authorizes the authenticated application to upload to Flickr. So, the user that runs the application will see a URL they have to visit in order to allow the application to upload. The user could be the same one that requested the API key, but doesn't have to be.

We create a $ua variable (an object of type LWP::UserAgent) to interact with the Flickr servers. You should look at the LWP::UserAgent documentation; that module is essential for any Web client written in Perl. Many other incredible modules, such as WWW::Mechanize, depend on LWP::UserAgent.

Listing 4. Authentication and authorization
my $ua = Flickr::Upload->new({
                              'key' => $api_key,
                              'secret' => $secret,

# from the flickr_upload script that comes with Flickr::Upload
unless (defined $auth_token)
 # 1. get a frob
 my $frob = getFrob( $ua );

 # 2. get a url for the frob
 my $url = $ua->request_auth_url('write', $frob);

 # 3. tell the user what to do with it
 print "1. Enter the following URL into your browser\n\n",
   "2. Follow the instructions on the web page\n",
    "3. Hit <Enter> when finished or Control-C to exit.\n\n";
 # 4. wait for enter.

 # 5. Get the token from the frob
 my $auth_token = getToken( $ua, $frob );
 die "Failed to get authentication token!" unless defined $auth_token;
 # 6. Tell the user what they won.
 print "Your authentication token for this application is\n\t\t",
  $auth_token, "\n";

 exit 0;

# from the flickr_upload script that comes with Flickr::Upload
sub getFrob
 my $ua = shift;

 my $res = $ua->execute_method("flickr.auth.getFrob");
 return undef unless defined $res and $res->{success};

 # FIXME: error checking, please. At least look for the node named 'frob'.
 return $res->{tree}->{children}->[1]->{children}->[0]->{content};

sub getToken
 my $ua = shift;
 my $frob = shift;

 my $res = $ua->execute_method("flickr.auth.getToken",
                   { 'frob' => $frob } );
 return undef unless defined $res and $res->{success};

 # FIXME: error checking, please.
 return $res->{tree}->{children}->[1]->{children}->[1]->{children}->[0]->{content};

It would be nice if Flickr gave you a way to get the authorization token without this URL visit, but that's not available currently, so you must cope. It's a one-time thing; once you have the authorization token, you can use it until the human race is extinct, the Flickr servers are turned off, or you don't need uploads anymore—whichever comes first.

As an aside, I wish Flickr would distinguish between authentication and authorization in their API documentation. They are very different things, and confusing them leads to confused users. (This has been a message from the Word Police. Thank you.)

To run the application once you have the token, you just enter:

./ key secret token

and if all goes well, it will exit quietly.

Yeah! Uploads!

At last, we can actually upload the image.

Listing 5. Uploading
            photo       => $save_name,
            title       => $title,
            description => $desc,
            auth_token  => $auth_token,
            tags        => 'MoneyCo fund performance',
            is_public   => 1,
            is_friend   => 1,
            is_family   => 1
           ) or die "Failed to upload $save_name: $!";

This part is pretty simple. There are many parameters you can use, all documented in the Flickr Upload API (see Resources). The photo parameter points to the actual file. The title and description are the same as the ones we used for title and subtitle when generating the chart. The tags are static at this point but could be appropriate for whatever chart is being generated. Finally, all the is_whatever parameters make the image really, truly, absolutely public.

An example upload is at

Now, when the user who authorized sees this image uploaded in their photostream, so will the whole world.


You've created randomized data representing fund performance, created a simple stock chart in a PNG file from that data, and uploaded the PNG file to Flickr. The whole process is fully automated.

In real life, of course, the data will not be random. You will have to write the code that gets the data out of a file or a database. Plotting the data will then produce consistent results every time.

Word to the wise: you can make the chart much better. Look at any financial chart for inspiration; common improvements are better labels, pointing out the maximum and minimum, and so on. This is a worthwhile project because better-looking charts will always translate into impressed customers, whereas improving the upload process by using a better Getopt module will not impress them. In other words, if you must produce a visual product, make sure it looks professional.

Good luck with your uploads!


Sample scriptchart_uploadr.zip2KB



Get products and technologies


  • Get involved in the My developerWorks community; with your personal profile and custom home page, you can tailor developerWorks to your interests and interact with other developerWorks users.


developerWorks: Sign in

Required fields are indicated with an asterisk (*).

Need an IBM ID?
Forgot your IBM ID?

Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.


All information submitted is secure.

Dig deeper into Linux on developerWorks

Zone=Linux, Open source, Web development
ArticleTitle=Cultured Perl: Flickr, a business's bst frnd