Skip to content
Snippets Groups Projects
Commit 2e1df7b6 authored by Julian Matschinske's avatar Julian Matschinske Committed by Julian Späth
Browse files

Optimize selection and deselection of nodes

parent 1358f06f
No related branches found
No related tags found
No related merge requests found
......@@ -37,7 +37,7 @@ export const MAX_TASKS = 3;
export class AnalysisService {
private selectedItems = new Map<string, Wrapper>();
private selectSubject = new Subject<{ item: Wrapper, selected: boolean }>();
private selectListSubject = new Subject<{ items: Wrapper[], selected: boolean | null }>();
public tokens: string[] = [];
public finishedTokens: string[] = [];
......@@ -82,40 +82,62 @@ export class AnalysisService {
});
}
public addItem(wrapper: Wrapper) {
if (!this.inSelection(wrapper)) {
this.selectedItems.set(wrapper.nodeId, wrapper);
this.selectSubject.next({item: wrapper, selected: true});
public addItems(wrappers: Wrapper[]) {
const addedWrappers: Wrapper[] = [];
for (const wrapper of wrappers) {
if (!this.inSelection(wrapper)) {
addedWrappers.push(wrapper);
this.selectedItems.set(wrapper.nodeId, wrapper);
}
}
this.selectListSubject.next({items: addedWrappers, selected: true});
}
public removeItems(wrappers: Wrapper[]) {
const removedWrappers: Wrapper[] = [];
for (const wrapper of wrappers) {
if (this.selectedItems.delete(wrapper.nodeId)) {
removedWrappers.push(wrapper);
}
}
this.selectListSubject.next({items: removedWrappers, selected: false});
}
public addAllHostProteins(nodes, proteins) {
const items: Wrapper[] = [];
const visibleIds = new Set<string>(nodes.getIds());
for (const protein of proteins) {
const wrapper = getWrapperFromProtein(protein);
const found = visibleIds.has(wrapper.nodeId);
if (found && !this.inSelection(wrapper)) {
this.addItem(wrapper);
items.push(wrapper);
this.selectedItems.set(wrapper.nodeId, wrapper);
}
}
this.selectListSubject.next({items, selected: true});
}
public addAllViralProteins(nodes, viralProteins) {
const items: Wrapper[] = [];
const visibleIds = new Set<string>(nodes.getIds());
for (const viralProtein of viralProteins) {
const wrapper = getWrapperFromViralProtein(viralProtein);
const found = visibleIds.has(wrapper.nodeId);
if (found && !this.inSelection(wrapper)) {
this.addItem(wrapper);
items.push(wrapper);
this.selectedItems.set(wrapper.nodeId, wrapper);
}
}
this.selectListSubject.next({items, selected: true});
}
resetSelection() {
const oldSelection = this.selectedItems.values();
for (const item of oldSelection) {
this.removeItem(item);
}
this.selectListSubject.next({items: [], selected: null});
this.selectedItems.clear();
}
idInSelection(nodeId: string): boolean {
return this.selectedItems.has(nodeId);
}
inSelection(wrapper: Wrapper): boolean {
......@@ -130,13 +152,6 @@ export class AnalysisService {
return this.inSelection(getWrapperFromViralProtein(viralProtein));
}
removeItem(wrapper: Wrapper) {
const item = this.selectedItems.get(wrapper.nodeId);
if (this.selectedItems.delete(wrapper.nodeId)) {
this.selectSubject.next({item, selected: false});
}
}
getSelection(): Wrapper[] {
return Array.from(this.selectedItems.values());
}
......@@ -145,9 +160,9 @@ export class AnalysisService {
return this.selectedItems.size;
}
subscribe(cb: (item: Wrapper, selected: boolean) => void) {
this.selectSubject.subscribe((event) => {
cb(event.item, event.selected);
subscribeList(cb: (items: Array<Wrapper>, selected: boolean | null) => void) {
this.selectListSubject.subscribe((event) => {
cb(event.items, event.selected);
});
}
......
......@@ -154,10 +154,10 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
}
const wrapper = node.wrapper;
if (this.analysis.inSelection(wrapper)) {
this.analysis.removeItem(wrapper);
this.analysis.removeItems([wrapper]);
this.analysis.getCount();
} else {
this.analysis.addItem(wrapper);
this.analysis.addItems([wrapper]);
this.analysis.getCount();
}
}
......@@ -174,40 +174,78 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
}
});
this.analysis.subscribe((item, selected) => {
if (item.type === 'host') {
// TODO: Refactor!
const found = this.tableSelectedProteins.findIndex((i) => getProteinNodeId(i) === item.nodeId);
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
this.tableSelectedProteins.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
this.tableSelectedProteins.splice(found, 1);
this.analysis.subscribeList((items, selected) => {
if (selected !== null) {
const updatedNodes = [];
for (const item of items) {
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
continue;
}
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));
updatedNodes.push(node);
}
this.tableSelectedProteins = [...this.tableSelectedProteins];
} else if (item.type === 'virus') {
// TODO: Refactor!
const found = this.tableSelectedViralProteins.findIndex((i) => getViralProteinNodeId(i) === item.nodeId);
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
this.tableSelectedViralProteins.push(tableItem);
this.nodeData.nodes.update(updatedNodes);
const proteinSelection = this.tableSelectedProteins;
const viralProteinSelection = this.tableSelectedViralProteins;
for (const item of items) {
if (item.type === 'host') {
// TODO: Refactor!
const found = proteinSelection.findIndex((i) => getProteinNodeId(i) === item.nodeId);
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
proteinSelection.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
proteinSelection.splice(found, 1);
}
} else if (item.type === 'virus') {
// TODO: Refactor!
const found = viralProteinSelection.findIndex((i) => getViralProteinNodeId(i) === item.nodeId);
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId);
if (selected && found === -1 && tableItem) {
viralProteinSelection.push(tableItem);
}
if (!selected && found !== -1 && tableItem) {
viralProteinSelection.splice(found, 1);
}
}
}
if (!selected && found !== -1 && tableItem) {
this.tableSelectedViralProteins.splice(found, 1);
this.tableSelectedProteins = [...proteinSelection];
this.tableSelectedViralProteins = [...viralProteinSelection];
} else {
const updatedNodes = [];
this.nodeData.nodes.forEach((node) => {
const nodeSelected = this.analysis.idInSelection(node.id);
if (selected !== nodeSelected) {
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, true, selected));
updatedNodes.push(node);
}
});
this.nodeData.nodes.update(updatedNodes);
const proteinSelection = [];
const viralProteinSelection = [];
for (const item of items) {
if (item.type === 'host') {
const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.nodeId);
if (tableItem) {
proteinSelection.push(tableItem);
}
} else if (item.type === 'virus') {
const tableItem = this.tableViralProteins.find((i) => getViralProteinNodeId(i) === item.nodeId);
if (tableItem) {
viralProteinSelection.push(tableItem);
}
}
}
this.tableSelectedViralProteins = [...this.tableSelectedViralProteins];
}
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
return;
this.tableSelectedProteins = [...proteinSelection];
this.tableSelectedViralProteins = [...viralProteinSelection];
}
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);
});
}
}
......@@ -390,20 +428,20 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
if (this.showDrugs) {
const result = await this.http.get<any>(
`${environment.backend}drug_interactions/?token=${this.token}`).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'}
(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;
});
this.showDrugs = false;
return;
});
const drugs = result.drugs;
const edges = result.edges;
......@@ -458,35 +496,43 @@ export class AnalysisWindowComponent implements OnInit, OnChanges {
public tableProteinSelection(e) {
const oldSelection = [...this.tableSelectedProteins];
this.tableSelectedProteins = e;
const addItems = [];
const removeItems = [];
for (const i of this.tableSelectedProteins) {
const wrapper = getWrapperFromProtein(i);
if (oldSelection.indexOf(i) === -1) {
this.analysis.addItem(wrapper);
addItems.push(wrapper);
}
}
for (const i of oldSelection) {
const wrapper = getWrapperFromProtein(i);
if (this.tableSelectedProteins.indexOf(i) === -1) {
this.analysis.removeItem(wrapper);
removeItems.push(wrapper);
}
}
this.analysis.addItems(addItems);
this.analysis.removeItems(removeItems);
}
public tableViralProteinSelection(e) {
const oldSelection = [...this.tableSelectedViralProteins];
this.tableSelectedViralProteins = e;
const addItems = [];
const removeItems = [];
for (const i of this.tableSelectedViralProteins) {
const wrapper = getWrapperFromViralProtein(i);
if (oldSelection.indexOf(i) === -1) {
this.analysis.addItem(wrapper);
addItems.push(wrapper);
}
}
for (const i of oldSelection) {
const wrapper = getWrapperFromViralProtein(i);
if (this.tableSelectedViralProteins.indexOf(i) === -1) {
this.analysis.removeItem(wrapper);
removeItems.push(wrapper);
}
}
this.analysis.addItems(addItems);
this.analysis.removeItems(removeItems);
}
}
......@@ -44,7 +44,7 @@
<div class="field has-addons add-remove-toggle" *ngIf="wrapper.type !== 'drug'">
<app-toggle [value]="analysis.inSelection(wrapper)"
(valueChange)="$event ? analysis.addItem(wrapper) : analysis.removeItem(wrapper)" textOn="Selected"
(valueChange)="$event ? analysis.addItems([wrapper]) : analysis.removeItems([wrapper])" textOn="Selected"
textOff="Deselected" tooltipOn="Add protein to selection." tooltipOff="Remove protein from selection."
icon="fa-plus"></app-toggle>
</div>
......
......@@ -57,7 +57,7 @@ export class LaunchAnalysisComponent implements OnInit, OnChanges {
constructor(public analysis: AnalysisService) {
this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'virus');
analysis.subscribe(() => {
analysis.subscribeList(() => {
this.hasBaits = !!analysis.getSelection().find((i) => i.type === 'virus');
});
}
......
......@@ -26,7 +26,6 @@
</a>
</header>
<div *ngIf="collapseData">
<div class="card-content">
<app-select-dataset [datasetItems]="datasetItems" [selectedDataset]="selectedDataset"
(selectedDatasetChange)="selectedDataset = $event; createNetwork($event.data)">
......@@ -371,7 +370,6 @@
</div>
</div>
<div class="card bar-large">
<header class="card-header">
<p class="card-header-title">
......@@ -408,7 +406,7 @@
<td *ngIf="p.type == 'virus'">{{p.data.effectName}}</td>
<td *ngIf="p.type == 'host'">{{p.data.name}}</td>
<td>
<button (click)="analysis.removeItem(p)" class="button is-small is-danger is-outlined has-tooltip"
<button (click)="analysis.removeItems([p])" class="button is-small is-danger is-outlined has-tooltip"
data-tooltip="Remove from selection.">
<i class="fa fa-trash"></i>
</button>
......
......@@ -114,16 +114,38 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.showDetails = false;
this.analysis.subscribe((item, selected) => {
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
this.analysis.subscribeList((items, selected) => {
if (!this.nodeData.nodes) {
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, true, selected));
this.nodeData.nodes.update(node);
if (selected !== null) {
if (items.length === 0) {
return;
}
const updatedNodes = [];
for (const item of items) {
const node = this.nodeData.nodes.get(item.nodeId);
if (!node) {
continue;
}
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, true, selected));
updatedNodes.push(node);
}
this.nodeData.nodes.update(updatedNodes);
} else {
const updatedNodes = [];
this.nodeData.nodes.forEach((node) => {
const nodeSelected = this.analysis.idInSelection(node.id);
if (selected !== nodeSelected) {
Object.assign(node, NetworkSettings.getNodeStyle(node.wrapper.type, true, selected));
updatedNodes.push(node);
}
});
this.nodeData.nodes.update(updatedNodes);
}
});
}
......@@ -234,9 +256,9 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
const node = this.nodeData.nodes.get(nodeId);
const wrapper = node.wrapper;
if (this.analysis.inSelection(wrapper)) {
this.analysis.removeItem(wrapper);
this.analysis.removeItems([wrapper]);
} else {
this.analysis.addItem(wrapper);
this.analysis.addItems([wrapper]);
}
}
});
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment