Skip to content
Snippets Groups Projects
Commit 28252c85 authored by Xiao Gui's avatar Xiao Gui
Browse files

feat: fetch receptor from bs

parent 9a046612
No related branches found
No related tags found
No related merge requests found
Showing
with 524 additions and 0 deletions
import { Input } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { TRegion } from "./constants";
export class BsRegionInputBase{
protected region$ = new BehaviorSubject<TRegion>(null)
private _region: TRegion
@Input()
set region(val: TRegion) {
this._region = val
this.region$.next(val)
}
get region() {
return this._region
}
constructor(){}
}
\ No newline at end of file
import { InjectionToken } from "@angular/core";
import { Observable } from "rxjs";
export const BS_ENDPOINT = new InjectionToken<string>('BS_ENDPOINT')
export type TFeature = 'ReceptorDistribution'
export type TRegion = {
name: string
status?: string
}
export const BS_DARKTHEME = new InjectionToken<Observable<boolean>>('BS_DARKTHEME')
\ No newline at end of file
export { BSFeatureModule } from './module'
export { BS_ENDPOINT, TFeature, TRegion, BS_DARKTHEME } from './constants'
// nb do not export BsRegionInputBase from here
// will result in cyclic imports
\ No newline at end of file
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { BS_ENDPOINT } from "./constants";
import { BSFeatureReceptorModule } from "./receptor";
import { BsFeatureService } from "./service";
@NgModule({
imports: [
CommonModule,
BSFeatureReceptorModule,
],
providers: [
{
provide: BS_ENDPOINT,
useValue: `https://brainscapes.apps-dev.hbp.eu`
},
BsFeatureService
],
exports: [
BSFeatureReceptorModule
]
})
export class BSFeatureModule{}
import { Component, Input, OnChanges } from "@angular/core";
import { BsFeatureReceptorBase } from "../base";
import { CONST } from 'common/constants'
const { RECEPTOR_AR_CAPTION } = CONST
@Component({
selector: 'bs-features-receptor-autoradiograph',
templateUrl: './autoradiograph.template.html',
styleUrls: [
'./autoradiograph.style.css'
]
})
export class BsFeatureReceptorAR extends BsFeatureReceptorBase implements OnChanges {
public RECEPTOR_AR_CAPTION = RECEPTOR_AR_CAPTION
private DS_PREVIEW_URL = DATASET_PREVIEW_URL
@Input()
bsLabel: string
public imgUrl: string
constructor(){
super()
}
ngOnChanges(){
this.error = null
this.urls = []
if (!this.bsFeature) {
this.error = `bsFeature not populated`
return
}
if (!this.bsLabel) {
this.error = `bsLabel not populated`
return
}
const url = this.bsFeature.data._ReceptorDistribution__autoradiographs[this.bsLabel]
if (!url) {
this.error = `ar cannot be found`
return
}
this.urls = [{ url }]
const query = url.replace('https://object.cscs.ch/v1', '')
this.imgUrl = `${this.DS_PREVIEW_URL}/imageProxy/v1?u=${encodeURIComponent(query)}`
}
}
\ No newline at end of file
.ar-container > img
{
width: 100%;
}
\ No newline at end of file
<ng-template [ngIf]="error">
{{ error }}
</ng-template>
<a *ngFor="let url of urls"
[href]="url.url"
class="no-hover"
download>
<i class="fas fa-download"></i>
<span>
{{ url.text || (url.url | getFilenamePipe) }}
</span>
</a>
<figure>
<figcaption class="text-muted">
Autoradiograph: {{ RECEPTOR_AR_CAPTION }}
</figcaption>
<div class="ar-container">
<img [src]="imgUrl">
</div>
</figure>
import { Input } from "@angular/core";
import { TBSResp } from "./type";
export class BsFeatureReceptorBase {
@Input()
bsFeature: TBSResp
public urls: {
url: string
text?: string
}[] = []
public error = null
constructor(){}
}
\ No newline at end of file
import { Component, OnDestroy } from "@angular/core";
import { Subscription } from "rxjs";
import { filter, map, shareReplay, startWith, switchMap, tap } from "rxjs/operators";
import { BsRegionInputBase } from "../../bsRegionInputBase";
import { BsFeatureReceptorService } from '../service'
@Component({
selector: 'bs-features-receptor-entry',
templateUrl: './entry.template.html',
styleUrls: [
'./entry.style.css'
],
})
export class BsFeatureReceptorEntry extends BsRegionInputBase implements OnDestroy{
private sub: Subscription[] = []
ngOnDestroy(){
while (this.sub.length > 0) this.sub.pop().unsubscribe()
}
public receptorResp$ = this.region$.pipe(
filter(v => !!v),
switchMap(val => this.featureReceptorService.getFeatureFromRegion(val)),
shareReplay(1),
)
public onSelectReceptor(receptor: string){
this.selectedReceptor = receptor
}
public selectedReceptor = null
public allReceptors$ = this.receptorResp$.pipe(
map(val => val?.receptor_symbols),
filter(v => !!v),
map(obj => Object.keys(obj)),
startWith<string[]>([])
)
constructor(
private featureReceptorService: BsFeatureReceptorService
){
super()
}
}
:host
{
display: block;
width: 100%;
height: 100%;
}
\ No newline at end of file
<bs-features-receptor-fingerprint
(onSelectReceptor)="onSelectReceptor($event)"
[bsFeature]="receptorResp$ | async">
</bs-features-receptor-fingerprint>
<mat-divider></mat-divider>
<mat-form-field class="mt-2 w-100">
<mat-label>
Select a receptor
</mat-label>
<mat-select [(value)]="selectedReceptor">
<mat-option
*ngFor="let receptorName of (allReceptors$ | async)"
[value]="receptorName">
{{ receptorName }}
</mat-option>
</mat-select>
</mat-form-field>
<bs-features-receptor-profile
*ngIf="selectedReceptor"
[bsFeature]="receptorResp$ | async"
[bsLabel]="selectedReceptor">
</bs-features-receptor-profile>
<bs-features-receptor-autoradiograph
*ngIf="selectedReceptor"
[bsFeature]="receptorResp$ | async"
[bsLabel]="selectedReceptor">
</bs-features-receptor-autoradiograph>
\ No newline at end of file
import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostListener, Inject, OnChanges, OnDestroy, OnInit, Optional, Output } from "@angular/core";
import { fromEvent, Observable, Subscription } from "rxjs";
import { distinctUntilChanged, map } from "rxjs/operators";
import { BS_DARKTHEME } from "../../constants";
import { BsFeatureReceptorBase } from "../base";
import { CONST } from 'common/constants'
const { RECEPTOR_FP_CAPTION } = CONST
@Component({
selector: 'bs-features-receptor-fingerprint',
templateUrl: './fp.template.html',
styleUrls: [
'./fp.style.css'
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BsFeatureReceptorFingerprint extends BsFeatureReceptorBase implements OnChanges, OnInit, OnDestroy{
public RECEPTOR_FP_CAPTION = RECEPTOR_FP_CAPTION
private sub: Subscription[] = []
@HostListener('click')
onClick(){
if (this.selectedReceptor) {
this.onSelectReceptor.emit(this.selectedReceptor)
}
}
@Output()
public onSelectReceptor = new EventEmitter()
private selectedReceptor: any
constructor(
private elRef: ElementRef,
@Optional() @Inject(BS_DARKTHEME) public darktheme$: Observable<boolean>,
){
super()
}
ngOnInit(){
// without, when devtool is out, runs sluggishly
// informing angular that change occurs here will be handled by programmer, and not angular
this.sub.push(
fromEvent<CustomEvent>(this.elRef.nativeElement, 'kg-ds-prv-regional-feature-mouseover').pipe(
map(ev => ev.detail?.data?.receptor?.label),
distinctUntilChanged(),
).subscribe(label => {
this.selectedReceptor = label
})
)
}
ngOnDestroy() {
while (this.sub.length > 0) this.sub.pop().unsubscribe()
}
ngOnChanges(){
this.error = null
this.urls = []
if (!this.bsFeature) {
this.error = `bsFeature is not populated`
return
}
this.urls.push(
...this.bsFeature.data.urls
.filter(u => /_fp_/.test(u))
.map(url => {
return {
url,
}
}),
...this.bsFeature.data.urls
.filter(u => !/_pr_|_ar_/.test(u) && /receptors\.tsv$/.test(u))
.map(url => {
return {
url,
}
})
)
const radarEl = (this.elRef.nativeElement as HTMLElement).querySelector<any>('kg-dataset-dumb-radar')
radarEl.radarBs = this.bsFeature.data._ReceptorDistribution__fingerprint
radarEl.metaBs = this.bsFeature.receptor_symbols
}
}
kg-dataset-dumb-radar
{
display: block;
min-height: 20em;
}
/* figure
{
width: 100%;
height: 100%;
}
kg-dataset-dumb-radar
{
display: block;
width: 100%;
height: 100%;
} */
\ No newline at end of file
<ng-template [ngIf]="error">
{{ error }}
</ng-template>
<a *ngFor="let url of urls"
[href]="url.url"
class="no-hover"
download>
<i class="fas fa-download"></i>
<span>
{{ url.text || (url.url | getFilenamePipe) }}
</span>
</a>
<figure>
<figcaption class="text-muted">
Fingerprint : {{ RECEPTOR_FP_CAPTION }}
</figcaption>
<kg-dataset-dumb-radar
[attr.kg-ds-prv-darkmode]="darktheme$ && (darktheme$ | async)">
</kg-dataset-dumb-radar>
</figure>
import { Directive, OnDestroy } from "@angular/core";
import { merge, of, Subscription } from "rxjs";
import { catchError, map, mapTo, switchMap } from "rxjs/operators";
import { BsRegionInputBase } from "../bsRegionInputBase";
import { BsFeatureReceptorService } from "./service";
@Directive({
selector: '[bs-features-receptor-directive]',
exportAs: 'bsFeatureReceptorDirective'
})
export class BsFeatureReceptorDirective extends BsRegionInputBase implements OnDestroy {
private sub: Subscription[] = []
ngOnDestroy(){
while (this.sub.length > 0) this.sub.pop().unsubscribe()
}
public hasReceptor$ = this.region$.pipe(
switchMap(val => merge(
of(null),
this.featureReceptorService.getFeatureFromRegion(val).pipe(
mapTo(true),
catchError(() => of(false))
)
)),
)
public fetching$ = this.hasReceptor$.pipe(
map(v => v === null),
)
constructor(
private featureReceptorService: BsFeatureReceptorService
){
super()
}
}
\ No newline at end of file
export { BSFeatureReceptorModule } from './module'
\ No newline at end of file
import { CommonModule } from "@angular/common";
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { AngularMaterialModule } from "src/ui/sharedModules/angularMaterial.module";
import { UtilModule } from "src/util";
import { BsFeatureReceptorAR } from "./ar/autoradiograph.component";
import { BsFeatureReceptorEntry } from "./entry/entry.component";
import { BsFeatureReceptorFingerprint } from "./fp/fp.component";
import { BsFeatureReceptorDirective } from "./hasReceptor.directive";
import { BsFeatureReceptorProfile } from "./profile/profile.component";
import { BsFeatureReceptorService } from "./service";
@NgModule({
imports: [
CommonModule,
UtilModule,
AngularMaterialModule,
FormsModule,
],
declarations: [
BsFeatureReceptorProfile,
BsFeatureReceptorAR,
BsFeatureReceptorFingerprint,
BsFeatureReceptorEntry,
BsFeatureReceptorDirective,
],
exports: [
BsFeatureReceptorEntry,
BsFeatureReceptorDirective,
],
providers: [
BsFeatureReceptorService,
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
})
export class BSFeatureReceptorModule{}
import { ChangeDetectionStrategy, Component, ElementRef, Inject, Input, OnChanges, Optional } from "@angular/core";
import { Observable } from "rxjs";
import { BS_DARKTHEME } from "../../constants";
import { BsFeatureReceptorBase } from "../base";
import { CONST } from 'common/constants'
const { RECEPTOR_PR_CAPTION } = CONST
@Component({
selector: 'bs-features-receptor-profile',
templateUrl: './profile.template.html',
styleUrls: [
'./profile.style.css'
],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BsFeatureReceptorProfile extends BsFeatureReceptorBase implements OnChanges{
public RECEPTOR_PR_CAPTION = RECEPTOR_PR_CAPTION
@Input()
bsLabel: string
constructor(
private elRef: ElementRef,
@Optional() @Inject(BS_DARKTHEME) public darktheme$: Observable<boolean>,
){
super()
}
ngOnChanges(){
this.error = null
this.urls = []
if (!this.bsFeature) {
this.error = `bsFeature not populated`
return
}
if (!this.bsLabel) {
this.error = `bsLabel not populated`
return
}
this.urls = this.bsFeature.data.urls
.filter(url => url.indexOf(`_pr_${this.bsLabel}`) >= 0)
.map(url => {
return { url }
})
const profileBs = this.bsFeature.data._ReceptorDistribution__profiles[this.bsLabel]
const lineEl = (this.elRef.nativeElement as HTMLElement).querySelector<any>('kg-dataset-dumb-line')
lineEl.profileBs = profileBs
}
}
<ng-template [ngIf]="error">
{{ error }}
</ng-template>
<a *ngFor="let url of urls"
[href]="url.url"
class="no-hover"
download>
<i class="fas fa-download"></i>
<span>
{{ url.text || (url.url | getFilenamePipe) }}
</span>
</a>
<figure>
<figcaption class="text-muted">
Profile: {{ RECEPTOR_PR_CAPTION }}
</figcaption>
<kg-dataset-dumb-line
[attr.kg-ds-prv-darkmode]="darktheme$ && (darktheme$ | async)">
</kg-dataset-dumb-line>
</figure>
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment