Skip to content

Commit

Permalink
Merge pull request #34 from EyeSeeTea/fix/local-timezone
Browse files Browse the repository at this point in the history
fix: local time zone fix, all last updated times
  • Loading branch information
bhavananarayanan authored Nov 11, 2024
2 parents 5fddf1d + 435d67e commit 0a809cf
Show file tree
Hide file tree
Showing 30 changed files with 277 additions and 81 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-10-16T14:36:22.158Z\n"
"PO-Revision-Date: 2024-10-16T14:36:22.158Z\n"
"POT-Creation-Date: 2024-11-03T18:35:32.151Z\n"
"PO-Revision-Date: 2024-11-03T18:35:32.151Z\n"

msgid "Low"
msgstr ""
Expand Down Expand Up @@ -87,9 +87,15 @@ msgstr ""
msgid "Edit Action Plan"
msgstr ""

msgid "Event completed"
msgstr ""

msgid "Edit Details"
msgstr ""

msgid "Complete Event"
msgstr ""

msgid "Notes"
msgstr ""

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dotenv": "^16.4.5",
"font-awesome": "4.7.0",
"moment": "^2.30.1",
"moment-timezone": "^0.5.46",
"purify-ts": "1.2.0",
"purify-ts-extra-codec": "0.6.0",
"react": "^18.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/CompositionRoot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import { GetConfigurationsUseCase } from "./domain/usecases/GetConfigurationsUse
import { ConfigurationsRepository } from "./domain/repositories/ConfigurationsRepository";
import { ConfigurationsD2Repository } from "./data/repositories/ConfigurationsD2Repository";
import { ConfigurationsTestRepository } from "./data/repositories/test/ConfigurationsTestRepository";
import { CompleteEventTrackerUseCase } from "./domain/usecases/CompleteEventTrackerUseCase";

export type CompositionRoot = ReturnType<typeof getCompositionRoot>;

