Dual-license your content for inclusion in The Perl 5 Wiki using this HOWTO, or join us for a chat on irc.freenode.net#PerlNet.

User:Alecclews/School report

From PerlNet

Jump to: navigation, search

#! /usr/bin/perl -w

=head1 Overview

This version updated for data fixes, and suggestions from Paul


=head2 Create a set of class lists for Mount View School Primary school.

Each year the MVP Parents Assoc. distributes a list to each child with a list of each
child in their form and their contact details.

Before a pupils details are included for the first time, the parent is asked to sign a form
giving permission yo have the details published.

In 2005 the historical database has been lost. So all records have to be reviewd and marked for release from scratch


=over 4

=item *

Input file: A CSV file (actually .xls) supplied by the school office of the current school students and thier details
		Format :  "lastName","firstName","currentForm","phoneNumber","unusedField"

=item *

Output file:
	2) A copy of last years class lists database
		Format :  ""lastName","firstName","lastYearsNumber","status","lastYearsForm"
			here status is one one 'Y' => included, 'N' => not included, 'U' => unknown

=back

This is a three step process:

=over 4

=item 1.

if no class list database file exists yet for this year
		create a new class list database file by merging lasts years database with the current
				school supplied list

=item 2.

Display the Class list database in a GUI and allow user to make changes. Update the database

=item 3.

Use the contents of the database to create one or more PDF files N.B. uses MiKTeX

=back

=cut


use strict;
use English;
use Tk;
use Tk::DialogBox;
use Tk::NoteBook;
use Tk::LabEntry;


sub parse_csv
{
	my $text = shift;    # record containing comma-separated values
	my @new  = ();
	push( @new, $+ ) while $text =~ m{
        # the first part groups the phrase inside the quotes.
        # see explanation of this pattern in MRE
        "([^\"\\]*(?:\\.[^\"\\]*)*)",?
           |  ([^,]+),?
           | ,
       }gx;
	push( @new, undef ) if substr( $text, -1, 1 ) eq ',';
	return @new;         # list of values that were comma-separated
}
sub trimfrontandback {  # Remove all leading and trailing whitespace -- Taken from the Perl Cookpook Ch 1
    my @out = @_;
    for (@out) {
        s/^\s+//;
        s/\s+$//;
    }
    return wantarray ? @out : $out[0];
}


sub capWords {  # Make all words lower case except first letter of each word -- Hacked from the Perl Cookbook Ch 1
    my @out = @_;
    for (@out) {
        s/(\w+)/\u\L$1/g;
    }
    return wantarray ? @out : $out[0];
}


(
   my $sec,  my $min,  my $hour, my $mday, my $mon,
   my $year, my $wday, my $yday, my $isdst
  )
  = localtime(time);

my $thisYear            = $year + 1900;
my $DataDir             = "c:\\DOCUME~1\\marilyn\\MYDOCU~1\\2005_MVP_ClassLists\\";
my $thisYearsDatabase   = "${DataDir}STUDENT_LIST_$thisYear.csv";
my $thisYearsSchoolDatabase = "${DataDir}SCHOOL_LIST_$thisYear.csv";
my $thisYearsClassList  = "${DataDir}STUDENT_LIST_$thisYear.tex";



my $top = MainWindow->new();
$top->title("Mount View PS $thisYear class lists");
my $menu_bar = $top->Frame()->pack( -side => 'top' );
my $runButton = $menu_bar->Menubutton(-text     => "File",
                                 -underline => 1,
                                 -menuitems => [
              [ Button => "Edit Lists",-command  => \&editDB ],
               [ Button => "Print PDFs",-command  => \&generateClassList  ] ])
                           ->pack(-side     => "left");

my $help_mb = $menu_bar->Menubutton(
									 -text        => 'Help',
									 -borderwidth => 2
)->pack( -side => 'left', -padx => 2 );

MainLoop;


