Skip to content

Commit

Permalink
Flow search services
Browse files Browse the repository at this point in the history
Nested needed services to complete Flow response
  • Loading branch information
manelcecs committed Apr 26, 2024
1 parent dd98672 commit fc78c44
Show file tree
Hide file tree
Showing 17 changed files with 2,072 additions and 0 deletions.
205 changes: 205 additions & 0 deletions src/domain-services/categories/category-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { type Database } from '@unocha/hpc-api-core/src/db';
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import {
Cond,
Op,
type Condition,
} from '@unocha/hpc-api-core/src/db/util/conditions';
import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model';
import { Service } from 'typedi';
import { type ReportDetail } from '../report-details/graphql/types';
import { type Category } from './graphql/types';

@Service()
export class CategoryService {
async getCategoriesForFlows(
flowWithVersion: Map<FlowId, number[]>,
models: Database
): Promise<Map<number, Map<number, Category[]>>> {
// Group of flowIDs and its versions
// Structure:
// flowID: {
// versionID: [categories]
// }
const flowVersionCategoryMap = new Map<number, Map<number, Category[]>>();

const flowIDs: FlowId[] = [];
for (const flowID of flowWithVersion.keys()) {
flowIDs.push(flowID);
}

const categoriesRef: Array<InstanceDataOfModel<Database['categoryRef']>> =
await models.categoryRef.find({
where: {
objectID: {
[Op.IN]: flowIDs,
},
objectType: 'flow',
},
});

const categories: Array<InstanceDataOfModel<Database['category']>> =
await models.category.find({
where: {
id: {
[Op.IN]: categoriesRef.map((catRef) => catRef.categoryID),
},
},
});

// Populate the map with categories for each flow
for (const catRef of categoriesRef) {
const flowId = catRef.objectID.valueOf();

if (!flowVersionCategoryMap.has(flowId)) {
flowVersionCategoryMap.set(flowId, new Map());
}

// Here the key is the versionID of the flow
const flowVersionMap = flowVersionCategoryMap.get(flowId)!;

const flowVersion = catRef.versionID;
if (!flowVersionMap.has(flowVersion)) {
flowVersionMap.set(flowVersion, []);
}

const categoriesPerFlowVersion = flowVersionMap.get(flowVersion)!;

const category = categories.find((cat) => cat.id === catRef.categoryID);

if (
category &&
!categoriesPerFlowVersion.some(
(cat) => cat.id === category.id.valueOf()
)
) {
const mappedCategory = this.mapCategoryToFlowCategory(category, catRef);
categoriesPerFlowVersion.push(mappedCategory);
}
}

return flowVersionCategoryMap;
}

private mapCategoryToFlowCategory(
category: InstanceDataOfModel<Database['category']>,
categoryRef: InstanceDataOfModel<Database['categoryRef']>
): Category {
return {
id: category.id,
name: category.name,
group: category.group,
createdAt: category.createdAt.toISOString(),
updatedAt: category.updatedAt.toISOString(),
description: category.description ?? '',
parentID: category.parentID ? category.parentID.valueOf() : null,
code: category.code ?? '',
includeTotals: category.includeTotals ?? false,
categoryRef: {
objectID: categoryRef.objectID.valueOf(),
versionID: categoryRef.versionID,
objectType: categoryRef.objectType,
categoryID: category.id.valueOf(),
createdAt: categoryRef.createdAt.toISOString(),
updatedAt: categoryRef.updatedAt.toISOString(),
},
versionID: categoryRef.versionID,
};
}

async findCategories(models: Database, where: any) {
const category = await models.category.find({
where,
});

return category;
}

async findCategoryRefs(models: Database, where: any) {
const categoryRef = await models.categoryRef.find({
where,
});

return categoryRef;
}

async addChannelToReportDetails(
models: Database,
reportDetails: ReportDetail[]
): Promise<ReportDetail[]> {
const listOfCategoryRefORs: Array<{
objectID: number;
objectType: string;
}> = [];

for (const reportDetail of reportDetails) {
const orClause = {
objectID: reportDetail.id,
objectType: 'reportDetail',
};

listOfCategoryRefORs.push(orClause);
}

const categoriesRef: Array<InstanceDataOfModel<Database['categoryRef']>> =
await models.categoryRef.find({
where: {
[Cond.OR]: listOfCategoryRefORs as Array<
Condition<InstanceDataOfModel<Database['categoryRef']>>
>,
},
});

const mapOfCategoriesAndReportDetails = new Map<number, ReportDetail[]>();

for (const categoryRef of categoriesRef) {
const reportDetail = reportDetails.find(
(reportDetail) => reportDetail.id === categoryRef.objectID.valueOf()
);

if (!reportDetail) {
continue;
}

if (
!mapOfCategoriesAndReportDetails.has(categoryRef.categoryID.valueOf())
) {
mapOfCategoriesAndReportDetails.set(
categoryRef.categoryID.valueOf(),
[]
);
}

const reportDetailsPerCategory = mapOfCategoriesAndReportDetails.get(
categoryRef.categoryID.valueOf()
)!;
reportDetailsPerCategory.push(reportDetail);
}

const categories: Array<InstanceDataOfModel<Database['category']>> =
await models.category.find({
where: {
id: {
[Op.IN]: categoriesRef.map((catRef) => catRef.categoryID),
},
},
});

for (const [
category,
reportDetails,
] of mapOfCategoriesAndReportDetails.entries()) {
const categoryObj = categories.find((cat) => cat.id === category);

if (!categoryObj) {
continue;
}

for (const reportDetail of reportDetails) {
reportDetail.channel = categoryObj.name;
}
}

return reportDetails;
}
}
35 changes: 35 additions & 0 deletions src/domain-services/categories/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { type Op } from '@unocha/hpc-api-core/src/db/util/conditions';

