Skip to content

Commit

Permalink
validate org. units for country dataSet
Browse files Browse the repository at this point in the history
  • Loading branch information
eperedo committed Aug 14, 2024
1 parent 0b58d87 commit af19440
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 47 deletions.
7 changes: 5 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2024-08-10T02:46:39.367Z\n"
"PO-Revision-Date: 2024-08-10T02:46:39.367Z\n"
"POT-Creation-Date: 2024-08-14T23:42:37.335Z\n"
"PO-Revision-Date: 2024-08-14T23:42:37.335Z\n"

msgid ""
"THIS NEW RELEASE INCLUDES SHARING SETTINGS PER INSTANCES. FOR THIS VERSION "
Expand Down Expand Up @@ -2228,6 +2228,9 @@ msgstr ""
msgid "Data sent to new DataSet. Click Next to see the data imported"
msgstr ""

msgid "Org. Unit: {{orgUnitsNames}} are not assigned to the Country DataSet"
msgstr ""

msgid "Send data to new DataSet"
msgstr ""

Expand Down
5 changes: 4 additions & 1 deletion i18n/es.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"POT-Creation-Date: 2024-08-07T02:46:01.537Z\n"
"POT-Creation-Date: 2024-08-14T23:34:06.739Z\n"
"PO-Revision-Date: 2020-07-10T06:53:30.625Z\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
Expand Down Expand Up @@ -2232,6 +2232,9 @@ msgstr ""
msgid "Data sent to new DataSet. Click Next to see the data imported"
msgstr ""

msgid "Org. Unit: {{orgUnitsNames}} are not assigned to the Country DataSet"
msgstr ""

msgid "Send data to new DataSet"
msgstr ""

Expand Down
5 changes: 4 additions & 1 deletion i18n/fr.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"POT-Creation-Date: 2024-08-07T02:46:01.537Z\n"
"POT-Creation-Date: 2024-08-14T23:34:06.739Z\n"
"PO-Revision-Date: 2020-07-10T06:53:30.625Z\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
Expand Down Expand Up @@ -2231,6 +2231,9 @@ msgstr ""
msgid "Data sent to new DataSet. Click Next to see the data imported"
msgstr ""

msgid "Org. Unit: {{orgUnitsNames}} are not assigned to the Country DataSet"
msgstr ""

msgid "Send data to new DataSet"
msgstr ""

Expand Down
5 changes: 4 additions & 1 deletion i18n/pt.po
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
msgid ""
msgstr ""
"Project-Id-Version: i18next-conv\n"
"POT-Creation-Date: 2024-08-07T02:46:01.537Z\n"
"POT-Creation-Date: 2024-08-14T23:34:06.739Z\n"
"PO-Revision-Date: 2020-07-10T06:53:30.625Z\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
Expand Down Expand Up @@ -2231,6 +2231,9 @@ msgstr ""
msgid "Data sent to new DataSet. Click Next to see the data imported"
msgstr ""

msgid "Org. Unit: {{orgUnitsNames}} are not assigned to the Country DataSet"
msgstr ""

msgid "Send data to new DataSet"
msgstr ""

Expand Down
33 changes: 33 additions & 0 deletions src/data/wmr/WmrDataSetD2Repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { WmrDataSet } from "../../domain/entities/wmr/entities/WmrDataSet";
import { WmrDataSetRepository } from "../../domain/entities/wmr/repositories/WmrDataSetRepository";
import { Instance } from "../../domain/instance/entities/Instance";
import { D2Api } from "../../types/d2-api";
import { getD2APiFromInstance } from "../../utils/d2-utils";
import { dataSetFields } from "./WmrSettingsD2Repository";

export class WmrDataSetD2Repository implements WmrDataSetRepository {
private api: D2Api;
constructor(private instance: Instance) {
this.api = getD2APiFromInstance(this.instance);
}

async getById(id: string): Promise<WmrDataSet> {
const response = await this.api.models.dataSets
.get({
filter: { id: { eq: id } },
fields: { ...dataSetFields, organisationUnits: { id: true, displayName: true } },
})
.getData();
const dataSet = response.objects[0];
if (!dataSet) throw new Error(`Data set not found: ${id}`);
return {
id: dataSet.id,
name: dataSet.displayName,
dataElements: dataSet.dataSetElements.map(({ dataElement }) => ({
id: dataElement.id,
name: dataElement.displayName,
})),
orgUnits: dataSet.organisationUnits.map(ou => ({ id: ou.id, name: ou.displayName })),
};
}
}
3 changes: 2 additions & 1 deletion src/data/wmr/WmrSettingsD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Instance } from "../../domain/instance/entities/Instance";
import { getD2APiFromInstance } from "../../utils/d2-utils";
import { Id } from "../../domain/common/entities/Schemas";

const dataSetFields = {
export const dataSetFields = {
id: true,
displayName: true,
dataSetElements: { dataElement: { id: true, displayName: true } },
Expand Down Expand Up @@ -40,6 +40,7 @@ export class WmrSettingsD2Repository implements WmrSettingsRepository {
id: dataElement.id,
name: dataElement.displayName,
})),
orgUnits: [],
})),
});
}
Expand Down
7 changes: 7 additions & 0 deletions src/domain/common/factories/RepositoryFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from "../../aggregated/repositories/AggregatedRepository";
import { ConfigRepositoryConstructor } from "../../config/repositories/ConfigRepository";
import { CustomDataRepositoryConstructor } from "../../custom-data/repository/CustomDataRepository";
import { WmrDataSetRepositoryConstructor } from "../../entities/wmr/repositories/WmrDataSetRepository";
import { WmrRepositoryConstructor } from "../../entities/wmr/repositories/WmrSettingsRepository";
import { EventsRepository, EventsRepositoryConstructor } from "../../events/repositories/EventsRepository";
import { FileRepositoryConstructor } from "../../file/repositories/FileRepository";
Expand Down Expand Up @@ -188,6 +189,11 @@ export class RepositoryFactory {
public wmrSettingsRepository(instance: Instance) {
return this.get<WmrRepositoryConstructor>(Repositories.WmrSettingsRepository, [instance]);
}

@cache()
public wmrDataSetRepository(instance: Instance) {
return this.get<WmrDataSetRepositoryConstructor>(Repositories.WmrDataSetRepository, [instance]);
}
}

type RepositoryKeys = typeof Repositories[keyof typeof Repositories];
Expand Down Expand Up @@ -216,4 +222,5 @@ export const Repositories = {
SettingsRepository: "settingsRepository",
SchedulerRepository: "schedulerRepository",
WmrSettingsRepository: "wmrSettingsRepository",
WmrDataSetRepository: "wmrDataSetRepository",
} as const;
27 changes: 27 additions & 0 deletions src/domain/entities/wmr/entities/WmrDataSet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import _ from "lodash";
import { NamedRef } from "../../../common/entities/Ref";
import { Id } from "../../../common/entities/Schemas";

export type WmrDataSetAttrs = NamedRef & { dataElements: NamedRef[]; orgUnits: NamedRef[] };

export class WmrDataSet {
public readonly id: Id;
public readonly name: string;
public readonly dataElements: NamedRef[];
public readonly orgUnits: NamedRef[];
constructor(data: WmrDataSetAttrs) {
this.id = data.id;
this.name = data.name;
this.dataElements = data.dataElements;
this.orgUnits = data.orgUnits;
}

static getOrgUnitsIds(dataSet: WmrDataSet): Id[] {
return dataSet.orgUnits.map(ou => ou.id);
}

static getDifferenceOrgsUnits(dataSet: WmrDataSet, otherDataSet: WmrDataSet): NamedRef[] {
const difference = _(dataSet.orgUnits).differenceBy(otherDataSet.orgUnits, "id").value();
return difference;
}
}
2 changes: 1 addition & 1 deletion src/domain/entities/wmr/entities/WmrSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Maybe } from "../../../../types/utils";
import { NamedRef } from "../../../common/entities/Ref";
import { Id } from "../../../common/entities/Schemas";

export type DataSetAttrs = NamedRef & { dataElements: NamedRef[] };
export type DataSetAttrs = NamedRef & { dataElements: NamedRef[]; orgUnits: NamedRef[] };
export type WmrSettingsAttrs = {
countryDataSetId: Id;
dataSets: DataSetAttrs[];
Expand Down
11 changes: 11 additions & 0 deletions src/domain/entities/wmr/repositories/WmrDataSetRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Id } from "../../../common/entities/Schemas";
import { Instance } from "../../../instance/entities/Instance";
import { WmrDataSet } from "../entities/WmrDataSet";

