Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into pathfinder-newsystem
Browse files Browse the repository at this point in the history
  • Loading branch information
rjawesome committed Aug 13, 2024
2 parents 8055fa9 + df25494 commit 2ff3a8b
Show file tree
Hide file tree
Showing 10 changed files with 375 additions and 201 deletions.
2 changes: 2 additions & 0 deletions data/templateGroups.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
],
"predicate": ["affects"],
"qualifiers": {
"qualified_predicate": "causes",
"object_aspect_qualifier": "activity_or_abundance",
"object_direction_qualifier": "increased"
},
Expand All @@ -40,6 +41,7 @@
],
"predicate": ["affects"],
"qualifiers": {
"qualified_predicate": "causes",
"object_aspect_qualifier": "activity_or_abundance",
"object_direction_qualifier": "decreased"
},
Expand Down
7 changes: 2 additions & 5 deletions src/edge_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,11 +207,8 @@ export default class QueryEdgeManager {
const objectIDs = [record.object.original, record.object.curie, ...record.object.equivalentCuries];

// there must be at least a minimal intersection
const subjectMatch =
subjectIDs.some((curie) => execSubjectCuries.includes(curie)) || execSubjectCuries.length === 0;
const objectMatch = objectIDs.some((curie) => execObjectCuries.includes(curie)) || execObjectCuries.length === 0;

//if both ends match then keep record
const subjectMatch = subjectIDs.some((curie) => execSubjectCuries.includes(curie));
const objectMatch = objectIDs.some((curie) => execObjectCuries.includes(curie));

// Don't keep self-edges
const selfEdge = [...subjectIDs].some((curie) => objectIDs.includes(curie));
Expand Down
5 changes: 5 additions & 0 deletions src/graph/kg_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ export interface KGNodeInfo {
curies: string[];
primaryCurie: string;
qNodeID: string;
originalCurie?: string;
}

export default class KGNode {
id: string;
primaryCurie: string;
qNodeID: string;
originalCurie: string;
curies: string[];
names: string[];
semanticType: string[];
Expand All @@ -36,6 +38,9 @@ export default class KGNode {
this.targetNodes = new Set();
this.sourceQNodeIDs = new Set();
this.targetQNodeIDs = new Set();

// store original curie to output `query_id bte#815`
this.originalCurie = info.originalCurie;
}

addSourceNode(kgNodeID: string): void {
Expand Down
50 changes: 44 additions & 6 deletions src/graph/knowledge_graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,42 @@ import KGNode from './kg_node';
import KGEdge from './kg_edge';
import { BTEGraphUpdate } from './graph';
import { APIDefinition } from '@biothings-explorer/types';
import { Telemetry } from '@biothings-explorer/utils';

const debug = Debug('bte:biothings-explorer-trapi:KnowledgeGraph');

const NON_ARRAY_ATTRIBUTES = ['biolink:knowledge_level', 'biolink:agent_type'];
const NON_ARRAY_ATTRIBUTES = ['biolink:knowledge_level', 'biolink:agent_type', 'biolink:evidence_count'];

interface SpecialAttributeHandlers {
[attribute_type_id: string]: (value: Set<string | number>, kgEdge: KGEdge) => TrapiAttribute['value'];
}

const SPECIAL_ATTRIBUTE_HANDLERS: SpecialAttributeHandlers = {
'biolink:max_research_phase': (value, kgEdge) => {
// Special handling for max research phase
const phase_map = {
'-1.0': 'not_provided',
'0.5': 'pre_clinical_research_phase',
'1.0': 'clinical_trial_phase_1',
'2.0': 'clinical_trial_phase_2',
'3.0': 'clinical_trial_phase_3',
'4.0': 'clinical_trial_phase_4',
};
function map_phase(val: string) {
let new_val = phase_map[val];
if (typeof new_val !== 'undefined') return new_val;

const source = Object.values(kgEdge.sources).find((src) => typeof src.primary_knowledge_source !== 'undefined')
.primary_knowledge_source.resource_id;
const err = new Error(
`Unrecognized research phase (${val}) from ${source} ${kgEdge.subject} > ${kgEdge.predicate} > ${kgEdge.object}`,
);
Telemetry.captureException(err);
return 'not_provided';
}
return Array.from(value as Set<string>).map(map_phase);
},
};

export default class KnowledgeGraph {
nodes: {
Expand Down Expand Up @@ -117,13 +149,19 @@ export default class KnowledgeGraph {

Object.entries(kgEdge.attributes).forEach(([key, value]) => {
if (key === 'edge-attributes') return;
// if (key == 'edge-attributes') return;

let formatted_value: TrapiAttribute['value'] = NON_ARRAY_ATTRIBUTES.includes(key)
? Array.from(value as Set<string>).reduce((acc, val) => acc + val)
: Array.from(value as Set<string>);

if (key in SPECIAL_ATTRIBUTE_HANDLERS) {
formatted_value = SPECIAL_ATTRIBUTE_HANDLERS[key](value as Set<string | number>, kgEdge);
}

attributes.push({
attribute_type_id: key,
value: // technically works for numbers as well
NON_ARRAY_ATTRIBUTES.includes(key)
? [...(value as Set<string>)].reduce((acc, val) => acc + val)
: Array.from(value as Set<string>),
// technically works for numbers as well
value: formatted_value,
//value_type_id: 'bts:' + key,
});
});
Expand Down
47 changes: 41 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import MetaKG from '@biothings-explorer/smartapi-kg';
import MetaKG, { SmartAPIQueryResult } from '@biothings-explorer/smartapi-kg';
import path from 'path';
import QueryGraph from './query_graph';
import KnowledgeGraph from './graph/knowledge_graph';
Expand Down Expand Up @@ -30,6 +30,7 @@ import { QueryHandlerOptions } from '@biothings-explorer/types';
import BTEGraph from './graph/graph';
import QEdge from './query_edge';
import { Telemetry } from '@biothings-explorer/utils';
import { enrichTrapiResultsWithPfocrFigures } from './results_assembly/pfocr';

// Exports for external availability
export * from './types';
Expand Down Expand Up @@ -73,14 +74,21 @@ export default class TRAPIQueryHandler {

async findUnregisteredAPIs() {
const configListAPIs = this.options.apiList['include'];
const smartapiRegistry = await fs.readFile(this.path, { encoding: 'utf8' });

let smartapiRegistry: SmartAPIQueryResult;
if (this.options.smartapi) {
smartapiRegistry = this.options.smartapi;
} else {
const file = await fs.readFile(this.path, "utf-8");
smartapiRegistry = JSON.parse(file);
}

const smartapiIds: string[] = [];
const inforesIds: string[] = [];
const unregisteredAPIs: string[] = [];

// TODO typing for smartapiRegistration
JSON.parse(smartapiRegistry).hits.forEach((smartapiRegistration) => {
smartapiRegistry.hits.forEach((smartapiRegistration) => {
smartapiIds.push(smartapiRegistration._id);
inforesIds.push(smartapiRegistration.info?.['x-translator']?.infores);
});
Expand All @@ -96,14 +104,23 @@ export default class TRAPIQueryHandler {
return unregisteredAPIs;
}

_loadMetaKG(): MetaKG {
const metaKG = new MetaKG(this.path, this.predicatePath);
async _loadMetaKG(): Promise<MetaKG> {
debug(
`Query options are: ${JSON.stringify({
...this.options,
schema: this.options.schema ? this.options.schema.info.version : 'not included',
metakg: "",
smartapi: ""
})}`,
);

if (this.options.metakg) {
const metaKG = new MetaKG(undefined, undefined, (this.options as any).metakg);
metaKG.filterKG(this.options);
return metaKG;
}

const metaKG = new MetaKG(this.path, this.predicatePath);
debug(`SmartAPI Specs read from path: ${this.path}`);
metaKG.constructMetaKGSync(this.includeReasoner, this.options);
return metaKG;
Expand Down Expand Up @@ -302,6 +319,18 @@ export default class TRAPIQueryHandler {
this.finalizedResults = fixedResults;
}

appendOriginalCuriesToResults(results: TrapiResult[]): void {
results.forEach(result => {
Object.entries(result.node_bindings).forEach(([_, bindings]) => {
bindings.forEach(binding => {
if (this.bteGraph.nodes[binding.id].originalCurie && this.bteGraph.nodes[binding.id].originalCurie !== binding.id) {
binding.query_id = this.bteGraph.nodes[binding.id].originalCurie;
}
})
})
})
}

async addQueryNodes(): Promise<void> {
const qNodeIDsByOriginalID: Map<string, TrapiQNode> = new Map();
const curiesToResolve = [
Expand All @@ -323,6 +352,7 @@ export default class TRAPIQueryHandler {
this.bteGraph.nodes[resolvedEntity.primaryID] = new KGNode(resolvedEntity.primaryID, {
primaryCurie: resolvedEntity.primaryID,
qNodeID: qNodeIDsByOriginalID[originalCurie],
originalCurie: originalCurie,
curies: resolvedEntity.equivalentIDs,
names: resolvedEntity.labelAliases,
semanticType: category ? [category] : ['biolink:NamedThing'],
Expand Down Expand Up @@ -629,7 +659,7 @@ export default class TRAPIQueryHandler {
const span1 = Telemetry.startSpan({ description: 'loadMetaKG' });

debug('Start to load metakg.');
const metaKG = this._loadMetaKG();
const metaKG = await this._loadMetaKG();
if (!metaKG.ops.length) {
let error: string;
if (this.options.smartAPIID) {
Expand Down Expand Up @@ -710,8 +740,13 @@ export default class TRAPIQueryHandler {
this.createSubclassSupportGraphs();
// prune bteGraph
this.bteGraph.prune(this.finalizedResults, this.auxGraphs);
// add original curies to results
this.appendOriginalCuriesToResults(this.finalizedResults);
this.bteGraph.notify();

// Attempt to enrich results with PFOCR figures
this.logs = [...this.logs, ...(await enrichTrapiResultsWithPfocrFigures(this.getResponse()))];

span3?.finish();

// check primary knowledge sources
Expand Down
Loading

0 comments on commit 2ff3a8b

Please sign in to comment.