diff --git a/README.md b/README.md index 729defb..5476cd6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ # Welcome to Outbreak Science Rapid PREreview! -## What is it? +## What is it? Outbreak Science Rapid PREreview is an **open source platform and a web extension to facilitate the rapid assessment of preprints during public health crises**. The platform allows any researcher with an ORCID iD to provide a quick high-level evaluation of preprints via a series of questions to assess the originality and soundness of the research findings. Aggregated data from these reviews is visualized to allow readers to identify the most relevant information. This tool has the capacity to be transformative for on-the-ground health workers, researchers, public health agencies, and the public, as it can quickly unlock key scientific information during an outbreak of infectious diseases. ## Our team -Outbreak Science Rapid PREreview is a project born from the collaboration of PREreview and Outbreak Science. +Outbreak Science Rapid PREreview is a project born from the collaboration of PREreview and Outbreak Science. -[PREreview](https://v2.prereview.org) is an open project fiscally sponsored by the non-profit organization Code for Science & Society. PREreview's mission is to increase diversity in the scholarly peer review process by empowering all researchers to engage with preprint reviews. +[PREreview](https://v2.prereview.org) is an open project fiscally sponsored by the non-profit organization Code for Science & Society. PREreview's mission is to increase diversity in the scholarly peer review process by empowering all researchers to engage with preprint reviews. [Outbreak Science](https://outbreasci.org) is a non-profit organization aimed at advancing the science of outbreak response, in particular by supporting early and open dissemination of data, code, and analytical results. @@ -220,3 +220,5 @@ will source the production environment variables. To reset all redis data (including sessions) run: `npm run azure:reset-redis`. Be aware that this will source the production environment variables. + +Some basic info about the service health can be found at https://rapid-prereview-service.azurewebsites.net/ diff --git a/src/dev-server.js b/src/dev-server.js index 253d784..cca488f 100644 --- a/src/dev-server.js +++ b/src/dev-server.js @@ -26,6 +26,7 @@ const config = { disableSsr: true }; +const redisClient = createRedisClient(config); const db = new DB(config); const feed = new Feed(db); feed.resume(); @@ -34,13 +35,22 @@ feed.on('start', seq => { }); feed.on('sync', (seq, preprint) => { logger.info({ seq, id: preprint._id, rev: preprint._rev }, 'Feed synced'); + + redisClient + .batch() + .del(createCacheKey('home:score')) + .del(createCacheKey('home:new')) + .del(createCacheKey('home:date')) + .exec(err => { + if (err) { + logger.error({ err }, 'Error invalidating cache on sync'); + } + }); }); feed.on('error', err => { logger.error({ err }, 'Feed error'); }); -const redisClient = createRedisClient(config); - const intervalId = setIntervalAsync( () => { return db.updateScores().then(updatedDocs => { diff --git a/src/middlewares/cache.js b/src/middlewares/cache.js index bb57af9..a4c25e0 100644 --- a/src/middlewares/cache.js +++ b/src/middlewares/cache.js @@ -176,7 +176,7 @@ export function invalidate() { case 'GrantModeratorRoleAction': case 'RevokeModeratorRoleAction': case 'ModerateRoleAction': { - // invalidate cacheKey for result + // Invalidate cacheKey for result const doc = action.result; const batch = redis.batch(); batch.set( @@ -207,9 +207,11 @@ export function invalidate() { action = action.result; } - // invalidate cache key for actionId, activity: (for profile activity log), home:score, + // Invalidate cache key for actionId, activity: (for profile activity log), home:score, // home:new, home:date (for home page with the various sort option) // and preprintId (`action.object`) containing `action` + // Note: `home:*` keys are also invalidated in `service.js` in response to the + // score update async interval and the changes feed sync const batch = redis.batch(); batch.del(createCacheKey(action)); diff --git a/src/service.js b/src/service.js index aa6f61e..5925f9e 100644 --- a/src/service.js +++ b/src/service.js @@ -15,6 +15,7 @@ import { createCacheKey } from './middlewares/cache'; let lastSeq = null; let lastChangeErr = null; let lastDateScoreUpdated = null; +let lastDateSynced = null; let lastScoreErr = null; const logger = pino({ logLevel: 'info' }); @@ -25,6 +26,7 @@ const config = { disableSsr: true }; +const redisClient = createRedisClient(config); const db = new DB(config); const feed = new Feed(db); feed.resume(); @@ -38,11 +40,20 @@ feed.on('start', seq => { }); feed.on('sync', (seq, preprint) => { lastSeq = seq; + lastDateSynced = new Date().toISOString(); logger.info({ seq, id: preprint._id, rev: preprint._rev }, 'Feed synced'); + redisClient + .batch() + .del(createCacheKey('home:score')) + .del(createCacheKey('home:new')) + .del(createCacheKey('home:date')) + .exec(err => { + if (err) { + logger.error({ err }, 'Error invalidating cache on sync'); + } + }); }); -const redisClient = createRedisClient(config); - const intervalId = setIntervalAsync( () => { lastDateScoreUpdated = new Date().toISOString(); @@ -71,7 +82,13 @@ const intervalId = setIntervalAsync( const app = express(); app.use(helmet()); app.get('/', (req, res, next) => { - res.json({ lastSeq, lastChangeErr, lastScoreErr, lastDateScoreUpdated }); + res.json({ + lastSeq, + lastChangeErr, + lastScoreErr, + lastDateScoreUpdated, + lastDateSynced + }); }); const server = http.createServer(app); diff --git a/src/utils/stats.js b/src/utils/stats.js index 0824eca..a43d721 100644 --- a/src/utils/stats.js +++ b/src/utils/stats.js @@ -184,17 +184,21 @@ export function getTextAnswers(actions = []) { return QUESTIONS.filter(({ type }) => { return type === 'Question'; - }).map(({ question, identifier }) => { + }).map(({ question, identifier, required }) => { return { questionId: `question:${identifier}`, question, - answers: answersData.map(({ actionId, roleId, answerMap }) => { - return { - actionId, - roleId, - text: answerMap[identifier] - }; - }) + answers: answersData + .map(({ actionId, roleId, answerMap }) => { + return { + actionId, + roleId, + text: answerMap[identifier] + }; + }) + .filter(({ text }) => { + return required || text !== undefined; + }) }; }); }