i have a local hash file that contains exchange rates like this:
GBP=1.0
USD=1.5846492843290054
EUR=1.5031468736658813
now basically, i want to create a script so that when the script is executed it updates the local exchangerates.dat file from WorldPay's up-to-date exchange rate file. The worldpay's exchange rate file is at: select.worldpay.com/wcc/info?op=rates&instId=51602 and it displays like this:
#Exchange rates for installation 51602
#Thu Mar 27 12:20:57 GMT+00:00 2003
rateDateMillis=1048723200000
GBP_GBP=1.0
allRatesCurrent=true
rateDateString=2003-03-27
GBP_EUR=1.5031468736658813
GBP_USD=1.587675023374303
Now basically, GBP_GBP is the base currency so there is no need to update that. I wish to update the GBP_EUR and GBP_USD with the EUR and USD that i have in my local file. So, basically the script will look for the line that reads GBP_USD= and take that value and update the local exchange rate file that reads USD= and the same with GBP_EUR.
I would be very very very greatful if someone can help create such a script.
Many Thanks
Linda
[edited by: jatar_k at 4:44 pm (utc) on Mar. 27, 2003]
[edit reason] delinked [/edit]
To request a resource Perl [perl.com] offers the LWP::UserAgent [perldoc.com] class which implements a http useragent just like your browser. After the resource is retrieved we split [perldoc.com] it on newline and loop over the resulting array. Since we are only interested in lines starting with GBP_ we skip over all other lines. If a line starts with GBP_ it is stripped off. The result is already in the format you want. A simple print [perldoc.com] will print it out to your rates file.
#!/usr/bin/Perl [perl.com]
use [perldoc.com] strict [perldoc.com];
use [perldoc.com] LWP::UserAgent [perldoc.com];
#
my $ua = new LWP::UserAgent;
my $url =
'http://select.worldpay.com/wcc/info?op=rates&instId=51602';
my $cont = '';
my $res = $ua->request(HTTP::Request->new(GET => $url),
sub {$cont .= shift [perldoc.com]});
#
open [perldoc.com] 'OUT', ">rates.dat";
#
foreach (split [perldoc.com] /\n+/, $cont) {
next [perldoc.com] unless /^GBP_/;
s [perldoc.com]/^GBP_//;
print [perldoc.com] OUT "$_\n";
}
#
close [perldoc.com] 'OUT';
I believe your request falls into the do my homework for me category which is frowned upon in WebmasterWorld. I provided a full solution nevertheless since it was just a quick write up. In the future you might want to consider presenting your own solution first and then comming for help. I remember you asking Perl [perl.com] script questions before so it might be a good idea to start learning a bit Perl [perl.com]. That´s the other reason why I provided a full solution and explained what it does.
HTH Andreas
im getting the following error:
Can't locate LWP/UserAgent.pm in @INC (@INC contains: /usr/perl5/5.00503/i86pc-solaris /usr/perl5/5.00503 /usr/perl5/site_perl/5.005/i86pc-solaris /usr/perl5/site_perl/5.005 .) at ./rates.cgi line 5.
I assumed i dont have the UserAgent.pm installed, so i downloaded it from a site that i found and put it in a folder called LWP/UserAgent.pm but i am still getting the same error. Any suggestions?
Cheers
Linda
im still getting a similar error. it says:
Can't locate LWP/UserAgent.pm in @INC (@INC contains: /usr/local/www/virtual/lindajames/exchange/LWP /usr/perl5/5.00503/i86pc-solaris /usr/perl5/5.00503 /usr/perl5/site_perl/5.005/i86pc-solaris /usr/perl5/site_perl/5.005 .) at rates.cgi line 5.
any suggestions?
cheers
linda
Just a small point the code does not check that the request to WorldPay was successful, which I have just tested and it renders the rates.dat file empty.
Just be aware Linda if you are going to use this script in a cron job to update your file every night then it needs more data validation. For example if WorldPay change the format of the file without telling its customers, then your rates.dat will become broke.
exit [perldoc.com] unless $cont;
¬ify, exit [perldoc.com]
if $cont!~ m [perldoc.com]!\nGBP_(EUR¦USD)=\d+\.\d+\n!;
But perhaps dive_into_perl will volunteer to provide a much more sophisticated error checking routine which might involve defining a grammar for the Worldpay file, making a validating parser based on that grammar and checking whether the returned resource adheres to that grammar.
Andreas
Andreas I commend you for supplying full scripts to peoples requests on this forum,
but I have found if people just copy full versions of scripts without understanding
the coding then they are not going to learn any Perl.
Perhaps its better to point people in the right direction, and let them read up and learn.
The level of data validation would depend on Linda's requirements, but if the rates are
to be used in some shopping cart system I would like to ensure my customers are getting
the correct exchange rates.
I would certainly check the return value of the HTTP request before opening the file.
I would then parse the contents and check that GBP, USD and EUR contain only numbers,
and would also check them against upper and lower values, I would do all this before
opening the rates.dat file. I would then check that the file open command succeeded,
before writing the contents to the file.
I am new to this forum but would like to make a few observations. I have 20 years experience in I.T. and have worked mainly with mainframes but more recently with Perl Javascript, PHP, MySQL, PostgreSQL and APache. I am no dummy when it comes to computer languages but I still need help sometimes and appreciate it when someone gives me a helping hand in the form of a complete script. I ususally try to figure out what it is doing for my own interest but sometimes its just beyond my capacity for wanting or even needing to know. The very principle of Object Oriented Programming relies on the fact that programmers can re-use the code without knowing how it works!
I thought forums like this were designed to help one another irrespective of the depth of individuals Knowledge?
As I am a newbie and have never posted to a forum like this before - please be gentle if I have got the wrong end of the stick. Thanks.
And i dont know if anyone can help me solve the actual current problem mentioned above.
Ive intalled all the LWP modules and i am not getting any errors when i run a debug on the script that Andreas provided, but when i access the script directly i get an 500 Internal Server error. Any suggestions would be greatly appreciated.
Cheers
Linda
i tried that its still coming up with 500 error. if the problem was with the header then it should at least update the rates.dat file shouldnt it?
Any ideas of what else it could be?
Hey Andreas i any chance of you commenting out each line (i hope thats not asking tooo much) as this will help me understand more easily
Cheers
Linda
I do believe in learning by example. It helps quite a lot. But I do not have the time nor enough knowledge about the individual situation to make scripts fully featured. So all these scripts really come with an implied "adding error handling as appropriate in one´s special situation left as an excercise to the reader". Although I do admit that if I were really strict with my learning by doing approach then I would have to do that myself. But this is not a book on Perl [perl.com] programming, I don´t get any money so this all I am capable of doing.
lindajames [webmasterworld.com] wrote at 05:43 AM on Mar. 27, 2003 in message #14 [webmasterworld.com]
>>i would really appreciate if you could comment on each
>>line (i hope thats not asking tooo much)
I´m afraid this is indeed asking too much. If you want fully commented scripts then you will need to buy a book on Perl [perl.com] programming. I use my auto-linking proxy to provide links to most Perl [perl.com] and PHP [php.net] functions, Apache [httpd.apache.org] directives, MySQL [mysql.com] syntax, html elements and attributes and CSS [w3.org] properties. So it is rather easy to read up on things in those excellent linked resources. If you come across something that you do not understand feel free to ask. This is more of an interactive medium. Providing the best of both worlds is beyond my available time.
If you do not know what use [perldoc.com] does simply click on the link and the perldocs will tell you that it
[i]mports some semantics into the current package from the named module, generally by aliasing certain subroutine or variable names into your package.
If you read up on open [perldoc.com] those nice Perl [perl.com] folks will tell you that
[w]hen opening a file, it's usually a bad idea to continue normal execution if the request failed, so open is frequently used in connection with die [perldoc.com]. Even if die [perldoc.com] won't do what you want (say, in a CGI [perldoc.com] script, where you want to make a nicely formatted error message (but there are modules that can help with that problem)) you should always check the return value from opening a file.
dive_into_perl [webmasterworld.com] wrote at 06:09 AM on Mar. 27, 2003 in message #15 [webmasterworld.com]
>>I think the 500 server error is caused by running the
>>script via the browser and it's not
>>outputting any headers to the browser.
Indeed, the script was not intended to be run from a browser.
Andreas
Try changing
[url=http://www.perldoc.com/perl5.8.0/pod/func/open.html]open[/url] 'OUT', ">rates.dat"; to read:
open [perldoc.com] 'OUT', ">rates.dat"
or die [perldoc.com] "Can't open rates.dat for writing: $!\n";
To have error messages redirected to the browser add the following to your script:
BEGIN {
use [perldoc.com] CGI::Carp [perldoc.com] qw(fatalsToBrowser);
}
The BEGIN block will cause the code contained in it to be executed as soon as possible, i.e. before the rest of the file is even parsed. That way you can capture most compilation errors as well.
Andreas
Try webmonkey for tutorials on Perl, PHP, Javascript, HTML, CSS etc. Very good info I use them myself. Also a great Perl book is Perl/CGI for the world wide web by Elizabeth Castro. It gives actual code examples on one page and a line by line explanation on the other page. Great for newbies and it only concentrates on useful stuff that you will use in the real world.
Chris.
#!/usr/bin/perl
#!perl
use lib "/usr/local/www/virtual/lindajames/exchange";
use strict;
use LWP::UserAgent;
BEGIN {
use CGI::Carp qw(fatalsToBrowser);
}
#
my $ua = new LWP::UserAgent;
my $url =
'http://select.worldpay.com/wcc/info?op=rates&instId=51602';
my $cont = '';
my $res = $ua->request(HTTP::Request->new(GET => $url),
sub {$cont .= shift});
#
exit unless $cont;
¬ify, exit
if $cont!~ m!\nGBP_(EUR¦USD)=\d+\.\d+\n!;
#
open 'OUT', ">rates.dat"
or die "Can't open rates.dat for writing: $!\n";
#
foreach (split /\n+/, $cont) {
next unless /^GBP_/;
s/^GBP_//;
print OUT "$_\n";
}
#
print "Content-type: text/html\n\n";
print "rates.dat updated";
close 'OUT';
$cont!~ m!\nGBP_(EUR¦USD)=\d+\.\d+\n!; was true, i.e. when the returned resource did not contain at least one line that contained GBP_ followed by either EUR or USD followed by = followed by one or more digits followed by a dot followed by one or more digits followed by a newline.
sub notify {
my ($title, $message) = @_;
my $mailprogram = 'path/to/sendmail';
open [perldoc.com] 'MAIL', "¦$mailprogram -t"
or die [perldoc.com] "Couldn't pipe mail to $mailprogram: $!\n";
print [perldoc.com] MAIL <<"ENDOFMAIL";
To: Linda <linda\@example.com>
From: Exchange Rates Script <rates\@example.com>
Subject: Format of Rates File Changed
__EMPTY_LINE_TO_DELIMIT_MAIL_HEADER_FROM_BODY__
Dear Linda,I'm writing you this email to inform you that the
format of the Worldpay exchange rates file changed.Date: ${\(scalar [perldoc.com] localtime [perldoc.com])}
Yours
A polite Perl [perl.com] script
ENDOFMAIL
close [perldoc.com] 'MAIL';
}
print [perldoc.com] MAIL <<"ENDOFMAIL"; is a nice way of quoting the entire message until ENDOFMAIL.
The line containing the date is a great example of a little bit of Perl [perl.com] sneakyness. It interpolates the call to localtime [perldoc.com] into the message string. This works since in a double quoted string @, $ and \ have special meaning. \() creates an anymous scalar reference which is derefenced by ${}. scalar [perldoc.com] is necessary to force localtime [perldoc.com] to scalar [perldoc.com] context to make it return a date string instead of a list of date components.
Andreas
#!/usr/bin/Perl [perl.com]
#!Perl [perl.com]
use lib "/usr/local/www/virtual/lindajames/exchange";
use strict;
use LWP::UserAgent;
BEGIN {
use CGI::Carp qw(fatalsToBrowser);
}
#
my $ua = new LWP::UserAgent;
my $url =
'http://select.worldpay.com/wcc/info?op=rates&instId=51602';
my $cont = '';
my $res = $ua->request(HTTP::Request->new(GET => $url),
sub {$cont .= shift});
#
die 'no content' unless $cont;
¬ify, die 'format changed'
if $cont!~ m!\nGBP_(EUR__PIPE__USD)=\d+\.\d+\n!;
#
open 'OUT', ">rates.dat"
or die "Can't open rates.dat for writing: $!\n";
#
foreach (split /\n+/, $cont) {
next unless /^GBP_/;
s/^GBP_//;
print OUT "$_\n";
}
#
close 'OUT';
#
print "Content-type: text/html\n\n";
print "rates.dat updated";
#
sub notify {
my ($title, $message) = @_;
my $mailprogram = '__SENDMAIL__';
open 'MAIL', "__PIPE__$mailprogram -t"
or die "Couldn't pipe mail to $mailprogram: $!\n";
print MAIL <<"ENDOFMAIL";
To: Linda <linda\@example.com>
From: Exchange Rates Script <rates\@example.com>
Subject: Format of Rates File Changed
__EMPTY_LINE__
Dear Linda,
I'm writing you this email to inform you that the format of the
Worldpay exchange rates file changed.
Date: ${\(scalar localtime)}
Yours
A polite Perl [perl.com] script
ENDOFMAIL__NOTHING__
close 'MAIL';
}
This code works for me. You need to adjust all __PLACEHOLDERS__. Make sure that ENDOFMAIL is on a line by itself. No whitespace in front, no whitespace behind. It must be followed by a newline immediately. The empty line needs to be empty. The pipe needs to be the real pipe and __SENDMAIL__ the path to sendmail.
Andreas