1
0
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:
Norbi 2021-06-14 15:37:03 +03:00 committed by GitHub
parent e8033687fb
commit 3a4851132d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 245 additions and 19 deletions

5
package-lock.json generated
View File

@ -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",

View 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';
}

View 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>
)
}

View File

@ -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"
/>
<AlgorithmDatasetsListForCompute algorithmDid={ddo.id} />
</>
) : (
<Formik
initialValues={getInitialValues()}

View File

@ -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 {

View File

@ -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>

View File

@ -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);
}

View File

@ -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>
)
}

View File

@ -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>} />

View File

@ -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">

View File

@ -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
}

View File

@ -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
}