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

Use new backend

parent 977c2103
Branches
Tags
No related merge requests found
import {Injectable} from '@angular/core';
import {ProteinGroup} from './pages/protein-network';
import {Protein} from './pages/protein-network';
import {Subject} from 'rxjs';
@Injectable({
......@@ -7,30 +7,30 @@ import {Subject} from 'rxjs';
})
export class AnalysisService {
private selectedProteins = new Map<string, ProteinGroup>();
private selectSubject = new Subject<{protein: ProteinGroup, selected: boolean}>();
private selectedProteins = new Map<string, Protein>();
private selectSubject = new Subject<{protein: Protein, selected: boolean}>();
constructor() {
}
addProtein(protein: ProteinGroup) {
addProtein(protein: Protein) {
if (!this.inSelection(protein)) {
this.selectedProteins.set(`${protein.groupId}`, protein);
this.selectedProteins.set(`${protein.proteinAc}`, protein);
this.selectSubject.next({protein, selected: true});
}
}
inSelection(protein: ProteinGroup): boolean {
return this.selectedProteins.has(`${protein.groupId}`);
inSelection(protein: Protein): boolean {
return this.selectedProteins.has(protein.proteinAc);
}
removeProtein(protein: ProteinGroup) {
if (this.selectedProteins.delete(`${protein.groupId}`)) {
removeProtein(protein: Protein) {
if (this.selectedProteins.delete(`${protein.proteinAc}`)) {
this.selectSubject.next({protein, selected: false});
}
}
getSelection(): ProteinGroup[] {
getSelection(): Protein[] {
return Array.from(this.selectedProteins.values());
}
......@@ -38,7 +38,7 @@ export class AnalysisService {
return this.selectedProteins.size;
}
subscribe(cb: (protein: ProteinGroup, selected: boolean) => void) {
subscribe(cb: (protein: Protein, selected: boolean) => void) {
this.selectSubject.subscribe((event) => {
cb(event.protein, event.selected);
});
......
......@@ -7,7 +7,7 @@ import {HomePageComponent} from './pages/home-page/home-page.component';
export const routes: Routes = [
{ path: '', component: HomePageComponent },
{ path: 'explorer', component: ExplorerPageComponent},
{ path: 'explorer/:proteinGroup', component: ExplorerPageComponent},
{ path: 'explorer/:protein', component: ExplorerPageComponent},
{ path: 'about', component: AboutPageComponent }
];
......
......@@ -16,7 +16,7 @@
</thead>
<tbody>
<tr *ngFor="let p of analysis.getSelection()">
<td>{{p.name}}</td>
<td>{{p.proteinAc}}</td>
<td>
<button (click)="analysis.removeProtein(p)" class="button is-small is-danger">
<i class="fa fa-trash"></i>
......
<div class="content">
<ng-select [items]="queryItems" bindLabel="name" [virtualScroll]="true" class="custom"
<ng-select [items]="queryItems" bindLabel="proteinAc" [virtualScroll]="true" class="custom"
placeholder="Search..." (change)="select($event)">
</ng-select>
</div>
import { Component, Input, Output, EventEmitter } from '@angular/core';
import {Protein} from '../../pages/protein-network';
@Component({
selector: 'app-query-component',
......@@ -8,12 +9,11 @@ import { Component, Input, Output, EventEmitter } from '@angular/core';
export class QueryComponent {
@Output() selectProtein: EventEmitter<string> = new EventEmitter();
@Input() queryItems: any[];
@Output() selectProtein: EventEmitter<Protein> = new EventEmitter();
@Input() queryItems: Protein[];
select(protein) {
console.log(protein);
this.selectProtein.emit('pg_' + protein.groupId);
this.selectProtein.emit(protein);
}
}
......@@ -24,7 +24,7 @@
<div class="level-item has-text-centered">
<div>
<p class="heading">Protein Groups</p>
<p class="title">{{ proteinData.proteinGroups.length }}</p>
<p class="title">{{ proteinData.proteins.length }}</p>
</div>
</div>
<div class="level-item has-text-centered">
......@@ -70,11 +70,11 @@
<div class="content">
<p><b>Baits</b></p>
<div class="bait-frame">
<div *ngFor="let bait of baitProteins">
<div *ngFor="let bait of viralProteinCheckboxes">
<label class="checkbox">
<input type="checkbox" class="checkbox" [ngModel]="bait.checked"
(ngModelChange)="bait.checked = $event; filterNodes()">
Bait {{ bait.data.name }}
Bait {{ bait.data.effectId }}
</label>
</div>
</div>
......@@ -139,13 +139,12 @@
<div class="card-content">
<div *ngIf="showDetails" class="content">
<p><b>Protein Group:</b> {{ proteinGroup }}</p>
<p><b>Protein Group:</b> {{ currentProteinAc }}</p>
<p><b>Gene Name(s):</b> <span *ngFor="let geneName of geneNames"> {{ geneName }}</span></p>
<p><b>Protein Name(s):</b> <span *ngFor="let proteinName of proteinNames"> {{ proteinName }}</span></p>
<p align="row"><b>Protein AC(s):</b>
<a href="https://www.uniprot.org/uniprot/{{proteinAC}}" target="_blank"
*ngFor="let proteinAC of proteinACs">
{{ proteinAC }}
<p><b>Protein AC(s):</b>
<a href="https://www.uniprot.org/uniprot/{{proteinAc}}" target="_blank" *ngFor="let proteinAc of proteinAcs">
{{ proteinAc }}
</a>
</p>
</div>
......@@ -170,8 +169,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>
<button class="button" *ngIf="!inSelection(currentProteinAc)" (click)="addToSelection(currentProteinAc)">Select for analysis</button>
<button class="button" *ngIf="inSelection(currentProteinAc)" (click)="removeFromSelection(currentProteinAc)">Remove from analysis</button>
</div>
</div>
</div>
......
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild, Output, EventEmitter} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Effect, ProteinGroup, ProteinNetwork} from '../protein-network';
import {Effect, Protein, ProteinNetwork} from '../protein-network';
import {HttpClient} from '@angular/common/http';
import {ApiService} from '../../api.service';
import {AnalysisService} from '../../analysis.service';
......@@ -15,36 +15,31 @@ declare var vis: any;
export class ExplorerPageComponent implements OnInit, AfterViewInit {
public showDetails = false;
public groupId = '';
public currentProteinAc = '';
public geneNames: Array<string> = [];
public proteinGroup = '';
public proteinNames: Array<string> = [];
public proteinACs: Array<string> = [];
public proteinAcs: Array<string> = [];
public baitProteins: Array<{ checked: boolean; data: Effect }> = [];
public viralProteinCheckboxes: Array<{ checked: boolean; data: Effect }> = [];
public proteinData: ProteinNetwork;
public filteredProteins = [];
public proteinGroups: any;
public proteins: any;
public effects: any;
public edges: any;
private network: any;
private nodeData: { nodes: any, edges: any } = {nodes: null, edges: null};
private networkData: any = [];
private seed = 1; // TODO: Remove this
private dumpPositions = false;
public physicsEnabled = false;
public queryItems = [];
public showAnalysisDialog = false;
@ViewChild('network', {static: false}) networkEl: ElementRef;
constructor(private http: HttpClient,
......@@ -54,39 +49,39 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
public analysis: AnalysisService) {
this.geneNames.push('IFI16');
this.proteinNames.push('Gamma-interface-inducible protein 16');
this.proteinACs.push('Q16666');
this.proteinAcs.push('Q16666');
this.route.queryParams.subscribe(async (params) => {
this.dumpPositions = params.dumpPositions;
this.physicsEnabled = !!this.dumpPositions;
const proteinGroup = params.proteinGroup;
if (!proteinGroup) {
const protein = params.protein;
if (!protein) {
// In this case, the URL is just `/explorer`
// Therefore, we do not show a modal
this.showDetails = false;
return;
}
// In this case, the URL is `/explorer/<proteinGroup>`
// In this case, the URL is `/explorer/<protein>`
if (this.proteinGroup === proteinGroup) {
if (this.currentProteinAc === protein) {
// The protein group is the same as before, so we do not need to do anything
// TODO Also highlight node when reloading the page/sharing the URL
return;
}
// We have a new proteinGroup id, so we need to load it and show the modal...
// We have a new protein id, so we need to load it and show the modal...
this.proteinGroup = proteinGroup;
this.currentProteinAc = protein;
// TODO: Perform call here for 'proteinGroup'...
// this.zoomToNode(proteinGroup)
// TODO: Perform call here for 'protein'...
// this.zoomToNode(protein)
this.showDetails = true;
});
this.analysis.subscribe((protein, selected) => {
const nodeId = `pg_${protein.groupId}`;
const nodeId = `pg_${protein.proteinAc}`;
if (selected) {
const node = this.nodeData.nodes.get(nodeId);
if (node) {
......@@ -119,14 +114,14 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
private async getNetwork() {
const data: any = await this.api.getNetwork();
this.proteinGroups = data.proteinGroups;
this.proteins = data.proteins;
this.effects = data.effects;
this.edges = data.edges;
}
public reset(event) {
const checked = event.target.checked;
this.baitProteins.forEach(item => item.checked = checked);
this.viralProteinCheckboxes.forEach(item => item.checked = checked);
this.filterNodes();
}
......@@ -139,14 +134,10 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
});
}
public getGroupId() {
return this.groupId;
}
public async openSummary(groupId: string, zoom: boolean) {
await this.router.navigate(['explorer'], {queryParams: {proteinGroup: groupId}});
public async openSummary(protein: Protein, zoom: boolean) {
await this.router.navigate(['explorer'], {queryParams: {protein: protein.proteinAc}});
if (zoom) {
this.zoomToNode(this.proteinGroup);
this.zoomToNode(`pg_${protein.proteinAc}`);
}
}
......@@ -157,7 +148,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
private async createNetwork() {
await this.getNetwork();
this.proteinData = new ProteinNetwork(this.proteinGroups, this.effects, this.edges);
this.proteinData = new ProteinNetwork(this.proteins, this.effects, this.edges);
if (!this.dumpPositions) {
await this.proteinData.loadPositions(this.http);
}
......@@ -165,7 +156,7 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
// Populate baits
this.proteinData.effects.forEach((effect) => {
this.baitProteins.push({
this.viralProteinCheckboxes.push({
checked: false,
data: effect,
});
......@@ -198,7 +189,12 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
console.log(id);
if (id.length > 0) {
console.log('clicked node:', id);
this.openSummary(id[0], false);
if (id[0].startsWith('pg_')) {
const protein = this.proteinData.getProtein(id[0].substr(3));
this.openSummary(protein, false);
} else {
this.closeSummary();
}
}
});
......@@ -209,11 +205,11 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
this.network.stabilize();
}
if (this.proteinGroup) {
this.zoomToNode(this.proteinGroup);
if (this.currentProteinAc) {
this.zoomToNode(`pg_${this.currentProteinAc}`);
}
this.filteredProteins = this.proteinGroups;
this.filteredProteins = this.proteins;
this.fillQueryItems();
}
......@@ -225,13 +221,13 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
const removeIds = new Set<string>();
const addNodes = new Map<string, Node>();
const showAll = !this.baitProteins.find((eff) => eff.checked);
const showAll = !this.viralProteinCheckboxes.find((eff) => eff.checked);
console.log(showAll);
const connectedProteinGroupIds = new Set<number>();
const connectedProteinAcs = new Set<string>();
this.baitProteins.forEach((bait) => {
const nodeId = `eff_${bait.data.name}`;
this.viralProteinCheckboxes.forEach((bait) => {
const nodeId = `eff_${bait.data.effectId}`;
const found = visibleIds.has(nodeId);
if ((bait.checked || showAll) && !found) {
const node = this.mapEffectToNode(bait.data);
......@@ -242,21 +238,21 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
removeIds.add(nodeId);
}
if (bait.checked || showAll) {
bait.data.proteinGroups.forEach((pg) => {
connectedProteinGroupIds.add(pg.id);
bait.data.proteins.forEach((pg) => {
connectedProteinAcs.add(pg.proteinAc);
});
}
});
this.filteredProteins = [];
for (const proteinGroup of this.proteinData.proteinGroups) {
const nodeId = `pg_${proteinGroup.groupId}`;
const contains = connectedProteinGroupIds.has(proteinGroup.id);
for (const protein of this.proteinData.proteins) {
const nodeId = `pg_${protein.proteinAc}`;
const contains = connectedProteinAcs.has(protein.proteinAc);
const found = visibleIds.has(nodeId);
if (contains) {
this.filteredProteins.push(proteinGroup);
this.filteredProteins.push(protein);
}
if (contains && !found) {
const node = this.mapProteinGroupToNode(proteinGroup);
const node = this.mapProteinToNode(protein);
// this.nodeData.nodes.add(node);
addNodes.set(node.id, node);
......@@ -279,24 +275,24 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
});
}
private mapProteinGroupToNode(proteinGroup: ProteinGroup): any {
private mapProteinToNode(protein: Protein): any {
let color = '#e2b600';
if (this.analysis.inSelection(proteinGroup)) {
if (this.analysis.inSelection(protein)) {
color = '#c42eff';
}
return {
id: `pg_${proteinGroup.groupId}`,
label: `${proteinGroup.name}`,
id: `pg_${protein.proteinAc}`,
label: `${protein.proteinAc}`,
size: 10, font: '5px', color, shape: 'ellipse', shadow: false,
x: proteinGroup.x,
y: proteinGroup.y
x: protein.x,
y: protein.y
};
}
private mapEffectToNode(effect: Effect): any {
return {
id: `eff_${effect.name}`,
label: `${effect.name}`,
id: `eff_${effect.effectId}`,
label: `${effect.effectId}`,
size: 10, color: '#118AB2', shape: 'box', shadow: true, font: {color: '#FFFFFF'},
x: effect.x,
y: effect.y
......@@ -304,15 +300,15 @@ 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.proteinAc}`, to: `eff_${edge.effectId}`, color: {color: '#afafaf', highlight: '#854141'}};
}
private mapDataToNodes(data: ProteinNetwork): { nodes: any[], edges: any[] } {
const nodes = [];
const edges = [];
for (const proteinGroup of data.proteinGroups) {
nodes.push(this.mapProteinGroupToNode(proteinGroup));
for (const protein of data.proteins) {
nodes.push(this.mapProteinToNode(protein));
}
for (const effect of data.effects) {
......@@ -338,30 +334,27 @@ export class ExplorerPageComponent implements OnInit, AfterViewInit {
// 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) {
inSelection(proteinAc: string): boolean {
if (!this.proteinData || !proteinAc) {
return false;
}
const groupId = Number(groupIdStr.split('_')[1]);
const protein = this.proteinData.getProteinGroup(groupId);
const protein = this.proteinData.getProtein(proteinAc);
return this.analysis.inSelection(protein);
}
addToSelection(groupIdStr: string) {
if (!groupIdStr) {
return;
addToSelection(proteinAc: string) {
if (!this.proteinData || !proteinAc) {
return false;
}
const groupId = Number(groupIdStr.split('_')[1]);
const protein = this.proteinData.getProteinGroup(groupId);
const protein = this.proteinData.getProtein(proteinAc);
this.analysis.addProtein(protein);
}
removeFromSelection(groupIdStr: string) {
if (!groupIdStr) {
return;
removeFromSelection(proteinAc: string) {
if (!this.proteinData || !proteinAc) {
return false;
}
const groupId = Number(groupIdStr.split('_')[1]);
const protein = this.proteinData.getProteinGroup(groupId);
const protein = this.proteinData.getProtein(proteinAc);
this.analysis.removeProtein(protein);
}
......
import {HttpClient} from '@angular/common/http';
export interface ProteinGroup {
id: number;
export interface Protein {
name: string;
groupId: number;
proteinAc: string;
effects?: Effect[];
x?: number;
y?: number;
}
export interface Effect {
id: number;
name: string;
proteinGroups?: ProteinGroup[];
effectId: string;
effectName: string;
datasetName: string;
virusName: string;
proteins?: Protein[];
x?: number;
y?: number;
}
export interface Edge {
groupId: number;
effectName: string;
proteinAc: string;
effectId: string;
}
export class ProteinNetwork {
constructor(public proteinGroups: ProteinGroup[], public effects: Effect[], public edges: Edge[]) {
constructor(public proteins: Protein[], public effects: Effect[], public edges: Edge[]) {
}
public async loadPositions(http: HttpClient) {
const nodePositions = await http.get(`assets/positions/network.json`).toPromise();
this.proteinGroups.forEach((node) => {
const nodePosition = nodePositions[`pg_${node.groupId}`];
this.proteins.forEach((node) => {
const nodePosition = nodePositions[`pg_${node.proteinAc}`];
if (nodePosition) {
node.x = nodePosition.x;
node.y = nodePosition.y;
}
});
this.effects.forEach((node) => {
const nodePosition = nodePositions[`eff_${node.name}`];
const nodePosition = nodePositions[`eff_${node.effectId}`];
if (nodePosition) {
node.x = nodePosition.x;
node.y = nodePosition.y;
......@@ -45,27 +46,27 @@ export class ProteinNetwork {
});
}
public getProteinGroup(id: number): ProteinGroup {
return this.proteinGroups.find((pg) => pg.groupId === id);
public getProtein(ac: string): Protein {
return this.proteins.find((p) => p.proteinAc === ac);
}
public getEffect(name: string): Effect {
return this.effects.find((eff) => eff.name === name);
return this.effects.find((eff) => eff.effectId === name);
}
public linkNodes() {
this.proteinGroups.forEach((pg) => {
this.proteins.forEach((pg) => {
pg.effects = [];
});
this.effects.forEach((eff) => {
eff.proteinGroups = [];
eff.proteins = [];
});
this.edges.forEach((edge) => {
const proteinGroup = this.getProteinGroup(edge.groupId);
const effect = this.getEffect(edge.effectName);
const proteinGroup = this.getProtein(edge.proteinAc);
const effect = this.getEffect(edge.effectId);
if (proteinGroup && effect) {
proteinGroup.effects.push(effect);
effect.proteinGroups.push(proteinGroup);
effect.proteins.push(proteinGroup);
}
});
}
......
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment