Skip to content

Commit

Permalink
Merge pull request #58 from Marvell-Consulting/SW-159
Browse files Browse the repository at this point in the history
SW-159: Translation export & import
  • Loading branch information
wheelsandcogs authored Dec 19, 2024
2 parents 0c916d7 + 2ceaf04 commit aabe289
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 69 deletions.
100 changes: 46 additions & 54 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"dependencies": {
"connect-redis": "^7.1.1",
"cookie-parser": "^1.4.6",
"csv-parse": "^5.6.0",
"date-fns": "^4.1.0",
"dotenv": "^16.4.5",
"ejs": "^3.1.10",
Expand Down
73 changes: 73 additions & 0 deletions src/controllers/publish.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Readable } from 'node:stream';

import { NextFunction, Request, Response } from 'express';
import { snakeCase, sortBy, uniqBy } from 'lodash';
import { FieldValidationError, matchedData } from 'express-validator';
import { nanoid } from 'nanoid';
import { v4 as uuid } from 'uuid';
import { isBefore, isValid, parseISO } from 'date-fns';
import { parse } from 'csv-parse';

import {
collectionValidator,
Expand Down Expand Up @@ -60,6 +63,8 @@ import { DimensionPatchDto } from '../dtos/dimension-patch-dto';
import { ApiException } from '../exceptions/api.exception';
import { DimensionInfoDTO } from '../dtos/dimension-info';
import { YearType } from '../enums/year-type';
import { addEditLinks } from '../utils/add-edit-links';
import { TranslationDTO } from '../dtos/translations';

export const start = (req: Request, res: Response, next: NextFunction) => {
res.render('publish/start');
Expand Down Expand Up @@ -1523,3 +1528,71 @@ export const provideOrganisation = async (req: Request, res: Response, next: Nex

res.render('publish/organisation', { values, organisations, teams, errors });
};

export const exportTranslations = async (req: Request, res: Response, next: NextFunction) => {
try {
const dataset = res.locals.dataset;

if (req.query.format === 'csv') {
const fileName = `translations-${dataset.id}.csv`;
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename=${fileName}`);
const translationStream = await req.swapi.getTranslationExport(dataset.id);
Readable.from(translationStream).pipe(res);
return;
}

let translations = await req.swapi.getTranslationPreview(dataset.id);
translations = addEditLinks(translations, dataset.id, req);
res.render('publish/translations/export', { translations });
} catch (err) {
next(new UnknownException());
}
};

const parseUploadedTranslations = async (fileBuffer: Buffer): Promise<TranslationDTO[]> => {
const translations: TranslationDTO[] = [];

const csvParser: AsyncIterable<TranslationDTO> = Readable.from(fileBuffer).pipe(
parse({ bom: true, columns: true })
);

for await (const row of csvParser) {
translations.push(row);
}

return translations;
};

export const importTranslations = async (req: Request, res: Response, next: NextFunction) => {
const dataset = res.locals.dataset;
let errors: ViewError[] = [];
let preview = false;
let translations: TranslationDTO[] = [];

try {
if (req.query.confirm === 'true') {
await req.swapi.updateTranslations(dataset.id);
res.redirect(req.buildUrl(`/publish/${dataset.id}/tasklist`, req.language));
return;
}

if (req.method === 'POST') {
if (!req.file) {
errors = [{ field: 'csv', message: { key: 'translations.import.form.file.error.missing' } }];
throw new Error();
}

const fileData = new Blob([req.file.buffer], { type: req.file.mimetype });
await req.swapi.uploadTranslationImport(dataset.id, fileData);

preview = true;
translations = await parseUploadedTranslations(req.file.buffer);
}
} catch (err) {
res.status(400);
errors.push({ field: 'csv', message: { key: 'translations.import.form.file.error.invalid' } });
}

res.render('publish/translations/import', { preview, translations, errors });
};
8 changes: 8 additions & 0 deletions src/dtos/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class TranslationDTO {
type: string;
key: string;
id?: string;
english?: string;
cymraeg?: string;
edit_link?: string;
}
3 changes: 3 additions & 0 deletions src/enums/task-status.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export enum TaskStatus {
CannotStart = 'cannot_start',
Available = 'available',
NotStarted = 'not_started',
Incomplete = 'incomplete',
Completed = 'completed',
NotImplemented = 'not_implemented'
}
Loading

0 comments on commit aabe289

Please sign in to comment.