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 :
python.observation.esvalue.DatationValue
,python.observation.esvalue.LocationValue
,python.observation.esvalue.PropertyValue
,python.observation.esvalue.NamedValue
python.observation.esvalue.ExternValue
and the parent class :
Documentation is available in other pages :
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
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
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
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 ofESValue.ESValue
objectsESValue.simple
(@property) : simplified value ofESValue.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
ESValue Exception
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- args