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
Tags
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
import {AboutPageComponent} from './pages/about-page/about-page.component';
import {HomePageComponent} from './pages/home-page/home-page.component';
import {HttpClientModule} from '@angular/common/http';
import {ProteinAnalysisComponent} from './components/protein-analysis/protein-analysis.component';
@NgModule({
declarations: [
......@@ -17,6 +18,7 @@ import {HttpClientModule} from '@angular/common/http';
ExplorerPageComponent,
AboutPageComponent,
HomePageComponent,
ProteinAnalysisComponent,
],
imports: [
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 bar-left">
......@@ -103,6 +105,26 @@
<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">
<header class="card-header">
<p class="card-header-title">
......@@ -145,6 +167,8 @@
<figure class="image">
<img src="assets/boxplot.png" alt="Boxplots">
</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>
......
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
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 {ApiService} from '../../api.service';
import {AnalysisService} from '../../analysis.service';
declare var vis: any;
......@@ -19,7 +20,6 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public proteinGroup = '';
public proteinNames: Array<string> = [];
public proteinACs: Array<string> = [];
public baitNames: Array<string> = [];
public baitProteins: Array<{ checked: boolean; data: Effect }> = [];
......@@ -39,18 +39,18 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
private dumpPositions = false;
public physicsEnabled = false;
public showAnalysisDialog = false;
@ViewChild('network', {static: false}) networkEl: ElementRef;
constructor(private http: HttpClient, private route: ActivatedRoute, private router: Router, private api: ApiService) {
this.groupId = 'IFI16';
constructor(private http: HttpClient,
private route: ActivatedRoute,
private router: Router,
private api: ApiService,
public analysis: AnalysisService) {
this.geneNames.push('IFI16');
this.proteinNames.push('Gamma-interface-inducible protein 16');
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.dumpPositions = params.dumpPositions;
......@@ -79,6 +79,23 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
// this.zoomToNode(proteinGroup)
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() {
......@@ -104,7 +121,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.filterNodes();
}
public zoomToNode(id: string) {
private zoomToNode(id: string) {
const coords = this.network.getPositions(id)[id];
this.network.moveTo({
position: {x: coords.x, y: coords.y},
......@@ -117,9 +134,11 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
return this.groupId;
}
public async openSummary(groupId: string) {
public async openSummary(groupId: string, zoom: boolean) {
await this.router.navigate(['explorer'], {queryParams: {proteinGroup: groupId}});
this.zoomToNode(this.proteinGroup);
if (zoom) {
this.zoomToNode(this.proteinGroup);
}
}
public async closeSummary() {
......@@ -170,7 +189,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
console.log(id);
if (id.length > 0) {
console.log('clicked node:', id);
this.openSummary(id[0]);
this.openSummary(id[0], false);
}
});
......@@ -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 {
id: `pg_${proteinGroup.groupId}`,
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,
y: proteinGroup.y
};
}
private mapEffectToNode(effect: any): any {
private mapEffectToNode(effect: Effect): any {
return {
id: `eff_${effect.name}`,
label: `${effect.name}`,
......@@ -260,7 +283,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
}
private mapEdge(edge: any): any {
return {from: `pg_${edge.groupId}`, to: `eff_${edge.effectName}`, color: { color: '#afafaf', highlight: '#854141' }};
return {from: `pg_${edge.groupId}`, to: `eff_${edge.effectName}`, color: {color: '#afafaf', highlight: '#854141'}};
}
private mapDataToNodes(data: ProteinNetwork): { nodes: any[], edges: any[] } {
......@@ -291,4 +314,34 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
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