Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/fix/periods-range-parameter' int…
Browse files Browse the repository at this point in the history
…o feature/support-v41
  • Loading branch information
eperedo committed Jan 10, 2025
2 parents 5e5a994 + aa6f70f commit 0e2d7e0
Show file tree
Hide file tree
Showing 17 changed files with 167 additions and 64 deletions.
10 changes: 8 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-05-16T07:04:50.457Z\n"
"PO-Revision-Date: 2024-05-16T07:04:50.458Z\n"
"POT-Creation-Date: 2024-11-04T15:12:23.322Z\n"
"PO-Revision-Date: 2024-11-04T15:12:23.322Z\n"

msgid "ID"
msgstr ""
Expand Down Expand Up @@ -56,6 +56,9 @@ msgstr ""
msgid "No"
msgstr ""

msgid "Select at least one organisation unit"
msgstr ""

msgid "Cannot be blank: {{fieldName}}"
msgstr ""

Expand Down Expand Up @@ -199,6 +202,9 @@ msgstr ""
msgid "Summary"
msgstr ""

msgid "You must save the Analysis configuration before running any step"
msgstr ""

msgid "Algorithm"
msgstr ""

Expand Down
8 changes: 7 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-05-16T07:04:50.457Z\n"
"POT-Creation-Date: 2024-11-04T15:12:23.322Z\n"
"PO-Revision-Date: 2018-10-25T09:02:35.143Z\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
Expand Down Expand Up @@ -56,6 +56,9 @@ msgstr ""
msgid "No"
msgstr ""

msgid "Select at least one organisation unit"
msgstr ""

msgid "Cannot be blank: {{fieldName}}"
msgstr ""

Expand Down Expand Up @@ -199,6 +202,9 @@ msgstr ""
msgid "Summary"
msgstr ""

msgid "You must save the Analysis configuration before running any step"
msgstr ""

msgid "Algorithm"
msgstr ""

