From bc46ac26f7d34c34b8775f38d85381b9df23e0b1 Mon Sep 17 00:00:00 2001
From: AndiMajore <andi.majore@googlemail.com>
Date: Wed, 5 Apr 2023 19:18:00 +0200
Subject: [PATCH] working on create view from selection feature

---
 src/app/app.module.ts                         |   2 +
 .../analysis-panel.component.html             |   4 +-
 .../analysis-panel.component.ts               | 463 +++++++++---------
 .../view-list/view-list.component.html        |  75 +++
 .../view-list/view-list.component.scss        |   0
 .../view-list/view-list.component.spec.ts     |  25 +
 .../view-list/view-list.component.ts          |  22 +
 src/app/config.ts                             |   2 +
 .../explorer-page.component.html              | 163 ++++--
 .../explorer-page/explorer-page.component.ts  |  36 +-
 src/app/services/analysis/analysis.service.ts |  51 ++
 11 files changed, 564 insertions(+), 279 deletions(-)
 create mode 100644 src/app/components/analysis-panel/view-list/view-list.component.html
 create mode 100644 src/app/components/analysis-panel/view-list/view-list.component.scss
 create mode 100644 src/app/components/analysis-panel/view-list/view-list.component.spec.ts
 create mode 100644 src/app/components/analysis-panel/view-list/view-list.component.ts

diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 4715e5a0..b8e8bd93 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -54,6 +54,7 @@ import { NetworkOverviewComponent } from './pages/explorer-page/network-overview
 import { InfoTileEdgeComponent } from './components/info-tile-edge/info-tile-edge/info-tile-edge.component';
 import { NetworkEmptyWarningComponent } from './components/network-empty-warning/network-empty-warning.component';
 import { BugReportComponent } from './components/bug-report/bug-report.component';
