private static function save_grade_item($grade_item)
 {
     if (!$grade_item) {
         throw new InvalidArgumentException("grade_item must be set");
     }
     if (!$grade_item->courseid) {
         throw new InvalidArgumentException("grade_item->courseid must be set");
     }
     if (!$grade_item->categoryid) {
         throw new InvalidArgumentException("grade_item->categoryid must be set");
     }
     if (!$grade_item->name) {
         throw new InvalidArgumentException("grade_item->name must be set");
     }
     if (!isset($grade_item->item_number)) {
         $grade_item->item_number = 0;
     }
     // check for an existing item and update or create
     $grade_item_tosave = grade_item::fetch(array('courseid' => $grade_item->courseid, 'itemmodule' => self::GRADE_ITEM_MODULE, 'itemname' => $grade_item->name));
     if (!$grade_item_tosave) {
         // create new one
         $grade_item_tosave = new grade_item();
         $grade_item_tosave->itemmodule = self::GRADE_ITEM_MODULE;
         $grade_item_tosave->courseid = $grade_item->courseid;
         $grade_item_tosave->categoryid = $grade_item->categoryid;
         $grade_item_tosave->iteminfo = $grade_item->typename;
         //$grade_item_tosave->iteminfo = $grade_item->name.' '.$grade_item->type.' '.self::GRADE_CATEGORY_NAME;
         $grade_item_tosave->itemnumber = $grade_item->item_number;
         //$grade_item_tosave->idnumber = $grade_item->name;
         $grade_item_tosave->itemname = $grade_item->name;
         $grade_item_tosave->itemtype = self::GRADE_ITEM_TYPE;
         //$grade_item_tosave->itemmodule = self::GRADE_ITEM_MODULE;
         if (isset($grade_item->points_possible) && $grade_item->points_possible > 0) {
             $grade_item_tosave->grademax = $grade_item->points_possible;
         }
         $grade_item_tosave->insert(self::GRADE_LOCATION_STR);
     } else {
         // update
         if (isset($grade_item->points_possible) && $grade_item->points_possible > 0) {
             $grade_item_tosave->grademax = $grade_item->points_possible;
         }
         $grade_item_tosave->categoryid = $grade_item->categoryid;
         $grade_item_tosave->iteminfo = $grade_item->typename;
         $grade_item_tosave->update(self::GRADE_LOCATION_STR);
     }
     $grade_item_id = $grade_item_tosave->id;
     $grade_item_pp = $grade_item_tosave->grademax;
     // now save the related scores
     if (isset($grade_item->scores) && !empty($grade_item->scores)) {
         // get the existing scores
         $current_scores = array();
         $existing_grades = grade_grade::fetch_all(array('itemid' => $grade_item_id));
         if ($existing_grades) {
             foreach ($existing_grades as $grade) {
                 $current_scores[$grade->userid] = $grade;
             }
         }
         // run through the scores in the gradeitem and try to save them
         $errors_count = 0;
         $processed_scores = array();
         foreach ($grade_item->scores as $score) {
             $user = self::get_users($score->user_id);
             if (!$user) {
                 $score->error = self::USER_DOES_NOT_EXIST_ERROR;
                 $processed_scores[] = $score;
                 $errors_count++;
                 continue;
             }
             $user_id = $user->id;
             // null/blank scores are not allowed
             if (!isset($score->score)) {
                 $score->error = 'NO_SCORE_ERROR';
                 $processed_scores[] = $score;
                 $errors_count++;
                 continue;
             }
             if (!is_numeric($score->score)) {
                 $score->error = 'SCORE_INVALID';
                 $processed_scores[] = $score;
                 $errors_count++;
                 continue;
             }
             $score->score = floatval($score->score);
             // Student Score should not be greater than the total points possible
             if ($score->score > $grade_item_pp) {
                 $score->error = self::POINTS_POSSIBLE_UPDATE_ERRORS;
                 $processed_scores[] = $score;
                 $errors_count++;
                 continue;
             }
             try {
                 $grade_tosave = null;
                 if (isset($current_scores[$user_id])) {
                     // existing score
                     $grade_tosave = $current_scores[$user_id];
                     // check against existing score
                     if ($score->score < $grade_tosave->rawgrade) {
                         $score->error = self::SCORE_UPDATE_ERRORS;
                         $processed_scores[] = $score;
                         $errors_count++;
                         continue;
                     }
                     $grade_tosave->finalgrade = $score->score;
                     $grade_tosave->rawgrade = $score->score;
                     $grade_tosave->timemodified = time();
                     /** @noinspection PhpUndefinedMethodInspection */
                     $grade_tosave->update(self::GRADE_LOCATION_STR);
                 } else {
                     // new score
                     $grade_tosave = new grade_grade();
                     $grade_tosave->itemid = $grade_item_id;
                     $grade_tosave->userid = $user_id;
                     $grade_tosave->finalgrade = $score->score;
                     $grade_tosave->rawgrade = $score->score;
                     $grade_tosave->rawgrademax = $grade_item_pp;
                     $now = time();
                     $grade_tosave->timecreated = $now;
                     $grade_tosave->timemodified = $now;
                     $grade_tosave->insert(self::GRADE_LOCATION_STR);
                 }
                 /** @noinspection PhpUndefinedFieldInspection */
                 $grade_tosave->user_id = $score->user_id;
                 $processed_scores[] = $grade_tosave;
             } catch (Exception $e) {
                 // General errors, caused while performing updates (Tag: generalerrors)
                 $score->error = self::GENERAL_ERRORS;
                 $processed_scores[] = $score;
                 $errors_count++;
             }
         }
         /** @noinspection PhpUndefinedFieldInspection */
         $grade_item_tosave->scores = $processed_scores;
         // put the errors in the item
         if ($errors_count > 0) {
             $errors = array();
             foreach ($processed_scores as $score) {
                 if (isset($score->error)) {
                     $errors[$score->user_id] = $score->error;
                 }
             }
             /** @noinspection PhpUndefinedFieldInspection */
             $grade_item_tosave->errors = $errors;
         }
         $grade_item_tosave->force_regrading();
     }
     return $grade_item_tosave;
 }