Expand Down
4 changes: 3 additions & 1 deletion src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ type Repositories = {

function getCompositionRoot(repositories: Repositories, metadata: MetadataItem) {
return {
countries: { getByIds: new GetCountriesByIdsUseCase(repositories.countryRepository) },
countries: {
getByIds: new GetCountriesByIdsUseCase(repositories.countryRepository, metadata),
},
users: { getCurrent: new GetCurrentUserUseCase(repositories.usersRepository) },
modules: {
get: new GetModulesUseCase(repositories.moduleRepository),
Expand Down
3 changes: 3 additions & 0 deletions src/data/repositories/ValidationRuleAnalysisD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import {
import { ValidationRule } from "$/domain/entities/ValidationRuleGroup";
import _ from "$/domain/entities/generic/Collection";
import { Future } from "$/domain/entities/generic/Future";
import i18n from "$/utils/i18n";

export class ValidationRuleAnalysisD2Repository implements ValidationRuleAnalysisRepository {
constructor(private api: D2Api) {}

get(options: ValidationRuleOptions): FutureData<ValidationRuleAnalysis[]> {
if (!options.countryId)
return Future.error(new Error(i18n.t("Select at least one organisation unit")));
return apiToFuture(
this.api.request<D2ValidationRuleAnalysis[]>({
method: "post",
Expand Down
46 changes: 36 additions & 10 deletions src/domain/entities/QualityAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,31 @@ export class QualityAnalysis extends Struct<QualityAnalysisAttrs>() {
static build(
attrs: QualityAnalysisAttrs
): Either<ValidationError<QualityAnalysis>[], QualityAnalysis> {
const qualityAnalysis = new QualityAnalysis({
...attrs,
name: attrs.name,
});
const qualityAnalysis = new QualityAnalysis({ ...attrs, name: attrs.name });
const errors: ValidationError<QualityAnalysis>[] = QualityAnalysis.getErrors(
qualityAnalysis,
{ validateCountries: false }
);
return errors.length === 0 ? Either.success(qualityAnalysis) : Either.error(errors);
}

const errors: ValidationError<QualityAnalysis>[] = [
private static getErrors(
qualityAnalysis: QualityAnalysis,
options: { validateCountries: boolean }
): ValidationError<QualityAnalysis>[] {
const countryProperty = options.validateCountries
? [
{
property: "countriesAnalysis" as const,
errors: validateRequired(
qualityAnalysis.countriesAnalysis,
"country_validation"
),
value: qualityAnalysis.countriesAnalysis,
},
]
: [];
return [
{
property: "name" as const,
errors: validateRequired(qualityAnalysis.name),
Expand All @@ -57,13 +76,19 @@ export class QualityAnalysis extends Struct<QualityAnalysisAttrs>() {
errors: validateRequired(qualityAnalysis.status),
value: qualityAnalysis.status,
},
...countryProperty,
].filter(validation => validation.errors.length > 0);
}

if (errors.length === 0) {
return Either.success(qualityAnalysis);
} else {
return Either.error(errors);
}
static updateConfiguration(
attrs: QualityAnalysisAttrs
): Either<ValidationError<QualityAnalysis>[], QualityAnalysis> {
const qualityAnalysis = this.build(attrs).get();
const errors: ValidationError<QualityAnalysis>[] = QualityAnalysis.getErrors(
qualityAnalysis,
{ validateCountries: true }
);
return errors.length === 0 ? Either.success(qualityAnalysis) : Either.error(errors);
}

static hasExecutedSections(qualityAnalysis: QualityAnalysis): boolean {
Expand All @@ -77,6 +102,7 @@ export class QualityAnalysis extends Struct<QualityAnalysisAttrs>() {
const formattedDate = date.toISOString().replace(/[-:T.]/g, "_");
return `${prefix} - ${formattedDate}`;
}

static isModuleTwo(qualityAnalysis: QualityAnalysis): boolean {
return qualityAnalysis.module.code === MODULE_2_CODE;
}
Expand Down
5 changes: 0 additions & 5 deletions src/domain/entities/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,6 @@ export class Settings extends Struct<SettingsAttrs>() {
errors: validateRequired(settings.startDate),
value: settings.startDate,
},
{
property: "countryIds" as const,
errors: validateRequired(settings.countryIds),
value: settings.countryIds,
},
].filter(validation => validation.errors.length > 0);

if (errors.length === 0) {
Expand Down
3 changes: 2 additions & 1 deletion src/domain/entities/generic/Errors.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import i18n from "$/utils/i18n";

export type ValidationErrorKey = "field_cannot_be_blank";
export type ValidationErrorKey = "field_cannot_be_blank" | "country_validation";

export const validationErrorMessages: Record<ValidationErrorKey, (fieldName: string) => string> = {
country_validation: () => i18n.t("Select at least one organisation unit"),
field_cannot_be_blank: (fieldName: string) =>
i18n.t(`Cannot be blank: {{fieldName}}`, { fieldName: fieldName, nsSeparator: false }),
};
Expand Down
11 changes: 9 additions & 2 deletions src/domain/entities/generic/validations.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { ValidationErrorKey } from "./Errors";

export function validateRequired(value: any): ValidationErrorKey[] {
export function validateRequired(
value: any,
errorCode: ValidationErrorKey = "field_cannot_be_blank"
): ValidationErrorKey[] {
const isBlank = !value || (value.length !== undefined && value.length === 0);

return isBlank ? ["field_cannot_be_blank"] : [];
return isBlank ? setErrorCode(errorCode) : [];
}

function setErrorCode(errorCode?: ValidationErrorKey): ValidationErrorKey[] {
return errorCode ? [errorCode] : ["field_cannot_be_blank"];
}
4 changes: 1 addition & 3 deletions src/domain/usecases/CreateQualityAnalysisUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ export class CreateQualityAnalysisUseCase {
status: "In Progress",
lastModification: "",
countriesAnalysis: defaultSettings.countryIds,
sequential: {
value: `DQ-${sequential.value}`,
},
sequential: { value: `DQ-${sequential.value}` },
}).match({
error: errors => {
const errorMessages = getErrors(errors);
Expand Down
4 changes: 3 additions & 1 deletion src/domain/usecases/GetCountriesByIdsUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { FutureData } from "$/data/api-futures";
import { Country } from "$/domain/entities/Country";
import { MetadataItem } from "$/domain/entities/MetadataItem";
import { Id } from "$/domain/entities/Ref";
import { Future } from "$/domain/entities/generic/Future";
import { CountryRepository } from "$/domain/repositories/CountryRepository";

export class GetCountriesByIdsUseCase {
constructor(private countryRepository: CountryRepository) {}
constructor(private countryRepository: CountryRepository, private config: MetadataItem) {}

execute(ids: Id[]): FutureData<Country[]> {
if (ids.length === 0) return Future.success([]);

return this.countryRepository.getByIds(ids);
}
}
22 changes: 8 additions & 14 deletions src/domain/usecases/RunPractitionersValidationUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import { IssueRepository } from "$/domain/repositories/IssueRepository";
import { UCIssue } from "./common/UCIssue";
import { UCAnalysis } from "./common/UCAnalysis";
import i18n from "$/utils/i18n";
import { UCDataValue } from "$/domain/usecases/common/UCDataValue";

export class RunPractitionersValidationUseCase {
issueUseCase: UCIssue;
analysysUseCase: UCAnalysis;
private dataValueUseCase: UCDataValue;

constructor(
private analysisRepository: QualityAnalysisRepository,
Expand All @@ -29,6 +31,7 @@ export class RunPractitionersValidationUseCase {
) {
this.analysysUseCase = new UCAnalysis(this.analysisRepository);
this.issueUseCase = new UCIssue(this.issueRepository);
this.dataValueUseCase = new UCDataValue(this.dataValueRepository);
}

execute(options: PractitionersValidationOptions): FutureData<QualityAnalysis> {
Expand Down Expand Up @@ -346,20 +349,11 @@ export class RunPractitionersValidationUseCase {
}

private getDataValues(qualityAnalysis: QualityAnalysis): FutureData<DataValue[]> {
const $requests = _(qualityAnalysis.countriesAnalysis)
.chunk(1)
.map(countryIds => {
return this.dataValueRepository.get({
moduleIds: [qualityAnalysis.module.id],
countriesIds: countryIds,
period: _([qualityAnalysis.startDate, qualityAnalysis.endDate]).uniq().value(),
});
})
.value();

return Future.sequential($requests).flatMap(result => {
return Future.success(_(result).flatten().value());
});
return this.dataValueUseCase.get(
qualityAnalysis.countriesAnalysis,
[qualityAnalysis.module.id],
[qualityAnalysis.startDate, qualityAnalysis.endDate]
);
}

private getDataElementsByDisaggregationIds(
Expand Down
3 changes: 3 additions & 0 deletions src/domain/usecases/RunValidationsUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { ValidationRuleGroupRepository } from "$/domain/repositories/ValidationR
import { ValidationRuleGroup } from "$/domain/entities/ValidationRuleGroup";
import { MetadataItem } from "$/domain/entities/MetadataItem";
import { CountryRepository } from "$/domain/repositories/CountryRepository";
import i18n from "$/utils/i18n";

export class RunValidationsUseCase {
private issueUseCase: UCIssue;
Expand All @@ -39,6 +40,8 @@ export class RunValidationsUseCase {
options.validationRuleGroupId
),
}).flatMap(({ analysis, validationRuleGroup }) => {
if (analysis.countriesAnalysis.length === 0)
return Future.error(new Error(i18n.t("Select at least one organisation unit")));
const checkAllCountries = this.isGlobalInCountries(analysis.countriesAnalysis);
return this.getAllCountries(checkAllCountries, analysis).flatMap(countriesIds => {
return this.getValidationRuleAnalysis(analysis, countriesIds, options).flatMap(
Expand Down
18 changes: 13 additions & 5 deletions src/domain/usecases/SaveConfigAnalysisUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Id } from "$/domain/entities/Ref";
import { FutureData } from "$/data/api-futures";
import { QualityAnalysis } from "$/domain/entities/QualityAnalysis";
import { QualityAnalysisRepository } from "$/domain/repositories/QualityAnalysisRepository";
import { getErrors } from "$/domain/entities/generic/Errors";

export class SaveConfigAnalysisUseCase {
constructor(private qualityAnalysisRepository: QualityAnalysisRepository) {}
Expand All @@ -10,15 +11,22 @@ export class SaveConfigAnalysisUseCase {
return this.getAnalysis(options.qualityAnalysis.id).flatMap(analysis => {
const wasExecuted = QualityAnalysis.hasExecutedSections(analysis);
const qualityAnalysisToSave = wasExecuted
? QualityAnalysis.build({
...analysis,
name: options.qualityAnalysis.name,
}).get()
: QualityAnalysis.build(options.qualityAnalysis).get();
? QualityAnalysis.build({ ...analysis, name: options.qualityAnalysis.name }).get()
: this.updateConfigAnalysis(options.qualityAnalysis);
return this.qualityAnalysisRepository.save([qualityAnalysisToSave]);
});
}

private updateConfigAnalysis(analysis: QualityAnalysis): QualityAnalysis {
return QualityAnalysis.updateConfiguration(analysis).match({
success: analysis => analysis,
error: errors => {
const errorMessage = getErrors(errors);
throw new Error(errorMessage);
},
});
}

private getAnalysis(id: Id): FutureData<QualityAnalysis> {
return this.qualityAnalysisRepository.getById(id);
}
Expand Down
9 changes: 8 additions & 1 deletion src/domain/usecases/common/UCDataValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@ import { DataValue } from "$/domain/entities/DataValue";
import { Id, Period } from "$/domain/entities/Ref";
import { DataValueRepository } from "$/domain/repositories/DataValueRepository";
import { Future } from "$/domain/entities/generic/Future";
import i18n from "$/utils/i18n";

export class UCDataValue {
constructor(private dataValueRepository: DataValueRepository) {}

get(countriesIds: Id[], moduleIds: Id[], periods: Period[]): FutureData<DataValue[]> {
if (countriesIds.length === 0)
throw new Error(i18n.t("Select at least one organisation unit"));

const [startDate, endDate] = periods;
if (!startDate || !endDate) throw new Error("Invalid period");
const periodsToSearch = _.range(Number(startDate), Number(endDate) + 1);
const $requests = _(countriesIds)
.chunk(1)
.map(countryIds => {
return this.dataValueRepository.get({
moduleIds: moduleIds,
countriesIds: countryIds,
period: _(periods).uniq().value(),
period: periodsToSearch.map(period => String(period)),
});
})
.value();
Expand Down
Loading

0 comments on commit 0e2d7e0

Please sign in to comment.