Skip to content
Open
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
f214ac5
feat: new assignment model
karimouf Mar 17, 2026
e27594a
feat: assignment ref in submissions
karimouf Mar 17, 2026
2355809
feat: assignment dashboard
karimouf Mar 17, 2026
f73e040
Merge branch 'dev' into feat-120-assigment_dashboard
karimouf Mar 23, 2026
affdaa1
refactor: remove assignment grouping
karimouf Mar 23, 2026
ef80e43
refactor: add assignment and check revision limit
karimouf Mar 25, 2026
5635b45
Merge remote-tracking branch 'origin/feat-44-template_dashboard' into…
karimouf Mar 25, 2026
111ccc3
feat: add header buttons
karimouf Mar 29, 2026
b5be1d3
feat: add unlimited in slider
karimouf Mar 29, 2026
65e10ed
refactor: split assignments into 2 views
karimouf Mar 30, 2026
d43914d
feat: add new role feature
karimouf Mar 30, 2026
b0603b6
feat: assignment view rights
karimouf Mar 30, 2026
1fe0540
feat: add study usage count in document
karimouf Mar 30, 2026
77162fd
feat: add user roles assignment + closed assignment
karimouf Mar 30, 2026
46c10d1
feat: add userId + public keys to assignment
karimouf Mar 30, 2026
cf3c526
feat: add user filter to assignments
karimouf Mar 30, 2026
ed5dda5
feat: updating study usage count
karimouf Mar 30, 2026
d85e8fa
fix: remove unused group key in submission
karimouf Mar 30, 2026
a12b63e
feat: adapt uploadSingleSubmission function
karimouf Mar 30, 2026
669ca4e
feat: assignment modal components
karimouf Mar 30, 2026
3388413
refactor: enhance copying
karimouf Mar 30, 2026
32af3fb
fix: copied assignment is not closed
karimouf Mar 30, 2026
1b1f548
feat: view submission if having admin rights
karimouf Mar 30, 2026
8bd3dab
feat: add assignment admin view rights
karimouf Mar 30, 2026
7c2f013
refactor: unified the submission table
karimouf Mar 30, 2026
a7da6fc
feat: extend submission with name and desc
karimouf Mar 30, 2026
6c8ee7d
feat: add submission name column
karimouf Mar 30, 2026
d4f2644
feat: add a description and name fields
karimouf Mar 30, 2026
2ae3255
refactor: remove previousSubmissionAssignmentId key
karimouf Mar 30, 2026
9ec43ce
Merge remote-tracking branch 'origin/dev' into feat-145-assignments_e…
mohammadsherif0 Apr 6, 2026
1220148
feat: add projectId to assignments
karimouf Apr 6, 2026
e05990e
feat: add rights to manage assignments and submissions
karimouf Apr 6, 2026
255c447
feat: enhanced filter and attributes function to handle user filters …
karimouf Apr 6, 2026
bef5d94
feat: change submission from admin to user nav elements
karimouf Apr 6, 2026
49cac4c
feat: change user roles to a public table
karimouf Apr 6, 2026
f08d7af
refactor: adapted the import from moodle functionality
karimouf Apr 6, 2026
066573a
refactor: move buttons in submissions + change view from admin and us…
karimouf Apr 6, 2026
ebd0f90
feat: adapted modals and subcomponents to new structure
karimouf Apr 6, 2026
11169cb
Merge branch 'dev' into feat-120-assigment_dashboard
dennis-zyska Apr 7, 2026
465d916
feat : add setting and placeholders for submission upload email
mohammadsherif0 Apr 11, 2026
8776a4a
feat : add submission upload email template type and placeholder hand…
mohammadsherif0 Apr 11, 2026
ba159ec
feat : notify assignment owner on submission upload with template or …
mohammadsherif0 Apr 11, 2026
fbd2d7d
feat : support submission upload email template in settings and templ…
mohammadsherif0 Apr 11, 2026
3930fd8
Merge branch 'feat-120-assigment_dashboard' into feat-145-assignments…
mohammadsherif0 Apr 11, 2026
16cccf6
feat : add notify on submission upload field to assignment
mohammadsherif0 Apr 11, 2026
e6c4f03
feat : require assignment name and drop link placeholder for submissi…
mohammadsherif0 Apr 11, 2026
8c30b59
refactor: reword notify-on-submission-upload toggle label and help
mohammadsherif0 Apr 11, 2026
8e5ae7a
Merge pull request #152 from UKPLab/feat-145-assignments_email_notifi…
karimouf Apr 13, 2026
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
137 changes: 137 additions & 0 deletions backend/db/migrations/20260316122741-create-assignment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.createTable('assignment', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
title: {
type: Sequelize.STRING,
allowNull: false,
},
description: {
type: Sequelize.TEXT,
allowNull: true,
},
public: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
},
projectId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'project',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
studyId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'study',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
workflowId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'workflow',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
userId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'user',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
maxRevisions: {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 1,
},
start: {
type: Sequelize.DATE,
allowNull: true,
defaultValue: null,
},
end: {
type: Sequelize.DATE,
allowNull: true,
defaultValue: null,
},
validationConfigurationId: {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: null,
references: {
model: 'configuration',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
parentAssignmentId: {
type: Sequelize.INTEGER,
allowNull: true,
defaultValue: null,
references: {
model: 'assignment',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
},
allowReUpload: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
},
notifyOnSubmissionUpload: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true,
},
deleted: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
deletedAt: {
allowNull: true,
defaultValue: null,
type: Sequelize.DATE,
},
});
},

