Skip to content

Commit

Permalink
clean up and extend assessment CSV downloads; close PrairieLearn#646
Browse files Browse the repository at this point in the history
  • Loading branch information
mwest1066 committed Apr 28, 2017
1 parent 71c7bf2 commit baee598
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 142 deletions.
4 changes: 3 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# ChangeLog

* __2.7.0__ - 2017-04-26
* __2.7.0__ - 2017-04-28

* Add `/pl/webhooks/ping` endpoint for automated health checks.

Expand All @@ -12,6 +12,8 @@

* Add link to detailed instances CSV file on instructor assessment page.

* Add more assessment CSV download options.

* Allow development use of non-master git branches for courses.

* Fix `max_points` update during regrading.
Expand Down
108 changes: 92 additions & 16 deletions pages/instructorAssessment/instructorAssessment.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,16 @@
<h3 class="panel-title"><%= assessment_set.name %> <%= assessment.number %>: Downloads</h3>
</div>

<% if (assessment.multiple_instance) { %>
<div class="panel-body">
<p>
<small>
This is a <strong>multiple instance</strong> assessment, so each student may have more than one assessment instance. The CSV files are available in plain and <strong><tt>_all</tt></strong> versions. The plain versions contain one record per student, which is taken to be the assessment instance for that student with the maximum percentage score. The <strong><tt>_all</tt></strong> CSV files contain one record per assessment instance, possibly including more than one record per student.
</small>
</p>
</div>
<% } // assessment.multiple_instance %>
<table class="table table-condensed table-hover">
<thead>
<tr>
Expand All @@ -458,44 +468,110 @@
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= scoresCsvFilename %>"><%= scoresCsvFilename %></a>
</td>
<td>
Total percentage scores for all students. Scores range
from 0 to 100 (or higher if bonus credit was
given). For assessments with multiple instances per
student, the maximum assessment instance score for the
student is returned.
Total percentage score for each student. Scores range
from 0 to 100 (or higher if bonus credit was given).
</td>
</tr>
<% if (assessment.multiple_instance) { %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= scoresAllCsvFilename %>"><%= scoresAllCsvFilename %></a>
</td>
<td>
Total percentage score for each assessment
instance. Scores range from 0 to 100 (or higher if
bonus credit was given).
</td>
</tr>
<% } // assessment.multiple_instance %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= scoresByUsernameCsvFilename %>"><%= scoresByUsernameCsvFilename %></a>
</td>
<td>
Total percentage scores for all students, formatted
for upload into LMS gradebooks. Scores range from 0 to
100 (or higher if bonus credit was given). For
assessments with multiple instances per student, the
maximum assessment instance score for the student is
returned.
Total percentage score for each student, formatted by
username for upload into LMS gradebooks. Scores range
from 0 to 100 (or higher if bonus credit was given).
</td>
</tr>
<% if (assessment.multiple_instance) { %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= allInstanceScoresCsvFilename %>"><%= allInstanceScoresCsvFilename %></a>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= scoresByUsernameAllCsvFilename %>"><%= scoresByUsernameAllCsvFilename %></a>
</td>
<td>
Total percentage scores for all students and for all
assessment instances. Scores range from 0 to 100 (or
higher if bonus credit was given).
Total percentage score for each assessment instance,
formatted by username for upload into LMS
gradebooks. Scores range from 0 to 100 (or higher if
bonus credit was given).
</td>
</tr>
<% } // assessment.multiple_instance %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= pointsCsvFilename %>"><%= pointsCsvFilename %></a>
</td>
<td>
Total points for each student. Points range
from 0 to the maximum for this assessment.
</td>
</tr>
<% if (assessment.multiple_instance) { %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= pointsAllCsvFilename %>"><%= pointsAllCsvFilename %></a>
</td>
<td>
Total points for each assessment instance. Points
range from 0 to the maximum for this assessment.
</td>
</tr>
<% } // assessment.multiple_instance %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= pointsByUsernameCsvFilename %>"><%= pointsByUsernameCsvFilename %></a>
</td>
<td>
Total points for each student, formatted by username
for upload into LMS gradebooks. Points range from 0 to
100 (or higher if bonus credit was given).
</td>
</tr>
<% if (assessment.multiple_instance) { %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= pointsByUsernameAllCsvFilename %>"><%= pointsByUsernameAllCsvFilename %></a>
</td>
<td>
Total points for each assessment instance, formatted
by username for upload into LMS gradebooks. Points
range from 0 to 100 (or higher if bonus credit was
given).
</td>
</tr>
<% } // assessment.multiple_instance %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= instancesCsvFilename %>"><%= instancesCsvFilename %></a>
</td>
<td>
Detailed data about all assessment instances. Includes points, percentage scores, durations, and other information.
Detailed data for each student. Includes points,
maximum points, percentage score, duration, and other
information.
</td>
</tr>
<% if (assessment.multiple_instance) { %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= instancesAllCsvFilename %>"><%= instancesAllCsvFilename %></a>
</td>
<td>
Detailed data for each assessment instance. Includes
points, maximum points, percentage score, duration,
and other information.
</td>
</tr>
<% } // assessment.multiple_instance %>
<tr>
<td>
<a href="<%= urlPrefix %>/assessment/<%= assessment.id %>/<%= allSubmissionsCsvFilename %>"><%= allSubmissionsCsvFilename %></a>
Expand Down
174 changes: 101 additions & 73 deletions pages/instructorAssessment/instructorAssessment.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,24 @@ var filenames = function(locals) {
+ sanitizeName(locals.assessment.number)
+ '_';
return {
scoreStatsCsvFilename: prefix + 'score_stats.csv',
durationStatsCsvFilename: prefix + 'duration_stats.csv',
instancesCsvFilename: prefix + 'instances.csv',
scoresCsvFilename: prefix + 'scores.csv',
scoresByUsernameCsvFilename: prefix + 'scores_by_username.csv',
allInstanceScoresCsvFilename: prefix + 'all_instance_scores.csv',
finalSubmissionsCsvFilename: prefix + 'final_submissions.csv',
allSubmissionsCsvFilename: prefix + 'all_submissions.csv',
finalFilesZipFilename: prefix + 'final_files.zip',
allFilesZipFilename: prefix + 'all_files.zip',
questionStatsCsvFilename: prefix + 'question_stats.csv',
statsByDateCsvFilename: prefix + 'scores_by_date.csv',
scoreStatsCsvFilename: prefix + 'score_stats.csv',
durationStatsCsvFilename: prefix + 'duration_stats.csv',
scoresCsvFilename: prefix + 'scores.csv',
scoresAllCsvFilename: prefix + 'scores_all.csv',
scoresByUsernameCsvFilename: prefix + 'scores_by_username.csv',
scoresByUsernameAllCsvFilename: prefix + 'scores_by_username_all.csv',
pointsCsvFilename: prefix + 'points.csv',
pointsAllCsvFilename: prefix + 'points_all.csv',
pointsByUsernameCsvFilename: prefix + 'points_by_username.csv',
pointsByUsernameAllCsvFilename: prefix + 'points_by_username_all.csv',
instancesCsvFilename: prefix + 'instances.csv',
instancesAllCsvFilename: prefix + 'instances_all.csv',
finalSubmissionsCsvFilename: prefix + 'final_submissions.csv',
allSubmissionsCsvFilename: prefix + 'all_submissions.csv',
finalFilesZipFilename: prefix + 'final_files.zip',
allFilesZipFilename: prefix + 'all_files.zip',
questionStatsCsvFilename: prefix + 'question_stats.csv',
statsByDateCsvFilename: prefix + 'scores_by_date.csv',
};
};

