Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
48d752f
Improve validation for problem creation
damianhxy Oct 13, 2022
56355ed
Add migration
damianhxy Oct 13, 2022
c41052c
Initial support for global comments
damianhxy Oct 23, 2022
964f72b
Handle case with no comments
damianhxy Oct 23, 2022
b4134da
Display max_score to 2dp
damianhxy Oct 27, 2022
ec7c6a8
Add support for global annotations on view feedback page
damianhxy Oct 27, 2022
0c0cbe8
Align chevrons to right
damianhxy Oct 27, 2022
0b19d08
Add floating annotation tools for global annotations
damianhxy Oct 27, 2022
63c8c62
Add events for delete global annotation, and (un)collapse annotations
damianhxy Oct 28, 2022
5ceba42
Prevent annotation tools from shrinking
damianhxy Oct 30, 2022
58816d8
Global add button WIP
damianhxy Oct 30, 2022
b832e98
Hide global checkbox for global annotations
damianhxy Oct 30, 2022
8c10ec7
Add headers for empty annotation categories
damianhxy Oct 31, 2022
ac58e07
Reorder code to simplify
damianhxy Oct 31, 2022
d3c8516
Fix js and touch-up panel
damianhxy Nov 1, 2022
4d8fd2d
Edit support for global annotations
damianhxy Nov 1, 2022
9c397db
Change form button text for global annotations
damianhxy Nov 1, 2022
764ae74
Remove scaffolding
damianhxy Nov 1, 2022
3b27530
Update unreleased annotations wording
damianhxy Nov 1, 2022
cfcc261
Show – for problem if no score given
damianhxy Nov 1, 2022
6f160c8
Hide controls from students
damianhxy Nov 1, 2022
c381daf
Allow add and edit boxes to be open at the same time
damianhxy Nov 1, 2022
9e25e91
Update annotations documentation
damianhxy Nov 2, 2022
1830201
Update logic for @problemReleased
damianhxy Nov 2, 2022
2153b93
Hide autograded problems from annotation pane
damianhxy Nov 3, 2022
ac680da
Fix mismatched div tag
damianhxy Nov 3, 2022
d36e58e
Fix off-by-one numbering
damianhxy Nov 4, 2022
19eed53
Properly wrap col in row
damianhxy Nov 4, 2022
9335c86
Append edit form after each annotation
damianhxy Nov 4, 2022
12165f9
Rename function AnnotationPanel -> AnnotationPane
damianhxy Nov 4, 2022
38ebad1
Touch-up code
damianhxy Nov 4, 2022
2861782
Merge branch 'master' into global-annotations
damianhxy Nov 4, 2022
9da6d66
Address nits with annotations documentation
damianhxy Nov 8, 2022
f76f5c9
Round score to 2dp on viewFeedback too
damianhxy Nov 13, 2022
9a8d224
Address nit
damianhxy Nov 21, 2022
9ae5f0d
Merge branch 'master' into global-annotations
damianhxy Nov 26, 2022
8455029
Merge branch 'master' into global-annotations
damianhxy Nov 27, 2022
56abc53
Update @problemReleased check for viewFeedback
damianhxy Nov 27, 2022
7823267
Use Object.assign when updating global annotation
damianhxy Nov 27, 2022
cf6277d
Update shared comments docs
damianhxy Nov 27, 2022
ec09cb6
Prevent Global annotation header from flashing
damianhxy Nov 27, 2022
df40dbe
Turn global annotation form header into a div, simplify styles
damianhxy Nov 27, 2022
f54bdf8
Move descriptTuple logic to controller
damianhxy Nov 27, 2022
b4e6c45
Handle annotations on deleted problems
damianhxy Nov 28, 2022
989a978
Fix message displayed when no annotations exist for a problem
damianhxy Nov 28, 2022
fbb57b1
Destroy annotations when deleting problems
damianhxy Nov 28, 2022
32db5a1
Simplify equality check with true
damianhxy Nov 29, 2022
ccd779f
Merge branch 'master' into global-annotations
damianhxy Nov 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
178 changes: 151 additions & 27 deletions app/assets/javascripts/annotations.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ function fillAnnotationBox() {
$('#annotationPane').load(document.URL + ' #annotationPane', function() {
$('.collapsible').collapsible({ accordion: false });
attachChangeFileEvents();
attachAnnotationPaneEvents();
});
}