async down (queryInterface, Sequelize) {
await queryInterface.dropTable('assignment');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.addColumn('submission', 'assignmentId', {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'assignment',
key: 'id',
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL',
});
},

async down (queryInterface, Sequelize) {
await queryInterface.removeColumn('submission', 'assignmentId');
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

const navElements = [
{
name: "Assignments",
groupId: "Default",
icon: "list-check",
order: 14,
admin: false,
path: "assignments",
component: "Assignments",
},
];

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.bulkInsert(
"nav_element",
await Promise.all(
navElements.map(async (t) => {
const groupId = await queryInterface.rawSelect(
"nav_group",
{
where: { name: t.groupId },
},
["id"]
);

t["createdAt"] = new Date();
t["updatedAt"] = new Date();
t["groupId"] = groupId;

return t;
})
),
{}
);
},

async down (queryInterface, Sequelize) {
await queryInterface.bulkDelete(
"nav_element",
{
name: navElements.map((t) => t.name),
},
{}
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

const assignmentViewRights = [
{
name: "frontend.dashboard.assignments.view",
description: "access to view assignments in the dashboard",
},
];

const roleRights = [
{ role: "teacher", userRightName: "frontend.dashboard.assignments.view" },
{ role: "mentor", userRightName: "frontend.dashboard.assignments.view" },
{ role: "admin", userRightName: "frontend.dashboard.assignments.view" },
{ role: "user", userRightName: "frontend.dashboard.assignments.view" },
{ role: "guest", userRightName: "frontend.dashboard.assignments.view" },
];

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up (queryInterface, Sequelize) {
await queryInterface.bulkInsert(
"user_right",
assignmentViewRights.map((right) => ({
...right,
createdAt: new Date(),
updatedAt: new Date(),
})),
{}
);

const userRoles = await queryInterface.sequelize.query('SELECT id, name FROM "user_role"', {
type: queryInterface.sequelize.QueryTypes.SELECT,
});

const roleNameIdMapping = userRoles.reduce((acc, role) => {
acc[role.name] = role.id;
return acc;
}, {});

await queryInterface.bulkInsert(
"role_right_matching",
roleRights
.filter((right) => roleNameIdMapping[right.role])
.map((right) => ({
userRoleId: roleNameIdMapping[right.role],
userRightName: right.userRightName,
createdAt: new Date(),
updatedAt: new Date(),
})),
{}
);
},

async down (queryInterface, Sequelize) {
await queryInterface.bulkDelete(
"role_right_matching",
{
userRightName: roleRights.map((r) => r.userRightName),
},
{}
);

await queryInterface.bulkDelete(
"user_right",
{
name: assignmentViewRights.map((r) => r.name),
},
{}
);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('assignment', 'assignedRoleIds', {
type: Sequelize.ARRAY(Sequelize.INTEGER),
allowNull: true,
defaultValue: [],
});

await queryInterface.addColumn('assignment', 'closed', {
type: Sequelize.DATE,
allowNull: true,
defaultValue: null,
});
},

async down(queryInterface, Sequelize) {
await queryInterface.removeColumn('assignment', 'closed');
await queryInterface.removeColumn('assignment', 'assignedRoleIds');
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn('document', 'studyUsageCount', {
type: Sequelize.INTEGER,
allowNull: false,
defaultValue: 0,
});
},

async down(queryInterface, Sequelize) {
await queryInterface.removeColumn('document', 'studyUsageCount');
},
};
Loading
Loading