#!/usr/bin/perl # Copyright 2001-2009 Leslie Richardson # This file is part of Open Admin for Schools. # Open Admin for Schools is free software; you can redistribute it # and/or modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # Passed Values: Subject/Section value (passed as subjsec). my $specchar = '*'; # causes this test to be skipped in calculating avg. my $altcolor = '#3B7'; #1B6 Complement to the 063 green of teacher site. my %lex = ('Nil' => 'Nil', 'marks were posted to the report card system' => 'marks were posted to the report card system', 'Post to Report Card System' => 'Post to Report Card System', 'GB Main' => 'GB Main', 'Main' => 'Main', 'There was an error storing your data' => 'There was an error storing your data', 'Please contact' => 'Please contact', 'System Disabled. Please contact secretary' => 'System Disabled. Please contact secretary', 'No Tests Found!' => 'No Tests Found!', 'Mark' => 'Mark', 'Objective:# to Post' => 'Objective:# to Post', 'Override Existing Marks?' => 'Override Existing Marks?', 'Name' => 'Name', 'Prev
Post' => 'Prev
Post', 'GBook
Average' => 'GBook
Average', 'Userid' => 'Userid', 'Password' => 'Password', 'Enter Gradebook' => 'Enter Gradebook', 'Term' => 'Term', 'Previous Value exists and not overridden. Skipping' => 'Previous Value exists and not overridden. Skipping', 'updated' => 'updated', 'Error' => 'Error', 'Please Log in!' => 'Please Log in!', 'Class Average' => 'Class Average', 'Student' => 'Student', 'Convert to Letter Grade' => 'Convert to Letter Grade', 'Convert Marks' => 'Convert Marks', 'Decimal Display' => 'Decimal Display', ); $markToLetter{1} = { '89.5' => 'E', '74.5' => 'M', '59.5' => 'B', '0' => 'N' }; #$markToLetter{2} = { '92.5' => 'A', '82.5' => 'B','72.5' => 'C', '62.5' => 'D', # '59.9' => 'D-','0' => 'F'}; my @schemes; foreach my $idx ( sort keys %markToLetter ) { my $scheme; foreach my $key ( sort reverse keys %{ $markToLetter{$idx} } ) { $scheme = $scheme. " $key < $markToLetter{$idx}{$key} < "; } chop $scheme; chop $scheme; $scheme .= "($idx)"; push @schemes, $scheme; } my $self = 'post.pl'; use DBI; use CGI; use CGI::Session; use Date::Business; use Number::Format qw(:all); my $q = new CGI; eval require "../../etc/admin.conf"; if ( $@ ) { print $lex{Error}. " $@
\n"; die $lex{Error}. " $@
\n"; } # get the markfield value... a1-a20 which stores numeric mark. eval require "../../etc/repcard.conf"; if ( $@ ) { print $lex{Error}. " $@
\n"; die $lex{Error}. " $@
\n"; } my $dsn = "DBI:$dbtype:dbname=$dbase"; my $dbh = DBI->connect($dsn,$user,$password); $dbh->{mysql_enable_utf8} = 1; # Get Session Information... my $session = new CGI::Session("driver:mysql;serializer:FreezeThaw", undef,{Handle => $dbh}) or die CGI::Session->errstr; my $logged_in = $session->param(logged_in); if ( not $logged_in ) { print $q->header( -charset, $charset ); print $lex{'Please Log in!'}. "
\n"; die; } my $subjsec = $session->param('subjsec'); print $q->header( -charset, $charset ); my %arr = $q->Vars; if ( not defined $arr{decimaldisplay} ) { $arr{decimaldisplay} = '0'; } my $markToLetter; # mode switch my %mark2Letter; if ( $arr{convertscheme} ) { # passed value from markToLetter table. my ( $dud, $id ) = split /\(/, $arr{convertscheme}; chop $id; $markToLetter = 1; %mark2Letter = %{ $markToLetter{$id} }; # holds the values } my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $iddst) = localtime(time); $year = $year + 1900; $mon++; $wday++; if (length($mon) ==1){ $mon = '0'.$mon;} if (length($mday) ==1){ $mday = '0'.$mday;} my $currdate = "$dow[$wday], $month[$mon] $mday, $year"; my $currsdate = "$year$mon$mday"; my $dateobj = new Date::Business(DATE => $currsdate); # Read in the term and check for disabled function. Fail if zero. my $termread = "Error!"; open(TERM, "../../etc/term") || die "Can't find the term number!"; $termread = ; if ( $termread == 0 ) { print "
". $lex{'System Disabled. Please contact secretary'}. ".\n"; print "\n"; die; } #### DISABLED: Otherwise calculate term from times... #### my $term = calcTerm(); # Print Head of Page print "$doctype\n". $lex{'Post to Report Card System'}; print "\n $chartype\n\n"; print "[ ". $lex{Main}; print " |\n ". $lex{'GB Main'}; print " ]\n"; print "
\n"; # Comment out, unless you want to lock into letter entry once chosen. #if ( $arr{convertscheme} ) { # print "\n"; #} print "\n"; print "
\n"; if ( $arr{postflag} ) { delete $arr{postflag}; postMarks(); } else { showStartPage( $subjsec ); # $subjsec comes from session. } #---------------- sub showStartPage { #---------------- my $subjsec = shift; # Get the Subject Information (objectives) $sth = $dbh->prepare("select * from subject where subjsec = ?"); $sth->execute( $subjsec ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr; } my $subjref = $sth->fetchrow_hashref; my $startterm = $subjref->{'startrptperiod'}; my $endterm = $subjref->{'endrptperiod'}; my @objective = (); for my $i (1 .. 20){ # index of objectives in subject table $key = 'a'. $i; if ( $subjref->{$key} ){ # if objective exists, push it into array $subjref->{$key} =~ s/://g; # strip any colons in objective push @objective, $subjref->{$key}. ":$i"; } } if ( not @objective ){ # no objectives entered; put in a single one $objective[0] = $lex{Mark}. ":1"; } # Creates array controlling student sort order. my @studnum = mkStudNum( $subjsec ); # Now lookup tests for this class. $sth = $dbh->prepare("select name, id, score, weight from gbtest where subjsec = ? and name != 'sortorder' order by tdate"); $sth->execute( $subjsec ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr;} while ( my ( $name,$id,$score,$weight ) = $sth->fetchrow ) { #$name =~ s/://g; # strip colons from name... just in case. #push @test, "$name:$id:$score"; $weight{$id} = $weight; # used for calculating averages. $maxscore{$id} = $score; } # Now done putting in the tests. # Load the markscheme field to get groupweight hash. $sth = $dbh->prepare("select markscheme from subject where subjsec = ?"); $sth->execute( $subjsec ); if ( $DBI::errstr ){ print $DBI::errstr; die $DBI::errstr; } my $markscheme = $sth->fetchrow; my %groupweight; my @fields = split (/[\n|\r]/, $markscheme); foreach my $fld ( @fields ) { if ( $fld ) { my ($grp, $percent) = split /=/, $fld; $groupweight{$grp} = $percent; } } # Print top of page heading print "

