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; var router = express.Router(); router.route('/') .get(function(request, response, next) { db.Project.findAll({ include: [ { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }) .then(projects => { response.display("projects", { user: request.user, pageTitle: "Projects - Mantra", projects: projects }) }) }) router.route('/:id/overview') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.ProjectArtifactVersions, include: [ { association: db.ProjectArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] }, { association: db.Project.ProjectTranslationArtifactVersions, include: [ { association: db.ProjectTranslationArtifactVersion.TranslationArtifactVersion, include: [ { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] } ] }, { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(project => { if (project) { response.display("project", { user: request.user, pageTitle: "Projects - Mantra", project: project }) } else { next() } }).catch(error => { next(error) }) }) router.route('/create') .get(function(request, response, next) { response.display("project-form", { user: request.user, pageTitle: "Projects - Mantra", project: { } }) }) .post(function(request, response, next) { db.Project.create({ creatorId: request.user.id, name: request.body.name, description: request.body.description, owner: { ownerEntities: [ { entityId: request.user.individualEntityUser.entityUser.entityId, } ] } }, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities } ] } ] }).then(project => { if (project) { response.redirect(`/projects/${project.id}`) } else { next() } }).catch(error => { next(error) }) }) router.route('/:id/add-artifact') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.ProjectArtifactVersions }, { association: db.Project.ProjectTranslationArtifactVersions }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { const artifacts = await db.Artifact.findAll({ // Narrow it down to artifacts this user has admin ownership off..., include: [ { association: db.Artifact.ArtifactVersions, limit: 1, required: true // TODO: Order by version... } ] }) response.display("project-add-artifact", { user: request.user, pageTitle: "Projects - Mantra", project: project, artifacts: artifacts }) } else { next() } }).catch(error => { next(error) }) }) .post(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.ProjectArtifactVersions, where: { artifactVersionId: request.body.artifactVersionId }, required: false, include: [ { association: db.ProjectArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] }, { association: db.Project.ProjectTranslationArtifactVersions, include: [ { association: db.ProjectTranslationArtifactVersion.TranslationArtifactVersion, include: [ { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] } ] }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { // TODO: check if project.artifactVersions.length == 0 const artifactVersion = await db.ArtifactVersion.findByPk(request.body.artifactVersionId, { // Narrow it down to artifacts this user has admin ownership off..., include: [ { association: db.ArtifactVersion.TranslationArtifactVersions } ] }) if (artifactVersion) { const projectArtifactVersion = await getProjectArtifactVersion(request.user.id, project, artifactVersion) // Bulk create the project translations... const projectTranslationArtifactVersionObjects = artifactVersion.translationArtifactVersions.filter(translationArtifactVersion => { // filter out translations already added to the project... return !project.projectTranslationArtifactVersions.some(projectTranslationArtifactVersions => { return projectTranslationArtifactVersions.translationArtifactVersionId == translationArtifactVersion.id }) }).map(translationArtifactVersion => { return { creatorId: request.user.id, projectId: project.id, translationArtifactVersionId: translationArtifactVersion.id, projectArtifactVersionId: projectArtifactVersion.id } }) if (projectTranslationArtifactVersionObjects.length > 0) { const projectTranslationArtifactVersions = await db.ProjectTranslationArtifactVersion.bulkCreate(projectTranslationArtifactVersionObjects) } response.redirect(`/projects/${project.id}`) } } else { next() } }).catch(error => { next(error) }) }) router.route('/:id/add-translation') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.ProjectArtifactVersions }, { association: db.Project.ProjectTranslationArtifactVersions }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { const translationArtifactVersions = await db.TranslationArtifactVersion.findAll({ // Narrow it down to artifacts this user has admin ownership off..., include: [ { association: db.TranslationArtifactVersion.ArtifactVersion, required: true, // TODO: Order by version... include: [ { association: db.ArtifactVersion.Artifact } ] } ] }) response.display("project-add-translation", { user: request.user, pageTitle: "Projects - Mantra", project: project, translationArtifactVersions: translationArtifactVersions }) } else { next() } }).catch(error => { next(error) }) }) .post(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.ProjectArtifactVersions }, { association: db.Project.ProjectTranslationArtifactVersions }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { // TODO: Fi const translationArtifactVersion = await db.TranslationArtifactVersion.findByPk(request.body.translationArtifactVersionId, { // Narrow it down to artifacts this user has admin ownership off..., include: [ { association: db.TranslationArtifactVersion.ArtifactVersion, required: true // TODO: Order by version... } ] }) if (translationArtifactVersion) { const projectTranslationArtifactVersion = await db.ProjectTranslationArtifactVersion.create({ creatorId: request.user.id, projectId: project.id, translationArtifactVersionId: translationArtifactVersion.id }) response.redirect(`/projects/${project.id}`) } else { response.redirect(`/projects/${project.id}/add-translation`) } } else { next() } }).catch(error => { next(error) }) }) router.route('/:id/campaign') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Campaign, required: true }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(project => { if (project) { response.redirect(`/campaigns/${project.campaign.id}`) } else { response.redirect(`/projects/${request.params.id}/campaign/create`) } }) }) router.route('/:id/campaign/create') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Campaign, }, { association: db.Project.ProjectArtifactVersions, include: [ { association: db.ProjectArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] }, { association: db.Project.ProjectTranslationArtifactVersions, include: [ { association: db.ProjectTranslationArtifactVersion.TranslationArtifactVersion, include: [ { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] } ] }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(project => { if (project) { if (project.campaign) { response.redirect(`/campaigns/${project.campaign.id}`) } else { response.display("campaign-form", { user: request.user, pageTitle: "Campaign - Mantra", campaign: { name: `Campaign for ${project.name} project` }, project: project, }) } } else { next() } }).catch(error => { next(error) }) }) .post(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Campaign, }, { association: db.Project.ProjectArtifactVersions, include: [ { association: db.ProjectArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] }, { association: db.Project.ProjectTranslationArtifactVersions, include: [ { association: db.ProjectTranslationArtifactVersion.TranslationArtifactVersion, include: [ { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact } ] } ] } ] }, { association: db.Project.Owner, required: true, include: [ { association: db.Owner.OwnerEntities, required: true, include: [ { association: db.OwnerEntity.Entity, required: true, include: [ { association: db.Entity.EntityUsers, required: true, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { if (project.campaign) { response.redirect(`/campaigns/${project.campaign.id}`) } else { const translationArtifactVersionCampaigns = project.projectTranslationArtifactVersions.map(projectTranslationArtifactVersion => { return { creatorId: request.user.id, translationArtifactVersionId: projectTranslationArtifactVersion.translationArtifactVersionId, owner: { ownerEntities: [ { entityId: request.user.individualEntityUser.entityUser.entityId, } ] }, satoshis: request.body.satoshis } }) const campaign = await db.Campaign.create({ creatorId: request.user.id, name: request.body.name, description: request.body.description, // defaultSatoshis: [request.body.satoshis].flat()[0], defaultSatoshis: request.body.satoshis, owner: { ownerEntities: [ { entityId: request.user.individualEntityUser.entityUser.entityId, } ] }, translationArtifactVersionCampaigns: translationArtifactVersionCampaigns }, { include: [ { association: db.Campaign.TranslationArtifactVersionCampaigns, include: [ { association: db.TranslationArtifactVersionCampaign.Owner, include: [ { association: db.Owner.OwnerEntities } ] } ] }, { association: db.Campaign.Owner, include: [ { association: db.Owner.OwnerEntities } ] } ] }) response.redirect(`/campaigns/${campaign.id}`) } } else { next() } }).catch(error => { next(error) }) }) router.route('/:id/spreadsheet') .post(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { db.TranslationArtifactVersion.findAll({ include: [ // TODO: require translationArtifactVersions in ProjectArtifactVersion { association: db.TranslationArtifactVersion.ProjectTranslationArtifactVersions, required: true, where: { projectId: request.params.id }, }, { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.ProjectArtifactVersions }, { association: db.ArtifactVersion.Artifact, include: [ { association: db.Artifact.Dialect } ] } ] }, { association: db.TranslationArtifactVersion.Dialect }, { association: db.TranslationArtifactVersion.TranslationChapters, include: [ { association: db.TranslationChapter.TranslationChunks, include: [ { association: db.TranslationChunk.Translation } ] } ] } ] }).then(translationArtifactVersions => { const workbook = new XL.Workbook(); const cellWidth = 45 const cellHeight = 25 translationArtifactVersions.forEach((translationArtifactVersion, index) => { const worksheet_name = `${index+1}-${translationArtifactVersion.dialect.countryId}-${translationArtifactVersion.dialect.languageId} - ${translationArtifactVersion.artifactVersion.artifact.name}`.substring(0, 30); // Don't have [ ] * ? : / \ const worksheet = workbook.addWorksheet(worksheet_name.replace(/[`*?:'\[\]\\\/]/gi, "")) worksheet.cell(2,2,2,3,true) .string(translationArtifactVersion.artifactVersion.artifact.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(4,2) .string(translationArtifactVersion.artifactVersion.artifact.dialect.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(4,3) .string(translationArtifactVersion.dialect.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) // Meta data for the translation and artifact worksheet.cell(4,4) .string(translationArtifactVersion.artifactVersion.id) worksheet.cell(4,5) .string(translationArtifactVersion.id) var cellIndex = 6 const translationChapters = translationArtifactVersion.translationChapters.sort((a, b) => a.index - b.index) for (const i in translationChapters) { const translationChapter = translationChapters[i] const translationChunks = translationChapter.translationChunks.sort((a,b) => a.index - b.index) for (const j in translationChunks) { const translationChunk = translationChunks[j] worksheet.cell(cellIndex,2) .string(translationChunk.text) .style({ alignment: { wrapText: true, vertical: 'justify' } }) const translationCell = worksheet.cell(cellIndex,3) .style({ alignment: { wrapText: true, vertical: 'justify' } }) if (translationChunk.translation) { translationCell.string(translationChunk.translation.text) } const textLength = translationChunk.text.length worksheet.row(cellIndex).setHeight( Math.ceil(Math.max(textLength/cellWidth, 1)*cellHeight) ) // Meta data... // ID for translationChunk worksheet.cell(cellIndex,4) .string(translationChunk.id) // ID for translation worksheet.cell(cellIndex,5) .string(translationChunk.translation?.id ?? "") cellIndex++ } cellIndex++ } // Format Columns worksheet.column(2).setWidth(cellWidth); worksheet.column(3).setWidth(cellWidth); worksheet.column(4).hide(); worksheet.column(5).hide(); // Format Rows... worksheet.row(5).freeze() worksheet.row(1).setHeight(5) worksheet.row(3).setHeight(5) worksheet.row(5).setHeight(5) }) if (translationArtifactVersions.length > 0) { workbook.write(`${project.name}.xlsx`, response); } else { response.redirect(`/projects/${request.params.id}`) } }).catch(error => { next(error) }) } else { next() } }) }) router.route('/:id/sheets') .post(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { db.TranslationChapter.findAll({ include: [ { association: db.TranslationChapter.TranslationChunks, include: [ { association: db.TranslationChunk.Translation } ] }, { association: db.TranslationChapter.TranslationArtifactVersion, required: true, include: [ { association: db.TranslationArtifactVersion.ProjectTranslationArtifactVersions, required: true, where: { projectId: request.params.id }, }, { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.ProjectArtifactVersions }, { association: db.ArtifactVersion.Artifact, include: [ { association: db.Artifact.Dialect } ] } ] }, { association: db.TranslationArtifactVersion.Dialect }, ] } ] }).then(translationChapters => { const workbook = new XL.Workbook(); const cellWidth = 45 const cellHeight = 25 translationChapters.sort((a, b) => a.index - b.index).forEach((translationChapter, index) => { const worksheet_name = `${index+1}-${translationChapter.translationArtifactVersion.dialect.countryId}-${translationChapter.translationArtifactVersion.dialect.languageId} - ${translationChapter.translationArtifactVersion.artifactVersion.artifact.name}`.substring(0, 30); // Don't have [ ] * ? : / \ const worksheet = workbook.addWorksheet(worksheet_name.replace(/[`*?:'\[\]\\\/]/gi, "")) worksheet.cell(2,2,2,3,true) .string(translationChapter.translationArtifactVersion.artifactVersion.artifact.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(4,2) .string(translationChapter.translationArtifactVersion.artifactVersion.artifact.dialect.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(4,3) .string(translationChapter.translationArtifactVersion.dialect.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) // Meta data for the translation and artifact worksheet.cell(4,4) .string(translationChapter.translationArtifactVersion.artifactVersion.id) worksheet.cell(4,5) .string(translationChapter.translationArtifactVersion.id) var cellIndex = 6 const translationChunks = translationChapter.translationChunks.sort((a,b) => a.index - b.index) for (const j in translationChunks) { const translationChunk = translationChunks[j] worksheet.cell(cellIndex,2) .string(translationChunk.text) .style({ alignment: { wrapText: true, vertical: 'justify' } }) const translationCell = worksheet.cell(cellIndex,3) .style({ alignment: { wrapText: true, vertical: 'justify' } }) if (translationChunk.translation) { translationCell.string(translationChunk.translation.text) } const textLength = translationChunk.text.length worksheet.row(cellIndex).setHeight( Math.ceil(Math.max(textLength/cellWidth, 1)*cellHeight) ) // Meta data... // ID for translationChunk worksheet.cell(cellIndex,4) .string(translationChunk.id) // ID for translation worksheet.cell(cellIndex,5) .string(translationChunk.translation?.id ?? "") cellIndex++ } // Format Columns worksheet.column(2).setWidth(cellWidth); worksheet.column(3).setWidth(cellWidth); worksheet.column(4).hide(); worksheet.column(5).hide(); // Format Rows... worksheet.row(5).freeze() worksheet.row(1).setHeight(5) worksheet.row(3).setHeight(5) worksheet.row(5).setHeight(5) }) if (translationChapters.length > 0) { workbook.write(`${project.name}.xlsx`, response); } else { response.redirect(`/projects/${request.params.id}`) } }).catch(error => { next(error) }) } else { next() } }) }) router.route('/:id') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { const translationChapterTranslatorInclude = { association: db.TranslationChapter.TranslationChapterTranslators, include: [ { association: db.TranslationChapterTranslator.Translator, include: [ { association: db.Entity.EntityEmail, include: [ { association: db.EntityEmail.Email } ] } ] } ] } if (request.query.translatorId) { translationChapterTranslatorInclude.required = true translationChapterTranslatorInclude.include[0].required = true translationChapterTranslatorInclude.include[0].where = { id: request.query.translatorId } } db.TranslationChapter.findAll({ include: [ { association: db.TranslationChapter.Chapter, }, request.query.sheets || request.query.markdown ? { association: db.TranslationChapter.TranslationChunks, include: [ { association: db.TranslationChunk.Translation } ] } : null, translationChapterTranslatorInclude, { association: db.TranslationChapter.TranslationChapterProofReaders, }, { association: db.TranslationChapter.TranslationArtifactVersion, required: true, include: [ { association: db.TranslationArtifactVersion.ProjectTranslationArtifactVersions, required: true, where: { projectId: request.params.id }, }, { association: db.TranslationArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.ProjectArtifactVersions }, { association: db.ArtifactVersion.Artifact, include: [ { association: db.Artifact.Dialect } ] } ] }, request.query.dialectId ? { association: db.TranslationArtifactVersion.Dialect, required: true, where: { id: request.query.dialectId } } : { association: db.TranslationArtifactVersion.Dialect, }, request.query.editorId ? { association: db.TranslationArtifactVersion.TranslationArtifactVersionEditor, required: true, where: { userId: request.query.editorId } } : { association: db.TranslationArtifactVersion.TranslationArtifactVersionEditor, } ] } ].filter(i => i) }).then(translationChapters => { if (request.query.sheets) { // Return sheets const workbook = new XL.Workbook(); const cellWidth = 45 const cellHeight = 25 translationChapters.sort((a, b) => a.index - b.index).forEach((translationChapter, index) => { const worksheet_name = `${index+1}-${translationChapter.translationArtifactVersion.dialect.countryId}-${translationChapter.translationArtifactVersion.dialect.languageId} - ${translationChapter.translationArtifactVersion.artifactVersion.artifact.name}`.substring(0, 30); // Don't have [ ] * ? : / \ const worksheet = workbook.addWorksheet(worksheet_name.replace(/[`*?:'\[\]\\\/]/gi, "")) worksheet.cell(2,2,2,3,true) .string(translationChapter.translationArtifactVersion.artifactVersion.artifact.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(3,2,3,3,true) .string(translationChapter.chapter.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(4,2) .string(translationChapter.translationArtifactVersion.artifactVersion.artifact.dialect.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) worksheet.cell(4,3) .string(translationChapter.translationArtifactVersion.dialect.name) .style({ font: { bold: true, }, alignment: { horizontal: 'center', wrapText: true } }) // Meta data for the translation and artifact worksheet.cell(4,4) .string(translationChapter.translationArtifactVersion.artifactVersion.id) worksheet.cell(4,5) .string(translationChapter.translationArtifactVersion.id) var cellIndex = 6 const translationChunks = translationChapter.translationChunks.sort((a,b) => a.index - b.index) for (const j in translationChunks) { const translationChunk = translationChunks[j] worksheet.cell(cellIndex,2) .string(translationChunk.text) .style({ alignment: { wrapText: true, vertical: 'justify' } }) const translationCell = worksheet.cell(cellIndex,3) .style({ alignment: { wrapText: true, vertical: 'justify' } }) if (translationChunk.translation) { translationCell.string(translationChunk.translation.text) } const textLength = translationChunk.text.length worksheet.row(cellIndex).setHeight( Math.ceil(Math.max(textLength/cellWidth, 1)*cellHeight) ) // Meta data... // ID for translationChunk worksheet.cell(cellIndex,4) .string(translationChunk.id) // ID for translation worksheet.cell(cellIndex,5) .string(translationChunk.translation?.id ?? "") cellIndex++ } // Format Columns worksheet.column(2).setWidth(cellWidth); worksheet.column(3).setWidth(cellWidth); worksheet.column(4).hide(); worksheet.column(5).hide(); // Format Rows... worksheet.row(5).freeze() worksheet.row(1).setHeight(5) worksheet.row(5).setHeight(5) }) if (translationChapters.length > 0) { workbook.write(`${project.name}.xlsx`, response); } 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, project: project, translationChapters: translationChapters }) } }).catch(error => { next(error) }) } else { next() } }) }) const getProjectArtifactVersion = (userId, project, artifactVersion) => { if (project.projectArtifactVersions.length == 0) { return db.ProjectArtifactVersion.create({ creatorId: userId, projectId: project.id, artifactVersionId: artifactVersion.id }) } else { return project.projectArtifactVersions[0] } } router.route('/:id/import') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { response.display("project-translation-importer", { user: request.user, project: project }) } else { next() } }).catch(error => { next(error) }) }) .post(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, // TODO: Make required... where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project && request.files) { const file = request.files.translations const workbook = XLSX.read(file.data, { }); for (const sheetName in workbook.Sheets) { const sheet = workbook.Sheets[sheetName]; const artifactVersionId = sheet["D4"].v const translationArtifactVersionId = sheet["E4"].v var lastTranslationChunkId = null var i = 6 do { lastTranslationChunkId = null const translationChunkCell = sheet[`D${i}`] if (translationChunkCell) { lastTranslationChunkId = translationChunkCell.v const translatedCell = sheet[`C${i}`] if (translatedCell) { const translatedText = translatedCell.v if (translatedText) { // TODO: Get translationChunk and create translation... const translationChunk = await db.TranslationChunk.findByPk(lastTranslationChunkId, { include: [ { association: db.TranslationChunk.Translation } ] }) if (translationChunk) { // Create or update translation if (translationChunk.translation) { // Update translation if (translationChunk.translation.text != translatedText) { translationChunk.translation.text = translatedText // TODO: Update who made the update?? await translationChunk.translation.save() } } else { // Create translation for this chunk const translation = await db.Translation.create({ creatorId: request.user.id, translationChunkId: translationChunk.id, text: translatedText, translationArtifactVersionId: translationArtifactVersionId }) } } } } else { console.log(`${sheetName}: Couldn't find a translated cell C${i}: `, translatedCell) console.log("Chunk Id: ", lastTranslationChunkId) } i++ } } while (lastTranslationChunkId); // Chunks... console.log("Done processing all...") } response.redirect(`/projects/${project.id}`) } else { next() } }).catch(error => { next(error) }) }) router.route('/:id/languages/add') .get(function(request, response, next) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { // TODO: Add language to response.display("project-language-form", { user: request.user, pageTitle: "Project Langauges - Mantra", project: project }) } else { next() } }).catch(error => { next(error) }) }) .post(async function(request, response, next) { console.log("Request: ", request.body) // TODO: Find dialectId const dialectTokens = request.body.dialect?.split(":") if (dialectTokens?.length == 2) { const countryId = dialectTokens[0].split("-")[0] const languageId = dialectTokens[0].split("-")[1] const dialect = await db.Dialect.findOne({ where: { countryId: countryId, languageId: languageId } }) if (dialect) { db.Project.findByPk(request.params.id, { include: [ { association: db.Project.ProjectArtifactVersions, include: [ { association: db.ProjectArtifactVersion.ArtifactVersion, include: [ { association: db.ArtifactVersion.Artifact }, { association: db.ArtifactVersion.TranslationArtifactVersions, where: { backTranslationFromId: null, // forkedFromId: null dialectId: dialect.id }, required: false, include: [ { association: db.TranslationArtifactVersion.ProjectTranslationArtifactVersions, where: { projectId: request.params.id }, required: false } ] }, { association: db.ArtifactVersion.Chapters, include: [ { association: db.Chapter.Chunks } ] } ] }, ] }, { association: db.Project.Owner, include: [ { association: db.Owner.OwnerEntities, include: [ { association: db.OwnerEntity.Entity, include: [ { association: db.Entity.EntityUsers, required: false, // TODO: Make required... where: { userId: request.user?.id ?? null } } ] } ] } ] } ] }).then(async (project) => { if (project) { project.projectArtifactVersions for (let i = 0; i < project.projectArtifactVersions.length; i++) { const artifactVersion = project.projectArtifactVersions[i].artifactVersion; if (artifactVersion.translationArtifactVersions.length == 0) { console.log("Add this new language: ", dialect.toJSON()) const translationArtifactVersion = await db.TranslationArtifactVersion.create({ creatorId: request.user.id, name: dialect.name, artifactVersionId: artifactVersion.id, visibility: artifactVersion.artifact.visibility, dialectId: dialect.id, owner: { ownerEntities: [ { entityId: request.user.individualEntityUser.entityUser.entityId, } ] }, translationChapters: artifactVersion.chapters.map(chapter => { return { creatorId: request.user.id, chapterId: chapter.id, index: chapter.index, translationChunks: chapter.chunks.map(chunk => { return { creatorId: request.user.id, chunkId: chunk.id, text: chunk.text, index: chunk.index } }) } }), projectTranslationArtifactVersions: [ { creatorId: request.user.id, projectId: project.id } ] }, { include: [ { association: db.TranslationArtifactVersion.TranslationChapters, include: [ { association: db.TranslationChapter.TranslationChunks } ] }, { association: db.TranslationArtifactVersion.ProjectTranslationArtifactVersions, }, { association: db.TranslationArtifactVersion.Owner, include: [ { association: db.Owner.OwnerEntities } ] } ] }) // TODO: Associate in the project... } else { console.log("Language already supported") const translationArtifactVersion = artifactVersion.translationArtifactVersions[0] // Associate in the project... if association doesn't exists if (translationArtifactVersion.projectTranslationArtifactVersions == 0) { const projectTranslationArtifactVersion = await db.ProjectTranslationArtifactVersion.create({ creatorId: request.user.id, projectId: project.id, translationArtifactVersionId: translationArtifactVersion.id }) } } } // TODO: Load all the entries associated with this project and translate them... return response.redirect(`/projects/${project.id}`) } else { next() } }).catch(error => { next(error) }) } else { // TODO: Show error next() } } else { // TODO: Show error... next() } }) return router; };