Skip to content

Commit

Permalink
Merge branch 'develop' into 532-support-drug-interactions-2
Browse files Browse the repository at this point in the history
  • Loading branch information
jmbrunskill committed Feb 1, 2024
2 parents 0de6382 + a416563 commit 3140ef8
Show file tree
Hide file tree
Showing 8 changed files with 869 additions and 706 deletions.
109 changes: 109 additions & 0 deletions data-loader/data/v2/config_items.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
Forms,Immediate packaging,Routes
Bar,Aerosol,Buccal
Block,Ampoule,Inhalation
Capsule,Applicator,Intramuscular
Capsule (enteric coated),Bag,Intrathecal
Capsule (hard),Blister pack,Intratracheal
Capsule (modified release),Bottle,Intrauterine
Cement,Can,Intraperitoneal
Chewing gum,Carton,Intravenous
Collodion,Cartridge,Nasal
Cream,Chamber,Ocular
Cream (modified),Device,Oral
Cream (vaginal),Dry powder inhaler,Otic
Drug delivery system (intrauterine),Inhaler,Rectal
Drug delivery system (vaginal),Metered dose inhaler,Subcutaneous
Ear drop,Nebule,Subdermal
Ear/eye drop,Pre-filled syringe,Sublingual
Enema,Pump actuated aerosol,Topical
Eye drop,Sachet,Transdermal
Eye drop (gel forming),Spray,Vaginal
Eye gel,Tube,
Eye ointment,Vial,
Eye solution,,
Eye/ear ointment,,
Foam,,
Gas,,
Gel,,
Gel (intestinal),,
Gel (modified release),,
Granules,,
Granules (effervescent),,
Granules (modified release),,
Implant,,
Infusion,,
Infusion (powder for),,
Inhalation,,
Inhalation (breath activated),,
Inhalation (powder for),,
Inhalation (pressurised),,
Inhalation (solution),,
Injection,,
Injection (concentrated),,
Injection (emulsion),,
Injection (intra-ocular),,
Injection (intrathecal),,
Injection (modified release),,
Injection (powder for),,
Injection (suspension),,
Injection (solution),,
Jelly (lubricating),,
Liniment,,
Liquid,,
Lotion,,
Lozenge,,
Mouth wash,,
Nasal drop,,
Nasal spray,,
Nsal gel,,
Oil,,
Oil (bath),,
Ointment,,
Ointment (modified),,
Oral application,,
Oral gel,,
Oral liquid,,
Oral liquid (powder for),,
Oral spray,,
Paint,,
Paste,,
Paste (oromucosal),,
Pastille,,
Pessary,,
Pessary (modified release),,
Powder,,
Powder (oral),,
Shampoo,,
Soap,,
Solution,,
Solution (concentrated dialysis),,
Solution (dialysis),,
Solution (irrigation),,
Solution (peritoneal dialysis),,
Solution (powder for dialysis),,
Solution (powder for),,
Spray,,
Spray (pressurised),,
Stick,,
Strip (diagnostic),,
Suppository,,
Suspension,,
Swab,,
Tablet,,
Tablet (buccal),,
Tablet (chewable),,
Tablet (compound diagnostic),,
Tablet (dispersible),,
Tablet (effervescent),,
Tablet (enteric coated),,
Tablet (gelatine coated),,
Tablet (modified release),,
Tablet (oral disintegrating),,
Tablet (soluble),,
Tablet (sublingual),,
Tincture,,
Toothpaste,,
Topical application,,
Topical liquid,,
Transdermal patch,,
Wafer,,
1,196 changes: 598 additions & 598 deletions data-loader/data/v2/drugs.csv

Large diffs are not rendered by default.

110 changes: 55 additions & 55 deletions data-loader/data/v2/vaccines.csv

Large diffs are not rendered by default.

12 changes: 11 additions & 1 deletion data-loader/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import path from 'path';
import { DataLoader, DataParser, SchemaParser } from './v2';

import propertyConfigItems from '../data/v2/properties.json';
import { ConfigItemsDataParser } from './v2/ConfigItemsParser';

const hostname = 'localhost';
const port = '9080';
Expand All @@ -12,6 +13,7 @@ const schemaPath = 'v2/schema.gql';
const drugDataPath = 'v2/drugs.csv';
const consumableDataPath = 'v2/consumables.csv';
const vaccineDataPath = 'v2/vaccines.csv';
const configItemsDataPath = 'v2/config_items.csv';

const schemaFile = path.resolve(__dirname, `${dirPath}/${schemaPath}`);
const drugDataFile = path.resolve(__dirname, `${dirPath}/${drugDataPath}`);
Expand All @@ -23,6 +25,10 @@ const vaccineDataFile = path.resolve(
__dirname,
`${dirPath}/${vaccineDataPath}`
);
const configItemsDataFile = path.resolve(
__dirname,
`${dirPath}/${configItemsDataPath}`
);

const main = async () => {
const schemaParser = new SchemaParser(schemaFile);
Expand All @@ -34,17 +40,21 @@ const main = async () => {
vaccines: vaccineDataFile,
});

const configItemsParser = new ConfigItemsDataParser(configItemsDataFile);

await dataParser.parseData();
await configItemsParser.parseData();

dataParser.buildGraph();

