python.observation.esvalue_base

Created on Mon Aug 2 14:51:23 2021

@author: philippe@loco-labs.io

The python.observation.esvalue_base is a module dedicated to structured data (such as dates, location or measurable properties) and groups common properties and concepts.

ESValue is build around two attributes :

  • 'name' which is a simple String
  • 'value' which corresponds to a more or less complex object :

    • DatationValue : value is a TimeSlot Object which represent a set of time intervals
    • LocationValue : value is a Shapely Geometry which represent a set of polygons
    • PropertyValue : value is a simple dictionary which specifies all the characteristics of a property
    • NamedValue : value can be any simple object
    • ExternValue : value can be any other object

This module groups the classes of the objects used in the python.observation.esobservation module :

and the parent class :

Documentation is available in other pages :

  • The concepts of 'ES value' are describe in this page.
  • The non-regression tests are at this page
  • Examples are here
  • The Json Standard for ESValue is define here
  1# -*- coding: utf-8 -*-
  2"""
  3Created on Mon Aug  2 14:51:23 2021
  4
  5@author: philippe@loco-labs.io
  6
  7The `python.observation.esvalue_base` is a module dedicated to structured data (such as dates,
  8location or measurable properties) and groups common properties and concepts.
  9
 10ESValue is build around two attributes :
 11
 12- 'name' which is a simple String
 13- 'value' which corresponds to a more or less complex object :
 14
 15    - DatationValue : value is a TimeSlot Object which represent a set of time intervals
 16    - LocationValue : value is a Shapely Geometry which represent a set of polygons
 17    - PropertyValue : value is a simple dictionary which specifies all the characteristics of a property
 18    - NamedValue    : value can be any simple object
 19    - ExternValue   : value can be any other object
 20
 21<img src="https://loco-philippe.github.io/ES/ESValue_class.png" width="800">
 22
 23This module groups the classes of the objects used in the `python.observation.esobservation` module :
 24
 25- `python.observation.esvalue.DatationValue`,
 26- `python.observation.esvalue.LocationValue`,
 27- `python.observation.esvalue.PropertyValue`,
 28- `python.observation.esvalue.NamedValue`
 29- `python.observation.esvalue.ExternValue`
 30
 31and the parent class :
 32
 33- `ESValue`
 34
 35Documentation is available in other pages :
 36    
 37- The concepts of 'ES value' are describe in 
 38[this page](https://github.com/loco-philippe/Environmental-Sensing/wiki/ESValue).
 39- The non-regression tests are at 
 40[this page](https://github.com/loco-philippe/Environmental-Sensing/blob/main/python/Tests/test_esvalue.py)
 41- Examples are 
 42[here](https://github.com/loco-philippe/Environmental-Sensing/tree/main/python/Examples)
 43- The Json Standard for ESValue is define 
 44[here](https://github.com/loco-philippe/Environmental-Sensing/tree/main/documentation/ESJSON-Standard.pdf)
 45
 46
 47
 48"""
 49import json
 50import re
 51import datetime
 52from json import JSONDecodeError
 53import cbor2
 54from copy import copy
 55
 56from observation.esconstante import ES, _classval
 57from observation.timeslot import TimeInterval
 58
 59ListESValue = ['LocationValue', 'DatationValue',
 60               'PropertyValue', 'NamedValue', 'ExternValue']
 61ListESValueSlot = ListESValue + ['TimeSlot']
 62
 63
 64class ESValueEncoder(json.JSONEncoder):
 65    """add a new json encoder for ESValue"""
 66
 67    def default(self, o):
 68        if isinstance(o, datetime.datetime):
 69            return o.isoformat()
 70        option = {'encoded': False, 'encode_format': 'json'}
 71        try:
 72            return o.json(**option)
 73        except:
 74            try:
 75                return o.__to_json__()
 76            except:
 77                return json.JSONEncoder.default(self, o)
 78
 79
 80class ESValue:
 81    """
 82    This class is the parent class for each kind of values
 83     (`DatationValue`, `LocationValue`, `PropertyValue`, `NamedValue`, `ExternValue`)
 84
 85    *Attributes* :
 86
 87    - **name**  : name  of `ESValue.ESValue` objects
 88    - **value** : value of `ESValue.ESValue` objects
 89    - `ESValue.bounds` (@property) : boundary  of `ESValue.ESValue` objects
 90    - `ESValue.simple` (@property) : simplified value of `ESValue.ESValue` objects
 91
 92    The methods defined in this class are :
 93
 94    **binary predicates**
 95
 96    - `ESValue.contains`
 97    - `ESValue.equals`
 98    - `ESValue.intersects`
 99    - `ESValue.within`
100    - `ESValue.disjoint`
101    - `ESValue.isEqual`
102    - `ESValue.isNotNull`
103    - `ESValue.isName`
104
105    **other methods**
106
107    - `ESValue.boundingBox` (@classmethod)
108    - `ESValue.from_obj` (@classmethod)
109    - `ESValue.boxUnion`
110    - `ESValue.getValue`
111    - `ESValue.getName`
112    - `ESValue.json`
113    - `ESValue.setName`
114    - `ESValue.setValue`
115    - `ESValue.simple`
116    - `ESValue.to_float`
117    - `ESValue.to_obj`
118    - `ESValue.valClassName`
119    - `ESValue.vName`
120    - `ESValue.vSimple`
121    """
122# %% constructor
123    """@staticmethod
124    # def from_obj(bs, classname=ES.nam_clsName):
125    def from_obj(bs, classname=None, simple=True):
126        '''Generate an ESValue Object from a bytes, json or dict object
127        Several configurations for bs parameters (name and type are string) :
128            - {name : value}
129            - name
130            - object
131            - {type : {name: value}}
132            - {type : name}
133            - {type : value}
134
135        *Parameters*
136
137        - **bs** : bytes, string or dict data to convert
138
139        *Returns* :  ESValue object '''
140        if classname:
141            simple = False
142        classn, name, val = ESValue._decodevalue(bs)
143        if classname == ES.ext_clsName and classn:
144            val = _classval()[classn](val)
145        if val.__class__.__name__ in ES.ESclassName:
146            return val
147        if not simple and classn in [ES.ili_clsName, ES.iin_clsName, ES.obs_clsName]:
148            classn = ES.ext_clsName
149        if (not classn or classn == ES.ES_clsName) and classname != ES.ES_clsName:
150            classn = classname
151        if (not classn or classn == ES.ES_clsName) and classname == ES.ES_clsName:
152            classn =  ES.nam_clsName
153        if not simple and not classn:
154            classn = ESValue.valClassName(val)
155        if classn in ES.ESvalName:
156            return _classval()[classn](val, name)
157        if classn in ES.valname:
158            return _classval()[classn].obj(val)
159        # return ESValue._castsimple(val)
160        return ESValue._castsimple(bs)
161
162    def __init__(self, val=None, name=None, className=None):
163        '''Initialize 'name' and 'value' attribute'''
164        self.name = ES.nullName
165        self.value = self.nullValue()
166
167# %% special
168    def __eq__(self, other):
169        '''equal if value and name are equal'''
170        return self.__class__.__name__ == other.__class__.__name__ and \
171            self.value == other.value and self.name == other.name
172
173    def __lt__(self, other):
174        '''lower if vSimple is lower. If vSimple are equal, self is lower if name is lower'''
175        if self.__class__.__name__ != other.__class__.__name__:
176            return hash(self) < hash(other)
177        simps = self.vSimple()
178        simpo = other.vSimple()
179        if simps == simpo:
180            return self.name < other.name
181        return simps < simpo
182
183    def __str__(self):
184        '''return json string format'''
185        js = self.json(encoded=True)
186        #js = self.json(encoded=False)
187        # if not isinstance(js, str): return str(js)
188        return js
189
190    def __repr__(self):
191        '''return classname and type of value (n, v, nv)'''
192        if not self.isNotNull():
193            return self.__class__.__name__ + '[]'
194        if not self.name:
195            return self.__class__.__name__ + '[v]'
196        if self.value == self.nullValue():
197            return self.__class__.__name__ + '[n]'
198        else:
199            return self.__class__.__name__ + '[nv]'
200
201    def __copy__(self):
202        '''return a new object with the same attributes'''
203        return self.__class__(self)
204
205    def __hash__(self):
206        '''return hash(name) + hash(value)'''
207        return hash(self.name) + hash(str(self.value))
208
209# %% binary predicates
210    def equals(self, other):
211        '''check if self value equals other value (return a boolean).'''
212        return self.link(other) == 'equals'
213
214    def disjoint(self, other):
215        '''check if self value is disjoint from other value (return a boolean).'''
216        return self.link(other) == 'disjoint'
217
218    def contains(self, other):
219        '''check if self value contains other value (return a boolean).'''
220        return self.link(other) == 'contains'
221
222    def within(self, other):
223        '''check if self value is within other value (return a boolean).'''
224        return self.link(other) == 'within'
225
226    def intersects(self, other):
227        '''check if self value intersects other value (return a boolean).'''
228        return self.link(other) == 'intersects'
229
230    def isNotNull(self, nullvalue=None):
231        '''return boolean. True if the 'ESValue' is not a NullValue'''
232        return self != self.__class__(nullvalue)
233
234    def isEqual(self, other, name=True, value=True):
235        '''Compare two `ESValue`
236
237        *Parameters*
238
239        - **other** : ESValue
240        - **name** : boolean (default True) - Include Name in comparison
241        - **value** : boolean (default True) - Include Value in comparison
242
243        *Returns*
244
245        - **boolean** : Result of the comparison '''
246        equalName = self.name == other.name
247        nullName = self.name == ES.nullName
248        #ListESValue = [LocationValue, DatationValue, PropertyValue, NamedValue, ExternValue]
249        # if   self.__class__ in ListESValue  :
250        if self.__class__.__name__ in ListESValue:
251            nullValue = self.value == self.__class__.nullValue()
252            equalValue = self.value == other.value
253        else:
254            equalValue = False
255        return (name and value and equalName and equalValue) or \
256            (name and not value and equalName and not nullName) or \
257            (not name and value and equalValue and not nullValue)
258
259    def isName(self, pattern):
260        '''check if a pattern (regex) is presenty in the ESValue name.'''
261        return re.search(pattern, self.getName()) is not None
262
263
264# %% methods
265
266    @staticmethod
267    def boundingBox(listValue):
268        ''' return a `ESValue` object with bounds values'''
269        box = copy(listValue[0])
270        for val in listValue:
271            box = box.boxUnion(val)
272        return box
273
274    @property
275    def bounds(self):
276        '''list or tuple (@property)
277        - DatationValue : boundingBox (tmin, tmax)
278        - LocationValue : boundingBox (minx, miny, maxx, maxy)
279        - PropertyValue : boundingBox (list of type property)
280        - Other ESValue : () '''
281        try:
282            if self.__class__.__name__ == 'PropertyValue':
283                return tuple(self.value[ES.prp_type])
284            return self.value.bounds
285        except:
286            return ()
287
288    def boxUnion(self, other, name=''):
289        '''return a new `ESValue` with :
290        - name : parameters
291        - value : union between box(self) and box(other)  '''
292        if self.__class__.__name__ == 'PropertyValue':
293            return self.__class__.Box(sorted(list(self._setprp(self.value[ES.prp_type]) |
294                                                  self._setprp(other.value[ES.prp_type]))),
295                                      name=name)
296        sbox = self.Box(self. bounds).value
297        obox = other.Box(other.bounds).value
298        if   sbox == obox:
299            ubox = sbox
300        elif sbox.__class__.__name__ == 'Polygon' and sbox.covers(obox):
301            ubox = sbox
302        elif sbox.__class__.__name__ == 'Polygon' and obox.covers(sbox):
303            ubox = obox
304        else:
305            ubox = sbox.union(obox)
306        boxunion = self.__class__(val=self.Box(ubox.bounds))
307        if name != '':
308            boxunion.name = name
309        return boxunion
310
311    def getValue(self):
312        ''' return self.value object '''
313        return self.value
314
315    def getName(self):
316        ''' return self.name object '''
317        return self.name
318
319    def json(self, **kwargs):
320        '''
321        Export in json/cbor format (string or dict).
322
323        *Parameters*
324
325        - **untyped** : boolean (default False) - include dtype in the json if True
326        - **encoded** : boolean (default True) - choice for return format (string/bytes if True, dict else)
327        - **encode_format**    : string (default 'json')- choice for return format (json, cbor)
328        - **simpleval** : boolean (default False) - if True, only value is included
329
330        *Returns* :  string or dict '''
331        return self.to_obj(**kwargs)
332
333    def to_json(self):
334        '''
335        Export in json format (string).
336        '''
337        return self.to_obj(untyped=False, encoded=True, encode_format='json')
338
339
340    def setName(self, nam):
341        '''
342        Set the Name of the `ESValue`
343
344        *Parameters*
345
346        - **nam** : string - value to set
347
348        *Returns* : None'''
349        self.name = nam
350
351    def setValue(self, val):
352        '''
353        Set a new Value
354
355        *Parameters*
356
357        - **val** : compatible ESValue - New ESValue
358
359        *Returns* : None'''
360        ESval = self.__class__(val)
361        self.value = ESval.value
362
363    @property
364    def simple(self):
365        '''return vSimple object (@property) '''
366        return self.vSimple(string=False)
367
368    def to_float(self, **kwargs):
369        '''return a converted float value or nan'''
370        if self.value is None:
371            return float('nan')
372        if isinstance(self.value, str):
373            if self.value == ES.nullAtt:
374                return float('nan')
375            try:
376                return float(self.value)
377            except:
378                return float('nan')
379        return float(self.value)
380
381    def to_obj(self, **kwargs):
382        '''
383        Export in json/cbor format (string or dict).
384
385        *Parameters*
386
387        - **untyped** : boolean (default False) - include dtype in the json if True
388        - **encoded** : boolean (default True) - choice for return format (string/bytes if True, dict else)
389        - **encode_format** : string (default 'json')- choice for return format (json, cbor)
390        - **simpleval** : boolean (default False)- if True only value
391
392        *Returns* :  string or dict '''
393        option = {'untyped': False, 'encoded': True, 'encode_format': 'json',
394                  'simpleval': False} | kwargs
395        option2 = option | {'encoded': False}
396        if option['simpleval']:
397            js = self._jsonValue(**option2)
398        else:
399            if not self.name or self.name == ES.nullName:
400                js = self._jsonValue(**option2)
401            elif self.value is None or self.value == self.__class__.nullValue():
402                js = self.name
403            else:
404                js = {self.name: self._jsonValue(**option2)}
405        if option['untyped']:
406            js = {ES.valname[self.__class__.__name__]: js}
407        if option['encoded'] and option['encode_format'] != 'cbor':
408            return json.dumps(js, cls=ESValueEncoder)
409        if option['encoded'] and option['encode_format'] == 'cbor':
410            return cbor2.dumps(js)
411        return js
412
413    def vSimple(self, string=False, **kwargs):
414        ''' Return the vSimple of the `ESValue` (string or object) '''
415        return self.__class__.vSimple(self, string=string)
416
417    @staticmethod
418    def ljson(listval, **kwargs):
419        '''
420        Export a list in json/cbor format (string or dict).
421
422        *Parameters*
423
424        - **untyped** : boolean (default False) - include dtype in the json if True
425        - **encoded** : boolean (default True) - choice for return format (string/bytes if True, dict else)
426        - **encode_format**    : string (default 'json')- choice for return format (json, cbor)
427        - **simpleval** : boolean (default False) - if True, only value is included
428
429        *Returns* :  list of string or dict '''
430        return [val.to_obj(**kwargs) for val in listval]
431
432    @staticmethod
433    def valClassName(val):
434        '''return the calculate ESValue Class of val (string)'''
435        if val is None:
436            return ES.nam_clsName
437        # if isinstance(val,(DatationValue, LocationValue, PropertyValue, NamedValue,
438        #                   ExternValue, TimeSlot)):
439        if val.__class__.__name__ in ListESValueSlot:
440            return val.__class__.__name__
441        if val.__class__.__name__ in [ES.obs_clsName, ES.ili_clsName]:
442            return ES.ext_clsName
443        if val.__class__.__name__ == ES.tim_clsName:
444            return ES.dat_clsName
445        if isinstance(val, str):
446            try:
447                dic = json.loads(val)
448            except:
449                dic = val
450        else:
451            dic = val
452        if isinstance(dic, (int, float, bool, list, str, tuple)):
453            return ES.nam_clsName
454        if isinstance(dic, dict) and len(dic) != 1:
455            return ES.prp_clsName
456        if isinstance(dic, dict) and len(dic) == 1 and list(dic.keys())[0] in ES.typeName.keys():
457            return ES.typeName[list(dic.keys())[0]]
458        if isinstance(dic, dict) and len(dic) == 1 and not list(dic.keys())[0] in ES.typeName.keys():
459            if isinstance(list(dic.values())[0], (int, float, bool, list, str, dict)):
460                return ES.nam_clsName
461            return ESValue.valClassName(list(dic.values())[0])
462        return ES.nam_clsName
463
464    def vName(self, default=ES.nullName, **kwargs):
465        '''
466        Return the Name of the `ESValue`
467
468        *Parameters*
469
470        - **default** : string (default nullName) - Return value if nullName
471
472        *Returns*
473
474        - **str** : Name of the ESValue
475        '''
476        if self.name == ES.nullName:
477            return default
478        return self.name
479
480    @staticmethod
481    def _castsimple(val):
482        ''' convert val in hashable val'''
483        typeval = val.__class__.__name__
484        if typeval == 'list':
485            return ESValue._tupled(val)
486        # if typeval == 'list': return tuple(val)
487        # if typeval == 'dict' and len(val) <= 1: return val
488        # if typeval == 'dict' and len(val) > 1: return str(val)
489        # if typeval == 'dict': return str(val)
490        if typeval == 'dict':
491            return json.dumps(val, cls=ESValueEncoder)
492        if typeval == 'str':
493            try:
494                return TimeInterval._dattz(datetime.datetime.fromisoformat(val))
495            except ValueError:
496                return val
497        return val
498
499    @staticmethod
500    def uncastsimple(val, datetime=True):
501        ''' convert val in hashable val'''
502        typeval = val.__class__.__name__
503        if typeval == 'tuple':
504            return ESValue._listed(val)
505        # if typeval == 'tuple': return list(val)
506        if typeval == 'str' and len(val) > 0 and val[0] == '{':
507            return json.loads(val)
508        if datetime and typeval == 'datetime':
509            return val.isoformat()
510        return val
511
512    @staticmethod
513    def _tupled(idx):
514        '''transform a list of list in a tuple of tuple'''
515        return tuple([val if not isinstance(val, list) else ESValue._tupled(val) for val in idx])
516
517    @staticmethod
518    def _listed(idx):
519        '''transform a tuple of tuple in a list of list'''
520        return [val if not isinstance(val, tuple) else ESValue._listed(val) for val in idx]
521
522    @staticmethod
523    def _decodeclass(val):
524        ''' return ESclassname of val'''
525        clss = val.__class__.__name__
526        if clss in ['bool', 'int', 'float']:
527            return 'NamedValue'
528        if clss == 'dict':
529            return 'PropertyValue'
530        if clss == 'str' and not (val.lstrip() and val.lstrip()[0] in ('{', '[', '(')):
531            return 'NamedValue'
532        try:
533            from esvalue import DatationValue
534            v = DatationValue(val)
535            return 'DatationValue'
536        except:
537            try:
538                from esvalue import LocationValue
539                v = LocationValue(val)
540                return 'LocationValue'
541            except:
542                try:
543                    from esvalue import NamedValue
544                    v = NamedValue(val)
545                    return 'NamedValue'
546                except:
547                    return clss
548
549    @staticmethod
550    def _decodevalue(bs):
551        ''' return tuple (class, name, val). If single value, it's val'''
552        bs2 = None
553        name = None
554        classname = None
555        val = None
556        if isinstance(bs, bytes):
557            bs = cbor2.loads(bs)
558        if isinstance(bs, str) and bs.lstrip() and bs.lstrip()[0] in ('{', '[', '('):
559            try:
560                bs = json.loads(bs)
561            except JSONDecodeError:
562                pass
563        if not isinstance(bs, dict):
564            val = bs
565        elif isinstance(bs, dict) and len(bs) != 1:
566            val = bs
567        elif list(bs.keys())[0] in ES.typeName:
568            classname = ES.typeName[list(bs.keys())[0]]
569            bs2 = bs[list(bs.keys())[0]]
570        else:
571            bs2 = bs
572        if bs2 is None:
573            return (classname, None, val)
574        if classname == ES.obs_clsName:
575            return (classname, None, bs2)
576        if not isinstance(bs2, dict):
577            val = bs2
578        elif isinstance(bs2, dict) and len(bs2) > 1:
579            val = bs2
580        else:
581            name = str(list(bs2.keys())[0])
582            val = list(bs2.values())[0]
583        return (classname, name, val)
584    """
585
586class ESValueError(Exception):
587    # %% ES except
588    ''' ESValue Exception'''
589    pass
ListESValue = ['LocationValue', 'DatationValue', 'PropertyValue', 'NamedValue', 'ExternValue']
ListESValueSlot = ['LocationValue', 'DatationValue', 'PropertyValue', 'NamedValue', 'ExternValue', 'TimeSlot']
class ESValueEncoder(json.encoder.JSONEncoder):
65class ESValueEncoder(json.JSONEncoder):
66    """add a new json encoder for ESValue"""
67
68    def default(self, o):
69        if isinstance(o, datetime.datetime):
70            return o.isoformat()
71        option = {'encoded': False, 'encode_format': 'json'}
72        try:
73            return o.json(**option)
74        except:
75            try:
76                return o.__to_json__()
77            except:
78                return json.JSONEncoder.default(self, o)

add a new json encoder for ESValue

def default(self, o):
68    def default(self, o):
69        if isinstance(o, datetime.datetime):
70            return o.isoformat()
71        option = {'encoded': False, 'encode_format': 'json'}
72        try:
73            return o.json(**option)
74        except:
75            try:
76                return o.__to_json__()
77            except:
78                return json.JSONEncoder.default(self, o)

Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this::

def default(self, o):
    try:
        iterable = iter(o)
    except TypeError:
        pass
    else:
        return list(iterable)
    # Let the base class default method raise the TypeError
    return JSONEncoder.default(self, o)
Inherited Members
json.encoder.JSONEncoder
JSONEncoder
item_separator
key_separator
skipkeys
ensure_ascii
check_circular
allow_nan
sort_keys
indent
encode
iterencode
class ESValue:
 81class ESValue:
 82    """
 83    This class is the parent class for each kind of values
 84     (`DatationValue`, `LocationValue`, `PropertyValue`, `NamedValue`, `ExternValue`)
 85
 86    *Attributes* :
 87
 88    - **name**  : name  of `ESValue.ESValue` objects
 89    - **value** : value of `ESValue.ESValue` objects
 90    - `ESValue.bounds` (@property) : boundary  of `ESValue.ESValue` objects
 91    - `ESValue.simple` (@property) : simplified value of `ESValue.ESValue` objects
 92
 93    The methods defined in this class are :
 94
 95    **binary predicates**
 96
 97    - `ESValue.contains`
 98    - `ESValue.equals`
 99    - `ESValue.intersects`
100    - `ESValue.within`
101    - `ESValue.disjoint`
102    - `ESValue.isEqual`
103    - `ESValue.isNotNull`
104    - `ESValue.isName`
105
106    **other methods**
107
108    - `ESValue.boundingBox` (@classmethod)
109    - `ESValue.from_obj` (@classmethod)
110    - `ESValue.boxUnion`
111    - `ESValue.getValue`
112    - `ESValue.getName`
113    - `ESValue.json`
114    - `ESValue.setName`
115    - `ESValue.setValue`
116    - `ESValue.simple`
117    - `ESValue.to_float`
118    - `ESValue.to_obj`
119    - `ESValue.valClassName`
120    - `ESValue.vName`
121    - `ESValue.vSimple`
122    """
123# %% constructor
124    """@staticmethod
125    # def from_obj(bs, classname=ES.nam_clsName):
126    def from_obj(bs, classname=None, simple=True):
127        '''Generate an ESValue Object from a bytes, json or dict object
128        Several configurations for bs parameters (name and type are string) :
129            - {name : value}
130            - name
131            - object
132            - {type : {name: value}}
133            - {type : name}
134            - {type : value}
135
136        *Parameters*
137
138        - **bs** : bytes, string or dict data to convert
139
140        *Returns* :  ESValue object '''
141        if classname:
142            simple = False
143        classn, name, val = ESValue._decodevalue(bs)
144        if classname == ES.ext_clsName and classn:
145            val = _classval()[classn](val)
146        if val.__class__.__name__ in ES.ESclassName:
147            return val
148        if not simple and classn in [ES.ili_clsName, ES.iin_clsName, ES.obs_clsName]:
149            classn = ES.ext_clsName
150        if (not classn or classn == ES.ES_clsName) and classname != ES.ES_clsName:
151            classn = classname
152        if (not classn or classn == ES.ES_clsName) and classname == ES.ES_clsName:
153            classn =  ES.nam_clsName
154        if not simple and not classn:
155            classn = ESValue.valClassName(val)
156        if classn in ES.ESvalName:
157            return _classval()[classn](val, name)
158        if classn in ES.valname:
159            return _classval()[classn].obj(val)
160        # return ESValue._castsimple(val)
161        return ESValue._castsimple(bs)
162
163    def __init__(self, val=None, name=None, className=None):
164        '''Initialize 'name' and 'value' attribute'''
165        self.name = ES.nullName
166        self.value = self.nullValue()
167
168# %% special
169    def __eq__(self, other):
170        '''equal if value and name are equal'''
171        return self.__class__.__name__ == other.__class__.__name__ and \
172            self.value == other.value and self.name == other.name
173
174    def __lt__(self, other):
175        '''lower if vSimple is lower. If vSimple are equal, self is lower if name is lower'''
176        if self.__class__.__name__ != other.__class__.__name__:
177            return hash(self) < hash(other)
178        simps = self.vSimple()
179        simpo = other.vSimple()
180        if simps == simpo:
181            return self.name < other.name
182        return simps < simpo
183
184    def __str__(self):
185        '''return json string format'''
186        js = self.json(encoded=True)
187        #js = self.json(encoded=False)
188        # if not isinstance(js, str): return str(js)
189        return js
190
191    def __repr__(self):
192        '''return classname and type of value (n, v, nv)'''
193        if not self.isNotNull():
194            return self.__class__.__name__ + '[]'
195        if not self.name:
196            return self.__class__.__name__ + '[v]'
197        if self.value == self.nullValue():
198            return self.__class__.__name__ + '[n]'
199        else:
200            return self.__class__.__name__ + '[nv]'
201
202    def __copy__(self):
203        '''return a new object with the same attributes'''
204        return self.__class__(self)
205
206    def __hash__(self):
207        '''return hash(name) + hash(value)'''
208        return hash(self.name) + hash(str(self.value))
209
210# %% binary predicates
211    def equals(self, other):
212        '''check if self value equals other value (return a boolean).'''
213        return self.link(other) == 'equals'
214
215    def disjoint(self, other):
216        '''check if self value is disjoint from other value (return a boolean).'''
217        return self.link(other) == 'disjoint'
218
219    def contains(self, other):
220        '''check if self value contains other value (return a boolean).'''
221        return self.link(other) == 'contains'
222
223    def within(self, other):
224        '''check if self value is within other value (return a boolean).'''
225        return self.link(other) == 'within'
226
227    def intersects(self, other):
228        '''check if self value intersects other value (return a boolean).'''
229        return self.link(other) == 'intersects'
230
231    def isNotNull(self, nullvalue=None):
232        '''return boolean. True if the 'ESValue' is not a NullValue'''
233        return self != self.__class__(nullvalue)
234
235    def isEqual(self, other, name=True, value=True):
236        '''Compare two `ESValue`
237
238        *Parameters*
239
240        - **other** : ESValue
241        - **name** : boolean (default True) - Include Name in comparison
242        - **value** : boolean (default True) - Include Value in comparison
243
244        *Returns*
245
246        - **boolean** : Result of the comparison '''
247        equalName = self.name == other.name
248        nullName = self.name == ES.nullName
249        #ListESValue = [LocationValue, DatationValue, PropertyValue, NamedValue, ExternValue]
250        # if   self.__class__ in ListESValue  :
251        if self.__class__.__name__ in ListESValue:
252            nullValue = self.value == self.__class__.nullValue()
253            equalValue = self.value == other.value
254        else:
255            equalValue = False
256        return (name and value and equalName and equalValue) or \
257            (name and not value and equalName and not nullName) or \
258            (not name and value and equalValue and not nullValue)
259
260    def isName(self, pattern):
261        '''check if a pattern (regex) is presenty in the ESValue name.'''
262        return re.search(pattern, self.getName()) is not None
263
264
265# %% methods
266
267    @staticmethod
268    def boundingBox(listValue):
269        ''' return a `ESValue` object with bounds values'''
270        box = copy(listValue[0])
271        for val in listValue:
272            box = box.boxUnion(val)
273        return box
274
275    @property
276    def bounds(self):
277        '''list or tuple (@property)
278        - DatationValue : boundingBox (tmin, tmax)
279        - LocationValue : boundingBox (minx, miny, maxx, maxy)
280        - PropertyValue : boundingBox (list of type property)
281        - Other ESValue : () '''
282        try:
283            if self.__class__.__name__ == 'PropertyValue':
284                return tuple(self.value[ES.prp_type])
285            return self.value.bounds
286        except:
287            return ()
288
289    def boxUnion(self, other, name=''):
290        '''return a new `ESValue` with :
291        - name : parameters
292        - value : union between box(self) and box(other)  '''
293        if self.__class__.__name__ == 'PropertyValue':
294            return self.__class__.Box(sorted(list(self._setprp(self.value[ES.prp_type]) |
295                                                  self._setprp(other.value[ES.prp_type]))),
296                                      name=name)
297        sbox = self.Box(self. bounds).value
298        obox = other.Box(other.bounds).value
299        if   sbox == obox:
300            ubox = sbox
301        elif sbox.__class__.__name__ == 'Polygon' and sbox.covers(obox):
302            ubox = sbox
303        elif sbox.__class__.__name__ == 'Polygon' and obox.covers(sbox):
304            ubox = obox
305        else:
306            ubox = sbox.union(obox)
307        boxunion = self.__class__(val=self.Box(ubox.bounds))
308        if name != '':
309            boxunion.name = name
310        return boxunion
311
312    def getValue(self):
313        ''' return self.value object '''
314        return self.value
315
316    def getName(self):
317        ''' return self.name object '''
318        return self.name
319
320    def json(self, **kwargs):
321        '''
322        Export in json/cbor format (string or dict).
323
324        *Parameters*
325
326        - **untyped** : boolean (default False) - include dtype in the json if True
327        - **encoded** : boolean (default True) - choice for return format (string/bytes if True, dict else)
328        - **encode_format**    : string (default 'json')- choice for return format (json, cbor)
329        - **simpleval** : boolean (default False) - if True, only value is included
330
331        *Returns* :  string or dict '''
332        return self.to_obj(**kwargs)
333
334    def to_json(self):
335        '''
336        Export in json format (string).
337        '''
338        return self.to_obj(untyped=False, encoded=True, encode_format='json')
339
340
341    def setName(self, nam):
342        '''
343        Set the Name of the `ESValue`
344
345        *Parameters*
346
347        - **nam** : string - value to set
348
349        *Returns* : None'''
350        self.name = nam
351
352    def setValue(self, val):
353        '''
354        Set a new Value
355
356        *Parameters*
357
358        - **val** : compatible ESValue - New ESValue
359
360        *Returns* : None'''
361        ESval = self.__class__(val)
362        self.value = ESval.value
363
364    @property
365    def simple(self):
366        '''return vSimple object (@property) '''
367        return self.vSimple(string=False)
368
369    def to_float(self, **kwargs):
370        '''return a converted float value or nan'''
371        if self.value is None:
372            return float('nan')
373        if isinstance(self.value, str):
374            if self.value == ES.nullAtt:
375                return float('nan')
376            try:
377                return float(self.value)
378            except:
379                return float('nan')
380        return float(self.value)
381
382    def to_obj(self, **kwargs):
383        '''
384        Export in json/cbor format (string or dict).
385
386        *Parameters*
387
388        - **untyped** : boolean (default False) - include dtype in the json if True
389        - **encoded** : boolean (default True) - choice for return format (string/bytes if True, dict else)
390        - **encode_format** : string (default 'json')- choice for return format (json, cbor)
391        - **simpleval** : boolean (default False)- if True only value
392
393        *Returns* :  string or dict '''
394        option = {'untyped': False, 'encoded': True, 'encode_format': 'json',
395                  'simpleval': False} | kwargs
396        option2 = option | {'encoded': False}
397        if option['simpleval']:
398            js = self._jsonValue(**option2)
399        else:
400            if not self.name or self.name == ES.nullName:
401                js = self._jsonValue(**option2)
402            elif self.value is None or self.value == self.__class__.nullValue():
403                js = self.name
404            else:
405                js = {self.name: self._jsonValue(**option2)}
406        if option['untyped']:
407            js = {ES.valname[self.__class__.__name__]: js}
408        if option['encoded'] and option['encode_format'] != 'cbor':
409            return json.dumps(js, cls=ESValueEncoder)
410        if option['encoded'] and option['encode_format'] == 'cbor':
411            return cbor2.dumps(js)
412        return js
413
414    def vSimple(self, string=False, **kwargs):
415        ''' Return the vSimple of the `ESValue` (string or object) '''
416        return self.__class__.vSimple(self, string=string)
417
418    @staticmethod
419    def ljson(listval, **kwargs):
420        '''
421        Export a list in json/cbor format (string or dict).
422
423        *Parameters*
424
425        - **untyped** : boolean (default False) - include dtype in the json if True
426        - **encoded** : boolean (default True) - choice for return format (string/bytes if True, dict else)
427        - **encode_format**    : string (default 'json')- choice for return format (json, cbor)
428        - **simpleval** : boolean (default False) - if True, only value is included
429
430        *Returns* :  list of string or dict '''
431        return [val.to_obj(**kwargs) for val in listval]
432
433    @staticmethod
434    def valClassName(val):
435        '''return the calculate ESValue Class of val (string)'''
436        if val is None:
437            return ES.nam_clsName
438        # if isinstance(val,(DatationValue, LocationValue, PropertyValue, NamedValue,
439        #                   ExternValue, TimeSlot)):
440        if val.__class__.__name__ in ListESValueSlot:
441            return val.__class__.__name__
442        if val.__class__.__name__ in [ES.obs_clsName, ES.ili_clsName]:
443            return ES.ext_clsName
444        if val.__class__.__name__ == ES.tim_clsName:
445            return ES.dat_clsName
446        if isinstance(val, str):
447            try:
448                dic = json.loads(val)
449            except:
450                dic = val
451        else:
452            dic = val
453        if isinstance(dic, (int, float, bool, list, str, tuple)):
454            return ES.nam_clsName
455        if isinstance(dic, dict) and len(dic) != 1:
456            return ES.prp_clsName
457        if isinstance(dic, dict) and len(dic) == 1 and list(dic.keys())[0] in ES.typeName.keys():
458            return ES.typeName[list(dic.keys())[0]]
459        if isinstance(dic, dict) and len(dic) == 1 and not list(dic.keys())[0] in ES.typeName.keys():
460            if isinstance(list(dic.values())[0], (int, float, bool, list, str, dict)):
461                return ES.nam_clsName
462            return ESValue.valClassName(list(dic.values())[0])
463        return ES.nam_clsName
464
465    def vName(self, default=ES.nullName, **kwargs):
466        '''
467        Return the Name of the `ESValue`
468
469        *Parameters*
470
471        - **default** : string (default nullName) - Return value if nullName
472
473        *Returns*
474
475        - **str** : Name of the ESValue
476        '''
477        if self.name == ES.nullName:
478            return default
479        return self.name
480
481    @staticmethod
482    def _castsimple(val):
483        ''' convert val in hashable val'''
484        typeval = val.__class__.__name__
485        if typeval == 'list':
486            return ESValue._tupled(val)
487        # if typeval == 'list': return tuple(val)
488        # if typeval == 'dict' and len(val) <= 1: return val
489        # if typeval == 'dict' and len(val) > 1: return str(val)
490        # if typeval == 'dict': return str(val)
491        if typeval == 'dict':
492            return json.dumps(val, cls=ESValueEncoder)
493        if typeval == 'str':
494            try:
495                return TimeInterval._dattz(datetime.datetime.fromisoformat(val))
496            except ValueError:
497                return val
498        return val
499
500    @staticmethod
501    def uncastsimple(val, datetime=True):
502        ''' convert val in hashable val'''
503        typeval = val.__class__.__name__
504        if typeval == 'tuple':
505            return ESValue._listed(val)
506        # if typeval == 'tuple': return list(val)
507        if typeval == 'str' and len(val) > 0 and val[0] == '{':
508            return json.loads(val)
509        if datetime and typeval == 'datetime':
510            return val.isoformat()
511        return val
512
513    @staticmethod
514    def _tupled(idx):
515        '''transform a list of list in a tuple of tuple'''
516        return tuple([val if not isinstance(val, list) else ESValue._tupled(val) for val in idx])
517
518    @staticmethod
519    def _listed(idx):
520        '''transform a tuple of tuple in a list of list'''
521        return [val if not isinstance(val, tuple) else ESValue._listed(val) for val in idx]
522
523    @staticmethod
524    def _decodeclass(val):
525        ''' return ESclassname of val'''
526        clss = val.__class__.__name__
527        if clss in ['bool', 'int', 'float']:
528            return 'NamedValue'
529        if clss == 'dict':
530            return 'PropertyValue'
531        if clss == 'str' and not (val.lstrip() and val.lstrip()[0] in ('{', '[', '(')):
532            return 'NamedValue'
533        try:
534            from esvalue import DatationValue
535            v = DatationValue(val)
536            return 'DatationValue'
537        except:
538            try:
539                from esvalue import LocationValue
540                v = LocationValue(val)
541                return 'LocationValue'
542            except:
543                try:
544                    from esvalue import NamedValue
545                    v = NamedValue(val)
546                    return 'NamedValue'
547                except:
548                    return clss
549
550    @staticmethod
551    def _decodevalue(bs):
552        ''' return tuple (class, name, val). If single value, it's val'''
553        bs2 = None
554        name = None
555        classname = None
556        val = None
557        if isinstance(bs, bytes):
558            bs = cbor2.loads(bs)
559        if isinstance(bs, str) and bs.lstrip() and bs.lstrip()[0] in ('{', '[', '('):
560            try:
561                bs = json.loads(bs)
562            except JSONDecodeError:
563                pass
564        if not isinstance(bs, dict):
565            val = bs
566        elif isinstance(bs, dict) and len(bs) != 1:
567            val = bs
568        elif list(bs.keys())[0] in ES.typeName:
569            classname = ES.typeName[list(bs.keys())[0]]
570            bs2 = bs[list(bs.keys())[0]]
571        else:
572            bs2 = bs
573        if bs2 is None:
574            return (classname, None, val)
575        if classname == ES.obs_clsName:
576            return (classname, None, bs2)
577        if not isinstance(bs2, dict):
578            val = bs2
579        elif isinstance(bs2, dict) and len(bs2) > 1:
580            val = bs2
581        else:
582            name = str(list(bs2.keys())[0])
583            val = list(bs2.values())[0]
584        return (classname, name, val)
585    """

This class is the parent class for each kind of values (DatationValue, LocationValue, PropertyValue, NamedValue, ExternValue)

Attributes :

  • name : name of ESValue.ESValue objects
  • value : value of ESValue.ESValue objects
  • ESValue.bounds (@property) : boundary of ESValue.ESValue objects
  • ESValue.simple (@property) : simplified value of ESValue.ESValue objects

The methods defined in this class are :

binary predicates

  • ESValue.contains
  • ESValue.equals
  • ESValue.intersects
  • ESValue.within
  • ESValue.disjoint
  • ESValue.isEqual
  • ESValue.isNotNull
  • ESValue.isName

other methods

  • ESValue.boundingBox (@classmethod)
  • ESValue.from_obj (@classmethod)
  • ESValue.boxUnion
  • ESValue.getValue
  • ESValue.getName
  • ESValue.json
  • ESValue.setName
  • ESValue.setValue
  • ESValue.simple
  • ESValue.to_float
  • ESValue.to_obj
  • ESValue.valClassName
  • ESValue.vName
  • ESValue.vSimple
class ESValueError(builtins.Exception):
587class ESValueError(Exception):
588    # %% ES except
589    ''' ESValue Exception'''
590    pass

ESValue Exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args