export type CategoryGroup =
| 'beneficiaryGroup'
| 'contributionStatus'
| 'contributionType'
| 'customLocation'
| 'earmarkingType'
| 'emergencyType'
| 'flowStatus'
| 'flowType'
| 'genderMarker'
| 'inactiveReason'
| 'keywords'
| 'method'
| 'organizationLevel'
| 'organizationType'
| 'pendingStatus'
| 'planCosting'
| 'planIndicated'
| 'planType'
| 'projectGrouping1'
| 'projectGrouping2'
| 'projectPriority'
| 'regions'
| 'reportChannel'
| 'responseType'
| 'sectorIASC'
| 'subsetOfPlan';

export type ShortcutCategoryFilter = {
category: string;
operation: typeof Op.IN | typeof Op.NOT_IN;
id: number;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { type Database } from '@unocha/hpc-api-core/src/db';
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { type InstanceDataOfModel } from '@unocha/hpc-api-core/src/db/util/raw-model';
import { createBrandedValue } from '@unocha/hpc-api-core/src/util/types';
import { Service } from 'typedi';
import { type FlowExternalReference } from '../flows/graphql/types';
import { type UniqueFlowEntity } from '../flows/model';
import { type SystemID } from '../report-details/graphql/types';

@Service()
export class ExternalReferenceService {
async getExternalReferencesForFlows(flowIDs: FlowId[], models: Database) {
const externalReferences = await models.externalReference.find({
where: {
flowID: {
[Op.IN]: flowIDs,
},
},
skipValidation: true,
});

const externalReferencesMap = new Map<number, any>();

for (const flowID of flowIDs) {
externalReferencesMap.set(flowID, []);
}

for (const externalReference of externalReferences) {
const flowID = externalReference.flowID;
const externalReferenceMapped =
this.mapExternalReferenceToExternalReferenceFlows(externalReference);

if (!externalReferencesMap.has(flowID)) {
externalReferencesMap.set(flowID, []);
}

externalReferencesMap.get(flowID).push(externalReferenceMapped);
}

return externalReferencesMap;
}

async getUniqueFlowIDsBySystemID(
models: Database,
systemID: SystemID
): Promise<UniqueFlowEntity[]> {
const externalRefences: Array<
InstanceDataOfModel<Database['externalReference']>
> = await models.externalReference.find({
where: {
systemID: systemID,
},
skipValidation: true,
});

const flowIDs: UniqueFlowEntity[] = [];

for (const reference of externalRefences) {
flowIDs.push(this.mapExternalDataToUniqueFlowEntity(reference));
}

return flowIDs;
}

private mapExternalReferenceToExternalReferenceFlows(
externalReference: InstanceDataOfModel<Database['externalReference']>
): FlowExternalReference {
return {
systemID: externalReference.systemID,
flowID: externalReference.flowID,
externalRecordID: externalReference.externalRecordID,
externalRecordDate: externalReference.externalRecordDate.toISOString(),
createdAt: externalReference.createdAt.toISOString(),
updatedAt: externalReference.updatedAt.toISOString(),
versionID: externalReference.versionID ?? 0,
};
}

private mapExternalDataToUniqueFlowEntity(
external: InstanceDataOfModel<Database['externalReference']>
): UniqueFlowEntity {
return {
id: createBrandedValue(external.flowID),
versionID: external.versionID ?? 1,
};
}
}
42 changes: 42 additions & 0 deletions src/domain-services/flow-link/flow-link-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { type FlowId } from '@unocha/hpc-api-core/src/db/models/flow';
import { type Database } from '@unocha/hpc-api-core/src/db/type';
import { Op } from '@unocha/hpc-api-core/src/db/util/conditions';
import { type InstanceOfModel } from '@unocha/hpc-api-core/src/db/util/types';
import { Service } from 'typedi';

@Service()
export class FlowLinkService {
async getFlowLinksForFlows(
flowIds: FlowId[],
models: Database
): Promise<Map<number, Array<InstanceOfModel<Database['flowLink']>>>> {
const flowLinks = await models.flowLink.find({
where: {
childID: {
[Op.IN]: flowIds,
},
},
});

// Group flowLinks by flow ID for easy mapping
const flowLinksMap = new Map<
number,
Array<InstanceOfModel<Database['flowLink']>>
>();

// Populate the map with flowLinks for each flow
for (const flowLink of flowLinks) {
const flowId = flowLink.childID.valueOf();

if (!flowLinksMap.has(flowId)) {
flowLinksMap.set(flowId, []);
}

const flowLinksForFlow = flowLinksMap.get(flowId)!;

flowLinksForFlow.push(flowLink);
}

return flowLinksMap;
}
}
Loading

0 comments on commit fc78c44

Please sign in to comment.