sub generateClassList()
{
	my %schoolDB = ();  # A list of classes, teachers, etc.
	
	open(SCHOOLDB,$thisYearsSchoolDatabase)
				|| die "Unable to open School DB $thisYearsSchoolDatabase";
	
	
	while(<SCHOOLDB>)
	{
		my @fields = trimfrontandback(parse_csv($_));
		( my $form = $fields[2]) =~ s/\s+//g;  # Remove all whitespace
		$form = uc $form;
		
		$schoolDB{$form}{'teacher'} = $fields[1]." ".capWords($fields[0]);
		$schoolDB{$form}{'roomNo'}  = $fields[3];
#		$schoolDB{$form}{'telExt'}  = $fields[3];
		$schoolDB{$form}{'staffEmail'} = $fields[4];
		$schoolDB{$fields[2]}{'classRep'} = $fields[5]  if $fields[5];
	}
	
	close(SCHOOLDB);
		
	
	my %classList = ();
	
	open( THISYEARSDB, $thisYearsDatabase )
	  || die "Could not open this years class list (file $thisYearsDatabase)";
	while (<THISYEARSDB>)
	{
		my @fields = parse_csv($_);
		
		$classList{ $fields[2] }{ $fields[0] }{ $fields[1] }{'phone'} = $fields[3]
																if ($fields[4] eq 'Y');
	}
	close(THISYEARSDB);
	
	open( THISYEARSLIST, ">$thisYearsClassList" )
	  || die "Could not open $thisYearsClassList for output";
	
	print THISYEARSLIST '\documentclass[10pt,a4paper]{article}' . "\n",
	                    '\usepackage[british]{babel}' . "\n",
	                    '\pagestyle{empty}' . "\n",
	                    '\begin{document}\large{' . "\n";

	foreach my $form ( sort keys %classList )
	{   
	    (my $schoolDBindex = $form ) =~ s/\s+//g;  # Remove all whitespace
		print THISYEARSLIST "\\section*{Mount View Primary School $thisYear}\n",
		                    "\\subsection*{Class $form contact list}\n",
		                    "Teacher: $schoolDB{$schoolDBindex}{'teacher'}\\protect{    }",
                                    "Room: $schoolDB{$schoolDBindex}{'roomNo'}\\\\\n";
		print THISYEARSLIST "Email: $schoolDB{$schoolDBindex}{'staffEmail'}\\\\\n" if $schoolDB{$schoolDBindex}{'staffEmail'};
                print THISYEARSLIST "Class Representative: $schoolDB{$schoolDBindex}{'classRep'}\\\\\n";

		my $fontSize = $schoolDBindex =~ /4F|6SK/ ? "large":"Large";  # 4F and 6SK does not fit on the page in Large size
		                    
		print THISYEARSLIST	"\\$fontSize\\begin{center}\\begin{tabular}[t]{|l|c|}\n\\hline\n";
		foreach my $family ( sort keys %{ $classList{$form} } )
		{
			foreach my $givenName ( keys %{ $classList{$form}{$family} } )
			{   $family =~ /\u\b\w/;
				print THISYEARSLIST	"$givenName $family & $classList{$form}{$family}{$givenName}{'phone'} \\\\ \\hline\n";
			}
		}
		print THISYEARSLIST "\\end{tabular}\\end{center}\n\\Large\\clearpage\n";
	}
	
	print THISYEARSLIST '}\end{document}' . "\n";
	close(THISYEARSLIST);
	system("texify --pdf --run-viewer $thisYearsClassList");
}

=head2 Edit the Class list database

Generates a set of tabed lists to allow the user to review and modify the class lists.

If the OK button is pressed then the database is updated, otherwise the changes are quitely
dropped

=cut