Expand Down Expand Up @@ -123,7 +129,7 @@ router.get('/', function(req, res, next) {
function(callback) {
debug('query assessment_instance_data');
var params = {assessment_id: res.locals.assessment.id};
sqldb.query(sql.assessment_instance_data, params, function(err, result) {
sqldb.query(sql.select_assessment_instances, params, function(err, result) {
if (ERR(err, callback)) return;
res.locals.user_scores = result.rows;
callback(null);
Expand All @@ -136,9 +142,60 @@ router.get('/', function(req, res, next) {
});
});

var sendInstancesCsv = function(res, req, columns, options, callback) {
var params = {assessment_id: res.locals.assessment.id};
sqldb.query(sql.select_assessment_instances, params, function(err, result) {
if (ERR(err, callback)) return;

var rows = result.rows;
if (options.only_highest) {
rows = _.filter(rows, 'highest_score');
}

csvMaker.rowsToCsv(rows, columns, function(err, csv) {
if (ERR(err, callback)) return;
res.attachment(req.params.filename);
res.send(csv);
});
});
};

router.get('/:filename', function(req, res, next) {
_.assign(res.locals, filenames(res.locals));

var assessmentName = res.locals.assessment_set.name + ' ' + res.locals.assessment.number;
var scoresColumns = [
['UID', 'uid'],
[assessmentName, 'score_perc'],
];
var scoresByUsernameColumns = [
['Username', 'username'],
[assessmentName, 'score_perc'],
];
var pointsColumns = [
['UID', 'uid'],
[assessmentName, 'points'],
];
var pointsByUsernameColumns = [
['Username', 'username'],
[assessmentName, 'points'],
];
var instancesColumns = [
['UID', 'uid'],
['Username', 'username'],
['Name', 'name'],
['Role', 'role'],
['Assessment', 'assessment_label'],
['Instance', 'number'],
['Started', 'date_formatted'],
['Remaining', 'time_remaining'],
['Score (%)', 'score_perc'],
['Points', 'points'],
['Max points', 'max_points'],
['Duration (min)', 'duration_mins'],
['Hightest score', 'highest_score'],
];

if (req.params.filename == res.locals.scoreStatsCsvFilename) {
var params = {assessment_id: res.locals.assessment.id};
sqldb.queryOneRow(sql.assessment_stats, params, function(err, result) {
Expand Down Expand Up @@ -211,74 +268,45 @@ router.get('/:filename', function(req, res, next) {
res.send(csv);
});
});
} else if (req.params.filename == res.locals.instancesCsvFilename) {
var params = {assessment_id: res.locals.assessment.id};
sqldb.query(sql.assessment_instance_data, params, function(err, result) {
} else if (req.params.filename == res.locals.scoresCsvFilename) {
sendInstancesCsv(res, req, scoresColumns, {only_highest: true}, (err) => {
if (ERR(err, next)) return;
var columns = [
['UID', 'uid'],
['Name', 'name'],
['Role', 'role'],
['Assessment', 'assessment_label'],
['Instance', 'number'],
['Started', 'date_formatted'],
['Remaining', 'time_remaining'],
['Score (%)', 'score_perc'],
['Points', 'points'],
['Max points', 'max_points'],
['Duration (min)', 'duration_mins'],
];
csvMaker.rowsToCsv(result.rows, columns, function(err, csv) {
if (ERR(err, next)) return;
res.attachment(req.params.filename);
res.send(csv);
});
});
} else if (req.params.filename == res.locals.scoresCsvFilename) {
var params = {assessment_id: res.locals.assessment.id};
sqldb.query(sql.assessment_instance_scores, params, function(err, result) {
} else if (req.params.filename == res.locals.scoresAllCsvFilename) {
sendInstancesCsv(res, req, scoresColumns, {only_highest: false}, (err) => {
if (ERR(err, next)) return;
var assessmentName = res.locals.assessment_set.name + ' ' + res.locals.assessment.number;
var columns = [
['UID', 'uid'],
[assessmentName, 'score_perc'],
];
csvMaker.rowsToCsv(result.rows, columns, function(err, csv) {
if (ERR(err, next)) return;
res.attachment(req.params.filename);
res.send(csv);
});
});
} else if (req.params.filename == res.locals.scoresByUsernameCsvFilename) {
var params = {assessment_id: res.locals.assessment.id};
sqldb.query(sql.assessment_instance_scores_by_username, params, function(err, result) {
sendInstancesCsv(res, req, scoresByUsernameColumns, {only_highest: true}, (err) => {
if (ERR(err, next)) return;
var assessmentName = res.locals.assessment_set.name + ' ' + res.locals.assessment.number;
var columns = [
['Username', 'username'],
[assessmentName, 'score_perc'],
];
csvMaker.rowsToCsv(result.rows, columns, function(err, csv) {
if (ERR(err, next)) return;
res.attachment(req.params.filename);
res.send(csv);
});
});
} else if (req.params.filename == res.locals.allInstanceScoresCsvFilename) {
var params = {assessment_id: res.locals.assessment.id};
sqldb.query(sql.assessment_instance_scores_all, params, function(err, result) {
} else if (req.params.filename == res.locals.scoresByUsernameAllCsvFilename) {
sendInstancesCsv(res, req, scoresByUsernameColumns, {only_highest: false}, (err) => {
if (ERR(err, next)) return;
});
} else if (req.params.filename == res.locals.pointsCsvFilename) {
sendInstancesCsv(res, req, pointsColumns, {only_highest: true}, (err) => {
if (ERR(err, next)) return;
});
} else if (req.params.filename == res.locals.pointsAllCsvFilename) {
sendInstancesCsv(res, req, pointsColumns, {only_highest: false}, (err) => {
if (ERR(err, next)) return;
});
} else if (req.params.filename == res.locals.pointsByUsernameCsvFilename) {
sendInstancesCsv(res, req, pointsByUsernameColumns, {only_highest: true}, (err) => {
if (ERR(err, next)) return;
});
} else if (req.params.filename == res.locals.pointsByUsernameAllCsvFilename) {
sendInstancesCsv(res, req, pointsByUsernameColumns, {only_highest: false}, (err) => {
if (ERR(err, next)) return;
});
} else if (req.params.filename == res.locals.instancesCsvFilename) {
sendInstancesCsv(res, req, instancesColumns, {only_highest: true}, (err) => {
if (ERR(err, next)) return;
});
} else if (req.params.filename == res.locals.instancesAllCsvFilename) {
sendInstancesCsv(res, req, instancesColumns, {only_highest: false}, (err) => {
if (ERR(err, next)) return;
var assessmentName = res.locals.assessment_set.name + ' ' + res.locals.assessment.number;
var columns = [
['UID', 'uid'],
['Instance', 'number'],
[assessmentName, 'score_perc'],
];
csvMaker.rowsToCsv(result.rows, columns, function(err, csv) {
if (ERR(err, next)) return;
res.attachment(req.params.filename);
res.send(csv);
});
});
} else if (req.params.filename == res.locals.allSubmissionsCsvFilename
|| req.params.filename == res.locals.finalSubmissionsCsvFilename) {
Expand Down
Loading

0 comments on commit baee598

Please sign in to comment.