Expand Down Expand Up @@ -106,6 +107,7 @@ function getCompositionRoot(repositories: Repositories) {
repositories.configurationsRepository,
repositories.teamMemberRepository
),
complete: new CompleteEventTrackerUseCase(repositories),
},
incidentActionPlan: {
get: new GetIncidentActionByIdUseCase(repositories),
Expand Down
43 changes: 40 additions & 3 deletions src/data/repositories/DiseaseOutbreakEventD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { D2Api } from "../../types/d2-api";
import { DiseaseOutbreakEventRepository } from "../../domain/repositories/DiseaseOutbreakEventRepository";
import { apiToFuture, FutureData } from "../api-futures";
import { DiseaseOutbreakEventBaseAttrs } from "../../domain/entities/disease-outbreak-event/DiseaseOutbreakEvent";
import { Id, ConfigLabel } from "../../domain/entities/Ref";
import { Id } from "../../domain/entities/Ref";
import {
mapDiseaseOutbreakEventToTrackedEntityAttributes,
mapTrackedEntityAttributesToDiseaseOutbreak,
Expand All @@ -13,6 +13,7 @@ import { getProgramTEAsMetadata } from "./utils/MetadataHelper";
import { assertOrError } from "./utils/AssertOrError";
import { Future } from "../../domain/entities/generic/Future";
import { getAllTrackedEntitiesAsync } from "./utils/getAllTrackedEntities";
import { D2TrackerEnrollment } from "@eyeseetea/d2-api/api/trackerEnrollments";

export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRepository {
constructor(private api: D2Api) {}
Expand Down Expand Up @@ -84,8 +85,44 @@ export class DiseaseOutbreakEventD2Repository implements DiseaseOutbreakEventRep
);
}

getConfigStrings(): FutureData<ConfigLabel[]> {
throw new Error("Method not implemented.");
complete(id: Id): FutureData<void> {
return apiToFuture(
this.api.tracker.enrollments.get({
fields: {
enrollment: true,
enrolledAt: true,
occurredAt: true,
},
trackedEntity: id,
enrolledBefore: new Date().toISOString(),
program: RTSL_ZEBRA_PROGRAM_ID,
orgUnit: RTSL_ZEBRA_ORG_UNIT_ID,
})
).flatMap(enrollmentResponse => {
const currentEnrollment = enrollmentResponse.instances[0];
const currentEnrollmentId = currentEnrollment?.enrollment;
if (!currentEnrollment || !currentEnrollmentId) {
return Future.error(new Error(`Enrollment not found for Event Tracker`));
}

const enrollment: D2TrackerEnrollment = {
...currentEnrollment,
orgUnit: RTSL_ZEBRA_ORG_UNIT_ID,
program: RTSL_ZEBRA_PROGRAM_ID,
trackedEntity: id,
status: "COMPLETED",
};

return apiToFuture(
this.api.tracker.post({ importStrategy: "UPDATE" }, { enrollments: [enrollment] })
).flatMap(response => {
if (response.status !== "OK") {
return Future.error(
new Error(`Error completing disease outbreak event : ${response.message}`)
);
} else return Future.success(undefined);
});
});
}

//TO DO : Implement delete/archive after requirement confirmation
Expand Down
5 changes: 4 additions & 1 deletion src/data/repositories/IncidentActionD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const incidentActionPlanIds = {
} as const;

export type IncidentActionPlanDataValues = {
lastUpdated: Maybe<Date>;
id: string;
iapType: Maybe<string>;
phoecLevel: Maybe<string>;
Expand Down Expand Up @@ -76,6 +77,7 @@ export class IncidentActionD2Repository implements IncidentActionRepository {

private fields = {
event: true,
updatedAt: true,
dataValues: {
dataElement: { id: true, code: true },
value: true,
Expand All @@ -97,7 +99,8 @@ export class IncidentActionD2Repository implements IncidentActionRepository {

const plan: IncidentActionPlanDataValues = mapDataElementsToIncidentActionPlan(
events.instances[0].event,
events.instances[0].dataValues
events.instances[0].dataValues,
events.instances[0].updatedAt
);

return plan;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export class IncidentManagementTeamD2Repository implements IncidentManagementTea
},
trackedEntity: true,
event: true,
updatedAt: true,
},
})
)
Expand Down
29 changes: 10 additions & 19 deletions src/data/repositories/SystemD2Repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { D2Api } from "@eyeseetea/d2-api/2.36";
import { SystemRepository } from "../../domain/repositories/SystemRepository";
import { apiToFuture, FutureData } from "../api-futures";
import { getDateAsLocaleDateTimeString } from "./utils/DateTimeHelper";
import "moment-timezone";
import moment from "moment";

export class SystemD2Repository implements SystemRepository {
constructor(private api: D2Api) {}
Expand All @@ -12,29 +14,18 @@ export class SystemD2Repository implements SystemRepository {

//@ts-ignore
const lastAnalyticsTablePartitionSuccess = info.lastAnalyticsTablePartitionSuccess;

//If continious analytics is turned on, return it.
if (
info.lastAnalyticsTableSuccess &&
lastAnalyticsTablePartitionSuccess &&
new Date(lastAnalyticsTablePartitionSuccess) >
new Date(info.lastAnalyticsTableSuccess)
) {
return getDateAsLocaleDateTimeString(new Date(lastAnalyticsTablePartitionSuccess));
}
//Else, return the lastAnalyticsTableSuccess time
else if (info.lastAnalyticsTableSuccess) {
return getDateAsLocaleDateTimeString(new Date(info.lastAnalyticsTableSuccess));
if (lastAnalyticsTablePartitionSuccess) {
const lastAnalyticsTablePartitionSuccessUTCString = moment
.tz(lastAnalyticsTablePartitionSuccess, info.serverTimeZoneId)
.utc()
.toString();

return getDateAsLocaleDateTimeString(lastAnalyticsTablePartitionSuccessUTCString);
} else {
return "Unable to fetch last analytics runtime";
}

//@ts-ignore
if (info.lastAnalyticsTablePartitionSuccess)
return getDateAsLocaleDateTimeString(
//@ts-ignore
new Date(info.lastAnalyticsTablePartitionSuccess)
);
else return "Unable to fetch last analytics runtime";
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { DiseaseOutbreakEventRepository } from "../../../domain/repositories/Dis
import { FutureData } from "../../api-futures";

export class DiseaseOutbreakEventTestRepository implements DiseaseOutbreakEventRepository {
complete(_id: Id): FutureData<void> {
return Future.success(undefined);
}
get(id: Id): FutureData<DiseaseOutbreakEventBaseAttrs> {
return Future.success({
id: id,
Expand Down
9 changes: 7 additions & 2 deletions src/data/repositories/utils/DateTimeHelper.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import moment from "moment";
import { Maybe } from "../../../utils/ts-utils";

export function getCurrentTimeString(): string {
Expand Down Expand Up @@ -26,9 +27,9 @@ export function getDateAsMonthYearString(date: Date): string {
}
}

export function getDateAsLocaleDateTimeString(date: Date): string {
export function getDateAsLocaleDateTimeString(date: string): string {
try {
return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`;
return moment(date).local().toDate().toString();
} catch (e) {
console.debug(e);
return "";
Expand All @@ -43,3 +44,7 @@ export function getDateAsLocaleDateString(date: Date): string {
return "";
}
}

export function getISODateAsLocaleDateString(date: string): Date {
return moment.utc(date).local().toDate();
}
10 changes: 7 additions & 3 deletions src/data/repositories/utils/DiseaseOutbreakMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import _ from "../../../domain/entities/generic/Collection";
import { SelectedPick } from "@eyeseetea/d2-api/api";
import { D2TrackedEntityAttributeSchema } from "../../../types/d2-api";
import { D2TrackerEnrollment } from "@eyeseetea/d2-api/api/trackerEnrollments";
import { getCurrentTimeString } from "./DateTimeHelper";
import { getCurrentTimeString, getISODateAsLocaleDateString } from "./DateTimeHelper";

type D2TrackedEntityAttribute = {
trackedEntityAttribute: SelectedPick<
Expand Down Expand Up @@ -47,8 +47,12 @@ export function mapTrackedEntityAttributesToDiseaseOutbreak(
status: trackedEntity.enrollments?.[0]?.status ?? "ACTIVE", //Zebra Outbreak has only one enrollment
name: fromMap("name"),
dataSource: dataSource,
created: trackedEntity.createdAt ? new Date(trackedEntity.createdAt) : undefined,
lastUpdated: trackedEntity.updatedAt ? new Date(trackedEntity.updatedAt) : undefined,
created: trackedEntity.createdAt
? getISODateAsLocaleDateString(trackedEntity.createdAt)
: undefined,
lastUpdated: trackedEntity.updatedAt
? getISODateAsLocaleDateString(trackedEntity.updatedAt)
: undefined,
createdByName: undefined,
hazardType: getHazardTypeByCode(fromMap("hazardType")),
mainSyndromeCode: fromMap("mainSyndrome"),
Expand Down
4 changes: 3 additions & 1 deletion src/data/repositories/utils/IncidentActionMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import {

export function mapDataElementsToIncidentActionPlan(
id: Id,
dataValues: DataValue[]
dataValues: DataValue[],
updatedAt: Maybe<string>
): IncidentActionPlanDataValues {
const iapType = getValueById(dataValues, incidentActionPlanIds.iapType);
const phoecLevel = getValueById(dataValues, incidentActionPlanIds.phoecLevel);
Expand All @@ -59,6 +60,7 @@ export function mapDataElementsToIncidentActionPlan(
responseStrategies: responseStrategies,
expectedResults: expectedResults,
responseActivitiesNarrative: responseActivitiesNarrative,
lastUpdated: updatedAt ? new Date(updatedAt) : undefined,
};

return incidentActionPlan;
Expand Down
10 changes: 10 additions & 0 deletions src/data/repositories/utils/IncidentManagementTeamMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { SelectedPick } from "@eyeseetea/d2-api/api";
import { D2DataElementSchema } from "@eyeseetea/d2-api/2.36";
import { RTSL_ZEBRA_INCIDENT_MANAGEMENT_TEAM_BUILDER_IDS_WITHOUT_ROLES } from "../consts/IncidentManagementTeamBuilderConstants";
import { Role } from "../../../domain/entities/incident-management-team/Role";
import { getISODateAsLocaleDateString } from "./DateTimeHelper";

export function mapD2EventsToIncidentManagementTeam(
d2Events: D2TrackerEvent[],
Expand Down Expand Up @@ -48,8 +49,17 @@ export function mapD2EventsToIncidentManagementTeam(
[]
);

const sortedByUpdatedDates = d2Events.sort(function (a, b) {
if (!a.updatedAt) return -1;
if (!b.updatedAt) return 1;
return a.updatedAt > b.updatedAt ? -1 : a.updatedAt < b.updatedAt ? 1 : 0;
});

return new IncidentManagementTeam({
teamHierarchy: teamHierarchy,
lastUpdated: sortedByUpdatedDates[0]?.updatedAt
? getISODateAsLocaleDateString(sortedByUpdatedDates[0]?.updatedAt)
: undefined,
});
}

Expand Down
2 changes: 2 additions & 0 deletions src/domain/entities/incident-action-plan/ActionPlan.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { Maybe } from "../../../utils/ts-utils";
import { Struct } from "../generic/Struct";
import { Id } from "../Ref";

export type ActionPlanIAPType = "Initial" | "Update" | "Final";
export type ActionPlanPhoecLevel = "Response" | "Watch" | "Alert";

export type ActionPlanAttrs = {
lastUpdated: Maybe<Date>;
id: Id;
iapType: string;
phoecLevel: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export type IncidentActionOptions = {
};

interface IncidentActionPlanAttrs extends Ref {
lastUpdated: Date;
actionPlan: Maybe<ActionPlan>;
responseActions: ResponseAction[];
incidentActionOptions: IncidentActionOptions;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Maybe } from "../../../utils/ts-utils";
import { Struct } from "../generic/Struct";
import { TeamMember } from "./TeamMember";

interface IncidentManagementTeamAttrs {
lastUpdated: Maybe<Date>;
teamHierarchy: TeamMember[];
}

Expand Down
4 changes: 2 additions & 2 deletions src/domain/repositories/DiseaseOutbreakEventRepository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { FutureData } from "../../data/api-futures";
import { DiseaseOutbreakEventBaseAttrs } from "../entities/disease-outbreak-event/DiseaseOutbreakEvent";
import { ConfigLabel, Id } from "../entities/Ref";
import { Id } from "../entities/Ref";

export interface DiseaseOutbreakEventRepository {
get(id: Id): FutureData<DiseaseOutbreakEventBaseAttrs>;
getAll(): FutureData<DiseaseOutbreakEventBaseAttrs[]>;
save(diseaseOutbreak: DiseaseOutbreakEventBaseAttrs): FutureData<Id>;
getConfigStrings(): FutureData<ConfigLabel[]>;
complete(id: Id): FutureData<void>;
}
15 changes: 15 additions & 0 deletions src/domain/usecases/CompleteEventTrackerUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FutureData } from "../../data/api-futures";
import { Id } from "../entities/Ref";
import { DiseaseOutbreakEventRepository } from "../repositories/DiseaseOutbreakEventRepository";

export class CompleteEventTrackerUseCase {
constructor(
private options: {
diseaseOutbreakEventRepository: DiseaseOutbreakEventRepository;
}
) {}

public execute(id: Id): FutureData<void> {
return this.options.diseaseOutbreakEventRepository.complete(id);
}
}
18 changes: 3 additions & 15 deletions src/domain/usecases/GetDiseaseOutbreakByIdUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import { OrgUnitRepository } from "../repositories/OrgUnitRepository";
import { RiskAssessmentRepository } from "../repositories/RiskAssessmentRepository";
import { RoleRepository } from "../repositories/RoleRepository";
import { TeamMemberRepository } from "../repositories/TeamMemberRepository";
import { getIncidentAction } from "./utils/incident-action/GetIncidentActionById";
import { getIncidentManagementTeamById } from "./utils/incident-management-team/GetIncidentManagementTeamById";
import { getAll } from "./utils/risk-assessment/GetRiskAssessmentById";

export class GetDiseaseOutbreakByIdUseCase {
Expand Down Expand Up @@ -62,18 +60,8 @@ export class GetDiseaseOutbreakByIdUseCase {
this.options.riskAssessmentRepository,
configurations
),
incidentAction: getIncidentAction(
id,
this.options.incidentActionRepository,
configurations
),
incidentManagementTeam: getIncidentManagementTeamById(
id,
this.options,
configurations
),
roles: this.options.roleRepository.getAll(),
}).flatMap(({ riskAssessment, incidentAction, incidentManagementTeam, roles }) => {
}).flatMap(({ riskAssessment, roles }) => {
return this.options.incidentManagementTeamRepository
.getIncidentManagementTeamMember(incidentManagerName, id, roles)
.flatMap(incidentManager => {
Expand All @@ -86,8 +74,8 @@ export class GetDiseaseOutbreakByIdUseCase {
notificationSource: notificationSource,
incidentManager: incidentManager,
riskAssessment: riskAssessment,
incidentActionPlan: incidentAction,
incidentManagementTeam: incidentManagementTeam,
incidentActionPlan: undefined, //IAP is fetched on menu click. It is not needed here.
incidentManagementTeam: undefined, //IMT is fetched on menu click. It is not needed here.
});
return Future.success(diseaseOutbreakEvent);
});
Expand Down
Loading

0 comments on commit 0a809cf

Please sign in to comment.