Skip to content

Feat 151 Document-based export - #196

Open
melolw wants to merge 11 commits into
devfrom
feat-68-extract_submissions-151-extract_reviews
Open

Feat 151 Document-based export#196
melolw wants to merge 11 commits into
devfrom
feat-68-extract_submissions-151-extract_reviews

Conversation

@melolw

@melolw melolw commented May 5, 2026

Copy link
Copy Markdown
Collaborator

This feature adds a way to export all (for now, filtering will be possible later) of the documents part of a project with the relevant files using the streaming download introduced with Feat #68 .

The exported ZIP only contains documents owned by users that did agree to data sharing, it follows this structure:

{exportName}/
{docHash}/
document_data.json (all types)
document.pdf (type 0 - PDF)
annotations.json (type 0 - PDF)
comments.json (type 0 - PDF, includes votes)
edits.json (type 1/2 - HTML/Modal)
html.html (type 1/2 - HTML/Modal)
text.txt (type 1/2 - HTML/Modal)
document.zip (type 4 - ZIP)

In the frontend, "export documents" was added, with a simple next step stating that all documents will be downloaded.
In the backend, a new case was added in the export route to handle documents.

@melolw
melolw marked this pull request as ready for review May 25, 2026 21:34

@dennis-zyska dennis-zyska left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments, there are also merge conflicts

Comment thread backend/webserver/routes/export.js Outdated
userIds = [];
}

try {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only used for the function processDocumentBasedExport? Can we move it into that function to make the code cleaner?

* @param {object|array} deltaOrOps - Quill Delta ({ ops: [...] }) or ops array
* @returns {string} A full HTML document string
*/
function deltaToHtml(deltaOrOps) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is an old package, but did you see that: https://www.npmjs.com/package/quill-delta-to-html

Comment thread backend/webserver/routes/export.js Outdated
Comment thread backend/webserver/routes/export.js
Comment thread backend/webserver/routes/export.js
Comment thread frontend/src/components/dashboard/projects/export/StepConfirmDownload.vue Outdated
<li v-for="row in submissionSelection" :key="row.userId">
{{ row.studentName || row.userName }} ({{ row.fileCount }} files)
<li v-for="row in userSelection" :key="row.userId">
{{ row.studentName || row.userName }} ({{ row.count }} {{ exportType === 'submissions' ? 'submission(s)' : 'document(s)' }})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see we changed row.fileCount to row.count here. Did we also update the data payload coming from the parent components for the existing submissions export? i couldn't confirm on my side.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind, it is updated. Sorry!

},
watch: {
hasEditorTypes(val) {
if (!val) this.$emit('update:excludeNonConsentingEdits', false);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if I am misreading this but resetting this to false means if a user unchecks a document type, their privacy preference is wiped and defaults back to allowing non-consenting data. Maybe we should just hide the UI block instead of resetting the underlying variable...?

const exportableTypes = [0, 1, 2, 4];
const exportDocs = this.documents.filter(doc =>
doc.projectId == this.projectId &&
!doc.parentDocumentId &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are filtering for doc.parentDocumentId && (must have a parent). But the backend export.js fetches root documents where parentDocumentId is null. Should this be !doc.parentDocumentId so we don't accidentally filter out all the root documents and show 0 for the users?

: (deltaOrOps.ops || []);

const converter = new QuillDeltaToHtmlConverter(ops, {});
const body = converter = converter.convert();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since converter is a const, I think that reassignment is going to crash the export. I think it should just be const body = converter.convert(); perhaps

@melolw
melolw force-pushed the feat-68-extract_submissions-151-extract_reviews branch from 8c32e42 to e5d9d1f Compare June 22, 2026 17:19
melolw and others added 10 commits June 26, 2026 18:20
All five conflicts resolved in favour of dev, which had refactored past the
branch's versions of this code:

- backend/webserver/Socket.js: ours computed rowVisibilityConditions /
  fullRowAccess but never applied them and had no column restriction; dev
  completes the logic. Took dev.
- backend/webserver/sockets/document.js: import paths only — dev moved
  utils/{generic,documentTemplateHelper} into utils/helper/ and added
  getEmailContent. Took dev's paths.
- frontend Study.vue: dev's AssignmentModal is a rename/refactor of the
  branch's BulkAssignmentModal + SingleAssignmentModal (8162ffd), and dev
  deleted both. The merged script section already imports AssignmentModal
  and defines modals.assignment, so the template hunk follows dev.
- frontend Users.vue + users/RoleManagementModal.vue: the branch deleted
  the component but left a dangling $refs.roleManagementModal call. Restored
  dev's component and re-added the import and registration that the clean
  merge had dropped along with ours' deletion.

Verified: no conflict markers remain, node --check passes on both backend
files, eslint passes on all three Vue files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants