import {Wrapper, Task, getWrapperFromProtein, getWrapperFromViralProtein, Protein, ViralProtein, Dataset} from './interfaces'; import {Subject} from 'rxjs'; import {HttpClient} from '@angular/common/http'; import {environment} from '../environments/environment'; import {toast} from 'bulma-toast'; import {Injectable} from '@angular/core'; export type AlgorithmType = 'trustrank' | 'keypathwayminer' | 'multisteiner' | 'closeness' | 'degree'; export type QuickAlgorithmType = 'quick' | 'super'; export const algorithmNames = { trustrank: 'TrustRank', keypathwayminer: 'KeyPathwayMiner', multisteiner: 'Multi-Steiner', closeness: 'Closeness Centrality', degree: 'Degree Centrality', quick: 'Simple', super: 'Quick-Start', }; export interface Algorithm { slug: AlgorithmType | QuickAlgorithmType; name: string; } export const TRUSTRANK: Algorithm = {slug: 'trustrank', name: algorithmNames.trustrank}; export const CLOSENESS_CENTRALITY: Algorithm = {slug: 'closeness', name: algorithmNames.closeness}; export const DEGREE_CENTRALITY: Algorithm = {slug: 'degree', name: algorithmNames.degree}; export const KEYPATHWAYMINER: Algorithm = {slug: 'keypathwayminer', name: algorithmNames.keypathwayminer}; export const MULTISTEINER: Algorithm = {slug: 'multisteiner', name: algorithmNames.multisteiner}; export const MAX_TASKS = 3; @Injectable({ providedIn: 'root' }) export class AnalysisService { private selectedItems = new Map<string, Wrapper>(); private selectListSubject = new Subject<{ items: Wrapper[], selected: boolean | null }>(); public tokens: string[] = []; public finishedTokens: string[] = []; public tasks: Task[] = []; private intervalId: any; private canLaunchNewTask = true; private launchingQuick = false; constructor(private http: HttpClient) { const tokens = localStorage.getItem('tokens'); const finishedTokens = localStorage.getItem('finishedTokens'); if (tokens) { this.tokens = JSON.parse(tokens); } if (finishedTokens) { this.finishedTokens = JSON.parse(finishedTokens); } this.startWatching(); } removeTask(token) { this.tokens = this.tokens.filter((item) => item !== token); this.finishedTokens = this.finishedTokens.filter((item) => item !== token); this.tasks = this.tasks.filter((item) => item.token !== (token)); localStorage.setItem('tokens', JSON.stringify(this.tokens)); } removeAllTasks() { this.tasks = []; this.finishedTokens = []; this.tokens = []; localStorage.removeItem('tokens'); } async getTasks() { return await this.http.get<any>(`${environment.backend}tasks/?tokens=${JSON.stringify(this.tokens)}`).toPromise().catch((e) => { clearInterval(this.intervalId); }); } public addItems(wrappers: Wrapper[]): number { 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}); return addedWrappers.length; } 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 addVisibleHostProteins(nodes, proteins): number { 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)) { items.push(wrapper); this.selectedItems.set(wrapper.nodeId, wrapper); } } this.selectListSubject.next({items, selected: true}); return items.length; } public addVisibleViralProteins(nodes, viralProteins): number { 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)) { items.push(wrapper); this.selectedItems.set(wrapper.nodeId, wrapper); } } this.selectListSubject.next({items, selected: true}); return items.length; } public removeAllHostProteins() { const items: Wrapper[] = Array.from(this.selectedItems.values()).filter(p => p.type === 'host'); for (const wrapper of items) { this.selectedItems.delete(wrapper.nodeId); } this.selectListSubject.next({items, selected: false}); } public removeAllViralProteins() { const items: Wrapper[] = Array.from(this.selectedItems.values()).filter(p => p.type === 'virus'); for (const wrapper of items) { this.selectedItems.delete(wrapper.nodeId); } this.selectListSubject.next({items, selected: false}); } resetSelection() { this.selectListSubject.next({items: [], selected: null}); this.selectedItems.clear(); } idInSelection(nodeId: string): boolean { return this.selectedItems.has(nodeId); } inSelection(wrapper: Wrapper): boolean { return this.selectedItems.has(wrapper.nodeId); } proteinInSelection(protein: Protein): boolean { return this.inSelection(getWrapperFromProtein(protein)); } viralProteinInSelection(viralProtein: ViralProtein): boolean { return this.inSelection(getWrapperFromViralProtein(viralProtein)); } getSelection(): Wrapper[] { return Array.from(this.selectedItems.values()); } getCount(): number { return this.selectedItems.size; } subscribeList(cb: (items: Array<Wrapper>, selected: boolean | null) => void) { this.selectListSubject.subscribe((event) => { cb(event.items, event.selected); }); } async startQuickAnalysis(isSuper: boolean, dataset: Dataset) { if (!this.canLaunchTask()) { toast({ message: `You can only run ${MAX_TASKS} tasks at once. Please wait for one of them to finish or delete it from the task list.`, duration: 5000, dismissible: true, pauseOnHover: true, type: 'is-danger', position: 'top-center', animate: {in: 'fadeIn', out: 'fadeOut'} }); return; } this.launchingQuick = true; const resp = await this.http.post<any>(`${environment.backend}task/`, { algorithm: isSuper ? 'super' : 'quick', target: 'drug', parameters: { strain_or_drugs: dataset.backendId, bait_datasets: dataset.data, seeds: isSuper ? [] : this.getSelection().map((i) => i.backendId), }, }).toPromise(); this.tokens.push(resp.token); localStorage.setItem('tokens', JSON.stringify(this.tokens)); this.startWatching(); toast({ message: 'Quick analysis started. This may take a while.' + ' Once the computation finished you can view the results in the task list to the right.', duration: 10000, dismissible: true, pauseOnHover: true, type: 'is-success', position: 'top-center', animate: {in: 'fadeIn', out: 'fadeOut'} }); } async startAnalysis(algorithm, target: 'drug' | 'drug-target', parameters) { if (!this.canLaunchTask()) { toast({ message: `You can only run ${MAX_TASKS} tasks at once. Please wait for one of them to finish or delete it from the task list.`, duration: 5000, dismissible: true, pauseOnHover: true, type: 'is-danger', position: 'top-center', animate: {in: 'fadeIn', out: 'fadeOut'} }); return; } const resp = await this.http.post<any>(`${environment.backend}task/`, { algorithm, target, parameters, }).toPromise(); this.tokens.push(resp.token); localStorage.setItem('tokens', JSON.stringify(this.tokens)); this.startWatching(); } public isLaunchingQuick(): boolean { return this.launchingQuick; } showToast(task: Task, status: 'DONE' | 'FAILED') { let toastMessage; let toastType; if (status === 'DONE') { toastMessage = 'Computation finished successfully. Click the task in the task list to view the results.'; toastType = 'is-success'; } else if (status === 'FAILED') { toastMessage = 'Computation failed.'; toastType = 'is-danger'; } toast({ message: toastMessage, duration: 5000, dismissible: true, pauseOnHover: true, type: toastType, position: 'top-center', animate: {in: 'fadeIn', out: 'fadeOut'} }); } public canLaunchTask(): boolean { return this.canLaunchNewTask; } startWatching() { const watch = async () => { if (this.tokens.length > 0) { this.tasks = await this.getTasks(); if (!this.tasks) { return; } let queuedOrRunningTasks = 0; this.tasks.forEach((task) => { if (!task.info.done && !task.info.failed) { queuedOrRunningTasks++; } if (!this.finishedTokens.find((finishedToken) => finishedToken === task.token)) { if (task.info.done) { this.finishedTokens.push(task.token); this.showToast(task, 'DONE'); localStorage.setItem('finishedTokens', JSON.stringify(this.finishedTokens)); } else if (task.info.failed) { this.finishedTokens.push(task.token); this.showToast(task, 'FAILED'); localStorage.setItem('finishedTokens', JSON.stringify(this.finishedTokens)); } else { } } }); this.canLaunchNewTask = queuedOrRunningTasks < MAX_TASKS; } else { this.canLaunchNewTask = true; } this.launchingQuick = false; }; watch(); if (this.intervalId) { clearInterval(this.intervalId); } this.intervalId = setInterval(watch, 5000); } }