This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | #!/usr/bin/env php |
||
| 2 | <?php |
||
| 3 | |||
| 4 | define('IGNORE_UI', true); |
||
| 5 | |||
| 6 | require_once __DIR__ . '/../common.inc.php'; |
||
| 7 | require_once __DIR__ . '/../constants.inc.php'; |
||
| 8 | |||
| 9 | use smtech\GradingAnalytics\Toolbox; |
||
| 10 | use smtech\CanvasPest\CanvasPest; |
||
| 11 | |||
| 12 | // http://stackoverflow.com/a/21896310 |
||
| 13 | function hoursRange($lower = 0, $upper = 86400, $step = 3600, $keyFormat = '', $value = '', $valueIsFormat = false) |
||
| 14 | { |
||
| 15 | $times = array(); |
||
| 16 | |||
| 17 | if (empty( $value ) && $valueIsFormat) { |
||
| 18 | $value = 'g:i a'; |
||
| 19 | } |
||
| 20 | |||
| 21 | if (empty($keyFormat)) { |
||
| 22 | $keyFormat = 'g:i a'; |
||
| 23 | } |
||
| 24 | |||
| 25 | foreach (range( $lower, $upper, $step ) as $increment) { |
||
| 26 | $increment = gmdate( $keyFormat, $increment ); |
||
| 27 | |||
| 28 | list( $hour, $minutes ) = explode( ':', $increment ); |
||
| 29 | |||
| 30 | $date = new DateTime( $hour . ':' . $minutes ); |
||
| 31 | |||
| 32 | $times[(string) $increment] = ($valueIsFormat ? $date->format( $value ) : $value); |
||
| 33 | } |
||
| 34 | |||
| 35 | return $times; |
||
| 36 | } |
||
| 37 | |||
| 38 | function collectStatistics($term, Toolbox $toolbox) |
||
| 39 | { |
||
| 40 | $courses = $toolbox->api_get( |
||
| 41 | '/accounts/' . $toolbox->config(Toolbox::TOOL_CANVAS_ACCOUNT_ID) . '/courses', |
||
| 42 | array( |
||
| 43 | 'with_enrollments' => 'true', |
||
| 44 | 'enrollment_term_id' => $term |
||
| 45 | ) |
||
| 46 | ); |
||
| 47 | |||
| 48 | // so that everything has a consistent benchmark |
||
| 49 | $timestamp = time(); |
||
| 50 | |||
| 51 | foreach ($courses as $course) { |
||
|
0 ignored issues
–
show
|
|||
| 52 | $statistic = array( |
||
| 53 | 'timestamp' => date(DATE_ISO8601, $timestamp), |
||
| 54 | 'course[id]' => $course['id'], |
||
| 55 | 'course[name]' => $course['name'], |
||
| 56 | 'course[account_id]' => $course['account_id'], |
||
| 57 | 'gradebook_url' => $_SESSION[CANVAS_INSTANCE_URL] . "/courses/{$course['id']}/gradebook2", |
||
| 58 | 'assignments_due_count' => 0, |
||
| 59 | 'dateless_assignment_count' => 0, |
||
| 60 | 'created_after_due_count' => 0, |
||
| 61 | 'gradeable_assignment_count' => 0, |
||
| 62 | 'graded_assignment_count' => 0, |
||
| 63 | 'zero_point_assignment_count' => 0, |
||
| 64 | 'analytics_page' => $_SESSION[CANVAS_INSTANCE_URL] . "/courses/{$course['id']}/external_tools/" . $toolbox->config(Toolbox::TOOL_CANVAS_EXTERNAL_TOOL_ID) |
||
| 65 | ); |
||
| 66 | |||
| 67 | $teacherIds = array(); |
||
| 68 | $teacherNames = array(); |
||
| 69 | $teachers = $toolbox->api_get( |
||
| 70 | "/courses/{$course['id']}/enrollments", |
||
| 71 | array( |
||
| 72 | 'type[]' => 'TeacherEnrollment' |
||
| 73 | ) |
||
| 74 | ); |
||
| 75 | foreach ($teachers as $teacher) { |
||
|
0 ignored issues
–
show
The expression
$teachers of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
Loading history...
|
|||
| 76 | $teacherIds[] = $teacher['user']['id']; |
||
| 77 | $teacherNames[] = $teacher['user']['sortable_name']; |
||
| 78 | } |
||
| 79 | $statistic['teacher[id]s'] = serialize($teacherIds); |
||
| 80 | $statistic['teacher[sortable_name]s'] = serialize($teacherNames); |
||
| 81 | |||
| 82 | $account = $toolbox->api_get("/accounts/{$course['account_id']}"); |
||
| 83 | $statistic['account[name]'] = $account['name']; |
||
| 84 | |||
| 85 | // ignore classes with no teachers (how do they even exist? weird.) |
||
| 86 | if (count($teacherIds) != 0) { |
||
| 87 | $statistic['student_count'] = 0; |
||
| 88 | $students = $toolbox->api_get( |
||
| 89 | "/courses/{$course['id']}/enrollments", |
||
| 90 | array( |
||
| 91 | 'type[]' => 'StudentEnrollment' |
||
| 92 | ) |
||
| 93 | ); |
||
| 94 | $statistic['student_count'] = $students->count(); |
||
|
0 ignored issues
–
show
The method
count does only exist in smtech\CanvasPest\CanvasArray, but not in smtech\CanvasPest\CanvasObject.
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
| 95 | |||
| 96 | // ignore classes with no students |
||
| 97 | if ($statistic['student_count'] != 0) { |
||
| 98 | $assignments = $toolbox->api_get( |
||
| 99 | "/courses/{$course['id']}/assignments" |
||
| 100 | ); |
||
| 101 | |||
| 102 | $gradedSubmissionsCount = 0; |
||
| 103 | $turnAroundTimeTally = 0; |
||
| 104 | $leadTimeTally = 0; |
||
| 105 | $createdModifiedHistogram = array( |
||
| 106 | HISTOGRAM_CREATED => hoursRange(0, 86400, 3600, '', 0), |
||
| 107 | HISTOGRAM_MODIFIED => hoursRange(0, 86400, 3600, '', 0) |
||
| 108 | ); |
||
| 109 | |||
| 110 | foreach ($assignments as $assignment) { |
||
|
0 ignored issues
–
show
The expression
$assignments of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
Loading history...
|
|||
| 111 | // ignore unpublished assignments |
||
| 112 | if ($assignment['published'] == true) { |
||
| 113 | // check for due dates |
||
| 114 | $dueDate = new DateTime($assignment['due_at']); |
||
| 115 | $dueDate->setTimeZone(new DateTimeZone(SCHOOL_TIME_ZONE)); |
||
| 116 | if (($timestamp - $dueDate->getTimestamp()) > 0) { |
||
| 117 | $statistic['assignments_due_count']++; |
||
| 118 | |||
| 119 | // update created_modified_histogram |
||
| 120 | $createdAt = new DateTime($assignment['created_at']); |
||
| 121 | $createdAt->setTimeZone(new DateTimeZone(SCHOOL_TIME_ZONE)); |
||
| 122 | $updatedAt = new DateTime($assignment['updated_at']); |
||
| 123 | $updatedAt->setTimeZone(new DateTimeZone(SCHOOL_TIME_ZONE)); |
||
| 124 | $createdModifiedHistogram[HISTOGRAM_CREATED][$createdAt->format('g:00 a')]++; |
||
| 125 | if ($createdAt != $updatedAt) { |
||
| 126 | $createdModifiedHistogram[HISTOGRAM_MODIFIED][$updatedAt->format('g:00 a')]++; |
||
| 127 | } |
||
| 128 | |||
| 129 | // tally lead time on the assignment |
||
| 130 | $leadTimeTally += strtotime($assignment['due_at']) - strtotime($assignment['created_at']); |
||
| 131 | |||
| 132 | // was the assignment created after it was due? |
||
| 133 | if (strtotime($assignment['due_at']) < strtotime($assignment['created_at'])) { |
||
| 134 | $statistic['created_after_due_count']++; |
||
| 135 | } |
||
| 136 | |||
| 137 | // ignore ungraded assignments |
||
| 138 | if ($assignment['grading_type'] != 'not_graded') { |
||
| 139 | $statistic['gradeable_assignment_count']++; |
||
| 140 | $hasBeenGraded = false; |
||
| 141 | |||
| 142 | // tally zero point assignments |
||
| 143 | if ($assignment['points_possible'] == '0') { |
||
| 144 | $statistic['zero_point_assignment_count']++; |
||
| 145 | } |
||
| 146 | |||
| 147 | // build submission statistic |
||
| 148 | $submissions = $toolbox->api_get( |
||
| 149 | "/courses/{$course['id']}/assignments/{$assignment['id']}/submissions" |
||
| 150 | ); |
||
| 151 | foreach ($submissions as $submission) { |
||
|
0 ignored issues
–
show
The expression
$submissions of type object<smtech\CanvasPest...CanvasPest\CanvasArray> is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
Loading history...
|
|||
| 152 | if ($submission['workflow_state'] == 'graded') { |
||
| 153 | if ($hasBeenGraded == false) { |
||
| 154 | $hasBeenGraded = true; |
||
| 155 | $statistic['graded_assignment_count']++; |
||
| 156 | } |
||
| 157 | $gradedSubmissionsCount++; |
||
| 158 | $turnAroundTimeTally += max( |
||
| 159 | 0, |
||
| 160 | strtotime($submission['graded_at']) - strtotime($assignment['due_at']) |
||
| 161 | ); |
||
| 162 | } |
||
| 163 | } |
||
| 164 | |||
| 165 | if (!$hasBeenGraded) { |
||
| 166 | if (array_key_exists('oldest_ungraded_assignment_due_date', $statistic)) { |
||
| 167 | if (strtotime($assignment['due_at']) < strtotime($statistic['oldest_ungraded_assignment_due_date'])) { |
||
| 168 | $statistic['oldest_ungraded_assignment_due_date'] = $assignment['due_at']; |
||
| 169 | $statistic['oldest_ungraded_assignment_url'] = $assignment['html_url']; |
||
| 170 | $statistic['oldest_ungraded_assignment_name'] = $assignment['name']; |
||
| 171 | } |
||
| 172 | } else { |
||
| 173 | $statistic['oldest_ungraded_assignment_due_date'] = $assignment['due_at']; |
||
| 174 | $statistic['oldest_ungraded_assignment_url'] = $assignment['html_url']; |
||
| 175 | $statistic['oldest_ungraded_assignment_name'] = $assignment['name']; |
||
| 176 | } |
||
| 177 | } |
||
| 178 | } |
||
| 179 | } else { |
||
| 180 | $statistic['dateless_assignment_count']++; |
||
| 181 | } |
||
| 182 | } |
||
| 183 | } |
||
| 184 | |||
| 185 | $statistic['created_modified_histogram'] = serialize($createdModifiedHistogram); |
||
| 186 | |||
| 187 | // calculate average submissions graded per assignment (if non-zero) |
||
| 188 | if ($statistic['gradeable_assignment_count'] && $statistic['student_count']) { |
||
| 189 | $statistic['average_submissions_graded'] = $gradedSubmissionsCount / ($statistic['gradeable_assignment_count'] * $statistic['student_count']); |
||
| 190 | } |
||
| 191 | |||
| 192 | // calculate the average lead-time on assignments |
||
| 193 | if ($statistic['assignments_due_count']) { |
||
| 194 | $statistic['average_assignment_lead_time'] = $leadTimeTally / $statistic['assignments_due_count'] / 60 / 60 / 24; |
||
| 195 | } |
||
| 196 | |||
| 197 | // calculate average grading turn-around per submission |
||
| 198 | if ($gradedSubmissionsCount) { |
||
| 199 | $statistic['average_grading_turn_around'] = $turnAroundTimeTally / $gradedSubmissionsCount / 60 / 60 / 24; |
||
| 200 | } |
||
| 201 | |||
| 202 | $query = "INSERT INTO `course_statistics`"; |
||
| 203 | $fields = array(); |
||
| 204 | $values = array(); |
||
| 205 | while (list($field, $value) = each($statistic)) { |
||
| 206 | $fields[] = $field; |
||
| 207 | $values[] = $value; |
||
| 208 | } |
||
| 209 | $query .= ' (`' . implode('`, `', $fields) . "`) VALUES ('" . implode("', '", $values) . "')"; |
||
| 210 | $result = $toolbox->mysql_query($query); |
||
| 211 | } |
||
| 212 | } |
||
| 213 | } |
||
| 214 | } |
||
| 215 | |||
| 216 | /* force API configuration from config file */ |
||
| 217 | $toolbox->setApi(new CanvasPest( |
||
| 218 | $_SESSION[CANVAS_INSTANCE_URL] . '/api/v1', |
||
| 219 | $toolbox->config(Toolbox::TOOL_CANVAS_API)['token'] |
||
| 220 | )); |
||
| 221 | |||
| 222 | /* collect data on terms currently in session */ |
||
| 223 | try { |
||
| 224 | $terms = $toolbox->api_get('accounts/1/terms'); |
||
| 225 | $now = strtotime('now'); |
||
| 226 | foreach ($terms['enrollment_terms'] as $term) { |
||
| 227 | if (isset($term['start_at']) && isset($term['end_at'])) { |
||
| 228 | if ((strtotime($term['start_at']) <= $now) && ($now <= strtotime($term['end_at']))) { |
||
| 229 | collectStatistics($term['id'], $toolbox); |
||
| 230 | } |
||
| 231 | } |
||
| 232 | } |
||
| 233 | } catch (Exception $e) { |
||
| 234 | echo $e->getMessage(); |
||
| 235 | } |
||
| 236 |
There are different options of fixing this problem.
If you want to be on the safe side, you can add an additional type-check:
If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:
Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.