http://qs321.pair.com?node_id=126950

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

I'm writing a web based calander, and want to display a month (or more) in a html table, add links and pull out whatever data is associated with the link (ala the yahoo datebook).

Now i've got it printing out a nice little table with all the dates in the correct spots etc, (which is good) but i'm iterating thru' three loops. one to pull out the days, one to build a monthly matrix (much like the `cal` command), and the final loop builds the html.

I'm thinking this is pretty slow and inefficient, and was wondering if there is a better way.

I thought of using a hash for the number of days in a month (and making the appropriate exceptions for a LY, then determining what day is the 1st of the month) however i'm still stuck with two loops.

Naturally any comments on the style of the code appreciated.

#!/usr/bin/perl -w use strict; use Data::Dumper; use Date::Calc qw( Delta_Days Add_Delta_Days Date_to_Text ); use CGI; my $q = new CGI; sub date_range { my(@date) = (@_)[0,1,2]; my(@list); my($i); my $output = ''; my $date; my %pos = (Su=>1, Mo=>2, Tu=>3, We=>4, Th=>5, Fr=>6, Sa=>7 ); my @box; $i = Delta_Days(@_); while ($i-- >= 0) { push( @list, [ @date ] ); @date = Add_Delta_Days(@date, 1) if ($i >= 0); } #build the matrix to hold the days. my @box; #the arrray to hold the calendar data my $row=0; foreach $date (@list) { my $col = $pos{substr(Date_to_Text(@{$date}),0,2)}; $box[$row][$col]= (@{$date}[2]); if ($col eq 7) {$row++} } for (my $i=0; $i<5; $i++){ $output .= "<tr>"; for (my $j=0; $j<8;$j++){ if (! defined $box[$i][$j]) { $output .= $q->td({align=>"right"},"&nbsp"); } else { $output .= $q->td({align=>"right"},$box[$i][$j]); } } $output .= "</tr>"; } return $output; } print $q->header(); print $q->table(&date_range(2001,11,1, 2001,11,30)); # in chronologica +l order

Replies are listed 'Best First'.
Re: Web month Calander
by growlf (Pilgrim) on Nov 22, 2001 at 15:34 UTC
    Hmm, is there a reason not to use HTML::CalendarMonth? Like so:



    Updated (removed html-assubs, since CGI does all this anyhow)

    #!/usr/bin/perl -w use strict; use HTML::CalendarMonth; use CGI; my $page = new CGI; my $calendar = HTML::CalendarMonth->new(); # Lets do some customization here, since today IS thanksgiving after a +ll $calendar->item($calendar->year, $calendar->month)->attr(bgcolor => '# +FF8000'); $calendar->item(22)->attr(bgcolor => '#FFD080'); $calendar->item(22)->wrap_content( new HTML::Element 'a', href => 'http://www.openresources.com/cgi-bin/ +dict?Form=dict2&Database=*&Query=Thanksgiving+Day', target=> '_blank'); print $page->header(); print $calendar->as_HTML;
    This results in a nice little calendar with alot of options already built for you like so:
    November2001
    SuMTuWThFSa
            123
    45678910
    11121314151617
    18192021222324
    252627282930 

    Please forgive the blatent and almost unhidden plagerism from PerlDoc.com

    *G*

      the only reason was ignorance! i had no idea it existed. sometimes i just launch head-long into things, without doing decent research beforehand.

      thanks for the tip

      In the interest's of efficiency, i'm still interested in how the above code could by modified.