". $subjref->{'description'}; print " (". $subjref->{'subjsec'}. ")

\n"; # Start form. print "
\n"; print "\n"; print "\n"; # Print the table header print "\n"; print "\n"; print "\n"; print "\n"; # Print Objectives to post into. print "\n"; # Print Override Marks Line print "\n"; print "\n\n"; print '\n"; my $sth = $dbh->prepare("select lastname, firstname from studentall where studnum = ?"); # Print the students and their marks; foreach my $studnum ( @studnum ) { $sth->execute( $studnum ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr; } my ($lastname,$firstname) = $sth->fetchrow; print "\n"; # Get current mark, if any for this term, subject, student. # $markfield defined in repcard.conf - the field a1 to a20 with numeric mark my $sth = $dbh->prepare("select $markfield from eval where term = ? and studnum = ? and subjcode = ?"); $sth->execute( $termread, $studnum, $subjsec ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr;} my $mark = $sth->fetchrow; # print "Mark: $mark Trm: $termread Studnum: $studnum Subject: $subjsec
\n"; if ( not $mark ) { $mark = $lex{Nil}; } print ""; my $studentavg = calcAverage($studnum, $subjsec, \%maxscore, \%weight, \%groupweight); push @studentavg, $studentavg; # used to calc class average below. if ( $markToLetter ) { # mode shift to Letter Grade conversion. my $letter; foreach my $threshold ( reverse sort keys %mark2Letter ) { # from large to small... #print "TH: $threshold SN:$studentavg
\n"; if ( $studentavg >= $threshold ) { $letter = $mark2Letter{$threshold}; last; } } print "\n"; } else { $studentavg = round($studentavg, $arr{decimaldisplay} ); print "\n"; } } # End of student loop. # Calc and print overall student average in this subject. # Note: This class average calculation method differs slightly from # the main gradebook method, since it doesn't use all of the weighted # values from all items, rather just using the average of all student # averages that don't have a totalweight of 0 (ie all excused). print "\n"; my ($avgcount, $avgtotal); foreach my $avg (@studentavg){ $avgcount++; $avgtotal += $avg; } if ( not $avgcount ){ print "
". $lex{'No Tests Found!'}. "
\n"; die;} my $avgstudenttotal = $avgtotal / $avgcount; $avgstudenttotal = round($avgstudenttotal, $arr{decimaldisplay} ); print "\n"; print "\n"; print "
". $lex{Term}. "
"; print $lex{'Objective:# to Post'}. "
". $lex{'Override Existing Marks?'}. ""; print ""; print "
'. $lex{Name}. ''. $lex{'Prev
Post'}. '
'; print $lex{'GBook
Average'}. "
$lastname, $firstname$mark $studentavg
"; print $lex{'Class Average'}. "$avgstudenttotal%
\n"; print "\n"; print "

