|
<?php |
|
|
|
/** |
|
* Entrada [ http://www.entrada-project.org ] |
|
* |
|
* Entrada 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 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* Entrada is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with Entrada. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
* Import one uploaded file containing grades. Try to detect upload file type and import grades. |
|
* |
|
* @author Organisation: University of Arizona |
|
* @author Developer: Jeff Davis <[email protected]> |
|
* @copyright 2017 University of Arizona. All Rights Reserved. |
|
*/ |
|
|
|
class Classes_Assessments_GradeFileImporter |
|
{ |
|
/** |
|
* The file name of the file to be imported |
|
* |
|
* @var mixed |
|
*/ |
|
protected $file; |
|
|
|
/** |
|
* @var Models_Course |
|
*/ |
|
protected $course; |
|
|
|
/** |
|
* @var Models_Assessment |
|
*/ |
|
protected $assessment; |
|
|
|
/** |
|
* The competencies that are being assessed for this |
|
* assessment. |
|
* |
|
* @var array |
|
*/ |
|
protected $assessmentCompetencies = []; |
|
|
|
/** |
|
* The competencies that are being assessed for this |
|
* assessment. |
|
* |
|
* @var array |
|
*/ |
|
protected $assessmentMarkingSchemeName = ''; |
|
|
|
/** |
|
* Contents of the file. Results of file($this->file); |
|
* |
|
* @var array|bool |
|
*/ |
|
protected $contents = []; |
|
|
|
/** |
|
* The first row of data (whether or not it contains names of columns). |
|
* |
|
* @var array |
|
*/ |
|
protected $headerRow = []; // output of str_getcsv() for first row. |
|
|
|
/** |
|
* Whether or not the first row contains header information |
|
* |
|
* @var bool |
|
*/ |
|
protected $hasHeaderRow = false; |
|
|
|
/** |
|
* Each row of $this->contents will be processed and put into this array, |
|
* according to the file type. |
|
* |
|
* @var array |
|
*/ |
|
protected $processed = []; |
|
|
|
/** |
|
* The number of columns |
|
* |
|
* @var int |
|
*/ |
|
protected $fieldCount = 0; |
|
|
|
/** |
|
* A name that indicates what kind of file is being imported |
|
* There should be method that starts with the name |
|
* e.g. default, grat, examSoftTab |
|
* Related methods would be defaultGradeGenerator(), grateGradeGenerator(), examSoftTabGradeGenerator() |
|
* |
|
* @var string |
|
*/ |
|
protected $importType = 'unknown'; |
|
|
|
/** |
|
* 'replace' - how to deal with existing grades: |
|
* - 'replace-all': replace all grades with new grades |
|
* - 'replace-higher': if new grade is higher, replace old grade |
|
* - 'replace-none': if a grade exists just leave it |
|
* |
|
* @var array |
|
*/ |
|
protected $importOptions = [ |
|
'replace' => 'replace-higher' |
|
]; |
|
|
|
/** |
|
* @var array |
|
*/ |
|
protected $grades = []; |
|
|
|
/** |
|
* An array of all the enrolled student's id numbers |
|
* |
|
* @var array |
|
*/ |
|
protected $enrolledStudentIds = []; |
|
|
|
/** |
|
* Contains information about the grade import for storing in a log or sending somewhere. |
|
* Html format. |
|
* |
|
* @var string |
|
*/ |
|
protected $report; |
|
|
|
/** |
|
* Some statistics about the import can be stored here. |
|
* |
|
* @var |
|
*/ |
|
protected $statistics = [ |
|
'notfound' => [], // usernames not found in db |
|
'new' => [], // new grades |
|
'updated' => [], // updated grades (usually higher grades) |
|
'unchanged' => [], // grades that exist but were not changed |
|
'errors' => [], // |
|
'notenrolled' => [], // students were found, but they are not enrolled in this course |
|
'blankLineCount' => 0, |
|
'outOfRange' => [] |
|
]; |
|
|
|
/** |
|
* @var string |
|
*/ |
|
public $log = "Grade File Import Log Report"; |
|
|
|
/** |
|
* @var string |
|
*/ |
|
protected $users = ''; |
|
|
|
/** |
|
* Classes_Assessments_GradeFileImporter constructor. |
|
* |
|
* A lot of things are done in the constructor, but |
|
* the actual creation of new grades is done in generateGrades(). |
|
* |
|
* @param $files - usually the $_FILES array |
|
* |
|
* @return |
|
*/ |
|
public function __construct($files = [], $assessmentId = 0) |
|
{ |
|
// Get the first file (and hopefully only file) |
|
if (count($files) > 0) { |
|
$this->file = array_pop($files); |
|
$this->contents = file($this->file['tmp_name']); |
|
$this->processRows(); |
|
$this->detectImportType(); |
|
$this->assessment = Models_Assessment::fetchRowByID($assessmentId); |
|
$this->assessmentCompetencies = |
|
Models_Ua_AssessmentCompetency::findByAssessmentId($this->assessment->getAssessmentID()); |
|
$this->fetchMarkingSchemeName(); |
|
$this->course = Models_Course::get($this->assessment->getCourseId()); |
|
$this->enrolledStudentIds = $this->course->getStudentIds($this->assessment->getCPeriodID()); |
|
} |
|
return $this; |
|
} |
|
|
|
/** |
|
* @param array $files |
|
*/ |
|
public static function import(array $files, $assessmentId) |
|
{ |
|
$self = new self($files, $assessmentId); |
|
return $self; |
|
} |
|
|
|
/** |
|
* |
|
*/ |
|
public function generateReportHeader() |
|
{ |
|
$this->report .= "<h2>Course — " . $this->course->getFullCourseTitle() . "<br>\n"; |
|
$this->report .= "Grade Import Report</h2>"; |
|
$this->report .= "\n<hr>\n<ul>"; |
|
$this->report .= "\n<li>Assessment Id: " . $this->assessment->getAssessmentID() . "</li>"; |
|
$this->report .= "\n<li>Assessed Competencies: " . implode(", ", array_keys($this->getAssessedCompetencies())); |
|
$this->report .= "\n<li>Marking Scheme: " . $this->assessmentMarkingSchemeName . '</li>'; |
|
$this->report .= "\n<li>Students enrolled in this course: " . count($this->enrolledStudentIds) . "</li>"; |
|
$this->report .= "\n</ul>\n<hr>"; |
|
} |
|
|
|
/** |
|
* Generate some helpful statistics about the file, the grades and the students |
|
*/ |
|
public function generateReportFooter() |
|
{ |
|
|
|
$html = "<h3>Notes:</h3>\n<ul>"; |
|
$html .= "\n<li>Import Type: " . $this->importType . "</li>"; |
|
$html .= "\n<li>Replace option: " . $this->importOptions['replace'] . "</li>"; |
|
|
|
// updated grades |
|
if (count($this->statistics['updated']) > 0) { |
|
$html .= "\n<li>" . count($this->statistics['updated']) . ' existing grades were updated.</li>'; |
|
} else { |
|
$html .= "\n<li>No grades were updated.</li>"; |
|
} |
|
|
|
// new grades |
|
if (count($this->statistics['new']) > 0) { |
|
$html .= "\n<li>" . count($this->statistics['new']) . ' new grades were added.</li>'; |
|
} |
|
|
|
// unchanged grades |
|
if (count($this->statistics['unchanged']) > 0) { |
|
$html .= "\n<li>" . count($this->statistics['unchanged']) . ' grades were unchanged.'; |
|
} |
|
|
|
// students from the file that were not found in the database |
|
if (count($this->statistics['notfound']) > 0) { |
|
$html .= "\n<li>" . count($this->statistics['notfound']) . ' students were not found in database.</li>'; |
|
} |
|
|
|
// students who were found in the database, but are not enrolled in this class |
|
if (count($this->statistics['notenrolled']) > 0) { |
|
$html .= "\n<li>" . count($this->statistics['notenrolled']) |
|
. ' students were not enrolled in this class.</li>'; |
|
} |
|
|
|
if ($this->hasHeaderRow()) { |
|
$html .= "\n<li>1 row contained header information.</li>"; |
|
} |
|
|
|
// rows with errors. This means that the field count for this row did not match the other rows. |
|
if (count($this->statistics['errors']) > 0) { |
|
$html .= "\n<li>" . count($this->statistics['errors']) . " rows contained errors.</li>"; |
|
foreach ($this->statistics['errors'] as $row) { |
|
$html .= "\n<code>" . implode(" ", $row) . '</code><br>'; |
|
} |
|
} else { |
|
$html .= "\n<li>There were no rows with errors."; |
|
} |
|
|
|
if ($this->statistics['blankLineCount'] > 0) { |
|
$html .= "\n<li>There were " . $this->statistics['blankLineCount'] . ' blank lines.'; |
|
} |
|
|
|
$html .= "\n<li>Filename: " . $this->file['name'] . ' (' . count($this->contents) . ' lines)'; |
|
$html .= "\n<li>Date/Time: " . date(DEFAULT_DATE_FORMAT . ' T') . '</li>'; |
|
$html .= "\n</ul>"; |
|
// grades that were out of range |
|
if (count($this->statistics['outOfRange']) > 0) { |
|
$html .= "<h3>These Grades were out of range:</h3>\n"; |
|
$html .= "\n<div class='alert'>" . implode('<br>', $this->statistics['outOfRange']) |
|
. '</div>'; |
|
} |
|
|
|
// uncomment out the next line to see more info (for testing) |
|
$html .= '<pre>' . $this->log . '</pre>'; |
|
|
|
// This will generate a list of students enrolled in the course with some fake grades for testing |
|
if (ENVIRONMENT !== 'production' && ENVIRONMENT !== 'staging') { |
|
// For testing purposes |
|
$html .= "<hr><br>The following is a list of NetIds for students enrolled in this class:<br> |
|
<textarea rows='" . count($this->enrolledStudentIds) . "' onclick='this.select()'>" |
|
. $this->getUsernamesForEnrolledStudents() . "</textarea>"; |
|
} |
|
|
|
$this->report .= $html; |
|
return $html; |
|
} |
|
|
|
/** |
|
* Only used for testing. |
|
* |
|
* This is just for me to grab lists of usernames (NetIds) |
|
* So I can make test grade import files. |
|
*/ |
|
public function getUsernamesForEnrolledStudents() |
|
{ |
|
$html = ""; |
|
foreach ($this->enrolledStudentIds as $id) { |
|
$user = Models_User::fetchRowByID($id); |
|
if ($this->assessmentMarkingSchemeName == 'Numeric') { |
|
$totalPoints = $this->assessment->getNumericGradePointsTotal(); |
|
$halfPoints = $totalPoints / 2; // just randomly selecting 50% |
|
$html .= "\n" . $user->getUsername() . ',' . rand($halfPoints, $totalPoints); |
|
} else { |
|
$html .= "\n" . $user->getUsername() . ',' . rand(65, 100); |
|
} |
|
} |
|
return $html; |
|
} |
|
|
|
/** |
|
* Run str_getcsv on each row and store it in $this->processed |
|
* |
|
* @return $this |
|
*/ |
|
public function processRows() |
|
{ |
|
foreach ($this->contents as $row) { |
|
// ignore blank rows |
|
if ($row == "\n") { |
|
$this->statistics['blankLineCount']++; |
|
continue; |
|
} |
|
$processedRow = str_getcsv($row); |
|
|
|
if ($this->fieldCount == 0) { |
|
$this->fieldCount = count($processedRow); |
|
$this->headerRow = $processedRow; |
|
} |
|
// If there are an unexpected number of fields, there is something wrong. |
|
if (count($processedRow) !== $this->fieldCount) { |
|
$this->statistics['errors'][] = $processedRow; |
|
} else { |
|
$this->processed[] = $processedRow; |
|
} |
|
} |
|
return $this; |
|
} |
|
|
|
/** |
|
* |
|
* |
|
* @return array |
|
*/ |
|
public function getAssessedCompetencies() |
|
{ |
|
$assessedCompetencies = []; |
|
foreach ($this->assessmentCompetencies as $name => $competency) { |
|
if (is_object($competency) && $competency->getPercentage() > 0) { |
|
$assessedCompetencies[$name] = $competency; |
|
} |
|
} |
|
if ($assessedCompetencies == []) { |
|
echo " |
|
<div class='well'> |
|
<div class='pull-left error-icon'> |
|
<i class=\"fa fa-exclamation-triangle fa-3x\" aria-hidden=\"true\"></i> |
|
</div> |
|
<div class='error-message'> |
|
<p> |
|
This assessment has no competencies assigned to it.<br> |
|
Please <strong><a href='#' onclick='window.history.back();' |
|
title='go back to previous page'>go back</a></strong> and |
|
assign competencies to this assessment. Then try to import grades again. |
|
</p> |
|
</div> |
|
</div> |
|
"; |
|
exit; |
|
} |
|
return $assessedCompetencies; |
|
} |
|
|
|
/** |
|
* @return mixed |
|
*/ |
|
public function fetchMarkingSchemeName() |
|
{ |
|
global $db; |
|
$markingSchemeId = $this->assessment->getMarkingSchemeID(); |
|
$query = "select * from `assessment_marking_schemes` where `id` = " . $db->qstr($markingSchemeId); |
|
|
|
$results = $db->GetAll($query); |
|
$markingScheme = array_pop($results); |
|
$this->assessmentMarkingSchemeName = $markingScheme['name']; |
|
return $markingScheme['name']; |
|
} |
|
|
|
/** |
|
* Try to guess what kind of file is being imported |
|
*/ |
|
public function detectImportType() |
|
{ |
|
/* |
|
* You can manually set a type by using $this->setImportType() |
|
* If not, then we will try to guess the type here |
|
* |
|
*/ |
|
|
|
// Don't guess if it is already set. |
|
if ($this->importType !== 'unknown') { |
|
return $this->importType; |
|
} |
|
|
|
if ($this->isExamSoftTab()) { |
|
$this->importType = "examSoftTab"; |
|
} |
|
|
|
if ($this->isExamSoftCsv()) { |
|
$this->importType = "examSoftCsv"; |
|
} |
|
|
|
if ($this->isNBME()) { |
|
$this->importType = "nbme"; |
|
} |
|
|
|
// If none of the other apply, this one will |
|
// But file needs to be default csv format (NetId, score) |
|
if ($this->isDefaultImportType()) { |
|
$this->importType = "default"; |
|
} |
|
$this->log("Detecting import type: " . $this->importType); |
|
return $this->importType; |
|
} |
|
|
|
/** |
|
* Is the import file in Exam Soft (tab delimited) format? |
|
* |
|
* @return bool |
|
*/ |
|
public function isExamSoftTab() |
|
{ |
|
if ($this->fieldCount == 1 && (stristr($this->headerRow[0], 'netid') !== false)) { |
|
$this->hasHeaderRow = true; |
|
array_shift($this->processed); // remove the header row from the processed array |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Is the import file in Exam Soft (Csv) format? |
|
* |
|
* @return bool |
|
*/ |
|
public function isExamSoftCsv() |
|
{ |
|
if ($this->fieldCount == 2 && (stristr($this->headerRow[0], 'netid') !== false)) { |
|
$this->hasHeaderRow = true; |
|
array_shift($this->processed); // remove the header row from the processed array |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Or is it NBME? |
|
* |
|
* @return bool |
|
*/ |
|
public function isNBME() |
|
{ |
|
if ($this->fieldCount == 11 && $this->headerRow[0] == 'School Id') { |
|
$this->hasHeaderRow = true; |
|
array_shift($this->processed); // remove the header row from the processed array |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Is this file the default import type? |
|
* That would be where each line of the file is |
|
* <netid>, <score> |
|
* with no header info |
|
* |
|
* @return bool |
|
*/ |
|
public function isDefaultImportType() |
|
{ |
|
if ($this->importType == 'unknown' && $this->fieldCount == 2) { |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Generate and insert grades (as appropriate) |
|
* depending on the file type. |
|
* |
|
* @return $this |
|
*/ |
|
public function generateGrades() |
|
{ |
|
|
|
$this->generateReportHeader(); |
|
|
|
if (isset($this->assessment)) { |
|
$importTypeMethod = $this->importType . 'GradeGenerator'; |
|
$this->log("Looking for import type method: $importTypeMethod"); |
|
if (method_exists($this, $importTypeMethod)) { |
|
$this->$importTypeMethod(); |
|
} else { |
|
// Unknown file type |
|
$this->report |
|
.= <<<HEREDOC |
|
<div class='well'> |
|
<div class='pull-left error-icon'> |
|
<i class="fa fa-exclamation-triangle fa-3x" aria-hidden="true"></i> |
|
</div> |
|
<div class='error-message'> |
|
<p> |
|
Unknown file type.<br> |
|
Please <strong><a href='#' onclick='window.history.back();' title='go back to previous page'> |
|
go back</a></strong> and try again. |
|
</p> |
|
</div> |
|
</div> |
|
HEREDOC; |
|
echo $this->report; |
|
exit; |
|
} |
|
} |
|
return $this; |
|
} |
|
|
|
/** |
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
|
* Grade Generators - each of the methods below should find a $user and a |
|
* $score from the imported data row. If the user is found, then call |
|
* $this->processStudentGradeSets() |
|
* |
|
* To add a new grade generator, create a test above like isNewGeneratorType() or make sure it |
|
* is set manually somewhere this $this->setImportType('newGenerat') |
|
* to identify what kind of generator needs to be used and then create a named generator |
|
* below, e.g. newGeneratorGradeGenerator(). |
|
*/ |
|
|
|
/** |
|
* Default Grade Generator. Assume first column is username and |
|
* second is score. |
|
*/ |
|
public function defaultGradeGenerator() |
|
{ |
|
foreach ($this->processed as $row) { |
|
$username = $row[0]; |
|
$user = Models_User::fetchRowByUsername($username); |
|
$score = $row[1]; |
|
if (is_object($user)) { |
|
$this->processStudentGradeSets($user, $score); |
|
} else { |
|
$this->statistics['notfound'][] = $user; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* The GRAT file that gets uploaded will have (hopefully) one student per group |
|
* This generator will find every other student in that group and assign them |
|
* the same grade. |
|
* |
|
*/ |
|
public function gratGradeGenerator() |
|
{ |
|
$courseGroupsModel = new Models_Course_Group(); |
|
$course_id = $this->course->getID(); |
|
$course_groups = $courseGroupsModel->getGroupsByCourseIDSearchTerm($course_id, "TL"); |
|
$courseGroupAudienceModel = new Models_Course_Group_Audience(); |
|
foreach ($this->processed as $row) { |
|
$username= $row[0]; |
|
$score = $row[1]; |
|
$student = Models_User::fetchRowByUsername($username); |
|
if ($student) { |
|
// Find the course group this student belongs to |
|
foreach ($course_groups as $group) { |
|
$courseGroupMembership = $courseGroupAudienceModel::fetchAllByCGroupIDProxyID( |
|
$group->getID(), |
|
$student->getID() |
|
); |
|
// If current student belongs to a group, process the score to every student in this group |
|
if (count($courseGroupMembership) > 0) { |
|
$this->log( |
|
"$username is a member of a course group with id = " |
|
. $courseGroupMembership[0]->getCgroupID() |
|
); |
|
$group_members = $courseGroupAudienceModel->getGroupMembersByGroupID($group->getID()); |
|
foreach ($group_members as $member) { |
|
$fellow_student = Models_User::fetchRowByUsername($member["username"]); |
|
if (is_object($fellow_student)) { |
|
$this->processStudentGradeSets($fellow_student, $score); |
|
} |
|
} |
|
} |
|
} |
|
} else { |
|
$this->log("Couldn't find user with netId = $username"); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* NetId,Score |
|
*/ |
|
public function examSoftCsvGradeGenerator() |
|
{ |
|
foreach ($this->processed as $row) { |
|
$username = $row[0]; |
|
$score = $row[1]; |
|
$user = Models_User::fetchRowByUsername($username); |
|
|
|
if (is_object($user)) { |
|
if ($this->importOptions["grat"] == true) { |
|
$this->gratGradeGenerator($user, $score); |
|
} else { |
|
$this->processStudentGradeSets($user, $score); |
|
} |
|
} else { |
|
$this->statistics['notfound'][] = $username; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* NetId <tab> Score |
|
*/ |
|
public function examSoftTabGradeGenerator() |
|
{ |
|
foreach ($this->processed as $row) { |
|
$exploded = explode("\t", $row[0]); |
|
$username = $exploded[0]; |
|
$user = Models_User::fetchRowByUsername($username); |
|
$score = $exploded[1]; |
|
if (is_object($user)) { |
|
if ($this->importOptions["grat"] == true) { |
|
$this->gratGradeGenerator($user, $score); |
|
} else { |
|
$this->processStudentGradeSets($user, $score); |
|
} |
|
} else { |
|
$this->statistics['notfound'][] = $username; |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* NBME files have rows with 11 elements |
|
* (elements go from 0 to 10) |
|
* - username (netId) is element 5 |
|
* - percentage score is the last element 10 |
|
*/ |
|
public function nbmeGradeGenerator() |
|
{ |
|
foreach ($this->processed as $row) { |
|
$username = strtolower($row[5]); |
|
$user = Models_User::fetchRowByUsername($username); |
|
if (is_object($user)) { |
|
$this->users .= "\n" . $user->getUsername(); |
|
$score = $row[10]; |
|
$this->processStudentGradeSets($user, $score); |
|
} else { |
|
$this->statistics['notfound'][] = $username; |
|
} |
|
} |
|
} |
|
|
|
|
|
public function isScoreInRange($score) |
|
{ |
|
$totalPoints = 100; //default total allowed since most grades are a percentage |
|
|
|
if ($this->assessmentMarkingSchemeName == 'Numeric') { |
|
$totalPoints = $this->assessment->getNumericGradePointsTotal(); |
|
} |
|
|
|
if (is_numeric($score)) { |
|
if ($score >= 0 && $score <= $totalPoints) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* If the Marking scheme is numeric, then the grade being uploaded is |
|
* not a percentage, but a number of points out of a total possible. |
|
* |
|
* @param $score |
|
* @return float|int |
|
*/ |
|
public function adjustScoreForMarkingScheme($score) |
|
{ |
|
if ($this->assessmentMarkingSchemeName == 'Numeric') { |
|
$totalPoints = $this->assessment->getNumericGradePointsTotal(); |
|
return 100 * $score / $totalPoints; |
|
} |
|
return $score; |
|
} |
|
|
|
/** |
|
* Go through the grades and decide what to do with each grade. |
|
* - If there is no previous grade, insert this one |
|
* - If there is a previous one, save the highest one. |
|
* |
|
* @param $user |
|
* @param $score |
|
*/ |
|
public function processStudentGradeSets(Models_User $user, $score) |
|
{ |
|
// Is this score above the max? is it numeric? |
|
if (!$this->isScoreInRange($score)) { |
|
$this->statistics['outOfRange'][] = $user->getUsername() . " | $score"; |
|
return; //we want to return if the grade was out of range. |
|
} |
|
|
|
// Only import grades for enrolled students |
|
if (!$this->isStudentEnrolled($user)) { |
|
$this->statistics['notEntrolled'][] = $user->getUsername() . " | $score"; |
|
return; |
|
} |
|
|
|
// if the marking scheme is numeric, convert to percentage |
|
$score = $this->adjustScoreForMarkingScheme($score); |
|
|
|
// This saves some duplicate importing, just in case the new grade |
|
// might not be a float (but still be numerically equal) |
|
$score = floatval($score); |
|
|
|
$assessmentId = $this->assessment->getAssessmentID(); |
|
|
|
$username = $user->getUsername(); //NEEDS TO BE ENROLLED |
|
|
|
// get grades |
|
$grades = Models_Ua_Gradebook_Grade::getGradeSet($assessmentId, $user->getId()); |
|
|
|
foreach ($grades as $competency => $grade) { |
|
if ($this->isCompetencyBeingAssessed($competency)) { |
|
// If competency grade isn't set, all properties will be null |
|
if ($grade->getCompetency() == null) { |
|
$grade->setCompetency($competency); |
|
$grade->setAssessmentId($assessmentId); |
|
$grade->setProxyId($user->getId()); |
|
$grade->setThresholdNotified(0); |
|
$grade->setValue($score); |
|
$grade->insert(); |
|
$this->statistics['new'][$username .' | ' . $competency] = $user; |
|
$this->log("Adding new grade for $username | $competency"); |
|
} elseif ($this->importOptions['replace'] == 'replace-all') { |
|
// there is already a grade, and we replace all existing grades |
|
$oldGrade = $grade->getValue(); |
|
$grade->setValue($score); |
|
$grade->update(); |
|
$this->statistics['updated'][$username .' | ' . $competency] = $grade; |
|
$this->log("Replace old grade (" . $oldGrade . ") with $score for $username | $competency"); |
|
} elseif (round($grade->getValue(), 6) < round($score, 6) |
|
&& $this->importOptions['replace'] == 'replace-higher') { |
|
// there is already a grade, but new grade is higher |
|
$oldGrade = $grade->getValue(); |
|
$grade->setValue($score); |
|
$grade->update(); |
|
$this->statistics['updated'][$username .' | ' . $competency] = $grade; |
|
$this->log("New grade is higher: ". $grade->getValue() . " for $username | $competency | $score"); |
|
} else { |
|
$this->log( |
|
"Old grade: " . $grade->getValue() |
|
. " is not being changed for $username | $competency | $score" |
|
); |
|
$this->statistics['unchanged'][$username .' | ' . $competency] = $grade; |
|
// do nothing old grade must be > new grade |
|
} |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Is the specified competency being assessed for the current assessment? |
|
* |
|
* @param $competency 2 letter string representing competency |
|
* |
|
* @return bool |
|
*/ |
|
public function isCompetencyBeingAssessed($competency) |
|
{ |
|
if (is_object($this->assessmentCompetencies[$competency]) |
|
&& $this->assessmentCompetencies[$competency]->getPercentage() > 0 |
|
) { |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Is this student enrolled in this course/course period? |
|
* |
|
* @param $student |
|
* |
|
* @return bool |
|
*/ |
|
public function isStudentEnrolled($student) |
|
{ |
|
if (!is_null($student)) { |
|
return in_array($student->getId(), $this->enrolledStudentIds); |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* Set an import option (currently, there is only 'replace') |
|
* |
|
* @param $name |
|
* @param $value |
|
* |
|
* @return $this |
|
*/ |
|
public function setImportOption($name, $value) |
|
{ |
|
$this->importOptions[$name] = $value; |
|
$this->log("Setting $name import option to $value"); |
|
return $this; |
|
} |
|
|
|
/** |
|
* @param $message |
|
*/ |
|
public function log($message) |
|
{ |
|
$this->log .= "\n" . trim($message); |
|
} |
|
|
|
/******************************************************** |
|
* getters and setters |
|
* |
|
*/ |
|
|
|
/** |
|
* @return mixed |
|
*/ |
|
public function getFile() |
|
{ |
|
return $this->file; |
|
} |
|
|
|
/** |
|
* @param mixed $file |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setFile($file) |
|
{ |
|
$this->file = $file; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return mixed |
|
*/ |
|
public function getCourse() |
|
{ |
|
return $this->course; |
|
} |
|
|
|
/** |
|
* @param mixed $course |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setCourse($course) |
|
{ |
|
$this->course = $course; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return mixed |
|
*/ |
|
public function getAssessment() |
|
{ |
|
return $this->assessment; |
|
} |
|
|
|
/** |
|
* @param mixed $assessment |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setAssessment($assessment) |
|
{ |
|
$this->assessment = $assessment; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return mixed |
|
*/ |
|
public function getAssessmentCompetencies() |
|
{ |
|
return $this->assessmentCompetencies; |
|
} |
|
|
|
/** |
|
* @param mixed $assessmentCompetencies |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setAssessmentCompetencies($assessmentCompetencies) |
|
{ |
|
$this->assessmentCompetencies = $assessmentCompetencies; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return array|bool |
|
*/ |
|
public function getContents() |
|
{ |
|
return $this->contents; |
|
} |
|
|
|
/** |
|
* @param array|bool $contents |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setContents($contents) |
|
{ |
|
$this->contents = $contents; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
public function getProcessed() |
|
{ |
|
return $this->processed; |
|
} |
|
|
|
/** |
|
* @param array $processed |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setProcessed($processed) |
|
{ |
|
$this->processed = $processed; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return int |
|
*/ |
|
public function getFieldCount() |
|
{ |
|
return $this->fieldCount; |
|
} |
|
|
|
/** |
|
* @param int $fieldCount |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setFieldCount($fieldCount) |
|
{ |
|
$this->fieldCount = $fieldCount; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return bool |
|
*/ |
|
public function hasHeaderRow() |
|
{ |
|
return $this->hasHeaderRow; |
|
} |
|
|
|
/** |
|
* @param bool $hasHeaderRow |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setHasHeaderRow($hasHeaderRow) |
|
{ |
|
$this->hasHeaderRow = $hasHeaderRow; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
public function getHeaderRow() |
|
{ |
|
return $this->headerRow; |
|
} |
|
|
|
/** |
|
* @param array $headerRow |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setHeaderRow($headerRow) |
|
{ |
|
$this->headerRow = $headerRow; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return string |
|
*/ |
|
public function getImportType() |
|
{ |
|
return $this->importType; |
|
} |
|
|
|
/** |
|
* @param string $importType |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setImportType($importType) |
|
{ |
|
$this->log("Setting import type to: " . $importType); |
|
$this->importType = $importType; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
public function getImportOptions() |
|
{ |
|
return $this->importOptions; |
|
} |
|
|
|
/** |
|
* @param array $importOptions only one option so far: replace (replace-all|replace-higher) |
|
*/ |
|
public function setImportOptions($importOptions) |
|
{ |
|
$this->importOptions = $importOptions; |
|
} |
|
|
|
/** |
|
* @return array |
|
*/ |
|
public function getGrades() |
|
{ |
|
return $this->grades; |
|
} |
|
|
|
/** |
|
* Set the grades |
|
* |
|
* @param array $grades Set grades |
|
* |
|
* @return Classes_Assessments_GradeFileImporter |
|
*/ |
|
public function setGrades($grades) |
|
{ |
|
$this->grades = $grades; |
|
return $this; |
|
} |
|
|
|
/** |
|
* @return string html block for report. |
|
*/ |
|
public function getReport() |
|
{ |
|
// This shouldn't be done until you want to display it |
|
$this->generateReportFooter(); |
|
return $this->report; |
|
} |
|
|
|
/** |
|
* Set the html for the Report |
|
* |
|
* @param string $report html block for the report |
|
* |
|
* @return $this |
|
*/ |
|
public function setReport($report) |
|
{ |
|
$this->report = $report; |
|
return $this; |
|
} |
|
|
|
/** |
|
* Get the statistics array |
|
* |
|
* @return mixed |
|
*/ |
|
public function getStatistics() |
|
{ |
|
return $this->statistics; |
|
} |
|
} |