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
beat
beat.core
Commits
64d18bf4
Commit
64d18bf4
authored
Mar 22, 2019
by
Jaden
Committed by
Samuel GAIST
Apr 18, 2019
Browse files
[plotterparameter] add plotterparameter type
Adds schema file, beat.core integration, test files & tests Closes
#66
parent
b263c4d8
Pipeline
#29414
passed with stage
in 17 minutes and 59 seconds
Changes
7
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
beat/core/plotterparameter.py
0 → 100644
View file @
64d18bf4
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
"""
================
plotterparameter
================
Validation for plotterparameters
"""
import
os
from
.
import
dataformat
from
.
import
schema
from
.
import
prototypes
from
.
import
utils
from
.
import
loader
from
.
import
plotter
class
Storage
(
utils
.
Storage
):
"""Resolves paths for plotterparameters
Parameters:
prefix (str): Establishes the prefix of your installation.
name (str): The name of the plotterparameter object in the format
``<user>/<plotterparameter-name>/<version>``
"""
def
__init__
(
self
,
prefix
,
name
):
if
name
.
count
(
"/"
)
!=
2
:
raise
RuntimeError
(
f
"invalid plotterparameter name:
{
name
}
"
)
self
.
username
,
self
.
name
,
self
.
version
=
name
.
split
(
"/"
)
self
.
fullname
=
name
self
.
prefix
=
prefix
path
=
utils
.
hashed_or_simple
(
self
.
prefix
,
"plotterparameters"
,
name
,
suffix
=
".json"
)
path
=
path
[:
-
5
]
super
(
Storage
,
self
).
__init__
(
path
)
# ----------------------------------------------------------
class
Plotterparameter
(
object
):
"""Each plotterparameter is a specific configuration for the specified
plotter. Plotterparameters configure all the parameters of the plotter,
much like an experiment contains configurations for the
algorithms'/databases' parameters.
Parameters:
prefix (str): Establishes the prefix of your installation.
data (:py:class:`object`, Optional): The piece of data representing the
plotterparameter. It must validate against the schema defined for
plotterparameters. If a string is passed, it is supposed to be a valid
path to a plotterparameter in the designated prefix area.
plotter_cache (:py:class:`dict`, Optional): A dictionary mapping
plotter names to loaded plotters. This parameter is optional and,
if passed, may greatly speed-up algorithm loading times as plotters
that are already loaded may be re-used.
Attributes:
name (str): The plotterparameter name
description (str): The short description string, loaded from the JSON
file if one was set.
documentation (str): The full-length docstring for this object.
storage (object): A simple object that provides information about file
paths for this plotterparameter
plotter (object): An object of type :py:class:`.plotter.Plotter`
that represents the plotter to which this plotterparameter is applicable.
errors (list): A list strings containing errors found while loading this
plotterparameter.
data (dict): The original data for this plotterparameter, as loaded by our
JSON decoder.
"""
def
__init__
(
self
,
prefix
,
data
,
plotter_cache
=
None
,
):
self
.
_name
=
None
self
.
storage
=
None
self
.
errors
=
[]
self
.
data
=
None
self
.
plotter
=
None
self
.
prefix
=
prefix
plotter_cache
=
plotter_cache
if
plotter_cache
is
not
None
else
{}
self
.
_load
(
data
,
plotter_cache
)
def
_load
(
self
,
data
,
plotter_cache
):
"""Loads the plotterparameter"""
self
.
_load_data
(
data
)
if
self
.
errors
:
return
# don't proceed with the rest of validation
self
.
_load_plotter
(
plotter_cache
)
if
self
.
errors
:
return
# don't proceed with the rest of validation
self
.
_validate_data
()
def
_load_data
(
self
,
data
):
"""Loads given plotterparameter data
and the plotterparameter's name
Parameters:
data (str): a string (the name of the param),
an object (the param data),
or a tuple/list (the param data & the plotter data)
"""
# first load the raw plotterparameter data, if data isnt None
if
isinstance
(
data
,
(
tuple
,
list
)):
# the user has passed a tuple
data
,
self
.
plotter
=
data
elif
isinstance
(
data
,
str
):
# user has passed the name
self
.
_name
=
data
self
.
storage
=
Storage
(
self
.
prefix
,
self
.
_name
)
if
not
self
.
storage
.
json
.
exists
():
self
.
errors
.
append
(
f
'Plotterparameter declaration file not found:
{
data
}
'
)
return
data
=
self
.
storage
.
json
.
path
# loads data from JSON declaration
# At this point, `data' can be a dictionary or ``None``
# Either way, assign something valid to `self.data'
if
data
is
None
:
# use the dummy plotterparameter
self
.
data
,
self
.
errors
=
prototypes
.
load
(
"plotterparameter"
)
assert
not
self
.
errors
,
"
\n
* %s"
%
"
\n
*"
.
join
(
self
.
errors
)
else
:
# this runs basic validation, including JSON loading if required
self
.
data
,
self
.
errors
=
schema
.
validate
(
"plotterparameter"
,
data
)
def
_load_plotter
(
self
,
plotter_cache
):
"""Loads the plotter for the plotterparameter.
Assumes that `self.data' has been calculated.
Parameters:
plotter_cache (:py:class:`dict`): a dict mapping plotter names
to already-loaded plotter objects
"""
# find the plotter if it wasnt given
if
self
.
plotter
is
None
:
plotter_name
=
self
.
data
[
'plotter'
]
pl
=
None
if
plotter_name
in
plotter_cache
:
pl
=
plotter_cache
[
plotter_name
]
else
:
pl
=
plotter
.
Plotter
(
self
.
prefix
,
plotter_name
)
if
pl
.
errors
:
self
.
errors
.
extend
(
pl
.
errors
)
return
plotter_cache
[
plotter_name
]
=
pl
self
.
plotter
=
pl
def
_validate_data
(
self
):
"""Validates that the properties in the plotterparameter's
data properly configure the plotter's fields
"""
for
key
,
val
in
self
.
data
[
'data'
].
items
():
try
:
self
.
plotter
.
clean_parameter
(
key
,
val
)
except
KeyError
:
self
.
errors
.
append
(
f
"'
{
key
}
' isn't a parameter for plotter
{
self
.
plotter
.
name
}
"
)
return
except
ValueError
:
self
.
errors
.
append
(
f
"'
{
value
}
' is invalid for parameter
{
key
}
of plotter
{
self
.
plotter
.
name
}
"
)
return
@
property
def
valid
(
self
):
"""A boolean that indicates if this plotterparameter is valid or not"""
return
not
bool
(
self
.
errors
)
@
property
def
name
(
self
):
"""Returns the name of this object"""
return
self
.
_name
or
"__unnamed_plotterparameter__"
@
name
.
setter
def
name
(
self
,
value
):
self
.
_name
=
value
self
.
storage
=
Storage
(
self
.
prefix
,
value
)
@
property
def
documentation
(
self
):
"""The full-length description for this object"""
if
not
self
.
_name
:
raise
RuntimeError
(
"plotterparameter has no name"
)
if
self
.
storage
.
doc
.
exists
():
return
self
.
storage
.
doc
.
load
()
return
None
@
documentation
.
setter
def
documentation
(
self
,
value
):
"""Sets the full-length description for this object"""
if
not
self
.
_name
:
raise
RuntimeError
(
"plotterparameter has no name"
)
if
callable
(
getattr
(
value
,
"read"
,
None
)):
self
.
storage
.
doc
.
save
(
value
.
read
())
else
:
self
.
storage
.
doc
.
save
(
value
)
def
hash
(
self
):
"""Returns the hexadecimal hash for the current plotterparameter"""
if
not
self
.
_name
:
raise
RuntimeError
(
"plotterparameter has no name"
)
return
self
.
storage
.
hash
()
def
write
(
self
,
storage
=
None
):
"""Writes contents to prefix location
Parameters:
storage (:py:class:`.Storage`, Optional): If you pass a new storage,
then this object will be written to that storage point rather than
its default.
"""
if
storage
is
None
:
if
not
self
.
_name
:
raise
RuntimeError
(
"plotterparameter has no name"
)
storage
=
self
.
storage
# overwrite
storage
.
save
(
str
(
self
),
self
.
code
,
self
.
description
)
beat/core/prototypes/plotterparameter.json
0 → 100644
View file @
64d18bf4
{
"plotter"
:
"plot/unknown/1"
,
"description"
:
""
,
"data"
:
{}
}
beat/core/schema/plotterparameter/1.json
0 → 100644
View file @
64d18bf4
{
"$schema"
:
"http://json-schema.org/draft-04/schema#"
,
"title"
:
"Plotter configurator descriptor"
,
"description"
:
"This schema defines the properties of a plotter configuration file"
,
"type"
:
"object"
,
"properties"
:
{
"description"
:
{
"$ref"
:
"../common/1.json#/definitions/description"
},
"plotter"
:
{
"$ref"
:
"../common/1.json#/definitions/reference"
},
"data"
:
{
"$ref"
:
"../experiment/common.json#/definitions/parameter_set"
},
"schema_version"
:
{
"$ref"
:
"../common/1.json#/definitions/version"
}
},
"required"
:
[
"plotter"
,
"data"
],
"additionalProperties"
:
false
}
beat/core/test/prefix/plotterparameters/plot/config/1.json
0 → 100644
View file @
64d18bf4
{
"plotter"
:
"user/scatter/1"
,
"description"
:
"A plotterparameter for the built-in scatter plotter"
,
"data"
:
{
"grid"
:
true
,
"legend"
:
"test data"
,
"mimetype"
:
"image/jpeg"
,
"yaxis_multiplier"
:
2.5
}
}
beat/core/test/prefix/plotterparameters/plot/invalid/1.json
0 → 100644
View file @
64d18bf4
{
"plotter"
:
"user/scatter/1"
,
"description"
:
"An invalid plotterparameter for the scatter plotter"
,
"data"
:
{
"not_an_option"
:
2.5
}
}
beat/core/test/prefix/plotterparameters/plot/invalid/2.json
0 → 100644
View file @
64d18bf4
{
"plotter"
:
"user/not_a_plotter/1"
,
"description"
:
"A plotterparameter for a non-existant plotter"
,
"data"
:
{
}
}
beat/core/test/test_plotterparameter.py
0 → 100644
View file @
64d18bf4
#!/usr/bin/env python
# vim: set fileencoding=utf-8 :
###################################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# Redistribution and use in source and binary forms, with or without #
# modification, are permitted provided that the following conditions are met: #
# #
# 1. Redistributions of source code must retain the above copyright notice, this #
# list of conditions and the following disclaimer. #
# #
# 2. Redistributions in binary form must reproduce the above copyright notice, #
# this list of conditions and the following disclaimer in the documentation #
# and/or other materials provided with the distribution. #
# #
# 3. Neither the name of the copyright holder nor the names of its contributors #
# may be used to endorse or promote products derived from this software without #
# specific prior written permission. #
# #
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED #
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE #
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE #
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL #
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR #
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, #
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE #
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #
# #
###################################################################################
import
nose.tools
from
.
import
prefix
from
..plotterparameter
import
Plotterparameter
def
test_default
():
# test for the "dummy" plotterparameter
p
=
Plotterparameter
(
prefix
,
data
=
None
)
nose
.
tools
.
assert_false
(
p
.
valid
)
def
test_plot_config_1
():
# test for a simple plotterparameter for a simple plotter
p
=
Plotterparameter
(
prefix
,
"plot/config/1"
)
nose
.
tools
.
assert_true
(
p
.
valid
,
"
\n
* %s"
%
"
\n
* "
.
join
(
p
.
errors
))
def
test_plot_invalid_1
():
# test for invalid parameter name
p
=
Plotterparameter
(
prefix
,
"plot/invalid/1"
)
nose
.
tools
.
assert_false
(
p
.
valid
)
nose
.
tools
.
assert_true
(
p
.
errors
[
0
]
==
"'not_an_option' isn't a parameter for plotter user/scatter/1"
)
def
test_plot_invalid_2
():
# test for invalid "plotter" field
p
=
Plotterparameter
(
prefix
,
"plot/invalid/2"
)
nose
.
tools
.
assert_false
(
p
.
valid
)
nose
.
tools
.
assert_true
(
p
.
errors
[
0
]
==
"Plotter declaration file not found: user/not_a_plotter/1"
)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment