mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 16:13:42 -05:00
108 lines
2.9 KiB
JavaScript
108 lines
2.9 KiB
JavaScript
|
/**
|
||
|
* Compress changes for projects that have too many text operations.
|
||
|
*
|
||
|
* Usage:
|
||
|
*
|
||
|
* node tasks/compress_changes.js CSV_FILE
|
||
|
*
|
||
|
* where CSV_FILE contains a list of project ids in the first column
|
||
|
*/
|
||
|
|
||
|
const fs = require('fs')
|
||
|
const BPromise = require('bluebird')
|
||
|
const { History } = require('overleaf-editor-core')
|
||
|
const { historyStore, chunkStore } = require('..')
|
||
|
|
||
|
const CONCURRENCY = 10
|
||
|
|
||
|
async function main() {
|
||
|
const filename = process.argv[2]
|
||
|
const projectIds = await readCsv(filename)
|
||
|
const chunks = []
|
||
|
for (const projectId of projectIds) {
|
||
|
const chunkIds = await chunkStore.getProjectChunkIds(projectId)
|
||
|
chunks.push(...chunkIds.map(id => ({ id, projectId })))
|
||
|
}
|
||
|
let totalCompressed = 0
|
||
|
await BPromise.map(
|
||
|
chunks,
|
||
|
async chunk => {
|
||
|
try {
|
||
|
const history = await getHistory(chunk)
|
||
|
const numCompressed = compressChanges(history)
|
||
|
if (numCompressed > 0) {
|
||
|
await storeHistory(chunk, history)
|
||
|
console.log(
|
||
|
`Compressed project ${chunk.projectId}, chunk ${chunk.id}`
|
||
|
)
|
||
|
}
|
||
|
totalCompressed += numCompressed
|
||
|
} catch (err) {
|
||
|
console.log(err)
|
||
|
}
|
||
|
},
|
||
|
{ concurrency: CONCURRENCY }
|
||
|
)
|
||
|
console.log('CHANGES:', totalCompressed)
|
||
|
}
|
||
|
|
||
|
async function readCsv(filename) {
|
||
|
const csv = await fs.promises.readFile(filename, 'utf-8')
|
||
|
const lines = csv.trim().split('\n')
|
||
|
const projectIds = lines.map(line => line.split(',')[0])
|
||
|
return projectIds
|
||
|
}
|
||
|
|
||
|
async function getHistory(chunk) {
|
||
|
const rawHistory = await historyStore.loadRaw(chunk.projectId, chunk.id)
|
||
|
const history = History.fromRaw(rawHistory)
|
||
|
return history
|
||
|
}
|
||
|
|
||
|
async function storeHistory(chunk, history) {
|
||
|
const rawHistory = history.toRaw()
|
||
|
await historyStore.storeRaw(chunk.projectId, chunk.id, rawHistory)
|
||
|
}
|
||
|
|
||
|
function compressChanges(history) {
|
||
|
let numCompressed = 0
|
||
|
for (const change of history.getChanges()) {
|
||
|
const newOperations = compressOperations(change.operations)
|
||
|
if (newOperations.length !== change.operations.length) {
|
||
|
numCompressed++
|
||
|
}
|
||
|
change.setOperations(newOperations)
|
||
|
}
|
||
|
return numCompressed
|
||
|
}
|
||
|
|
||
|
function compressOperations(operations) {
|
||
|
if (!operations.length) return []
|
||
|
|
||
|
const newOperations = []
|
||
|
let currentOperation = operations[0]
|
||
|
for (let operationId = 1; operationId < operations.length; operationId++) {
|
||
|
const nextOperation = operations[operationId]
|
||
|
if (currentOperation.canBeComposedWith(nextOperation)) {
|
||
|
currentOperation = currentOperation.compose(nextOperation)
|
||
|
} else {
|
||
|
// currentOperation and nextOperation cannot be composed. Push the
|
||
|
// currentOperation and start over with nextOperation.
|
||
|
newOperations.push(currentOperation)
|
||
|
currentOperation = nextOperation
|
||
|
}
|
||
|
}
|
||
|
newOperations.push(currentOperation)
|
||
|
|
||
|
return newOperations
|
||
|
}
|
||
|
|
||
|
main()
|
||
|
.then(() => {
|
||
|
process.exit()
|
||
|
})
|
||
|
.catch(err => {
|
||
|
console.error(err)
|
||
|
process.exit(1)
|
||
|
})
|