export interface WmrDataSetRepositoryConstructor {
new (instance: Instance): WmrDataSetRepository;
}

export interface WmrDataSetRepository {
getById(id: Id): Promise<WmrDataSet>;
}
12 changes: 12 additions & 0 deletions src/domain/entities/wmr/usecases/GetDataSetByIdUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Id } from "../../../common/entities/Schemas";
import { RepositoryFactory } from "../../../common/factories/RepositoryFactory";
import { Instance } from "../../../instance/entities/Instance";
import { WmrDataSet } from "../entities/WmrDataSet";

export class GetDataSetByIdUseCase {
constructor(private repositoryFactory: RepositoryFactory, private localInstance: Instance) {}

execute(id: Id): Promise<WmrDataSet> {
return this.repositoryFactory.wmrDataSetRepository(this.localInstance).getById(id);
}
}
4 changes: 4 additions & 0 deletions src/presentation/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { SystemInfoD2ApiRepository } from "../data/system-info/SystemInfoD2ApiRe
import { TEID2ApiRepository } from "../data/tracked-entity-instances/TEID2ApiRepository";
import { TransformationD2ApiRepository } from "../data/transformations/TransformationD2ApiRepository";
import { UserD2ApiRepository } from "../data/user/UserD2ApiRepository";
import { WmrDataSetD2Repository } from "../data/wmr/WmrDataSetD2Repository";
import { WmrSettingsD2Repository } from "../data/wmr/WmrSettingsD2Repository";
import { AggregatedSyncUseCase } from "../domain/aggregated/usecases/AggregatedSyncUseCase";
import { DeleteAggregatedUseCase } from "../domain/aggregated/usecases/DeleteAggregatedUseCase";
Expand All @@ -32,6 +33,7 @@ import { GetStorageConfigUseCase } from "../domain/config/usecases/GetStorageCon
import { SetStorageConfigUseCase } from "../domain/config/usecases/SetStorageConfigUseCase";
import { GetCustomDataUseCase } from "../domain/custom-data/usecases/GetCustomDataUseCase";
import { SaveCustomDataUseCase } from "../domain/custom-data/usecases/SaveCustomDataUseCase";
import { GetDataSetByIdUseCase } from "../domain/entities/wmr/usecases/GetDataSetByIdUseCase";
import { GetWmrSettingsUseCase } from "../domain/entities/wmr/usecases/GetWmrSettingsUseCase";
import { EventsSyncUseCase } from "../domain/events/usecases/EventsSyncUseCase";
import { ListEventsUseCase } from "../domain/events/usecases/ListEventsUseCase";
Expand Down Expand Up @@ -151,12 +153,14 @@ export class CompositionRoot {
this.repositoryFactory.bind(Repositories.SchedulerRepository, SchedulerD2ApiRepository);
this.repositoryFactory.bind(Repositories.SettingsRepository, SettingsD2ApiRepository);
this.repositoryFactory.bind(Repositories.WmrSettingsRepository, WmrSettingsD2Repository);
this.repositoryFactory.bind(Repositories.WmrDataSetRepository, WmrDataSetD2Repository);
}

@cache()
public get wmr() {
return getExecute({
settings: new GetWmrSettingsUseCase(this.repositoryFactory, this.localInstance),
getDataSetById: new GetDataSetByIdUseCase(this.repositoryFactory, this.localInstance),
});
}

Expand Down
6 changes: 4 additions & 2 deletions src/presentation/webapp/wmr/MapWmrData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,21 @@ import i18n from "../../../types/i18n";
import InstanceMappingPage from "../core/pages/instance-mapping/InstanceMappingPage";
import { WmrSettings } from "../../../domain/entities/wmr/entities/WmrSettings";
import { Id } from "../../../domain/common/entities/Schemas";
import { WmrSyncRule } from "./WmrPage";

type MapWmrDataProps = { settings: WmrSettings };
type MapWmrDataProps = { settings: WmrSettings; wmrSyncRule: WmrSyncRule };