\n"; # Now print the mark To Letter table, if desired. if ( %markToLetter ) { print "\n"; print "\n"; print "\n"; if ( $arr{decimaldisplay} ) { print "\n"; } print "\n"; print "\n"; print "
". $lex{'Convert to Letter Grade'}. "
\n"; print "
\n"; } print "
\n"; } # end of showStartPage #-------- sub login { # print error, login screen and die; #-------- my $error = shift; print "$doctype\n$error $chartype\n

$error

". $lex{Userid}. ": ". $lex{Password}. ":

[ ". $lex{Main}. " ]
"; die; } # NOT USED ANY MORE.... #----------- sub calcTerm { # Figure out what term it is and return it. #----------- # Figure out the current term, if not passed term # Requires Date::Business my $maxterm = 12; my $term = 0; for (1..$maxterm){ # worst case scenario.. what's reasonable? if (not $g_termstart{$_}){ next;} my $startdate = $g_termstart{$_}; $startdate =~ s/-//g; my $startobj = new Date::Business(DATE => $startdate); my $enddate = $g_termend{$_}; $enddate =~ s/-//g; my $endobj = new Date::Business(DATE => $enddate); $startoffset = $dateobj->diffb($startobj,'prev','next'); $endoffset = $dateobj->diffb($endobj,'prev','next'); #print "StartOffset: $startoffset Endoffset: $endoffset
\n"; if ($startoffset >= 0 and $endoffset <= 0){ $term = $_; last; } } return $term; } # End of CalcTerm #------------ sub mkStudNum { #------------ my $subjsec = shift; my ( @eval, @studnum, %remove ); # Create the studnum array that controls the order that students are # displayed in. Load the eval records first into an array, sorted by # lastname and firstname. (@eval) Load the sort order records in order # into an array and also into a hash (%remove) # Any remaining array elements in eval are added to the end of # the studnum array once done. If there _are_ no sortorder recs, then # the final array becomes the eval records only. This has the desired # behavior. # Find the enrollments for this class and read them into @eval array $sth = $dbh->prepare("select distinct eval.studnum from eval left outer join studentall on studentall.studnum = eval.studnum where eval.subjcode = ? order by studentall.lastname, studentall.firstname"); $sth->execute( $subjsec ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr;} while ( my $studnum = $sth->fetchrow ) { push @eval,$studnum; } # Load 'sortorder' test if it exists... $sth = $dbh->prepare("select id from gbtest where subjsec = ? and name = 'sortorder'"); $sth->execute( $subjsec ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr;} my $sortid = $sth->fetchrow; if ( $sortid ){ # We have a sortorder $sth = $dbh->prepare("select studnum from gbscore where testid = ? order by score"); $sth->execute( $sortid ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr;} while (my $sn = $sth->fetchrow){ push @studnum, $sn; $remove{$sn} = 1; } # Now add on @eval elements who are not in @studnum foreach my $en (@eval){ if (not $remove{$en}){ push @studnum, $en;} } return @studnum; } else { # no sortorder return @eval; } } #------------ sub postMarks { #------------ #foreach my $key ( sort keys %arr ) { print "K:$key V:$arr{$key}
\n"; } # Really needs to have checking added to check for an existing # record (mark) and then ignore updates if a mark already # exists. It should also say if it has done this for a particular # student. my $subjsec = $arr{subjsec}; delete $arr{subjsec}; my $term = $arr{term}; delete $arr{term}; my $markoverride = $arr{markoverride}; delete $arr{markoverride}; my $dsn = "DBI:$dbtype:dbname=$dbase"; my $dbh = DBI->connect($dsn,$user,$password); $dbh->{mysql_enable_utf8} = 1; # Read in the term to check for failure my $termread = "Error!"; if ( not open(TERM, "../../etc/term")) { print "Can't find the term number!
"; die "Can't find the term number!"; } $termread = ; if ( $termread == 0 ) { print "
". $lex{'System Disabled. Please contact secretary'}. ".\n"; print "\n"; die; } # Set which objective to post into. my ($objective,$idx) = split /:/,$arr{objective}; $idx = 'a'.$idx; # make a1, a2, etc. delete $arr{objective}; print "

