From 0229a501913ce214224d6ef23b25f9dea786fd44 Mon Sep 17 00:00:00 2001 From: Xiao Gui <xgui3783@gmail.com> Date: Tue, 12 Apr 2022 11:28:40 +0200 Subject: [PATCH] chore: remove unused imports chore: update sapi schema feat: add selected feature url encoding --- src/atlasComponents/sapi/features/index.ts | 3 + .../sapi/features/sapiFeature.ts | 13 +++ src/atlasComponents/sapi/sapi.service.ts | 5 + src/atlasComponents/sapi/schema.ts | 101 ++++++++++++++++-- .../routeStateTransform.service.ts | 26 +++-- src/routerModule/router.service.ts | 4 +- src/routerModule/type.ts | 7 +- src/util/interfaces.ts | 2 - .../viewerCmp/viewerCmp.component.ts | 35 +----- .../viewerCmp/viewerCmp.template.html | 29 +---- 10 files changed, 140 insertions(+), 85 deletions(-) create mode 100644 src/atlasComponents/sapi/features/index.ts create mode 100644 src/atlasComponents/sapi/features/sapiFeature.ts diff --git a/src/atlasComponents/sapi/features/index.ts b/src/atlasComponents/sapi/features/index.ts new file mode 100644 index 000000000..560300f7f --- /dev/null +++ b/src/atlasComponents/sapi/features/index.ts @@ -0,0 +1,3 @@ +export { + SAPIFeature +} from "./sapiFeature" \ No newline at end of file diff --git a/src/atlasComponents/sapi/features/sapiFeature.ts b/src/atlasComponents/sapi/features/sapiFeature.ts new file mode 100644 index 000000000..5abaa0040 --- /dev/null +++ b/src/atlasComponents/sapi/features/sapiFeature.ts @@ -0,0 +1,13 @@ +import { SAPI } from "../sapi.service"; +import { SapiFeatureModel } from "../type"; + +export class SAPIFeature { + constructor(private sapi: SAPI, public id: string, public opts: Record<string, string> = {}){ + + } + + public detail$ = this.sapi.httpGet<SapiFeatureModel>( + `${SAPI.bsEndpoint}/features/${this.id}`, + this.opts + ) +} diff --git a/src/atlasComponents/sapi/sapi.service.ts b/src/atlasComponents/sapi/sapi.service.ts index 5f80668aa..a91867a79 100644 --- a/src/atlasComponents/sapi/sapi.service.ts +++ b/src/atlasComponents/sapi/sapi.service.ts @@ -11,6 +11,7 @@ import { AtlasWorkerService } from "src/atlasViewer/atlasViewer.workerService.se import { EnumColorMapName } from "src/util/colorMaps"; import { PRIORITY_HEADER } from "src/util/priority"; import { Observable } from "rxjs"; +import { SAPIFeature } from "./features"; export const SIIBRA_API_VERSION_HEADER_KEY='x-siibra-api-version' export const SIIBRA_API_VERSION = '0.2.0' @@ -70,6 +71,10 @@ export class SAPI{ return parc.getRegions(spaceId, queryParam) } + getFeature(featureId: string, opts: Record<string, string> = {}) { + return new SAPIFeature(this, featureId, opts) + } + getRegionFeatures(atlasId: string, parcId: string, spaceId: string, regionId: string, priority = 0): Observable<(SapiRegionalFeatureModel | SxplrCleanedFeatureModel)[]>{ const reg = this.getRegion(atlasId, parcId, regionId) diff --git a/src/atlasComponents/sapi/schema.ts b/src/atlasComponents/sapi/schema.ts index 483b4d627..3eb4599d5 100644 --- a/src/atlasComponents/sapi/schema.ts +++ b/src/atlasComponents/sapi/schema.ts @@ -89,9 +89,24 @@ export interface paths { /** Return all genes (name, acronym) in siibra */ get: operations["get_gene_names_genes_get"] } - "/features": { + "/modalities": { /** Return all possible modalities */ - get: operations["get_all_available_modalities_features_get"] + get: operations["get_all_available_modalities_modalities_get"] + } + "/features/{feature_id}": { + /** + * Get all details for one feature by id. + * Since the feature id is unique, no atlas concept is required. + * + * Further optional params can extend the result. + * :param feature_id: + * :param atlas_id: + * :param space_id: + * :param parcellation_id: + * :param region_id: + * :return: UnionRegionalFeatureModels + */ + get: operations["get_feature_details_features__feature_id__get"] } } @@ -156,7 +171,7 @@ export interface components { * @Type * @constant */ - "@type"?: "siibra/core/dataset" + "@type"?: "https://openminds.ebrains.eu/core/DatasetVersion" metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"] /** Urls */ urls: components["schemas"]["Url"][] @@ -190,8 +205,8 @@ export interface components { maxpoint: components["schemas"]["siibra__openminds__SANDS__v3__miscellaneous__coordinatePoint__Model"] /** Shape */ shape: number[] - /** Is Planar */ - is_planar: boolean + /** Isplanar */ + isPlanar: boolean } /** ConnectivityMatrixDataModel */ ConnectivityMatrixDataModel: { @@ -207,6 +222,25 @@ export interface components { /** Columns */ columns?: string[] } + /** CorticalCellDistributionModel */ + CorticalCellDistributionModel: { + /** @Id */ + "@id": string + /** + * @Type + * @constant + */ + "@type"?: "siibra/features/cells" + metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"] + /** Urls */ + urls: components["schemas"]["Url"][] + /** Cells */ + cells: string + /** Section */ + section: string + /** Patch */ + patch: string + } /** DatasetJsonModel */ DatasetJsonModel: { /** @Id */ @@ -215,7 +249,7 @@ export interface components { * @Type * @constant */ - "@type"?: "siibra/core/dataset" + "@type"?: "https://openminds.ebrains.eu/core/DatasetVersion" metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"] /** Urls */ urls: components["schemas"]["Url"][] @@ -792,7 +826,7 @@ export interface components { * @Type * @constant */ - "@type"?: "siibra/core/dataset" + "@type"?: "https://openminds.ebrains.eu/core/DatasetVersion" metadata: components["schemas"]["siibra__openminds__core__v4__products__datasetVersion__Model"] /** Urls */ urls: components["schemas"]["Url"][] @@ -1347,6 +1381,7 @@ export interface operations { } query: { space_id?: string + type?: string } } responses: { @@ -1356,7 +1391,8 @@ export interface operations { "application/json": (Partial< components["schemas"]["ReceptorDatasetModel"] > & - Partial<components["schemas"]["BaseDatasetJsonModel"]>)[] + Partial<components["schemas"]["BaseDatasetJsonModel"]> & + Partial<components["schemas"]["CorticalCellDistributionModel"]>)[] } } /** Validation Error */ @@ -1388,7 +1424,8 @@ export interface operations { "application/json": Partial< components["schemas"]["ReceptorDatasetModel"] > & - Partial<components["schemas"]["BaseDatasetJsonModel"]> + Partial<components["schemas"]["BaseDatasetJsonModel"]> & + Partial<components["schemas"]["CorticalCellDistributionModel"]> } } /** Validation Error */ @@ -1537,6 +1574,7 @@ export interface operations { parcellation_id: string } query: { + type?: string per_page?: number page?: number } @@ -1831,7 +1869,7 @@ export interface operations { } } /** Return all possible modalities */ - get_all_available_modalities_features_get: { + get_all_available_modalities_modalities_get: { responses: { /** Successful Response */ 200: { @@ -1841,6 +1879,49 @@ export interface operations { } } } + /** + * Get all details for one feature by id. + * Since the feature id is unique, no atlas concept is required. + * + * Further optional params can extend the result. + * :param feature_id: + * :param atlas_id: + * :param space_id: + * :param parcellation_id: + * :param region_id: + * :return: UnionRegionalFeatureModels + */ + get_feature_details_features__feature_id__get: { + parameters: { + path: { + feature_id: string + } + query: { + atlas_id?: string + space_id?: string + parcellation_id?: string + region_id?: string + } + } + responses: { + /** Successful Response */ + 200: { + content: { + "application/json": Partial< + components["schemas"]["ReceptorDatasetModel"] + > & + Partial<components["schemas"]["BaseDatasetJsonModel"]> & + Partial<components["schemas"]["CorticalCellDistributionModel"]> + } + } + /** Validation Error */ + 422: { + content: { + "application/json": components["schemas"]["HTTPValidationError"] + } + } + } + } } export interface external {} diff --git a/src/routerModule/routeStateTransform.service.ts b/src/routerModule/routeStateTransform.service.ts index 532d5a0d3..d7deecf51 100644 --- a/src/routerModule/routeStateTransform.service.ts +++ b/src/routerModule/routeStateTransform.service.ts @@ -2,7 +2,7 @@ import { Injectable } from "@angular/core"; import { UrlSegment, UrlTree } from "@angular/router"; import { map } from "rxjs/operators"; import { SAPI } from "src/atlasComponents/sapi"; -import { atlasSelection, defaultState, MainState, plugins } from "src/state"; +import { atlasSelection, defaultState, MainState, plugins, userInteraction } from "src/state"; import { getParcNgId, getRegionLabelIndex } from "src/viewerModule/nehuba/config.service"; import { decodeToNumber, encodeNumber, encodeURIFull, separator } from "./cipher"; import { TUrlAtlas, TUrlPathObj, TUrlStandaloneVolume } from "./type"; @@ -179,6 +179,16 @@ export class RouteStateTransformSvc { // if any error occurs, parse rest per normal } + // try to get feature + try { + if (returnObj.f && returnObj.f.length === 1) { + const decodedFeatId = decodeId(returnObj.f[0]) + const feature = await this.sapi.getFeature(decodedFeatId).detail$.toPromise() + returnState["[state.userInteraction]"].selectedFeature = feature + } + } catch (e) { + console.error(`fetching selected feature error`) + } try { const { selectedAtlas, selectedParcellation, selectedRegions = [], selectedTemplate, allParcellationRegions } = await this.getATPR(returnObj as TUrlPathObj<string[], TUrlAtlas<string[]>>) @@ -210,8 +220,8 @@ export class RouteStateTransformSvc { const selectedRegions = atlasSelection.selectors.selectedRegions(state) const standaloneVolumes = atlasSelection.selectors.standaloneVolumes(state) const navigation = atlasSelection.selectors.navigation(state) + const selectedFeature = userInteraction.selectors.selectedFeature(state) - let dsPrvString: string const searchParam = new URLSearchParams() let cNavString: string @@ -237,9 +247,9 @@ export class RouteStateTransformSvc { const ngId = getParcNgId(selectedAtlas, selectedTemplate, selectedParcellation, region) selectedRegionsString = `${ngId}::${encodeNumber(labelIndex, { float: false })}` } - let routes: any + let routes: TUrlPathObj<string, TUrlAtlas<string>> | TUrlPathObj<string, TUrlStandaloneVolume<string>> - routes= { + routes = { // for atlas a: selectedAtlas && encodeId(selectedAtlas['@id']), // for template @@ -250,9 +260,9 @@ export class RouteStateTransformSvc { r: selectedRegionsString && encodeURIFull(selectedRegionsString), // nav ['@']: cNavString, - // dataset file preview - dsp: dsPrvString && encodeURI(dsPrvString), - } as TUrlPathObj<string, TUrlAtlas<string>> + // showing dataset + f: selectedFeature && encodeId(selectedFeature["@id"]) + } /** * if any params needs to overwrite previosu routes, put them here @@ -262,7 +272,7 @@ export class RouteStateTransformSvc { routes = { // nav ['@']: cNavString, - } as TUrlPathObj<string|string[], TUrlStandaloneVolume<string[]>> + } as TUrlPathObj<string, TUrlStandaloneVolume<string>> } const routesArr: string[] = [] diff --git a/src/routerModule/router.service.ts b/src/routerModule/router.service.ts index 288849e98..3500cc3dc 100644 --- a/src/routerModule/router.service.ts +++ b/src/routerModule/router.service.ts @@ -3,9 +3,9 @@ import { APP_BASE_HREF } from "@angular/common"; import { Inject } from "@angular/core"; import { NavigationEnd, Router } from '@angular/router' import { Store } from "@ngrx/store"; -import { debounceTime, distinctUntilChanged, filter, finalize, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, take, tap, withLatestFrom } from "rxjs/operators"; +import { debounceTime, distinctUntilChanged, filter, map, mapTo, shareReplay, startWith, switchMap, switchMapTo, take, withLatestFrom } from "rxjs/operators"; import { encodeCustomState, decodeCustomState, verifyCustomState } from "./util"; -import { BehaviorSubject, combineLatest, concat, EMPTY, merge, NEVER, Observable, of, timer } from 'rxjs' +import { BehaviorSubject, combineLatest, concat, merge, Observable, timer } from 'rxjs' import { scan } from 'rxjs/operators' import { RouteStateTransformSvc } from "./routeStateTransform.service"; import { SAPI } from "src/atlasComponents/sapi"; diff --git a/src/routerModule/type.ts b/src/routerModule/type.ts index 10f7e0e76..13205ad3e 100644 --- a/src/routerModule/type.ts +++ b/src/routerModule/type.ts @@ -17,9 +17,14 @@ export type TUrlNav<T> = { ['@']: T // navstring } +export type TUrlViewFeat<T> = { + f: T +} + export type TConditional<T> = Partial< TUrlPlugin<T> & - TUrlNav<T> + TUrlNav<T> & + TUrlViewFeat<T> > export type TUrlPathObj<T, V> = diff --git a/src/util/interfaces.ts b/src/util/interfaces.ts index 21de2b34d..73d73fca2 100644 --- a/src/util/interfaces.ts +++ b/src/util/interfaces.ts @@ -16,8 +16,6 @@ export interface IHasFullId{ export type TOverwriteShowDatasetDialog = (arg: any) => void -export const OVERWRITE_SHOW_DATASET_DIALOG_TOKEN = new InjectionToken<TOverwriteShowDatasetDialog>('OVERWRITE_SHOW_DATASET_DIALOG_TOKEN') - export type TRegionOfInterest = { ['fullId']: string } export const CANCELLABLE_DIALOG = new InjectionToken('CANCELLABLE_DIALOG') diff --git a/src/viewerModule/viewerCmp/viewerCmp.component.ts b/src/viewerModule/viewerCmp/viewerCmp.component.ts index b97593f7b..31b28ddfb 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.component.ts +++ b/src/viewerModule/viewerCmp/viewerCmp.component.ts @@ -1,23 +1,17 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, TemplateRef, ViewChild, ViewContainerRef } from "@angular/core"; import { select, Store } from "@ngrx/store"; -import { combineLatest, NEVER, Observable, of, Subscription } from "rxjs"; +import { combineLatest, Observable, of, Subscription } from "rxjs"; import { debounceTime, distinctUntilChanged, map, shareReplay, startWith, switchMap } from "rxjs/operators"; import { CONST, ARIA_LABELS, QUICKTOUR_DESC } from 'common/constants' -import { OVERWRITE_SHOW_DATASET_DIALOG_TOKEN } from "src/util/interfaces"; import { animate, state, style, transition, trigger } from "@angular/animations"; import { IQuickTourData } from "src/ui/quickTour"; import { EnumViewerEvt, TContextArg, TSupportedViewers, TViewerEvent } from "../viewer.interface"; import { ContextMenuService, TContextMenuReg } from "src/contextMenuModule"; -import { ComponentStore } from "../componentStore"; import { DialogService } from "src/services/dialogService.service"; import { SAPI, SapiRegionModel } from "src/atlasComponents/sapi"; import { atlasSelection, userInteraction, } from "src/state"; import { SapiSpatialFeatureModel, SapiFeatureModel, SapiParcellationModel } from "src/atlasComponents/sapi/type"; -type TCStoreViewerCmp = { - overlaySideNav: any -} - @Component({ selector: 'iav-cmp-viewer-container', templateUrl: './viewerCmp.template.html', @@ -58,18 +52,6 @@ type TCStoreViewerCmp = { ]), ], providers: [ - { - provide: OVERWRITE_SHOW_DATASET_DIALOG_TOKEN, - useFactory: (cStore: ComponentStore<TCStoreViewerCmp>) => { - return function overwriteShowDatasetDialog( arg: any ){ - cStore.setState({ - overlaySideNav: arg - }) - } - }, - deps: [ ComponentStore ] - }, - ComponentStore, DialogService ], changeDetection: ChangeDetectionStrategy.OnPush @@ -80,8 +62,6 @@ export class ViewerCmp implements OnDestroy { public CONST = CONST public ARIA_LABELS = ARIA_LABELS - public overlaySidenav$ = NEVER - @ViewChild('genericInfoVCR', { read: ViewContainerRef }) genericInfoVCR: ViewContainerRef @@ -209,16 +189,12 @@ export class ViewerCmp implements OnDestroy { constructor( private store$: Store<any>, private ctxMenuSvc: ContextMenuService<TContextArg<'threeSurfer' | 'nehuba'>>, - private cStore: ComponentStore<TCStoreViewerCmp>, private dialogSvc: DialogService, private cdr: ChangeDetectorRef, private sapi: SAPI, ){ this.subscriptions.push( - this.selectedRegions$.subscribe(() => { - this.clearPreviewingDataset() - }), this.ctxMenuSvc.context$.subscribe( (ctx: any) => this.context = ctx ), @@ -340,15 +316,6 @@ export class ViewerCmp implements OnDestroy { ) } - public clearPreviewingDataset(): void{ - /** - * clear all preview - */ - this.cStore.setState({ - overlaySideNav: null - }) - } - public handleViewerEvent(event: TViewerEvent<'nehuba' | 'threeSurfer'>): void{ switch(event.type) { case EnumViewerEvt.VIEWERLOADED: diff --git a/src/viewerModule/viewerCmp/viewerCmp.template.html b/src/viewerModule/viewerCmp/viewerCmp.template.html index 95c9168e3..05481d0b4 100644 --- a/src/viewerModule/viewerCmp/viewerCmp.template.html +++ b/src/viewerModule/viewerCmp/viewerCmp.template.html @@ -190,32 +190,7 @@ let-drawer="drawer" let-showFullSidenavSwitch="showFullSidenavSwitch"> - <!-- check if preview volume --> - <ng-template [ngIf]="overlaySidenav$ | async" let-overlaySideNav> - - <!-- back btn --> - <button mat-button - (click)="clearPreviewingDataset()" - [attr.aria-label]="ARIA_LABELS.CLOSE" - class="position-absolute z-index-10 m-2"> - <i class="fas fa-chevron-left"></i> - <span class="ml-1"> - Back - </span> - </button> - - <ng-template #genericInfoVCR> - </ng-template> - </ng-template> - - <div [ngClass]="{ - 'invisible overflow-hidden h-0': overlaySidenav$ | async, - 'h-100': !(overlaySidenav$ | async) - }" class="pe-all position-relative d-flex flex-column"> - - - <!-- if pli voi is visible, show pli template - otherwise show region tmpl --> + <!-- selectedFeature || selectedRegion --> <ng-template [ngTemplateOutlet]="(selectedFeature$ | async) ? selectedFeatureTmpl @@ -226,8 +201,6 @@ feature: selectedFeature$ | async }"> </ng-template> - - </div> </ng-template> -- GitLab