From 2c4b9d94b57af1277eeb83815adeb6f3542edf21 Mon Sep 17 00:00:00 2001 From: idelcano Date: Tue, 22 Mar 2022 18:25:41 +0100 Subject: [PATCH 01/37] fix boolean translations --- src/domain/usecases/DownloadTemplateUseCase.ts | 2 ++ src/types/modules.d.ts | 1 + 2 files changed, 3 insertions(+) diff --git a/src/domain/usecases/DownloadTemplateUseCase.ts b/src/domain/usecases/DownloadTemplateUseCase.ts index f8e40e55..d0d73b7a 100644 --- a/src/domain/usecases/DownloadTemplateUseCase.ts +++ b/src/domain/usecases/DownloadTemplateUseCase.ts @@ -4,6 +4,7 @@ import _ from "lodash"; import { Moment } from "moment"; import { UseCase } from "../../CompositionRoot"; import { getRelationshipMetadata, RelationshipOrgUnitFilter } from "../../data/Dhis2RelationshipTypes"; +import i18n from "../../locales"; import { D2Api } from "../../types/d2-api"; import { promiseMap } from "../../utils/promises"; import Settings from "../../webapp/logic/settings"; @@ -60,6 +61,7 @@ export class DownloadTemplateUseCase implements UseCase { relationshipsOuFilter, }: DownloadTemplateProps ): Promise { + i18n.setDefaultNamespace("bulk-load"); const { id: templateId } = getTemplateId(type, id); const template = this.templateRepository.getTemplate(templateId); const theme = themeId ? await this.templateRepository.getTheme(themeId) : undefined; diff --git a/src/types/modules.d.ts b/src/types/modules.d.ts index c8d14f7e..acfe1c43 100644 --- a/src/types/modules.d.ts +++ b/src/types/modules.d.ts @@ -11,6 +11,7 @@ declare module "@dhis2/d2-i18n" { export function t(value: string): string; export function t(value: string, options?: { [key: string]: any }): string; export function changeLanguage(locale: string); + export function setDefaultNamespace(namespace: string); } declare module "nano-memoize" { From a701c370870947d52fc789e4bc691517e312bb35 Mon Sep 17 00:00:00 2001 From: idelcano Date: Tue, 22 Mar 2022 18:26:08 +0100 Subject: [PATCH 02/37] fix boolean translations to import, ignoring translated value and using code --- src/domain/usecases/ImportTemplateUseCase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/domain/usecases/ImportTemplateUseCase.ts b/src/domain/usecases/ImportTemplateUseCase.ts index 4ce3d94c..0ee4bd97 100644 --- a/src/domain/usecases/ImportTemplateUseCase.ts +++ b/src/domain/usecases/ImportTemplateUseCase.ts @@ -330,7 +330,7 @@ export const compareDataPackages = ( const formatDhis2Value = (item: DataPackageDataValue, dataForm: DataForm): DataPackageDataValue | undefined => { const dataElement = dataForm.dataElements.find(({ id }) => item.dataElement === id); - const booleanValue = String(item.value) === "true" || item.value === "Yes"; + const booleanValue = String(item.optionId) === "true" || item.optionId === "true"; if (dataElement?.valueType === "BOOLEAN") { return { ...item, value: booleanValue }; From 7b484c8626da761fb581afdbde129048f05d017d Mon Sep 17 00:00:00 2001 From: Arnau Sanchez Date: Thu, 31 Mar 2022 12:13:49 +0200 Subject: [PATCH 03/37] Add nvmrc with node version used --- .nvmrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..bf79505b --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +v16.14.0 From b50529a3ad6872390e7c63b29708c29b59476e1a Mon Sep 17 00:00:00 2001 From: Arnau Sanchez Date: Thu, 31 Mar 2022 12:36:27 +0200 Subject: [PATCH 04/37] Move custom modules to persisted storage (includes migration script) --- package.json | 1 + src/CompositionRoot.ts | 2 + src/data/ExcelPopulateRepository.ts | 5 +++ src/data/TemplateWebRepository.ts | 40 +++++++++++++------ src/data/templates/custom-templates.ts | 10 +++++ src/data/templates/index.ts | 11 ----- src/domain/entities/Template.ts | 8 +++- src/domain/repositories/ExcelRepository.ts | 17 ++++---- src/domain/repositories/TemplateRepository.ts | 5 ++- src/domain/usecases/AnalyzeTemplateUseCase.ts | 2 +- .../usecases/DownloadTemplateUseCase.ts | 4 +- src/domain/usecases/ImportTemplateUseCase.ts | 2 +- ...ersistTemplatesFromStaticModulesUseCase.ts | 12 ++++++ .../persist-templates-from-static-modules.ts | 32 +++++++++++++++ src/scripts/tsconfig.json | 25 ++++++++++++ yarn.lock | 15 +++++++ 16 files changed, 151 insertions(+), 40 deletions(-) create mode 100644 src/data/templates/custom-templates.ts create mode 100644 src/domain/usecases/PersistTemplatesFromStaticModulesUseCase.ts create mode 100644 src/scripts/persist-templates-from-static-modules.ts create mode 100644 src/scripts/tsconfig.json diff --git a/package.json b/package.json index 04537a68..1149393a 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "babel-core": "6.26.3", "babel-eslint": "10.1.0", "babel-preset-react-app": "10.0.1", + "cmd-ts": "^0.10.0", "craco": "0.0.3", "cypress": "9.2.1", "cypress-xpath": "1.6.2", diff --git a/src/CompositionRoot.ts b/src/CompositionRoot.ts index da27c8ac..ea6656fc 100644 --- a/src/CompositionRoot.ts +++ b/src/CompositionRoot.ts @@ -26,6 +26,7 @@ import { ImportTemplateUseCase } from "./domain/usecases/ImportTemplateUseCase"; import { ListDataFormsUseCase } from "./domain/usecases/ListDataFormsUseCase"; import { ListLanguagesUseCase } from "./domain/usecases/ListLanguagesUseCase"; import { ListThemesUseCase } from "./domain/usecases/ListThemesUseCase"; +import { PersistTemplatesFromStaticModulesUseCase } from "./domain/usecases/PersistTemplatesFromStaticModulesUseCase"; import { ReadSettingsUseCase } from "./domain/usecases/ReadSettingsUseCase"; import { RunMigrationsUseCase } from "./domain/usecases/RunMigrationsUseCase"; import { SaveThemeUseCase } from "./domain/usecases/SaveThemeUseCase"; @@ -63,6 +64,7 @@ export function getCompositionRoot({ appConfig, dhisInstance, mockApi }: Composi download: new DownloadTemplateUseCase(instance, templateManager, excelReader), import: new ImportTemplateUseCase(instance, templateManager, excelReader), list: new ListDataFormsUseCase(instance), + persistFromStaticModules: new PersistTemplatesFromStaticModulesUseCase(templateManager), }), themes: getExecute({ list: new ListThemesUseCase(templateManager), diff --git a/src/data/ExcelPopulateRepository.ts b/src/data/ExcelPopulateRepository.ts index 6fed6a32..d15ea251 100644 --- a/src/data/ExcelPopulateRepository.ts +++ b/src/data/ExcelPopulateRepository.ts @@ -39,6 +39,11 @@ export class ExcelPopulateRepository extends ExcelRepository { case "file": { return XLSX.fromDataAsync(options.file); } + case "file-base64": { + const buffer = Buffer.from(options.contents, "base64"); + const blob = new Blob([buffer]); + return XLSX.fromDataAsync(blob); + } default: { return XLSX.fromBlankAsync(); } diff --git a/src/data/TemplateWebRepository.ts b/src/data/TemplateWebRepository.ts index 3785e9c2..ab7fda88 100644 --- a/src/data/TemplateWebRepository.ts +++ b/src/data/TemplateWebRepository.ts @@ -1,32 +1,46 @@ import _ from "lodash"; +import path from "path"; +import fs from "fs"; import { Id } from "../domain/entities/ReferenceObject"; import { Template } from "../domain/entities/Template"; import { Theme } from "../domain/entities/Theme"; import { StorageRepository } from "../domain/repositories/StorageRepository"; import { TemplateRepository } from "../domain/repositories/TemplateRepository"; +import { cache } from "../utils/cache"; import * as templates from "./templates"; +import * as customTemplates from "./templates/custom-templates"; const themeCollectionKey = "themes"; -export function getTemplates(): Template[] { - return _.values(templates).map(TemplateClass => { - return new TemplateClass(); - }); -} - export class TemplateWebRepository implements TemplateRepository { - private templates: Template[]; + constructor(private storage: StorageRepository) {} + + @cache() + private async getTemplates(): Promise { + const customTemplates = await this.storage.getObject("templates", []); + const genericTemplates = _.values(templates).map(TemplateClass => new TemplateClass()); + return _.concat(genericTemplates, customTemplates); + } + + public getCustomTemplates(): Template[] { + const rootDir = path.join(__dirname, "../..", "public"); - constructor(private storage: StorageRepository) { - this.templates = getTemplates(); + return _.values(customTemplates).map((TemplateClass): Template => { + const template = new TemplateClass(); + const spreadsheetPath = path.join(rootDir, template.url); + const buffer = fs.readFileSync(spreadsheetPath); + const file = { blob: buffer.toString("base64") }; + return { ...template, file }; + }); } - public listTemplates(): Pick[] { - return this.templates.map(({ id, name }) => ({ id, name })); + public saveTemplates(templates: Template[]): Promise { + return this.storage.saveObject("templates", templates); } - public getTemplate(templateId: Id): Template { - const template = this.templates.find(({ id }) => id === templateId); + public async getTemplate(templateId: Id): Promise