-
Julian Matschinske authoredJulian Matschinske authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
analysis-window.component.ts 12.52 KiB
import {
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
ViewChild
} from '@angular/core';
import {HttpClient, HttpErrorResponse} from '@angular/common/http';
import {environment} from '../../../environments/environment';
import {AnalysisService, algorithmNames} from '../../analysis.service';
import {
Protein, Task, ViralProtein, Drug, Wrapper, WrapperType,
getWrapperFromProtein, getWrapperFromDrug, getWrapperFromViralProtein, getNodeIdsFromPDI, getNodeIdsFromPPI
} from '../../interfaces';
import html2canvas from 'html2canvas';
import {toast} from 'bulma-toast';
import {NetworkSettings} from '../../network-settings';
declare var vis: any;
interface Scored {
score: number; // Normalized or unnormalized (whichever user selects, will be displayed in the table)
rawScore: number; // Unnormalized (kept to restore unnormalized value)
}
@Component({
selector: 'app-analysis-window',
templateUrl: './analysis-window.component.html',
styleUrls: ['./analysis-window.component.scss'],
})
export class AnalysisWindowComponent implements OnInit, OnChanges {
@ViewChild('network', {static: false}) networkEl: ElementRef;
@Input() token: string | null = null;
@Output() tokenChange = new EventEmitter<string | null>();
@Output() showDetailsChange = new EventEmitter<Wrapper>();
@Output() visibleItems = new EventEmitter<any>();
public task: Task | null = null;
public indexscreenshot = 1;
private network: any;
private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
private drugNodes: any[] = [];
private drugEdges: any[] = [];
public showDrugs = false;
public tab = 'network';
public physicsEnabled = true;
public drugstatus = true;
private proteins: any;
public effects: any;
public tableDrugs: Array<Drug & Scored> = [];
public tableProteins: Array<Protein & Scored> = [];
public tableViralProteins: Array<ViralProtein & Scored> = [];
public tableNormalize = false;
public tableHasScores = false;
public algorithmNames = algorithmNames;
constructor(private http: HttpClient, public analysis: AnalysisService) {
}
async ngOnInit() {
}
async ngOnChanges(changes: SimpleChanges) {
await this.refresh();
}
private async refresh() {
if (this.token) {
this.task = await this.getTask(this.token);
if (this.task && this.task.info.done) {
const result = await this.http.get<any>(`${environment.backend}task_result/?token=${this.token}`).toPromise();
// Reset
this.nodeData = {nodes: null, edges: null};
this.networkEl.nativeElement.innerHTML = '';
this.network = null;
this.showDrugs = false;
// Create
const {nodes, edges} = this.createNetwork(result);
this.nodeData.nodes = new vis.DataSet(nodes);
this.nodeData.edges = new vis.DataSet(edges);
const container = this.networkEl.nativeElement;
const options = NetworkSettings.getOptions('analysis');
this.network = new vis.Network(container, this.nodeData, options);
const promises: Promise<any>[] = [];
promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=proteins`).toPromise()
.then((table) => {
this.tableProteins = table;
this.tableProteins.forEach((r) => r.rawScore = r.score);
}));
promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=viral_proteins`).toPromise()
.then((table) => {
this.tableViralProteins = table;
this.tableViralProteins.forEach((r) => r.rawScore = r.score);
}));
promises.push(this.http.get<any>(`${environment.backend}task_result/?token=${this.token}&view=drugs`).toPromise()
.then((table) => {
this.tableDrugs = table;
this.tableDrugs.forEach((r) => r.rawScore = r.score);
}));
await Promise.all(promises);
this.tableHasScores = ['trustrank', 'closeness', 'degree', 'quick'].indexOf(this.task.info.algorithm) !== -1;
if (this.tableHasScores) {
this.toggleNormalization(true);
}
this.network.on('deselectNode', (properties) => {
this.showDetailsChange.emit(null);
});
this.network.on('click', (properties) => {
const selectedNodes = this.nodeData.nodes.get(properties.nodes);
if (selectedNodes.length > 0) {
const selectedNode = selectedNodes[0];
const wrapper = selectedNode.wrapper;
if (properties.event.srcEvent.ctrlKey) {
if (this.analysis.inSelection(wrapper)) {
this.analysis.removeItem(wrapper);
} else {
this.analysis.addItem(wrapper);
this.analysis.getCount();
}
}
this.showDetailsChange.emit(wrapper);
} else {
this.showDetailsChange.emit(null);
}
});
this.analysis.subscribe((item, selected) => {
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
return;
}
const pos = this.network.getPositions([item.nodeId]);
node.x = pos[item.nodeId].x;
node.y = pos[item.nodeId].y;
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, node.isSeed, selected));
this.nodeData.nodes.update(node);
});
}
}
this.emitVisibleItems(true);
}
public emitVisibleItems(on: boolean) {
if (on) {
this.visibleItems.emit([this.nodeData.nodes, [this.proteins, this.effects]]);
} else {
this.visibleItems.emit(null);
}
}
private async getTask(token: string): Promise<any> {
return await this.http.get(`${environment.backend}task/?token=${token}`).toPromise();
}
close() {
this.token = null;
this.tokenChange.emit(this.token);
this.emitVisibleItems(false);
}
export() {
}
public toggleNormalization(normalize: boolean) {
this.tableNormalize = normalize;
const normalizeFn = (table) => {
let max = 0;
table.forEach(i => {
if (i.rawScore > max) {
max = i.rawScore;
}
});
table.forEach(i => {
i.score = i.rawScore / max;
});
};
const unnormalizeFn = (table) => {
table.forEach(i => {
i.score = i.rawScore;
});
};
if (normalize) {
normalizeFn(this.tableDrugs);
normalizeFn(this.tableProteins);
normalizeFn(this.tableViralProteins);
} else {
unnormalizeFn(this.tableDrugs);
unnormalizeFn(this.tableProteins);
unnormalizeFn(this.tableViralProteins);
}
}
public downloadLink(view: string): string {
return `${environment.backend}task_result/?token=${this.token}&view=${view}&fmt=csv`;
}
public inferNodeType(nodeId: string): WrapperType {
if (nodeId.indexOf('-') !== -1 || nodeId.indexOf('_') !== -1) {
return 'virus';
} else if (nodeId.startsWith('DB')) {
return 'drug';
}
return 'host';
}
public createNetwork(result: any): { edges: any[], nodes: any[] } {
const nodes = [];
const edges = [];
const attributes = result.nodeAttributes || {};
this.proteins = [];
this.effects = [];
const network = result.network;
const nodeTypes = attributes.nodeTypes || {};
const isSeed = attributes.isSeed || {};
const scores = attributes.scores || {};
const details = attributes.details || {};
const wrappers: {[key: string]: Wrapper} = {};
for (const node of network.nodes) {
if (nodeTypes[node] === 'host') {
this.proteins.push(details[node]);
wrappers[node] = getWrapperFromProtein(details[node]);
} else if (nodeTypes[node] === 'virus') {
this.effects.push(details[node]);
wrappers[node] = getWrapperFromViralProtein(details[node]);
} else if (nodeTypes[node] === 'drug') {
wrappers[node] = getWrapperFromDrug(details[node]);
}
nodes.push(this.mapNode(this.inferNodeType(node), details[node], isSeed[node], scores[node]));
}
for (const edge of network.edges) {
edges.push(this.mapEdge(edge, 'protein-protein', wrappers));
}
for (const edge of network.edges) {
edges.push(this.mapEdge(edge, 'protein-protein', wrappers));
}
return {
nodes,
edges,
};
}
private mapNode(nodeType: WrapperType, details: Protein | ViralProtein | Drug, isSeed?: boolean, score?: number): any {
let nodeLabel;
let wrapper: Wrapper;
if (nodeType === 'host') {
const protein = details as Protein;
wrapper = getWrapperFromProtein(protein);
nodeLabel = protein.name;
if (!protein.name) {
nodeLabel = protein.proteinAc;
}
} else if (nodeType === 'drug') {
const drug = details as Drug;
wrapper = getWrapperFromDrug(drug);
nodeLabel = drug.name;
} else if (nodeType === 'virus') {
const viralProtein = details as ViralProtein;
wrapper = getWrapperFromViralProtein(viralProtein);
nodeLabel = viralProtein.effectName;
}
const node = NetworkSettings.getNodeStyle(nodeType, isSeed, this.analysis.inSelection(wrapper));
node.id = wrapper.nodeId;
node.label = nodeLabel;
node.nodeType = nodeType;
node.isSeed = isSeed;
node.wrapper = wrapper;
return node;
}
private mapEdge(edge: any, type: 'protein-protein' | 'to-drug', wrappers?: {[key: string]: Wrapper}): any {
let edgeColor;
if (type === 'protein-protein') {
edgeColor = {
color: NetworkSettings.getColor('edgeHostVirus'),
highlight: NetworkSettings.getColor('edgeHostVirusHighlight'),
};
const {from, to} = getNodeIdsFromPPI(edge, wrappers);
return {
from, to,
color: edgeColor,
};
} else if (type === 'to-drug') {
edgeColor = {
color: NetworkSettings.getColor('edgeHostDrug'),
highlight: NetworkSettings.getColor('edgeHostDrugHighlight'),
};
const {from, to} = getNodeIdsFromPDI(edge);
return {
from, to,
color: edgeColor,
};
}
}
public async toggleDrugs(bool: boolean) {
this.showDrugs = bool;
this.nodeData.nodes.remove(this.drugNodes);
this.nodeData.edges.remove(this.drugEdges);
this.drugNodes = [];
this.drugEdges = [];
if (this.showDrugs) {
const proteinAcs = this.proteins.map((protein) => protein.proteinAc);
// tslint:disable-next-line:max-line-length
const result = await this.http.get<any>(`${environment.backend}drug_interactions/?proteins=${JSON.stringify(proteinAcs)}`).toPromise().catch((err: HttpErrorResponse) => {
// simple logging, but you can do a lot more, see below
toast({
message: 'An error occured while fetching the drugs.',
duration: 5000,
dismissible: true,
pauseOnHover: true,
type: 'is-danger',
position: 'top-center',
animate: {in: 'fadeIn', out: 'fadeOut'}
});
this.showDrugs = false;
return;
});
const drugs = result.drugs;
const edges = result.edges;
if (drugs.length === 0) {
toast({
message: 'No drugs found.',
duration: 5000,
dismissible: true,
pauseOnHover: true,
type: 'is-warning',
position: 'top-center',
animate: {in: 'fadeIn', out: 'fadeOut'}
});
} else {
for (const drug of drugs) {
this.drugNodes.push(this.mapNode('drug', drug, false, null));
}
for (const interaction of edges) {
const edge = {from: interaction.proteinAc, to: interaction.drugId};
this.drugEdges.push(this.mapEdge(edge, 'to-drug'));
}
this.nodeData.nodes.add(Array.from(this.drugNodes.values()));
this.nodeData.edges.add(Array.from(this.drugEdges.values()));
}
}
}
public updatePhysicsEnabled(bool: boolean) {
this.physicsEnabled = bool;
this.network.setOptions({
physics: {
enabled: this.physicsEnabled,
stabilization: {
enabled: false,
},
}
});
}
public screenshot() {
const elem = document.getElementById(this.indexscreenshot.toString());
html2canvas(elem).then((canvas) => {
const generatedImage1 = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
const a = document.createElement('a');
a.href = generatedImage1;
a.download = `Resulting_Network.png`;
a.click();
});
}
public updateshowdrugs(bool) {
this.drugstatus = bool;
}
}