Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
nazrulworld committed Jul 18, 2019
1 parent b823e67 commit b9c1f64
Showing 1 changed file with 55 additions and 55 deletions.
110 changes: 55 additions & 55 deletions Sample/fhirabstractbase.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ class FHIRValidationError(Exception):
""" Exception raised when one or more errors occurred during model
validation.
"""

def __init__(self, errors, path=None):
""" Initializer.
:param errors: List of Exception instances. Also accepts a string,
which is converted to a TypeError.
:param str path: The property path on the object where errors occurred
Expand All @@ -23,16 +23,16 @@ def __init__(self, errors, path=None):
errors = [TypeError(errors)]
msgs = "\n ".join([str(e).replace("\n", "\n ") for e in errors])
message = "{}:\n {}".format(path or "{root}", msgs)

super(FHIRValidationError, self).__init__(message)

self.errors = errors
""" A list of validation errors encountered. Typically contains
TypeError, KeyError, possibly AttributeError and others. """

self.path = path
""" The path on the object where the errors occurred. """

def prefixed(self, path_prefix):
""" Creates a new instance of the receiver, with the given path prefix
applied. """
Expand All @@ -43,19 +43,19 @@ def prefixed(self, path_prefix):
class FHIRAbstractBase(object):
""" Abstract base class for all FHIR elements.
"""

def __init__(self, jsondict=None, strict=True):
""" Initializer. If strict is true, raises on errors, otherwise uses
`logging.warning()`.
:raises: FHIRValidationError on validation errors, unless strict is False
:param dict jsondict: A JSON dictionary to use for initialization
:param bool strict: If True (the default), invalid variables will raise a TypeError
"""

self._owner = None
""" Points to the parent resource, if there is one. """

if jsondict is not None:
if strict:
self.update_with_json(jsondict)
Expand All @@ -65,26 +65,26 @@ def __init__(self, jsondict=None, strict=True):
except FHIRValidationError as e:
for err in e.errors:
logging.warning(err)


# MARK: Instantiation from JSON

@classmethod
def with_json(cls, jsonobj):
""" Initialize an element from a JSON dictionary or array.
If the JSON dictionary has a "resourceType" entry and the specified
resource type is not the receiving classes type, uses
`FHIRElementFactory` to return a correct class instance.
:raises: TypeError on anything but dict or list of dicts
:raises: FHIRValidationError if instantiation fails
:param jsonobj: A dict or list of dicts to instantiate from
:returns: An instance or a list of instances created from JSON data
"""
if isinstance(jsonobj, dict):
return cls._with_json_dict(jsonobj)

if isinstance(jsonobj, list):
arr = []
for jsondict in jsonobj:
Expand All @@ -93,29 +93,29 @@ def with_json(cls, jsonobj):
except FHIRValidationError as e:
raise e.prefixed(str(len(arr)))
return arr

raise TypeError("`with_json()` on {} only takes dict or list of dict, but you provided {}"
.format(cls, type(jsonobj)))

@classmethod
def _with_json_dict(cls, jsondict):
""" Internal method to instantiate from JSON dictionary.
:raises: TypeError on anything but dict
:raises: FHIRValidationError if instantiation fails
:returns: An instance created from dictionary data
"""
if not isinstance(jsondict, dict):
raise TypeError("Can only use `_with_json_dict()` on {} with a dictionary, got {}"
.format(type(self), type(jsondict)))
raise TypeError("Can only use `_with_json_dict()` on {0} with a dictionary, but got {1}"
.format(cls, type(jsondict)))
return cls(jsondict)

@classmethod
def with_json_and_owner(cls, jsonobj, owner):
""" Instantiates by forwarding to `with_json()`, then remembers the
"owner" of the instantiated elements. The "owner" is the resource
containing the receiver and is used to resolve contained resources.
:raises: TypeError on anything but dict or list of dicts
:raises: FHIRValidationError if instantiation fails
:param dict jsonobj: Decoded JSON dictionary (or list thereof)
Expand All @@ -128,32 +128,32 @@ def with_json_and_owner(cls, jsonobj, owner):
inst._owner = owner
else:
instance._owner = owner

return instance


# MARK: (De)Serialization

def elementProperties(self):
""" Returns a list of tuples, one tuple for each property that should
be serialized, as: ("name", "json_name", type, is_list, "of_many", not_optional)
"""
return []

