mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Show the list of data sets an algorithm is allowed to run on (#579)
* WIP * UI changes * get and display datasets on both compute and consume * new component for datasets that algorithm can run compute job on * AssetSelection className refactor * added internal link to AssetSelection * show loading page when changing asset * Component and asset title UI update * created new component for dataset list * removed unnecessary changes * updated link margin * prettier fix * merge fix * another fix Co-authored-by: Norbi <katunanorbert@gmai.com> Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
e8033687fb
commit
3a4851132d
5
package-lock.json
generated
5
package-lock.json
generated
@ -65001,11 +65001,6 @@
|
||||
"clsx": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"reactjs-popup": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reactjs-popup/-/reactjs-popup-2.0.4.tgz",
|
||||
"integrity": "sha512-G5jTXL2JkClKAYAdqedf+K9QvbNsFWvdbrXW1/vWiyanuCU/d7DtQzQux+uKOz2HeNVRsFQHvs7abs0Z7VLAhg=="
|
||||
},
|
||||
"read": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
|
||||
|
59
src/components/molecules/AssetComputeList.module.css
Normal file
59
src/components/molecules/AssetComputeList.module.css
Normal file
@ -0,0 +1,59 @@
|
||||
.display {
|
||||
composes: selection from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.display [class*='loaderWrap'] {
|
||||
margin: calc(var(--spacer) / 3);
|
||||
}
|
||||
|
||||
.scroll {
|
||||
composes: scroll from './FormFields/AssetSelection.module.css';
|
||||
margin-top: 0;
|
||||
border-top: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row {
|
||||
composes: row from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.row:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.row:hover {
|
||||
background-color: var(--background-content);
|
||||
}
|
||||
|
||||
.info {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.title {
|
||||
composes: title from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.hover:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.price {
|
||||
composes: price from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.price [class*='symbol'] {
|
||||
font-size: calc(var(--font-size-small) / 1.2) !important;
|
||||
}
|
||||
|
||||
.did {
|
||||
composes: did from './FormFields/AssetSelection.module.css';
|
||||
}
|
||||
|
||||
.empty {
|
||||
composes: empty from './FormFields/AssetSelection.module.css';
|
||||
}
|
49
src/components/molecules/AssetComputeList.tsx
Normal file
49
src/components/molecules/AssetComputeList.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import React from 'react'
|
||||
import Dotdotdot from 'react-dotdotdot'
|
||||
import { Link } from 'gatsby'
|
||||
import PriceUnit from '../atoms/Price/PriceUnit'
|
||||
import Loader from '../atoms/Loader'
|
||||
import styles from './AssetComputeList.module.css'
|
||||
import { AssetSelectionAsset } from './FormFields/AssetSelection'
|
||||
|
||||
function Empty() {
|
||||
return <div className={styles.empty}>No assets found.</div>
|
||||
}
|
||||
|
||||
export default function AssetComputeSelection({
|
||||
assets
|
||||
}: {
|
||||
assets: AssetSelectionAsset[]
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className={styles.display}>
|
||||
<div className={styles.scroll}>
|
||||
{!assets ? (
|
||||
<Loader />
|
||||
) : assets && !assets.length ? (
|
||||
<Empty />
|
||||
) : (
|
||||
assets.map((asset: AssetSelectionAsset) => (
|
||||
<Link
|
||||
to={`/asset/${asset.did}`}
|
||||
className={styles.row}
|
||||
key={asset.did}
|
||||
>
|
||||
<div className={styles.info}>
|
||||
<h3 className={styles.title}>
|
||||
<Dotdotdot clamp={1} tagName="span">
|
||||
{asset.name}
|
||||
</Dotdotdot>
|
||||
</h3>
|
||||
<Dotdotdot clamp={1} tagName="code" className={styles.did}>
|
||||
{asset.symbol} | {asset.did}
|
||||
</Dotdotdot>
|
||||
</div>
|
||||
<PriceUnit price={asset.price} small className={styles.price} />
|
||||
</Link>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -29,7 +29,6 @@ import {
|
||||
ComputeAlgorithm,
|
||||
ComputeOutput
|
||||
} from '@oceanprotocol/lib/dist/node/ocean/interfaces/Compute'
|
||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||
import { SearchQuery } from '@oceanprotocol/lib/dist/node/metadatacache/MetadataCache'
|
||||
import axios from 'axios'
|
||||
import FormStartComputeDataset from './FormComputeDataset'
|
||||
@ -37,6 +36,8 @@ import styles from './index.module.css'
|
||||
import SuccessConfetti from '../../../atoms/SuccessConfetti'
|
||||
import Button from '../../../atoms/Button'
|
||||
import { secondsToString } from '../../../../utils/metadata'
|
||||
import { AssetSelectionAsset } from '../../../molecules/FormFields/AssetSelection'
|
||||
import AlgorithmDatasetsListForCompute from '../../AssetContent/AlgorithmDatasetsListForCompute'
|
||||
import { getPreviousOrders, getPrice } from '../../../../utils/subgraph'
|
||||
|
||||
const SuccessAction = () => (
|
||||
@ -377,10 +378,13 @@ export default function Compute({
|
||||
</div>
|
||||
|
||||
{type === 'algorithm' ? (
|
||||
<Alert
|
||||
text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!"
|
||||
state="info"
|
||||
/>
|
||||
<>
|
||||
<Alert
|
||||
text="This algorithm has been set to private by the publisher and can't be downloaded. You can run it against any allowed data sets though!"
|
||||
state="info"
|
||||
/>
|
||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} />
|
||||
</>
|
||||
) : (
|
||||
<Formik
|
||||
initialValues={getInitialValues()}
|
||||
|
@ -5,7 +5,10 @@
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
width: auto;
|
||||
margin-left: -2rem;
|
||||
margin-right: -2rem;
|
||||
padding: 0 calc(var(--spacer)) 0 calc(var(--spacer));
|
||||
}
|
||||
|
||||
.filewrapper {
|
||||
|
@ -16,6 +16,7 @@ import { useWeb3 } from '../../../providers/Web3'
|
||||
import { usePricing } from '../../../hooks/usePricing'
|
||||
import { useConsume } from '../../../hooks/useConsume'
|
||||
import ButtonBuy from '../../atoms/ButtonBuy'
|
||||
import AlgorithmDatasetsListForCompute from '../AssetContent/AlgorithmDatasetsListForCompute'
|
||||
|
||||
const previousOrderQuery = gql`
|
||||
query PreviousOrder($id: String!, $account: String!) {
|
||||
@ -168,6 +169,9 @@ export default function Consume({
|
||||
{!isInPurgatory && <PurchaseButton />}
|
||||
</div>
|
||||
</div>
|
||||
{type === 'algorithm' && (
|
||||
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} />
|
||||
)}
|
||||
<footer className={styles.feedback}>
|
||||
<Web3Feedback isBalanceSufficient={isBalanceSufficient} />
|
||||
</footer>
|
||||
|
@ -0,0 +1,29 @@
|
||||
.datasetsContainer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: auto;
|
||||
margin-left: -2rem;
|
||||
margin-right: -2rem;
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
}
|
||||
|
||||
.datasetsContainer div[class*='AssetSelection-module--selection'] {
|
||||
width: 100%;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.datasetsContainer .text {
|
||||
margin-bottom: calc(var(--spacer) / 2);
|
||||
margin-top: calc(var(--spacer) / 2);
|
||||
text-align: center;
|
||||
color: var(--font-color-text);
|
||||
font-size: var(--font-size-base);
|
||||
font-family: var(--font-family-heading);
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
import React, { ReactElement, useEffect, useState } from 'react'
|
||||
import styles from './AlgorithmDatasetsListForCompute.module.css'
|
||||
import { getAlgorithmDatasetsForCompute } from '../../../utils/aquarius'
|
||||
import { AssetSelectionAsset } from '../../molecules/FormFields/AssetSelection'
|
||||
import AssetComputeList from '../../molecules/AssetComputeList'
|
||||
import { useOcean } from '../../../providers/Ocean'
|
||||
import { useAsset } from '../../../providers/Asset'
|
||||
|
||||
export default function AlgorithmDatasetsListForCompute({
|
||||
algorithmDid
|
||||
}: {
|
||||
algorithmDid: string
|
||||
}): ReactElement {
|
||||
const { config } = useOcean()
|
||||
const { type } = useAsset()
|
||||
const [datasetsForCompute, setDatasetsForCompute] =
|
||||
useState<AssetSelectionAsset[]>()
|
||||
|
||||
useEffect(() => {
|
||||
async function getDatasetsAllowedForCompute() {
|
||||
const datasets = await getAlgorithmDatasetsForCompute(
|
||||
algorithmDid,
|
||||
config.metadataCacheUri
|
||||
)
|
||||
setDatasetsForCompute(datasets)
|
||||
}
|
||||
type === 'algorithm' && getDatasetsAllowedForCompute()
|
||||
}, [type])
|
||||
|
||||
return (
|
||||
<div className={styles.datasetsContainer}>
|
||||
<h3 className={styles.text}>Datasets algorithm is allowed to run on</h3>
|
||||
<AssetComputeList assets={datasetsForCompute} />
|
||||
</div>
|
||||
)
|
||||
}
|
@ -6,12 +6,10 @@ import { useAsset } from '../../../providers/Asset'
|
||||
|
||||
export default function MetaFull(): ReactElement {
|
||||
const { ddo, metadata, isInPurgatory, type } = useAsset()
|
||||
const { algorithm } = ddo.findServiceByType('metadata').attributes.main
|
||||
|
||||
function DockerImage() {
|
||||
const algorithmContainer =
|
||||
ddo.findServiceByType('metadata').attributes.main.algorithm.container
|
||||
const { image } = algorithmContainer
|
||||
const { tag } = algorithmContainer
|
||||
const { image, tag } = algorithm.container
|
||||
return <span>{`${image}:${tag}`}</span>
|
||||
}
|
||||
|
||||
@ -25,7 +23,7 @@ export default function MetaFull(): ReactElement {
|
||||
content={<Publisher account={ddo?.publicKey[0].owner} />}
|
||||
/>
|
||||
|
||||
{type === 'algorithm' && (
|
||||
{type === 'algorithm' && algorithm && (
|
||||
<MetaItem title="Docker Image" content={<DockerImage />} />
|
||||
)}
|
||||
<MetaItem title="DID" content={<code>{ddo?.id}</code>} />
|
||||
|
@ -11,7 +11,7 @@ export default function PageTemplateAssetDetails({
|
||||
}: {
|
||||
uri: string
|
||||
}): ReactElement {
|
||||
const { ddo, title, error, isInPurgatory } = useAsset()
|
||||
const { ddo, title, error, isInPurgatory, loading } = useAsset()
|
||||
const [pageTitle, setPageTitle] = useState<string>()
|
||||
|
||||
useEffect(() => {
|
||||
@ -23,7 +23,7 @@ export default function PageTemplateAssetDetails({
|
||||
setPageTitle(isInPurgatory ? '' : title)
|
||||
}, [ddo, error, isInPurgatory, title])
|
||||
|
||||
return ddo && pageTitle !== undefined ? (
|
||||
return ddo && pageTitle !== undefined && !loading ? (
|
||||
<>
|
||||
<Page title={pageTitle} uri={uri}>
|
||||
<Router basepath="/asset">
|
||||
|
@ -28,6 +28,7 @@ interface AssetProviderValue {
|
||||
type: MetadataMain['type'] | undefined
|
||||
error?: string
|
||||
refreshInterval: number
|
||||
loading: boolean
|
||||
refreshDdo: (token?: CancelToken) => Promise<void>
|
||||
}
|
||||
|
||||
@ -53,9 +54,11 @@ function AssetProvider({
|
||||
const [owner, setOwner] = useState<string>()
|
||||
const [error, setError] = useState<string>()
|
||||
const [type, setType] = useState<MetadataMain['type']>()
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
|
||||
const fetchDdo = async (token?: CancelToken) => {
|
||||
Logger.log('[asset] Init asset, get DDO')
|
||||
setLoading(true)
|
||||
const ddo = await retrieveDDO(
|
||||
asset as string,
|
||||
config.metadataCacheUri,
|
||||
@ -69,13 +72,16 @@ function AssetProvider({
|
||||
} else {
|
||||
setError(undefined)
|
||||
}
|
||||
setLoading(false)
|
||||
return ddo
|
||||
}
|
||||
|
||||
const refreshDdo = async (token?: CancelToken) => {
|
||||
setLoading(true)
|
||||
const ddo = await fetchDdo(token)
|
||||
Logger.debug('[asset] Got DDO', ddo)
|
||||
setDDO(ddo)
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
//
|
||||
@ -116,7 +122,7 @@ function AssetProvider({
|
||||
|
||||
const initMetadata = useCallback(async (ddo: DDO): Promise<void> => {
|
||||
if (!ddo) return
|
||||
|
||||
setLoading(true)
|
||||
const returnedPrice = await getPrice(ddo)
|
||||
setPrice({ ...returnedPrice })
|
||||
|
||||
@ -130,6 +136,7 @@ function AssetProvider({
|
||||
|
||||
setIsInPurgatory(ddo.isInPurgatory === 'true')
|
||||
await setPurgatory(ddo.id)
|
||||
setLoading(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
@ -152,6 +159,7 @@ function AssetProvider({
|
||||
isInPurgatory,
|
||||
purgatoryData,
|
||||
refreshInterval,
|
||||
loading,
|
||||
refreshDdo
|
||||
} as AssetProviderValue
|
||||
}
|
||||
|
@ -13,6 +13,17 @@ import { AssetSelectionAsset } from '../components/molecules/FormFields/AssetSel
|
||||
import { PriceList, getAssetsPriceList } from './subgraph'
|
||||
import axios, { CancelToken, AxiosResponse } from 'axios'
|
||||
|
||||
function getQueryForAlgorithmDatasets(algorithmDid: string) {
|
||||
return {
|
||||
query: {
|
||||
query_string: {
|
||||
query: `service.attributes.main.privacy.publisherTrustedAlgorithms.did:${algorithmDid}`
|
||||
}
|
||||
},
|
||||
sort: { created: -1 }
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: import directly from ocean.js somehow.
|
||||
// Transforming Aquarius' direct response is needed for getting actual DDOs
|
||||
// and not just strings of DDOs. For now, taken from
|
||||
@ -148,3 +159,33 @@ export async function transformDDOToAssetSelection(
|
||||
})
|
||||
return algorithmList
|
||||
}
|
||||
|
||||
export async function getAlgorithmDatasetsForCompute(
|
||||
algorithmId: string,
|
||||
metadataCacheUri: string
|
||||
): Promise<AssetSelectionAsset[]> {
|
||||
const source = axios.CancelToken.source()
|
||||
const computeDatasets = await queryMetadata(
|
||||
getQueryForAlgorithmDatasets(algorithmId),
|
||||
metadataCacheUri,
|
||||
source.token
|
||||
)
|
||||
const computeDatasetsForCurrentAlgorithm: DDO[] = []
|
||||
computeDatasets.results.forEach((data: DDO) => {
|
||||
const algorithm = data
|
||||
.findServiceByType('compute')
|
||||
.attributes.main.privacy.publisherTrustedAlgorithms.find(
|
||||
(algo) => algo.did === algorithmId
|
||||
)
|
||||
algorithm && computeDatasetsForCurrentAlgorithm.push(data)
|
||||
})
|
||||
if (computeDatasetsForCurrentAlgorithm.length === 0) {
|
||||
return []
|
||||
}
|
||||
const datasets = await transformDDOToAssetSelection(
|
||||
computeDatasetsForCurrentAlgorithm,
|
||||
metadataCacheUri,
|
||||
[]
|
||||
)
|
||||
return datasets
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user