Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manage Submissions #2248

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
142 changes: 142 additions & 0 deletions app/assets/javascripts/annotations_helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/* following paths/functions for annotations */
var sharedCommentsPath = basePath + "/shared_comments";

var createPath = basePath + ".json";
var updatePath = function (ann) {
return [basePath, "/", ann.id, ".json"].join("");
};
var deletePath = updatePath;

function getSharedCommentsForProblem(problem_id) {
return localCache['shared_comments'][problem_id]?.map(
(annotation) => {
return {label: annotation.comment ?? annotation, value: annotation}
}
)
}

var selectAnnotation = box => (e, ui) => {
const {value} = ui.item;

const score = value.value ?? 0;
box.find('#comment-score').val(score);

const $textarea = box.find("#comment-textarea");
M.textareaAutoResize($textarea);

return false;
}

function focusAnnotation( event, ui ) {
$(this).val(ui.item.label);
return false;
}

// retrieve shared comments
// also retrieves annotation id to allow easy deletion in the future
function retrieveSharedComments(cb) {
$.getJSON(sharedCommentsPath, function (data) {
localCache['shared_comments'] = {};
data.forEach(e => {
if (!e.problem_id)
return;
localCache['shared_comments'][e.problem_id] ||= [];
localCache['shared_comments'][e.problem_id].push(e);
});
cb?.();
});
}

function purgeCurrentPageCache() {
localCache[currentHeaderPos] = {
codeBox: `<div id="code-box">${$('#code-box').html()}</div>`,
pdf: false,
symbolTree: `<div id="symbol-tree-box">${$('#symbol-tree-box').html()}</div>`,
versionLinks: `<span id="version-links">${$('#version-links').html()}</span>`,
versionDropdown: `<span id="version-dropdown">${$('#version-dropdown').html()}</span>`,
url: window.location.href,
};
}

function plusFix(n) {
n = parseFloat(n)
if (isNaN(n)) n = 0;

if (n > 0) {
return "+" + n.toFixed(2);
}

return n.toFixed(2);
}

function getProblemNameWithId(problem_id) {
var problem_id = parseInt(problem_id, 10);
var problem = _.findWhere(problems, { "id": problem_id });
if (problem == undefined) return "Deleted Problem(s)";
return problem.name;
}


// create an HTML element real nice and easy like
function elt(t, a) {
var el = document.createElement(t);
if (a) {
for (var attr in a)
if (a.hasOwnProperty(attr))
el.setAttribute(attr, a[attr]);
}
for (var i = 2; i < arguments.length; ++i) {
var arg = arguments[i];
if (typeof arg === "string")
arg = document.createTextNode(arg);
el.appendChild(arg);
}
return el;
}


// this creates a JSON representation of what the actual Rails Annotation model looks like
function createAnnotation() {
var annObj = {
submitted_by: cudEmailStr,
};
if (fileNameStr != null) {
annObj.filename = fileNameStr
}

if (currentHeaderPos || currentHeaderPos === 0) {
annObj.position = currentHeaderPos
}

return annObj;
}

function getAnnotationObject(annotationId) {
for (var i = 0; i < annotations.length; i++) {
if (annotations[i].id == annotationId) {
return annotations[i];
}
}
}


