Newer
Older
import {ProteinNetwork, mapNetexEdge} from '../../main-network';
import {AnalysisService} from '../../services/analysis/analysis.service';
import {NetworkSettings} from '../../network-settings';
import {defaultConfig, EdgeGroup, NodeGroup} from '../../config';
import {NetexControllerService} from 'src/app/services/netex-controller/netex-controller.service';
import {DrugstoneConfigService} from 'src/app/services/drugstone-config/drugstone-config.service';
import {NetworkHandlerService} from 'src/app/services/network-handler/network-handler.service';
Michael Hartung
committed
@Component({
selector: 'app-explorer-page',
templateUrl: './explorer-page.component.html',
styleUrls: ['./explorer-page.component.scss'],
})
export class ExplorerPageComponent implements OnInit, AfterViewInit {

Hartung, Michael
committed
private networkJSON = undefined; //'{"nodes": [], "edges": []}'
@Input()
public onload: undefined | string;
@Input()
public id: undefined | string;
@Input()
public set config(config: string | undefined) {
this._config = config;
if (this.id !== null) {
this.activateConfig();
@Input()
public set groups(groups: string | undefined) {
if (groups == null) {
return;
}
this._groups = groups;
if (this.id !== null) {
@Input()
public set network(network: string | undefined) {
this.drugstoneConfig.gettingNetworkIssue = false;
if (network == null) {
this.drugstoneConfig.gettingNetworkIssue = true;
console.error('Failed parsing input network! Reason: Network JSON is empty');

Hartung, Michael
committed
try {
const nw = typeof network === 'string' ? JSON5.parse(network) : network;
if (!nw.nodes || nw.nodes.length === 0) {
this.drugstoneConfig.gettingNetworkEmpty = true;
console.log('No nodes specified in network config.');
return;
}
this.networkJSON = JSON.stringify(nw);
} catch (e) {
this.drugstoneConfig.parsingIssueNetwork = true;
console.error('Failed parsing input network');
console.error(e);

Hartung, Michael
committed
}
@Output()
public taskEvent = new EventEmitter<object>();
public get network() {
return this.networkJSON;
}
public collapseTask = true;
public collapseSelection = true;
public collapseQuery = true;
public collapseData = true;
// public proteins: Node[];
// public edges: NodeInteraction[];
public nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
public analysisDialogTarget: 'drug' | 'drug-target';
public selectedAnalysisToken: string | null = null;
@Input() set taskId(token: string | null) {
if (token == null || token.length === 0) {
this.selectedAnalysisToken = null;
}
this.selectedAnalysisToken = token;
}
// public omnipath: OmnipathControllerService,
public drugstoneConfig: DrugstoneConfigService,
public netex: NetexControllerService,
public networkHandler: NetworkHandlerService) {
this.analysis.subscribeList(async (items, selected) => {
Michael Hartung
committed
// return if analysis panel is open or no nodes are loaded
if (this.selectedAnalysisToken || !this.nodeData.nodes) {
if (selected !== null) {
if (items == null || items.length === 0) {
return;
}
const updatedNodes = [];
Michael Hartung
committed
// const node: Node = this.nodeData.nodes.get(wrapper.id);
const node = wrapper.data as Node;
if (!node) {
continue;
}
const pos = this.networkHandler.activeNetwork.networkInternal.getPositions([wrapper.id]);
node.x = pos[wrapper.id].x;
node.y = pos[wrapper.id].y;
const nodeStyled = NetworkSettings.getNodeStyle(
node,
this.networkHandler.activeNetwork.getGradient(node.id),

Hartung, Michael
committed
this.networkHandler.activeNetwork.nodeRenderer
Michael Hartung
committed
nodeStyled.x = pos[wrapper.id].x;
nodeStyled.y = pos[wrapper.id].y;
updatedNodes.push(nodeStyled);
}
this.nodeData.nodes.update(updatedNodes);
}
@HostListener('window:resize', ['$event'])
onResize(event) {
this.setWindowWidth(document.getElementById('appWindow').getBoundingClientRect().width);
this.setWindowWidth(document.getElementById('appWindow').getBoundingClientRect().width);
this.networkHandler.setActiveNetwork('explorer');
if (this.onload) {
// tslint:disable-next-line:no-eval
eval(this.onload);
}
public activateConfig(updateNetworkFlag = false) {
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
let configObj = {};
let groupsObj = {};
try {
if (typeof this._config === 'string') {
if (this._config.length > 0) {
configObj = JSON5.parse(this._config);
}
} else {
configObj = this._config;
}
} catch (e) {
this.drugstoneConfig.parsingIssueConfig = true;
console.error('Error when parsing user defined config JSON. Please check your JSON string for syntax errors.');
console.error(e);
}
try {
if (typeof this._groups === 'string') {
if (this._groups.length > 0) {
groupsObj = JSON5.parse(this._groups);
}
} else {
groupsObj = this._groups;
}
} catch (e) {
this.drugstoneConfig.parsingIssueGroups = true;
console.error('Error when parsing user defined groups JSON. Please check your JSON string for syntax errors.');
console.error(e);
}
if (this.drugstoneConfig.analysisConfig) {
this.drugstoneConfig.set_analysisConfig(merge(this.drugstoneConfig.analysisConfig, configObj));
} else {
this.drugstoneConfig.config = merge(this.drugstoneConfig.config, configObj);
}
// update Drugst.One according to the settings
// check if config updates affect network
for (const key of Object.keys(configObj)) {
if (key === 'nodeGroups') {
this.setConfigNodeGroup(key, configObj[key]);
updateNetworkFlag = true;
} else if (key === 'edgeGroups') {
this.setConfigEdgeGroup(key, configObj[key]);
updateNetworkFlag = true;
} else if (key === 'interactions') {
this.networkHandler.activeNetwork.getInteractions(configObj[key]);
}
AndiMajore
committed
this.networkHandler.networkSidebarOpen = this.drugstoneConfig.config.expandNetworkMenu;
// trigger updates on config e.g. in legend
this.drugstoneConfig.analysisConfig = {...this.drugstoneConfig.analysisConfig};
this.drugstoneConfig.config = {...this.drugstoneConfig.config};
if (updateNetworkFlag && typeof this.networkJSON !== 'undefined') {
// update network if network config has changed and networkJSON exists
if (this.networkHandler.activeNetwork.networkInternal !== null) {
// a network exists, save node positions
this.networkHandler.activeNetwork.networkPositions = this.networkHandler.activeNetwork.networkInternal.getPositions();
}
if (this.drugstoneConfig.config.physicsOn) {
this.networkHandler.activeNetwork.updatePhysicsEnabled(true);
this.networkHandler.updateAdjacentNodes(!this.networkHandler.activeNetwork.isBig()).then((updated) => {
}).catch(e => {
console.error(e);
});
}).catch(e => {
console.error(e);
analysisWindowChanged($event: [any[], [Node[], Tissue], NodeInteraction[]]) {
if ($event) {
this.networkHandler.setActiveNetwork('analysis');
this.networkHandler.activeNetwork.currentViewNodes = $event[0];
this.networkHandler.activeNetwork.currentViewEdges = $event[2];
this.networkHandler.activeNetwork.currentViewProteins = $event[1][0];
this.networkHandler.activeNetwork.currentViewSelectedTissue = $event[1][1];
} else {
this.networkHandler.setActiveNetwork('explorer');
// changes for either way (analysis open and close)
this.networkHandler.activeNetwork.selectedWrapper = null;
this.networkHandler.activeNetwork.updateQueryItems();
// this.fillQueryItems(this.currentViewNodes);
/**
* Creates the explorer network. Analysis component has distinct function.
*/
public createNetwork(): Promise<any> {
return new Promise<any>(async (resolve, reject) => {
this.analysis.resetSelection();
this.networkHandler.activeNetwork.selectedWrapper = null;
// getNetwork synchronizes the input network with the database
await this.getNetwork();
this.proteinData = new ProteinNetwork(this.networkHandler.activeNetwork.inputNetwork.nodes, this.networkHandler.activeNetwork.inputNetwork.edges);
if (this.networkHandler.activeNetwork.networkPositions) {
this.proteinData.updateNodePositions(this.networkHandler.activeNetwork.networkPositions);
}
let {nodes, edges} = this.proteinData.mapDataToNetworkInput(this.drugstoneConfig.currentConfig(), this.drugstoneConfig);
if (this.drugstoneConfig.config.autofillEdges && nodes.length) {
let node_map = {};
nodes.filter(n => n.drugstoneType === 'protein').forEach(node => {
if (typeof node.drugstoneId === 'string') {
if (node_map[node.drugstoneId]) {
node_map[node.drugstoneId].push(node.id);
} else {
node_map[node.drugstoneId] = [node.id];
}
node.drugstoneId.forEach(n => {
if (node_map[n]) {
node_map[n].push(node.id);
} else {
node_map[n] = [node.id];
}
});
});
const netexEdges = await this.netex.fetchEdges(nodes, this.drugstoneConfig.config.interactionProteinProtein, this.drugstoneConfig.config.licensedDatasets);
edges.push(...netexEdges.map(netexEdge => mapNetexEdge(netexEdge, this.drugstoneConfig.currentConfig(), node_map)).flatMap(e => e));
}
const edge_map = {};
edges = edges.filter(edge => {
if (edge_map[edge.to] && edge_map[edge.to].indexOf(edge.from) !== -1) {
return false;
}
if (edge_map[edge.from] && edge_map[edge.from].indexOf(edge.to) !== -1) {
return false;
}
if (!edge_map[edge.from]) {
edge_map[edge.from] = [edge.to];
// @ts-ignore
if (!this.drugstoneConfig.selfReferences) {
edges = edges.filter(el => el.from !== el.to);
this.nodeData.nodes = new vis.DataSet(nodes);
this.nodeData.edges = new vis.DataSet(edges);
const container = this.networkHandler.activeNetwork.networkEl.nativeElement;
const options = NetworkSettings.getOptions('main', this.drugstoneConfig.currentConfig());
this.networkHandler.activeNetwork.networkInternal = new vis.Network(container, this.nodeData, options);
this.networkHandler.activeNetwork.networkInternal.once('stabilizationIterationsDone', () => {
if (!this.drugstoneConfig.config.physicsOn) {
this.networkHandler.activeNetwork.updatePhysicsEnabled(false);
}
});
// if (!this.drugstoneConfig.config.showSidebar) {
// // skip network options for selecting nodes when there are no options to use it
// return;
// }
this.networkHandler.activeNetwork.networkInternal.on('doubleClick', (properties) => {
const nodeIds: Array<string> = properties.nodes;
if (nodeIds != null && nodeIds.length > 0) {
const nodeId = nodeIds[0];
const node = this.nodeData.nodes.get(nodeId);
if (node.drugstoneId === undefined || node.drugstoneType !== 'protein') {
this.analysis.unmappedNodeToast();
// skip if node is not a protein mapped to backend
return;
}
const wrapper = getWrapperFromNode(node);
if (this.analysis.inSelection(node)) {
this.analysis.removeItems([wrapper]);
} else {
this.analysis.addItems([wrapper]);
}
this.networkHandler.activeNetwork.networkInternal.on('click', (properties) => {
if (properties.nodes.length === 0 && properties.edges.length === 1) {
// clicked on one edge
const edgeId = properties.edges[0];
this.networkHandler.activeNetwork.openEdgeSummary(edgeId);
// clicked not on one edge
const nodeIds: Array<string> = properties.nodes;
if (nodeIds != null && nodeIds.length > 0) {
const nodeId = nodeIds[0];
const node = this.nodeData.nodes.get(nodeId);
const wrapper = getWrapperFromNode(node);
this.openNodeSummary(wrapper, false);
} else {
this.closeSummary();
}
this.networkHandler.activeNetwork.networkInternal.on('deselectNode', (properties) => {
if (this.networkHandler.activeNetwork.selectedWrapper) {
this.zoomToNode(this.networkHandler.activeNetwork.selectedWrapper.id);
}
this.networkHandler.activeNetwork.currentViewNodes = this.nodeData.nodes;
this.networkHandler.activeNetwork.currentViewEdges = this.nodeData.edges;
this.networkHandler.activeNetwork.queryItems = [];
this.networkHandler.activeNetwork.updateQueryItems();
this.networkHandler.activeNetwork.currentViewProteins = this.networkHandler.activeNetwork.inputNetwork.nodes;
// this.fillQueryItems(this.currentViewNodes);
if (this.networkHandler.activeNetwork.selectedWrapper) {
this.networkHandler.activeNetwork.networkInternal.selectNodes([this.networkHandler.activeNetwork.selectedWrapper.id]);
}
public zoomToNode(id: string) {
this.networkHandler.activeNetwork.zoomToNode(id);
const network = JSON.parse(this.networkJSON);
if (this.drugstoneConfig.currentConfig().identifier === 'ensg') {
// @ts-ignore
network.nodes.forEach(node => {
node.id = this.removeEnsemblVersion(node.id);
});
if (network.edges != null)
network.edges.forEach(edge => {
edge.from = this.removeEnsemblVersion(edge.from);
edge.to = this.removeEnsemblVersion(edge.to);
});
// map data to nodes in backend
if (network.nodes != null && network.nodes.length) {
network.nodes = await this.netex.mapNodes(network.nodes, this.drugstoneConfig.currentConfig().identifier);
// if (this.drugstoneConfig.config.identifier === 'ensg') {
// remove possible duplicate IDs
// network.nodes = removeDuplicateObjectsFromList(network.nodes, 'drugstoneId');
// }
// at this point, we have nodes synched with the backend
// use drugstoneIds where posssible, but use original id as node name if no label given
// const nodeIdMap = {};
network.nodes.forEach((node) => {
// set node label to original id before node id will be set to netex id
node.label = node.label ? node.label : node.id;
});
// adjust edge labels accordingly and filter
const edges = new Array();
network.edges.forEach(edge => {
if (edge.from !== undefined && edge.to !== undefined) {
edges.push(edge);
// remove edges without endpoints
network.edges = edges;
this.networkHandler.activeNetwork.inputNetwork = network;
// this.proteins = network.nodes;
// this.edges = network.edges;
private setWindowWidth(width: number) {
this.windowWidth = width;
this.drugstoneConfig.smallStyle = this.windowWidth < 1250;
public async openNodeSummary(item: Wrapper, zoom: boolean) {
// close edge summary if open
this.networkHandler.activeNetwork.activeEdge = null;
this.networkHandler.activeNetwork.selectedWrapper = item;
// add expression information if loaded
if (this.networkHandler.activeNetwork.expressionMap && this.networkHandler.activeNetwork.selectedWrapper.id in this.networkHandler.activeNetwork.expressionMap) {
this.networkHandler.activeNetwork.selectedWrapper.expression = this.networkHandler.activeNetwork.expressionMap[this.networkHandler.activeNetwork.selectedWrapper.id];
}
if (zoom) {
this.zoomToNode(item.id);
public async closeSummary() {
this.networkHandler.activeNetwork.selectedWrapper = null;
this.showDetails = false;
}
removeEnsemblVersion(versionId: string): string {
return versionId.startsWith('ENSG') ? versionId.split('.')[0] : versionId;
}
public queryAction(item: any) {
if (item) {
Michael Hartung
committed
}
}
/**
* Function to set the node group attribute in config
* Validates input NodeGroups and handles setting defaults
public setConfigNodeGroup(key: string, nodeGroups: { [key: string]: NodeGroup } | {}) {
Michael Hartung
committed
// make sure that return-groups (seeds, drugs, found nodes) are set
const defaultNodeGroups = JSON.parse(JSON.stringify(defaultConfig.nodeGroups));
Michael Hartung
committed
// user merge function to do deep merge
nodeGroups = merge(defaultNodeGroups, nodeGroups);
Object.entries(nodeGroups).forEach(([key, group]) => {
Michael Hartung
committed
if (!group.color && key !== 'selectedNode') {
console.error(`Group ${key} has no attribute 'color'.`);
Michael Hartung
committed
}
Michael Hartung
committed
if (!group.shape && key !== 'selectedNode') {
console.error(`Group ${key} has no attribute 'shape'.`);
Michael Hartung
committed
}
Michael Hartung
committed
if (!group.groupName && key !== 'selectedNode') {
console.error(`Group ${key} has no attribute 'groupName'.`);
}
Michael Hartung
committed
// set default values in case they are not set by user
// these values are not mandatory but are neede to override default vis js styles after e.g. deselecting
// because vis js "remembers" styles even though they are removed
if (!group.borderWidth) {
group.borderWidth = 0;
Michael Hartung
committed
if (!group.borderWidthSelected) {
group.borderWidthSelected = 0;
}
Michael Hartung
committed
if (!group.font) {
group.font = defaultConfig.nodeGroups.default.font;
}
Michael Hartung
committed
// if color is set as string, add detail settings
if (typeof group.color === 'string') {
group.color = {
border: group.color,
background: group.color,
highlight: {
border: group.color,
background: group.color
}
Michael Hartung
committed
}
// if image is given, set node shape to image
if (group.image) {
group.shape = 'image';
// implement nodeShadow option, it needs to be set for all nodes or none
group.shadow = this.drugstoneConfig.currentConfig().nodeShadow;

Hartung, Michael
committed
// group must not have id, otherwise node id's would be overwritten which causes duplciates
if (group.hasOwnProperty('id')) {
delete group['id'];
}
this.drugstoneConfig.currentConfig()[key] = nodeGroups;
/**
* Function to set the edge group attribute in config
* Validates input EdgeGroups and handles setting defaults
public setConfigEdgeGroup(key: string, edgeGroups: {
[key
:
string
]:
EdgeGroup;
}
Michael Hartung
committed
// make sure that default-groups are set
const defaultNodeGroups = JSON.parse(JSON.stringify(defaultConfig.edgeGroups));
edgeGroups = merge(defaultNodeGroups, edgeGroups);
// // do not allow '_' in node Group names since it causes problems with backend
// edgeGroups = removeUnderscoreFromKeys(edgeGroups)
// make sure all keys are set
Object.entries(edgeGroups).forEach(([key, value]) => {
if (!('dashes' in value)) {
// use dashes default value if not set
value['dashes'] = defaultConfig.edgeGroups.default.dashes;
// implement edgeShadow option, it needs to be set for all nodes or none
value.shadow = this.drugstoneConfig.currentConfig().edgeShadow;
this.drugstoneConfig.currentConfig()[key] = edgeGroups;
.filter(wrapper => wrapper.data.drugstoneType === 'protein')
.map(wrapper => wrapper.data.id)
return 'https://biit.cs.ut.ee/gprofiler/gost?' +
'organism=hsapiens&' +
`query=${queryString}&` +
'ordered=false&' +
'all_results=false&' +
'no_iea=false&' +
'combined=false&' +
'measure_underrepresentation=false&' +
'domain_scope=annotated&' +
'significance_threshold_method=g_SCS&' +
'user_threshold=0.05&' +
'numeric_namespace=ENTREZGENE_ACC&' +
'sources=GO:MF,GO:CC,GO:BP,KEGG,TF,REAC,MIRNA,HPA,CORUM,HP,WP&' +
'background=';
}
openExternal(url) {
window.open(url, '_blank').focus();
}
async openDigest() {
let proteins = this.analysis.getSelection()
.filter(wrapper => wrapper.data.drugstoneType === 'protein')
.map(wrapper => wrapper.data.id);
let params = {
runs: 1000,
replace: 100,
distance: 'jaccard',
background_model: 'complete',
type: 'gene',
target: proteins,
target_id: this.drugstoneConfig.currentConfig().identifier
};
let resp = await this.netex.digest_request(params).catch(err => console.error(err));
let url = 'https://digest-validation.net/result?id=' + resp.task;
@ViewChild('analysisPanel')
analysisPanel;
if (this.selectedAnalysisToken && this.analysisPanel) {
return this.analysisPanel.getResultNodes();
}
return this.networkHandler.activeNetwork.inputNetwork.nodes;
if (this.selectedAnalysisToken && this.analysisPanel) {
return this.analysisPanel.getResultEdges();
}
return this.networkHandler.activeNetwork.inputNetwork.edges;