#!/usr/bin/perl
# file: rdann             G. Moody        9 April 2000
#                         Last revised:   29 December 2004
# _____________________________________________________________________________
# CGI program to convert PhysioBank signals to text
# Copyright (C) 2000-2004 George B. Moody
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# You may contact the author by e-mail (george@mit.edu) or postal mail
# (MIT Room E25-505A, Cambridge, MA 02139 USA).  For updates to this software,
# please visit PhysioNet (http://www.physionet.org/).
# _____________________________________________________________________________

use CGI qw/:standard/;
use CGI::Carp 'fatalsToBrowser';

$| = 1;

# _____________________________________________________________________________
# Read the list of databases and their descriptions (once only).
if (!$databases[0]) {
    read_dblist();
}

# Decide what is to be done next.
if (param) {
    $action = param('doit');  # the name of the button that was clicked, if any
}
else {
    $action = "Select database";  # default: choose a database on entry
}

if ($action eq "Select database") {
    choose_db();
}
else {  # database has been chosen; get list of records
    $database = param('database');
    if (!param('record')) {
	choose_record();
	$action = "Continue";
    }
    $record = param('record');
    if ($record =~ /.+\n/) {
	chop($record);
    }
    $annotator = param('annotator');
    $tstart = param('tstart');
    $tend = param('tend');
    if ($action ne 'Continue') {
	output_results(); # depending on which button clicked, show or email
    }
}

# _____________________________________________________________________________
# Read the list of available databases.
sub read_dblist {
    $dblistfile = '/home/physionet/html/physiobank/database/DBS-annotated';

    if (open(DBS,$dblistfile)) {
	@dblist = <DBS>;
	$i = 0;
	foreach $d (@dblist) {
	    @fields = split(/\t+/,$d);
	    chop($fields[1]);
	    $dblist[$i++] = $fields[0];
	    $dblabels{$fields[0]} = $fields[1] . " (" . $fields[0] . ")";
	}
    }
    else {
	@dblist = ('');
    }
}

# _____________________________________________________________________________
# Show the database choice form.
sub choose_db {
    print header, start_html(-dtd=>'-//IETF//DTD HTML//EN',
			     -bgcolor=>'white',-title=>'rdann-O-Matic');
    print_header();
    print start_form,
        table({-border=>0,-bgcolor=>'#d0d0ff',-width=>'100%'},
    		Tr({-align=>CENTER,-valign=>CENTER},
		   [
		    th('Database:'.
		       popup_menu(-name=>'database',
				  -value=>[@dblist],
				  -labels=>{%dblabels}).
		       submit(-name=>'doit',-value=>'Continue'))
		    ]
		   )),
	  end_form;
    print h2('rdann-O-Matic: Convert annotations to text');
    print <<EOF;
<p>
The rdann-O-Matic allows you to convert binary annotation files from PhysioBank
into text form.  Please choose a database from the list above, then click
on <em>Continue</em>.  Once you have done so, you will be able to choose a
record, an annotator (a set of annotations), and the starting and ending times
for the conversion.  You will be able to view the text with your web browser,
or you can have it sent to you via e-mail.

<p>
A key for the annotation codes is available
<a href="/physiobank/annotations.shtml">here</a>.
To view the annotations together with the associated signals, try the
<a href="/cgi-bin/chart">Chart-O-Matic</a>.  To view the digitized signals
as text, try the <a href="/cgi-bin/rdsamp">rdsamp-O-Matic</a>.
<hr>
EOF
    print_footer();
}

# _____________________________________________________________________________
# Read the list of records for the chosen database.
sub read_rlist {
    $rlistfile = '/home/physionet/html/physiobank/database/' . $database .
	'/RECORDS';

    if (open(RECORDS,$rlistfile)) {
	@rlist = <RECORDS>;
    }
    else {
	@rlist = ('');
    }
}
# _____________________________________________________________________________
# Read the list of annotators for the chosen database.
sub read_alist {
    $alistfile = '/home/physionet/html/physiobank/database/' . $database .
	'/ANNOTATORS';
    if (open(ANNOTATORS,$alistfile)) {
	@annotators = <ANNOTATORS>;
	$i = 0;
	foreach $a (@annotators) {
	    @fields = split(/\t+/,$a);
	    chop($fields[1]);
	    $alist[$i++] = $fields[0];
	    $adlist{$fields[0]} = $fields[0] . " (" . $fields[1] . ")"; 
	}
    }
    else {
	$alist[0] = '';
	$adlist{''} = ('(no annotations)');
    }
}

