Export a zipped file of the translations on a project
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
const express = require('express')
|
||||
const XL = require('excel4node');
|
||||
const XLSX = require("xlsx");
|
||||
const markdownProducer = require("../../../lib/markdown-producer")
|
||||
const fs = require("fs")
|
||||
const archiver = require('archiver');
|
||||
const path = require("path")
|
||||
const os = require("os")
|
||||
|
||||
module.exports = function (options) {
|
||||
const db = options.db;
|
||||
@@ -1181,7 +1186,7 @@ module.exports = function (options) {
|
||||
{
|
||||
association: db.TranslationChapter.Chapter,
|
||||
},
|
||||
request.query.sheets ? {
|
||||
request.query.sheets || request.query.markdown ? {
|
||||
association: db.TranslationChapter.TranslationChunks,
|
||||
include: [
|
||||
{
|
||||
@@ -1366,6 +1371,98 @@ module.exports = function (options) {
|
||||
} else {
|
||||
response.redirect(`/projects/${request.params.id}`)
|
||||
}
|
||||
} else if (request.query.markdown) {
|
||||
// Create temporary directory for export...
|
||||
const directoryNamePrefix = `${project.name}-`
|
||||
|
||||
const tempExportDirectory = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), directoryNamePrefix)
|
||||
)
|
||||
|
||||
console.log("Saving to: ", tempExportDirectory)
|
||||
|
||||
translationChapters.forEach((translationChapter, index) => {
|
||||
// Save to files to temp directory...
|
||||
const translationChapterTextContent = markdownProducer.produceMarkdownString(translationChapter)
|
||||
|
||||
// TODO: Create artifact sub directory...
|
||||
|
||||
// Create lang sub directory...
|
||||
const dialectDirectoryPath = path.join(
|
||||
tempExportDirectory,
|
||||
translationChapter.translationArtifactVersion.dialect.name
|
||||
)
|
||||
|
||||
if (!fs.existsSync(dialectDirectoryPath)) {
|
||||
fs.mkdirSync(
|
||||
dialectDirectoryPath
|
||||
)
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(dialectDirectoryPath, `${translationChapter.chapter.name}${translationChapter.chapter.name.endsWith(".md") ? "" : ".md"}`),
|
||||
translationChapterTextContent
|
||||
)
|
||||
});
|
||||
|
||||
// Zip tempExportDirectory and download the zipped file...
|
||||
const tempZipDirectory = fs.mkdtempSync(
|
||||
path.join(os.tmpdir(), directoryNamePrefix)
|
||||
)
|
||||
|
||||
const zippedFilePath = path.join(tempZipDirectory, `${project.name}.zip`)
|
||||
const zippedFile = fs.createWriteStream(
|
||||
zippedFilePath
|
||||
);
|
||||
const archive = archiver('zip', {
|
||||
zlib: { level: 9 } // Sets the compression level.
|
||||
});
|
||||
|
||||
// listen for all archive data to be written
|
||||
// 'close' event is fired only when a file descriptor is involved
|
||||
zippedFile.on('close', function() {
|
||||
console.log(archive.pointer() + ' total bytes');
|
||||
console.log('archiver has been finalized and the zippedFile file descriptor has closed.');
|
||||
response.set('Content-Disposition', `attachment; filename="${project.name}.zip"`);
|
||||
response.sendFile(
|
||||
zippedFilePath
|
||||
)
|
||||
});
|
||||
|
||||
// This event is fired when the data source is drained no matter what was the data source.
|
||||
// It is not part of this library but rather from the NodeJS Stream API.
|
||||
// @see: https://nodejs.org/api/stream.html#stream_event_end
|
||||
zippedFile.on('end', function() {
|
||||
console.log('Data has been drained');
|
||||
|
||||
});
|
||||
|
||||
// good practice to catch warnings (ie stat failures and other non-blocking errors)
|
||||
archive.on('warning', function(err) {
|
||||
console.log("Waring: ", err)
|
||||
if (err.code === 'ENOENT') {
|
||||
// log warning
|
||||
} else {
|
||||
// throw error
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
// good practice to catch this error explicitly
|
||||
archive.on('error', function(err) {
|
||||
console.log("Error: ", err)
|
||||
throw err;
|
||||
});
|
||||
|
||||
// pipe archive data to the file
|
||||
archive.pipe(zippedFile);
|
||||
|
||||
// append files from a sub-directory, putting its contents at the root of archive
|
||||
archive.directory(tempExportDirectory, false);
|
||||
|
||||
// finalize the archive (ie we are done appending files but streams have to finish yet)
|
||||
// 'close', 'end' or 'finish' may be fired right after calling this method so register to them beforehand
|
||||
archive.finalize();
|
||||
} else {
|
||||
response.display("project-tracker", {
|
||||
user: request.user,
|
||||
|
||||
@@ -1,41 +1,12 @@
|
||||
const express = require('express')
|
||||
const { Op } = require("sequelize")
|
||||
const idGenerator = require('mantra-db-models/src/lib/id-generator');
|
||||
const Token = require('markdown-it/lib/token');
|
||||
const md = require("markdown-it")().disable(['link'])
|
||||
const markdownProducer = require("../../../lib/markdown-producer")
|
||||
|
||||
module.exports = function (options) {
|
||||
const db = options.db;
|
||||
var router = express.Router();
|
||||
|
||||
const produceTranslatedChapterOutput = (translationChapter) => {
|
||||
return md.parse(
|
||||
translationChapter.chapter.originalText
|
||||
).map(token => {
|
||||
if (token.content) {
|
||||
const translationChunk = translationChapter.translationChunks.find(tc => {
|
||||
return tc.text == token.content
|
||||
})
|
||||
|
||||
if (translationChunk) {
|
||||
token.content = translationChunk.translation.text
|
||||
if (token.children.length == 1) {
|
||||
token.children[0].content = translationChunk.translation.text
|
||||
} else {
|
||||
const newChild = new Token(
|
||||
"text"
|
||||
)
|
||||
newChild.content = translationChunk.translation.text
|
||||
token.children = [
|
||||
newChild
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return token
|
||||
})
|
||||
}
|
||||
router.route('/:id')
|
||||
.get(function(request, response, next) {
|
||||
db.TranslationArtifactVersion.findByPk(request.params.id, {
|
||||
@@ -249,16 +220,11 @@ module.exports = function (options) {
|
||||
]
|
||||
}).then(translationChapter => {
|
||||
if (translationChapter) {
|
||||
const tokens = produceTranslatedChapterOutput(translationChapter)
|
||||
// TODO: Make markdown string...
|
||||
|
||||
|
||||
response.display("translate-chapter-view", {
|
||||
user: request.user,
|
||||
pageTitle: `Translate Chapter ${translationChapter.name}`,
|
||||
translatedChapterOutput: md.renderer.render(
|
||||
tokens,
|
||||
{}
|
||||
),
|
||||
translatedChapterOutput: markdownProducer.produceHtml(translationChapter),
|
||||
translationChapter: translationChapter
|
||||
})
|
||||
} else {
|
||||
@@ -306,16 +272,8 @@ module.exports = function (options) {
|
||||
]
|
||||
}).then(translationChapter => {
|
||||
if (translationChapter) {
|
||||
// TODO: Parse markdown...
|
||||
const tokens = produceTranslatedChapterOutput(translationChapter)
|
||||
// TODO: Make markdown string...
|
||||
|
||||
const translatedChapterContent = md.renderer.render(
|
||||
tokens,
|
||||
{}
|
||||
)
|
||||
response.contentType = "text/plain"
|
||||
response.send(translatedChapterContent)
|
||||
response.send(markdownProducer.produceHtml(translationChapter))
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
@@ -361,54 +319,8 @@ module.exports = function (options) {
|
||||
]
|
||||
}).then(translationChapter => {
|
||||
if (translationChapter) {
|
||||
// TODO: Parse markdown...
|
||||
const tokens = produceTranslatedChapterOutput(translationChapter)
|
||||
// TODO: Make markdown string...
|
||||
// collect tokens...
|
||||
response.contentType = "text/plain"
|
||||
response.send(tokens.reduce(
|
||||
(accumulator, token) => {
|
||||
if (token.type == "list_item_close") {
|
||||
return accumulator
|
||||
}
|
||||
|
||||
if (token.type == "ordered_list_open" || token.type == "bullet_list_open") {
|
||||
return accumulator
|
||||
}
|
||||
|
||||
if (token.type == "bullet_list_close" || token.type == "ordered_list_close") {
|
||||
return accumulator + "\n"
|
||||
}
|
||||
|
||||
if (token.type == "paragraph_close") {
|
||||
if (token.level == 0) {
|
||||
return accumulator + "\n\n"
|
||||
} else {
|
||||
return accumulator + "\n"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (token.type == "list_item_open") {
|
||||
return accumulator + `${token.info}${token.markup} `
|
||||
}
|
||||
|
||||
if (token.type == "inline") {
|
||||
return accumulator + token.content
|
||||
}
|
||||
|
||||
if (token.type == "heading_open") {
|
||||
return `${accumulator}${token.markup} `
|
||||
}
|
||||
|
||||
if (token.type == "heading_close") {
|
||||
return `${accumulator}\n`
|
||||
}
|
||||
|
||||
return accumulator + token.markup
|
||||
},
|
||||
""
|
||||
))
|
||||
response.send(markdownProducer.produceMarkdownString(tokens))
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
||||
@@ -32,6 +32,18 @@ block content
|
||||
button.btn.black(type="submit")
|
||||
i.material-icons.left file_download
|
||||
span export to spreadsheets
|
||||
|
||||
.row
|
||||
.col.s12
|
||||
form(action=urlEndpoint, method="get")
|
||||
input(type="hidden", name="markdown", value="true")
|
||||
each val, key in query
|
||||
if key != "markdown"
|
||||
input(type="hidden", name=key, value=val)
|
||||
button.btn.black(type="submit")
|
||||
i.material-icons.left file_download
|
||||
span export translations to markdown
|
||||
|
||||
.row
|
||||
.col.s12
|
||||
a.btn.black(href=`/projects/${project.id}/import`)
|
||||
|
||||
Reference in New Issue
Block a user