Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
nomad-lab
nomad-FAIR
Commits
40f8745e
Commit
40f8745e
authored
Apr 07, 2020
by
Alvin Noe Ladines
Browse files
Implemented fixes in issue
#312
parent
e1952263
Pipeline
#72446
passed with stages
in 17 minutes and 27 seconds
Changes
11
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
gui/src/components/api.js
View file @
40f8745e
...
...
@@ -386,7 +386,7 @@ class Api {
.
finally
(
this
.
onFinishLoading
)
}
async
search
(
search
)
{
async
search
(
search
,
statisticsToRefresh
=
[]
)
{
this
.
onStartLoading
()
return
this
.
swagger
()
.
then
(
client
=>
client
.
apis
.
repo
.
search
({
...
...
@@ -399,11 +399,12 @@ class Api {
// this helps to keep consistent values, e.g. in the metadata search view
if
(
response
.
statistics
)
{
const
empty
=
{}
const
refreshList
=
[
'
total
'
,
'
authors
'
,
'
atoms
'
].
concat
(
statisticsToRefresh
)
Object
.
keys
(
response
.
statistics
.
total
.
all
).
forEach
(
metric
=>
{
empty
[
metric
]
=
0
})
Object
.
keys
(
response
.
statistics
)
.
filter
(
key
=>
!
[
'
total
'
,
'
authors
'
,
'
atoms
'
]
.
includes
(
key
))
.
filter
(
key
=>
!
refreshList
.
includes
(
key
))
.
forEach
(
key
=>
{
if
(
!
this
.
statistics
[
key
])
{
this
.
statistics
[
key
]
=
new
Set
()
...
...
gui/src/components/dft/DFTSearchAggregations.js
View file @
40f8745e
...
...
@@ -12,6 +12,11 @@ class DFTSearchAggregations extends React.Component {
static
contextType
=
SearchContext
.
type
componentDidMount
()
{
const
{
setStatisticsToRefresh
}
=
this
.
context
setStatisticsToRefresh
(
'
dft.labels_springer_compound_class
'
)
}
render
()
{
const
{
info
}
=
this
.
props
const
{
state
:
{
response
:
{
statistics
},
usedMetric
}}
=
this
.
context
...
...
gui/src/components/dft/DFTSearchByPropertyAggregations.js
View file @
40f8745e
...
...
@@ -12,6 +12,11 @@ class DFTSearchByPropertyAggregations extends React.Component {
static
contextType
=
SearchContext
.
type
componentDidMount
()
{
const
{
setStatisticsToRefresh
}
=
this
.
context
setStatisticsToRefresh
(
'
dft.labels_springer_classification
'
)
}
render
()
{
const
{
info
}
=
this
.
props
const
{
state
:
{
response
:
{
statistics
},
usedMetric
}}
=
this
.
context
...
...
gui/src/components/search/QuantityHistogram.js
View file @
40f8745e
...
...
@@ -35,11 +35,17 @@ const _mapping = {
'
oscillator_strengths
'
:
'
Oscillator strengths
'
,
'
transition_dipole_moments
'
:
'
Transition dipole moments
'
}
function
mapKey
(
name
)
{
if
(
name
in
_mapping
)
{
return
_mapping
[
name
]
function
mapKey
(
key
)
{
let
name
=
key
const
maxLength
=
17
if
(
key
in
_mapping
)
{
name
=
_mapping
[
key
]
}
if
(
name
.
length
>
maxLength
)
{
return
name
.
substring
(
0
,
maxLength
)
+
'
...
'
}
else
{
return
name
}
return
name
}
class
QuantityHistogramUnstyled
extends
React
.
Component
{
...
...
@@ -82,10 +88,10 @@ class QuantityHistogramUnstyled extends React.Component {
}
handleItemClicked
(
item
)
{
if
(
this
.
props
.
value
===
item
.
name
)
{
if
(
this
.
props
.
value
===
item
.
key
)
{
this
.
props
.
onChanged
(
null
)
}
else
{
this
.
props
.
onChanged
(
item
.
name
)
this
.
props
.
onChanged
(
item
.
key
)
}
}
...
...
@@ -102,6 +108,7 @@ class QuantityHistogramUnstyled extends React.Component {
const
data
=
Object
.
keys
(
this
.
props
.
data
)
.
map
(
key
=>
({
key
:
key
,
name
:
mapKey
(
key
),
value
:
this
.
props
.
data
[
key
][
this
.
props
.
metric
]
}))
...
...
@@ -147,8 +154,8 @@ class QuantityHistogramUnstyled extends React.Component {
withData
.
exit
().
remove
()
const
rectColor
=
d
=>
selected
===
d
.
name
?
nomadPrimaryColor
.
main
:
nomadSecondaryColor
.
light
const
textColor
=
d
=>
selected
===
d
.
name
?
'
#FFF
'
:
'
#000
'
const
rectColor
=
d
=>
selected
===
d
.
key
?
nomadPrimaryColor
.
main
:
nomadSecondaryColor
.
light
const
textColor
=
d
=>
selected
===
d
.
key
?
'
#FFF
'
:
'
#000
'
let
item
=
withData
.
enter
()
.
append
(
'
g
'
)
...
...
gui/src/components/search/Search.js
View file @
40f8745e
...
...
@@ -17,6 +17,7 @@ import GroupList from './GroupList'
import
ApiDialogButton
from
'
../ApiDialogButton
'
import
SearchIcon
from
'
@material-ui/icons/Search
'
import
UploadsChart
from
'
./UploadsChart
'
import
UploadersList
from
'
./UploadersList
'
class
Search
extends
React
.
Component
{
static
tabs
=
{
...
...
@@ -255,11 +256,14 @@ class UsersVisualization extends React.Component {
const
{
open
}
=
this
.
props
return
<
KeepState
visible
=
{
open
}
render
=
{()
=>
<
Card
>
<
CardContent
>
<
UploadsChart
metricsDefinitions
=
{
domain
.
searchMetrics
}
/
>
<
/CardContent
>
<
/Card
>
<
div
>
<
Card
>
<
CardContent
>
<
UploadsChart
metricsDefinitions
=
{
domain
.
searchMetrics
}
/
>
<
/CardContent
>
<
/Card
>
<
UploadersList
/>
<
/div
>
}
/
>
}
}
...
...
gui/src/components/search/SearchContext.js
View file @
40f8745e
...
...
@@ -34,6 +34,7 @@ class SearchContext extends React.Component {
this
.
handleQueryChange
=
this
.
handleQueryChange
.
bind
(
this
)
this
.
handleMetricChange
=
this
.
handleMetricChange
.
bind
(
this
)
this
.
handleDomainChange
=
this
.
handleDomainChange
.
bind
(
this
)
this
.
handleStatisticsToRefreshChange
=
this
.
handleStatisticsToRefreshChange
.
bind
(
this
)
this
.
state
.
query
=
this
.
props
.
initialQuery
||
{}
if
(
this
.
props
.
initialRequest
)
{
this
.
state
.
request
=
{...
this
.
state
.
request
,
...
this
.
props
.
initialRequest
}
...
...
@@ -56,7 +57,8 @@ class SearchContext extends React.Component {
metric
:
this
.
defaultMetric
,
usedMetric
:
this
.
defaultMetric
,
domain
:
domains
.
dft
,
query
:
{}
query
:
{},
statisticsToRefresh
:
[]
}
handleRequestChange
(
changes
)
{
...
...
@@ -99,9 +101,15 @@ class SearchContext extends React.Component {
}
}
handleStatisticsToRefreshChange
(
statistics
)
{
let
currentValue
=
this
.
state
.
statisticsToRefresh
currentValue
.
push
(
statistics
)
this
.
setState
({
statisticsToRefresh
:
currentValue
})
}
update
()
{
const
{
api
,
raiseError
}
=
this
.
props
const
{
request
,
query
,
metric
,
domain
}
=
this
.
state
const
{
request
,
query
,
metric
,
domain
,
statisticsToRefresh
}
=
this
.
state
const
search
=
{
...
request
,
...
query
,
...
...
@@ -109,7 +117,7 @@ class SearchContext extends React.Component {
metrics
:
metric
===
this
.
defaultMetric
?
[]
:
[
metric
],
...(
this
.
props
.
query
||
{})}
api
.
search
(
search
)
api
.
search
(
search
,
statisticsToRefresh
)
.
then
(
response
=>
{
// find the first statistic to determine which metric is used
const
{
statistics
}
=
response
...
...
@@ -153,7 +161,8 @@ class SearchContext extends React.Component {
setRequest
:
this
.
handleRequestChange
,
setQuery
:
this
.
handleQueryChange
,
setMetric
:
this
.
handleMetricChange
,
setDomain
:
this
.
handleDomainChange
setDomain
:
this
.
handleDomainChange
,
setStatisticsToRefresh
:
this
.
handleStatisticsToRefreshChange
}
return
<
SearchContext
.
type
.
Provider
value
=
{
value
}
>
{
children
}
...
...
gui/src/components/search/UploadersList.js
0 → 100644
View file @
40f8745e
import
React
from
'
react
'
import
PropTypes
from
'
prop-types
'
import
Grid
from
'
@material-ui/core/Grid
'
import
{
Quantity
}
from
'
./QuantityHistogram
'
import
{
withStyles
}
from
'
@material-ui/core
'
import
SearchContext
from
'
./SearchContext
'
import
{
compose
}
from
'
recompose
'
import
{
withApi
}
from
'
../api
'
class
UploadersList
extends
React
.
Component
{
static
propTypes
=
{
classes
:
PropTypes
.
object
.
isRequired
}
static
styles
=
theme
=>
({
root
:
{
marginTop
:
theme
.
spacing
.
unit
*
2
}
})
static
contextType
=
SearchContext
.
type
componentDidMount
()
{
const
{
state
:
{
query
},
setQuery
,
setStatisticsToRefresh
}
=
this
.
context
setQuery
({...
query
,
statistics_order
:
'
_count
'
})
setStatisticsToRefresh
(
'
uploader
'
)
}
render
()
{
const
{
state
:
{
usedMetric
}}
=
this
.
context
return
(
<
Grid
>
<
Quantity
quantity
=
"
uploader
"
title
=
"
Uploaders
"
scale
=
{
1
}
metric
=
{
usedMetric
}
/
>
<
/Grid
>
)
}
}
export
default
compose
(
withApi
(
false
,
false
),
withStyles
(
UploadersList
.
styles
))(
UploadersList
)
gui/src/components/search/UploadsChart.js
View file @
40f8745e
import
React
from
'
react
'
import
PropTypes
from
'
prop-types
'
import
{
withStyles
,
Select
,
MenuItem
}
from
'
@material-ui/core
'
import
Button
from
'
@material-ui/core/Button
'
import
RefreshIcon
from
'
@material-ui/icons/Refresh
'
import
Grid
from
'
@material-ui/core/Grid
'
import
TextField
from
'
@material-ui/core/TextField
'
import
*
as
d3
from
'
d3
'
...
...
@@ -9,14 +11,12 @@ import { nomadSecondaryColor } from '../../config.js'
import
SearchContext
from
'
./SearchContext
'
import
{
compose
}
from
'
recompose
'
import
{
withApi
}
from
'
../api
'
import
{
Quantity
}
from
'
./QuantityHistogram
'
class
UploadsHistogramUnstyled
extends
React
.
Component
{
static
propTypes
=
{
classes
:
PropTypes
.
object
.
isRequired
,
height
:
PropTypes
.
number
.
isRequired
,
data
:
PropTypes
.
object
,
interval
:
PropTypes
.
string
,
metric
:
PropTypes
.
string
.
isRequired
,
metricsDefinitions
:
PropTypes
.
object
.
isRequired
,
onChanged
:
PropTypes
.
func
.
isRequired
,
...
...
@@ -34,7 +34,6 @@ class UploadsHistogramUnstyled extends React.Component {
super
(
props
)
this
.
state
=
{
scalePower
:
this
.
props
.
defaultScale
||
1.0
,
interval
:
this
.
props
.
interval
||
'
1M
'
,
time
:
null
,
from_time
:
0
,
until_time
:
0
...
...
@@ -65,41 +64,6 @@ class UploadsHistogramUnstyled extends React.Component {
}
]
intervals
=
[
{
label
:
'
Yearly
'
,
value
:
'
1y
'
,
number
:
31536000000
},
{
label
:
'
Monthly
'
,
value
:
'
1M
'
,
number
:
2678400000
},
{
label
:
'
Daily
'
,
value
:
'
1d
'
,
number
:
86400000
},
{
label
:
'
Hourly
'
,
value
:
'
1h
'
,
number
:
3600000
},
{
label
:
'
Minute
'
,
value
:
'
1m
'
,
number
:
60000
},
{
label
:
'
Second
'
,
value
:
'
1s
'
,
number
:
1000
}
]
timeInterval
=
Object
.
assign
({},
...
this
.
intervals
.
map
(
e
=>
({[
e
.
value
]:
e
.
number
})))
componentDidMount
()
{
const
from_time
=
new
Date
(
this
.
startDate
).
getTime
()
const
until_time
=
new
Date
().
getTime
()
...
...
@@ -112,15 +76,9 @@ class UploadsHistogramUnstyled extends React.Component {
}
handleQueryChange
()
{
const
interval
=
this
.
state
.
interval
const
from_time
=
new
Date
(
this
.
state
.
from_time
)
const
until_time
=
new
Date
(
this
.
state
.
until_time
)
this
.
props
.
onChanged
(
from_time
.
toISOString
(),
until_time
.
toISOString
(),
interval
)
}
handleIntervalChange
(
newInterval
)
{
// TODO: add a refresh button so directly updating interval is not necessary
this
.
setState
({
interval
:
newInterval
},
()
=>
this
.
handleQueryChange
())
this
.
props
.
onChanged
(
from_time
.
toISOString
(),
until_time
.
toISOString
())
}
handleTimeChange
(
newTime
,
key
,
target
)
{
...
...
@@ -130,45 +88,54 @@ class UploadsHistogramUnstyled extends React.Component {
}
else
{
date
=
new
Date
(
newTime
)
}
if
(
target
===
'
state
'
||
target
===
'
all
'
)
{
key
===
'
from_time
'
?
this
.
setState
({
from_time
:
date
.
getTime
()})
:
this
.
setState
({
until_time
:
date
.
getTime
()})
if
(
key
===
'
from_time
'
)
{
if
(
this
.
state
.
from_time
!==
date
.
getTime
())
{
this
.
setState
({
from_time
:
date
.
getTime
()})
}
}
else
if
(
key
===
'
until_time
'
)
{
if
(
this
.
state
.
until_time
!==
date
.
getTime
())
{
this
.
setState
({
until_time
:
date
.
getTime
()})
}
}
}
if
(
target
===
'
picker
'
||
target
===
'
all
'
)
{
document
.
getElementById
(
key
).
value
=
date
.
toISOString
().
substring
(
0
,
10
)
}
}
handleItemClicked
(
item
)
{
handleItemClicked
(
item
,
deltaT
)
{
const
selected
=
item
.
time
if
(
selected
===
this
.
state
.
time
)
{
this
.
props
.
onChanged
(
null
,
null
,
null
)
this
.
props
.
onChanged
(
null
,
null
)
}
else
{
const
deltaT
=
this
.
timeInterval
[
this
.
state
.
interval
]
this
.
handleTimeChange
(
selected
,
'
from_time
'
,
'
all
'
)
this
.
handleTimeChange
(
selected
+
deltaT
,
'
until_time
'
,
'
all
'
)
this
.
handleQueryChange
()
}
}
resolveDate
(
name
)
{
resolveDate
(
name
,
deltaT
)
{
const
date
=
new
Date
(
parseInt
(
name
,
10
))
const
year
=
date
.
toLocaleDateString
(
undefined
,
{
year
:
'
numeric
'
})
const
quarter
=
Math
.
floor
((
date
.
getMonth
()
+
3
)
/
3
)
const
month
=
date
.
toLocaleDateString
(
undefined
,
{
month
:
'
short
'
})
const
week
=
(
date
)
=>
{
const
first
=
new
Date
(
date
.
getFullYear
(),
0
,
1
)
return
Math
.
ceil
((((
date
-
first
)
/
86400000
)
+
first
.
getDay
()
+
1
)
/
7
)
}
const
day
=
date
.
toLocaleDateString
(
undefined
,
{
day
:
'
numeric
'
})
const
hour
=
date
.
toLocaleTimeString
(
undefined
,
{
hour
:
'
numeric
'
})
const
min
=
date
.
toLocaleTimeString
(
undefined
,
{
minute
:
'
numeric
'
})
const
sec
=
date
.
toLocaleTimeString
(
undefined
,
{
second
:
'
numeric
'
})
const
intervals
=
{
'
1y
'
:
year
,
'
1M
'
:
month
,
'
1d
'
:
day
,
'
1h
'
:
hour
,
'
1m
'
:
min
,
'
1s
'
:
sec
}
const
times
=
[
31536000
,
7776000
,
2419200
,
604800
,
864000
,
3600
,
60
,
1
]
const
diffs
=
times
.
map
(
t
=>
Math
.
abs
(
t
-
(
deltaT
/
1000
)))
return
intervals
[
this
.
state
.
interval
]
const
intervals
=
[
year
,
'
Q
'
+
quarter
,
month
,
'
W
'
+
week
,
day
,
hour
,
min
,
sec
]
return
intervals
[
diffs
.
indexOf
(
Math
.
min
(...
diffs
))]
}
hover
(
svg
,
bar
)
{
...
...
@@ -215,7 +182,7 @@ class UploadsHistogramUnstyled extends React.Component {
}
else
{
data
=
Object
.
keys
(
this
.
props
.
data
).
map
(
key
=>
({
time
:
parseInt
(
key
,
10
),
name
:
this
.
resolveDate
(
key
),
//
name: this.resolveDate(key),
value
:
this
.
props
.
data
[
key
][
this
.
props
.
metric
]
}))
}
...
...
@@ -226,10 +193,17 @@ class UploadsHistogramUnstyled extends React.Component {
this
.
handleTimeChange
(
this
.
state
.
until_time
,
'
until_time
'
,
'
picker
'
)
}
let
deltaT
=
31536000000
if
(
data
.
length
>
1
)
{
deltaT
=
data
[
1
].
time
-
data
[
0
].
time
}
data
.
forEach
(
d
=>
{
d
.
name
=
this
.
resolveDate
(
d
.
time
,
deltaT
)
})
const
scalePower
=
this
.
state
.
scalePower
const
width
=
this
.
container
.
current
.
offsetWidth
const
height
=
this
.
props
.
height
const
margin
=
Math
.
round
(
0.1
*
height
)
const
margin
=
Math
.
round
(
0.1
5
*
height
)
const
x
=
scaleBand
().
rangeRound
([
margin
,
width
]).
padding
(
0.1
)
const
y
=
scalePow
().
range
([
height
-
margin
,
margin
]).
exponent
(
scalePower
)
...
...
@@ -258,7 +232,7 @@ class UploadsHistogramUnstyled extends React.Component {
.
attr
(
'
font-size
'
,
'
12px
'
)
.
style
(
'
text-anchor
'
,
'
end
'
)
const
yAxis
=
d3
.
axisLeft
(
y
)
const
yAxis
=
d3
.
axisLeft
(
y
)
.
ticks
(
Math
.
min
(
max
,
5
),
'
.0s
'
)
svg
.
select
(
'
.yaxis
'
).
remove
()
svg
.
append
(
'
g
'
)
.
attr
(
'
transform
'
,
`translate(
${
margin
}
, 0)`
)
...
...
@@ -294,16 +268,17 @@ class UploadsHistogramUnstyled extends React.Component {
item
.
style
(
'
cursor
'
,
'
pointer
'
)
.
on
(
'
click
'
,
d
=>
this
.
handleItemClicked
(
d
))
.
on
(
'
click
'
,
d
=>
this
.
handleItemClicked
(
d
,
deltaT
))
svg
.
select
(
'
.tooltip
'
).
remove
()
svg
.
call
(
this
.
hover
,
item
)
}
render
()
{
return
(
<
div
>
<
Grid
container
justify
=
'
space-
around
'
spacing
=
{
24
}
>
<
Grid
container
justify
=
'
space-
between
'
alignItems
=
'
flex-end
'
>
<
Grid
item
xs
=
{
2
}
>
<
Select
margin
=
'
none
'
...
...
@@ -318,7 +293,7 @@ class UploadsHistogramUnstyled extends React.Component {
<
/MenuItem>
))
}
<
/Select
>
<
/Grid
>
<
Grid
item
xs
=
{
3
}
>
<
Grid
item
xs
=
{
2
}
>
<
TextField
id
=
'
from_time
'
label
=
"
from time
"
...
...
@@ -329,19 +304,7 @@ class UploadsHistogramUnstyled extends React.Component {
}}
/
>
<
/Grid
>
<
Grid
item
xs
=
{
3
}
>
<
Select
id
=
'
interval
'
value
=
{
this
.
state
.
interval
}
onChange
=
{(
event
)
=>
this
.
handleIntervalChange
(
event
.
target
.
value
)}
label
=
'
interval
'
>
{
this
.
intervals
.
map
(
item
=>
(
<
MenuItem
value
=
{
item
.
value
}
key
=
{
item
.
value
}
>
{
item
.
label
}
<
/MenuItem>
))
}
<
/Select
>
<
/Grid
>
<
Grid
item
xs
=
{
3
}
>
<
Grid
item
xs
=
{
2
}
>
<
TextField
id
=
'
until_time
'
label
=
"
until time
"
...
...
@@ -352,6 +315,14 @@ class UploadsHistogramUnstyled extends React.Component {
}}
/
>
<
/Grid
>
<
Grid
item
xs
=
{
2
}
>
<
Button
variant
=
'
outlined
'
color
=
'
default
'
onClick
=
{()
=>
this
.
handleQueryChange
()}
>
Refresh
<
RefreshIcon
/>
<
/Button
>
<
/Grid
>
<
/Grid
>
<
div
ref
=
{
this
.
container
}
>
<
svg
ref
=
{
this
.
svgEl
}
><
/svg
>
...
...
@@ -363,32 +334,6 @@ class UploadsHistogramUnstyled extends React.Component {
export
const
UploadsHistogram
=
withStyles
(
UploadsHistogramUnstyled
.
styles
)(
UploadsHistogramUnstyled
)
class
UploadersListUnstyled
extends
React
.
Component
{
static
propTypes
=
{
classes
:
PropTypes
.
object
.
isRequired
}
static
styles
=
theme
=>
({
root
:
{
marginTop
:
theme
.
spacing
.
unit
*
2
}
})
static
contextType
=
SearchContext
.
type
render
()
{
const
{
state
:
{
usedMetric
}}
=
this
.
context
return
(
<
Grid
>
<
Quantity
quantity
=
"
uploader
"
title
=
"
Top Uploaders
"
scale
=
{
1
}
metric
=
{
usedMetric
}
/
>
<
/Grid
>
)
}
}
export
const
UploadersList
=
withStyles
(
UploadersListUnstyled
.
styles
)(
UploadersListUnstyled
)
class
UploadsChart
extends
React
.
Component
{
static
propTypes
=
{
classes
:
PropTypes
.
object
.
isRequired
,
...
...
@@ -402,6 +347,11 @@ class UploadsChart extends React.Component {
static
contextType
=
SearchContext
.
type
componentDidMount
()
{
const
{
setStatisticsToRefresh
}
=
this
.
context
setStatisticsToRefresh
(
'
date_histogram
'
)
}
render
()
{
const
{
classes
,
metricsDefinitions
,
...
props
}
=
this
.
props
const
{
state
:
{
response
,
usedMetric
,
query
},
setQuery
}
=
this
.
context
...
...
@@ -416,13 +366,9 @@ class UploadsChart extends React.Component {
data
=
{
response
.
statistics
.
date_histogram
}
metric
=
{
usedMetric
}
metricsDefinitions
=
{
metricsDefinitions
}
interval
=
{
'
1M
'
}
onChanged
=
{(
from_time
,
until_time
,
interval
)
=>
setQuery
({...
query
,
from_time
:
from_time
,
until_time
:
until_time
,
interval
:
interval
})}
onChanged
=
{(
from_time
,
until_time
)
=>
setQuery
({...
query
,
from_time
:
from_time
,
until_time
:
until_time
})}
{...
props
}
/
>
<
/Grid
>
<
Grid
item
xs
=
{
12
}
>
<
UploadersList
/>
<
/Grid
>
<
/Grid
>
)
}
...
...
nomad/app/api/repo.py
View file @
40f8745e
...
...
@@ -29,7 +29,7 @@ from nomad import search, utils, datamodel, processing as proc, infrastructure,
from
nomad.metainfo
import
search_extension
from
nomad.datamodel
import
Dataset
,
User
,
EditableUserMetadata
from
nomad.app
import
common
from
nomad.app.common
import
RFC3339DateTime
,
DotKeyNested
from
nomad.app.common
import
RFC3339DateTime
,
DotKeyNested
,
rfc3339DateTime
from
.api
import
api
from
.auth
import
authenticate
...
...
@@ -73,6 +73,29 @@ class RepoCalcResource(Resource):
return
result
,
200
def
resolve_interval
(
from_time
,
until_time
):
if
from_time
is
None
:
from_time
=
datetime
.
fromtimestamp
(
0
)
if
until_time
is
None
:
until_time
=
datetime
.
utcnow
()
dt
=
rfc3339DateTime
.
parse
(
until_time
)
-
rfc3339DateTime
.
parse
(
from_time
)
if
dt
.
days
>=
1826
:
return
'1y'
elif
dt
.
days
>=
731
:
return
'1q'
elif
dt
.
days
>=
121
:
return
'1M'
elif
dt
.
days
>=
28
:
return
'1w'
elif
dt
.
days
>=
4
:
return
'1d'
elif
dt
.
total_seconds
()
>=
14400
:
return
'1h'
else
:
return
'1m'
_search_request_parser
=
api
.
parser
()
add_pagination_parameters
(
_search_request_parser
)
add_scroll_parameters
(
_search_request_parser
)
...
...
@@ -87,6 +110,8 @@ _search_request_parser.add_argument(
'Possible values are %s.'
%
', '
.
join
(
search_extension
.
metrics
.
keys
())))
_search_request_parser
.
add_argument
(
'statistics'
,
type
=
bool
,
help
=
(
'Return statistics.'
))
_search_request_parser
.
add_argument
(
'statistics_order'
,
type
=
str
,
help
=
'Statistics order (can be _key or _count)'
)
_search_request_parser
.
add_argument
(
'exclude'
,
type
=
str
,
action
=
'split'
,
help
=
'Excludes the given keys in the returned data.'
)
for
group_name
in
search_extension
.
groups
:
...
...
@@ -170,17 +195,25 @@ class RepoCalcsResource(Resource):
order_by
=
args
.
get
(
'order_by'
,
'upload_time'
)
date_histogram
=
args
.
get
(
'date_histogram'
,
False
)
interval
=
args
.
get
(
'interval'
,
'
1M
'
)