Skip to content

Commit 6d95ec6

Browse files
authored
Merge pull request #8 from EyeSeeTea/development
Development
2 parents c2ee2a3 + 8f212ca commit 6d95ec6

22 files changed

+301
-97
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,4 @@ cypress/videos/
3838

3939
# Custom
4040
bak
41+
NOTES*

README.md

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
## Introduction
22

3-
_d2-report_ provides the infrastructure to create DHIS2 reports with a React frontend.
4-
5-
Those reports developed an an standard webapp, and they can both be used as an standalone DHIS2 webapp or an standard HTML report (App: Reports).
6-
7-
Target DHIS2: 2.34.
3+
_d2-reports_ than can be used as an standalone DHIS2 webapp or an standard HTML report (App: Reports). DHIS2 versions tested: 2.34.
84

95
## Reports
106

@@ -15,12 +11,10 @@ This report shows data values for data sets `NHWA Module ...`. There are two kin
1511
1. Data values that have comments.
1612
2. Data values related pairs (value/comment), which are rendered as a single row. The pairing criteria is:
1713

18-
- Comment data element: `NHWA_Comment of Abc`.
14+
- Comment data element `NHWA_Comment of Abc`.
1915
- Value data element: `NHWA_Abc`.
2016

21-
The API endpoint `/dataValueSets` does not provide all the features we need, so we use a custom SQL View instead. It will be included in the metadata.
22-
23-
We use the data element group to put data elements in the same sections together. Note that only data elements belonging to a data element group will be displayed.
17+
The API endpoint `/dataValueSets` does not provide all the features we need, so we use a custom SQL View instead.
2418

2519
## Initial setup
2620

@@ -30,24 +24,24 @@ $ yarn install
3024

3125
## Development
3226

33-
Start development server at `http://localhost:8082` using `https://play.dhis2.org/2.34` as backend:
27+
Start the development server at `http://localhost:8082` using `https://play.dhis2.org/2.34` as a backend:
3428

3529
```
3630
$ PORT=8082 REACT_APP_DHIS2_BASE_URL="https://play.dhis2.org/2.34" yarn start
3731
```
3832

3933
## Deploy
4034

41-
Create standard report:
35+
Create an standard report:
4236

4337
```
44-
$ yarn build-report # Create dist/index.html
45-
$ yarn build-metadata -u 'user:password' http://dhis2-server.org # Creates dist/metadata.json
46-
$ yarn post-metadata -u 'user:password' http://dhis2-server.org # Posts dist/metadata.json
38+
$ yarn build-report # Creates dist/index.html
39+
$ yarn build-metadata -u 'user:pass' http://dhis2-server.org # Creates dist/metadata.json
40+
$ yarn post-metadata -u 'user:pass' http://dhis2-server.org # Posts dist/metadata.json
4741
```
4842

49-
Create web-app zip (`dist/d2-reports.zip`):
43+
Create an standalone DHIS2 webapp app:
5044

5145
```
52-
$ yarn build-webapp
46+
$ yarn build-webapp # Creates dist/d2-reports.zip
5347
```

i18n/en.pot

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ msgstr ""
55
"Content-Type: text/plain; charset=utf-8\n"
66
"Content-Transfer-Encoding: 8bit\n"
77
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
8-
"POT-Creation-Date: 2020-11-18T10:58:48.575Z\n"
9-
"PO-Revision-Date: 2020-11-18T10:58:48.575Z\n"
8+
"POT-Creation-Date: 2021-03-05T08:55:57.149Z\n"
9+
"PO-Revision-Date: 2021-03-05T08:55:57.149Z\n"
1010

1111
msgid "Periods"
1212
msgstr ""
@@ -17,6 +17,9 @@ msgstr ""
1717
msgid "Sections"
1818
msgstr ""
1919

20+
msgid "NHWA Comments Report"
21+
msgstr ""
22+
2023
msgid "Data set"
2124
msgstr ""
2225

@@ -47,6 +50,18 @@ msgstr ""
4750
msgid "Stored by"
4851
msgstr ""
4952

53+
msgid "Toggle filters"
54+
msgstr ""
55+
56+
msgid "Loading..."
57+
msgstr ""
58+
59+
msgid "Select parent organisation unit"
60+
msgstr ""
61+
62+
msgid "Close"
63+
msgstr ""
64+
5065
msgid "<No value>"
5166
msgstr ""
5267

i18n/es.po

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
msgid ""
22
msgstr ""
33
"Project-Id-Version: i18next-conv\n"
4-
"POT-Creation-Date: 2020-11-18T10:58:48.575Z\n"
4+
"POT-Creation-Date: 2021-03-05T08:55:57.149Z\n"
55
"PO-Revision-Date: 2018-10-25T09:02:35.143Z\n"
66
"MIME-Version: 1.0\n"
77
"Content-Type: text/plain; charset=UTF-8\n"
@@ -17,6 +17,9 @@ msgstr ""
1717
msgid "Sections"
1818
msgstr ""
1919

20+
msgid "NHWA Comments Report"
21+
msgstr ""
22+
2023
msgid "Data set"
2124
msgstr ""
2225

@@ -47,6 +50,18 @@ msgstr ""
4750
msgid "Stored by"
4851
msgstr ""
4952

53+
msgid "Toggle filters"
54+
msgstr ""
55+
56+
msgid "Loading..."
57+
msgstr ""
58+
59+
msgid "Select parent organisation unit"
60+
msgstr ""
61+
62+
msgid "Close"
63+
msgstr ""
64+
5065
msgid "<No value>"
5166
msgstr ""
5267

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "d2-reports",
33
"description": "DHIS2 Reports",
4-
"version": "0.0.2",
4+
"version": "0.0.4",
55
"license": "GPL-3.0",
66
"author": "EyeSeeTea team",
77
"homepage": ".",

src/compositionRoot.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,22 @@ import { GetDataValuesUseCase } from "./domain/usecases/GetDataValuesUseCase";
44
import { GetConfig } from "./domain/usecases/GetConfig";
55
import { Dhis2ConfigRepository } from "./data/Dhis2ConfigRepository";
66
import { SaveDataValuesUseCase } from "./domain/usecases/SaveDataValuesCsvUseCase";
7+
import { GetOrgUnitsUseCase } from "./domain/usecases/GetOrgUnitsUseCase";
8+
import { Dhis2OrgUnitsRepository } from "./data/Dhis2OrgUnitsRepository";
79