# _____________________________________________________________________________
# Show the record/start/end choice form.
sub choose_record {
    read_rlist();
    read_alist();
    print header, start_html(-dtd=>'-//IETF//DTD HTML//EN',
			     -bgcolor=>'white',-title=>'rdann-O-Matic');
    print_header();
    print start_form,
          table({-border=>0,-bgcolor=>'#d0d0ff',-width=>'100%'},
		Tr({-valign=>CENTER},
		   [
		    th({-align=>RIGHT},
		       'Database:') .
		    td({-align=>LEFT}, '<a href=/physiobank/database/' .
		       $database . "/>" . $dblabels{$database} . "</a>",
		       submit(-name=>'doit', -value=>'Select database')),

		    th({-align=>RIGHT},'Record:') .
		    td({-align=>LEFT}, popup_menu(-name=>'record',
						 -value=>[@rlist])) .
		    td({-align=>RIGHT}, '<a href=/cgi-bin/chart?database=' .
		       $database . '&record=' . $record . '&annotator=' .
		       $annotator . '&tstart=' . $tstart .
		       '&width=medium>View signals</a>'),

		    th({-align=>RIGHT},'Annotator:') .
		    td({-align=>LEFT},popup_menu(-name=>'annotator',
						-value=>[@alist],
						-labels=>{%adlist})),

		    th({-align=>RIGHT},'Start time:') .
		    td({-align=>LEFT},textfield(-name=>'tstart',
						-size=>40, -value=>'0')) .
		    td({-align=>RIGHT},
		   '<a href=/physiobank/annotations.shtml>Annotation key</a>'),


		    th({-align=>RIGHT},'End time:') .
		    td({-align=>LEFT},textfield(-name=>'tend',
						-size=>40, -value=>'end')),

		    td({-align=>RIGHT},
		       submit(-name=>'doit',-value=>'Show annotations')) .
		    td({-align=>LEFT},submit(-name=>'doit',
					     -value=>'E-mail annotations to:'),
		       textfield(-name=>'email',-size=>20)) .
		    td({-align=>RIGHT},submit(-name=>'doit',
					      -value=>'rdann-O-Matic Help'))
		    ]
		   )),
	  hidden(-name=>'database',-value=>$database),
          end_form;
}

# _____________________________________________________________________________
# Show instructions for filling in the record choice form.
sub print_help {
    print h2('rdann-O-Matic: Convert annotations to text');
    print <<EOF;

<p> Please choose a record and an annotator (a set of annotations)
from the list above.  In the start time field, enter the elapsed time
from the beginning of the record, in <em>hh:mm:ss</em> format.  For
example, to begin two minutes from the beginning of the record, enter
<tt>2:0</tt> in the start time field (leading zero digits may be
omitted).  Enter the end time in the same format.  As a special case,
use <tt>e</tt> or <tt>end</tt> in the end time field to specify that
you wish to list annotations until the end of the record.
(Four to six hours of annotations typically convert to about one megabyte of
text.)

<p>
To view the requested data with your web browser, click on
<em>Show annotations</em>.
Cut and paste the text or use the <em>Save</em> or <em>Save As</em> features
provided by your browser to copy the output to a file on your disk. 

<p> To transmit the requested data via e-mail, enter
your e-mail address in the space provided above, then click on
<em>E-mail annotations to:</em>.  The e-mail will be sent within a few
seconds, although your mail server may take a few minutes to transfer it
to your mailbox.  If you do not receive it within a reasonable time,
try specifying a shorter segment, since your mail server may reject very long
messages.

<p>
The output is generated by
<a href="/physiotools/wag/rdann-1.htm"><tt>rdann</tt></a>,
which is freely available in portable C source form and in precompiled binary
versions for several popular operating systems.  You may run <tt>rdann</tt>
on your own computer to convert annotation files from PhysioNet and other
sources into text form. 
<hr>
EOF
}