var updateAnnotationBox = function (annObj) {

var problemStr = annObj.problem_id ? getProblemNameWithId(annObj.problem_id) : "General";
var valueStr = annObj.value ? annObj.value.toString() : "None";
var commentStr = annObj.comment;

if (annotationMode === "PDF") {
$('#ann-box-' + annObj.id).find('.score-box').html("<div>Problem: " + problemStr + "</div><div> Score: " + valueStr + "</div>");
$("#ann-box-" + annObj.id).find('.body').html(commentStr);
}
else {
$('#ann-box-' + annObj.id).find('.score-box').html("<span>" + problemStr + "</span><span>" + valueStr + "</span>");
}
$('#ann-box-' + annObj.id).find('.edit').show();
$('#ann-box-' + annObj.id).find('.body').show();
$('#ann-box-' + annObj.id).find('.score-box').show();
$('#ann-box-' + annObj.id).find('.minimize').show();
$('#ann-box-' + annObj.id).draggable('enable');
$('#ann-box-' + annObj.id).resizable('enable');
}
158 changes: 158 additions & 0 deletions app/assets/javascripts/annotations_popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
const updateEditTweakButtons = () => {
tweaks.forEach(({tweak, submission}) => {
get_tweak_total(submission.id).then(data => {
tweak?.setState({ amount: data })
})
})
}
const get_tweak_total = (submission_id) => {
return new Promise((resolve, reject) => {
$.ajax({
url: `submissions/${submission_id}/tweak_total`,
method: 'GET',
dataType: 'json',
success: (data) => {
resolve(data);
},
error: (error) => {
console.error("There was an error fetching the scores:", error);
reject(error);
}
});
});
}
function newAnnotationFormCode() {
var box = $(".base-annotation-line").clone();
box.removeClass("base-annotation-line");

box.addClass("new-annotation");

// Creates a dictionary of problem and grader_id
var problemGraderId = {};

_.each(scores, function (score) {
problemGraderId[score.problem_id] = score.grader_id;
});

_.each(problems, function (problem, i) {
if (problemGraderId[problem.id] !== 0) { // Because grader == 0 is autograder
box.find("select")?.append(
$("<option />").val(problem.id).text(problem.name)
);
}
});

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

box.find('#comment-textarea').autocomplete({
appendTo: box.find('#comment-textarea').parent(),
source: getSharedCommentsForProblem(box.find("select").val()) || [],
minLength: 0,
delay: 0,
select: selectAnnotation(box),
focus: focusAnnotation
}).focus(function () {
M.textareaAutoResize($(this));
$(this).autocomplete('search', $(this).val())
});

box.tooltip();

box.find("select").on('change', function () {
const problem_id = $(this).val();

// Update autocomplete to display shared comments for selected problem
box.find("#comment-textarea").autocomplete({
source: getSharedCommentsForProblem(problem_id) || []
});
});

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 = $(this).find(".problem-id").val();

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 (problem_id == undefined) {
if ($('.select').children('option').length > 0)
box.find('.error').text("Problem not selected").show();
else
box.find('.error').text("There are no non-autograded problems. Create a new one at Edit Assessment > Problems").show();
return;
}
submitNewAnnotation(comment, shared_comment, true, score, problem_id, 0, $(this));
});

return box;
}

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

if (comment === undefined || comment === "") {
$(form).find('.error').text("Could not save annotation. Please refresh the page and try again.").show();
return;
}

$(form).find('.error').hide();

$.ajax({
url: createPath,
accepts: "json",
dataType: "json",
data: {
annotation: newAnnotation
},
type: "POST",
success: function (data, type) {
updateEditTweakButtons();
$(form).parent().remove();
$('#annotation-modal').modal('close');
},
error: function (result, type) {
$(form).find('.error').text("Could not save annotation. Please refresh the page and try again.").show();
},
complete: function (result, type) { }
});

}

var updateAnnotation = function (annotationObj, box) {
$(box).find(".error").hide();
$.ajax({
url: updatePath(annotationObj),
accepts: "json",
dataType: "json",
data: {
annotation: annotationObj
},
type: "PUT",
success: function (data, type) {
$(box).remove();
displayAnnotations();
},
error: function (result, type) {
$(box).find('.error').text("Failed to save changes to the annotation. Please refresh the page and try again.").show();
},
complete: function (result, type) { }
});
}

45 changes: 45 additions & 0 deletions app/assets/javascripts/autolab_component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Usage:
* // Create a new instance, associating it with the element specified by id 'app'
const MyComponent = new AutolabComponent('app');

// Define a template for the instance
MyComponent.template = function() {
return `
<div>
<p>Name: ${this.state.name}</p>
<p>Age: ${this.state.age}</p>
</div>
`;
};

// Set the initial state
MyComponent.setState({
name: 'John',
age: 30
});

// Later in the code, you can update the state like this:
// MyComponent.setState({ age: 31 });
*/


function AutolabComponent(elementId, initialState = {}, template = () => {}) {
this.elementId = elementId;
this.state = initialState;
this.template = template;

this.setState = function(newState = {}) {
$.extend(this.state, newState);
this.render();
};

this.template = function() {
// Default template; should be overridden by users of the library
return `<div></div>`;
};

this.render = function() {
$(`#${this.elementId}`).html(this.template());
};
KesterTan marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading