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

Merged
merged 23 commits into from
Feb 2, 2025
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
96e5995
Main Table UI Changes (#1886)
lykimchee Apr 19, 2023
ed5bf82
Add sorting icons to new manage submissions (#1890)
michellexliu Apr 23, 2023
3a9a25f
Adds Score Details (#1893)
victorhuangwq May 13, 2023
3a62572
Merge branch 'master' into feature/new-manage-submissions
lykimchee Sep 20, 2023
9fb21b7
Manage submissions rebase to master (#2047)
michellexliu Feb 4, 2024
1d4e11e
[Manage Submissions] Updated tweak annotations (#1992)
michellexliu Mar 18, 2024
f06e55c
[Manage Submissions] Score popup modal styling + fixes (#1952)
lykimchee Nov 19, 2024
d7d881a
Manage Submissions Button Functionalities (#1903)
lykimchee Dec 5, 2024
cecd682
Merge conflicts
KesterTan Dec 18, 2024
00e126a
Merge conflicts for env template and gitignore
KesterTan Dec 18, 2024
605c957
Merge conflicts for style
KesterTan Dec 18, 2024
d523c2e
Fixed delete confirm button
KesterTan Dec 26, 2024
f06cd71
Fixed linting
KesterTan Dec 26, 2024
f6a0c05
Fixed tests and linting
KesterTan Jan 2, 2025
4854c5c
Merge branch 'master' into feature/new-manage-submissions
KesterTan Jan 2, 2025
36b2fd6
Merge branch 'master' into feature/new-manage-submissions
KesterTan Jan 13, 2025
44c33e8
Removed unnecessary duplicate styles
KesterTan Jan 13, 2025
088cfdc
Added checks for excuse popover
KesterTan Jan 13, 2025
b514a64
Removed svn which was deprecated
KesterTan Jan 26, 2025
61a546c
Fixed broken final_score and late penalty
KesterTan Jan 28, 2025
55dcf8b
Style fixes and pagination
KesterTan Jan 30, 2025
3e48f8f
Fixes pagination and select all
KesterTan Jan 30, 2025
8255c0f
Added hover message for unexcuse
KesterTan Feb 2, 2025
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