Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d66b0fa
update attribute to filter
gqcorneby Jul 30, 2025
c0854b4
use shared workflow
gqcorneby Jul 30, 2025
f7e5dd1
Merge pull request #71 from EyeSeeTea/fix/search-use-filter
Ramon-Jimenez Jul 30, 2025
e6b783b
Merge pull request #72 from EyeSeeTea/feature/use-shared-workflow
Ramon-Jimenez Jul 30, 2025
e021d48
usecase to manually create issue
gqcorneby Jul 31, 2025
e8431c2
add manual step
gqcorneby Jul 31, 2025
d958593
add issue modal skeleton
gqcorneby Jul 31, 2025
94ad602
Add issue dialog form
gqcorneby Jul 31, 2025
67222dd
finalize form, build issues
gqcorneby Aug 3, 2025
6448423
save manual issues
gqcorneby Aug 4, 2025
d286b59
update loading message
gqcorneby Aug 4, 2025
6420bb7
rename merge for clarity
gqcorneby Aug 5, 2025
ab38a22
translations
gqcorneby Aug 5, 2025
41687e1
add utils for generate periods
gqcorneby Aug 5, 2025
f572b61
clean up formatting
gqcorneby Aug 5, 2025
1bffa05
update loading text
gqcorneby Aug 5, 2025
7599088
update yarn lock
gqcorneby Aug 5, 2025
a6b272a
minor improvements
gqcorneby Aug 13, 2025
5c1a502
update d2-ui-component
gqcorneby Aug 18, 2025
bee6d45
add children to selectable ids
gqcorneby Aug 18, 2025
e106732
Merge pull request #73 from EyeSeeTea/feature/manual-issue
Ramon-Jimenez Aug 19, 2025
b2e95c5
version bumped
Ramon-Jimenez Aug 19, 2025
70d9320
update default selection to all in step 4
gqcorneby Sep 29, 2025
ff625a4
add check for children x.1 and x.2
gqcorneby Sep 29, 2025
32c6011
update names of step 3 and setp 4
gqcorneby Sep 30, 2025
d4719c8
clean up unused prop
gqcorneby Oct 2, 2025
2f5ba21
Merge pull request #74 from EyeSeeTea/feature/add-GP-check
Ramon-Jimenez Oct 7, 2025
63a91b8
update check to accept 0 value
gqcorneby Oct 9, 2025
705299b
apply child xor check only to 1.x parent
gqcorneby Oct 15, 2025
a8a9e45
Merge pull request #76 from EyeSeeTea/fix/childrenXORCheck
Ramon-Jimenez Oct 15, 2025
91ba40f
Merge pull request #77 from EyeSeeTea/fix/apply-childXOR-to-1
Ramon-Jimenez Oct 15, 2025
518de4a
update sum of children != parent message
gqcorneby Oct 23, 2025
8395b91
Merge pull request #78 from EyeSeeTea/fix/issue-message
Ramon-Jimenez Oct 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 10 additions & 45 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,12 @@
name: Application testing
on:
push:
workflow_dispatch:
jobs:
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Install apt libraries
run: sudo apt install gettext -y

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: "18"

- name: Install yarn
run: npm install -g yarn

- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
name: "Shared Master Workflow"

- name: Cache yarn dependencies
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

- name: Install dependencies
run: yarn install --frozen-lockfile --silent

- name: Install translations
run: yarn localize

- name: Run jest tests
run: yarn test
on:
push:
branches: ["master", "development"]
pull_request:
branches: [ "master", "development" ]
workflow_dispatch:

- name: Build typescript
run: npx tsc
jobs:
master-workflow:
uses: EyeSeeTea/github-workflows/.github/workflows/master.yml@master
25 changes: 20 additions & 5 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-11-04T15:12:23.322Z\n"
"PO-Revision-Date: 2024-11-04T15:12:23.322Z\n"
"POT-Creation-Date: 2025-08-05T05:01:40.620Z\n"
"PO-Revision-Date: 2025-08-05T05:01:40.620Z\n"

msgid "ID"
msgstr ""
Expand Down Expand Up @@ -68,6 +68,15 @@ msgstr ""
msgid "This analysis is not available for this module"
msgstr ""

msgid "Add Issue"
msgstr ""

msgid "Periods"
msgstr ""

msgid "Category Options"
msgstr ""

msgid "Open analysis"
msgstr ""

Expand Down Expand Up @@ -136,9 +145,6 @@ msgstr ""
msgid "Countries"
msgstr ""

msgid "Periods"
msgstr ""

msgid "Step"
msgstr ""

Expand Down Expand Up @@ -226,6 +232,15 @@ msgstr ""
msgid "Validation Rule Group"
msgstr ""