sub editDB()
{
	open( THISYEARSLIST, $thisYearsDatabase )
	                          || die "Could not open this years class list (file $thisYearsDatabase)";
	  
	my %classList = ();
	
	my @fields = []; $fields[4]='';
	
	while (<THISYEARSLIST>)
	{
	    next if /^(\s)*$/;  # skip blank lines
	    next if /SURNAME,FIRST_NAME,HOME_GROUP,TELEPHONE/; #skip the header
	
		@fields = capWords(trimfrontandback(parse_csv($_)));
		
		$classList{ uc($fields[2]) }{ $fields[0] }{ $fields[1] }{'status'} =
		                                                             (($fields[4]=~ /[Yy]/) ? $fields[4] : 'N');
		$classList{ uc($fields[2]) }{ $fields[0] }{ $fields[1] }{'phone'} = $fields[3] ;
	}
	close(THISYEARSLIST);

	# This uses a DialogBox, but you could just
	# as easily not use one... replace the following by
	# $n = $top->NoteBook(-ipadx => 6, -ipady => 6);
	# Of course, then you'd have to take care of the OK and Cancel
	# buttons yourself.
	my $f = $top->DialogBox(    -title   => "View and Modify class list database",
							 -buttons => [ "OK", "Cancel" ] );
	my $tn        = $f->add( 'NoteBook', -ipadx => 6, -ipady => 6 );
	my %formTabs = ();
	my %rows = ();
	my @yf;
	my @n;
	
	my $currentBook = 0;   # 0 = Prep
	$yf[$currentBook] = $tn->add("Year_$currentBook", -label => 'Prep', -underline => 0 );
	$n[$currentBook] = $yf[$currentBook]->NoteBook(-ipadx => 6, -ipady => 6 );
	
	foreach my $form ( sort keys %classList )
	{
#	    print "processing for \"$form\"";
		( my $label = $form ) =~ s/\s+//g;  # Remove all whitespace
		if ( $label !~ m/$currentBook.+/)
		{
		$n[$currentBook]->pack( -expand => "yes",
			  -fill   => "both",
			  -padx   => 5,
			  -pady   => 5,
			  -side   => "top");
			$currentBook++;
			$yf[$currentBook] = $tn->add("Year_$currentBook", -label => "Year $currentBook", -underline => 0 );
			$n[$currentBook] = $yf[$currentBook]->NoteBook(-ipadx => 6, -ipady => 6 );
#			 print "Creating a new notebook for Year $currentBook\n";
		}
		#print "Creating a new notebook for form $form, label is $label\n";
		$formTabs{$form} = $n[$currentBook]->add( $label, -label => "$form", -underline => 0 );
		foreach my $family ( sort keys %{ $classList{$form} } )
		{
			foreach my $givenName ( keys %{ $classList{$form}{$family} } )
			{
				$rows{$form}{$family}{$givenName} =
				                 $formTabs{$form}->Frame()->pack( -side => 'top', -anchor => "nw" );
#print "adding $givenName $family $form\n";
				$rows{$form}{$family}{$givenName}->LabEntry( -label     => "$givenName $family",
							 -labelPack => [ -side => "left", -anchor => "nw" ],
							 -width     => 30,
							 -textvariable =>
							   \$classList{$form}{$family}{$givenName}{'phone'}
				)->pack( -side => "left", -anchor => "nw" );
				$rows{$form}{$family}{$givenName}->Entry(
							 -width        => 2,
							 -textvariable =>
							   \$classList{$form}{$family}{$givenName}{'status'}
				)->pack( -side => "left", -anchor => "nw" );
			}
		}
	}
	$n[$currentBook]->pack( -expand => "yes",
			  -fill   => "both",
			  -padx   => 5,
			  -pady   => 5,
			  -side   => "top");
	$tn->pack( -expand => "yes",
			  -fill   => "both",
			  -padx   => 5,
			  -pady   => 5,
			  -side   => "top");

	my $result = $f->Show();
	if ( $result =~ /OK/ )
	{
		open( THISYEARSDB, ">$thisYearsDatabase" )
		  || die
		  "Could not open this years class list (file $thisYearsDatabase)";

		foreach my $form ( sort keys %classList )
		{
			foreach my $family ( sort keys %{ $classList{$form} } )
			{
				foreach my $givenName ( keys %{ $classList{$form}{$family} } )
				{
				    my $stat = (($classList{$form}{$family}{$givenName}{'status'} =~ /[yY]/) ? 'Y':'N');
					print THISYEARSDB
					qq{"$family","$givenName","$form","$classList{$form}{$family}{$givenName}{'phone'}","$stat"\n};
				}
			}
		}
		close(THISYEARSDB);
	}
}
Personal tools