Commit 003809cc authored by Markus Scheidgen's avatar Markus Scheidgen
Browse files

Updated react scripts. Minor fixes. Added overview figure. #233

parent a420d4bc
Pipeline #74237 failed with stages
in 6 minutes and 37 seconds
......@@ -4,6 +4,12 @@
"commit": "e98694e",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.2",
"@testing-library/user-event": "^7.1.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.1",
"@material-ui/core": "^4.0.0",
"@material-ui/icons": "^4.0.0",
"@material-ui/lab": "^4.0.0-alpha.49",
......@@ -26,19 +32,16 @@
"pace-js": "^1.0.2",
"piwik-react-router": "^0.12.1",
"qs": "^6.8.0",
"react": "^16.8.0",
"react-app-polyfill": "^1.0.1",
"react-autosuggest": "^9.4.3",
"react-cookie": "^3.0.8",
"react-copy-to-clipboard": "^5.0.1",
"react-dom": "^16.8.0",
"react-dropzone": "^5.0.1",
"react-highlight": "^0.12.0",
"react-infinite-scroller": "^1.2.4",
"react-json-view": "^1.19.1",
"react-keycloak": "^6.1.0",
"react-router-dom": "^5.1.2",
"react-scripts": "1.1.4",
"react-swipeable-views": "^0.13.0",
"recompose": "^0.28.2",
"swagger-client": "^3.8.22",
......@@ -51,13 +54,13 @@
"prebuild": "npm run generate-build-version",
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"devDependencies": {
"@material-ui/codemod": "^4.5.0",
"babel-eslint": "^8.2.6",
"eslint": "^4.19.1",
"babel-eslint": "^10.1.0",
"eslint": "^6.6.0",
"eslint-config-standard": "^11.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^8.0.1",
......@@ -67,5 +70,20 @@
"react-docgen": "^5.3.0",
"serve": "^10.0.0"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"homepage": "http://example.com/fairdi/nomad/latest/gui"
}
import React from 'react'
import React, { useContext, useLayoutEffect, useRef, useCallback, useEffect } from 'react'
import {ReactComponent as AboutSvg} from './about.svg'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import Markdown from './Markdown'
import { appBase, optimadeBase, apiBase, debug, consent } from '../config'
import { compose } from 'recompose'
import { withApi } from './api'
import { apiContext } from './api'
import packageJson from '../../package.json'
import { domains } from './domains'
import { Grid, Card, CardContent, Typography, makeStyles, Link } from '@material-ui/core'
import { Link as RouterLink, useHistory } from 'react-router-dom'
class About extends React.Component {
static propTypes = {
classes: PropTypes.object.isRequired,
info: PropTypes.object,
raiseError: PropTypes.func.isRequired
const useCardStyles = makeStyles(theme => ({
title: {
marginBottom: theme.spacing(1)
}
}))
static styles = theme => ({
root: {
padding: theme.spacing(3)
function MarkdownCard({title, children, xs, top, bottom}) {
const classes = useCardStyles()
const style = {}
if (top) {
style['paddingBottom'] = 0
}
if (bottom) {
style['paddingTop'] = 0
}
return <Grid item xs={xs} style={style}>
<Card>
<CardContent>
<Typography variant="h6" className={classes.title}>{title}</Typography>
<Typography>{children}</Typography>
</CardContent>
</Card>
</Grid>
}
MarkdownCard.propTypes = {
title: PropTypes.string.isRequired,
children: PropTypes.node,
xs: PropTypes.number,
top: PropTypes.bool,
bottom: PropTypes.bool
}
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(3)
},
container: {
maxWidth: 1024,
margin: 'auto',
width: '100%'
}
}))
export default function About() {
const classes = useStyles()
const {info} = useContext(apiContext)
const svg = useRef()
const history = useHistory()
const makeClickable = useCallback((id, onClick) => {
const element = svg.current.querySelector('#' + id)
element.style.cursor = 'pointer'
element.firstChild.onclick = () => {
onClick()
}
})
}, [svg])
const setText = useCallback((id, lines) => {
const element = svg.current.querySelector('#' + id)
const x = element.getAttribute('x')
element.innerHTML = lines.map((line, i) => `<tspan x="${x}" dy="${i === 0 ? '0' : '1.2em'}">${line}</tspan>`).join('')
}, [svg])
useLayoutEffect(() => {
makeClickable('upload', () => {
history.push('/upload')
})
makeClickable('encyclopedia', () => {
window.location.href = 'https://encyclopedia.nomad-coe.eu/gui/#/search'
})
makeClickable('search', () => {
history.push('/search')
})
}, [svg])
render() {
const { classes, info } = this.props
useEffect(() => {
const statistics = (info && info.statistics) || {}
const value = (key, unit) => {
const nominal = statistics[key]
let stringValue = null
if (nominal) {
if (nominal >= 1.0e+9) {
stringValue = Math.floor(nominal / 1.0e+9) + ' bln.'
} else if (nominal >= 1.0e+6) {
stringValue = Math.floor(nominal / 1.0e+6) + ' mln.'
} else {
stringValue = Math.floor(nominal / 1.0e+3) + ' tsd.'
}
return `${stringValue || '...'} ${unit}`
}
}
setText('repositoryStats', [
value('n_entries', 'entries'),
value('n_uploads', 'uploads')
])
setText('archiveStats', [
value('n_calculations', 'results'),
value('n_quantities', 'quantities')
])
}, [svg, info])
return (
<div className={classes.root}>
return <div className={classes.root}>
<Grid className={classes.container} container spacing={2}>
<Grid item xs={12}>
<Markdown>{`
# The NOMAD Repository and Archive
This is the *graphical user interface* (GUI) for the NOMAD Repository and
Archive. It allows you to **search, access, and download all NOMAD data** in its
raw (Repository) and processed (Archive) form. You can **upload and manage your own
raw materials science data**. Learn more about what data can be uploaded
and how to prepare your data on the [NOMAD Repository homepage](https://repository.nomad-coe.eu/).
You can access all published data without an account. If you want to provide
your own data, please login or register for an account.
In the future, this web-page will include more and more features of other NOMAD
components as an effort to consolidate the various web applications from the
NOMAD Repository, Archive, Metainfo, Encyclopedia, and Analytics Toolkit.
### Getting Help
If you encounter any difficulties, please write to
[webmaster@nomad-coe.eu](mailto:webmaster@nomad-coe.eu). If you think
that this web-page is not working as expected, or if you want to start a discussion
about possible features, feel free to open an issue on our [issue tracking
system](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR/issues).
### APIs
The NOMAD Repository and Archive can also be accessed programmatically via ReST APIs.
There is the proprietary NOMAD API and an implementation of the
[OPTiMaDe API (0.10.0)](https://github.com/Materials-Consortia/OPTiMaDe/tree/master)
standardized by the [OPTiMaDe consortium](https://www.optimade.org/)
Both APIs are described via [swagger](https://swagger.io/) (also known as OpenAPI spec.),
therefore you can use your favorite swagger client library
(e.g. [bravado](https://github.com/Yelp/bravado) for Python).
There are also web-based GUIs that allow to explore the APIs and their documentation:
- [NOMAD API](${apiBase}/)
- [OPTiMaDe API](${optimadeBase}/)
There is a [tutorial on how to use the API with Python](${appBase}/docs/api_tutorial.html).
There is also a Python library. You can use *pip* to install the library.
\`\`\`
pip install ${appBase}/dist/nomad-0.8.0.tar.gz
\`\`\`
The NOMAD Archive uses data that adheres to formal data definitions that we call
the NOMAD Metainfo. You can download these definition in their [JSON form
here](${apiBase}/archive/metainfo/all.nomadmetainfo.json). Otherwise, you
can use the Meta Info browser from the menu to explore.
${debug ? `
### Material science data and domains
Originally NOMAD was build for DFT calculations and data from the respective
community code. By NOMAD supports multiple materials science domains:
${info && info.domains.map(domain => domains[domain.name]).map(domain => `- ${domain.name}: ${domain.about}`).join('\n')}
` : ''}
### Developer Documentation
The [in-depth developer documentation](${appBase}/docs/index.html)
contains a general introduction to NOMAD, the underlying architecture,
is (meta)data, and processing. You will also find some information on how to use
the NOMAD ReST API. It contains information about how to develop NOMAD, how to
operate it, how to contribute parsers, and much more.
### Source code
The source-code for this new version of NOMAD (dubbed *nomad@FAIRDI*) is maintained
at the MPCDF's [gitlab](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR).
To push code, you need an MPCDF account and you can apply
[here](https://www.mpcdf.mpg.de/userspace/forms/onlineregistrationform).
${debug ? `
### Log management with Elastic stack
We use a central logging system based on the *elastic*-stack
(previously called *Elastic Logstash Kibana* (ELK)-stack).
This system pushes logs, events, monitoring data,
and other application metrics to a central database where it
can be analysed visually by us.
### Test user
During development this GUI might not be connected to the actual NOMAD
repository. Therefore, you cannot create a user or login with an existing
user. You might use the test user \`leonard.hofstadter@nomad-fairdi.tests.de\`
with password \`password\`. The user \`sheldon.cooper@nomad-fairdi.tests.de\` is
used for data that has no provenance with the original NOMAD CoE database.
` : ''}
### Terms of use and licenses
${consent}
### About this version
- version (API): \`${info ? info.version : 'loading'}/${info ? info.git.commit : 'loading'}\`
- version (GUI): \`${packageJson.version}/${packageJson.commit}\`
- domains: ${info ? Object.keys(info.domains).map(domain => info.domains[domain].name).join(', ') : 'loading'}
- git: \`${info ? info.git.ref : 'loading'}; ${info ? info.git.version : 'loading'}\`
- last commit message: *${info ? info.git.log : 'loading'}*
- supported codes: ${info ? info.codes.join(', ') : 'loading'}
- parsers: ${info ? info.parsers.join(', ') : 'loading'}
- normalizers: ${info ? info.normalizers.join(', ') : 'loading'}
# The NOMAD Repository and Archive
This is the *graphical user interface* (GUI) for the NOMAD Repository and
Archive. It allows you to **search, access, and download all NOMAD data** in its
raw (Repository) and processed (Archive) form. You can **upload and manage your own
raw materials science data**. Learn more about what data can be uploaded
and how to prepare your data on the [NOMAD Repository homepage](https://repository.nomad-coe.eu/).
You can access all published data without an account. If you want to provide
your own data, please login or register for an account.
`}</Markdown>
</div>
)
}
}
</Grid>
<MarkdownCard xs={6} title="Interactive Search" top>
NOMAD extracts <b>rich metadata</b> from uploaded raw-data. <Link component={RouterLink} to={'/search'}>
Explore NOMAD&apos;s data</Link> by creating complex queries from interactive data visualizations of key
properties, including the simulated composition/system, used method, upload metadata,
as well as material classifications and available quantities. Or use
the <b>Optimade</b> filter language to add arbitrarily nested queries.
</MarkdownCard>
<MarkdownCard xs={6} title="A common data format" top>
The <b>NOMAD Archive</b> provides data in processed and normalized form in a machine processable and common hierarchical format.
All data in the NOMAD Archive is organized into nested sections of quantities with well defined units,
data types, shapes, and descriptions. These definitions are called the <b>NOMAD Metainfo</b> and they
can be <Link component={RouterLink} to={'/metainfo'}>browsed here</Link>.
</MarkdownCard>
<Grid item xs={12} style={{paddingTop: 0, paddingBottom: 0}}>
<AboutSvg ref={svg}></AboutSvg>
</Grid>
<MarkdownCard xs={4} title="Uploading is simple" bottom>
<p>
You provide your own data <i>as is</i>. Just zip your code input and out files as they are,
including nested directory structures and potential auxiliary files, and upload
up to 32GB in a single .zip or .tar(.gz) file. NOMAD will automatically discover
and process the relevant files.
</p>
<p>
You can <b>privately</b> inspect, curate, or delete your data before publishing.
Data can be published with an <b>embargo (up to 3 years)</b> to only share data with
selected users.
</p>
<p>
Add additional metadata like <b>comments</b>, <b>references</b> to websites or papers, and your
<b>co-authors</b>. Curate your uploaded code runs into larger <b>datasets</b> and cite your data with a <b>DOI</b>
that we provide on request.
</p>
<p>
You can provide via GUI or shell command <Link component={RouterLink} to={'/uploads'}>here</Link>.
Manage already uploaded data <Link component={RouterLink} to={'/userdata'}>here</Link>.
</p>
</MarkdownCard>
<MarkdownCard xs={4} title="Processing" bottom>
<p>
Uploaded data is automatically processed and made available
in the uploaded <b>raw files</b> or in its processed and unified <b>Archive</b> form.
NOMAD parsers convert raw code input and output files into NOMAD&apos;s common data format.
You can inspect the Archive form and extracted metadata before
publishing your data.
</p>
<p>NOMAD supports most community codes: {info ? info.codes.join(', ') : '...'}</p>
<p>
To use NOMAD&apos;s parsers and normalizers outside of NOMAD.
Read <Link href="">here</Link> on how to install
our software and how to use NOMAD processing in your Python environment.
</p>
</MarkdownCard>
<MarkdownCard xs={4} title="APIs" bottom><Markdown>{`
The NOMAD can also be accessed programmatically via ReST APIs.
There is the proprietary NOMAD API and an implementation of the
standardized [OPTiMaDe API (0.10.0)](https://github.com/Materials-Consortia/OPTiMaDe/tree/master)
materials science database API.
Both APIs are described via [swagger/OpenAPI spec.](https://swagger.io/),
therefore you can use your favorite swagger client library
(e.g. [bravado](https://github.com/Yelp/bravado) for Python):
- [NOMAD API](${apiBase}/)
- [OPTiMaDe API](${optimadeBase}/)
There is a [tutorial on how to use the API with plain Python](${appBase}/docs/api_tutorial.html).
Another [tutorial covers how to install and use NOMAD's Python client library](${appBase}/docs/archive_tutorial.html).
The [NOMAD Analytics Toolkit](https://analytic-toolkit.nomad-coe.eu) allows to use
this without installation and directly on NOMAD servers.
`}</Markdown></MarkdownCard>
<Grid item xs={12}>
<Markdown>{`
### Getting Help
If you encounter any difficulties, please write to
[webmaster@nomad-coe.eu](mailto:webmaster@nomad-coe.eu). If you think
that this web-page is not working as expected, or if you want to start a discussion
about possible features, feel free to open an issue on our [issue tracking
system](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR/issues).
### Developer Documentation
The [in-depth developer documentation](${appBase}/docs/index.html)
contains a general introduction to NOMAD, the underlying architecture,
is (meta)data, and processing. You will also find some information on how to use
the NOMAD ReST API. It contains information about how to develop NOMAD, how to
operate it, how to contribute parsers, and much more.
### Source code
The source-code for this new version of NOMAD (dubbed *nomad@FAIRDI*) is maintained
at the MPCDF's [gitlab](https://gitlab.mpcdf.mpg.de/nomad-lab/nomad-FAIR).
To push code, you need an MPCDF account and you can apply
[here](https://www.mpcdf.mpg.de/userspace/forms/onlineregistrationform).
export default compose(withApi(), withStyles(About.styles))(About)
${debug ? `
### Material science data and domains
Originally NOMAD was build for DFT calculations and data from the respective
community code. By NOMAD supports multiple materials science domains:
${info && info.domains.map(domain => domains[domain.name]).map(domain => `- ${domain.name}: ${domain.about}`).join('\n')}
` : ''}
${debug ? `
### Log management with Elastic stack
We use a central logging system based on the *elastic*-stack
(previously called *Elastic Logstash Kibana* (ELK)-stack).
This system pushes logs, events, monitoring data,
and other application metrics to a central database where it
can be analysed visually by us.
### Test user
During development this GUI might not be connected to the actual NOMAD
repository. Therefore, you cannot create a user or login with an existing
user. You might use the test user \`leonard.hofstadter@nomad-fairdi.tests.de\`
with password \`password\`. The user \`sheldon.cooper@nomad-fairdi.tests.de\` is
used for data that has no provenance with the original NOMAD CoE database.
` : ''}
### Terms of use and licenses
${consent}
### About this version
- version (API): \`${info ? info.version : 'loading'}/${info ? info.git.commit : 'loading'}\`
- version (GUI): \`${packageJson.version}/${packageJson.commit}\`
- domains: ${info ? Object.keys(info.domains).map(domain => info.domains[domain].name).join(', ') : 'loading'}
- git: \`${info ? info.git.ref : 'loading'}; ${info ? info.git.version : 'loading'}\`
- last commit message: *${info ? info.git.log : 'loading'}*
- supported codes: ${info ? info.codes.join(', ') : 'loading'}
- parsers: ${info ? info.parsers.join(', ') : 'loading'}
- normalizers: ${info ? info.normalizers.join(', ') : 'loading'}
`}</Markdown>
</Grid>
</Grid>
</div>
}
......@@ -5,7 +5,7 @@ import PropTypes, { instanceOf } from 'prop-types'
import { compose } from 'recompose'
import classNames from 'classnames'
import { MuiThemeProvider, withStyles, makeStyles } from '@material-ui/core/styles'
import { LinearProgress, ListItemIcon, ListItemText, MenuList, MenuItem, Typography,
import { LinearProgress, MenuList, Typography,
AppBar, Toolbar, Button, DialogContent, DialogTitle, DialogActions, Dialog, Tooltip,
Snackbar, SnackbarContent } from '@material-ui/core'
import { Route, Link, withRouter, useLocation } from 'react-router-dom'
......@@ -183,7 +183,7 @@ class NavigationUnstyled extends React.Component {
marginRight: 0
},
divider: {
flexGrow: 1
width: theme.spacing(3)
}
})
......@@ -301,9 +301,15 @@ class NavigationUnstyled extends React.Component {
tooltip="Manage your data"
icon={<UserDataIcon/>}
/>
<MainMenuItem
title="Meta Info"
path="/metainfo"
tooltip="Browse the archive schema"
icon={<MetainfoIcon/>}
/>
<div className={classes.divider} />
<MainMenuItem
title="Overview"
title="About"
path="/"
tooltip="NOMAD Repository and Archive"
icon={<AboutIcon/>}
......@@ -314,12 +320,6 @@ class NavigationUnstyled extends React.Component {
tooltip="Frequently Asked Questions (FAQ)"
icon={<FAQIcon/>}
/>
<MainMenuItem
title="Meta Info"
path="/metainfo"
tooltip="Browse the archive schema"
icon={<MetainfoIcon/>}
/>
</MenuList>
<LoadingIndicator />
</AppBar>
......
......@@ -12,6 +12,11 @@ class FAQ extends React.Component {
static styles = theme => ({
root: {
padding: theme.spacing(3)
},
container: {
maxWidth: 1024,
margin: 'auto',
width: '100%'
}
})
......@@ -19,8 +24,8 @@ class FAQ extends React.Component {
const { classes } = this.props
return (
<div className={classes.root}>
<Markdown>{`
<div className={classes.root}><div className={classes.container}>
<Markdown >{`
# Frequently Asked Questions (FAQ)
These are often repeated questions that cover the basic NOMAD use-cases. If you have
......@@ -160,7 +165,7 @@ class FAQ extends React.Component {
write us an Email ([${email}](mailto:${email})) and we will figure out if and how
to support this code in the future.
`}</Markdown>
</div>
</div></div>
)
}
}
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 1025 632" version="1.1" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.5;">
<g id="Artboard1" transform="matrix(1.00641,0,0,1.03856,-2.66723,0)">
<rect x="2.65" y="0" width="1018.16" height="608.36" style="fill:none;"/>
<clipPath id="_clip1">
<rect x="2.65" y="0" width="1018.16" height="608.36"/>
</clipPath>
<g clip-path="url(#_clip1)">
<g transform="matrix(1,0,0,0.964223,9.59233e-14,12.4654)">
<g transform="matrix(0.993631,0,0,0.998598,2.65025,0)">
<path d="M30.051,395.2L9,382.85L9,-9.946" style="fill:none;stroke:black;stroke-width:0.5px;"/>
</g>
<g transform="matrix(0.993631,0,0,0.998598,2.65025,0)">
<path d="M570.5,154.635L570.5,-9.946" style="fill:none;stroke:black;stroke-width:0.5px;"/>
</g>
<g transform="matrix(0.993631,0,0,0.998598,2.65025,0)">
<path d="M173.761,518.483L173.761,615.873" style="fill:none;stroke:black;stroke-width:0.5px;"/>
</g>
<g transform="matrix(0.993631,0,0,0.998598,2.65025,0)">
<path d="M476.136,344.02L551.24,386.958L551.24,615.873" style="fill:none;stroke:black;stroke-width:0.5px;"/>
</g>
<g transform="matrix(0.993631,0,0,0.998598,2.65025,0)">
<path d="M884.281,286.732L855.686,302.319L855.686,615.873" style="fill:none;stroke:black;stroke-width:0.5px;"/>
</g>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-418.948,-98.2253)">
<path d="M247.871,178.885L278.855,196.774" style="fill:none;stroke:black;stroke-width:2.07px;"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-418.948,-98.2253)">
<path d="M247.871,214.663L309.839,250.44L371.806,214.663L371.806,207.187L309.839,171.41L247.871,207.187L247.871,214.663Z" style="fill:rgb(123,31,162);"/>
</g>
<g transform="matrix(0.993631,0,0,0.962871,2.65025,0)">
<path d="M33.307,410.333L475.711,154.911L554.127,200.185L551.627,204.515L475.711,160.685L38.307,413.22L33.307,410.333Z"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-103.005,-255.503)">
<path d="M327.372,177.707L358.588,195.461L373.365,186.93L369.268,171.406L342.381,169.042L327.372,177.707Z" style="fill:white;stroke:black;stroke-width:0.83px;stroke-linecap:square;stroke-linejoin:miter;"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-401.534,-88.4828)">
<path d="M327.35,177.72L358.566,195.474L395.044,174.413L390.948,158.89L364.06,156.525L327.35,177.72Z" style="fill:white;stroke:black;stroke-width:0.83px;stroke-linecap:square;stroke-linejoin:miter;"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-699.627,78.2935)">
<path d="M325.331,178.885L356.547,196.64L395.044,174.413L390.948,158.89L364.06,156.525L325.331,178.885Z" style="fill:white;stroke:black;stroke-width:0.83px;stroke-linecap:square;stroke-linejoin:miter;"/>
</g>
<g transform="matrix(0.993631,0,0,0.962871,2.65025,0)">
<path d="M858.552,280.292L863.552,283.179L475.711,507.099L397.295,461.826L399.795,457.496L475.711,501.326L858.552,280.292Z"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-418.948,-98.2253)">
<path d="M453.475,154.483L495.742,178.885" style="fill:none;stroke:black;stroke-width:2.07px;stroke-linecap:butt;stroke-linejoin:miter;"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-418.948,-98.2253)">
<path d="M371.806,143.108L433.774,178.885L495.742,143.108L495.742,135.633L433.774,99.856L371.806,135.633L371.806,143.108Z" style="fill:rgb(0,121,107);"/>
</g>
<g transform="matrix(2.40523,0,0,2.33077,-171.125,-236.877)">
<path d="M371.806,143.108L433.774,178.885L495.742,143.108L433.774,107.331L371.806,143.108Z" style="fill:white;"/>
<path d="M371.806,143.108L433.774,178.885L495.742,143.108L433.774,107.331L371.806,143.108ZM374.698,143.108L433.774,177.216L492.85,143.108L433.774,109.001L374.698,143.108Z" style="fill:rgb(255,160,0);"/>
</g>
<g transform="matrix(1.44927,-0.810834,1.67347,0.93627,-632.38,373.612)">
<text x="280px" y="325.975px" style="font-family:'TitilliumWeb-Bold', 'Titillium Web';font-weight:700;font-size:14.25px;fill:rgb(235,235,235);">repository</text>
</g>
<g transform="matrix(1.44927,-0.810834,1.67347,0.93627,-736.377,315.428)">
<text id="repositoryStats" x="280px" y="322.821px" style="font-family:'TitilliumWeb-Regular', 'Titillium Web';font-size:9.5px;fill:rgb(235,235,235);">repository stats</text>
</g>
<g transform="matrix(1.44927,-0.810834,1.67347,0.93627,-437.669,148.307)">
<text id="archiveStats" x="280px" y="322.821px" style="font-family:'TitilliumWeb-Regular', 'Titillium Web';font-size:9.5px;fill:rgb(235,235,235);">archiv<tspan x="303.76px " y="322.821px ">e</tspan> stats</text>
</g>
<g transform="matrix(1.44927,-0.810834,1.67347,0.93627,-516.391,267.591)">
<text x="280px" y="324.526px" style="font-family:'TitilliumWeb-Regular', 'Titillium Web';font-size:11.875px;">pr<tspan x="290.248px " y="324.526px ">o</tspan>cessing</text>