I'm doing a small project concerning key performance indicators - and one important kpi is (internal)customer satisfaction. I needed a small voting system for this. To keep it flexible I decided to use yaml-files for the question- and conf-files. Using HTML::Template makes it easy to adapt it to a corporate-style. This is the actual result, and there is quite a lot to improve.
There are directories for templates, images and the conf-file.

yaml-files

conf.yaml

--- db_host: localhost db_database: poll db_user: itpeople db_pass: secret

poll.yaml

--- #YAML:1.0 identifier: IT_Staff description: Please vote about ... public: Yes --- #YAML:1.0 - question: How do you think... id: q01 - question: Please rate ... id: q02
If public is 'Yes' every voter sees the result.

.pl-files

create_poll.pl

#!/usr/bin/perl use strict; use warnings; use DBI; use YAML qw ( LoadFile ); my $poll_file = shift @ARGV or die "no file no poll!\n"; my ($poll, $questions) = LoadFile($poll_file) or die "Sorry, no questi +on-file!\n"; my $config = LoadFile('conf/conf.yaml'); my $sql_create = qq{ CREATE TABLE $poll->{identifier} ( ID bigint(20) unsigned NOT NULL auto_increment, Zeitstempel timestamp(14) NOT NULL, }; $sql_create .= join("\n", map { " $_->{id} smallint(5) unsigned defau +lt '3'," } @$questions); $sql_create .= "\n primary key (ID))\n TYPE=MyISAM;"; my $dbh = DBI->connect ("DBI:mysql:host=$config->{db_host};database=$c +onfig->{db_database}", $config->{db_user}, $config->{db_pass}, {PrintError => 0, Ra +iseError => 1}) or die "Failed to connect via DBI:$!\n"; $dbh->do($sql_create) or die "$sql_create: $dbh->errstr\n"; exit;

poll.pl

#!/usr/bin/perl use strict; use warnings; use CGI; use CGI::Carp qw ( warningsToBrowser fatalsToBrowser ); use DBI; use HTML::Template; use YAML qw ( LoadFile ); my $q = new CGI; my $action = $q->param('action') || ''; my $what_poll = $q->param('what_poll') || ''; $q->delete('action', 'what_poll'); my $results = $q->Vars; my ($poll, $questions) = LoadFile("$what_poll.yaml") or die "Sorry, no + question-file!\n"; print $q->header(); if ($action eq 'Abstimmen') { my $dbh = &connect_to_db(); &vote($dbh); &show_results($dbh); $dbh->disconnect(); } else { &fill_form(); } exit; # subs sub fill_form { my $template = HTML::Template->new(filename => 'templates/poll.tmpl' +, die_on_bad_params => 0); $template->param( 'questions' => $questions, 'description' => ${poll}->{description}, 'what_poll' => $what_poll ); print $template->output; } sub vote { my $dbh = shift; my $sql_insert = "INSERT INTO $what_poll ("; $sql_insert .= join(", ", keys %$results); $sql_insert .= ") VALUES ("; $sql_insert .= join(", ", values %$results); $sql_insert .= ");"; my $sth = $dbh->prepare($sql_insert) or die "$sql_insert: $dbh->errstr\n"; $sth->execute(); $sth->finish(); } sub show_results { my $dbh = shift; my $sql_query = "SELECT "; $sql_query .= join(", ", map { 'AVG('.$_->{id}.')' } @$questions); $sql_query .= " FROM $what_poll;"; my $sth = $dbh->prepare($sql_query) or die "$sql_query: $dbh->errstr\n"; $sth->execute(); my @row = $sth->fetchrow_array(); $sth->finish(); my $sth = $dbh->prepare("SELECT COUNT(*) AS Anzahl FROM $what_poll;" +) or die "$sql_query: $dbh->errstr\n"; $sth->execute(); my @count = $sth->fetchrow_array(); $sth->finish(); my @bar = map { sprintf("%u", ($_/5.0)*100) } @row; map { $_ = sprintf("%.2f", $_) } @row; $_->{result} = shift @row for @$questions; $_->{bar} = shift @bar for @$questions; my $template = HTML::Template->new(filename => 'templates/result.tmp +l', die_on_bad_params => 0); $template->param( 'public' => ( ${poll}->{public} =~ /yes/i) ? 1 + : undef, 'questions' => $questions, 'number_of_votes' => $count[0], ); print $template->output; } sub connect_to_db { my $config = LoadFile('conf/conf.yaml'); my $dbh = DBI->connect ("DBI:mysql:host=$config->{db_host};database= +$config->{db_database}", $config->{db_user}, $config->{db_pass}, {PrintError => 0, Ra +iseError => 1}) or die "Failed to connect via DBI:$!\n"; return $dbh; }

