Moodle Plugin for Grade Chart

Moodle Plugin for Grade Chart

by Omprakash Saini -
Number of replies: 10

Moodle 3.4+ (Build: 20180112)
No Error Messages.
Operating Version : Ubuntu 16.04 LTS with apache-2 & mysql for database.

I have to build a plugin for my Institute's Website which uses moodle. Basically the core function of the plugin would be to display a chart : frequency of students(on y-axis) and the Gradeletters(x-axis) stored previously in moodle database for a current Gardeitem.
The Fequency would be determined from the ranges of Lower boundary and Higher Boundary of the particular Grade and these boundaries would be taken input from the teacher.
Let's say for example :
I have Gradeitem selected as Mid-Sem. The finalgrade values of the users enrolled in the Mid-Sem course would be taken from the database and based on the boundaries which teacher enters in the fields would be taken to calculate the frequency.  I want the plugin to populate the bar on the chart, as soon as the teacher enters the value in the boundary division of the particular grade. 

I have coded the plugin but the chart is not working.
Please find repository for the plugin : https://github.com/omrpakash1996/moodle-plugin

Screenshot of the Current state of the Plugin: https://ibb.co/giY4Tb

Average of ratings: -
In reply to Omprakash Saini

Re: Moodle Plugin for Grade Chart

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

What specifically isn't working? What have you done to debug it?  Exactly how to reproduce the problem you have?

In reply to Howard Miller

Re: Moodle Plugin for Grade Chart

by Omprakash Saini -
Hey, I have done moodle->debugging and didn't find any issues on the plugin. I think i am some where wrong in the code. If you could please have a look to these files-



I think I am not able to provide the lower boundary values and higher boundary values to the classes $lowerboundaries & $higherboundaries and finally not able to produce disrtibution from the function load_distibution(). This might be the issue with the array values insertion and It would be oblige if you can help me wherever I am wrong. And going to the chart, I think absoult[] is not getting proper values.
In reply to Omprakash Saini

Re: Moodle Plugin for Grade Chart

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

I don't think anybody - certainly not me - is going to spend loads of time debugging your code without something more specific to go on. 

In reply to Howard Miller

Re: Moodle Plugin for Grade Chart

by Omprakash Saini -

okay let's start with a short piece of code.

$lowerboundaries = new stdClass();
$lowerboundaries=  array_fill_keys($letters,null);
$higherboundaries=new stdClass();
$higherboundaries=array_fill_keys($letters,null);
$i=1;
foreach ($letters as $letter){
    $lowerboundary = new stdClass();$higherboundary =new stdClass();
    $lowerboundary->value=0;$higherboundary->value=0;
    $lowerboundary->value='grp_gradeboundaries_lower['.$i.']';$higherboundary->value='grp_gradeboundaries_higher['.$i.']';

    $lowerboundaries[$letter]=$lowerboundary;

    $higherboundaries[$letter]=$higherboundary;
$i++;
}

$grader = new grade_report_gradedist($course->id, $gpr, $context, $letters,$higherboundaries,$lowerboundaries);

This is the code i am using in ajax_handler and doubts to be wrong on inserting array values. If it is okay I pass the values to

$grader = new grade_report_gradedist($course->id, $gpr, $context, $letters,$higherboundaries,$lowerboundaries);

and in lib.php:

class grade_report_gradedist extends grade_report_grader {
    private $letters;
    private $higherboundaries;
    private $lowerboundaries;
    
