Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 36 additions & 35 deletions apps/kg-explorer/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import {
ChangeDetectionStrategy,
Component,
computed,
effect,
ElementRef,
inject,
Signal,
signal,
} from '@angular/core';
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, Component, computed, effect, ElementRef, inject, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { MatDividerModule } from '@angular/material/divider';
import { MatMenuModule } from '@angular/material/menu';
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
import { DigitalObjectsJsonLd, HraKgService } from '@hra-api/ng-client';
import { BaseApplicationComponent } from '@hra-ui/application';
import { HraCommonModule } from '@hra-ui/common';
import { ButtonsModule } from '@hra-ui/design-system/buttons';
import { NavigationModule } from '@hra-ui/design-system/navigation';
import { MarkdownModule } from 'ngx-markdown';
import { HelpMenuOptions } from './app.routes';
import { setMirrorUrl, setRemoteApiEndpoint } from './utils/endpoints';
import { DigitalObjectsJsonLd } from './digital-objects-metadata.schema';
import { injectMirrorUrl, setMirrorUrl, setRemoteApiEndpoint } from './utils/endpoints';
import { isNavigating } from './utils/navigation';
import { routeData } from './utils/route-data';

Expand Down Expand Up @@ -76,8 +68,9 @@ export class AppComponent extends BaseApplicationComponent {
/** Activated route service */
private readonly route = inject(ActivatedRoute);

/** HRA KG API service */
private readonly kg = inject(HraKgService);
private readonly http = inject(HttpClient);

readonly mirrorUrl = injectMirrorUrl();

/** Page title to display on the breadcrumbs */
private readonly pageTitle = signal<string>('');
Expand Down Expand Up @@ -116,17 +109,32 @@ export class AppComponent extends BaseApplicationComponent {
readonly params = signal<string[]>([]);

/** Id of digital object computed from params */
readonly objectId = computed(() => ['https://lod.humanatlas.io'].concat(this.params()).join('/'));
readonly objectId = computed(() => [this.baseUrl()].concat(this.params()).join('/'));

readonly baseUrl = computed(() => {
const lod = 'https://lod.humanatlas.io';
return this.mirrorUrl() === 'https://cdn.humanatlas.io/digital-objects' ? lod : this.mirrorUrl();
});

/** Digital objects */
private readonly digitalObjects: Signal<DigitalObjectsJsonLd>;
private readonly digitalObjects = signal<DigitalObjectsJsonLd>({ '@context': {}, '@graph': [] });

/**
* Gets the page title for breadcrumbs
*/
constructor() {
super({ screenSizeNotice: { width: 864, height: 486 } });

const el = inject(ElementRef).nativeElement as HTMLElement;
const apiEndpoint = el.getAttribute('remote-api-endpoint');
if (apiEndpoint) {
setRemoteApiEndpoint(apiEndpoint);
}
const customMirrorUrl = el.getAttribute('mirror-url');
if (customMirrorUrl) {
setMirrorUrl(customMirrorUrl);
}

effect(() => {
if (this.typeLabel() && this.documentationUrl()) {
this.extraMenuOption.set({
Expand All @@ -149,13 +157,6 @@ export class AppComponent extends BaseApplicationComponent {
}
});

effect(() => {
const id = this.objectId();
const objects = this.digitalObjects();
const match = objects['@graph']?.find((object) => object['@id'] === id);
this.pageTitle.set(match?.title || '');
});

this.router.events.pipe(takeUntilDestroyed()).subscribe(() => {
const type = this.route.snapshot.root.firstChild?.params['type'];
const name = this.route.snapshot.root.firstChild?.params['name'];
Expand All @@ -164,18 +165,18 @@ export class AppComponent extends BaseApplicationComponent {
} else {
this.params.set([]);
}
});

const el = inject(ElementRef).nativeElement as HTMLElement;
const apiEndpoint = el.getAttribute('remote-api-endpoint');
if (apiEndpoint) {
setRemoteApiEndpoint(apiEndpoint);
}
const mirrorUrl = el.getAttribute('mirror-url');
if (mirrorUrl) {
setMirrorUrl(mirrorUrl);
}
this.setPageTitle();
});
}

this.digitalObjects = toSignal(this.kg.digitalObjects(), { initialValue: {} });
private setPageTitle() {
this.http.get(`${this.mirrorUrl()}/kg/digital-objects.jsonld`).subscribe((data) => {
this.digitalObjects.set(data as DigitalObjectsJsonLd);
const id = this.objectId();
const objects = this.digitalObjects();
const match = objects['@graph']?.find((object) => object['@id'] === id);
this.pageTitle.set(match?.title || '');
});
}
}
18 changes: 7 additions & 11 deletions apps/kg-explorer/src/app/app.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ import { NotFoundPageComponent } from '@hra-ui/design-system/error-pages/not-fou
import { ServerErrorPageComponent } from '@hra-ui/design-system/error-pages/server-error-page';
import { TableColumn } from '@hra-ui/design-system/table';

import { AsctbTermsSchema, DigitalObjectsJsonLdSchema, TermsIndexSchema } from './digital-objects-metadata.schema';
import { MainPageComponent } from './pages/main-page/main-page.component';
import { MetadataPageComponent } from './pages/metadata-page/metadata-page.component';
import {
asctbResolver,
biomarkersResolver,
cellTypeResolver,
documentationUrlResolver,
doMetadataResolver,
kgResolver,
ontologyResolver,
kgJsonResolver,
productLabelResolver,
} from './utils/kg-resolver';

Expand Down Expand Up @@ -111,11 +108,9 @@ export const appRoutes: Route[] = [
columns: DO_COLUMNS,
},
resolve: {
data: kgResolver(),
asctbTermOccurrences: asctbResolver(),
ontologyTree: ontologyResolver(),
cellTypeTree: cellTypeResolver(),
biomarkerTree: biomarkersResolver(),
data: kgJsonResolver('/kg/digital-objects.jsonld', DigitalObjectsJsonLdSchema),
asctbTerms: kgJsonResolver('/kg/asctb-terms.json', AsctbTermsSchema),
termsIndex: kgJsonResolver('/kg/kg-terms-index.json', TermsIndexSchema),
},
},
{
Expand All @@ -125,7 +120,8 @@ export const appRoutes: Route[] = [
columns: METADATA_COLUMNS,
},
resolve: {
doData: kgResolver(),
doData: kgJsonResolver('/kg/digital-objects.jsonld', DigitalObjectsJsonLdSchema),
asctbTerms: kgJsonResolver('/kg/asctb-terms.json', AsctbTermsSchema),
metadata: doMetadataResolver(),
documentationUrl: documentationUrlResolver(),
typeLabel: productLabelResolver(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class FilterMenuOverlayComponent implements OnInit {
readonly filterOptionCategory = input.required<FilterOptionCategory>();

/** Currently selected filter IDs */
readonly currentFilters = input<string[] | undefined>();
readonly currentFilters = input<string[] | null>();

/** Currently selected options */
readonly selectedOptions = signal<FilterOption[]>([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { IconsModule } from '@hra-ui/design-system/icons';
import { ScrollingModule } from '@hra-ui/design-system/scrolling';
import { PlainTooltipDirective } from '@hra-ui/design-system/tooltips/plain-tooltip';

import { CurrentFilters } from '../../pages/main-page/main-page.component';
import { FilterOption, FilterOptionCategory } from '../../utils/utils';
import { FilterOption, FilterOptionCategory, FilterType } from '../../utils/utils';
import { FilterMenuOverlayComponent } from './filter-menu-overlay/filter-menu-overlay.component';

/** Filter form values */
Expand All @@ -28,8 +27,23 @@ export interface FilterFormValues {
biomarkers: FilterOption[] | null;
}

/** Filter types for the filter form */
type FilterType = 'digitalObjects' | 'releaseVersion' | 'organs' | 'anatomicalStructures' | 'cellTypes' | 'biomarkers';
/** Current filter interface (each category contains string of filter option IDs) */
export interface CurrentFilters {
/** Digital object filters */
digitalObjects: string[] | null;
/** Release version filters */
releaseVersion: string[] | null;
/** Organ filters */
organs: string[] | null;
/** Anatomical structures filters */
anatomicalStructures: string[] | null;
/** Cell type filters */
cellTypes: string[] | null;
/** Biomarker filters */
biomarkers: string[] | null;
/** Search term filters */
searchTerm: string | null;
}

/**
* Filter menu for the KG Explorer
Expand Down
57 changes: 57 additions & 0 deletions apps/kg-explorer/src/app/digital-objects-metadata.schema.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DigitalObjectInfoTypeEnum } from '@hra-api/ng-client';
import * as z from 'zod';

/** Person info type */
Expand Down Expand Up @@ -81,3 +82,59 @@ export const DigitalObjectMetadataSchema = z
}),
})
.meta({ id: 'DigitalObjectMetadata' });

/** Digital object info type */
export type DigitalObjectInfo = z.infer<typeof DigitalObjectInfoSchema>;

/** Digital object schema */
export const DigitalObjectInfoSchema = z
.object({
'@id': z.string(),
'@type': z.enum(DigitalObjectInfoTypeEnum),
title: z.string(),
doType: z.string(),
doName: z.string(),
doVersion: z.string(),
lastUpdated: z.string(),
hraVersions: z.union([z.string(), z.string().array()]).optional(),
versions: z.union([z.string(), z.string().array()]),
purl: z.string(),
datasets: z.union([z.string(), z.string().array()]),
lod: z.string(),
cell_count: z.string().optional(),
biomarker_count: z.string().optional(),
organs: z.union([z.string(), z.string().array()]).optional(),
organIds: z.union([z.string(), z.string().array()]).optional(),
})
.meta({ id: 'DigitalObjectInfo' });

export type DigitalObjectsJsonLd = z.infer<typeof DigitalObjectsJsonLdSchema>;

export const DigitalObjectsJsonLdSchema = z
.object({
'@context': z.record(z.string(), z.any()),
'@graph': z.array(DigitalObjectInfoSchema),
})
.meta({ id: 'DigitalObjectsJsonLd' });

export type AsctbTerms = z.infer<typeof AsctbTermsSchema>;

export const AsctbTermsSchema = z
.object({
asctb_type: z.string(),
iri: z.string(),
label: z.string(),
})
.array()
.meta({ id: 'AsctbTerms' });

export type TermsIndex = z.infer<typeof TermsIndexSchema>;

export const TermsIndexSchema = z
.object({
terms: z.string().array(),
purls: z.string().array(),
term_to_purls: z.number().array().array(),
purl_to_terms: z.number().array().array(),
})
.meta({ id: 'TermsIndex' });
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
>
<hra-filter-menu
hraFeature="filter-menu"
[filterCategories]="filterCategoriesArray()"
[filterCategories]="filterCategories()"
[formClosed]="!sidenav.opened"
[currentFilters]="filters()"
[currentFilters]="store.currentFilters()"
(toggleForm)="sidenav.toggle()"
(formChanges)="handleFilterSelectionChanges($event); table.scrollToTop()"
/>
Expand All @@ -37,7 +37,11 @@
<mat-label>Search</mat-label>
<input matInput [formControl]="searchControl" />
</mat-form-field>
<hra-results-indicator description="Viewing" [value]="filteredRows().length" [total]="allRows().length" />
<hra-results-indicator
description="Viewing"
[value]="filteredRows().length"
[total]="store.allRows().length"
/>
</div>
<hra-table
variant="divider"
Expand Down
Loading
Loading