# _____________________________________________________________________________
# Show or email the selected data.
sub output_results {
    choose_record();
    set_rdann_variables();
    if ($action eq 'rdann-O-Matic Help') {
	print_help();
	print_footer();
	return;
    }

    # Convert the selected data and save in a temporary file.
    $tfile = "/tmp/rdann.$$";
    unless (fork) {
	open(STDOUT, ">$tfile");
	exec("/usr/bin/rdann", "-r", $database . "/" . $record, "-a",
	     $annotator, "-f", $tstart, "-t", $tend, "-v");
    }
    # When rdann is finished, check if it wrote anything.
    wait;
    open(RDANN, $tfile);
    $annotations = <RDANN>;
    if (!$annotations) {
	print h1('No annotations!');
	print <<EOF;
<p>
No annotations were read.  This might occur if the record or annotator name
is incorrect, if the start or end time is improperly formatted, or if no
annotations exist between the start and end times specified.  Please check
and correct your entries above.

<p>
If you are using one of the PhysioNet mirrors, it may not be able to convert
annotations to text;  in this case, try using the <a href="http://www.physionet.org/cgi-bin/rdann">rdann-O-Matic on the master PhysioNet server</a>.
<hr>
EOF
        print_footer();
    }

    else {  # rdann was successful!
	if ($action eq 'E-mail annotations to:') {
	    output_email();
	}
	else {
	    print '<pre>';
	    print $annotations;
	    while ($annotations = <RDANN>) {
		print $annotations;
	    }
	    print '</pre>';
	}
    }
    unlink($tfile);
}

# _____________________________________________________________________________
# Set variables for the rdann command-line options.
sub set_rdann_variables {
    $database = param('database');
    $record = param('record');
    $annotator = param('annotator');
    if ($record =~ /.+\n/) {
	chop($record);
    }
    $tstart = param('tstart');
    $tend = param('tend');

    if (!$database) {
	$database = "mitdb";
    }
    if (!$record) {
	$record = "100";
    }
    if (!$annotator) {
	$annotator = "atr";
    }
    if (!$tstart) {
	$tstart = 0;
    }
    if (!$tend) {
	$tend = "end";
    }
    if (!$action) {
	$action = "Show samples";
    }
}

# _____________________________________________________________________________
# Send the output as email.
sub output_email {
    my $email = param('email');

    # Check that the user entered a plausible email address.
    if ($email =~ /^([-\w.]+)@([-\w]+)\.([-\w.]+)$/) {
	my $subject = 'Annotations of record ' . $database . '/' . $record .
	    ', annotator ' . $annotator;
	system("/bin/mail -s '$subject' $email <$tfile");
	print h1('Data sent');
	print <<EOF;
<p>
The requested data have been sent to $email.
EOF
    }
    else {
	print h1('E-mail address needed');
	print <<EOF;
<p>
If you wish to transmit the requested data via e-mail, enter your e-mail
address in the space provided on the form, then click again on <em>E-mail
annotations to:</em>.

<p>
If you wish to view the requested data with your web browser, click on
<em>Show annotations</em>.  <strong>The data will appear in another
window.</strong>
<hr>
EOF
    }
    print_footer();
o}

# _____________________________________________________________________________
# Boilerplate header.
sub print_header {
    if (open(HEADER,"/home/physionet/html/links-physiobank.html")) {
	while (<HEADER>) {
	    print $_;
	}
    }
}

# _____________________________________________________________________________
# Boilerplate footer.
sub print_footer {
    print <<EOF;

<font size=-1><p>
Please e-mail your comments and suggestions to 
<a
href="mailto:webmaster\@physionet.org?subject=rdann-O-Matic"><tt>webmaster\@physionet.org</tt></a>, 
or post them to:
<p>
<i><address>
PhysioNet<br>
MIT Room E25-505A<br>
77 Massachusetts Avenue<br>
Cambridge, MA 02139 USA<br></address></i>
<p>
Updated Friday, 10 September 2004 at 11:28 EDT
</font>
EOF
    print end_html;
}