    public function __construct($courseid, $gpr, $context, $letters,$higherboundaries,$lowerboundaries, $page=null, $sortitemid=null) {
        parent::__construct($courseid, $gpr, $context, $page, $sortitemid);

        $this->letters = $letters;
        $this->lowerboundaries= new stdClass();
        $this->lowerboundaries=$lowerboundaries;
        $this->higherboundaries=new stdClass();
        $this->higherboundaries=$higherboundaries;
    }

I think it will be also fine.. after this in lib.php i do have functions

public function load_distribution($letters, $gradeitem=0, $groupid=0, $groupingid=0) {
        global $CFG, $DB;

        $this->load_users();
        $selectedusers = array();
        if ($groupingid == 0) {
            if ($groupid == 0) { // Tackle all users.
                $selectedusers = $this->users;
            } else { // Tackle the users of a single group.
                $groupusers = groups_get_members($groupid);
                $selectedusers = array_intersect_key($groupusers, $this->users);
            }
        } else { // Tackle the users of the groups of a grouping.
            $groupsusers = groups_get_grouping_members($groupingid);
            $selectedusers = array_intersect_key($groupsusers, $this->users);
        }

        $userids = array_keys($selectedusers);

        $sql = "SELECT g.*, gi.grademax, gi.grademin
                  FROM {grade_items} gi,
                       {grade_grades} g
                WHERE g.itemid = gi.id AND gi.courseid = :courseid
                AND g.itemid = :gradeitem";
        $params = array('gradeitem' => $gradeitem, 'courseid' => $this->courseid);

        //krsort($this->letters); // Just to be sure.

        $total = 0;
        $count = 0;

        $return = new stdClass();
        $return->distribution = array_fill_keys($this->letters, null);
        $return->coverage = array(0, 0);

        foreach ($this->letters as $letter) {
            $gradedist = new stdClass();
            $gradedist->count       = 0;
            $return->distribution[$letter] = $gradedist;
        }

        if ($grades = $DB->get_records_sql($sql, $params)) {
            foreach ($grades as $grade) {
                if (in_array($grade->userid, $userids) && array_key_exists($grade->itemid, $this->gtree->get_items())) {
                    // Some items may not be present!!
                    if (is_null($grade->finalgrade)) {
                        continue;
                    }
                    $total++;

                    // Calculate gradeletter.
                    $letter = $this->get_gradeletter($letters, $grade);

                    if (array_key_exists($letter, $return->distribution)) {
                        $return->distribution[$letter]->count++;
                        $count++;
                    }
                  
                }
            }
        }
        $return->coverage = array($total - $count, $total, ($total > 0) ? round(($total - $count) * 100 / $total, 2) : 0);
        return $return;
    }

    public function get_gradeletter($letters, $grade) {
        if (is_null($grade->finalgrade) || !$gradeitem = grade_item::fetch(array('id' => $grade->itemid))) {
            return '-';
        }

        // Map to range.
        $gradeint = $gradeitem->grademax - $gradeitem->grademin;
                     $value=$grade->finalgrade;
        // Calculate gradeletter.
        $value = bounded_number(0, $value, $gradeitem->grademax); // Just in case.
        foreach ($this->letters as $letter) {
           
            if (($value <=$this->higherboudaries[$letter]->value)&&($value >= $this->lowerboundaries[$letter]->value)) {
                return format_string($letter);
            }
            else{return '-';}
        }
    }


Now , if it is fine i will post another doubt in the code.

In reply to Omprakash Saini

Re: Moodle Plugin for Grade Chart

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

So...

