blink has asked for the wisdom of the Perl Monks concerning the following question:

I have been asked to add a colorized set of HTML anchors to the top of a report that I am generating. My problem is, that I'd like to do this without reiterating through a hash. Here's what I mean:
################################# # open report files for printing # yes, @fhandles is populated # yes, I am using strict # yes, I am using perl -w # = ) my $filepath = "/usr/openv/scripts"; open $fhandles[0], ">>$filepath/nt.html" or die "cannot open $filepath/nt.html for writing: $!\n"; open $fhandles[1], ">>$filepath/mail.html" or die "cannot open $filepath/mail.html for writing: $!\n"; open $fhandles[2], ">>$filepath/unix.html" or die "cannot open $filepath/unix.html for writing: $!\n"; ##### code................... # Print HTML-header the hard way... foreach (@fhandles) { my $team; $outhandle = $_; if ( $outhandle =~ /NT/ ) { $team = "Windows Infrastructure Team"; } if ( $outhandle =~ /MAIL/ ) { $team = "Mail Team"; } if ( $outhandle =~ /UNIX/ ) { $team = "UNIX Infrastructure Team"; } print $outhandle <<EOC <html> <head> <title>[Backup report] $humanstart - $humanstop</title> </head> <body bgcolor="#ffffff"> <h2 align="left">Daily Backup Report: $team</h2> <h3 align="left">From $humanstart to $humanstop</h3> EOC } ## ## ## HERES WHERE I WANT TO PLACE MY LIST OF HTML ANCHORS ## ## foreach my $class ( sort keys %classHash ) { foreach my $status ( sort keys %{$classHash{$class}} ) { if ( $classname{$class} eq "NT" ) { $outhandle = *NTOUT; } if ( $classname{$class} eq "UNIX" ) { $outhandle = *UNIXOUT; } if ( $classname{$class} eq "MAIL" ) { $outhandle = *MAILOUT; } if ($status eq "success") { print $outhandle <<SUC; <table border=1 width="100%"> <tr> <td colspan="12" align="left" valign="center" bgcolor="#33cc33"> <b><font size="+2">$class: Successful Jobs</b></font> </td> </tr> SUC } elsif ($status eq "partial" ) { print $outhandle <<PAR; <table border=1 width="100%" > <tr> <td colspan="12" align="left" valign="center" bgcolor="#ffcc00"> <b><font size="+2">$class: Partially Successful Jobs</b></font> </td> </tr> PAR } elsif ( $status eq "failed" ) { print $outhandle <<FAIL; <table border=1 width="100%"> <tr> <td colspan="12" align="left" valign="center" bgcolor="#ff0000"> <b><font size="+2">$class: Failed Jobs</b></font> </td> </tr> FAIL } elsif ( $status eq "active" ) { print $outhandle <<ACT; <table border=1 width="100%"> <tr> <td colspan="12" align="left" valign="center" bgcolor="#c1ffc1"> <b><font size="+2">$class: Active Jobs</b></font> </td> </tr> ACT } elsif ( $status eq "queued" ) { print $outhandle <<QUE; <table border=1 width="100%"> <tr> <td colspan="12" align="left" valign="center" bgcolor="#b23aee"> <b><font size="+2">$class: Queued Jobs</b></font> </td> </tr> QUE } print $outhandle <<EOC; <tr> <th align="center">Hostname</th> <th align="center">Job-ID</th> <th align="center">Schedule</th> <th align="center">Status</th> <th align="center">Server</th> <th align="center">Files</th> <th align="center">Tries</th> <th align="center">Start </th> <th align="center">End </th> <th align="center">Duration</th> <th align="center">Mbytes</th> <th align="center">Speed</th> </tr> EOC foreach my $id ( @{$classHash{$class}->{$status}} ) { my $thisjob = $alljobs{$id}; my $MB = $thisjob->{kbytes} / 1000; &print_report_line( $thisjob->{client}, $thisjob->{jobid}, $thisjob->{schedule}, $thisjob->{status}, $thisjob->{server}, $thisjob->{files}, $thisjob->{trycount}, &epoch($thisjob->{started}), &epoch($thisjob->{ended}), $thisjob->{elapsed}, #&beautify_number($thisjob->{kbytes}), &round($MB), ($thisjob->{elapsed} gt 0 ? int(($thisjob->{kbytes}/$thisjob->{elapsed})*1 +00)/100 : "0.00")); if ($status eq "failed" ) { &print_errortext_line +($thisjob->{status}); } } print $outhandle "</table><br>\n"; } }
What I would like to do is, foreach "my $class ( sort keys %classHash )", place a colorzed (based upon status) link to the actual stats on the report. In other words, at the place in the code that I have commented:

failed class 1 failed class 2 successful class 1

... and so on, WITHOUT having to reiterate through the two hashes.

Replies are listed 'Best First'.
Re: Writing to specific position in a file
by graff (Chancellor) on Aug 05, 2002 at 03:11 UTC
    Okay, first one possible answer. Let me rephrase the situation, to see if I got it:

    You want to create colored links near the top of the page, and these links will point into a big table that comes afterwards on the same page, and you won't really know where the link targets will be in that table until you actually process the big hash array(s) of table data -- but you would prefer to process all that data only once. Right? I agree with that.

    The answer: create a server-side include statement at the spot where you want to include the colored links, and open an output file that can be included at that point; e.g. something like this:

    print $outhandle "<!--#include virtual=\"my_link.incl\"-->\n"; open( LINKER, ">my_link.incl" );
    Now, once you go into that grotty part about actually writing the table based on the hash data, just figure out what points need to be link targets, and how the colorized link tag should be written, then write that link tag to the LINKER file handle.

    When you close the page, close the linker file. You're done.

    Now about that code... Naturally, many folks here will say you should be using CGI.pm, and they're probably right. but it looks like you're creating static html content here, so nevermind that.

    You said: # yes, I am using strict But I couldn't find the declarations for the crucial data hashes ("%alljobs"? "%classname"? "%classHash"?) Well, nevermind that.

    Try to think how you would refactor some of the logic into a subroutine; instead of a chain of "if ... elsif ... elsif ... elsif ...", where the blocks are all pretty much the same activity, just use one subroutine call, like

    printTableRow( $outhandle, $status, $class ); # and maybe this is where you do your linkage, too: print LINKER $linkstring if ( $need_to_link_here );
    and put hashes in the sub that map the varying strings and colors to $status, since that is all that differs:
    sub printTableRow { my ($handle,$status,$class) = @_; my %colors = ( active => "#c1ffc1", failed => "#ff0000", queued => "#b23aee", partial => "#ffcc00", success => "#33cc33", ); # and a similar hash (%title) for "Active Jobs", etc... print $handle <<ROW; <tr> <td colspan="12" align="left" valign="center" bgcolor="$colors{$st +atus}"> <b><font size="+2">$class: $title{$status}</b></font> </td> </tr> ROW }
    Notice that no "if ... elsif ... elsif ..." is needed this way, and the "ROW" heredoc only needs to be typed once. Makes things easier to maintain later. Also, I think you want this bit:
    print "\n<table border=1 width=\"100%\">\n";
    to be outside the double loop, rather than inside, and you'll then want something like this after the loops:
    print "\n</table>\n";

    And maybe you could work on using proper indentation. Everybody likes that.

    update: I'm sure you'll realize that you would actually need four separate linker files, since you are creating four different html files. This level of the structure is probably another case where you may be better off by refactoring into a subroutine that you call four times, to do one page/include.file at a time -- this should not require changing the underlying data hashes.

      Big ++ for your reply. Not only did you cleverly point me to a solution to my problem, but you've taught me a few things in the process. Thanks for making time for your response.