msgid "Adding issues..."
msgstr ""

msgid "New Issue"
msgstr ""

msgid "No Issues Found"
msgstr ""

msgid "Run"
msgstr ""

Expand Down
23 changes: 19 additions & 4 deletions 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-11-04T15:12:23.322Z\n"
"POT-Creation-Date: 2025-08-05T05:01:40.620Z\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 @@ -68,6 +68,15 @@ msgstr ""
msgid "This analysis is not available for this module"
msgstr ""

msgid "Add Issue"
msgstr ""

msgid "Periods"
msgstr ""

msgid "Category Options"
msgstr ""

msgid "Open analysis"
msgstr ""

Expand Down Expand Up @@ -136,9 +145,6 @@ msgstr ""
msgid "Countries"
msgstr ""

msgid "Periods"
msgstr ""

msgid "Step"
msgstr ""

Expand Down Expand Up @@ -226,6 +232,15 @@ msgstr ""
msgid "Validation Rule Group"
msgstr ""

msgid "Adding issues..."
msgstr ""

msgid "New Issue"
msgstr ""

msgid "No Issues Found"
msgstr ""

msgid "Run"
msgstr ""

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "data-quality",
"description": "Data Quality",
"version": "1.2.3",
"version": "1.3.0",
"license": "GPL-3.0",
"author": "EyeSeeTea team",
"homepage": ".",
Expand All @@ -18,7 +18,7 @@
"@dhis2/ui": "6.12.0",
"@eyeseetea/d2-api": "1.14.0",
"@eyeseetea/d2-logger": "1.2.0-beta.1",
"@eyeseetea/d2-ui-components": "2.9.0-beta.2",
"@eyeseetea/d2-ui-components": "2.10.1",
"@eyeseetea/feedback-component": "0.0.3",
"@material-ui/core": "4.12.4",
"@material-ui/icons": "4.11.3",
Expand Down
5 changes: 5 additions & 0 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import { ExportIssuesUseCase } from "$/domain/usecases/ExportIssuesUseCase";
import { IssueExportRepository } from "$/domain/repositories/IssueExportRepository";
import { IssueSpreadSheetRepository } from "./data/repositories/IssueSpreadSheetRepository";
import { IssueSpreadSheetTestRepository } from "./data/repositories/IssueSpreadSheetTestRepository";
import { CreateIssueUseCase } from "$/domain/usecases/CreateIssueUseCase";

export type CompositionRoot = ReturnType<typeof getCompositionRoot>;