export function MapWmrData(props: MapWmrDataProps) {
const [dataSetId, setDataSetId] = React.useState<Id>();
const { settings } = props;
const { wmrSyncRule, settings } = props;

const dataSets = settings.dataSets.map(dataSet => {
return { text: dataSet.name, value: dataSet.id };
});

const onChangeDataSet = (value: Id | undefined) => {
setDataSetId(value);
wmrSyncRule.localDataSetId = value;
};

const allowedLocalDataElementsIds = settings.getDataElementsIds(dataSetId);
Expand Down
34 changes: 25 additions & 9 deletions src/presentation/webapp/wmr/MapWmrOrgUnits.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import { OrgUnitsSelector, useLoading, useSnackbar } from "@eyeseetea/d2-ui-comp

import i18n from "../../../types/i18n";
import { useAppContext } from "../../react/core/contexts/AppContext";
import { SynchronizationRule } from "../../../domain/rules/entities/SynchronizationRule";
import { useMappingDataElements } from "./hooks/useMappingDataElements";
import { useGetDataSetOrgUnits, useMappingDataElements } from "./hooks/useMappingDataElements";
import { WmrSettings } from "../../../domain/entities/wmr/entities/WmrSettings";
import { WmrSyncRule } from "./WmrPage";
import { WmrDataSet } from "../../../domain/entities/wmr/entities/WmrDataSet";
import { Alert } from "@material-ui/lab";

const orgUnitControls = {
filterByLevel: false,
Expand All @@ -14,18 +17,17 @@ const orgUnitControls = {
selectAll: false,
};

type MapWmrOrgUnitsProps = {
syncRule: SynchronizationRule;
updateRule: React.Dispatch<React.SetStateAction<SynchronizationRule>>;
};
type MapWmrOrgUnitsProps = { settings: WmrSettings; wmrSyncRule: WmrSyncRule };

export function MapWmrOrgUnits(props: MapWmrOrgUnitsProps) {
const { syncRule, updateRule } = props;
const { settings, wmrSyncRule } = props;
const { api, compositionRoot } = useAppContext();
const loading = useLoading();
const snackbar = useSnackbar();
const [selectedOrgUnits, updateOrgUnits] = React.useState<string[]>([]);
const { dataElementsToMigrate } = useMappingDataElements();
const { dataSet: localDataSet } = useGetDataSetOrgUnits({ id: wmrSyncRule.localDataSetId || "" });
const { dataSet: countryDataSet } = useGetDataSetOrgUnits({ id: settings.countryDataSetId || "" });

async function onSubmit(e: React.FormEvent) {
e.preventDefault();
Expand All @@ -34,7 +36,7 @@ export function MapWmrOrgUnits(props: MapWmrOrgUnitsProps) {
return;
}

const syncRuleUpdated = syncRule
const syncRuleUpdated = wmrSyncRule.rule
.updateBuilder({ metadataIds: dataElementsToMigrate })
.updateDataSyncOrgUnitPaths(selectedOrgUnits);

Expand All @@ -56,7 +58,7 @@ export function MapWmrOrgUnits(props: MapWmrOrgUnitsProps) {
await result.match({
success: async () => {
await synchronize();
updateRule(syncRuleUpdated);
wmrSyncRule.rule = syncRuleUpdated;
snackbar.success(i18n.t("Data sent to new DataSet. Click Next to see the data imported"));
},
error: async code => {
Expand All @@ -69,16 +71,30 @@ export function MapWmrOrgUnits(props: MapWmrOrgUnitsProps) {
updateOrgUnits(orgUnitsPaths);
};

const orgUnitsDifference =
localDataSet && countryDataSet ? WmrDataSet.getDifferenceOrgsUnits(localDataSet, countryDataSet) : undefined;

return (
<form onSubmit={onSubmit}>
<Grid container spacing={3}>
{orgUnitsDifference && (
<Grid item xs={12}>
<Alert severity="warning">
{i18n.t("Org. Unit: {{orgUnitsNames}} are not assigned to the Country DataSet", {
orgUnitsNames: orgUnitsDifference.map(ou => ou.name).join(", "),
nsSeparator: false,
})}
</Alert>
</Grid>
)}
<Grid item xs={12}>
<OrgUnitsSelector
api={api}
controls={orgUnitControls}
onChange={onOrgUnitChange}
selected={selectedOrgUnits}
singleSelection
selectableIds={localDataSet ? WmrDataSet.getOrgUnitsIds(localDataSet) : undefined}
/>
</Grid>
<Grid item xs={12}>
Expand Down
Loading

0 comments on commit af19440

Please sign in to comment.