what_poll.pl

#!/usr/bin/perl use strict; use warnings; use CGI; use CGI::Carp qw ( warningsToBrowser fatalsToBrowser ); use HTML::Template; my $q = new CGI; my @polls = <*.yaml>; my $polls = []; map {$_ =~ s/(.*)\.yaml$/$1/} @polls; foreach my $poll (@polls) { my %row; $row{poll} = $poll; push @$polls, \%row; } #die join(", ", @polls); print $q->header(); my $template = HTML::Template->new(filename => 'templates/what_poll.tm +pl', die_on_bad_params => 1); $template->param( 'polls' => $polls ); print $template->output;

The templates

header.tmpl

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><TMPL_VAR NAME="description"></title> <meta name="author" content="Ronnie Neumann" /> <meta name="generator" content="Perl-Script using HTML::Template" /> <link rel="stylesheet" type="text/css" href="default.css" /> </head>

poll.tmpl

<TMPL_INCLUDE NAME="header.tmpl"> <body> <div> <form method="POST"> <input type="hidden" name="what_poll" value="<TMPL_VAR NAME="what_poll +">" /> <h1><TMPL_VAR NAME="description"></h1> <table summary="Questions"> <tr> <th>Frage</th> <th> 1 - 2 - 3 - 4 -5 </th> </tr> <TMPL_LOOP NAME="questions"> <tr> <td> <TMPL_VAR NAME="question"> </td> <td> <input type="radio" name="<TMPL_VAR NAME="id">" value="1" /> <input type="radio" name="<TMPL_VAR NAME="id">" value="2" /> <input type="radio" name="<TMPL_VAR NAME="id">" value="3" /> <input type="radio" name="<TMPL_VAR NAME="id">" value="4" /> <input type="radio" name="<TMPL_VAR NAME="id">" value="5" /> </td> </tr> </TMPL_LOOP> </table> <hr /> <input type="submit" name="action" value="Abstimmen" /> <input type="reset" value="Leeren" /> </form> <hr /> </div> <div> Bitte bewerten Sie die einzelnen Fragen von 1 (schlecht) bis 5 (sehr g +ut). </div> </body> </html>

report.tmpl

<TMPL_INCLUDE NAME="header.tmpl"> <body> <h1>Danke für die Teilnahme an dieser Umfrage</h1> <TMPL_IF NAME="public"> <table summary="Umfrageergebniss"> <TMPL_LOOP NAME="questions"> <tr> <td><TMPL_VAR NAME="question"></td> <td> <img style="width: <TMPL_VAR NAME="bar">px; height: 14px; border-width:1px; border-style:solid;" src="images/bar.png" alt="<TMPL_VAR NAME="result">" /> </td> <td><TMPL_VAR NAME="result"></td> </tr> </TMPL_LOOP> </table> <hr /> Anzahl abgegebener Stimmen: <TMPL_VAR NAME="number_of_votes"><br /> </TMPL_IF> </body> </html>

what_poll.tmpl

<TMPL_INCLUDE NAME="header.tmpl"> <body> <TMPL_LOOP NAME="polls"> <li /><a href="poll.pl?what_poll=<TMPL_VAR NAME="poll">"><TMPL_VAR N +AME="poll"></a> </TMPL_LOOP> </body> </html>
The image I used for the bar-chart is a 1px width and 18px height blue bar named bar.png. Sorry, some parts of the templates are in german.

It would be fine to read some suggestions.
best regards,
neniro Update: Changed it to the right (and running) version.