+import { ViewListComponent } from './components/analysis-panel/view-list/view-list.component';
 
 
 @NgModule({
@@ -97,6 +98,7 @@ import { BugReportComponent } from './components/bug-report/bug-report.component
     InfoTileEdgeComponent,
     NetworkEmptyWarningComponent,
     BugReportComponent,
+    ViewListComponent,
   ],
   imports: [
     BrowserModule,
diff --git a/src/app/components/analysis-panel/analysis-panel.component.html b/src/app/components/analysis-panel/analysis-panel.component.html
index 35cff928..8677a27f 100644
--- a/src/app/components/analysis-panel/analysis-panel.component.html
+++ b/src/app/components/analysis-panel/analysis-panel.component.html
@@ -122,7 +122,7 @@
       </div>
       <div
         class="tab-content meta parameter-tab"
-        *ngIf="task && task.info.done"
+        *ngIf="task && task.info &&  task.info.done"
         [class.is-visible]="tab === 'meta'"
       >
         <div class="columns m-1">
@@ -385,7 +385,7 @@
       </div>
       <div
         class="content tab-content scrollable table-tab"
-        *ngIf="task && task.info.done"
+        *ngIf="task && task.info && task.info.done"
         [class.is-visible]="tab === 'table'"
       >
         <div *ngIf="task.info.target === 'drug'">
diff --git a/src/app/components/analysis-panel/analysis-panel.component.ts b/src/app/components/analysis-panel/analysis-panel.component.ts
index b9b152d6..3dd07172 100644
--- a/src/app/components/analysis-panel/analysis-panel.component.ts
+++ b/src/app/components/analysis-panel/analysis-panel.component.ts
@@ -55,6 +55,7 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit
 
   @ViewChild('networkWithLegend', {static: false}) networkWithLegendEl: ElementRef;
   @Input() token: string | null = null;
+  @Input() tokenType: string | null = null;
 
 
   @Output() tokenChange = new EventEmitter<string | null>();
@@ -115,7 +116,7 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit
   constructor(public legendService: LegendService, public networkHandler: NetworkHandlerService, public drugstoneConfig: DrugstoneConfigService, private http: HttpClient, public analysis: AnalysisService, public netex: NetexControllerService, public loadingScreen: LoadingScreenService) {
     try {
       this.versionString = version;
-    }catch (e){
+    } catch (e) {
     }
   }
 
@@ -146,250 +147,258 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit
 
   private async refresh() {
     if (this.token) {
-      this.loadingScreen.stateUpdate(true);
-      this.task = await this.getTask(this.token);
-      this.analysis.switchSelection(this.token);
-
-      if (this.task.info.algorithm === 'degree') {
-        this.tableDrugScoreTooltip =
-          'Normalized number of direct interactions of the drug with the seeds. ' +
-          'The higher the score, the more relevant the drug.';
-        this.tableProteinScoreTooltip =
-          'Normalized number of direct interactions of the protein with the seeds. ' +
-          'The higher the score, the more relevant the protein.';
-      } else if (this.task.info.algorithm === 'closeness' || this.task.info.algorithm === 'quick' || this.task.info.algorithm === 'super') {
-        this.tableDrugScoreTooltip =
-          'Normalized inverse mean distance of the drug to the seeds. ' +
-          'The higher the score, the more relevant the drug.';
-        this.tableProteinScoreTooltip =
-          'Normalized inverse mean distance of the protein to the seeds. ' +
-          'The higher the score, the more relevant the protein.';
-      } else if (this.task.info.algorithm === 'trustrank') {
-        this.tableDrugScoreTooltip =
-          'Amount of ‘trust’ on the drug at termination of the algorithm. ' +
-          'The higher the score, the more relevant the drug.';
-        this.tableProteinScoreTooltip =
-          'Amount of ‘trust’ on the protein at termination of the algorithm. ' +
-          'The higher the score, the more relevant the protein.';
-      } else if (this.task.info.algorithm === 'proximity') {
-        this.tableDrugScoreTooltip =
-          'Empirical z-score of mean minimum distance between the drug’s targets and the seeds. ' +
-          'The lower the score, the more relevant the drug.';
-        this.tableProteinScoreTooltip =
-          'Empirical z-score of mean minimum distance between the drug’s targets and the seeds. ' +
-          'The lower the score, the more relevant the drug.';
-      }
+      if (this.tokenType === 'view') {
+        this.loadingScreen.stateUpdate(true);
+        this.task = await this.getView(this.token);
+        console.log(this.task)
+        this.analysis.switchSelection(this.token);
+        this.loadingScreen.stateUpdate(false);
+      } else {
+        this.loadingScreen.stateUpdate(true);
+        this.task = await this.getTask(this.token);
+        this.analysis.switchSelection(this.token);
+
+        if (this.task.info.algorithm === 'degree') {
+          this.tableDrugScoreTooltip =
+            'Normalized number of direct interactions of the drug with the seeds. ' +
+            'The higher the score, the more relevant the drug.';
+          this.tableProteinScoreTooltip =
+            'Normalized number of direct interactions of the protein with the seeds. ' +
+            'The higher the score, the more relevant the protein.';
+        } else if (this.task.info.algorithm === 'closeness' || this.task.info.algorithm === 'quick' || this.task.info.algorithm === 'super') {
+          this.tableDrugScoreTooltip =
+            'Normalized inverse mean distance of the drug to the seeds. ' +
+            'The higher the score, the more relevant the drug.';
+          this.tableProteinScoreTooltip =
+            'Normalized inverse mean distance of the protein to the seeds. ' +
+            'The higher the score, the more relevant the protein.';
+        } else if (this.task.info.algorithm === 'trustrank') {
+          this.tableDrugScoreTooltip =
+            'Amount of ‘trust’ on the drug at termination of the algorithm. ' +
+            'The higher the score, the more relevant the drug.';
+          this.tableProteinScoreTooltip =
+            'Amount of ‘trust’ on the protein at termination of the algorithm. ' +
+            'The higher the score, the more relevant the protein.';
+        } else if (this.task.info.algorithm === 'proximity') {
+          this.tableDrugScoreTooltip =
+            'Empirical z-score of mean minimum distance between the drug’s targets and the seeds. ' +
+            'The lower the score, the more relevant the drug.';
+          this.tableProteinScoreTooltip =
+            'Empirical z-score of mean minimum distance between the drug’s targets and the seeds. ' +
+            'The lower the score, the more relevant the drug.';
+        }
 
-      if (this.task && this.task.info.done) {
-
-        this.loading = true;
-        this.netex.getTaskResult(this.token).then(async result => {
-          this.drugstoneConfig.set_analysisConfig(result.parameters.config);
-          this.result = result;
-          if (this.result.parameters.target === 'drug') {
-            this.legendService.add_to_context('drug');
-          } else {
-            this.legendService.add_to_context('drugTarget');
-          }
-          const nodeAttributes = this.result.nodeAttributes || {};
-
-          this.networkHandler.activeNetwork.seedMap = nodeAttributes.isSeed || {};
-
-          // Reset
-          this.nodeData = {nodes: null, edges: null};
-          this.networkHandler.activeNetwork.networkEl.nativeElement.innerHTML = '';
-          this.networkHandler.activeNetwork.networkInternal = null;
-          // Create
-          await this.createNetwork(this.result).then(nw => {
-            return new Promise<any>((resolve, reject) => {
-
-              const nodes = nw.nodes;
-              const edges = nw.edges;
-              this.networkHandler.activeNetwork.inputNetwork = {nodes: nodes, edges: edges};
-              this.nodeData.nodes = new vis.DataSet(nodes);
-              this.nodeData.edges = new vis.DataSet(edges);
-              const container = this.networkHandler.activeNetwork.networkEl.nativeElement;
-              const isBig = nodes.length > 100 || edges.length > 100;
-              const options = NetworkSettings.getOptions(isBig ? 'analysis-big' : 'analysis', this.drugstoneConfig.currentConfig());
-              // @ts-ignore
-              options.groups = this.drugstoneConfig.currentConfig().nodeGroups;
-              // @ts-ignore
-              for (const g of Object.values(options.groups)) {
+        if (this.task && this.task.info && this.task.info.done) {
+
+          this.loading = true;
+          this.netex.getTaskResult(this.token).then(async result => {
+            this.drugstoneConfig.set_analysisConfig(result.parameters.config);
+            this.result = result;
+            if (this.result.parameters.target === 'drug') {
+              this.legendService.add_to_context('drug');
+            } else {
+              this.legendService.add_to_context('drugTarget');
+            }
+            const nodeAttributes = this.result.nodeAttributes || {};
+
+            this.networkHandler.activeNetwork.seedMap = nodeAttributes.isSeed || {};
+
+            // Reset
+            this.nodeData = {nodes: null, edges: null};
+            this.networkHandler.activeNetwork.networkEl.nativeElement.innerHTML = '';
+            this.networkHandler.activeNetwork.networkInternal = null;
+            // Create
+            await this.createNetwork(this.result).then(nw => {
+              return new Promise<any>((resolve, reject) => {
+
+                const nodes = nw.nodes;
+                const edges = nw.edges;
+                this.networkHandler.activeNetwork.inputNetwork = {nodes: nodes, edges: edges};
+                this.nodeData.nodes = new vis.DataSet(nodes);
+                this.nodeData.edges = new vis.DataSet(edges);
+                const container = this.networkHandler.activeNetwork.networkEl.nativeElement;
+                const isBig = nodes.length > 100 || edges.length > 100;
+                const options = NetworkSettings.getOptions(isBig ? 'analysis-big' : 'analysis', this.drugstoneConfig.currentConfig());
                 // @ts-ignore
-                delete g.renderer;
-              }
-              if (this.drugstoneConfig.config.physicsOn) {
-                this.drugstoneConfig.config.physicsOn = !isBig;
-              }
-              this.networkHandler.activeNetwork.networkInternal = new vis.Network(container, this.nodeData, options);
-
-              if (isBig) {
-                resolve(nodes);
-              }
-              this.networkHandler.activeNetwork.networkInternal.once('stabilizationIterationsDone', async () => {
-                if (!this.drugstoneConfig.config.physicsOn || this.networkHandler.activeNetwork.isBig()) {
-                  this.networkHandler.activeNetwork.updatePhysicsEnabled(false);
+                options.groups = this.drugstoneConfig.currentConfig().nodeGroups;
+                // @ts-ignore
+                for (const g of Object.values(options.groups)) {
+                  // @ts-ignore
+                  delete g.renderer;
+                }
+                if (this.drugstoneConfig.config.physicsOn) {
+                  this.drugstoneConfig.config.physicsOn = !isBig;
                 }
-                this.networkHandler.updateAdjacentNodes(this.networkHandler.activeNetwork.isBig()).then(() => {
+                this.networkHandler.activeNetwork.networkInternal = new vis.Network(container, this.nodeData, options);
+
+                if (isBig) {
                   resolve(nodes);
-                });
-              });
-            }).then(nodes => {
-
-              this.tableDrugs = nodes.filter(e => e.drugstoneId && e.drugstoneType === 'drug');
-              this.tableDrugs.forEach((r) => {
-                r.rawScore = r.score;
-              });
-              // @ts-ignore
-              this.tableDrugs.sort((a, b) => b.score - a.score);
-              this.rankTable(this.tableDrugs);
-              this.tableProteins = nodes.filter(e => e.drugstoneId && e.drugstoneType === 'protein');
-              this.tableSelectedProteins = [];
-              this.tableProteins.forEach((r) => {
-                r.rawScore = r.score;
-                r.isSeed = this.networkHandler.activeNetwork.seedMap[r.id];
-                const wrapper = getWrapperFromNode(r);
-                if (this.analysis.inSelection(wrapper)) {
-                  this.tableSelectedProteins.push(r);
                 }
-              });
-              this.tableProteins.sort((a, b) => b.score - a.score);
-              this.rankTable(this.tableProteins);
-
-              this.tableHasScores = ['trustrank', 'closeness', 'degree', 'betweenness', 'quick', 'super']
-                .indexOf(this.task.info.algorithm) !== -1;
-              if (this.tableHasScores) {
-                this.toggleNormalization(true);
-              }
-              this.networkHandler.activeNetwork.networkInternal.setData({nodes: undefined, edge: undefined});
-              setTimeout(() => {
-                this.networkHandler.activeNetwork.networkInternal.setData(this.nodeData);
-              }, 1000);
-
-              this.networkHandler.activeNetwork.networkInternal.on('deselectNode', (properties) => {
-                this.showDetailsChange.emit(null);
-              });
-
-              this.networkHandler.activeNetwork.networkInternal.on('doubleClick', (properties) => {
-                const nodeIds: Array<string> = properties.nodes;
-                if (nodeIds.length > 0) {
-                  const nodeId = nodeIds[0];
-                  const node = this.nodeData.nodes.get(nodeId);
-                  if (node.drugstoneId === undefined || node.nodeType === 'drug' || node.drugstoneType !== 'protein') {
-                    this.analysis.unmappedNodeToast();
-                    return;
+                this.networkHandler.activeNetwork.networkInternal.once('stabilizationIterationsDone', async () => {
+                  if (!this.drugstoneConfig.config.physicsOn || this.networkHandler.activeNetwork.isBig()) {
+                    this.networkHandler.activeNetwork.updatePhysicsEnabled(false);
                   }
-                  const wrapper = getWrapperFromNode(node);
+                  this.networkHandler.updateAdjacentNodes(this.networkHandler.activeNetwork.isBig()).then(() => {
+                    resolve(nodes);
+                  });
+                });
+              }).then(nodes => {
+
+                this.tableDrugs = nodes.filter(e => e.drugstoneId && e.drugstoneType === 'drug');
+                this.tableDrugs.forEach((r) => {
+                  r.rawScore = r.score;
+                });
+                // @ts-ignore
+                this.tableDrugs.sort((a, b) => b.score - a.score);
+                this.rankTable(this.tableDrugs);
+                this.tableProteins = nodes.filter(e => e.drugstoneId && e.drugstoneType === 'protein');
+                this.tableSelectedProteins = [];
+                this.tableProteins.forEach((r) => {
+                  r.rawScore = r.score;
+                  r.isSeed = this.networkHandler.activeNetwork.seedMap[r.id];
+                  const wrapper = getWrapperFromNode(r);
                   if (this.analysis.inSelection(wrapper)) {
-                    this.analysis.removeItems([wrapper]);
-                    this.analysis.getCount();
-                  } else {
-                    this.analysis.addItems([wrapper]);
-                    this.analysis.getCount();
-                  }
-                }
-              });
-
-              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);
-                } else {
-                  this.networkHandler.activeNetwork.activeEdge = null;
-                  const selectedNodes = this.nodeData.nodes.get(properties.nodes);
-                  if (selectedNodes.length > 0) {
-                    this.showDetailsChange.emit(getWrapperFromNode(selectedNodes[0]));
-                  } else {
-                    this.showDetailsChange.emit(null);
+                    this.tableSelectedProteins.push(r);
                   }
-                }
-              });
+                });
+                this.tableProteins.sort((a, b) => b.score - a.score);
+                this.rankTable(this.tableProteins);
 
-              this.analysis.subscribeList((items, selected) => {
-                // return if analysis panel is closed or no nodes are loaded
-                if (!this.token) {
-                  return;
+                this.tableHasScores = ['trustrank', 'closeness', 'degree', 'betweenness', 'quick', 'super']
+                  .indexOf(this.task.info.algorithm) !== -1;
+                if (this.tableHasScores) {
+                  this.toggleNormalization(true);
                 }
+                this.networkHandler.activeNetwork.networkInternal.setData({nodes: undefined, edge: undefined});
+                setTimeout(() => {
+                  this.networkHandler.activeNetwork.networkInternal.setData(this.nodeData);
+                }, 1000);
 
-                if (selected !== null) {
-                  const updatedNodes: Node[] = [];
-                  for (const item of items) {
-                    const node = this.nodeData.nodes.get(item.id);
-                    if (!node) {
-                      continue;
-                    }
-                    const pos = this.networkHandler.activeNetwork.networkInternal.getPositions([item.id]);
-                    node.x = pos[item.id].x;
-                    node.y = pos[item.id].y;
-                    const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false;
-                    const nodeStyled = NetworkSettings.getNodeStyle(
-                      node,
-                      this.drugstoneConfig.currentConfig(),
-                      isSeed,
-                      selected,
-                      this.networkHandler.activeNetwork.getGradient(item.id),
-                      this.networkHandler.activeNetwork.nodeRenderer
-                    );
-                    updatedNodes.push(nodeStyled);
-                  }
-                  this.nodeData.nodes.update(updatedNodes);
-
-                  const proteinSelection = this.tableSelectedProteins;
-                  for (const item of items) {
-                    // TODO: Refactor!
-                    const found = proteinSelection.findIndex((i) => getProteinNodeId(i) === item.id);
-                    const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.id);
-                    if (selected && found === -1 && tableItem) {
-                      proteinSelection.push(tableItem);
+                this.networkHandler.activeNetwork.networkInternal.on('deselectNode', (properties) => {
+                  this.showDetailsChange.emit(null);
+                });
+
+                this.networkHandler.activeNetwork.networkInternal.on('doubleClick', (properties) => {
+                  const nodeIds: Array<string> = properties.nodes;
+                  if (nodeIds.length > 0) {
+                    const nodeId = nodeIds[0];
+                    const node = this.nodeData.nodes.get(nodeId);
+                    if (node.drugstoneId === undefined || node.nodeType === 'drug' || node.drugstoneType !== 'protein') {
+                      this.analysis.unmappedNodeToast();
+                      return;
                     }
-                    if (!selected && found !== -1 && tableItem) {
-                      proteinSelection.splice(found, 1);
+                    const wrapper = getWrapperFromNode(node);
+                    if (this.analysis.inSelection(wrapper)) {
+                      this.analysis.removeItems([wrapper]);
+                      this.analysis.getCount();
+                    } else {
+                      this.analysis.addItems([wrapper]);
+                      this.analysis.getCount();
                     }
                   }
-                  this.tableSelectedProteins = [...proteinSelection];
-                } else {
-                  // else: selected is null
-                  const updatedNodes = [];
-                  this.nodeData.nodes.forEach((node) => {
-                    const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false;
-                    if (!isSeed) {
-                      return;
+                });
+
+                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);
+                  } else {
+                    this.networkHandler.activeNetwork.activeEdge = null;
+                    const selectedNodes = this.nodeData.nodes.get(properties.nodes);
+                    if (selectedNodes.length > 0) {
+                      this.showDetailsChange.emit(getWrapperFromNode(selectedNodes[0]));
+                    } else {
+                      this.showDetailsChange.emit(null);
                     }
-                    const nodeStyled = NetworkSettings.getNodeStyle(
-                      node,
-                      this.drugstoneConfig.currentConfig(),
-                      isSeed,
-                      selected,
-                      this.networkHandler.activeNetwork.getGradient(node.id),
-                      this.networkHandler.activeNetwork.nodeRenderer
-                    );
-                    updatedNodes.push(nodeStyled);
-                  });
-                  this.nodeData.nodes.update(updatedNodes);
+                  }
+                });
+
+                this.analysis.subscribeList((items, selected) => {
+                  // return if analysis panel is closed or no nodes are loaded
+                  if (!this.token) {
+                    return;
+                  }
 
-                  const proteinSelection = [];
-                  for (const item of items) {
-                    const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.id);
-                    if (tableItem) {
-                      proteinSelection.push(tableItem);
+                  if (selected !== null) {
+                    const updatedNodes: Node[] = [];
+                    for (const item of items) {
+                      const node = this.nodeData.nodes.get(item.id);
+                      if (!node) {
+                        continue;
+                      }
+                      const pos = this.networkHandler.activeNetwork.networkInternal.getPositions([item.id]);
+                      node.x = pos[item.id].x;
+                      node.y = pos[item.id].y;
+                      const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false;
+                      const nodeStyled = NetworkSettings.getNodeStyle(
+                        node,
+                        this.drugstoneConfig.currentConfig(),
+                        isSeed,
+                        selected,
+                        this.networkHandler.activeNetwork.getGradient(item.id),
+                        this.networkHandler.activeNetwork.nodeRenderer
+                      );
+                      updatedNodes.push(nodeStyled);
+                    }
+                    this.nodeData.nodes.update(updatedNodes);
+
+                    const proteinSelection = this.tableSelectedProteins;
+                    for (const item of items) {
+                      // TODO: Refactor!
+                      const found = proteinSelection.findIndex((i) => getProteinNodeId(i) === item.id);
+                      const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.id);
+                      if (selected && found === -1 && tableItem) {
+                        proteinSelection.push(tableItem);
+                      }
+                      if (!selected && found !== -1 && tableItem) {
+                        proteinSelection.splice(found, 1);
+                      }
                     }
+                    this.tableSelectedProteins = [...proteinSelection];
+                  } else {
+                    // else: selected is null
+                    const updatedNodes = [];
+                    this.nodeData.nodes.forEach((node) => {
+                      const isSeed = this.networkHandler.activeNetwork.highlightSeeds ? this.networkHandler.activeNetwork.seedMap[node.id] : false;
+                      if (!isSeed) {
+                        return;
+                      }
+                      const nodeStyled = NetworkSettings.getNodeStyle(
+                        node,
+                        this.drugstoneConfig.currentConfig(),
+                        isSeed,
+                        selected,
+                        this.networkHandler.activeNetwork.getGradient(node.id),
+                        this.networkHandler.activeNetwork.nodeRenderer
+                      );
+                      updatedNodes.push(nodeStyled);
+                    });
+                    this.nodeData.nodes.update(updatedNodes);
+
+                    const proteinSelection = [];
+                    for (const item of items) {
+                      const tableItem = this.tableProteins.find((i) => getProteinNodeId(i) === item.id);
+                      if (tableItem) {
+                        proteinSelection.push(tableItem);
+                      }
+                    }
+                    this.tableSelectedProteins = [...proteinSelection];
                   }
-                  this.tableSelectedProteins = [...proteinSelection];
+                });
+                this.emitVisibleItems(true);
+              }).then(() => {
+                if (!['quick', 'super', 'connect', 'connectSelected'].includes(this.task.info.algorithm)) {
+                  return;
                 }
-              });
-              this.emitVisibleItems(true);
-            }).then(() => {
-              if (!['quick', 'super', 'connect', 'connectSelected'].includes(this.task.info.algorithm)) {
-                return;
-              }
-              this.netex.getAlgorithmDefaults(this.task.info.algorithm).then(response => {
-                this.algorithmDefault = response
-              });
-            }).catch(console.error);
+                this.netex.getAlgorithmDefaults(this.task.info.algorithm).then(response => {
+                  this.algorithmDefault = response;
+                });
+              }).catch(console.error);
+            });
+            this.loadingScreen.stateUpdate(false);
           });
-          this.loadingScreen.stateUpdate(false);
-        });
+        }
       }
     }
 
@@ -404,6 +413,10 @@ export class AnalysisPanelComponent implements OnInit, OnChanges, AfterViewInit
     }
   }
 
+  private async getView(token: string): Promise<any> {
+    return await this.http.get(`${this.netex.getBackend()}view/?token=${token}`).toPromise();
+  }
+
   private async getTask(token: string): Promise<any> {
     return await this.http.get(`${this.netex.getBackend()}task/?token=${token}`).toPromise();
   }
diff --git a/src/app/components/analysis-panel/view-list/view-list.component.html b/src/app/components/analysis-panel/view-list/view-list.component.html
new file mode 100644
index 00000000..f9d5b932
--- /dev/null
+++ b/src/app/components/analysis-panel/view-list/view-list.component.html
@@ -0,0 +1,75 @@
+<div class="content">
+  <div
+    class="list is-hoverable"
+    [ngClass]="{ 'text-normal': drugstoneConfig.smallStyle }"
+  >
+    <a
+      *ngFor="let task of analysis.selectionTokens"
+      class="list-item box small-box"
+      [class.is-active]="task === token"
+    >
+<!--      <div *ngIf="!task">-->
+<!--        <div class="columns mb-0">-->
+<!--          <div class="column">-->
+<!--            <app-fa-solid-icon-->
+<!--              classString="is-pulled-right has-text-warning"-->
+<!--              icon="pause"-->
+<!--            ></app-fa-solid-icon>-->
+<!--          </div>-->
+<!--        </div>-->
+<!--        <div class="columns mb-0">-->
+<!--          <div class="column pt-0 pb-0">-->
+<!--            <a-->
+<!--              (click)="analysis.removeSelection(task)"-->
+<!--              class="has-text-danger is-pulled-right"-->
+<!--            >-->
+<!--              <app-fa-solid-icon icon="trash"></app-fa-solid-icon>-->
+<!--            </a>-->
+<!--          </div>-->
+<!--        </div>-->
+<!--      </div>-->
+      <div
+        *ngIf="task"
+        (click)="open(task)"
+        pTooltip="Show selection view"
+        [tooltipStyleClass]="'drgstn drgstn-tooltip drgstn-tooltip-top'"
+        tooltipPosition="top"
+      >
+        <div class="columns mb-0">
+          <div class="column is-8">
+<!--            <span class="is-capitalized"-->
+<!--            ><app-fa-solid-icon-->
+<!--              icon="crosshairs"-->
+<!--              *ngIf="task.info.target === 'drug-target'"-->
+<!--            ></app-fa-solid-icon>-->
+<!--              <app-fa-solid-icon-->
+<!--                icon="capsules"-->
+<!--                *ngIf="task.info.target === 'drug'"-->
+<!--              ></app-fa-solid-icon>-->
+<!--              {{ algorithmNames[task.info.algorithm] }}</span-->
+<!--            >-->
+          </div>
+          <div class="column">
+            <app-fa-solid-icon
+              classString="is-pulled-right has-text-success"
+              icon="check"
+            ></app-fa-solid-icon>
+          </div>
+        </div>
+        <div class="columns mb-0">
+<!--          <div class="column is-8 pt-0 pb-0">-->
+<!--            <small>Created {{ task.info.finishedAt | date: "short" }}</small>-->
+<!--          </div>-->
+          <div class="column pt-0 pb-0">
+            <a
+              (click)="analysis.removeTask(task)"
+              class="has-text-danger is-pulled-right"
+            >
+              <app-fa-solid-icon icon="trash"></app-fa-solid-icon>
+            </a>
+          </div>
+        </div>
+      </div>
+    </a>
+  </div>
+</div>
diff --git a/src/app/components/analysis-panel/view-list/view-list.component.scss b/src/app/components/analysis-panel/view-list/view-list.component.scss
new file mode 100644
index 00000000..e69de29b
diff --git a/src/app/components/analysis-panel/view-list/view-list.component.spec.ts b/src/app/components/analysis-panel/view-list/view-list.component.spec.ts
new file mode 100644
index 00000000..2b65a6b4
--- /dev/null
+++ b/src/app/components/analysis-panel/view-list/view-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ViewListComponent } from './view-list.component';
+
+describe('ViewListComponent', () => {
+  let component: ViewListComponent;
+  let fixture: ComponentFixture<ViewListComponent>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [ ViewListComponent ]
+    })
+    .compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ViewListComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/src/app/components/analysis-panel/view-list/view-list.component.ts b/src/app/components/analysis-panel/view-list/view-list.component.ts
new file mode 100644
index 00000000..fda85513
--- /dev/null
+++ b/src/app/components/analysis-panel/view-list/view-list.component.ts
@@ -0,0 +1,22 @@
+import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
+import {DrugstoneConfigService} from '../../../services/drugstone-config/drugstone-config.service';
+import {AnalysisService} from '../../../services/analysis/analysis.service';
+
+@Component({
+  selector: 'app-view-list',
+  templateUrl: './view-list.component.html',
+  styleUrls: ['./view-list.component.scss']
+})
+export class ViewListComponent implements OnInit {
+  @Input() token: string;
+  @Output() tokenChange: EventEmitter<string> = new EventEmitter();
+  constructor(public drugstoneConfig: DrugstoneConfigService, public analysis: AnalysisService) { }
+
+  ngOnInit(): void {
+  }
+
+  open(token) {
+    this.token = token;
+    this.tokenChange.emit(token);
+  }
+}
diff --git a/src/app/config.ts b/src/app/config.ts
index e6f08392..73dff93a 100644
--- a/src/app/config.ts
+++ b/src/app/config.ts
@@ -54,6 +54,7 @@ export interface IConfig {
   showAdvAnalysis: boolean;
   showAdvAnalysisContent: Array<AdvAnalysisContentTypes>;
   showTasks: boolean;
+  showViews: boolean;
   showSelection: boolean;
   showNetworkMenu: false | 'left' | 'right';
   expandNetworkMenu: boolean;
@@ -153,6 +154,7 @@ export const defaultConfig: IConfig = {
   showAdvAnalysisContent: ['drug-search', 'drug-target-search', 'enrichment-gprofiler', 'enrichment-digest', 'search-ndex'],
   showSelection: true,
   showTasks: true,
+  showViews: true,
   showNetworkMenu: 'right',
   showLegend: true,
   expandNetworkMenu: true,
diff --git a/src/app/pages/explorer-page/explorer-page.component.html b/src/app/pages/explorer-page/explorer-page.component.html
index 1adab2e1..9f3269d4 100644
--- a/src/app/pages/explorer-page/explorer-page.component.html
+++ b/src/app/pages/explorer-page/explorer-page.component.html
@@ -319,55 +319,6 @@
           </div>
         </div>
 
-        <div *ngIf="drugstoneConfig.config.showTasks" class="card bar-large">
-          <header
-            class="card-header"
-            [ngClass]="{ 'b-text-small': drugstoneConfig.smallStyle }"
-          >
-            <p class="card-header-title">
-              <app-fa-solid-icon icon="tasks"></app-fa-solid-icon>
-              Tasks ({{ analysis.tasks != null ? analysis.tasks.length : 0 }})
-            </p>
-            <a
-              (click)="collapseTask = !collapseTask"
-              data-action="collapse"
-              class="card-header-icon is-hidden-fullscreen"
-              aria-label="more options"
-            >
-              <app-fa-solid-icon
-                *ngIf="collapseTask"
-                icon="angle-down"
-              ></app-fa-solid-icon>
-              <app-fa-solid-icon
-                *ngIf="!collapseTask"
-                icon="angle-left"
-              ></app-fa-solid-icon>
-            </a>
-          </header>
-          <div *ngIf="collapseTask">
-            <div
-              class="card-content overflow task-list-container"
-              *ngIf="analysis.tasks && analysis.tasks.length > 0"
-            >
-              <app-task-list [(token)]="selectedAnalysisToken"></app-task-list>
-            </div>
-            <footer class="card-footer">
-              <a
-                *ngIf="analysis.tasks && analysis.tasks.length > 0"
-                (click)="
-                  analysis.removeAllTasks(); selectedAnalysisToken = null
-                "
-                class="card-footer-item has-text-danger"
-                pTooltip="Delete all tasks."
-                [tooltipStyleClass]="'drgstn drgstn-tooltip drgstn-tooltip-top'"
-                tooltipPosition="top"
-              >
-                <app-fa-solid-icon icon="trash"></app-fa-solid-icon>
-                <span> Delete all </span>
-              </a>
-            </footer>
-          </div>
-        </div>
 
         <div
           *ngIf="drugstoneConfig.config.showSelection"
@@ -513,20 +464,130 @@
                 <app-fa-solid-icon icon="broom"></app-fa-solid-icon>
                 <span> Reset </span>
               </a>
+              <a
+                *ngIf="analysis.getSelection().length"
+                (click)="analysis.viewFromSelection()"
+                class="card-footer-item has-text-danger"
+                tooltipPosition="top"
+                pTooltip="Create view from the selection."
+              >
+                <app-fa-solid-icon icon="check"></app-fa-solid-icon>
+                <span> Save selection </span>
+              </a>
             </footer>
 
             <!-- </div> -->
             <!-- </div>  -->
           </div>
         </div>
+        <div *ngIf="drugstoneConfig.config.showViews" class="card bar-large">
+          <header
+            class="card-header"
+            [ngClass]="{ 'b-text-small': drugstoneConfig.smallStyle }"
+          >
+            <p class="card-header-title">
+              <app-fa-solid-icon icon="tasks"></app-fa-solid-icon>
+              Views ({{ analysis.selectionTokens != null ? analysis.selectionTokens.length : 0 }})
+            </p>
+            <a
+              (click)="collapseViews = !collapseViews"
+              data-action="collapse"
+              class="card-header-icon is-hidden-fullscreen"
+              aria-label="more options"
+            >
+              <app-fa-solid-icon
+                *ngIf="collapseViews"
+                icon="angle-down"
+              ></app-fa-solid-icon>
+              <app-fa-solid-icon
+                *ngIf="!collapseViews"
+                icon="angle-left"
+              ></app-fa-solid-icon>
+            </a>
+          </header>
+          <div *ngIf="collapseViews">
+            <div
+              class="card-content overflow task-list-container"
+              *ngIf="analysis.selectionTokens && analysis.selectionTokens.length > 0"
+            >
+              <app-view-list [(token)]="selectedViewToken"></app-view-list>
+            </div>
+            <footer class="card-footer">
+              <a
+                *ngIf="analysis.selectionTokens && analysis.selectionTokens.length > 0"
+                (click)="
+                  analysis.removeAllSelections(); selectedViewToken = null
+                "
+                class="card-footer-item has-text-danger"
+                pTooltip="Delete all selection views."
+                [tooltipStyleClass]="'drgstn drgstn-tooltip drgstn-tooltip-top'"
+                tooltipPosition="top"
+              >
+                <app-fa-solid-icon icon="trash"></app-fa-solid-icon>
+                <span> Delete all </span>
+              </a>
+            </footer>
+          </div>
+        </div>
+        <div *ngIf="drugstoneConfig.config.showTasks" class="card bar-large">
+          <header
+            class="card-header"
+            [ngClass]="{ 'b-text-small': drugstoneConfig.smallStyle }"
+          >
+            <p class="card-header-title">
+              <app-fa-solid-icon icon="tasks"></app-fa-solid-icon>
+              Tasks ({{ analysis.tasks != null ? analysis.tasks.length : 0 }})
+            </p>
+            <a
+              (click)="collapseTask = !collapseTask"
+              data-action="collapse"
+              class="card-header-icon is-hidden-fullscreen"
+              aria-label="more options"
+            >
+              <app-fa-solid-icon
+                *ngIf="collapseTask"
+                icon="angle-down"
+              ></app-fa-solid-icon>
+              <app-fa-solid-icon
+                *ngIf="!collapseTask"
+                icon="angle-left"
+              ></app-fa-solid-icon>
+            </a>
+          </header>
+          <div *ngIf="collapseTask">
+            <div
+              class="card-content overflow task-list-container"
+              *ngIf="analysis.tasks && analysis.tasks.length > 0"
+            >
+              <app-task-list [(token)]="selectedAnalysisToken"></app-task-list>
+            </div>
+            <footer class="card-footer">
+              <a
+                *ngIf="analysis.tasks && analysis.tasks.length > 0"
+                (click)="
+                  analysis.removeAllTasks(); selectedAnalysisToken = null
+                "
+                class="card-footer-item has-text-danger"
+                pTooltip="Delete all tasks."
+                [tooltipStyleClass]="'drgstn drgstn-tooltip drgstn-tooltip-top'"
+                tooltipPosition="top"
+              >
+                <app-fa-solid-icon icon="trash"></app-fa-solid-icon>
+                <span> Delete all </span>
+              </a>
+            </footer>
+          </div>
+        </div>
+
       </div>
 
       <!-- Start network block -->
       <div class="drugstone network column" id="main-column">
         <!-- analysis panel with analysis network -->
-        <div *ngIf="selectedAnalysisToken">
+        <div *ngIf="selectedToken">
           <app-analysis-panel
-            [(token)]="selectedAnalysisToken"
+            [(token)]="selectedToken"
+            [(tokenType)]="selectedAnalysisTokenType"
             (showDetailsChange)="
               networkHandler.activeNetwork.selectedWrapper = $event
             "
diff --git a/src/app/pages/explorer-page/explorer-page.component.ts b/src/app/pages/explorer-page/explorer-page.component.ts
index e28cb8eb..c316d55e 100644
--- a/src/app/pages/explorer-page/explorer-page.component.ts
+++ b/src/app/pages/explorer-page/explorer-page.component.ts
@@ -107,6 +107,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
 
   public collapseAnalysis = true;
   public collapseTask = true;
+  public collapseViews = true;
   public collapseSelection = true;
   public collapseBaitFilter = true;
   public collapseQuery = true;
@@ -127,8 +128,41 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
 
 
   public showCustomProteinsDialog = false;
+  public selectedAnalysisTokenType: 'task' | 'view' | null = null;
+  // public selectedAnalysisToken: string | null = null;
+  public selectedToken: string | null = null;
 
-  public selectedAnalysisToken: string | null = null;
+  public set selectedViewToken(token: string | null) {
+    if (token == null || token.length === 0) {
+      this.selectedToken = null;
+    } else {
+      this.selectedToken = token;
+      this.selectedAnalysisTokenType = 'view';
+    }
+  }
+
+  public set selectedAnalysisToken(token: string | null) {
+    if (token == null || token.length === 0) {
+      this.selectedToken = null;
+    } else {
+      this.selectedToken = token;
+      this.selectedAnalysisTokenType = 'task';
+    }
+  }
+
+  public get selectedAnalysisToken() {
+    if (this.selectedAnalysisTokenType === 'view') {
+      return null;
+    }
+    return this.selectedToken;
+  }
+
+  public get selectedViewToken() {
+    if (this.selectedAnalysisTokenType === 'task') {
+      return null;
+    }
+    return this.selectedToken;
+  }
 
   @Input() set taskId(token: string | null) {
     if (token == null || token.length === 0) {
diff --git a/src/app/services/analysis/analysis.service.ts b/src/app/services/analysis/analysis.service.ts
index 4afebd0a..81422449 100644
--- a/src/app/services/analysis/analysis.service.ts
+++ b/src/app/services/analysis/analysis.service.ts
@@ -61,7 +61,9 @@ export class AnalysisService {
   private selections = new Map<string, Map<string, Wrapper>>();
 
   public tokens: string[] = [];
+  public selectionTokens: string[] = [];
   private tokensCookieKey = `drugstone-tokens-${window.location.host}`;
+  private selectionsCookieKey = `drugstone-selections-${window.location.host}`;
   private tokensFinishedCookieKey = `drugstone-finishedTokens-${window.location.host}`;
   public finishedTokens: string[] = [];
   public tasks: Task[] = [];
@@ -81,11 +83,16 @@ export class AnalysisService {
     public networkHandler: NetworkHandlerService
   ) {
     const tokens = localStorage.getItem(this.tokensCookieKey);
+    const selections = localStorage.getItem(this.selectionsCookieKey);
     const finishedTokens = localStorage.getItem(this.tokensFinishedCookieKey);
 
     if (tokens) {
       this.tokens = JSON.parse(tokens);
     }
+    if (selections) {
+      this.selectionTokens = JSON.parse(selections);
+      console.log(this.selectionTokens);
+    }
     if (finishedTokens) {
       this.finishedTokens = JSON.parse(finishedTokens);
     }
@@ -103,6 +110,11 @@ export class AnalysisService {
     localStorage.setItem(this.tokensCookieKey, JSON.stringify(this.tokens));
   }
 
+  removeSelection(token) {
+    this.selectionTokens = this.selectionTokens.filter((item) => item !== token);
+    localStorage.setItem(this.selectionsCookieKey, JSON.stringify(this.selectionTokens));
+  }
+
   removeAllTasks() {
     this.tasks = [];
     this.finishedTokens = [];
@@ -110,12 +122,23 @@ export class AnalysisService {
     localStorage.removeItem(this.tokensCookieKey);
   }
 
+  removeAllSelections() {
+    this.selectionTokens = [];
+    localStorage.removeItem(this.selectionsCookieKey);
+  }
+
   async getTasks() {
     return await this.netex.getTasks(this.finishedTokens.length > 0 && this.tasks.length === 0 ? this.tokens : this.tokens.filter(t => this.finishedTokens.indexOf(t) === -1)).catch((e) => {
       clearInterval(this.intervalId);
     });
   }
 
+  async getViews() {
+    return await this.netex.getTasks(this.finishedTokens.length > 0 && this.tasks.length === 0 ? this.tokens : this.tokens.filter(t => this.finishedTokens.indexOf(t) === -1)).catch((e) => {
+      clearInterval(this.intervalId);
+    });
+  }
+
   public getTissues(): Tissue[] {
     return this.tissues;
   }
@@ -244,6 +267,34 @@ export class AnalysisService {
     this.selectedItems.clear();
   }
 
+  async viewFromSelection() {
+
+    const seeds = this.getSelection().map((item) => item.id);
+    const seedsFiltered = seeds.filter(el => el != null);
+    const initialNetwork = this.networkHandler.activeNetwork.getResetInputNetwork();
+    const filteredNodes = initialNetwork.nodes.filter(node => seedsFiltered.includes(node.id));
+    const filteredEdges = initialNetwork.edges.filter(edge => seedsFiltered.includes(edge.from) && seedsFiltered.includes(edge.to));
+    this.resetSelection();
+    const payload: any = {
+      config: this.drugstoneConfig.currentConfig(),
+      network: {nodes: filteredNodes, edges: filteredEdges}
+    };
+    const resp = await this.http.post<any>(`${this.netex.getBackend()}save_selection`, payload).toPromise();
+    // @ts-ignore
+    console.log(resp.token);
+    // @ts-ignore
+    this.selectionTokens.push(resp.token);
+    localStorage.setItem(this.selectionsCookieKey, JSON.stringify(this.selectionTokens));
+
+    this.toast.setNewToast({
+      message: 'Analysis task started. This may take a while. ' +
+        `Once the computation finished you can view the results in the task list to the ${this.drugstoneConfig.config.showSidebar}.`,
+      type: 'success'
+    });
+    // @ts-ignore
+    return resp.token;
+  }
+
   idInSelection(nodeId: string): boolean {
     return this.selectedItems.has(nodeId);
   }
-- 
GitLab