1
votes

I have a script which fetches the summary file from the NCBI website using command line argument (accession number).

Example:

./efetch.pl NM_000040

Now I am trying to fetch the same file using a HTML webpage which takes the form request via a CGI script.

My question: Is it possible to combine the CGI and my Perl script in one file and pass the HTML form argument from the CGI portion of the code to the perl script in single run?

I have tried to do some scripting but it seems that the argument from the CGI is not getting passed to the Perl script.

Any help will be greatly appreciated.

CGI and Perl Script in one single file:

#!/usr/bin/perl -wT
use strict;
use warnings;
use LWP::Simple;
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);

################### Environmental Variables ###################
my ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;
if ($ENV{'REQUEST_METHOD'} eq "POST")
{
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
} else {
$buffer = $ENV{'QUERY_STRING'};
}
#print "$buffer\n";
# Split information into name/value pairs
@pairs = split(/&/, $buffer);
foreach $pair (@pairs) {
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
#$value =~ s/%(..)/pack("C", hex($1))/eg;
$FORM{$name} = $value;
}
my $access    = $FORM{accession};
if ($access =~ m{\A(\w+\d+)\z}) {
$access = $1;
}

print "Content-type:text/html\r\n\r\n";
print "<html>";
print "<head>";
print "<title> CGI Program</title>";
print "</head>";
print "<body>";
if ($access eq "") {
    print "<h2> Please check the accession number</h2>";
exit;
}
print "<h2>$access</h2>";
print "</body>";
print "</html>";
print <HEADING
<html>
 <head>
  <title> Output result of the program </title>
 </head>
<body>
<h1> Summary result </h1>
 <table border=1>
  <tr>
   <th>S.No.</th>
   <th>Fragment</th>
   <th>Position</th>
   <th>Region</th>
   <th>GC%</th>
  </tr>
HEADING
;

######################## INPUT PARAMETERS #####################
my $utils = "http://www.ncbi.nlm.nih.gov/entrez/eutils";
my $db     = "nuccore";
my $query  = $access; #"$ARGV[0]" or die "Please provide input for the accession number. $!"; 

############### END OF INPUT PARAMETERS ######################
############### FILE DOWNLOAD FROM NCBI ######################
my $report = "gb";   # downloads the summary text file
open (IN,">", $query.".summary");

my $esearch = "$utils/esearch.fcgi?" . "db=$db&retmax=1&usehistory=y&term=";
my $esearch_result = get($esearch . $query);

$esearch_result =~ m|<Count>(\d+)</Count>.*<QueryKey>(\d+)</QueryKey>.*<WebEnv>(\S+)</WebEnv>|s;
my $Count    = $1; my $QueryKey = $2; my $WebEnv   = $3;
my $retstart; my $retmax=3;
for($retstart = 0; $retstart < $Count; $retstart += $retmax) {
my $efetch = "$utils/efetch.fcgi?" .
"rettype=$report&retmode=text&retstart=$retstart&retmax=$retmax&" .
"db=$db&query_key=$QueryKey&WebEnv=$WebEnv";
my $efetch_result = get($efetch);
print IN $efetch_result, "\n";
}
close (IN);

Print command in the perl script prints the $access but it fails to pass the value of $access to $query.

HTML form:

<form action="/cgi-bin/efetch.cgi" method="post" id="myform">
<div>
    NCBI accession number:<label for="accession"> <input type="text" name="accession">       </label><br>
    <input type="submit" value="Submit" form="myform">
 </div>
</form>
2
Where did you pick up your CGI programming from? You are using techniques that have been out of date for fifteen years. If you're sure that you want a CGI program (and in 2014 that's questionable) at least look at the CGI module which will reduce the first half of your program to about three lines of code.Dave Cross

2 Answers

0
votes

Your script is much more complicated than it needs to be. Specifically - you're using the CGI module (which is deprecated, so you might want to consider something else*) but then you're trying to roll your own input handling in your script.

You can write a single script that sends 'POST' or 'GET' data to itself for processing. That's not too difficult at all.

A simple example might be

#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;

print "Content-Type: text/html\n\n";

my %param;
while ( <STDIN> ) {
   my ( $key, $value ) = split ( "=" );
   $param{$key} = $value;
}

print Dumper \%param;

