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

feat: added hover compound feat index

maint: rc deploy on rancher
parent 4f218691
No related branches found
No related tags found
No related merge requests found
......@@ -9,6 +9,10 @@ on:
IMAGE_TAG:
required: true
type: string
IMAGE_DIGEST:
required: false
type: string
default: 'unknown-digest'
secrets:
KUBECONFIG:
......@@ -32,12 +36,14 @@ jobs:
helm --kubeconfig=$kubecfg_path \
upgrade \
--set image.tag=${{ inputs.IMAGE_TAG }} \
--set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \
${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/
else
echo "tag ${{ inputs.DEPLOYMENT_NAME }} not found. Install"
helm --kubeconfig=$kubecfg_path \
install \
--set image.tag=${{ inputs.IMAGE_TAG }} \
--set podLabels.image-digest=${{ inputs.IMAGE_DIGEST }} \
${{ inputs.DEPLOYMENT_NAME }} .helm/siibra-explorer/
fi
......
......@@ -25,6 +25,9 @@ jobs:
SIIBRA_API_RC: 'https://siibra-api-rc.apps.hbp.eu/v3_0'
SIIBRA_API_LATEST: 'https://siibra-api-latest.apps-dev.hbp.eu/v3_0'
outputs:
IMAGE_DIGEST: ${{ steps.build-docker-image.outputs.IMAGE_DIGEST }}
steps:
- uses: actions/checkout@v4
with:
......@@ -59,7 +62,8 @@ jobs:
else
echo "dev bulid, enable experimental features"
fi
- name: 'Build docker image'
- id: 'build-docker-image'
name: 'Build docker image'
run: |
DOCKER_BUILT_TAG=${{ env.DOCKER_REGISTRY }}siibra-explorer:$BRANCH_NAME
echo "Building $DOCKER_BUILT_TAG"
......@@ -73,6 +77,10 @@ jobs:
echo "Successfully built $DOCKER_BUILT_TAG"
echo "DOCKER_BUILT_TAG=$DOCKER_BUILT_TAG" >> $GITHUB_ENV
IMAGE_DIGEST=$(docker inspect --format='{{ index .RepoDigests 0 }}' $DOCKER_BUILT_TAG)
echo "Built image digest: $IMAGE_DIGEST"
echo "IMAGE_DIGEST=$IMAGE_DIGEST" >> $GITHUB_OUTPUT
- name: 'Push to docker registry'
run: |
echo "Login to docker registry"
......@@ -138,6 +146,19 @@ jobs:
secrets:
okd_token: ${{ secrets.OKD_PROD_SECRET }}
trigger-deploy-rc-rancher:
if: ${{ needs.setting-vars.outputs.BRANCH_NAME == 'rc' && success() }}
needs:
- build-docker-img
- setting-vars
uses: ./.github/workflows/deploy-helm.yml
with:
DEPLOYMENT_NAME: rc
IMAGE_TAG: ${{ needs.setting-vars.outputs.SXPLR_VERSION }}
IMAGE_DIGEST: ${{ needs.build-docker-img.outputs.IMAGE_DIGEST }}
secrets:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
trigger-deploy-master-rancher:
if: ${{ needs.setting-vars.outputs.BRANCH_NAME == 'master' && success() }}
needs:
......@@ -147,6 +168,7 @@ jobs:
with:
DEPLOYMENT_NAME: master
IMAGE_TAG: ${{ needs.setting-vars.outputs.SXPLR_VERSION }}
IMAGE_DIGEST: ${{ needs.build-docker-img.outputs.IMAGE_DIGEST }}
secrets:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
......
......@@ -15,7 +15,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.1
version: 0.1.2
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
......
import { Pipe, PipeTransform, SecurityContext } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
@Pipe({
name: 'transformOnhoverSegment',
})
export class TransformOnhoverSegmentPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {
}
private sanitizeHtml(inc: string): SafeHtml {
return this.sanitizer.sanitize(SecurityContext.HTML, inc)
}
private getStatus(text: string) {
return ` <span class="text-muted">(${this.sanitizeHtml(text)})</span>`
}
public transform(segment: any | number): SafeHtml {
return this.sanitizer.bypassSecurityTrustHtml((
( this.sanitizeHtml(segment.name) || segment) +
(segment.status
? this.getStatus(segment.status)
: '')
))
}
}
......@@ -23,6 +23,7 @@
</table>
<pointcloud-intents [points]="view.indices | filterForPoints"
(point-clicked)="handleOnClick($event)"
[selected-template]="view.selectedTemplate">
</pointcloud-intents>
</ng-template>
......
......@@ -5,6 +5,8 @@ import { CompoundFeatureIndices } from "./compoundFeatureIndices.component";
import { IndexToStrPipe } from "./idxToText.pipe";
import { IndexToIconPipe } from "./idxToIcon.pipe";
import { PointCloudIntents, FilterPointTransformer } from "src/features/pointcloud-intents";
import { RENDER_CF_POINT, RenderCfPoint } from "../pointcloud-intents/intents.component";
@NgModule({
imports: [
......@@ -20,6 +22,16 @@ import { PointCloudIntents, FilterPointTransformer } from "src/features/pointclo
],
exports: [
CompoundFeatureIndices,
],
providers: [
{
provide: RENDER_CF_POINT,
useFactory: () => {
const pipe = new IndexToStrPipe()
const renderCfPoint: RenderCfPoint = cfIndex => pipe.transform(cfIndex.index)
return renderCfPoint
}
}
]
})
......
import { CommonModule } from "@angular/common";
import { Component, EventEmitter, Input, Output, inject } from "@angular/core";
import { Component, EventEmitter, Inject, InjectionToken, Input, Optional, Output, inject } from "@angular/core";
import { BehaviorSubject, Observable, combineLatest } from "rxjs";
import { Point, SxplrTemplate } from "src/atlasComponents/sapi/sxplrTypes";
import { PathReturn } from "src/atlasComponents/sapi/typeV3";
......@@ -7,7 +7,8 @@ import { AngularMaterialModule } from "src/sharedModules";
import { DestroyDirective } from "src/util/directives/destroy.directive";
import { CFIndex } from "./util";
import { AnnotationLayer } from "src/atlasComponents/annotations";
import { map, takeUntil } from "rxjs/operators";
import { map, takeUntil, withLatestFrom } from "rxjs/operators";
import { CLICK_INTERCEPTOR_INJECTOR, ClickInterceptor, HOVER_INTERCEPTOR_INJECTOR, HoverInterceptor, THoverConfig } from "src/util/injectionTokens";
type Intent = PathReturn<"/feature/{feature_id}/intents">['items'][number]
......@@ -61,33 +62,104 @@ export class PointCloudIntents {
this.#selectedTemplate$.next(tmpl)
}
spaceMatchedPoints$ = combineLatest([
#spaceMatchedCfIndices$ = combineLatest([
this.#points$,
this.#selectedTemplate$
]).pipe(
map(([ points, selectedTemplate ]) => points.filter(p => p.index.spaceId === selectedTemplate?.id).map(v => v.index))
map(([ points, selectedTemplate ]) => points.filter(p => p.index.spaceId === selectedTemplate?.id))
)
#spaceMatchedAnnIdToCfIdx$ = this.#spaceMatchedCfIndices$.pipe(
map(indices => {
const idToIndexMap = new Map<string, CFIndex<Point>>()
for (const idx of indices){
idToIndexMap.set(
serializeToId(idx.index).id,
idx
)
}
return idToIndexMap
})
)
@Output('on-click')
onClick = new EventEmitter<Point>()
@Output('point-clicked')
pointClicked = new EventEmitter<CFIndex<Point>>()
annLayer: AnnotationLayer
constructor(){
constructor(
@Inject(RENDER_CF_POINT) render: RenderCfPoint,
@Optional() @Inject(CLICK_INTERCEPTOR_INJECTOR) clickInterceptor: ClickInterceptor,
@Optional() @Inject(HOVER_INTERCEPTOR_INJECTOR) hoverInterceptor: HoverInterceptor,
){
this.annLayer = new AnnotationLayer("intents", "#ff0000")
this.spaceMatchedPoints$.pipe(
this.#spaceMatchedCfIndices$.pipe(
takeUntil(this.#destroy$)
).subscribe(pts => {
const anns = pts.map(serializeToId)
).subscribe(indices => {
const anns = indices.map(idx => serializeToId(idx.index))
this.annLayer.addAnnotation(anns)
},
e => {
console.error("error", e)
},
() => {
console.log("dismissing!")
this.annLayer.dispose()
})
this.annLayer.onHover.pipe(
takeUntil(this.#destroy$),
withLatestFrom(this.#spaceMatchedAnnIdToCfIdx$),
).subscribe(([hover, map]) => {
if (hoverInterceptor && !!this.#hoveredMessage){
const { remove } = hoverInterceptor
remove(this.#hoveredMessage)
this.#hoveredMessage = null
}
this.#hoveredCfIndex = null
if (!hover) {
return
}
const idx = map.get(hover.id)
if (!idx) {
console.error(`Couldn't find AnnId: ${hover.id}`)
return
}
this.#hoveredCfIndex = idx
if (hoverInterceptor) {
const { append } = hoverInterceptor
const text = render(idx)
this.#hoveredMessage = {
message: `Hovering ${text}`
}
append(this.#hoveredMessage)
}
})
if (clickInterceptor) {
const { register, deregister } = clickInterceptor
const onClickHandler = this.onViewerClick.bind(this)
register(onClickHandler)
this.#destroy$.subscribe(() => deregister(onClickHandler))
}
}
onViewerClick(){
if (this.#hoveredCfIndex) {
this.pointClicked.next(this.#hoveredCfIndex)
return false
}
return true
}
#hoveredCfIndex: CFIndex<Point> = null
#hoveredMessage: THoverConfig = null
}
export const RENDER_CF_POINT = new InjectionToken("RENDER_CF_POINT")
export type RenderCfPoint = (cfIndex: CFIndex<Point>) => string
......@@ -6,6 +6,7 @@ import { TOnHoverObj, temporalPositveScanFn } from "./util"
import { ModularUserAnnotationToolService } from "src/atlasComponents/userAnnotations/tools/service";
import { userInteraction } from "src/state"
import { arrayEqual } from "src/util/array"
import { MouseOverSvc } from "./service"
@Directive({
selector: '[iav-mouse-hover]',
......@@ -14,6 +15,13 @@ import { arrayEqual } from "src/util/array"
export class MouseHoverDirective {
/**
* TODO move
* - mousing over regions
* - hovering annotation
* - hovering voi feature
* to use hover interceptor
*/
public currentOnHoverObs$: Observable<TOnHoverObj> = merge(
this.store$.pipe(
select(userInteraction.selectors.mousingOverRegions),
......@@ -58,6 +66,9 @@ export class MouseHoverDirective {
constructor(
private store$: Store<any>,
private annotSvc: ModularUserAnnotationToolService,
private svc: MouseOverSvc,
) {
}
messages$ = this.svc.messages$
}
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { TransformOnhoverSegmentPipe } from "src/atlasViewer/onhoverSegment.pipe";
import { TransformOnhoverSegmentPipe } from "./transformOnhoverSegment.pipe";
import { MouseHoverDirective } from "./mouseover.directive";
import { MouseOverConvertPipe } from "./mouseOverCvt.pipe";
import { HOVER_INTERCEPTOR_INJECTOR } from "src/util/injectionTokens";
import { MouseOverSvc } from "./service";
@NgModule({
......@@ -18,7 +20,20 @@ import { MouseOverConvertPipe } from "./mouseOverCvt.pipe";
MouseHoverDirective,
TransformOnhoverSegmentPipe,
MouseOverConvertPipe,
],
providers: [
MouseOverSvc,
{
provide: HOVER_INTERCEPTOR_INJECTOR,
useFactory: (svc: MouseOverSvc) => {
return {
append: svc.append.bind(svc),
remove: svc.remove.bind(svc),
}
},
deps: [ MouseOverSvc ]
}
]
})
export class MouseoverModule{}
\ No newline at end of file
export class MouseoverModule{}
import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";
import { THoverConfig } from "src/util/injectionTokens";
@Injectable()
export class MouseOverSvc {
#messages: THoverConfig[] = []
messages$ = new BehaviorSubject(this.#messages)
set messages(messages: THoverConfig[]){
this.#messages = messages
this.messages$.next(this.#messages)
}
get messages(): THoverConfig[]{
return this.#messages
}
append(message: THoverConfig){
this.messages = this.messages.concat(message)
}
remove(message: THoverConfig){
this.messages = this.messages.filter(v => v !== message)
}
}
......@@ -19,6 +19,19 @@ export interface ClickInterceptor{
deregister: (interceptorFunction: (ev: any) => any) => void
}
export const HOVER_INTERCEPTOR_INJECTOR = new InjectionToken<HoverInterceptor>("HOVER_INTERCEPTOR_INJECTOR")
export type THoverConfig = {
fontSet?: string
fontIcon?: string
message: string
}
export interface HoverInterceptor {
append(message: THoverConfig): void
remove(message: THoverConfig): void
}
export const CONTEXT_MENU_ITEM_INJECTOR = new InjectionToken('CONTEXT_MENU_ITEM_INJECTOR')
export type TContextMenu<T> = {
......
......@@ -22,6 +22,17 @@
<span class="centered" matListItemIcon [class]="cvtOutput.icon.cls"></span>
<span matListItemTitle>{{ cvtOutput.text }}</span>
</mat-list-item>
<mat-list-item *ngFor="let message of iavMouseHoverContextualBlock.messages$ | async"
class="h-auto">
<ng-template [ngIf]="message.fontIcon && message.fontSet">
<mat-icon matListItemIcon
[fontSet]="message.fontSet"
[fontIcon]="message.fontIcon">
</mat-icon>
</ng-template>
<span matListItemTitle>{{ message.message }}</span>
</mat-list-item>
</mat-list>
</div>
......
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