The DailyPic and its code

Scroll down to see the some of the code used to generate the DailyPic.

sub show : Local {
    my ($self, $c, $type, $date) = @_;

    $type ||= "indoors";
    clear_cache();
    Global->init($c);
    my $today = tt_today($c);
    my $last_date = date($string{sys_last_config_date});
    my $dt;
    if ($date) {
        $dt = date($date);
    }
    elsif (my $fdate = $c->request->params->{date}) {
        $dt = date($fdate);
        if ((! $dt)
#            || ($dt < $today)      # how far backwards in time can we go???
            || ($dt->as_d8() > $last_date)
        ) {
            # ??? better error message
            # in case it is beyond the last date?
            $c->stash->{mess} = "Illegal date: $fdate";
            $c->stash->{template} = "gen_error.tt2";
            return;
        }
    }
    else {
        $dt = $today;
    }
    my $d8 = $dt->as_d8();

    my @houses = grep { $_->disp_code ne 'X' }
                 @{ $houses_in{$type} };
    my @house_ids = map { $_->id } @houses;

    # get blocks on the current day into a hash.
    # indexed by house_id with value of the # of beds blocked
    # there may be more than one block in the house on this day.
    #
    my %blocks;
    for my $b (model($c, 'Block')->search({
                   sdate => { '<=', $d8 },
                   edate => { '>', $d8 },
                   house_id => { 'in', \@house_ids }
               })
    ) {
        $blocks{$b->house_id()} += $b->nbeds();
    }

    if ($type eq 'resident') {
        html_show($c, $dt);
        return;
    }
    # first determine the size of the entire image
    # by looking at the coordinates and codes of the houses.
    my ($width, $height) = (0, 0);
    for my $h (@houses) {
        my $wd = $h->x + $h->max * $string{house_width} + 6;
        my $disp_code = $h->disp_code;
        if (substr($disp_code, 0, 1) eq 'R') {
            my $name = $h->name;
            if ($disp_code =~ m{t}) {
                $name =~ s{^\S*\s*}{};
            }
            $wd += length($name)*$string{house_let};
        }
        my $ht = $h->y + $string{house_height};
        if (substr($disp_code, 0, 1) eq 'B') {
            $ht += $string{house_height};
        }
        if ($wd > $width) {
            $width = $wd;
        }
        if ($ht > $height) {
            $height = $ht;
        }
    }
    # margin???
    $width += $string{dp_margin_right};
    $height += $string{dp_margin_bottom};

    my $dp = GD::Image->new($width+1, $height+1);
    my $pct = $string{dp_img_percent}/100;
    my $resize_height = $height*$pct;
    my $white = $dp->colorAllocate(255,255,255);    # 1st color = background
    my $black = $dp->colorAllocate(  0,  0,  0);
    my %char_color;
    for my $c (qw/ M F X R B S empty_bed resize /) {
        $char_color{$c} = $dp->colorAllocate(
                              $string{"dp_$c\_color"} =~ m{(\d+)}g);
    }
    $dp->rectangle(0, 0, $width, $height, $black);

    my $dp_map = "";    
    #
    # with one SQL request get all the needed config records
    # into a hash with keys of the house id.
    # ALSO??? also restrict it to config records where cur > 0?
    # if ! exists $config{$house_id} then we know it is empty.
    # sure!
    #
    my %config;
    for my $cf (model($c, 'Config')->search({
                    house_id => { -in => \@house_ids },
                    the_date => $d8,
                    cur      => { '>', 0 },
                })
    ) {
        $config{$cf->house_id()} = $cf;
    }
    HOUSE:
    for my $h (@houses) {
        my $x1 = $h->x;
        my $y1 = $h->y;
        my $hid = $h->id;
        my $name = $h->name;
        my $tname = $name;
        my $disp_code = $h->disp_code;
        if (substr($disp_code, 1, 1) eq 't') {
            $tname =~ s{^\S+\s*}{};        
        }
        my $code = substr($disp_code, 0, 1);
        my ($offset) = $disp_code =~ m{(\d+)};
        $offset |= 0;
        # the 3 and 6 below are for margins
        my $x2 = $x1 + $h->max * $string{house_width} + 6;
        my $y2 = $y1 + $string{house_height};
        $dp->rectangle($x1, $y1, $x2, $y2, $black);
        # below we have a cool use of the ?: operator!  (what is its name?)
        # ternary
        $dp->string(gdGiantFont,
            ($code eq 'L')? ($x1-length($tname)*$string{house_let}-2,$y1+3)
           :($code eq 'R')? ($x2+3, $y1+3)
           :($code eq 'A')? ($x1-$offset, $y1-$string{house_height}+3)
           :($code eq 'B')? ($x1, $y1+$string{house_height}+3)
           :                (0, 0),    # shouldn't happen
                    $tname, $black);
        my ($sex, $cur, $curmax);
        my $color = $white;
        if (exists $config{$hid}) {
            my $cf = $config{$hid};
            $sex    = $cf->sex();
            if ($sex eq 'N' || $sex eq 'T') {
                # X is for mixed or other gender
                # including Transgender and Prefer not to respond
                $sex = 'X';
            }
            $cur    = $cf->cur();
            $curmax = $cf->curmax();

            # we may have a color different than white.
            #
            if (my $pid = $cf->program_id()) {
                $color = cache_color($c, $dp, 'Program', $pid, $color);
            }
            elsif (my $rid = $cf->rental_id()) {
                $color = cache_color($c, $dp, 'Rental', $rid, $color);
            }
        }
        else {
            $sex = 'U';     # doesn't matter
            $cur = 0;
            $curmax = $h->max();
        }
        $dp->filledRectangle($x1+1, $y1+1, $x2-1, $y2-1, $color);
        my $cw = 9.2;       # char_width - seems to work, empirically derived

        # encode the config record in a string to put
        # inside the above rectangle
        # consider blocks as well...
        #
        my $sexcode = ($sex x $cur);
        my $n = $blocks{$hid} || 0;
        if ($n && $sex ne 'B' && $h->max != $n) {
            # in this circumstance use / to show the blocks 
            substr($sexcode, -$n) = $string{dp_resize_block_char} x $n;
        }
        #if ($sexcode eq 'XX') {
        #    # for non-sexist purposes...
        #    # to not make the women angry ...
        #    $sexcode = (int(rand(2)) == 1)? 'MF': 'FM';
        #}

        $dp->string(gdGiantFont, $x1+3, $y1+3,
                    $sexcode, $char_color{$sex})  if $cur;
        $dp->string(gdGiantFont, $x1+3 + $cw*$cur, $y1+3,
                    $string{dp_empty_bed_char} x ($curmax - $cur),
                    $char_color{empty_bed})            if ($curmax - $cur);
        $dp->string(gdGiantFont, $x1+3 + $cw*$curmax, $y1+3,
                    $string{dp_resize_char}    x ($h->max() - $curmax),
                    $char_color{resize})               if $curmax;
        if ($cur == 0) {
            next;       # assume that the config and the
                    # Registrations/RentalBookings are in synch.
                    # if not, we're screwed.
                    # this is why I made hcck!
        }
        # for the image maps to work we need to adjust
        # the coordinates according to how the browser
        # will resize the image.
        #
        $x1 *= $pct;
        $y1 *= $pct;
        $x2 *= $pct;
        $y2 *= $pct;
        $dp_map .= "<area shape=rect coords='$x1, $y1, $x2, $y2'"
                . qq! onclick="Send('$sex', $hid);"!
                . qq! onmouseout="return nd();">\n!
                ;
    }
    #
    # render any annotations for this cluster type
    #
    for my $a (@{$annotations_for{$type}} ) {
        my $color;
        if (! empty($a->color())) {
            $color = $dp->colorAllocate($a->color() =~ m{(\d+)}g);
        }
        else {
            $color = $black;
        }
        if ($a->shape() ne 'none') {
            my $shape = $a->shape();
            $dp->setThickness($a->thickness());
            $dp->$shape($a->x1(), $a->y1(),
                        $a->x2(), $a->y2(),
                        $color);
            $dp->setThickness(1);
        }
        else {
            $dp->string(gdGiantFont,
                        $a->x(), $a->y(),
                        $a->label(),
                        $color);
        }
    }
    # write the image (to be used shortly) to a file
    # with a well defined name
    #
    my $im_name = "im"
                  . uc(substr($type, 0, 1)) 
                  . sprintf("%04d%02d%02d%02d%02d%02d",
                            (localtime())[reverse (0 .. 5)])
                  . ".png";
    open my $imf, ">", "/var/Reg/images/$im_name"
        or die "no $im_name: $!\n"; 
    print {$imf} $dp->png;
    close $imf;
    my $image = $c->uri_for("/dailypic/dp_image/$im_name");
    my $campsites = "";
    if ($type eq 'outdoors') {
        $campsites = join '<br>',
                     map {
                         "<img border=0 src='/static/images/$_'>"
                     }
                     qw/
                         oaks.gif
                         mad.gif
                     /;
                   ;
    }
    my $event_table = event_table($c, $d8);
    my $who_is_there = $c->uri_for("/registration/who_is_there");
    my $dp_form = dp_form($type, $dt);
    my $ucf_type = ucfirst $type;
    my $html = <<"EOH";
<head>
<title>$ucf_type Daily Picture</title>
<link rel="stylesheet" type="text/css" href="/static/cal.css" />
<script type="text/javascript" src="/static/js/overlib.js">
<!-- overLIB (c) Erik Bosrup -->
</script>
<script type="text/javascript">

// prepare for an Ajax call:
var xmlhttp = false;
var ua = navigator.userAgent.toLowerCase();
if (!window.ActiveXObject)
    xmlhttp = new XMLHttpRequest();
else if (ua.indexOf('msie 5') == -1)
    xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
else
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");

function Get() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        return overlib(xmlhttp.responseText,
                       STICKY, MOUSEOFF, TEXTFONT,
                       'Verdana', TEXTSIZE, 5, WRAP, CELLPAD, 7,
                       FGCOLOR, '#FFFFFF', BORDER, 2)
    }
}

function Send(sex, house_id) {
    var url = '$who_is_there/'
            + sex
            + '/'
            + house_id
            + '/'
            + $d8
            ;
    xmlhttp.open('GET', url, true);
    xmlhttp.onreadystatechange = Get;
    xmlhttp.send(null);

    return true;
}
</script>
</head>
<body>
$dp_form
<table cellpadding=3>
<tr><td>
<img height=$resize_height src=$image border=0 usemap=#dailypic>
</td><td valign=center>
<table cellpadding=2>
<tr><td>$string{dp_empty_bed_char}</td><td>empty bed</td></tr>
<tr><td>F</td><td>female</td></tr>
<tr><td>M</td><td>male</td></tr>
<tr><td>X</td><td>mixed or other gender</td></tr>
<tr><td>R</td><td>rental</td></tr>
<tr><td>S</td><td>meeting space</td></tr>
<tr><td>$string{dp_resize_char}</td><td>resized room</td></tr>
<tr><td>B</td><td>block</td></tr>
<tr><td>$string{dp_resize_block_char}</td><td>resize block</td></tr>
</table>
</td>
</tr></table>
$event_table
$campsites
<map name=dailypic>
$dp_map</map>
</body>
EOH
    $c->res->output($html);
}