def update_with_json(self, jsondict):
""" Update the receiver with data in a JSON dictionary.
:raises: FHIRValidationError on validation errors
:param dict jsondict: The JSON dictionary to use to update the receiver
:returns: None on success, a list of errors if there were errors
"""
if jsondict is None:
return

if not isinstance(jsondict, dict):
raise FHIRValidationError("Non-dict type {} fed to `update_with_json` on {}"
.format(type(jsondict), type(self)))

# loop all registered properties and instantiate
errs = []
valid = set(['resourceType'])
Expand All @@ -163,7 +163,7 @@ def update_with_json(self, jsondict):
valid.add(jsname)
if of_many is not None:
valid.add(of_many)

# bring the value in shape
err = None
value = jsondict.get(jsname)
Expand All @@ -173,7 +173,7 @@ def update_with_json(self, jsondict):
except Exception as e:
value = None
err = e

# got a value, test if it is of required type and assign
if value is not None:
testval = value
Expand All @@ -184,73 +184,73 @@ def update_with_json(self, jsondict):
testval = None
else:
testval = value[0] if value and len(value) > 0 else None

if testval is not None and not self._matches_type(testval, typ):
err = TypeError("Wrong type {} for property \"{}\" on {}, expecting {}"
.format(type(testval), name, type(self), typ))
else:
setattr(self, name, value)

found.add(jsname)
if of_many is not None:
found.add(of_many)

# not optional and missing, report (we clean `of_many` later on)
elif not_optional:
nonoptionals.add(of_many or jsname)

# TODO: look at `_name` only if this is a primitive!
_jsname = '_'+jsname
_value = jsondict.get(_jsname)
if _value is not None:
valid.add(_jsname)
found.add(_jsname)

# report errors
if err is not None:
errs.append(err.prefixed(name) if isinstance(err, FHIRValidationError) else FHIRValidationError([err], name))

# were there missing non-optional entries?
if len(nonoptionals) > 0:
for miss in nonoptionals - found:
errs.append(KeyError("Non-optional property \"{}\" on {} is missing"
.format(miss, self)))

# were there superfluous dictionary keys?
if len(set(jsondict.keys()) - valid) > 0:
for supflu in set(jsondict.keys()) - valid:
errs.append(AttributeError("Superfluous entry \"{}\" in data for {}"
.format(supflu, self)))

if len(errs) > 0:
raise FHIRValidationError(errs)

def as_json(self):
""" Serializes to JSON by inspecting `elementProperties()` and creating
a JSON dictionary of all registered properties. Checks:
- whether required properties are not None (and lists not empty)
- whether not-None properties are of the correct type
:raises: FHIRValidationError if properties have the wrong type or if
required properties are empty
:returns: A validated dict object that can be JSON serialized
"""
js = {}
errs = []

# JSONify all registered properties
found = set()
nonoptionals = set()
for name, jsname, typ, is_list, of_many, not_optional in self.elementProperties():
if not_optional:
nonoptionals.add(of_many or jsname)

err = None
value = getattr(self, name)
if value is None:
continue

if is_list:
if not isinstance(value, list):
err = TypeError("Expecting property \"{}\" on {} to be list, but is {}"
Expand Down Expand Up @@ -278,20 +278,20 @@ def as_json(self):
js[jsname] = value.as_json() if hasattr(value, 'as_json') else value
except FHIRValidationError as e:
err = e.prefixed(name)

if err is not None:
errs.append(err if isinstance(err, FHIRValidationError) else FHIRValidationError([err], name))

# any missing non-optionals?
if len(nonoptionals - found) > 0:
for nonop in nonoptionals - found:
errs.append(KeyError("Property \"{}\" on {} is not optional, you must provide a value for it"
.format(nonop, self)))

if len(errs) > 0:
raise FHIRValidationError(errs)
return js

def _matches_type(self, value, typ):
if value is None:
return True
Expand All @@ -302,10 +302,10 @@ def _matches_type(self, value, typ):
if (sys.version_info < (3, 0)) and (str == typ or unicode == typ):
return (isinstance(value, str) or isinstance(value, unicode))
return False


# MARK: Owner

def owningResource(self):
""" Walks the owner hierarchy and returns the next parent that is a
`DomainResource` instance.
Expand All @@ -314,7 +314,7 @@ def owningResource(self):
while owner is not None and not hasattr(owner, "contained"):
owner = owner._owner
return owner

def owningBundle(self):
""" Walks the owner hierarchy and returns the next parent that is a
`Bundle` instance.
Expand Down

0 comments on commit b9c1f64

Please sign in to comment.