        $this->lowerboundaries= new stdClass();
        $this->lowerboundaries=$lowerboundaries;
        $this->higherboundaries=new stdClass();
        $this->higherboundaries=$higherboundaries


You assign a 'stdclass' object to lowerboundaries and then immediately overwrite it with the $lowerboundaries parameter. What do you think the 'new stdclass' assignment is doing? It might not be wrong as such but it's a sign of some muddled thinking. 


In reply to Howard Miller

Re: Moodle Plugin for Grade Chart

by Omprakash Saini -

So, instead of making new stdClass() for $lowerboundaries and $higherboundaries, should I make two .php files(classes) for the above and do create a object of those. Basically then how should i write the classes as earlier, i was doing like this:

$lowerboundaries = new stdClass();
$lowerboundaries=  array_fill_keys($letters,null);
$higherboundaries=new stdClass();
$higherboundaries=array_fill_keys($letters,null);
$i=1;
foreach ($letters as $letter){
    $lowerboundary = new stdClass();$higherboundary =new stdClass();
    $lowerboundary->value=0;$higherboundary->value=0;
    $lowerboundary->value='grp_gradeboundaries_lower['.$i.']';$higherboundary->value='grp_gradeboundaries_higher['.$i.']';

    $lowerboundaries[$letter]=$lowerboundary;

    $higherboundaries[$letter]=$higherboundary;
$i++;
}

Or any alternate method if you suggest.

In reply to Omprakash Saini

Re: Moodle Plugin for Grade Chart

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

I think you might be missing my point. I'm not suggesting that this is your problem, but, I like to eradicate any obvious coding errors. Anyway...

$lowerboundaries = something

$lowerboundaries = something else


You set $lowerboundaries to one thing (new stdClass()) and then immediately, without using it, change it to something else. The stdClass() assignment does nothing. So, this makes me think you don't understand what new stdClass() does. Or something like that. 

In reply to Howard Miller

Re: Moodle Plugin for Grade Chart

by Omprakash Saini -

Okay i got your point, removed new stdClass assignment to both of them and ran it again now no issues but bars are not still coming on the chart when i select the grade item.

Moreover lets move ahead..

In handler file later on i have this

$actdist = $grader->load_distribution($letters, $gradeitem, $groupid, $groupingid);

$data   = new stdClass();

$data->actdist = $actdist->distribution;
$data->actcoverage = $actdist->coverage;
$data->courseid = $courseid;
$data->gradeitem = $gradeitem;
$data->title = $gradeitems[$gradeitem]->name;
$data->title .= $chartsubtitle;
$data->updateall = $updateall;

echo json_encode($data);


And further i start with index.php file here i do have same scenario

$boundarieslower = optional_param_array('grp_gradeboundaries_lower',array(),PARAM_TEXT);
$boundarieshigher = optional_param_array('grp_gradeboundaries_higher',array(),PARAM_TEXT);

$lowerboundaries = new stdClass();
$lowerboundaries=  array_fill_keys($letters,null);
$higherboundaries=new stdClass();
$higherboundaries=array_fill_keys($letters,null);
$i=1;
foreach ($letters as $letter){
    $lowerboundary = new stdClass();$higherboundary =new stdClass();
    $lowerboundary->value=0;
    $higherboundary->value=0;
    $lowerboundary->value='grp_gradeboundaries_lower['.$i.']';
    $higherboundary->value='grp_gradeboundaries_higher['.$i.']';

    $lowerboundaries[$letter]=$lowerboundary;
    $higherboundaries[$letter]=$higherboundary;
$i++;
}
$grader = new grade_report_gradedist($course->id, $gpr, $context, $letters,$higherboundaries,$lowerboundaries);

$mdata = new stdClass();

$mdata->gradeitem = $gradeitem;
$i = 1; $max = 500.01;
foreach ($letters as $boundary => $letter) {
     $gradelettername = 'grp_gradeletters['.$i.']';
      $mdata->$gradelettername   = $letter;
       $i++;
}

$groupid = 0; $groupingid = 0;
$actdist = $grader->load_distribution($letters, $gradeitem, $groupid, $groupingid);

$gsel = $grader->group_selector;

$mform = new edit_letter_form($returnurl, array(
            'id' => $course->id,
            'num' => count($letters),
            'edit' => $edit,
            'gradeitems' => $gradeitems,
            'coursegroups' => $coursegroups,
            'coursegroupings' => $coursegroupings,
            'groupmode' => $groupmode,
            'actcoverage' => $actdist->coverage),
            'post', '', array('id' => 'letterform'));
$mform->set_data($mdata);

if (($data = $mform->get_data()) ) {
   
    $gradeitem = new stdClass();
    $gradeitem->id = $data->gradeitem;
    $gradeitem->name = $gradeitems[$data->gradeitem]->name;

    $groupid = isset($data->coursegroup) ? $data->coursegroup : 0;
    $groupingid = isset($data->coursegrouping) ? $data->coursegrouping : 0;

 }
    $data = new stdClass();
    $data->courseid = $course->id;
    $data->actdist = $actdist->distribution;
    $data->actcoverage = $actdist->coverage;
    $data->title = $gradeitems[$gradeitem]->name;
    $data->highcharts = $highcharts;

    // Start output.
    $jsmodule = array(
        'name' => 'gradereport_gradedist',
        'fullpath' => '/grade/report/gradedist/js/gradedist.js',
        'requires' => array('io-form'),
        'strings'  => array(array('interval', 'gradereport_gradedist'),
                            array('decimals', 'gradereport_gradedist'),
                            array('predecessor', 'gradereport_gradedist'),
                            array('coverage', 'gradereport_gradedist'),
                            array('absolut', 'gradereport_gradedist'),
                           
                            array('gradeletter', 'gradereport_gradedist'),
                            array('printchart', 'gradereport_gradedist'),
                            array('downloadpng', 'gradereport_gradedist'),
                            array('downloadjpeg', 'gradereport_gradedist'),
                            array('downloadpdf', 'gradereport_gradedist'),
                            array('downloadsvg', 'gradereport_gradedist'),
                            array('contextbuttontitle', 'gradereport_gradedist'),
                            array('highchartsmissing', 'gradereport_gradedist'),
            ));
    $PAGE->requires->js_init_call('M.gradereport_gradedist.init',
            array($data), true, $jsmodule);
   
    print_grade_page_head($course->id, 'report', 'gradedist', get_string('pluginname', 'gradereport_gradedist'));

    if ($saved) {
        echo $OUTPUT->notification(get_string('saved', 'gradereport_gradedist'), 'notifysuccess');
    } else if ($boundaryerror) {
        echo $OUTPUT->notification(get_string('boundaryerror', 'gradereport_gradedist'));
    }
   
    // Gradedist settings.
    $mform->display();

  echo $OUTPUT->footer();

In reply to Omprakash Saini

Re: Moodle Plugin for Grade Chart

by Howard Miller -
Picture of Core developers Picture of Documentation writers Picture of Particularly helpful Moodlers Picture of Peer reviewers Picture of Plugin developers

Also, there's a few is_null() calls in there. Are you sure about that? That specifically checks for a null value... I think you might be looking for isset() or even empty(). Check carefully, you are using the right one.