forked from code-dot-org/code-dot-org
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprogress.js
More file actions
171 lines (148 loc) · 6.42 KB
/
Copy pathprogress.js
File metadata and controls
171 lines (148 loc) · 6.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* globals dashboard, appOptions */
var clientState = require('./clientState');
var StageProgress = require('./components/progress/stage_progress');
var CourseProgress = require('./components/progress/course_progress');
var progress = module.exports;
/**
* See ApplicationHelper#activity_css_class.
* @param result
* @return {string}
*/
progress.activityCssClass = function (result) {
if (!result) {
return 'not_tried';
}
if (result >= 30) {
return 'perfect';
}
if (result >= 20) {
return 'passed';
}
return 'attempted';
};
/**
* Returns the "best" of the two results, as defined in apps/src/constants.js.
* Note that there are negative results that count as an attempt, so we can't
* just take the maximum.
* @param {Number} a
* @param {Number} b
* @return {string} The result css class.
*/
progress.mergedActivityCssClass = function (a, b) {
return progress.activityCssClass(clientState.mergeActivityResult(a, b));
};
progress.populateProgress = function (scriptName, puzzlePage) {
var status;
// Render the progress the client knows about (from sessionStorage)
var clientProgress = clientState.allLevelsProgress()[scriptName] || {};
Object.keys(clientProgress).forEach(function (levelId) {
$('.user-stats-block .level-' + levelId).addClass(progress.activityCssClass(clientProgress[levelId]));
});
$.ajax('/api/user_progress/' + scriptName).done(function (data) {
data = data || {};
// Show lesson plan links if teacher
if (data.isTeacher) {
$('.stage-lesson-plan-link').show();
}
// Merge progress from server (loaded via AJAX)
var serverProgress = data.levels || {};
Object.keys(serverProgress).forEach(function (levelId) {
// Only the server can speak to whether a level is submitted. If it is,
// we show the submitted styling.
if (serverProgress[levelId].submitted) {
// Clear the existing class and replace
$('.level-' + levelId).attr('class', 'level_link submitted');
} else if (serverProgress[levelId].pages_completed) {
// This is a multi-page level. There will be multiple dots for the same level ID,
// so we need to decorate each of them individually.
var pagesCompleted = serverProgress[levelId].pages_completed;
for (var page = 0; page < pagesCompleted.length; page++) {
// The dot is considered perfect if the page is considered complete.
var pageCompleted = pagesCompleted[page];
status = pageCompleted ? "perfect" : "attempted";
// Clear the existing class and replace.
$($('.user-stats-block .level-' + levelId)[page]).attr('class', 'level-' + levelId + ' level_link ' + status);
// If this is the current level, highlight it.
if (window.appOptions && appOptions.serverLevelId && levelId == appOptions.serverLevelId && puzzlePage-1 == page) {
$($('.user-stats-block .level-' + appOptions.serverLevelId)[puzzlePage-1]).parent().addClass('puzzle_outer_current');
}
}
} else if (serverProgress[levelId].result !== clientProgress[levelId]) {
status = progress.mergedActivityCssClass(clientProgress[levelId], serverProgress[levelId].result);
// Clear the existing class and replace
$('.level-' + levelId).attr('class', 'level_link ' + status);
// Write down new progress in sessionStorage
clientState.trackProgress(null, null, serverProgress[levelId].result, scriptName, levelId);
}
});
});
// Unless we already highlighted a specific page, highlight the current level.
if (puzzlePage == -1 && window.appOptions && appOptions.serverLevelId) {
$('.level-' + appOptions.serverLevelId).parent().addClass('puzzle_outer_current');
}
};
progress.renderStageProgress = function (stageData, progressData, clientProgress, currentLevelId, puzzlePage) {
var serverProgress = progressData.levels || {};
var currentLevelIndex = null;
var lastLevelId = null;
var levelRepeat = 0;
var combinedProgress = stageData.levels.map(function (level, index) {
// Determine the current level index.
// However, because long assessments can have the same level appearing
// multiple times, just set this the first time it's determined.
if (level.id === currentLevelId && currentLevelIndex === null) {
currentLevelIndex = index;
}
// If we have a multi-page level, then we will encounter the same level ID
// multiple times in a row. Keep track of how many times we've seen it
// repeat, so that we know what page we're up to.
if (level.id === lastLevelId) {
levelRepeat++;
} else {
lastLevelId = level.id;
levelRepeat = 0;
}
var status;
if (serverProgress && serverProgress[level.id] && serverProgress[level.id].submitted) {
status = "submitted";
} else if (serverProgress && serverProgress[level.id] && serverProgress[level.id].pages_completed) {
// The dot is considered perfect if the page is considered complete.
var pageCompleted = serverProgress[level.id].pages_completed[levelRepeat];
status = pageCompleted ? "perfect" : "attempted";
} else if (clientState.queryParams('user_id')) {
// Show server progress only (the student's progress)
status = progress.activityCssClass((serverProgress[level.id] || {}).result);
} else {
// Merge server progress with local progress
status = progress.mergedActivityCssClass((serverProgress[level.id] || {}).result, clientProgress[level.id]);
}
var href = level.url + location.search;
return {
title: level.title,
status: status,
kind: level.kind,
url: href,
id: level.id
};
});
if (currentLevelIndex !== null && puzzlePage !== -1) {
currentLevelIndex += puzzlePage - 1;
}
var mountPoint = document.createElement('div');
mountPoint.style.display = 'inline-block';
$('.progress_container').replaceWith(mountPoint);
ReactDOM.render(React.createElement(StageProgress, {
levels: combinedProgress,
currentLevelIndex: currentLevelIndex
}), mountPoint);
};
progress.renderCourseProgress = function (scriptData) {
var teacherCourse = $('#landingpage').hasClass('teacher-course');
var mountPoint = document.createElement('div');
$('.user-stats-block').prepend(mountPoint);
ReactDOM.render(React.createElement(CourseProgress, {
display: teacherCourse ? 'list' : 'dots',
stages: scriptData.stages
}), mountPoint);
progress.populateProgress(scriptData.name);
};