From bddfc9f63d1dfadd5cda3026494c9c53ed77535f Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 19 Jan 2024 10:14:44 +0100 Subject: [PATCH 1/5] fix: Most recent date filter needs to take other filters into account --- app/rdf/queries.ts | 43 +++----- app/rdf/query-dimension-values.ts | 166 +++++++++++++++++++----------- 2 files changed, 121 insertions(+), 88 deletions(-) diff --git a/app/rdf/queries.ts b/app/rdf/queries.ts index 14f8926af..4b132c2f4 100644 --- a/app/rdf/queries.ts +++ b/app/rdf/queries.ts @@ -1,4 +1,3 @@ -import { SELECT } from "@tpluscode/sparql-builder"; import { descending, group, index } from "d3"; import { Maybe } from "graphql-tools"; import keyBy from "lodash/keyBy"; @@ -39,6 +38,7 @@ import { } from "./parse"; import { loadDimensionValues, + loadMaxDimensionValue, loadMinMaxDimensionValues, } from "./query-dimension-values"; import { loadResourceLabels } from "./query-labels"; @@ -343,10 +343,13 @@ export const getCubeDimensionValuesWithMetadata = async ({ const loaders = [ filters ? undefined : () => dimension.in ?? [], () => - loadDimensionValues( - { datasetIri: cube.term, dimension, cube, sparqlClient }, - filters - ), + loadDimensionValues({ + datasetIri: cube.term, + dimension, + cube, + sparqlClient, + filters, + }), ].filter(truthy); for (const loader of loaders) { @@ -864,27 +867,15 @@ const buildFilters = async ({ switch (filter.type) { case "single": { if (isDynamicMaxValue(filter.value)) { - if (cubeDimension.maxInclusive) { - return [ - filterDimension.filter.eq( - toRDFValue(cubeDimension.maxInclusive.value) - ), - ]; - } - - // Ideally we would query the max value directly in the observations - // query, but it doesn't seem to be supported by the cube-view-query - const maxValueQuery = SELECT`(MAX(?value) as ?value)` - .WHERE` <${cube.term?.value}> ?observationSet . - ?observationSet ?source . - ?source <${cubeDimension.path?.value}> ?value .`; - const maxValueRaw = await maxValueQuery.execute( - sparqlClient.query, - { operation: "postUrlencoded" } - ); - const maxValue = maxValueRaw?.[0]?.value?.value as string; - - return [filterDimension.filter.eq(toRDFValue(maxValue))]; + const maxValue = await loadMaxDimensionValue({ + datasetIri: cube.term, + dimension: cubeDimension, + cube, + sparqlClient, + filters, + }); + + return [filterDimension.filter.eq(toRDFValue(maxValue[0].value))]; } return [filterDimension.filter.eq(toRDFValue(`${filter.value}`))]; diff --git a/app/rdf/query-dimension-values.ts b/app/rdf/query-dimension-values.ts index 60a6aa159..c829edc73 100644 --- a/app/rdf/query-dimension-values.ts +++ b/app/rdf/query-dimension-values.ts @@ -8,6 +8,7 @@ import { Literal, NamedNode, Term } from "rdf-js"; import { ParsingClient } from "sparql-http-client/ParsingClient"; import { LRUCache } from "typescript-lru-cache"; +import { isDynamicMaxValue } from "@/configurator/components/field"; import { FIELD_VALUE_NONE } from "@/configurator/constants"; import { parseObservationValue } from "@/domain/data"; import { pragmas } from "@/rdf/create-source"; @@ -124,6 +125,14 @@ const getFilterOrder = (filter: Filters[number]) => { } }; +type LoadDimensionValuesProps = { + datasetIri: Term | undefined; + dimension: CubeDimension; + cube: ExtendedCube; + sparqlClient: ParsingClient; + filters?: Filters; +}; + /** * Load dimension values. * @@ -131,74 +140,19 @@ const getFilterOrder = (filter: Filters[number]) => { * */ export async function loadDimensionValues( - { - datasetIri, - dimension, - cube, - sparqlClient, - }: { - datasetIri: Term | undefined; - dimension: CubeDimension; - cube: ExtendedCube; - sparqlClient: ParsingClient; - }, - filters?: Filters + props: LoadDimensionValuesProps ): Promise> { + const { datasetIri, dimension, cube, sparqlClient, filters } = props; const dimensionIri = dimension.path; - const allFiltersList = filters ? Object.entries(filters) : []; - const filterList = - // Consider filters before the current filter to fetch the values for - // the current filter - sortBy( - allFiltersList.slice( - 0, - allFiltersList.findIndex(([iri]) => iri == dimensionIri?.value) - ), - ([, filterValue]) => getFilterOrder(filterValue) - ); + const filterList = filters + ? getFiltersList(filters, dimensionIri?.value) + : []; const query = SELECT.DISTINCT`?value`.WHERE` ${datasetIri} ${cubeNs.observationSet} ?observationSet . ?observationSet ${cubeNs.observation} ?observation . ?observation ${dimensionIri} ?value . - ${ - filters - ? filterList.map(([iri, value], idx) => { - const filterDimension = cube.dimensions.find( - (d) => d.path?.value === iri - ); - - if (!filterDimension || dimensionIri?.value === iri) { - return ""; - } - - if (value.type === "single" && value.value === FIELD_VALUE_NONE) { - return ""; - } - - // Ignore range filters for now. - if (value.type === "range") { - return ""; - } - const versioned = filterDimension - ? dimensionIsVersioned(filterDimension) - : false; - - return sparql`${ - versioned - ? sparql`?dimension${idx} ${ns.schema.sameAs} ?dimension_unversioned${idx}.` - : "" - } - ?observation <${iri}> ?dimension${idx}. - ${formatFilterIntoSparqlFilter( - value, - filterDimension, - versioned, - idx - )}`; - }) - : "" - } + ${getQueryFilters(filterList, cube, dimensionIri?.value)} `.prologue`${pragmas}`; let result: Array = []; @@ -208,12 +162,100 @@ export async function loadDimensionValues( operation: "postUrlencoded", })) as unknown as Array; } catch { - console.warn(`Failed to fetch dimension values for ${datasetIri}.`); + console.warn(`Failed to fetch dimension values for ${datasetIri}!`); } finally { return result.map((d) => d.value); } } +/** + * Load max dimension value. + * + * Filters on other dimensions can be passed. + * + */ +export async function loadMaxDimensionValue( + props: LoadDimensionValuesProps +): Promise> { + const { datasetIri, dimension, cube, sparqlClient, filters } = props; + const dimensionIri = dimension.path; + const filterList = filters + ? getFiltersList(filters, dimensionIri?.value) + : []; + + const query = SELECT`(MAX(?value) as ?value)`.WHERE` + ${datasetIri} ${cubeNs.observationSet} ?observationSet . + ?observationSet ${cubeNs.observation} ?observation . + ?observation ${dimensionIri} ?value . + ${getQueryFilters(filterList, cube, dimensionIri?.value)} + `.prologue`${pragmas}`; + + let result: Array = []; + + try { + result = (await query.execute(sparqlClient.query, { + operation: "postUrlencoded", + })) as unknown as Array; + } catch { + console.warn(`Failed to fetch max dimension value for ${datasetIri}!`); + } finally { + return result.map((d) => d.value); + } +} + +const getFiltersList = (filters: Filters, dimensionIri: string | undefined) => { + const entries = Object.entries(filters); + // Consider filters before the current filter to fetch the values for + // the current filter + return sortBy( + entries.slice( + 0, + entries.findIndex(([iri]) => iri == dimensionIri) + ), + ([, v]) => getFilterOrder(v) + ); +}; + +const getQueryFilters = ( + filtersList: ReturnType, + cube: ExtendedCube, + dimensionIri: string | undefined +) => { + return filtersList.length > 0 + ? filtersList.map(([iri, value], i) => { + const dimension = cube.dimensions.find((d) => d.path?.value === iri); + + // Ignore the current dimension + if (!dimension || dimensionIri === iri) { + return ""; + } + + // Ignore filters with no value or with the special value + if ( + value.type === "single" && + (value.value === FIELD_VALUE_NONE || isDynamicMaxValue(value.value)) + ) { + return ""; + } + + // Ignore range filters for now + if (value.type === "range") { + return ""; + } + + const versioned = dimension ? dimensionIsVersioned(dimension) : false; + + return sparql`${ + versioned + ? sparql`?dimension${i} ${ns.schema.sameAs} ?dimension_unversioned${i}.` + : "" + } + ?observation <${iri}> ?dimension${i}. + ${formatFilterIntoSparqlFilter(value, dimension, versioned, i)}`; + }) + : ""; +}; + type MinMaxResult = [{ minValue: LiteralExt; maxValue: LiteralExt }]; const parseMinMax = (result: MinMaxResult) => { From 4ef3414d57475bd0c213be17daf724f628ac81a9 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 19 Jan 2024 10:44:49 +0100 Subject: [PATCH 2/5] style: Use small Switch for most recent date toggle --- app/configurator/components/field.tsx | 1 + app/themes/federal.tsx | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/app/configurator/components/field.tsx b/app/configurator/components/field.tsx index bf5c41e99..49a3de095 100644 --- a/app/configurator/components/field.tsx +++ b/app/configurator/components/field.tsx @@ -354,6 +354,7 @@ export const DataFilterTemporal = (props: DataFilterTemporalProps) => { fieldProps.onChange({ diff --git a/app/themes/federal.tsx b/app/themes/federal.tsx index 5c39537af..c89c79d4f 100644 --- a/app/themes/federal.tsx +++ b/app/themes/federal.tsx @@ -637,12 +637,17 @@ theme.components = { }, MuiSwitch: { styleOverrides: { - root: { - width: 28, - height: 16, - padding: 0, - + root: ({ ownerState }) => ({ display: "flex", + ...(ownerState.size === "small" && { + width: 24, + height: 12, + }), + ...(ownerState.size === "medium" && { + width: 28, + height: 16, + }), + padding: 0, "& .MuiSwitch-switchBase": { padding: 2, @@ -661,8 +666,14 @@ theme.components = { }, "& .MuiSwitch-thumb": { backgroundColor: theme.palette.background.paper, - width: 12, - height: 12, + ...(ownerState.size === "small" && { + width: 8, + height: 8, + }), + ...(ownerState.size === "medium" && { + width: 12, + height: 12, + }), borderRadius: 6, transition: theme.transitions.create(["width"], { duration: 200, @@ -683,7 +694,7 @@ theme.components = { transform: "translateX(9px)", }, }, - }, + }), }, }, MuiTableCell: { From 14080293c7b2dc4f4184489060c9f3c60e733c84 Mon Sep 17 00:00:00 2001 From: Bartosz Prusinowski Date: Fri, 19 Jan 2024 10:57:25 +0100 Subject: [PATCH 3/5] style: Update filter margins --- app/components/form.tsx | 2 +- app/components/select-tree.tsx | 2 +- app/configurator/components/field.tsx | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/components/form.tsx b/app/components/form.tsx index c47d400bf..496a53245 100644 --- a/app/components/form.tsx +++ b/app/components/form.tsx @@ -353,7 +353,7 @@ export const Select = ({ {label && ( -