Expand Down Expand Up @@ -159,6 +160,10 @@ function getCompositionRoot(repositories: Repositories, metadata: MetadataItem)
repositories.issueRepository,
repositories.issueExportRepository
),
create: new CreateIssueUseCase(
repositories.qualityAnalysisRepository,
repositories.issueRepository
),
},
settings: { get: new GetSettingsUseCase(repositories.settingsRepository) },
summary: {
Expand Down
2 changes: 1 addition & 1 deletion src/data/repositories/IssueD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export class IssueD2Repository implements IssueRepository {
},
{
id: this.metadata.dataElements.period.id,
value: issue.period,
value: issue.period || "",
},
{
id: this.metadata.dataElements.categoryOption.id,
Expand Down
2 changes: 1 addition & 1 deletion src/data/repositories/QualityAnalysisD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export class QualityAnalysisD2Repository implements QualityAnalysisRepository {
// is deprecated
// @ts-ignore
trackedEntities: options.filters.ids ? options.filters.ids.join(";") : undefined,
attribute: this.buildFilters(options.filters)?.join(",") || undefined,
filter: this.buildFilters(options.filters)?.join(",") || undefined,
// @ts-ignore
order: this.buildOrder(options.sorting) || undefined,
totalPages: true,
Expand Down
2 changes: 1 addition & 1 deletion src/domain/entities/DismissedAnalysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export type DismissedAnalysisAttrs = {
};

export class DismissedAnalysis extends Struct<DismissedAnalysisAttrs>() {
mergeDuplicates(): QualityAnalysisIssue[] {
inheritDismissedDuplicates(): QualityAnalysisIssue[] {
const dismissedIssues = this.existingIssues.filter(issue => issue.status?.code === "4");
const issues = _(this.newIssues)
.map(issue => {
Expand Down
2 changes: 1 addition & 1 deletion src/domain/entities/QualityAnalysisIssue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export interface QualityAnalysisIssueAttrs {
id: Id;
number: IssueNumber;
azureUrl: string;
period: IssuePeriod;
period: Maybe<IssuePeriod>;
country: Maybe<Country>;
dataElement: Maybe<NamedRef>;
categoryOption: Maybe<NamedRef>;
Expand Down
109 changes: 109 additions & 0 deletions src/domain/usecases/CreateIssueUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { FutureData } from "$/data/api-futures";
import { IssueRepository } from "$/domain/repositories/IssueRepository";
import { UCIssue } from "$/domain/usecases/common/UCIssue";
import { QualityAnalysisIssue } from "$/domain/entities/QualityAnalysisIssue";
import { Id, Period } from "$/domain/entities/Ref";
import { Future } from "$/domain/entities/generic/Future";
import { UCAnalysis } from "$/domain/usecases/common/UCAnalysis";
import { QualityAnalysisRepository } from "$/domain/repositories/QualityAnalysisRepository";
import { QualityAnalysis } from "$/domain/entities/QualityAnalysis";
import { Maybe } from "$/utils/ts-utils";

export type IssueTemplate = {
categoryOptionComboId: Maybe<Id>;
countryId: Maybe<Id>;
dataElementId: Maybe<Id>;
description: string;
period: Maybe<Period>;
};

export class CreateIssueUseCase {
private issueUseCase: UCIssue;
private analysisUseCase: UCAnalysis;

constructor(
private analysisRepository: QualityAnalysisRepository,
private issueRepository: IssueRepository
) {
this.issueUseCase = new UCIssue(this.issueRepository);
this.analysisUseCase = new UCAnalysis(this.analysisRepository);
}

execute(options: CreateIssueUseCaseOptions): FutureData<Maybe<QualityAnalysis>> {
const { qualityAnalysisId, sectionId, issues } = options;

if (issues.length <= 0) return Future.success(undefined);

return this.fetchAnalysisAndTotalIssues(qualityAnalysisId, sectionId).flatMap(
analysisAndTotalIssues => {
const issuesToCreate = this.buildIssues({
issues,
sectionId,
...analysisAndTotalIssues,
});
const analysisUpdate = this.analysisUseCase.updateAnalysis(
analysisAndTotalIssues.analysis,
sectionId,
issues.length + analysisAndTotalIssues.totalIssues
);
return this.saveIssuesAndUpdateAnalysis(issuesToCreate, analysisUpdate);
}
);
}

private fetchAnalysisAndTotalIssues(
qualityAnalysisId: Id,
sectionId: Id
): FutureData<AnalysisAndTotalIssues> {
return this.analysisUseCase
.getById(qualityAnalysisId)
.flatMap(analysis =>
this.issueUseCase
.getTotalIssuesBySection(analysis, sectionId)
.map(totalIssues => ({ analysis, totalIssues }))
);
}

private buildIssues(
params: Omit<CreateIssueUseCaseOptions, "qualityAnalysisId"> & AnalysisAndTotalIssues
): QualityAnalysisIssue[] {
const { issues, sectionId, analysis, totalIssues } = params;

const sectionNumber = this.issueUseCase.getSectionNumber(analysis.sections, sectionId);
const prefix = `${analysis.sequential.value}-${sectionNumber}`;

return issues.map((issue, index) => {
const currentNumber = totalIssues + 1 + index;
const issueNumber = this.issueUseCase.generateIssueNumber(currentNumber, prefix);
return this.issueUseCase.buildDefaultIssue(
{
categoryOptionComboId: issue.categoryOptionComboId,
countryId: issue.countryId,
dataElementId: issue.dataElementId,
period: issue.period,
description: issue.description,
correlative: String(currentNumber),
issueNumber: issueNumber,
},
sectionId
);
});
}

private saveIssuesAndUpdateAnalysis(
issues: QualityAnalysisIssue[],
analysis: QualityAnalysis
): FutureData<QualityAnalysis> {
return this.issueUseCase
.save(issues, analysis.id)
.flatMap(() => this.analysisRepository.save([analysis]).map(() => analysis));
}
}

type CreateIssueUseCaseOptions = {
issues: IssueTemplate[];
qualityAnalysisId: Id;
sectionId: Id;
};

type AnalysisAndTotalIssues = { analysis: QualityAnalysis; totalIssues: number };
5 changes: 3 additions & 2 deletions src/domain/usecases/GetModulesUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { FutureData } from "$/data/api-futures";
import { Module } from "$/domain/entities/Module";
import { ModuleRepository } from "$/domain/repositories/ModuleRepository";
import { Id } from "$/domain/entities/Ref";

export class GetModulesUseCase {
constructor(private moduleRepository: ModuleRepository) {}

execute(): FutureData<Module[]> {
return this.moduleRepository.get();
execute(ids?: Id[]): FutureData<Module[]> {
return ids?.length ? this.moduleRepository.getByIds(ids) : this.moduleRepository.get();
}
}
Loading