Skip to content
Snippets Groups Projects
Commit eda342f4 authored by Mhaned Oubounyt's avatar Mhaned Oubounyt
Browse files

Merge branch 'protein-selection' into 'master'

Protein selection

See merge request covid-19/frontend!12
parents 8bed0cdc 02074689
Branches
No related tags found
No related merge requests found
import {Injectable} from '@angular/core';
import {ProteinGroup} from './pages/protein-network';
import {Subject} from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AnalysisService {
private selectedProteins = new Map<string, ProteinGroup>();
private selectSubject = new Subject<{protein: ProteinGroup, selected: boolean}>();
constructor() {
}
addProtein(protein: ProteinGroup) {
if (!this.inSelection(protein)) {
this.selectedProteins.set(`${protein.groupId}`, protein);
this.selectSubject.next({protein, selected: true});
}
}
inSelection(protein: ProteinGroup): boolean {
return this.selectedProteins.has(`${protein.groupId}`);
}
removeProtein(protein: ProteinGroup) {
if (this.selectedProteins.delete(`${protein.groupId}`)) {
this.selectSubject.next({protein, selected: false});
}
}
getSelection(): ProteinGroup[] {
return Array.from(this.selectedProteins.values());
}
getCount(): number {
return this.selectedProteins.size;
}
subscribe(cb: (protein: ProteinGroup, selected: boolean) => void) {
this.selectSubject.subscribe((event) => {
cb(event.protein, event.selected);
});
}
}
...@@ -10,6 +10,7 @@ import {ExplorerPageComponent} from './pages/explorer-page/explorer-page.compone ...@@ -10,6 +10,7 @@ import {ExplorerPageComponent} from './pages/explorer-page/explorer-page.compone
import {AboutPageComponent} from './pages/about-page/about-page.component'; import {AboutPageComponent} from './pages/about-page/about-page.component';
import {HomePageComponent} from './pages/home-page/home-page.component'; import {HomePageComponent} from './pages/home-page/home-page.component';
import {HttpClientModule} from '@angular/common/http'; import {HttpClientModule} from '@angular/common/http';
import {ProteinAnalysisComponent} from './components/protein-analysis/protein-analysis.component';
@NgModule({ @NgModule({
declarations: [ declarations: [
...@@ -17,6 +18,7 @@ import {HttpClientModule} from '@angular/common/http'; ...@@ -17,6 +18,7 @@ import {HttpClientModule} from '@angular/common/http';
ExplorerPageComponent, ExplorerPageComponent,
AboutPageComponent, AboutPageComponent,
HomePageComponent, HomePageComponent,
ProteinAnalysisComponent,
], ],
imports: [ imports: [
BrowserModule, BrowserModule,
......
<div class="modal" [class.is-active]="show">
<div class="modal-background"></div>
<div class="modal-card">
<header class="modal-card-head">
<p class="modal-card-title">Launch Protein Analysis</p>
<button class="delete" aria-label="close" (click)="close()"></button>
</header>
<section class="modal-card-body">
<h4 class="title is-4">Selection</h4>
<table class="table">
<thead>
<tr>
<td>AC</td>
<td>Actions</td>
</tr>
</thead>
<tbody>
<tr *ngFor="let p of analysis.getSelection()">
<td>{{p.name}}</td>
<td>
<button (click)="analysis.removeProtein(p)" class="button is-small is-danger">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</section>
<footer class="modal-card-foot">
<button class="button is-success">
<span class="icon"><i class="fa fa-play"></i></span>
<span>Multi Steiner</span>
</button>
<button class="button is-success">
<span class="icon"><i class="fa fa-play"></i></span>
<span>Key Pathway Miner</span>
</button>
</footer>
</div>
</div>
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {AnalysisService} from '../../analysis.service';
@Component({
selector: 'app-protein-analysis',
templateUrl: './protein-analysis.component.html',
styleUrls: ['./protein-analysis.component.scss']
})
export class ProteinAnalysisComponent implements OnInit {
@Input()
public show = false;
@Output()
public showChange = new EventEmitter<boolean>();
constructor(public analysis: AnalysisService) {
}
ngOnInit(): void {
}
public close() {
this.show = false;
this.showChange.emit(this.show);
}
}
<app-protein-analysis [(show)]="showAnalysisDialog"></app-protein-analysis>
<div class="content explorer"> <div class="content explorer">
<div class="content bar-left"> <div class="content bar-left">
...@@ -103,6 +105,26 @@ ...@@ -103,6 +105,26 @@
<div class="content bar-right"> <div class="content bar-right">
<div class="card bar">
<header class="card-header">
<p class="card-header-title">
<span class="icon">
<i class="fas fa-flask" aria-hidden="true"></i>
</span> Analysis
</p>
</header>
<div class="card-content">
<button (click)="showAnalysisDialog = true" class="button is-success" [disabled]="analysis.getCount() === 0">
<span class="icon">
<i class="fa fa-list"></i>
</span>
<span>
Open Protein Selection
</span>
</button>
</div>
</div>
<div class="card bar"> <div class="card bar">
<header class="card-header"> <header class="card-header">
<p class="card-header-title"> <p class="card-header-title">
...@@ -145,6 +167,8 @@ ...@@ -145,6 +167,8 @@
<figure class="image"> <figure class="image">
<img src="assets/boxplot.png" alt="Boxplots"> <img src="assets/boxplot.png" alt="Boxplots">
</figure> </figure>
<button class="button" *ngIf="!inSelection(proteinGroup)" (click)="addToSelection(proteinGroup)">Select for analysis</button>
<button class="button" *ngIf="inSelection(proteinGroup)" (click)="removeFromSelection(proteinGroup)">Remove from analysis</button>
</div> </div>
</div> </div>
</div> </div>
......
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core'; import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {Effect, ProteinNetwork} from '../protein-network'; import {Effect, ProteinGroup, ProteinNetwork} from '../protein-network';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {ApiService} from '../../api.service'; import {ApiService} from '../../api.service';
import {AnalysisService} from '../../analysis.service';
declare var vis: any; declare var vis: any;
...@@ -19,7 +20,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -19,7 +20,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public proteinGroup = ''; public proteinGroup = '';
public proteinNames: Array<string> = []; public proteinNames: Array<string> = [];
public proteinACs: Array<string> = []; public proteinACs: Array<string> = [];
public baitNames: Array<string> = [];
public baitProteins: Array<{ checked: boolean; data: Effect }> = []; public baitProteins: Array<{ checked: boolean; data: Effect }> = [];
...@@ -39,18 +39,18 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -39,18 +39,18 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
private dumpPositions = false; private dumpPositions = false;
public physicsEnabled = false; public physicsEnabled = false;
public showAnalysisDialog = false;
@ViewChild('network', {static: false}) networkEl: ElementRef; @ViewChild('network', {static: false}) networkEl: ElementRef;
constructor(private http: HttpClient, private route: ActivatedRoute, private router: Router, private api: ApiService) { constructor(private http: HttpClient,
this.groupId = 'IFI16'; private route: ActivatedRoute,
private router: Router,
private api: ApiService,
public analysis: AnalysisService) {
this.geneNames.push('IFI16'); this.geneNames.push('IFI16');
this.proteinNames.push('Gamma-interface-inducible protein 16'); this.proteinNames.push('Gamma-interface-inducible protein 16');
this.proteinACs.push('Q16666'); this.proteinACs.push('Q16666');
this.baitNames.push('Bait Protein 1');
this.baitNames.push('Bait Protein 2');
this.baitNames.push('Bait Protein 3');
this.baitNames.push('Bait Protein 4');
this.baitNames.push('Bait Protein 5');
this.route.queryParams.subscribe(async (params) => { this.route.queryParams.subscribe(async (params) => {
this.dumpPositions = params.dumpPositions; this.dumpPositions = params.dumpPositions;
...@@ -79,6 +79,23 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -79,6 +79,23 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
// this.zoomToNode(proteinGroup) // this.zoomToNode(proteinGroup)
this.showDetails = true; this.showDetails = true;
}); });
this.analysis.subscribe((protein, selected) => {
const nodeId = `pg_${protein.groupId}`;
if (selected) {
const node = this.nodeData.nodes.get(nodeId);
if (node) {
node.color = '#c42eff';
this.nodeData.nodes.update(node);
}
} else {
const node = this.nodeData.nodes.get(nodeId);
if (node) {
node.color = '#e2b600';
this.nodeData.nodes.update(node);
}
}
});
} }
ngOnInit() { ngOnInit() {
...@@ -104,7 +121,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -104,7 +121,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.filterNodes(); this.filterNodes();
} }
public zoomToNode(id: string) { private zoomToNode(id: string) {
const coords = this.network.getPositions(id)[id]; const coords = this.network.getPositions(id)[id];
this.network.moveTo({ this.network.moveTo({
position: {x: coords.x, y: coords.y}, position: {x: coords.x, y: coords.y},
...@@ -117,10 +134,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -117,10 +134,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
return this.groupId; return this.groupId;
} }
public async openSummary(groupId: string) { public async openSummary(groupId: string, zoom: boolean) {
await this.router.navigate(['explorer'], {queryParams: {proteinGroup: groupId}}); await this.router.navigate(['explorer'], {queryParams: {proteinGroup: groupId}});
if (zoom) {
this.zoomToNode(this.proteinGroup); this.zoomToNode(this.proteinGroup);
} }
}
public async closeSummary() { public async closeSummary() {
await this.router.navigate(['explorer']); await this.router.navigate(['explorer']);
...@@ -170,7 +189,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -170,7 +189,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
console.log(id); console.log(id);
if (id.length > 0) { if (id.length > 0) {
console.log('clicked node:', id); console.log('clicked node:', id);
this.openSummary(id[0]); this.openSummary(id[0], false);
} }
}); });
...@@ -239,17 +258,21 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -239,17 +258,21 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
}); });
} }
private mapProteinGroupToNode(proteinGroup: any): any { private mapProteinGroupToNode(proteinGroup: ProteinGroup): any {
let color = '#e2b600';
if (this.analysis.inSelection(proteinGroup)) {
color = '#c42eff';
}
return { return {
id: `pg_${proteinGroup.groupId}`, id: `pg_${proteinGroup.groupId}`,
label: `${proteinGroup.name}`, label: `${proteinGroup.name}`,
size: 10, font: '5px', color: '#e2b600', shape: 'ellipse', shadow: false, size: 10, font: '5px', color, shape: 'ellipse', shadow: false,
x: proteinGroup.x, x: proteinGroup.x,
y: proteinGroup.y y: proteinGroup.y
}; };
} }
private mapEffectToNode(effect: any): any { private mapEffectToNode(effect: Effect): any {
return { return {
id: `eff_${effect.name}`, id: `eff_${effect.name}`,
label: `${effect.name}`, label: `${effect.name}`,
...@@ -291,4 +314,34 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit { ...@@ -291,4 +314,34 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
return x - Math.floor(x); return x - Math.floor(x);
} }
// Selection
// TODO: Improve usage of group ids, revise this after models have been changed to just protein
inSelection(groupIdStr: string): boolean {
if (!this.proteinData || !groupIdStr) {
return false;
}
const groupId = Number(groupIdStr.split('_')[1]);
const protein = this.proteinData.getProteinGroup(groupId);
return this.analysis.inSelection(protein);
}
addToSelection(groupIdStr: string) {
if (!groupIdStr) {
return;
}
const groupId = Number(groupIdStr.split('_')[1]);
const protein = this.proteinData.getProteinGroup(groupId);
this.analysis.addProtein(protein);
}
removeFromSelection(groupIdStr: string) {
if (!groupIdStr) {
return;
}
const groupId = Number(groupIdStr.split('_')[1]);
const protein = this.proteinData.getProteinGroup(groupId);
this.analysis.removeProtein(protein);
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment