Skip to content

Commit

Permalink
feat(web): adjust web to new pathogen.json format
Browse files Browse the repository at this point in the history
  • Loading branch information
ivan-aksamentov committed Oct 24, 2023
1 parent b2b5e70 commit a529628
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 44 deletions.
40 changes: 20 additions & 20 deletions packages_rs/nextclade-web/src/components/Main/DatasetInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNil } from 'lodash'
import { isNil, last } from 'lodash'
import { darken } from 'polished'
import React, { useMemo } from 'react'
import { Badge } from 'reactstrap'
Expand All @@ -12,6 +12,7 @@ import {
DATASET_ID_UNDETECTED,
numberAutodetectResultsAtom,
} from 'src/state/autodetect.state'
import { AnyType, attrBoolMaybe, attrStrMaybe } from 'src/types'
import type { Dataset } from 'src/types'
import styled from 'styled-components'

Expand Down Expand Up @@ -77,8 +78,7 @@ export interface DatasetInfoProps {

export function DatasetInfo({ dataset }: DatasetInfoProps) {
const { t } = useTranslationSafe()
const { attributes, official, deprecated, enabled, experimental, path, version } = dataset
const { name, reference } = attributes
const { attributes, path, version } = dataset

const updatedAt = useMemo(() => {
let updatedAt = version?.updatedAt ? formatDateIsoUtcSimple(version?.updatedAt) : 'unknown'
Expand All @@ -88,10 +88,6 @@ export function DatasetInfo({ dataset }: DatasetInfoProps) {
return updatedAt
}, [version?.tag, version?.updatedAt])

if (!enabled) {
return null
}

if (path === DATASET_ID_UNDETECTED) {
return <DatasetUndetectedInfo />
}
Expand All @@ -104,10 +100,10 @@ export function DatasetInfo({ dataset }: DatasetInfoProps) {

<FlexRight>
<DatasetName>
<span>{name.valueFriendly ?? name.value ?? path}</span>
<span>{attrStrMaybe(attributes, 'name') ?? path}</span>

<span className="d-flex ml-auto">
{official ? (
{path.startsWith('nextstrain') ? (
<DatasetInfoBadge
className="ml-2 my-auto"
color="success"
Expand All @@ -125,7 +121,7 @@ export function DatasetInfo({ dataset }: DatasetInfoProps) {
</DatasetInfoBadge>
)}

{experimental && (
{attrBoolMaybe(attributes, 'experimental') && (
<DatasetInfoBadge
className="ml-2 my-auto"
color="warning"
Expand All @@ -135,7 +131,7 @@ export function DatasetInfo({ dataset }: DatasetInfoProps) {
</DatasetInfoBadge>
)}

{deprecated && (
{attrBoolMaybe(attributes, 'deprecated') && (
<DatasetInfoBadge
className="ml-2 my-auto"
color="secondary"
Expand All @@ -147,19 +143,23 @@ export function DatasetInfo({ dataset }: DatasetInfoProps) {
</span>
</DatasetName>

<DatasetInfoLine>
{t('Reference: {{ name }} ({{ accession }})', {
name: reference.valueFriendly ?? 'Untitled',
accession: reference.value,
})}
</DatasetInfoLine>
<DatasetInfoLine>{t('Reference: {{ ref }})', { ref: formatReference(attributes) })}</DatasetInfoLine>
<DatasetInfoLine>{t('Updated at: {{updated}}', { updated: updatedAt })}</DatasetInfoLine>
<DatasetInfoLine>{t('Dataset name: {{name}}', { name: path })}</DatasetInfoLine>
</FlexRight>
</Container>
)
}

function formatReference(attributes: Record<string, AnyType> | undefined) {
const name = attrStrMaybe(attributes, 'reference name') ?? 'unknown'
const accession = attrStrMaybe(attributes, 'reference accession')
if (accession) {
return `${name} (${accession})`
}
return name
}

export function DatasetUndetectedInfo() {
const { t } = useTranslationSafe()

Expand All @@ -181,7 +181,7 @@ export interface DatasetInfoCircleProps {

function DatasetInfoAutodetectProgressCircle({ dataset }: DatasetInfoCircleProps) {
const { attributes, path } = dataset
const { name } = attributes
const name = attrStrMaybe(attributes, 'name') ?? last(path.split('/')) ?? '?'

const circleBg = useMemo(() => darken(0.1)(colorHash(path, { saturation: 0.5, reverse: true })), [path])
const records = useRecoilValue(autodetectResultsByDatasetAtom(path))
Expand All @@ -190,7 +190,7 @@ function DatasetInfoAutodetectProgressCircle({ dataset }: DatasetInfoCircleProps
const { circleText, countText, percentage } = useMemo(() => {
if (isNil(records)) {
return {
circleText: (firstLetter(name.valueFriendly ?? name.value) ?? ' ').toUpperCase(),
circleText: (firstLetter(name) ?? ' ').toUpperCase(),
percentage: 0,
countText: '\u00A0',
}
Expand All @@ -203,7 +203,7 @@ function DatasetInfoAutodetectProgressCircle({ dataset }: DatasetInfoCircleProps
return { circleText, percentage, countText }
}
return { circleText: `0%`, percentage: 0, countText: `0 / ${numberAutodetectResults}` }
}, [records, name.value, name.valueFriendly, numberAutodetectResults])
}, [records, numberAutodetectResults, name])

return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { get, isNil, sortBy } from 'lodash'
import { lighten } from 'polished'
import React, { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import styled from 'styled-components'
import { areDatasetsEqual, attrStrMaybe, Dataset } from 'src/types'
import { ListGenericCss } from 'src/components/Common/List'
import { DatasetInfo } from 'src/components/Main/DatasetInfo'
import { search } from 'src/helpers/search'
Expand All @@ -11,9 +13,6 @@ import {
autodetectRunStateAtom,
groupByDatasets,
} from 'src/state/autodetect.state'
import type { Dataset } from 'src/types'
import { areDatasetsEqual } from 'src/types'
import styled from 'styled-components'

export interface DatasetSelectorListProps {
datasets: Dataset[]
Expand Down Expand Up @@ -61,10 +60,10 @@ export function DatasetSelectorList({
[...autodetectResult.itemsStartWith, ...autodetectResult.itemsInclude, ...autodetectResult.itemsNotInclude],
searchTerm,
(dataset) => [
dataset.attributes.name.value,
dataset.attributes.name.valueFriendly ?? '',
dataset.attributes.reference.value,
dataset.attributes.reference.valueFriendly ?? '',
dataset.path,
attrStrMaybe(dataset.attributes, 'name') ?? '',
attrStrMaybe(dataset.attributes, 'reference name') ?? '',
attrStrMaybe(dataset.attributes, 'reference accession') ?? '',
],
)
}, [autodetectResult, searchTerm])
Expand Down
6 changes: 1 addition & 5 deletions packages_rs/nextclade-web/src/io/fetchDatasetsIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import { axiosFetch } from 'src/io/axiosFetch'
const MINIMIZER_INDEX_ALGO_VERSION = 'v1'
const PACKAGE_VERSION = process.env.PACKAGE_VERSION ?? ''

export function isEnabled(dataset: Dataset) {
return dataset.enabled
}

export function isCompatible(dataset: Dataset): boolean {
const minVersion = dataset.version?.compatibility?.web ?? PACKAGE_VERSION
return semver.gte(PACKAGE_VERSION, minVersion)
Expand All @@ -35,7 +31,7 @@ export function fileUrlsToAbsolute(datasetServerUrl: string, dataset: Dataset):

export function getLatestCompatibleEnabledDatasets(datasetServerUrl: string, datasetsIndexJson: DatasetsIndexV2Json) {
const datasets = datasetsIndexJson.collections
.flatMap((collection) => collection.datasets.filter(isEnabled).filter(isCompatible).filter(isLatest))
.flatMap((collection) => collection.datasets.filter(isCompatible).filter(isLatest))
.map((dataset) => fileUrlsToAbsolute(datasetServerUrl, dataset))
return { datasets }
}
Expand Down
7 changes: 3 additions & 4 deletions packages_rs/nextclade-web/src/io/fetchSingleDatasetFromUrl.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import urljoin from 'url-join'
import { concurrent } from 'fasy'

import { Dataset, VirusProperties } from 'src/types'
import { attrStrMaybe, Dataset, VirusProperties } from 'src/types'
import { removeTrailingSlash } from 'src/io/url'
import { axiosFetch, axiosHead } from 'src/io/axiosFetch'
import { sanitizeError } from 'src/helpers/sanitizeError'
Expand All @@ -24,9 +23,9 @@ export async function fetchSingleDatasetFromUrl(

const datasets = [currentDataset]
const defaultDataset = currentDataset
const currentDatasetName = currentDataset.attributes.name.value
const currentDatasetName = currentDataset.path
const defaultDatasetName = currentDatasetName
const defaultDatasetNameFriendly = currentDataset.attributes.name.valueFriendly ?? currentDatasetName
const defaultDatasetNameFriendly = attrStrMaybe(currentDataset.attributes, 'name') ?? currentDatasetName

await concurrent.forEach(
async ([filename, fileUrl]) => {
Expand Down
6 changes: 3 additions & 3 deletions packages_rs/nextclade-web/src/state/results.state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ export const analysisStatusGlobalAtom = atom({
switch (status) {
case AlgorithmGlobalStatus.started:
void getPromise(datasetCurrentAtom).then((dataset) => {
plausible('Run started', { props: { dataset: dataset?.attributes.name.value ?? 'unknown' } })
plausible('Run started', { props: { 'dataset v3': dataset?.path ?? 'unknown' } })
})
break

Expand All @@ -342,8 +342,8 @@ export const analysisStatusGlobalAtom = atom({
([results, dataset]) => {
plausible('Run completed', {
props: {
sequences: results.length,
dataset: dataset?.attributes.name.value ?? 'unknown',
'sequences': results.length,
'dataset v3': dataset?.path ?? 'unknown',
},
})
},
Expand Down
29 changes: 27 additions & 2 deletions packages_rs/nextclade-web/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { isNil, range, sumBy } from 'lodash'
import { isNil, isNumber, isFinite, isString, range, sumBy, isBoolean, get } from 'lodash'
import type {
Aa,
AnyType,
Cds,
CdsSegment,
Dataset,
Expand All @@ -10,13 +11,13 @@ import type {
LetterRangeFor_AaAnd_Position, // eslint-disable-line camelcase
LetterRangeFor_NucAnd_Position, // eslint-disable-line camelcase
NextcladeErrorOutputs,
NextcladeOutputs,
NextcladeResult,
Nuc,
NucSub,
NucSubLabeled,
PrivateNucMutations,
RangeFor_Position, // eslint-disable-line camelcase
NextcladeOutputs,
} from 'src/gen/_SchemaRoot'
import { StrictOmit } from 'ts-essentials'

Expand Down Expand Up @@ -139,3 +140,27 @@ export interface AlgorithmInput {
export function areDatasetsEqual(left?: Dataset, right?: Dataset): boolean {
return !isNil(left?.path) && !isNil(right?.path) && left?.path === right?.path
}

export function anyAsStrMaybe(x: AnyType | undefined): string | undefined {
return isString(x) ? x : undefined
}

export function anyAsNumberMaybe(x: AnyType | undefined): number | undefined {
return isNumber(x) && isFinite(x) ? x : undefined
}

export function anyAsBoolMaybe(x: AnyType | undefined): boolean | undefined {
return isBoolean(x) ? x : undefined
}

export function attrStrMaybe(attributes: Record<string, AnyType> | undefined, name: string): string | undefined {
return anyAsStrMaybe(get(attributes, name))
}

export function attrNumberMaybe(attributes: Record<string, AnyType> | undefined, name: string): number | undefined {
return anyAsNumberMaybe(get(attributes, name))
}

export function attrBoolMaybe(attributes: Record<string, AnyType> | undefined, name: string): boolean | undefined {
return anyAsBoolMaybe(get(attributes, name))
}
26 changes: 23 additions & 3 deletions packages_rs/nextclade/src/utils/any.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
use derive_more::Display;
use crate::io::json::{json_stringify, JsonPretty};
use eyre::{eyre, Report};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};

#[derive(Debug, Display, Clone, Serialize, Deserialize, JsonSchema)]
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[serde(untagged)]
pub enum AnyType {
String(String),
Int(isize),
Float(f64),
Bool(bool),
Other(serde_json::Value),
Array(Vec<AnyType>),
Object(BTreeMap<String, AnyType>),
Null,
}

#[allow(clippy::map_err_ignore)]
impl Display for AnyType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let s = match self {
AnyType::String(x) => x.clone(),
AnyType::Int(x) => x.to_string(),
AnyType::Float(x) => x.to_string(),
AnyType::Bool(x) => x.to_string(),
AnyType::Array(x) => json_stringify(&x, JsonPretty(false)).map_err(|_| std::fmt::Error)?,
AnyType::Object(x) => json_stringify(&x, JsonPretty(false)).map_err(|_| std::fmt::Error)?,
AnyType::Null => "null".to_owned(),
};
write!(f, "{s}")
}
}

impl AnyType {
Expand Down

0 comments on commit a529628

Please sign in to comment.