print "<FORM METHOD=\"POST\">\n";
print "  <INPUT TYPE=\"text\" NAME=\"access\">\n";
print "  <INPUT TYPE=\"submit\">\n";
print "</FORM>\n";

This isn't a good example, but it'll work, and hopefully it'll give you an idea of what's going on - POSTed stuff comes on STDIN. GET stuff comes in the URL.

You can test for the existence of such input, and either render your basic form or process the input you got.

 if ( $param{'access'} ) { 
      #process it;
 else {
       #print form;
 }

There are many modules that make this easier (you're even using one already, in the form of CGI), so I wouldn't EVER suggest doing it this way 'for real' - this is purely an illustration of the basics.

With the CGI module, which is perhaps the thing that'd require least code alteration, you could use the 'CGI::param()' method to retrieve parameters:

use CGI;

print CGI::header;
print CGI::param('access'); 

#form stuff. 

But a more complete one would be to consider a bit more of an in-depth rewrite, and consider using one of the more up to date 'web handling' frameworks. There really are a lot of potential gotchas. (Although it does depend rather, on how much control over your environment you have - internal/limited user scripts I'm a lot more relaxed about than internet facing).

* See: CGI::Alternatives

0
votes

Writing a CGI program in 2014 is a lot like using a typewriter. Sure, it'll work, but people will look at you very strangely.

But given that you already have a CGI program, let's look at what it might look like if you used techniques that weren't out of date in the last millennium.

There are basically two underlying problems with your code.

  1. You open a file using a name that comes from user input. And that violates the taint mode rules, so your program dies. You would have seen this in your web server error log, had you looked there.
  2. You don't actually need to write the data to a file, because you want to send the data to the user's web browser.

So here's an improved version of your code. It fixes the two problems I mentioned above but it also uses modern tools.

  1. The CGI module has a param() method which makes it far easier for us to get the parameters passed to our program. We also use its header() method to output the CGI header (basically just the Content-type header).
  2. We use the Template module to move all of the HTML out of out code and put it in a separate area. Here I've cheated slightly and have just put it in the DATA section of the CGI program. Usually we'd put it in a completely separate file. Notice how separating the Perl and the HTML makes the program look cleaner and easier to maintain.

It wasn't clear to me exactly how you wanted to format the data you're getting back from the other web site. So I've just stuck it in a "pre" tag. You'll need to work that out for yourself.

Here's the code:

#!/usr/bin/perl -T
use strict;
use warnings;
use LWP::Simple;
use Template;
use CGI ':cgi';
use CGI::Carp qw(warningsToBrowser fatalsToBrowser);

my $access = param('accession');

my $utils = "http://www.ncbi.nlm.nih.gov/entrez/eutils";
my $db     = "nuccore";
my $query  = $access;
my $report = "gb";   # downloads the summary text file
my $esearch = "$utils/esearch.fcgi?" . "db=$db&retmax=1&usehistory=y&term=";
my $esearch_result = get($esearch . $query);

my $data = '';
if (my ($Count, $QueryKey, $WebEnv) = $esearch_result =~ m|<Count>(\d+)</Count>.*<QueryKey>(\d+)</QueryKey>.*<WebEnv>(\S+)</WebEnv>|s) {
  my $retstart;
  my $retmax=3;
  for ($retstart = 0; $retstart < $Count; $retstart += $retmax) {
    my $efetch = "$utils/efetch.fcgi?" .
      "rettype=$report&retmode=text&retstart=$retstart&retmax=$retmax&" .
      "db=$db&query_key=$QueryKey&WebEnv=$WebEnv";
    my $efetch_result = get($efetch);
    $data .= $efetch_result;
  }
}

my $tt = Template->new;
print header;
$tt->process(\*DATA, { data => $data })
  or die $tt->error;

__END__
<html>
  <head>
    <title> CGI Program</title>
  </head>
  <body>
    <h1>Input</h1>
    <form action="/cgi-bin/efetch.cgi" method="post" id="myform">
      <div>NCBI accession number:<label for="accession"> <input type="text" name="accession"></label><br>
        <input type="submit" value="Submit" form="myform"></div>
    </form>
[% IF data -%]
    <h1>Summary Result</h1>
    <pre>
[% data %]
    </pre>
[% END -%]
  </body>
</html>