Skip to content

Commit

Permalink
Merge pull request #256 from danielBingham/217-preprint-submission-ti…
Browse files Browse the repository at this point in the history
…ed-to-paper-version

Issue #217 - Preprint and Submission are Tied to Version
  • Loading branch information
danielBingham authored Oct 6, 2024
2 parents d3fbe72 + a0e886f commit d10e0fb
Show file tree
Hide file tree
Showing 93 changed files with 2,271 additions and 1,185 deletions.
13 changes: 10 additions & 3 deletions database/initialization-scripts/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -286,16 +286,18 @@ CREATE TABLE papers (
CREATE INDEX papers__title_search_index ON papers USING GIN (searchable_title);

CREATE TABLE paper_versions (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
paper_id bigint REFERENCES papers(id) ON DELETE CASCADE,
version int NOT NULL,
file_id uuid REFERENCES files(id) ON DELETE CASCADE,
is_published boolean DEFAULT false,
is_preprint boolean DEFAULT false,
is_submitted boolean DEFAULT false,
review_count int default 0,
content text NOT NULL,
searchable_content tsvector GENERATED ALWAYS AS (to_tsvector('english', content)) STORED,
created_date timestamptz,
updated_date timestamptz,
PRIMARY KEY (paper_id, version)
updated_date timestamptz
);
CREATE INDEX paper_versions__search_index ON paper_versions USING GIN (searchable_content);
CREATE INDEX paper_versions__file_id_index ON paper_versions (file_id);
Expand All @@ -319,6 +321,7 @@ CREATE TYPE paper_comments_status AS ENUM('in-progress', 'committed', 'edit-in-p
CREATE TABLE paper_comments (
id bigserial PRIMARY KEY,
paper_id bigint REFERENCES papers(id) ON DELETE CASCADE,
paper_version_id uuid REFERENCES paper_versions(id) ON DELETE CASCADE,
paper_version bigint,
user_id bigint REFERENCES users(id) ON DELETE CASCADE,
status paper_comments_status,
Expand All @@ -328,6 +331,7 @@ CREATE TABLE paper_comments (
committed_date timestamptz DEFAULT NULL
);
CREATE INDEX paper_comments__paper_id ON paper_comments (paper_id);
CREATE INDEX paper_comments__paper_version_id ON paper_comments (paper_version_id);
CREATE INDEX paper_comments__user_id ON paper_comments (user_id);

CREATE TABLE paper_comment_versions (
Expand Down Expand Up @@ -450,7 +454,8 @@ CREATE TABLE reviews (
paper_id bigint REFERENCES papers(id) ON DELETE CASCADE,
submission_id bigint REFERENCES journal_submissions(id) DEFAULT null,
user_id bigint REFERENCES users(id) ON DELETE CASCADE,
version int,
paper_version_id uuid REFERENCES paper_versions(id) ON DELETE CASCADE,
version int, /* Deprecated: Old reference to paper_versions. */
number int, /* Number of review on this paper version. 0 - n where n is the number of reviews on this paper specifically. */
summary text,
recommendation review_recommendation,
Expand All @@ -461,6 +466,7 @@ CREATE TABLE reviews (
CREATE INDEX reviews__paper_id ON reviews (paper_id);
CREATE INDEX reviews__user_id ON reviews (user_id);
CREATE INDEX reviews__version ON reviews (version);
CREATE INDEX reviews__version_id ON reviews (version_id);

CREATE TABLE review_comment_threads (
id bigserial PRIMARY KEY,
Expand Down Expand Up @@ -546,6 +552,7 @@ CREATE TYPE paper_event_status AS ENUM(
CREATE TABLE paper_events (
id bigserial PRIMARY KEY,
paper_id bigint REFERENCES papers(id) ON DELETE CASCADE NOT NULL,
paper_version_id uuid REFERENCES paper_versions(id) ON DELETE CASCADE NOT NULL,
actor_id bigint REFERENCES users(id) NOT NULL,
version int NOT NULL,
status paper_event_status DEFAULT 'committed',
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/daos/JournalSubmissionDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ module.exports = class JournalSubmissionDAO {
journal_submission_reviewers.user_id as "reviewer_userId",
journal_submission_reviewers.created_date as "reviewer_assignedDate",
reviews.id as review_id, reviews.version as review_version,
reviews.id as review_id, reviews.paper_version_id as review_paperVersionId,
reviews.recommendation as review_recommendation, reviews.user_id as "review_userId",
journal_submission_editors.submission_id as "editor_submissionId",
Expand Down
8 changes: 4 additions & 4 deletions packages/backend/daos/PaperCommentDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ module.exports = class PaperCommentDAO {
return {
id: row.comment_id,
paperId: row.comment_paperId,
paperVersion: row.comment_paperVersion,
paperVersionId: row.comment_paperVersionId,
userId: row.comment_userId,
status: row.comment_status,
content: row.comment_content,
Expand Down Expand Up @@ -47,7 +47,7 @@ module.exports = class PaperCommentDAO {
SELECT
paper_comments.id as comment_id,
paper_comments.paper_id as "comment_paperId",
paper_comments.paper_version as "comment_paperVersion",
paper_comments.paper_version_id as "comment_paperVersionId",
paper_comments.user_id as "comment_userId",
paper_comments.status as comment_status,
paper_comments.content as comment_content,
Expand All @@ -65,10 +65,10 @@ module.exports = class PaperCommentDAO {

async insertPaperComment(paperComment) {
const results = await this.database.query(`
INSERT INTO paper_comments (paper_id, paper_version, user_id, status, content, created_date, updated_date )
INSERT INTO paper_comments (paper_id, paper_version_id, user_id, status, content, created_date, updated_date )
VALUES ( $1, $2, $3, $4, $5, now(), now())
RETURNING id
`, [ paperComment.paperId, paperComment.paperVersion, paperComment.userId, paperComment.status, paperComment.content ])
`, [ paperComment.paperId, paperComment.paperVersionId, paperComment.userId, paperComment.status, paperComment.content ])

if ( results.rowCount <= 0 ) {
throw new DAOError('insert-failure',
Expand Down
129 changes: 23 additions & 106 deletions packages/backend/daos/PaperDAO.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
const mime = require('mime')
const sanitizeFilename = require('sanitize-filename')
const fs = require('fs')
const pdfjslib = require('pdfjs-dist/legacy/build/pdf.js')
/******************************************************************************
*
* JournalHub -- Universal Scholarly Publishing
* Copyright (C) 2022 - 2024 Daniel Bingham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
******************************************************************************/

const DAOError = require('../errors/DAOError')

const UserDAO = require('./UserDAO')
const FileDAO = require('./FileDAO')
const FieldDAO = require('./FieldDAO')
const S3FileService = require('../services/S3FileService')

const PAGE_SIZE = 50

Expand All @@ -19,9 +32,7 @@ module.exports = class PaperDAO {

this.database = core.database
this.userDAO = new UserDAO(core)
this.fileDAO = new FileDAO(core)
this.fieldDAO = new FieldDAO(core)
this.fileService = new S3FileService(core)
}

/**
Expand All @@ -45,8 +56,7 @@ module.exports = class PaperDAO {
createdDate: row.paper_createdDate,
updatedDate: row.paper_updatedDate,
authors: [],
fields: [],
versions: []
fields: []
}

if ( ! dictionary[paper.id] ) {
Expand All @@ -65,18 +75,6 @@ module.exports = class PaperDAO {
dictionary[paper.id].authors.push(author)
}

const paper_version = {
file: this.fileDAO.hydrateFile(row),
version: row.version_version,
content: row.version_content,
reviewCount: row.version_reviewCount,
createdDate: row.version_createdDate,
updatedDate: row.version_updatedDate
}
// Ignore versions that haven't finished uploading.
if (paper_version.version && ! dictionary[paper.id].versions.find((v) => v.version == paper_version.version)) {
dictionary[paper.id].versions.push(paper_version)
}

if ( row.paperField_fieldId) {
const paper_field = {
Expand Down Expand Up @@ -108,16 +106,12 @@ module.exports = class PaperDAO {

// TECHDEBT - Definitely not the ideal way to handle this. But so it goes.
let activeOrderJoins = ''
if ( order == 'published-active' ) {
activeOrderJoins = `
LEFT OUTER JOIN responses ON responses.paper_id = papers.id
`
order = 'greatest(responses.updated_date, papers.updated_date) DESC NULLS LAST, '
} else if ( order == 'draft-active' ) {
if ( order == 'active' ) {
activeOrderJoins = `
LEFT OUTER JOIN reviews ON reviews.paper_id = papers.id AND reviews.status != 'in-progress'
LEFT OUTER JOIN review_comment_threads ON review_comment_threads.review_id = reviews.id
LEFT OUTER JOIN review_comments ON review_comments.thread_id = review_comment_threads.id AND review_comments.status != 'in-progress'
LEFT OUTER JOIN paper_versions ON paper_versions.paper_id = papers.id
`
order = 'greatest(paper_versions.updated_date, reviews.updated_date, review_comments.updated_date) DESC NULLS LAST, '
} else {
Expand All @@ -134,22 +128,14 @@ module.exports = class PaperDAO {
paper_authors.user_id as "author_userId", paper_authors.author_order as author_order,
paper_authors.owner as author_owner, paper_authors.submitter as author_submitter,
paper_versions.version as version_version,
paper_versions.content as version_content, paper_versions.review_count as "version_reviewCount",
paper_versions.created_date as "version_createdDate", paper_versions.updated_date as "version_updatedDate",
${ this.fileDAO.getFilesSelectionString() },
paper_fields.field_id as "paperField_fieldId"
FROM papers
LEFT OUTER JOIN paper_authors ON papers.id = paper_authors.paper_id
LEFT OUTER JOIN paper_versions ON papers.id = paper_versions.paper_id
LEFT OUTER JOIN files on paper_versions.file_id = files.id
LEFT OUTER JOIN paper_fields ON papers.id = paper_fields.paper_id
${activeOrderJoins}
${where}
ORDER BY ${order}paper_authors.author_order asc, paper_versions.version desc
ORDER BY ${order}paper_authors.author_order asc
`
const results = await this.database.query(sql, params)

Expand Down Expand Up @@ -327,74 +313,6 @@ module.exports = class PaperDAO {
}
}

async insertVersions(paper) {
for(const version of paper.versions ) {
await this.insertVersion(paper, version)
}
}

async insertVersion(paper, version) {
const files = await this.fileDAO.selectFiles('WHERE files.id = $1', [ version.file.id ])
if ( files.length <= 0) {
throw new DAOError('invalid-file', `Invalid file_id posted with paper ${paper.id}.`)
}
const file = files[0]

const maxVersionResults = await this.database.query(
'SELECT MAX(version)+1 as version FROM paper_versions WHERE paper_id=$1',
[ paper.id ]
)
let versionNumber = 1
if ( maxVersionResults.rows.length > 0 && maxVersionResults.rows[0].version) {
versionNumber = maxVersionResults.rows[0].version
}

const url = new URL(file.filepath, file.location)
const pdf = await pdfjslib.getDocument(url.toString()).promise
let content = ''
for(let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber++) {
const page = await pdf.getPage(pageNumber)
const textContent = await page.getTextContent()

for(const item of textContent.items ) {
content += item.str
}
}

content = content.replace(/\0/g, '')

if ( content.trim().length == 0 ) {
console.log('Empty PDF!')
}

const versionResults = await this.database.query(`
INSERT INTO paper_versions (paper_id, version, file_id, content, created_date, updated_date)
VALUES ($1, $2, $3, $4, now(), now())
`, [ paper.id, versionNumber, file.id, content ])

if ( versionResults.rowCount <= 0) {
throw new DAOError('failed-insertion', `Failed to insert version for paper ${paper.id} and file ${file.id}.`)
}

const title = paper.title
let titleFilename = title.replaceAll(/\s/g, '-')
titleFilename = titleFilename.toLowerCase()
titleFilename = sanitizeFilename(titleFilename)

const filename = `${paper.id}-${versionNumber}-${titleFilename}.${mime.getExtension(file.type)}`
const filepath = 'papers/' + filename

const newFile = { ...file }
newFile.filepath = filepath

// TODO Should moving the file when the file is updated be the
// responsibility of the FileDAO? Probably.
await this.fileService.copyFile(file.filepath, filepath)
await this.fileDAO.updateFile(newFile)
await this.fileService.removeFile(file.filepath)
}


async updatePartialPaper(paper) {
// We'll ignore these fields when assembling the patch SQL. These are
// fields that either need more processing (authors) or that we let the
Expand Down Expand Up @@ -429,7 +347,6 @@ module.exports = class PaperDAO {
if ( results.rowCount <= 0 ) {
throw new DAOError('update-failure', `Failed to update paper ${paper.id} with partial update.`)
}

}

async deletePaper(id) {
Expand Down
8 changes: 5 additions & 3 deletions packages/backend/daos/PaperEventDAO.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = class PaperEventsDAO {
id: row.event_id,
paperId: row.event_paperId,
actorId: row.event_actorId,
version: row.event_version,
paperVersionId: row.event_paperVersionId,
status: row.event_status,
type: row.event_type,
visibility: row.event_visibility,
Expand Down Expand Up @@ -58,7 +58,7 @@ module.exports = class PaperEventsDAO {
paper_events.id as event_id,
paper_events.paper_id as "event_paperId",
paper_events.actor_id as "event_actorId",
paper_events.version as event_version,
paper_events.paper_version_id as "event_paperVersionId",
paper_events.status as event_status,
paper_events.type as event_type,
paper_events.visibility::text[] as event_visibility,
Expand Down Expand Up @@ -90,7 +90,7 @@ module.exports = class PaperEventsDAO {
let params = []

const validFields = [
'paperId', 'actorId', 'version', 'status', 'type',
'paperId', 'actorId', 'paperVersionId', 'status', 'type',
'visibility', 'eventDate', 'assigneeId', 'reviewId',
'reviewCommentId', 'submissionId', 'newStatus', 'paperCommentId'
]
Expand All @@ -103,6 +103,8 @@ module.exports = class PaperEventsDAO {

if ( key == 'paperId' ) {
columns += `paper_id, `
} else if ( key == 'paperVersionId' ) {
columns += `paper_version_id, `
} else if ( key == 'actorId' ) {
columns += `actor_id, `
} else if ( key == 'assigneeId' ) {
Expand Down
Loading

0 comments on commit d10e0fb

Please sign in to comment.