##################################################################
# Copyright 2018 Open Source Geospatial Foundation and others #
# licensed under MIT, Please consult LICENSE.txt for details #
##################################################################
"""Literaltypes are used for LiteralInputs, to make sure, input data are OK
"""
from pywps._compat import urlparse
from dateutil.parser import parse as date_parser
import datetime
from pywps.exceptions import InvalidParameterValue
from pywps.validator.allowed_value import RANGECLOSURETYPE
from pywps.validator.allowed_value import ALLOWEDVALUETYPE
from pywps._compat import PY2
import logging
LOGGER = logging.getLogger('PYWPS')
LITERAL_DATA_TYPES = ('float', 'boolean', 'integer', 'string',
'positiveInteger', 'anyURI', 'time', 'date', 'dateTime',
'scale', 'angle',
'nonNegativeInteger')
# currently we are supporting just ^^^ data types, feel free to add support for
# more
# 'measure', 'angleList',
# 'angle', 'integerList',
# 'positiveIntegerList',
# 'lengthOrAngle', 'gridLength',
# 'measureList', 'lengthList',
# 'gridLengthList', 'scaleList', 'timeList',
# 'nonNegativeInteger', 'length'
[docs]class AnyValue(object):
"""Specifies that any value is allowed for this quantity.
"""
@property
def value(self):
return None
@property
def json(self):
return {
'type': 'anyvalue',
}
def __eq__(self, other):
return isinstance(other, AnyValue) and self.json == other.json
class NoValue(object):
"""No value allowed
NOTE: not really implemented
"""
@property
def value(self):
return None
@property
def json(self):
return {'type': 'novalue'}
def __eq__(self, other):
return isinstance(other, NoValue) and self.json == other.json
[docs]class ValuesReference(object):
"""Reference to list of all valid values and/or ranges of values for this quantity.
NOTE: Validation of values is not implemented.
:param: reference: URL from which this set of ranges and values can be retrieved
:param: values_form: Reference to a description of the mimetype, encoding,
and schema used for this set of values and ranges.
"""
def __init__(self, reference=None, values_form=None):
self.reference = reference
self.values_form = values_form
if not self.reference:
raise InvalidParameterValue("values reference is missing.")
@property
def value(self):
return None
@property
def json(self):
return {
'type': 'valuesreference',
'reference': self.reference,
'values_form': self.values_form
}
@classmethod
def from_json(cls, json_input):
instance = cls(
reference=json_input['reference'],
values_form=json_input['values_form'],
)
return instance
def __eq__(self, other):
return isinstance(other, ValuesReference) and self.json == other.json
[docs]class AllowedValue(object):
"""List of all valid values and/or ranges of values for this quantity.
The values are evaluated in literal validator functions
:param pywps.validator.allowed_value.ALLOWEDVALUETYPE allowed_type: VALUE or RANGE
:param value: single value
:param minval: minimal value in case of Range
:param maxval: maximal value in case of Range
:param spacing: spacing in case of Range
:param pywps.input.literaltypes.RANGECLOSURETYPE range_closure:
"""
def __init__(self, allowed_type=None, value=None,
minval=None, maxval=None, spacing=None,
range_closure=RANGECLOSURETYPE.CLOSED):
self.allowed_type = allowed_type
self.value = value
self.minval = minval
self.maxval = maxval
self.spacing = spacing
self.range_closure = range_closure
if not self.allowed_type:
# automatically set allowed_type: RANGE or VALUE
if self.minval or self.maxval or self.spacing:
self.allowed_type = ALLOWEDVALUETYPE.RANGE
else:
self.allowed_type = ALLOWEDVALUETYPE.VALUE
def __eq__(self, other):
return isinstance(other, AllowedValue) and self.json == other.json
@property
def json(self):
value = self.value
if hasattr(value, 'json'):
value = value.json
return {
'type': 'allowedvalue',
'allowed_type': self.allowed_type,
'value': value,
'minval': self.minval,
'maxval': self.maxval,
'spacing': self.spacing,
'range_closure': self.range_closure
}
@classmethod
def from_json(cls, json_input):
instance = cls(
allowed_type=json_input['allowed_type'],
value=json_input['value'],
minval=json_input['minval'],
maxval=json_input['maxval'],
spacing=json_input['spacing'],
range_closure=json_input['range_closure']
)
return instance
ALLOWED_VALUES_TYPES = (AllowedValue, AnyValue, NoValue, ValuesReference)
def get_converter(convertor):
"""function for decoration of convert
"""
def decorator_selector(data_type, data):
convert = None
if data_type in LITERAL_DATA_TYPES:
if data_type == 'string':
convert = convert_string
elif data_type == 'integer':
convert = convert_integer
elif data_type == 'float':
convert = convert_float
elif data_type == 'boolean':
convert = convert_boolean
elif data_type == 'positiveInteger':
convert = convert_positiveInteger
elif data_type == 'anyURI':
convert = convert_anyURI
elif data_type == 'time':
convert = convert_time
elif data_type == 'date':
convert = convert_date
elif data_type == 'dateTime':
convert = convert_datetime
elif data_type == 'scale':
convert = convert_scale
elif data_type == 'angle':
convert = convert_angle
elif data_type == 'nonNegativeInteger':
convert = convert_positiveInteger
else:
raise InvalidParameterValue(
"Invalid data_type value of LiteralInput "
"set to '{}'".format(data_type))
try:
return convert(data)
except ValueError:
raise InvalidParameterValue(
"Could not convert value '{}' to format '{}'".format(
data, data_type))
return decorator_selector
@get_converter
def convert(data_type, data):
"""Convert data to target value
"""
return data_type, data
def convert_boolean(inpt):
"""Return boolean value from input boolean input
>>> convert_boolean('1')
True
>>> convert_boolean('-1')
True
>>> convert_boolean('FaLsE')
False
>>> convert_boolean('FaLsEx')
True
>>> convert_boolean(0)
False
"""
val = False
if str(inpt).lower() in ['false', 'f']:
val = False
else:
try:
val = int(inpt)
if val == 0:
val = False
else:
val = True
except Exception:
val = True
return val
def convert_float(inpt):
"""Return float value from inpt
>>> convert_float('1')
1.0
"""
return float(inpt)
def convert_integer(inpt):
"""Return integer value from input inpt
>>> convert_integer('1.0')
1
"""
return int(float(inpt))
def convert_string(inpt):
"""Return string value from input lit_input
>>> convert_string(1)
'1'
"""
if PY2:
return str(inpt).decode()
else:
return str(inpt)
def convert_positiveInteger(inpt):
"""Return value of input"""
inpt = convert_integer(inpt)
if inpt < 0:
raise InvalidParameterValue(
'The value "{}" is not of type positiveInteger'.format(inpt))
else:
return inpt
def convert_anyURI(inpt):
"""Return value of input
:rtype: url components
"""
inpt = convert_string(inpt)
components = urlparse(inpt)
if (components[0] and components[1]) or components[0] == 'file':
return components
else:
raise InvalidParameterValue(
'The value "{}" does not seem to be of type anyURI'.format(inpt))
def convert_time(inpt):
"""Return value of input
time formating assumed according to ISO standard:
https://www.w3.org/TR/xmlschema-2/#time
Examples: 12:00:00
:rtype: datetime.time object
"""
if not isinstance(inpt, datetime.time):
inpt = convert_datetime(inpt).time()
return inpt
def convert_date(inpt):
"""Return value of input
date formating assumed according to ISO standard:
https://www.w3.org/TR/xmlschema-2/#date
Examples: 2016-09-20
:rtype: datetime.date object
"""
if not isinstance(inpt, datetime.date):
inpt = convert_datetime(inpt).date()
return inpt
def convert_datetime(inpt):
"""Return value of input
dateTime formating assumed according to ISO standard:
* http://www.w3.org/TR/NOTE-datetime
* https://www.w3.org/TR/xmlschema-2/#dateTime
Examples: 2016-09-20T12:00:00, 2012-12-31T06:30:00Z,
2017-01-01T18:00:00+01:00
:rtype: datetime.datetime object
"""
# TODO: %z directive works only with python 3
# time_format = '%Y-%m-%dT%H:%M:%S%z'
# time_format = '%Y-%m-%dT%H:%M:%S%Z'
# inpt = time.strptime(convert_string(inpt), time_format)
if not isinstance(inpt, datetime.datetime):
inpt = convert_string(inpt)
inpt = date_parser(inpt)
return inpt
def convert_scale(inpt):
"""Return value of input"""
return convert_float(inpt)
def convert_angle(inpt):
"""Return value of input
return degrees
"""
inpt = convert_float(inpt)
return inpt % 360
def make_allowedvalues(allowed_values):
"""convert given value list to AllowedValue objects
:return: list of pywps.inout.literaltypes.AllowedValue
"""
new_allowedvalues = []
if not isinstance(allowed_values, (tuple, list)):
allowed_values = [allowed_values]
for value in allowed_values:
if value in ALLOWED_VALUES_TYPES:
# value is equal to one of the allowed classes objects
new_allowedvalues.append(value())
elif isinstance(value, ALLOWED_VALUES_TYPES):
# value is an instance of one of the allowed classes
new_allowedvalues.append(value)
elif type(value) == tuple or type(value) == list:
spacing = None
if len(value) == 2:
minval = value[0]
maxval = value[1]
else:
minval = value[0]
spacing = value[1]
maxval = value[2]
new_allowedvalues.append(
AllowedValue(minval=minval, maxval=maxval,
spacing=spacing)
)
else:
new_allowedvalues.append(AllowedValue(value=value))
return new_allowedvalues
def is_anyvalue(value):
"""Check for any value object of given value
"""
is_av = False
if value is AnyValue:
is_av = True
elif value is None:
is_av = True
elif isinstance(value, AnyValue):
is_av = True
elif str(value).lower() == 'anyvalue':
is_av = True
return is_av
def is_values_reference(value):
"""Check for ValuesReference in given value
"""
check = False
if value is ValuesReference:
check = True
elif value is None:
check = False
elif isinstance(value, ValuesReference):
check = True
elif str(value).lower() == 'valuesreference':
check = True
return check