if (dataParser.isValid()) {
const schema = schemaParser.getSchema();
const graph = dataParser.getGraph();
const configItems = configItemsParser.getItems();

try {
const loader = new DataLoader(hostname, port);
await loader.load(schema, graph, propertyConfigItems);
await loader.load(schema, graph, configItems, propertyConfigItems);
} catch (err) {
console.log(`Failed to load data due to following error: ${err}`);
}
Expand Down
80 changes: 80 additions & 0 deletions data-loader/src/v2/ConfigItemsParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import * as fs from 'fs';
import csv from 'csv-parser';
import { ConfigItems } from './types';

export class ConfigItemsDataParser {
public readonly path: fs.PathLike;

private data: ConfigItems;

private isParsed: boolean;
private isBuilt: boolean;

constructor(path: fs.PathLike) {
this.path = path;

this.isParsed = false;
this.isBuilt = false;

this.data = {
forms: [],
immediatePackaging: [],
routes: [],
};
}

public async parseData(): Promise<ConfigItems> {
if (this.isParsed) return this.data;

this.data = {
forms: [],
immediatePackaging: [],
routes: [],
};

const parseColumn = (column: string) => {
const REGEX = {
CR_LF: /[\r\n]/g,
BRACKETED_DESCRIPTION: / *\([^)]*\) */g,
SPACE: / /g,
};

return column
.trim()
.toLowerCase()
.replace(REGEX.CR_LF, '')
.replace(REGEX.BRACKETED_DESCRIPTION, '')
.replace(REGEX.SPACE, '_')
.split('_')
.map((word, index) =>
index === 0 ? word : word[0].toUpperCase() + word.slice(1)
)
.join('');
};

// Read data stream.
const stream = fs.createReadStream(this.path);
await new Promise(resolve => {
stream
.pipe(csv())
.on('data', (row: string) => {
Object.entries<string>(row).forEach(
([column, value]: [string, string]) => {
if (value) {
const key = parseColumn(column);
this.data[key].push(value);
}
}
);
})
.on('end', () => resolve(null));
});

this.isParsed = true;
return this.data;
}

public getItems(): ConfigItems {
return this.data;
}
}
62 changes: 10 additions & 52 deletions data-loader/src/v2/DataLoader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import dgraph from 'dgraph-js';

import { IEntityNode, IEntityGraph, PropertyConfigItem } from './types';
import {
IEntityNode,
IEntityGraph,
PropertyConfigItem,
ConfigItems,
} from './types';

export class DgraphClient {
public readonly host: string;
Expand Down Expand Up @@ -83,6 +88,7 @@ export class DataLoader {
public async load(
schema: string,
graph: IEntityGraph,
configItems: ConfigItems,
propertyConfigItems: PropertyConfigItem[]
): Promise<boolean> {
// Delete all existing data.
Expand All @@ -100,11 +106,6 @@ export class DataLoader {

console.log(`INFO: Extracted entity nodes`);

const routes = new Set();
const forms = new Set();
const formQualifiers = new Set();
const immediatePackagings = new Set();

// Load individual entities.
for await (const entity of entities) {
const query = `
Expand All @@ -131,18 +132,6 @@ export class DataLoader {
} else {
console.log(`WARNING: Failed to load entity with code ${entity.code}`);
}
if (entity.type === 'Route') routes.add(entity.name.trim());
if (entity.type === 'Form') forms.add(entity.name.trim());
if (
entity.type === 'FormQualifier' &&
entity.name &&
entity.name.trim() &&
entity.name.trim().length < 20
) {
formQualifiers.add(entity.name.trim());
}
if (entity.type === 'PackImmediate')
immediatePackagings.add(entity.name.trim());
}

// Link parent entities to children.
Expand Down Expand Up @@ -199,7 +188,7 @@ export class DataLoader {
}

// Create routes.
for await (const route of routes) {
for await (const route of configItems.routes) {
if (!route) continue;
const nQuads = `
_:route <name> "${route}" .
Expand All @@ -216,7 +205,7 @@ export class DataLoader {
}

// Create forms.
for await (const form of forms) {
for await (const form of configItems.forms) {
if (!form) continue;
const nQuads = `
_:form <name> "${form}" .
Expand All @@ -230,41 +219,10 @@ export class DataLoader {
} else {
console.log(`WARNING: Failed to load form with name ${form}`);
}

// Look to see if the form and qualifier combination exists in a entity description...
// If so we need to create a form with the qualifier attached

for await (const formQualifier of formQualifiers) {
if (!formQualifier) continue;
for await (const entity of entities) {
if (
entity.description &&
entity.description.includes(`${form} ${formQualifier}`)
) {
const nQuads = `
_:form <name> "${form} (${formQualifier})" .
_:form <code> "${form} (${formQualifier})" .
_:form <type> "Form" .
_:form <dgraph.type> "ConfigurationItem" .
`;

if (await this.dgraph.mutate(nQuads)) {
console.log(
`INFO: Loaded form qualifier with name ${formQualifier}`
);
} else {
console.log(
`WARNING: Failed to load form qualifier with name ${formQualifier}`
);
}
break;
}
}
}
}

// Create immediate packagings.
for await (const immediatePackaging of immediatePackagings) {
for await (const immediatePackaging of configItems.immediatePackaging) {
if (!immediatePackaging) continue;
const nQuads = `
_:pack <name> "${immediatePackaging}" .
Expand Down
5 changes: 5 additions & 0 deletions data-loader/src/v2/types/ConfigItems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ConfigItems {
forms: string[];
immediatePackaging: string[];
routes: string[];
}
1 change: 1 addition & 0 deletions data-loader/src/v2/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './EntityGraph';
export * from './EntityNode';
export * from './ParserOptions';
export * from './PropertyConfigItem';
export * from './ConfigItems';

0 comments on commit 3140ef8

Please sign in to comment.