(crazyinsomniac) Re: Web month Calander
by crazyinsomniac (Prior) on Nov 22, 2001 at 19:39 UTC
    Well here's one way to modify it (take advantage of CGI.pm's table generating capabilities, and use map -- don't wanna look at Date::Calc at the moment, maybe tomorrow ;D)
    #!/usr/bin/perl -w use strict; use Data::Dumper; use Date::Calc qw( Delta_Days Add_Delta_Days Date_to_Text ); use CGI; my $q = new CGI; sub date_range { my(@date) = (@_)[0,1,2]; my(@list); my($i); my $output = ''; my $date; my %POS = (Su=>1, Mo=>2, Tu=>3, We=>4, Th=>5, Fr=>6, Sa=>7 ); $i = Delta_Days(@_); while ($i-- >= 0) { push( @list, [ @date ] ); @date = Add_Delta_Days(@date, 1) if ($i >= 0); } #build the matrix to hold the days. my @box; #the arrray to hold the calendar data my $row=0; foreach $date (@list) { my $col = $POS{substr(Date_to_Text(@{$date}),0,2)}; $box[$row][$col]= (@{$date}[2]); $row++ if ($col == 7); } ## for the header ## &nbsp; is in here because array indices start at 0, not one ## and I didn't feel like modifying your code above (-1) $output.= $q->Tr( [ $q->th( [ qw( &nbsp; Su Mo Tu We Th Fr Sa) ] ) ] ); ## gets warnings, since you don't init the array #$output.= join '', map { $q->Tr( [ $q->td( [ @{$_} ]) ] ) } @box +; ## no warnings, cause i test for definedness $output.= join '', map { $q->Tr( [ $q->td( [ map { defined $_ ? $_ :" " } @{$_} ,] ,) ,] ,) } @box; } print $q->header(); ## since => is just a fancy comma, print $q->table( &date_range(2001,11,1 => 2001,11,30) ); ## or for even "better" readability print $q->table( &date_range(qw/2001 11 1/ => qw/2001 11 30/) ); __END__ Yielding the following:
    Content-Type: text/html; charset=ISO-8859-1
      Su Mo Tu We Th Fr Sa
    1 2 3
    4 5 6 7 8 9 10
    11 12 13 14 15 16 17
    18 19 20 21 22 23 24
    25 26 27 28 29 30

     
    ___crazyinsomniac_______________________________________
    Disclaimer: Don't blame. It came from inside the void

    perl -e "$q=$_;map({chr unpack qq;H*;,$_}split(q;;,q*H*));print;$q/$q;"

Re: Web month Calender
by jlongino (Parson) on Nov 22, 2001 at 22:38 UTC
    Ryszard, it looks to me that you are well on your way. I've modified/written something similar for our University calendar that you might want to look at for some ideas on layout. It provides several different views (6-month calendar, plain-text monthly, weekly and daily views). The link is USA Campus Calendar. I borrowed the core 6-month view code from an Ill. University (Urbana, I think, with their permission of course). It reads directly from the xnix cal command.

    I don't really see that the speed to create the calendar table itself will present much of a bottleneck. It is more likely that fetching your data will take much more time (depending on your method), particularly if you have to retrieve data dynamically from Yahoo. You might want to consider a batch process that periodically updates a database or series of files (one file per event). The series of files method is the one I used since our events are generated manually at our site (through a Web interface). Unfortunately, we don't have a hit counter in place, so I can't say exactly how much it is used.

    If you are interested in seeing some of my code, I can post it to my scratch pad. Particularly how the 6-month view is generated.

    As far as your code goes, the only thing that some people might recommend is changing your  for (;;){} loops to  for (..){} loops. But I don't think in this case that it is objectionable (besides, the code that I modified/wrote uses them and doesn't utilize CGI table building methods as you have, so I'm not worthy to complain).

    Good luck finishing your project!

    --Jim

      hey jlongino thanks for the comments.

      You're right. IRT (In Real Time) the routine prolly wouldnt pose much of a performance threat, however it looks messy and i'm sure there are ways to optimise it. The back end is actually a Postgresql database, so that's where i'm thinking there will be some performance hit.

      crazyinsomniac posted some cool CGI.pm stuff i really didnt have a good handle on before.

      The project is fun, (as well as practical). I'm basically writing a personal web portal, with addrbk, datebk, ToDo, and notes interfacing with my palm pilot.

      I'm also thinking of writing a 'file-manager' type interface (using the pilot-xfer tools), as well as some kind of avantgo syncing (with a malsync backend).

      I've pretty much done all the session management, and addressbook thus far and am about 20% into the datebk.

      As you can see I suffer from feature creep. I should concentrate on the datebk before things get out of hand.. :-)