epita-std
/
ACU
Archived
1
0
Fork 0
This repository has been archived on 2021-10-08. You can view files and clone it, but cannot push or open issues or pull requests.
ACU/ACU/Grading.pm

367 lines
6.9 KiB
Perl

#! /usr/bin/env perl
package Grading;
use v5.10.1;
use strict;
use warnings;
use XML::LibXML;
sub new
{
my $class = shift;
my $self = {
operators => {
'add' => '$a+$b'
},
tree => [],
ids => {}
};
bless $self, $class;
if ($#_ >= 0) {
$self->_initialize(@_);
}
return $self;
}
sub _initialize
{
my $self = shift;
my $dom = XML::LibXML->load_xml(string => shift);
$self->{tree} = $self->parseGrade($dom->documentElement());
$self->{max} = $dom->documentElement()->getAttribute("max") // "20";
}
sub create_from_ids
{
my $self = shift;
my $trace_name = shift;
my $ids = shift;
my $trace_id = $trace_name;
$trace_id =~ s/[^a-zA-Z0-9_]/_/g;
my $g = Grade->new($trace_id, $trace_name);
for my $id (sort( keys %{ $ids } ))
{
my $p = Point->new($ids->{$id}, $id, 0, 0);
push @{ $g->{tree} }, $p;
}
push @{ $self->{tree} }, $g;
}
sub parseGrade ($$)
{
my $self = shift;
my $ret = [];
my $node = shift;
foreach my $grade ($node->childNodes())
{
if ($grade->nodeName eq "operator")
{
my $tmp = $grade->textContent;
chomp($tmp);
$self->{operators}{ $grade->getAttribute("name") } = $tmp;
}
elsif ($grade->nodeName eq "grade")
{
my $g = Grade->new(
$grade->getAttribute("id"),
$grade->getAttribute("title"),
$grade->getAttribute("operator"),
$grade->getAttribute("factor")
);
$g->append(@{ $self->parseGrade($grade) });
push @$ret, $g;
}
elsif ($grade->nodeName eq "point")
{
my $n = Point->new(
$grade->textContent,
$grade->getAttribute("ref"),
$grade->getAttribute("qversion"),
$grade->getAttribute("not")
);
push @$ret, $n;
}
}
return $ret;
}
sub reset ($)
{
my $self = shift;
$self->{ids} = {};
}
sub insert ($$$)
{
my $self = shift;
$self->{ids}{$_[0]} = $_[1];
}
sub fill ($$)
{
my $self = shift;
my $ids = shift;
for my $k (keys %{ $ids }) {
$self->{ids}{$k} = $ids->{$k};
}
}
sub toString ($)
{
my $self = shift;
my $doc = XML::LibXML::Document->new('1.0');
my $root = $doc->createElement("grading");
for my $op (keys %{ $self->{operators} }) {
my $ope = $doc->createElement("operator");
$ope->addChild( $doc->createAttribute("name", $op) );
$ope->appendText($self->{operators}{$op});
$root->appendChild($ope);
}
for my $grade (@{ $self->{tree} }) {
$grade->toString($doc, $root, $root);
}
$doc->setDocumentElement( $root );
return $doc->toString();
}
sub compute ($)
{
my $self = shift;
my $sum = 0;
for my $grade (@{ $self->{tree} }) {
my $tmp = $grade->compute($self->{operators}, $self->{ids});
$sum += $tmp if $tmp;
}
return $sum;
}
sub computeXML ($;$)
{
my $self = shift;
my $doc = XML::LibXML::Document->new('1.0');
my $root = $doc->createElement("grading");
for my $grade (@{ $self->{tree} }) {
$grade->compute($self->{operators}, $self->{ids}, $doc, $root);
}
$root->addChild( $doc->createAttribute("max", $self->{max}) );
$doc->setDocumentElement( $root );
return $doc->toString();
}
package Grade;
use v5.10.1;
use strict;
use warnings;
use Carp;
use Safe;
use List::Util "reduce";
use XML::LibXML;
sub new ($$$;$$)
{
my $class = shift;
my $self = {
id => shift,
title => shift,
operator => shift // "add",
factor => shift // 1,
tree => []
};
return bless $self;
}
sub toString ($$$)
{
my $self = shift;
my $doc = shift;
my $parent = shift;
my $grade = $doc->createElement("grade");
$grade->addChild( $doc->createAttribute("id", $self->{id}) );
$grade->addChild( $doc->createAttribute("title", $self->{title}) );
$grade->addChild( $doc->createAttribute("operator", $self->{operator}) ) if ($self->{operator} ne "add");
$grade->addChild( $doc->createAttribute("factor", $self->{factor}) ) if ($self->{factor} != 1);
$parent->appendChild($grade);
for my $item (@{ $self->{tree} }) {
$item->toString($doc, $grade);
}
}
sub append ($@)
{
my $self = shift;
push @{ $self->{tree} }, @_;
}
sub compute ($$$;$$)
{
my $self = shift;
my $operators = shift;
my $ids = shift;
my $doc = shift;
my $parent = shift;
my @current = ();
my $grade;
if ($doc && $parent)
{
$grade = $doc->createElement("grade");
$grade->addChild( $doc->createAttribute("factor", $self->{factor}) );
$grade->addChild( $doc->createAttribute("name", $self->{title}) );
$parent->appendChild( $grade );
}
for my $node (@{ $self->{tree} })
{
my $t = $node->compute($operators, $ids, $doc, $grade);
push @current, $t if (defined $t);
}
my $res;
if (@current > 1)
{
my $operator = $self->{operator};
my $cpt = new Safe;
$cpt->permit_only(qw(:base_core :base_mem :base_loop padany rv2gv));
$res = reduce {
$cpt->share('$a');
$cpt->share('$b');
$cpt->reval($operators->{ $operator }) // die "Safe alert: $@";
}
@current;
}
else {
$res = $current[0] // 0;
}
$res = $res * $self->{factor};
$ids->{ $self->{id} } = $res;
$grade->addChild( $doc->createAttribute("value", $res) ) if ($grade);
return $res;
}
sub add
{
$a + $b;
}
package Point;
use v5.10.1;
use strict;
use warnings;
use Term::ANSIColor qw(:constants);
sub new ($$$$$)
{
my $class = shift;
my $self = {
value => $_[0],
ref => $_[1],
qversion => $_[2],
not => $_[3]
};
return bless $self;
}
sub toString ($$$)
{
my $self = shift;
my $doc = shift;
my $parent = shift;
my $point = $doc->createElement("point");
$point->addChild( $doc->createAttribute("ref", $self->{ref}) ) if ($self->{ref} ne "");
$point->addChild( $doc->createAttribute("qversion", $self->{qversion}) ) if ($self->{qversion} != 0);
$point->addChild( $doc->createAttribute("not", $self->{not}) ) if ($self->{not} != 0);
$point->appendText($self->{value});
$parent->appendChild($point);
}
sub getValue ($$;$)
{
my $self = shift;
my $ids = shift;
my $justMatch = shift;
if ($self->{ref} && !$justMatch && !$ids->{ $self->{ref} } // 0) {
return 0;
}
elsif ($self->{value} eq "") {
return $ids->{ $self->{ref} } // 0;
}
else {
return $self->{value};
}
}
sub compute ($$$;$$)
{
my $self = shift;
my $operators = shift;
my $ids = shift;
my $ret = undef;
if ((not $self->{ref}) || grep { $self->{ref} eq $_ } keys %$ids) {
$ret = $self->getValue( $ids );
}
if ($self->{not})
{
if ($ret) {
$ret = undef;
} else {
$ret = $self->getValue( $ids );
}
}
if ($main::debug)
{
my $str = "not=".($self->{not}//0).", qversion".($self->{qversion}//"*").", ref=".($self->{ref}//"").",\tvalue=".$self->getValue( $ids, 1 ).", got=".($ret // 0);
if ($ret) {
say GREEN, ">>>", RESET, " Matching point: ", $str;
} else {
say RED, " * ", RESET, " Skipped point: ", $str;
}
}
return $ret;
}
1;