\n"; my $postedCount = 0; # track how many marks were posted. foreach my $key ( keys %arr ) { $sth = $dbh->prepare("select $idx from eval where term = ? and subjcode = ? and studnum = ?"); $sth->execute( $term, $subjsec, $key ); if ($DBI::errstr) { print $lex{Student}. " $studnum ". $lex{Error}. ": $DBI::errstr"; die $lex{Student}. " $studnum ". $lex{Error}. ": $DBI::errstr"; } my $prevalue = $sth->fetchrow; if ( $prevalue and not $markoverride ){ # skip updating # We have a mark/value already submitted and not overridden # Get Name. $sth1 = $dbh->prepare("select lastname, firstname from studentall where studnum = ?"); $sth1->execute( $key ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr; } my ($lastname, $firstname) = $sth1->fetchrow; print $lex{'Previous Value exists and not overridden. Skipping'}. " $lastname, $firstname"; print "($key) "; print $lex{Mark}. ": $arr{$key} ". $lex{Term}. ": $term
\n"; } else { # update mark # Get Name $sth1 = $dbh->prepare("select lastname, firstname from studentall where studnum = ?"); $sth1->execute( $key ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr;} my ($lastname, $firstname) = $sth1->fetchrow; print "$lastname, $firstname ($key) ". $lex{updated}. ".\n"; print $lex{Mark}. ": $arr{$key} ". $lex{Term}. ": $term
\n"; $postedCount++; # increment counter. $sth = $dbh->prepare("update eval set $idx = ? where term = ? and subjcode = ? and studnum = ?"); #print "T: $term IDX: $idx V: $arr{$key} SN: $key
\n"; $sth->execute( $arr{$key}, $term, $subjsec, $key); if ( $DBI::errstr ) { print $lex{Student}. " $studnum ". $lex{Error}. ": $DBI::errstr"; die $lex{Student}. " $studnum ". $lex{Error}. ": $DBI::errstr"; } } } # End of Mark Update Loop if ( not $DBI::errstr ) { print "

$postedCount "; print $lex{'marks were posted to the report card system'}. ".

\n"; } else { print '

'. $lex{'There was an error storing your data'}. ".\n"; print $lex{'Please contact'}. " $adminname [ $adminemail ]\n"; print "$DBI::errstr

"; } print "\n"; exit; } # end of postMarks #-------------- sub calcAverage { #-------------- require strict; # needed: $maxscore{$id}, $weight{$id}, $groupweight{$grp} my ($studnum, $subjsec, $maxscore_ref, $weight_ref, $groupweight_ref ) = @_; my %maxscore = %$maxscore_ref; my %weight = %$weight_ref; my %groupweight = %$groupweight_ref; my $totalweight; my $totalscore; my $sth = $dbh->prepare("select gbscore.id, gbscore.testid, gbscore.score, gbtest.grp from gbscore, gbtest where gbscore.testid = gbtest.id and gbtest.name != 'sortorder' and gbscore.studnum = ? and gbtest.subjsec = ? order by gbtest.grp"); $sth->execute( $studnum, $subjsec ); if ($DBI::errstr) { print $DBI::errstr; die $DBI::errstr; } my $currgrp = -1; my $oldgrp; my $first = 1; my $totalscore; # running total for score; zeroed for each new group. my $totalweight; # running total for weight; zeroed for each new group. my %groupscore; while ( my ( $id, $testid, $score, $grp ) = $sth->fetchrow ) { $score =~ s/\s//g; # strip any spaces in score; if ( $score eq $specchar or $score eq '' or not defined $score ) { next; } # skip $oldgrp = $currgrp; $currgrp = $grp; if ( $currgrp ne $oldgrp and not $first) { # setup group values for the current group if ($totalweight) { $groupscore{$oldgrp} = $totalscore / $totalweight; } # What to do if totalweight == 0? $totalscore = 0; $totalweight = 0; } if ( $first ) { $first = 0; } if ( $score =~ /\d/ ) { # if score is a digit... do it. $totalscore += $score / $maxscore{$testid} * $weight{$testid}; $totalweight += $weight{$testid}; } else { # some sort of text score...count as zero... update total weight. $totalweight += $weight{$testid}; } #print "TS: $totalscore TW:$totalweight GP:$grp
\n"; } # End of Scores Loop # Get Last group, if any; if ( $totalweight ) { $groupscore{$currgrp} = $totalscore / $totalweight; } $totalscore = 0; $totalweight = 0; #foreach my $key (keys %groupscore) { print "K: $key GS: $groupscore{$key}
\n"; } # Add up the group scores and weight them for overall average foreach my $grp (keys %groupscore) { $totalscore += $groupscore{$grp} * $groupweight{$grp}; #print "GS: $groupscore{$grp} GW:$groupweight{$grp}
\n"; $totalweight += $groupweight{$grp}; } #print "TS:$totalscore TW:$totalweight

\n"; if ( $totalweight ) { return round( 100 * $totalscore / $totalweight, $arr{decimaldisplay} ). '%'; } else { return 0; } } # End of CalcAverage