diff --git a/deploy/datasets/index.js b/deploy/datasets/index.js index cb69686463aa6cd2240a183f3c09340a5d0e5b02..c5bb0a0d1e9a485f54960ee3e3ad4bf95191fff3 100644 --- a/deploy/datasets/index.js +++ b/deploy/datasets/index.js @@ -2,11 +2,12 @@ const express = require('express') const path = require('path') const fs = require('fs') const datasetsRouter = express.Router() -const { init, getDatasets, getPreview, getDatasetFromId, getDatasetFileAsZip } = require('./query') +const { init, getDatasets, getPreview, getDatasetFromId, getDatasetFileAsZip, getTos } = require('./query') const url = require('url') const qs = require('querystring') const bodyParser = require('body-parser') + datasetsRouter.use(bodyParser.urlencoded({ extended: false })) datasetsRouter.use(bodyParser.json()) @@ -34,6 +35,17 @@ const getVary = (headers) => (_req, res, next) => { next() } + +datasetsRouter.get('/tos', cacheMaxAge24Hr, async (req, res) => { + const tos = getTos() + if (tos) { + res.setHeader('Content-Type', 'text/markdown; charset=utf-8') + res.status(200).send(tos) + } else { + res.status(404).end() + } +}) + datasetsRouter.use('/spatialSearch', noCacheMiddleWare, require('./spatialRouter')) datasetsRouter.get('/templateName/:templateName', noCacheMiddleWare, (req, res, next) => { @@ -145,48 +157,8 @@ datasetsRouter.get('/downloadKgFiles', checkKgQuery, async (req, res) => { stream.pipe(res) } catch (e) { console.warn('datasets/index#downloadKgFiles', e) - res.status(400).send(e) + res.status(400).send(e.toString()) } }) -/** - * TODO - * deprecate jszip in favour of archiver - */ - -var JSZip = require("jszip"); - -datasetsRouter.post("/downloadParcellationThemself", (req,res, next) => { - - - //ToDo We can add termsOfUse Text file somewhere - will be better - const termsOfUse = 'Access to the data and metadata provided through HBP Knowledge Graph Data Platform ' + - '("KG") requires that you cite and acknowledge said data and metadata according to the Terms and' + - ' Conditions of the Platform.\r\n## Citation requirements are outlined - https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations' + - '\r\n## Acknowledgement requirements are outlined - https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements' + - '\r\n## These outlines are based on the authoritative Terms and Conditions are found - https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use' + - '\r\n## If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in.' - - - var zip = new JSZip(); - - zip.file("credits.txt", req.body['publicationsText']) - zip.file("Terms of use.txt", termsOfUse) - - - - - //ToDo: Need to download files dynamically. Nii folder should be removed - if (req.body['niiFiles']) { - var nii = zip.folder("nifti") - const filepath = process.env.STORAGE_PATH || path.join(__dirname, 'nii') - req.body['niiFiles'].forEach(file => { - nii.file(file['file'], fs.readFileSync(path.join(filepath, file['file']))) - }) - } - - res.setHeader('Content-Type', 'application/zip') - zip.generateNodeStream().pipe(res) -}); - module.exports = datasetsRouter \ No newline at end of file diff --git a/deploy/datasets/kgtos.md b/deploy/datasets/kgtos.md new file mode 100644 index 0000000000000000000000000000000000000000..44bc5966c7674b3bfbd5d40ff535d9464bd4ba4a --- /dev/null +++ b/deploy/datasets/kgtos.md @@ -0,0 +1,16 @@ +The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets. + + +Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform. + + +Citation requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations> . + + +Acknowledgement requirements are outlined <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements> + + +These outlines are based on the authoritative Terms and Conditions are found <https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use> + + +If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in. diff --git a/deploy/datasets/query.js b/deploy/datasets/query.js index 1516c3e4559d579e2360e9c9b7374af30c8eb093..d243a34c8794a207aeba72e334234120fd81c27a 100644 --- a/deploy/datasets/query.js +++ b/deploy/datasets/query.js @@ -5,8 +5,7 @@ const path = require('path') const archiver = require('archiver') const { commonSenseDsFilter } = require('./supplements/commonSense') const { getPreviewFile, hasPreview } = require('./supplements/previewFile') - -const kgQueryUtil = require('./../auth/util') +const { init: kgQueryUtilInit, getUserKGRequestParam } = require('./util') let cachedData = null let otherQueryResult = null @@ -29,11 +28,6 @@ const getKgQuerySingleDatasetUrl = ({ kgId }) => { return _newUrl } -const timeout = process.env.TIMEOUT || 5000 -const STORAGE_PATH = process.env.STORAGE_PATH || path.join(__dirname, 'data') - -let getPublicAccessToken - const fetchDatasetFromKg = async ({ user } = {}) => { const { releasedOnly, option } = await getUserKGRequestParam({ user }) @@ -220,197 +214,87 @@ const filter = (datasets = [], { templateName, parcellationName }) => datasets /** * on init, populate the cached data */ -exports.init = async () => { - const { getPublicAccessToken: getPublic } = await kgQueryUtil() - getPublicAccessToken = getPublic +const init = async () => { + await kgQueryUtilInit() return await getPublicDs() } -exports.getDatasets = ({ templateName, parcellationName, user }) => getDs({ user }) +const getDatasets = ({ templateName, parcellationName, user }) => getDs({ user }) .then(json => filter(json, { templateName, parcellationName })) -exports.getPreview = ({ datasetName, templateSelected }) => getPreviewFile({ datasetName, templateSelected }) - -/** - * TODO - * change to real spatial query - */ -const cachedMap = new Map() +const getPreview = ({ datasetName, templateSelected }) => getPreviewFile({ datasetName, templateSelected }) -/** - * TODO change to URL constructor to improve readability - */ -const spatialQuery = 'https://kg.humanbrainproject.eu/query/neuroglancer/seeg/coordinate/v1.0.0/spatialWithCoordinatesNG/instances?vocab=https%3A%2F%2Fschema.hbp.eu%2FmyQuery%2F' - -const getXformFn = (templateSpace) => { - const _ = {} - switch(templateSpace){ - case 'Waxholm Space rat brain atlas v.2.0': - _['nmToVoxel'] = transformWaxholmV2NmToVoxel - _['voxelToNm'] = transformWaxholmV2VoxelToNm - break; - default: { - _['nmToVoxel'] = defaultXform - _['voxelToNm'] = defaultXform - } - } - return _ -} - -const getSpatialSearcParam = ({ templateName, queryArg }) => { - let kgSpaceName - const { nmToVoxel } = getXformFn(templateName) - - const coordsString = queryArg.split('__'); - const boundingBoxCorners = coordsString.map(coordString => coordString.split('_')) - const bbInVoxelSpace = boundingBoxCorners.map(nmToVoxel) - - switch (templateName){ - case 'Waxholm Space rat brain atlas v.2.0': - kgSpaceName = 'waxholmV2' - break; - default: - kgSpaceName = templateName - } - return { - boundingBox: `${kgSpaceName}:${bbInVoxelSpace.map(v => v.join(',')).join(',')}` - } +const getDatasetFromId = async ({ user, kgId, returnAsStream = false }) => { + const { option, releasedOnly } = await getUserKGRequestParam({ user }) + const _url = getKgQuerySingleDatasetUrl({ kgId }) + if (releasedOnly) _url.searchParams.set('databaseScope', 'RELEASED') + if (returnAsStream) return request(_url, option) + else return new Promise((resolve, reject) => { + request(_url, option, (err, resp, body) => { + if (err) return reject(err) + if (resp.statusCode >= 400) return reject(resp.statusCode) + return resolve(JSON.parse(body)) + }) + }) } -const fetchSpatialDataFromKg = async ({ templateName, queryGeometry, queryArg, user }) => { +const renderPublication = ({ name, cite, doi }) => `${name} + ${cite} + https://doi.org/${doi} +` - const { releasedOnly, option } = await getUserKGRequestParam({ user }) +const prepareDatasetMetadata = ({ name, kgReference, contributors, publications }) => { - const _ = getSpatialSearcParam({ templateName, queryGeometry, queryArg }) - const url = require('url') - const search = new url.URLSearchParams() - - for (let key in _) { - search.append(key, _[key]) - } - if (releasedOnly) search.append('databaseScope', 'RELEASED') - - const _url = `${spatialQuery}&${search.toString()}` - return await new Promise((resolve, reject) => { - request(_url, option, (err, resp, body) => { - if (err) - return reject(err) - if (resp.statusCode >= 400) { - return reject(resp.statusCode) - } - const json = JSON.parse(body) + return `${name} - const { voxelToNm } = getXformFn(templateName) - - const _ = json.results.map(({ name, coordinates, dataset}) => { - return { - name, - templateSpace: templateName, - dataset: dataset.map(ds => ds = {name: ds.name, externalLink: 'https://kg.humanbrainproject.eu/instances/Dataset/' + ds.identifier}), - geometry: { - type: 'point', - space: 'real', - position: voxelToNm([ - coordinates[0].x, - coordinates[0].y, - coordinates[0].z - ]) - } - } - }) +Contributors: ${contributors.join(', ')} - return resolve(_) - }) - }) +Publications: +${publications.map(renderPublication).join('\n')} +` } -exports.getSpatialDatasets = async ({ templateName, queryGeometry, queryArg, user }) => { - /** - * Local data can be injected here - */ - return await fetchSpatialDataFromKg({ templateName, queryGeometry, queryArg, user }) -} +let kgtos -let publicAccessToken +fs.readFile(path.join(__dirname, 'kgtos.md') , 'utf-8', (err, data) => { + if (err) console.warn(`reading kgtos Error`, err) + kgtos = data +}) -async function getUserKGRequestParam({ user }) { - /** - * n.b. ACCESS_TOKEN env var is usually only set during dev - */ - const accessToken = (user && user.tokenset && user.tokenset.access_token) || process.env.ACCESS_TOKEN - const releasedOnly = !accessToken - if (!accessToken && !publicAccessToken && getPublicAccessToken) { - publicAccessToken = await getPublicAccessToken() - } - const option = accessToken || publicAccessToken - ? { - auth: { - 'bearer': accessToken || publicAccessToken || process.env.ACCESS_TOKEN - } - } - : {} +const getTos = () => kgtos - return { - option, - releasedOnly, - token: accessToken || publicAccessToken - } -} +const getDatasetFileAsZip = async ({ user, kgId } = {}) => { + if (!kgId) { + throw new Error('kgId must be defined') + } -/** - * perhaps export the xform fns into a different module - * ideally, in the future, KG can handle xform of voxel to nm - */ -const transformWaxholmV2NmToVoxel = (coord) => { - /** - * as waxholm is already in RAS, does not need to swap axis - */ + const dataset = await getDatasetFromId({ user, kgId }) + const { files, name: datasetName } = dataset + const zip = archiver('zip') /** - * atlas viewer applies translation (below in nm) in order to center the brain - * query already translates nm to mm, so the unit of transl should be [mm, mm, mm] + * append citation information */ - const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6) + zip.append(prepareDatasetMetadata(dataset), { + name: `${datasetName}.txt` + }) /** - * mm/voxel + * append kg citation policy */ - const voxelDim = [0.0390625, 0.0390625, 0.0390625] - return coord.map((v, idx) => (v - transl[idx]) / voxelDim[idx]) -} - -const transformWaxholmV2VoxelToNm = (coord) => { - const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6) - const voxelDim = [0.0390625, 0.0390625, 0.0390625] - return coord.map((v, idx) => (v * voxelDim[idx]) + transl[idx]) -} -const defaultXform = (coord) => coord -const getDatasetFromId = async ({ user, kgId, returnAsStream = false }) => { - const { option, releasedOnly } = await getUserKGRequestParam({ user }) - const _url = getKgQuerySingleDatasetUrl({ kgId }) - if (releasedOnly) _url.searchParams.set('databaseScope', 'RELEASED') - if (returnAsStream) return request(_url, option) - else return new Promise((resolve, reject) => { - request(_url, option, (err, resp, body) => { - if (err) return reject(err) - if (resp.statusCode >= 400) return reject(resp.statusCode) - return resolve(JSON.parse(body)) - }) + zip.append(getTos(), { + name: `Terms Of Use.md` }) -} -const getDatasetFileAsZip = async ({ user, kgId } = {}) => { - if (!kgId) { - throw new Error('kgId must be defined') - } - - const result = await getDatasetFromId({ user, kgId }) - const { files } = result - const zip = archiver('zip') + /** + * append all of the files + */ for (let file of files) { const { name, absolutePath } = file - zip.append(request(absolutePath), { name }) + zip.append(request(absolutePath), { + name: path.join(datasetName, name) + }) } zip.finalize() @@ -418,5 +302,12 @@ const getDatasetFileAsZip = async ({ user, kgId } = {}) => { return zip } -exports.getDatasetFromId = getDatasetFromId -exports.getDatasetFileAsZip = getDatasetFileAsZip +module.exports = { + getDatasetFromId, + getDatasetFileAsZip, + init, + getDatasets, + getPreview, + getTos +} + diff --git a/deploy/datasets/spatialQuery.js b/deploy/datasets/spatialQuery.js new file mode 100644 index 0000000000000000000000000000000000000000..51409c90f272c0e0f7ee8677a531f2af4995448f --- /dev/null +++ b/deploy/datasets/spatialQuery.js @@ -0,0 +1,105 @@ +const url = require('url') +const request = require('request') +const { getUserKGRequestParam } = require('./util') +const { transformWaxholmV2NmToVoxel, transformWaxholmV2VoxelToNm } = require('./spatialXform/waxholmRat') + +/** + * TODO change to URL constructor to improve readability + */ +const spatialQuery = 'https://kg.humanbrainproject.eu/query/neuroglancer/seeg/coordinate/v1.0.0/spatialWithCoordinatesNG/instances?vocab=https%3A%2F%2Fschema.hbp.eu%2FmyQuery%2F' + +const defaultXform = (coord) => coord + +const getXformFn = (templateSpace) => { + const _ = {} + switch(templateSpace){ + case 'Waxholm Space rat brain atlas v.2.0': + _['nmToVoxel'] = transformWaxholmV2NmToVoxel + _['voxelToNm'] = transformWaxholmV2VoxelToNm + break; + default: { + _['nmToVoxel'] = defaultXform + _['voxelToNm'] = defaultXform + } + } + return _ +} + +const getSpatialSearcParam = ({ templateName, queryArg }) => { + let kgSpaceName + const { nmToVoxel } = getXformFn(templateName) + + const coordsString = queryArg.split('__'); + const boundingBoxCorners = coordsString.map(coordString => coordString.split('_')) + const bbInVoxelSpace = boundingBoxCorners.map(nmToVoxel) + + switch (templateName){ + case 'Waxholm Space rat brain atlas v.2.0': + kgSpaceName = 'waxholmV2' + break; + default: + kgSpaceName = templateName + } + return { + boundingBox: `${kgSpaceName}:${bbInVoxelSpace.map(v => v.join(',')).join(',')}` + } +} + + +const fetchSpatialDataFromKg = async ({ templateName, queryGeometry, queryArg, user }) => { + + const { releasedOnly, option } = await getUserKGRequestParam({ user }) + + const _ = getSpatialSearcParam({ templateName, queryGeometry, queryArg }) + const search = new url.URLSearchParams() + + for (let key in _) { + search.append(key, _[key]) + } + if (releasedOnly) search.append('databaseScope', 'RELEASED') + + const _url = `${spatialQuery}&${search.toString()}` + return await new Promise((resolve, reject) => { + request(_url, option, (err, resp, body) => { + if (err) + return reject(err) + if (resp.statusCode >= 400) { + return reject(resp.statusCode) + } + const json = JSON.parse(body) + + const { voxelToNm } = getXformFn(templateName) + + const _ = json.results.map(({ name, coordinates, dataset}) => { + return { + name, + templateSpace: templateName, + dataset: dataset.map(ds => ds = {name: ds.name, externalLink: 'https://kg.humanbrainproject.eu/instances/Dataset/' + ds.identifier}), + geometry: { + type: 'point', + space: 'real', + position: voxelToNm([ + coordinates[0].x, + coordinates[0].y, + coordinates[0].z + ]) + } + } + }) + + return resolve(_) + }) + }) +} + + +const getSpatialDatasets = async ({ templateName, queryGeometry, queryArg, user }) => { + /** + * Local data can be injected here + */ + return await fetchSpatialDataFromKg({ templateName, queryGeometry, queryArg, user }) +} + +module.exports = { + getSpatialDatasets +} \ No newline at end of file diff --git a/deploy/datasets/spatialRouter.js b/deploy/datasets/spatialRouter.js index ef6574fc6ce86a78ddcc541eb475a8391032198b..5bfb78a2c241c4d134628a11dce2914842d9b430 100644 --- a/deploy/datasets/spatialRouter.js +++ b/deploy/datasets/spatialRouter.js @@ -1,5 +1,5 @@ const router = require('express').Router() -const { getSpatialDatasets } = require('./query') +const { getSpatialDatasets } = require('./spatialQuery') const badRequestString = `spatialSearch endpoint uses param as follows: diff --git a/deploy/datasets/spatialXform/waxholmRat.js b/deploy/datasets/spatialXform/waxholmRat.js new file mode 100644 index 0000000000000000000000000000000000000000..480013b5b98310c8d77c6f117d550d4126737db0 --- /dev/null +++ b/deploy/datasets/spatialXform/waxholmRat.js @@ -0,0 +1,33 @@ + +/** + * perhaps export the xform fns into a different module + * ideally, in the future, KG can handle xform of voxel to nm + */ +const transformWaxholmV2NmToVoxel = (coord) => { + /** + * as waxholm is already in RAS, does not need to swap axis + */ + + /** + * atlas viewer applies translation (below in nm) in order to center the brain + * query already translates nm to mm, so the unit of transl should be [mm, mm, mm] + */ + const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6) + + /** + * mm/voxel + */ + const voxelDim = [0.0390625, 0.0390625, 0.0390625] + return coord.map((v, idx) => (v - transl[idx]) / voxelDim[idx]) +} + +const transformWaxholmV2VoxelToNm = (coord) => { + const transl = [-9550781, -24355468, -9707031].map(v => v / 1e6) + const voxelDim = [0.0390625, 0.0390625, 0.0390625] + return coord.map((v, idx) => (v * voxelDim[idx]) + transl[idx]) +} + +module.exports = { + transformWaxholmV2NmToVoxel, + transformWaxholmV2VoxelToNm +} \ No newline at end of file diff --git a/deploy/datasets/util.js b/deploy/datasets/util.js new file mode 100644 index 0000000000000000000000000000000000000000..93b8c8a4cee91e1eb960aecccd71723d749798c4 --- /dev/null +++ b/deploy/datasets/util.js @@ -0,0 +1,36 @@ +const kgQueryUtil = require('./../auth/util') + +let getPublicAccessToken, publicAccessToken + +const getUserKGRequestParam = async ({ user }) => { + /** + * n.b. ACCESS_TOKEN env var is usually only set during dev + */ + const accessToken = (user && user.tokenset && user.tokenset.access_token) || process.env.ACCESS_TOKEN + const releasedOnly = !accessToken + if (!accessToken && !publicAccessToken && getPublicAccessToken) { + publicAccessToken = await getPublicAccessToken() + } + const option = accessToken || publicAccessToken + ? { + auth: { bearer: accessToken || publicAccessToken } + } + : {} + + return { + option, + releasedOnly, + token: accessToken || publicAccessToken + } +} + +const init = async () => { + if (getPublicAccessToken) return + const { getPublicAccessToken: getPublic } = await kgQueryUtil() + getPublicAccessToken = getPublic +} + +module.exports = { + init, + getUserKGRequestParam +} \ No newline at end of file diff --git a/deploy/package.json b/deploy/package.json index 2d33823ef6a544b335a6a6edfc2096456b09419c..f65ac9c5183114edabe9ca040ab05f087de9dcfc 100644 --- a/deploy/package.json +++ b/deploy/package.json @@ -18,7 +18,6 @@ "express": "^4.16.4", "express-session": "^1.15.6", "helmet-csp": "^2.8.0", - "jszip": "^3.2.1", "jwt-decode": "^2.2.0", "memorystore": "^1.6.1", "openid-client": "^2.4.5", diff --git a/src/atlasViewer/atlasViewer.constantService.service.ts b/src/atlasViewer/atlasViewer.constantService.service.ts index bb1d4183e203ac97f3c9d54c52a49d18e5e660e1..7b9026aec1fed0984ebc59361fdc0c8be26423d1 100644 --- a/src/atlasViewer/atlasViewer.constantService.service.ts +++ b/src/atlasViewer/atlasViewer.constantService.service.ts @@ -4,6 +4,7 @@ import { ViewerStateInterface } from "../services/stateStore.service"; import { Subject, Observable } from "rxjs"; import { ACTION_TYPES, ViewerConfiguration } from 'src/services/state/viewerConfig.store' import { map, shareReplay, filter } from "rxjs/operators"; +import { MatSnackBar } from "@angular/material"; export const CM_THRESHOLD = `0.05` export const CM_MATLAB_JET = `float r;if( x < 0.7 ){r = 4.0 * x - 1.5;} else {r = -4.0 * x + 4.5;}float g;if (x < 0.5) {g = 4.0 * x - 0.5;} else {g = -4.0 * x + 3.5;}float b;if (x < 0.3) {b = 4.0 * x + 0.5;} else {b = -4.0 * x + 2.5;}float a = 1.0;` @@ -239,7 +240,10 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float private supportEmailAddress = `x.gui@fz-juelich.de` private repoUrl = `https://github.com/HumanBrainProject/interactive-viewer` - constructor(private store : Store<ViewerStateInterface>){ + constructor( + private store : Store<ViewerStateInterface>, + private snackbar: MatSnackBar + ){ const ua = window && window.navigator && window.navigator.userAgent ? window.navigator.userAgent @@ -270,11 +274,9 @@ Interactive atlas viewer requires **webgl2.0**, and the \`EXT_color_buffer_float } catchError(e: Error | string){ - /** - * DO NOT REMOVE - * general catch all & reflect in UI - */ - console.warn(e) + this.snackbar.open(e.toString(), 'Dismiss', { + duration: 3000 + }) } } diff --git a/src/atlasViewer/atlasViewer.template.html b/src/atlasViewer/atlasViewer.template.html index d22eae5adfd3f8b045326761fc9d7bc2033e233b..412c75ef2043698334961d251208789f4e2f2277 100644 --- a/src/atlasViewer/atlasViewer.template.html +++ b/src/atlasViewer/atlasViewer.template.html @@ -169,6 +169,7 @@ </div> + <!-- TODO deprecate --> <!-- TODO move to nehuba overlay container --> <panel-component class="shadow" fixedMouseContextualContainerDirective #rClContextMenu> <div heading> diff --git a/src/ui/databrowserModule/databrowser.service.ts b/src/ui/databrowserModule/databrowser.service.ts index 65f9a241b4e1640b4e6cdffc8938a83e84bf38c8..2d6a0edf442ada66de691a7ba8061067f426a04d 100644 --- a/src/ui/databrowserModule/databrowser.service.ts +++ b/src/ui/databrowserModule/databrowser.service.ts @@ -14,6 +14,7 @@ import { WidgetUnit } from "src/atlasViewer/widgetUnit/widgetUnit.component"; import { SHOW_KG_TOS } from "src/services/state/uiState.store"; import { regionFlattener } from "src/util/regionFlattener"; import { DATASETS_ACTIONS_TYPES } from "src/services/state/dataStore.store"; +import { HttpClient } from "@angular/common/http"; const noMethodDisplayName = 'No methods described' @@ -46,8 +47,8 @@ function generateToken() { }) export class DatabrowserService implements OnDestroy{ + public kgTos$: Observable<any> public favedDataentries$: Observable<DataEntry[]> - public darktheme: boolean = false public instantiatedWidgetUnits: WidgetUnit[] = [] @@ -80,8 +81,18 @@ export class DatabrowserService implements OnDestroy{ constructor( private workerService: AtlasWorkerService, private constantService: AtlasViewerConstantsServices, - private store: Store<ViewerConfiguration> + private store: Store<ViewerConfiguration>, + private http: HttpClient ){ + this.kgTos$ = this.http.get(`${this.constantService.backendUrl}datasets/tos`, { + responseType: 'text' + }).pipe( + catchError((err,obs) => { + console.warn(`fetching kgTos error`, err) + return of(null) + }), + shareReplay(1) + ) this.favedDataentries$ = this.store.pipe( select('dataStore'), diff --git a/src/ui/databrowserModule/databrowser/databrowser.template.html b/src/ui/databrowserModule/databrowser/databrowser.template.html index 060a49ad118bbaea29a100e86db11bc0b128d144..9df68602e046068f4758d8445076d6b10ac55040 100644 --- a/src/ui/databrowserModule/databrowser/databrowser.template.html +++ b/src/ui/databrowserModule/databrowser/databrowser.template.html @@ -27,6 +27,7 @@ </readmore-component> <!-- modality picker --> + <!-- TODO use material for popover, then remove popover module and ngx/bootstrap --> <div> <span placement="right" diff --git a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html index 3517468edf64042507f0768dead716797805adfe..2f05732a5ca04d28adda8776a13abafcd009aa89 100644 --- a/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html +++ b/src/ui/databrowserModule/fileviewer/dedicated/dedicated.template.html @@ -4,7 +4,7 @@ <a href="#" - [tooltip]="tooltipText" + [matTooltip]="tooltipText" container="body" class="btn btn-sm rounded-circle" [ngClass]="isShowing ? 'btn-primary' : 'btn-outline-secondary'" diff --git a/src/ui/databrowserModule/singleDataset/singleDataset.component.ts b/src/ui/databrowserModule/singleDataset/singleDataset.component.ts index df2e0e71bf37141fa63e464f9b71b012c58486f2..a62636ede17fed686626debeea225bfa54790bdd 100644 --- a/src/ui/databrowserModule/singleDataset/singleDataset.component.ts +++ b/src/ui/databrowserModule/singleDataset/singleDataset.component.ts @@ -1,6 +1,7 @@ import { Component, Input, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from "@angular/core"; import { KgSingleDatasetService } from "../kgSingleDatasetService.service"; import { Publication, File } from 'src/services/state/dataStore.store' +import { AtlasViewerConstantsServices } from "src/atlasViewer/atlasViewer.constantService.service"; @Component({ selector: 'single-dataset-view', @@ -41,7 +42,8 @@ export class SingleDatasetView implements OnInit { constructor( private singleDatasetService: KgSingleDatasetService, - private cdr: ChangeDetectorRef + private cdr: ChangeDetectorRef, + private constantService: AtlasViewerConstantsServices ){} ngOnInit() { @@ -105,8 +107,8 @@ export class SingleDatasetView implements OnInit { this.singleDatasetService.downloadZipFromKg({ kgId, kgSchema - }) - .catch(console.error) + }, this.name) + .catch(err => this.constantService.catchError(err)) .finally(() => this.downloadInProgress = false) } } diff --git a/src/ui/kgtos/kgtos.component.ts b/src/ui/kgtos/kgtos.component.ts index d191860a02a4e316abc662944cc02be068c948ff..53edea2a8ec08f93674f856bebe07b4c23c48c0c 100644 --- a/src/ui/kgtos/kgtos.component.ts +++ b/src/ui/kgtos/kgtos.component.ts @@ -1,4 +1,6 @@ import { Component } from "@angular/core"; +import { DatabrowserService } from "../databrowserModule/databrowser.service"; +import { Observable } from "rxjs"; @Component({ selector: 'kgtos-component', @@ -10,4 +12,11 @@ import { Component } from "@angular/core"; export class KGToS{ + public kgTos$: Observable<string> + + constructor( + private dbService: DatabrowserService + ){ + this.kgTos$ = this.dbService.kgTos$ + } } \ No newline at end of file diff --git a/src/ui/kgtos/kgtos.template.html b/src/ui/kgtos/kgtos.template.html index 5f8fe4eb6240d7227cec618be5efa43e478cceaf..f0eee2bb6cba0905078ea3331eeb3f3faac9ff83 100644 --- a/src/ui/kgtos/kgtos.template.html +++ b/src/ui/kgtos/kgtos.template.html @@ -1,20 +1,27 @@ -<div> - <p> - The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets. - </p> - <p> - Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform. - </p> - <p> - Citation requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations">here</a>. - </p> - <p> - Acknowledgement requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements">here</a>. - </p> - <p> - These outlines are based on the authoritative Terms and Conditions are found <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use">here</a>. - </p> - <p> - If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in. - </p> -</div> \ No newline at end of file +<ng-container *ngIf="kgTos$ | async as kgTos; else backup"> + <markdown-dom [markdown]="kgTos"> + </markdown-dom> +</ng-container> + +<ng-template #backup> + <div> + <p> + The interactive viewer queries HBP Knowledge Graph Data Platform ("KG") for published datasets. + </p> + <p> + Access to the data and metadata provided through KG requires that you cite and acknowledge said data and metadata according to the Terms and Conditions of the Platform. + </p> + <p> + Citation requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#citations">here</a>. + </p> + <p> + Acknowledgement requirements are outlined <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use#acknowledgements">here</a>. + </p> + <p> + These outlines are based on the authoritative Terms and Conditions are found <a target="_blank" href="https://www.humanbrainproject.eu/en/explore-the-brain/search-terms-of-use">here</a>. + </p> + <p> + If you do not accept the Terms & Conditions you are not permitted to access or use the KG to search for, to submit, to post, or to download any materials found there-in. + </p> + </div> +</ng-template> \ No newline at end of file diff --git a/src/ui/nehubaContainer/nehubaContainer.component.ts b/src/ui/nehubaContainer/nehubaContainer.component.ts index 3112fa91a944a0766c871af90034af33c989a715..049cf8b121318723b2718d938ab3899280e292a7 100644 --- a/src/ui/nehubaContainer/nehubaContainer.component.ts +++ b/src/ui/nehubaContainer/nehubaContainer.component.ts @@ -1253,7 +1253,9 @@ export class NehubaContainer implements OnInit, OnDestroy{ downloadDs(event: MouseEvent, ds: DataEntry, downloadBtn: MatButton){ downloadBtn.disabled = true const id = getIdFromDataEntry(ds) - this.kgSingleDataset.downloadZipFromKg({kgId: id}) + const { name } = ds + this.kgSingleDataset.downloadZipFromKg({kgId: id}, name) + .catch(err => this.constantService.catchError(err)) .finally(() => downloadBtn.disabled = false) } } diff --git a/src/ui/pluginBanner/pluginBanner.template.html b/src/ui/pluginBanner/pluginBanner.template.html index d88e991af9c9dfbe02335b789c60c398ba98d392..b79ca60cccb56c912f2fb0bb00ea5bbbd52f0c02 100644 --- a/src/ui/pluginBanner/pluginBanner.template.html +++ b/src/ui/pluginBanner/pluginBanner.template.html @@ -1,6 +1,6 @@ <div placement = "bottom" - [tooltip] = "pluginEnabledFlag ? null : 'coming soon'" + [matTooltip] = "pluginEnabledFlag ? null : 'coming soon'" *ngFor = "let plugin of pluginServices.fetchedPluginManifests" (click) = "clickPlugin(plugin)" [ngClass] = "{'btn-disabled' : !pluginEnabledFlag}" diff --git a/src/ui/ui.module.ts b/src/ui/ui.module.ts index ae129d6220403450c0ebc00cff530c85e1cf842c..78463d9efc5eac6af2a03001284b9f455397409a 100644 --- a/src/ui/ui.module.ts +++ b/src/ui/ui.module.ts @@ -18,7 +18,6 @@ import { SafeStylePipe } from "../util/pipes/safeStyle.pipe"; import { PluginBannerUI } from "./pluginBanner/pluginBanner.component"; import { CitationsContainer } from "./citation/citations.component"; import { LayerBrowser, LockedLayerBtnClsPipe } from "./layerbrowser/layerbrowser.component"; -import { TooltipModule } from "ngx-bootstrap/tooltip"; import { KgEntryViewer } from "./kgEntryViewer/kgentry.component"; import { SubjectViewer } from "./kgEntryViewer/subjectViewer/subjectViewer.component"; import { GetLayerNameFromDatasets } from "../util/pipes/getLayerNamePipe.pipe"; @@ -34,7 +33,6 @@ import { ShowToastDirective } from "../util/directives/showToast.directive"; import { HelpComponent } from "./help/help.component"; import { ConfigComponent } from './config/config.component' import { FlatmapArrayPipe } from "src/util/pipes/flatMapArray.pipe"; -import { PopoverModule } from 'ngx-bootstrap/popover' import { DatabrowserModule } from "./databrowserModule/databrowser.module"; import { SigninBanner } from "./signinBanner/signinBanner.components"; import { SigninModal } from "./signinModal/signinModal.component"; @@ -75,9 +73,6 @@ import { KgSearchBtnColorPipe } from "src/util/pipes/kgSearchBtnColor.pipe"; UtilModule, ScrollingModule, AngularMaterialModule, - - PopoverModule.forRoot(), - TooltipModule.forRoot() ], declarations : [ NehubaContainer,