810
export function getCompositionRoot(api: D2Api) {
911
const configRepository = new Dhis2ConfigRepository(api);
1012
const dataValueRepository = new Dhis2DataValueRepository(api);
13+
const orgUnitsRepository = new Dhis2OrgUnitsRepository(api);
1114

1215
return {
1316
dataValues: {
1417
get: new GetDataValuesUseCase(dataValueRepository),
1518
save: new SaveDataValuesUseCase(dataValueRepository),
1619
},
20+
orgUnits: {
21+
get: new GetOrgUnitsUseCase(orgUnitsRepository),
22+
},
1723
config: {
1824
get: new GetConfig(configRepository),
1925
},

src/data/Dhis2ConfigRepository.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,16 @@ import { User } from "../domain/entities/User";
88
const base = {
99
dataSets: { namePrefix: "NHWA", nameExcluded: /old$/ },
1010
sqlViewName: "NHWA Data Comments",
11-
sectionOrderAttributeCode: "SECTION_ORDER",
1211
constantCode: "NHWA_COMMENTS",
1312
};
1413

1514
export class Dhis2ConfigRepository implements ConfigRepository {
1615
constructor(private api: D2Api) {}
1716

1817
async get(): Promise<Config> {
19-
const { dataSets, constants, sqlViews, attributes } = await this.getMetadata();
18+
const { dataSets, constants, sqlViews } = await this.getMetadata();
2019
const filteredDataSets = getFilteredDataSets(dataSets);
21-
const attributeCode = base.sectionOrderAttributeCode;
2220
const getDataValuesSqlView = getFirst(sqlViews, `Missing sqlView: ${base.sqlViewName}`);
23-
const sectionOrderAttribute = getFirst(attributes, `Missing attribute: ${attributeCode}`);
2421
const constant = getFirst(constants, `Missing constant: ${base.constantCode}`);
2522
const currentUser = await this.getCurrentUser();
2623
const pairedDataElements = getPairedMapping(filteredDataSets);
@@ -32,7 +29,6 @@ export class Dhis2ConfigRepository implements ConfigRepository {
3229
dataSets: keyById(filteredDataSets),
3330
currentUser,
3431
getDataValuesSqlView,
35-
sectionOrderAttribute,
3632
pairedDataElementsByDataSet: pairedDataElements,
3733
sections: keyById(sections),
3834
sectionsByDataSet,
@@ -60,10 +56,6 @@ export class Dhis2ConfigRepository implements ConfigRepository {
6056
fields: { id: true },
6157
filter: { name: { eq: base.sqlViewName } },
6258
},
63-
attributes: {
64-
fields: { id: true },
65-
filter: { code: { eq: base.sectionOrderAttributeCode } },
66-
},
6759
});
6860

6961
return metadata$.getData();

src/data/Dhis2DataValueRepository.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ interface Variables {
1717
periods: string;
1818
orderByColumn: SqlField;
1919
orderByDirection: "asc" | "desc";
20-
sectionOrderAttributeId: Id;
2120
commentPairs: string;
2221
}
2322

@@ -75,7 +74,6 @@ export class Dhis2DataValueRepository implements DataValueRepository {
7574
orderByColumn: fieldMapping[sorting.field],
7675
orderByDirection: sorting.direction,
7776
commentPairs,
78-
sectionOrderAttributeId: config.sectionOrderAttribute.id,
7977
},
8078
paging
8179
)

src/data/Dhis2OrgUnitsRepository.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { OrgUnitPath, OrgUnit, getOrgUnitIdsFromPaths } from "../domain/entities/OrgUnit";
2+
import { OrgUnitsRepository } from "../domain/repositories/OrgUnitsRepository";
3+
import { D2Api } from "../types/d2-api";
4+
5+
export class Dhis2OrgUnitsRepository implements OrgUnitsRepository {
6+
constructor(private api: D2Api) {}
7+
8+
async getFromPaths(paths: OrgUnitPath[]): Promise<OrgUnit[]> {
9+
const ids = getOrgUnitIdsFromPaths(paths);
10+
11+
const { organisationUnits } = await this.api.metadata
12+
.get({
13+
organisationUnits: {
14+
filter: { id: { in: ids } },
15+
fields: { id: true, path: true, name: true, level: true },
16+
},
17+
})
18+
.getData();
19+
20+
return organisationUnits;
21+
}
22+
}

src/domain/entities/Config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import _ from "lodash";
12
import { Id, NamedRef, Ref } from "./Base";
3+
import { getPath } from "./OrgUnit";
24
import { User } from "./User";
35

46
export interface Config {
57
dataSets: Record<Id, NamedRef>;
68
sections: Record<Id, NamedRef>;
7-
sectionOrderAttribute: Ref;
89
currentUser: User;
910
getDataValuesSqlView: Ref;
1011
pairedDataElementsByDataSet: {
@@ -15,3 +16,7 @@ export interface Config {
1516
};
1617
years: string[];
1718
}
19+
20+
export function getMainUserPaths(config: Config) {
21+
return _.compact([getPath(config.currentUser.orgUnits)]);
22+
}

src/domain/entities/OrgUnit.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import _ from "lodash";
22
import { Id } from "./Base";
33

4-
type Path = string;
4+
export type OrgUnitPath = string;
55

66
export interface OrgUnit {
7-
id: string;
8-
path: Path;
7+
id: Id;
8+
path: OrgUnitPath;
99
name: string;
1010
level: number;
1111
}
1212

13+
const pathSeparator = "/";
14+
1315
export function getRoots(orgUnits: OrgUnit[]): OrgUnit[] {
1416
const minLevel = _.min(orgUnits.map(ou => ou.level));
1517
return _(orgUnits)
@@ -22,13 +24,17 @@ export function getRootIds(orgUnits: OrgUnit[]): Id[] {
2224
return getRoots(orgUnits).map(ou => ou.id);
2325
}
2426

25-
export function getPath(orgUnits: OrgUnit[]): Path | undefined {
27+
export function getPath(orgUnits: OrgUnit[]): OrgUnitPath | undefined {
2628
return getRoots(orgUnits).map(ou => ou.path)[0];
2729
}
2830

29-
export function getOrgUnitIdsFromPaths(orgUnitPathsSelected: Path[]): Id[] {
31+
export function getOrgUnitIdsFromPaths(orgUnitPathsSelected: OrgUnitPath[]): Id[] {
3032
return _(orgUnitPathsSelected)
31-
.map(path => _.last(path.split("/")))
33+
.map(path => _.last(path.split(pathSeparator)))
3234
.compact()
3335
.value();
3436
}
37+
38+
export function getOrgUnitParentPath(path: OrgUnitPath) {
39+
return _(path).split(pathSeparator).initial().join(pathSeparator);
40+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { OrgUnit, OrgUnitPath } from "../entities/OrgUnit";
2+
3+
export interface OrgUnitsRepository {
4+
getFromPaths(paths: OrgUnitPath[]): Promise<OrgUnit[]>;
5+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { OrgUnit, OrgUnitPath } from "../entities/OrgUnit";
2+
import { OrgUnitsRepository } from "../repositories/OrgUnitsRepository";
3+
4+
export class GetOrgUnitsUseCase {
5+
constructor(private orgUnitsRepository: OrgUnitsRepository) {}
6+
7+
execute(options: { paths: OrgUnitPath[] }): Promise<OrgUnit[]> {
8+
return this.orgUnitsRepository.getFromPaths(options.paths);
9+
}
10+
}

src/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ async function getBaseUrl() {
1515
return baseUrl.replace(/\/*$/, "");
1616
} else {
1717
const { data: manifest } = await axios.get("manifest.webapp");
18-
return manifest.activities.dhis.href;
18+
const href = manifest.activities.dhis.href;
19+
return href === "*" ? ".." : href;
1920
}
2021
}
2122

src/webapp/components/app/App.css

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,8 @@ body {
1111
li {
1212
line-height: 1.75;
1313
}
14+
15+
table th,
16+
table td {
17+
border: none !important;
18+
}

0 commit comments

Comments
 (0)