Expand Down Expand Up @@ -294,10 +295,13 @@ function displayAnnotations() {

_.each(annotationsByPositionByLine[currentHeaderPos], function (arr_annotations, line) {
_.each(arr_annotations, function (annotationObj, ind) {
if (annotationObj.global_comment)
return;
$("#annotation-line-" + line).append(newAnnotationBox(annotationObj));
refreshAnnotations();
});
});

refreshAnnotations();
}

function attachEvents() {
Expand All @@ -310,23 +314,22 @@ function attachEvents() {

$(".add-button").on("click", function (e) {
e.preventDefault();
var line = $(this).parent().parent().parent();
var annotationContainer = line.data("lineId");
const line = $(this).parent().parent().parent();
const annotationContainer = line.data("lineId");

// append an annotation form only if there is none currently
if ($("#annotation-line-" + annotationContainer).find(".annotation-line").length == 0) {
if ($("#annotation-line-" + annotationContainer).find(".annotation-line").length === 0) {
$("#annotation-line-" + annotationContainer).append(newAnnotationFormCode());

refreshAnnotations();
}

});
}

function attachChangeFileEvents() {
// Set up file switching to use the local cache
function changeFileClickHandler(e) {
wasCachedLocally = changeFile($(this).data("header_position"));
const wasCachedLocally = changeFile($(this).data("header_position"));
if (wasCachedLocally) {
e.preventDefault();
if ($(this).data("line")) {
Expand All @@ -343,6 +346,61 @@ function attachChangeFileEvents() {
$(".descript-link").on("click", changeFileClickHandler);
}

// Events that deal with the annotation pane
function attachAnnotationPaneEvents() {
// Add action
$(".global-annotation-add-button").on("click", function (e) {
e.preventDefault();
const problem = $(this).data("problem");
const $headerDiv = $(this).parent().parent();

if (!$headerDiv.next().hasClass("global-annotation-form")) {
$headerDiv.after(globalAnnotationFormCode(true, { problem }));
}
});

// Edit action
$(".global-annotation-edit-button").on("click", function (e) {
e.preventDefault();
const problem = $(this).parent().data("problem");
const score = $(this).parent().data("score");
const comment = $(this).parent().data("comment");
const annotationId = $(this).parent().data("annotationid");
const $annotationDiv = $(this).parents(".global-annotation");

if (!$annotationDiv.next().hasClass("global-annotation-form")) {
$annotationDiv.after(globalAnnotationFormCode(false, { problem, score, comment, annotationId }));
}
});

// Delete action for global annotations
$('.global-annotation-delete-button').on("click", function (e) {
e.preventDefault();
if (!confirm("Are you sure you want to delete this annotation?")) return;
const annotationIdData = $(this).parent().data('annotationid');
const annotationId = annotations.findIndex((e) => e.id === annotationIdData);

if (annotationId === -1) return;

const annotation = annotations[annotationId];

$.ajax({
url: deletePath(annotation),
type: 'DELETE',
complete: function () {
annotations.splice(annotationId, 1);
fillAnnotationBox();
}
});
});

// Chevron events (collapse / show problem)
$('.collapsible-header-controls .collapse-icon, .collapsible-header-controls .expand-icon').on('click', function(e) {
e.preventDefault();
$(e.target).closest(".collapsible-header-wrap").find(".collapsible-header").click();
});
}

var initializeAnnotationsForCode = function () {
window.annotationMode = "Code";

Expand Down Expand Up @@ -421,15 +479,15 @@ function newAnnotationFormCode() {
_.each(problems, function (problem) {
if (autogradedproblems[problem.id] != 0) { // Because grader == 0 is autograder
box.find("select").append(
$("<option />").val(problem.id).text(problem.name)
$("<option />").val(problem.id).text(problem.name)
)
}
})

box.find('.annotation-form').show();
box.find('.annotation-cancel-button').click(function (e) {
e.preventDefault();
$(this).parent().parent().parent().parent().remove();
$(this).parents(".annotation-form").parent().remove();
refreshAnnotations();
})

Expand Down Expand Up @@ -471,7 +529,73 @@ function newAnnotationFormCode() {
}


submitNewAnnotation(comment, shared_comment, score, problem_id, line, $(this));
submitNewAnnotation(comment, shared_comment, false, score, problem_id, line, $(this));
});

return box;
}

// Create form code to create / update a global annotation
// config takes the following keys: problem, score (for edit), comment (for edit)
function globalAnnotationFormCode(newAnnotation, config) {
const box = $(".base-global-annotation-form").clone();
box.removeClass("base-global-annotation-form");

if (!newAnnotation) {
box.find(".annotation-form-header").text("Update global annotation");
box.find(".comment").val(config.comment);
box.find(".score").val(config.score);
box.find('input[type=submit]').val("Update annotation");
}

var problemNameToIdMap = {};
_.each(problems, function (problem) {
problemNameToIdMap[problem.name] = problem.id;
})

box.find(".annotation-form-header").show();
box.find('.annotation-form').show();
box.find('.annotation-cancel-button').click(function (e) {
e.preventDefault();
$(this).parents(".annotation-form").parent().remove();
})

box.find('#comment-textarea').autocomplete({
appendTo: box.find('#comment-textarea').parent(),
minLength: 0,
delay: 0,
source: localCache["shared_comments"]
}).focus(function () {
$(this).autocomplete('search', $(this).val())
});

box.tooltip();

box.find('.annotation-form').submit(function (e) {
e.preventDefault();
var comment = $(this).find(".comment").val();
var shared_comment = $(this).find("#shared-comment").is(":checked");
var score = $(this).find(".score").val();
var problem_id = problemNameToIdMap[config.problem];

if (comment === undefined || comment === "") {
box.find('.error').text("Annotation comment can not be blank!").show();
return;
}

if (score === undefined || score === "") {
box.find('.error').text("Annotation score can not be blank!").show();
return;
}

if (newAnnotation) {
submitNewAnnotation(comment, shared_comment, true, score, problem_id, 0, $(this));
} else {
const annotationObject = getAnnotationObject(config.annotationId);
Comment thread
damianhxy marked this conversation as resolved.
Object.assign(annotationObject, { comment, value: score, problem_id, shared_comment, global_comment: true });

updateAnnotation(annotationObject, box);
}
});

return box;
Expand Down Expand Up @@ -529,14 +653,14 @@ function initializeBoxForm(box, annotation) {
annotationObject.value = score;
annotationObject.problem_id = problem_id;
annotationObject.shared_comment = shared_comment;
annotationObject.global_comment = false;

updateAnnotation(annotationObject, box);
});
}

// this creates the HTML to display an annotation.
function newAnnotationBox(annotation) {

var box = $(".base-annotation-line").clone();
box.removeClass("base-annotation-line");

Expand Down Expand Up @@ -1060,16 +1184,11 @@ var submitNewPDFAnnotation = function (comment, value, problem_id, pageInd, xRat
}

/* sets up and calls $.ajax to submit an annotation */
var submitNewAnnotation = function (comment, shared_comment, value, problem_id, lineInd, form) {
var submitNewAnnotation = function (comment, shared_comment, global_comment, value, problem_id, lineInd, form) {
var newAnnotation = createAnnotation();
newAnnotation.line = parseInt(lineInd);
newAnnotation.comment = comment;
newAnnotation.value = value;
newAnnotation.problem_id = problem_id;
newAnnotation.filename = fileNameStr;
newAnnotation.shared_comment = shared_comment;
Object.assign(newAnnotation, { line: parseInt(lineInd), comment, value, problem_id, filename: fileNameStr, shared_comment, global_comment });

if (comment == undefined || comment == "") {
if (comment === undefined || comment === "") {
$(form).find('.error').text("Could not save annotation. Please refresh the page and try again.").show();
return;
}
Expand All @@ -1086,20 +1205,25 @@ var submitNewAnnotation = function (comment, shared_comment, value, problem_id,
type: "POST",
success: function (data, type) {
$(form).parent().remove();
$("#annotation-line-" + lineInd).append(newAnnotationBox(data));
refreshAnnotations();

if (!annotationsByPositionByLine[currentHeaderPos]) {
annotationsByPositionByLine[currentHeaderPos] = {};
}
// Logic to render annotation boxes, not applicable to global annotations
if (!global_comment) {
$("#annotation-line-" + lineInd).append(newAnnotationBox(data));
refreshAnnotations();

if (!annotationsByPositionByLine[currentHeaderPos]) {
annotationsByPositionByLine[currentHeaderPos] = {};
}

var annotationsByLine = annotationsByPositionByLine[currentHeaderPos];
var annotationsByLine = annotationsByPositionByLine[currentHeaderPos];

if (!annotationsByLine[lineInd]) {
annotationsByLine[lineInd] = [];
}

if (!annotationsByLine[lineInd]) {
annotationsByLine[lineInd] = [];
annotationsByLine[lineInd].push(data);
}

annotationsByLine[lineInd].push(data);
annotations.push(data);
fillAnnotationBox();
purgeCurrentPageCache();
Expand Down
43 changes: 24 additions & 19 deletions app/assets/stylesheets/annotations.css
Original file line number Diff line number Diff line change
Expand Up @@ -511,27 +511,26 @@
margin-bottom: 15px;
color: #4f4f4f;
}
.annotationSummary h1{
margin: 0px;
.annotationSummary .annotation-form-header {
background-color: #0882af;
padding: 8px;
border-right: none;
border-left: none;
font-weight: normal !important;
font-size: 1em !important;
padding: 8px 15px 8px 10px;
font-weight: normal;
color: white;
white-space: nowrap;
overflow: hidden;
padding-left: 10px;
padding-right: 15px;
text-align: left;
}
.annotationSummary ul {
box-shadow: none;
border: none;
margin: 10px 0;
}

.annotationSummary .collapsible-header-wrap {
display: flex;
}

.annotationSummary .collapsible-header-controls {
margin-left: auto;
}

.annotationSummary .collapsible-header{
min-height: inherit;
line-height: inherit;
Expand Down Expand Up @@ -559,13 +558,6 @@
overflow: auto;
}

/* Annotations collapsible icons */

.annotationSummary .collapsible i.expand-icon,
.annotationSummary .collapsible i.collapse-icon {
font-size: 2rem;
}

.annotationSummary .collapsible li.active i.expand-icon {
display: none;
}
Expand All @@ -574,6 +566,19 @@
display: none;
}

.annotationSummary .global-annotation {
display: flex;
}

.annotationSummary .global-annotation .annotation-tools {
flex-shrink: 0;
margin-left: auto;
}

.annotationSummary .global-annotation:not(:hover) .annotation-tools {
visibility: hidden;
}

.annotationSummary .summary_score {
float: right;
font-weight: normal;
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/annotations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ def annotation_params
params[:annotation].delete(:created_at)
params[:annotation].delete(:updated_at)
params.require(:annotation).permit(:filename, :position, :line, :submitted_by,
:comment, :shared_comment, :value, :problem_id,
:submission_id, :coordinate)
:comment, :shared_comment, :global_comment, :value,
:problem_id, :submission_id, :coordinate)
end

def set_annotation
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/assessments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,8 @@ def viewFeedback
if Archive.archive? @submission.handin_file_path
@files = Archive.get_files @submission.handin_file_path
end
@problemReleased = @submission.scores.pluck(:released).all?
@problemReleased = @submission.scores.pluck(:released).all? &&
!@assessment.before_grading_deadline?
# get_correct_filename is protected, so we wrap around controller-specific call
@get_correct_filename = ->(annotation) {
get_correct_filename(annotation, @files, @submission)
Expand Down
Loading