Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

13 primitive fields extension #69

Merged
merged 15 commits into from
Oct 26, 2020
36 changes: 29 additions & 7 deletions fhirzeug/fhirclass.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
"""Define representation of FHIRClasses e.g. FHIR Resources."""

from .logger import logger
from typing import List, Dict, Optional, Set, TYPE_CHECKING

if TYPE_CHECKING:
from .fhirspec import FHIRStructureDefinitionElement, FHIRElementType

JSON_PRIMITIVE_FIELDS: Set[str] = {
"bool",
"float",
"int",
"str",
}


class FHIRClass:
""" An element/resource that should become its own class.
"""
"""An element/resource that should become its own class."""

known: Dict[str, "FHIRClass"] = {}

@classmethod
def for_element(cls, element):
""" Returns an existing class or creates one for the given element.
Returns a tuple with the class and a bool indicating creation.
"""Return an existing class or creates one for the given element.

Return a tuple with the class and a bool indicating creation.
"""
assert element.represents_class
class_name = element.name_if_class
Expand Down Expand Up @@ -46,7 +55,7 @@ def __init__(self, element, class_name):
self.add_url(element.profile.url)

def add_property(self, prop: "FHIRClassProperty") -> None:
""" Add a property to the receiver.
"""Add a property to the receiver.

:param FHIRClassProperty prop: A FHIRClassProperty instance
"""
Expand Down Expand Up @@ -221,8 +230,7 @@ def __repr__(self):


class FHIRClassProperty:
""" An element describing an instance property.
"""
"""An element describing an instance property."""

def __init__(
self,
Expand Down Expand Up @@ -259,10 +267,24 @@ def __init__(
self.module_name = (
None # should only be set if it's an external module (think Python)
)

# JSON class is the field type used when json encoded: dict, str, float, bool or int
self.json_class = spec.json_class_for_class_name(self.class_name)

# is_native means that the field type is a "native" python type
# Note: "native" or "at least already defined in the file when generating the field"
# Example: str, decimal.Decimal, bool, FHIRDateTime, FHIRString...
# Exhaustive list is defined in settings > mapping_rules > natives)
self.is_native = (
False if self.enum else spec.class_name_is_native(self.class_name)
)

# A JSON primitive field is any JSON data type that is not dict (e.g "single value field")
# /!\ `is_json_primitive_field` is different from `is_native`
# Example : AccountStatus is an enum, not a native type of Python but
# represented as a primitive JSON type (str).
self.is_json_primitive_field = self.json_class in JSON_PRIMITIVE_FIELDS

self.is_array = True if element.n_max == "*" else False
self.is_summary = element.is_summary
self.is_summary_n_min_conflict = element.summary_n_min_conflict
Expand Down
17 changes: 6 additions & 11 deletions fhirzeug/fhirrenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@


class FHIRRenderer:
""" Superclass for all renderer implementations.
"""
"""Superclass for all renderer implementations."""

def __init__(self, spec: "FHIRSpec"):
self.spec = spec
Expand All @@ -31,8 +30,7 @@ def __init__(self, spec: "FHIRSpec"):
self.jinjaenv.filters["snake_case"] = snakecase

def render(self, f_out: Optional[TextIO] = None) -> None:
""" The main rendering start point, for subclasses to override.
"""
"""The main rendering start point, for subclasses to override."""
raise Exception("Cannot use abstract superclass' `render` method")

def do_render(
Expand All @@ -42,7 +40,7 @@ def do_render(
target_path: Optional[Path] = None,
f_out: Optional[TextIO] = None,
) -> None:
""" Render the given data using a Jinja2 template, writing to the file
"""Render the given data using a Jinja2 template, writing to the file
at the target path.

:param template_name: The Jinja2 template to render
Expand Down Expand Up @@ -74,12 +72,10 @@ def do_render(


class FHIRStructureDefinitionRenderer(FHIRRenderer):
""" Write classes for a profile/structure-definition.
"""
"""Write classes for a profile/structure-definition."""

def copy_files(self, target_dir, f_out):
""" Copy base resources to the target location, according to config.
"""
"""Copy base resources to the target location, according to config."""
for manual_profile in self.generator_config.manual_profiles:
origpath = manual_profile.origpath
if origpath and origpath.exists():
Expand Down Expand Up @@ -129,8 +125,7 @@ def get_classes_to_render(self):


class FHIRValueSetRenderer(FHIRRenderer):
""" Write ValueSet and CodeSystem contained in the FHIR spec.
"""
"""Write ValueSet and CodeSystem contained in the FHIR spec."""

def render(self, f_out):
systems = [v for k, v in self.spec.codesystems.items()]
Expand Down
2 changes: 1 addition & 1 deletion fhirzeug/fhirspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ def parsed_codes(self, codes, prefix=None):
if c["code"][:1].isdigit():
self.generate_enum = False
logger.info(
"Will not generate enum for CodeSystem '{self.url}' because at least one concept code starts with a number"
f"Will not generate enum for CodeSystem '{self.url}' because at least one concept code starts with a number"
)
return None

Expand Down
82 changes: 52 additions & 30 deletions fhirzeug/generators/default/generator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ template:

# Configuration for classes and resources
default_base:
"complex-type": FHIRAbstractBase # the class to use for "Element" types
resource: FHIRAbstractResource # the class to use for "Resource" types
"complex-type": FHIRAbstractBase # the class to use for "Element" types
resource: FHIRAbstractResource # the class to use for "Resource" types

# Naming rules to apply
naming_rules:
Expand All @@ -46,9 +46,9 @@ naming_rules:
backbone_class_adds_parent: True

manual_profiles:
- origpath : ./fhirzeug/generators/python_pydantic/templates/fhirabstractbase.py
module : fhirabstractbase
contains :
- origpath: ./fhirzeug/generators/python_pydantic/templates/fhirabstractbase.py
module: fhirabstractbase
contains:
- boolean
- string
- base64Binary
Expand All @@ -63,32 +63,30 @@ manual_profiles:
- uuid
- FHIRAbstractBase


- origpath : ./fhirzeug/generators/python_pydantic/templates/fhirabstractresource.py
module : fhirabstractresource
contains :
- origpath: ./fhirzeug/generators/python_pydantic/templates/fhirabstractresource.py
module: fhirabstractresource
contains:
- FHIRAbstractResource

- origpath : ./fhirzeug/generators/python_pydantic/templates/fhir_basic_types.py
module : fhirdate
contains :
- origpath: ./fhirzeug/generators/python_pydantic/templates/fhir_basic_types.py
module: fhirdate
contains:
- date
- dateTime
- instant
- time

- origpath : ./fhirzeug/generators/python_pydantic/templates/fhirsearch.py
module : fhirsearch
contains :
- origpath: ./fhirzeug/generators/python_pydantic/templates/fhirsearch.py
module: fhirsearch
contains:
- FHIRSearch


# Mappings for the FHIR class generator.
mapping_rules:
# Which class names to map to resources and elements
classmap:
Any: Resource
Practitioner.role: PractRole # to avoid Practinioner.role and PractitionerRole generating the same class
Practitioner.role: PractRole # to avoid Practinioner.role and PractitionerRole generating the same class
boolean: bool
integer: FHIRInt
positiveInt: FHIRPositiveInt
Expand All @@ -101,7 +99,7 @@ mapping_rules:
string: FHIRString
markdown: FHIRString
id: FHIRId
code: FHIRCode # for now we're not generating enums for these
code: FHIRCode # for now we're not generating enums for these
uri: FHIRString
url: FHIRString
canonical: FHIRString
Expand All @@ -111,28 +109,53 @@ mapping_rules:
base64Binary: FHIRBase64Binary

# Classes to be replaced with different ones at resource rendering time
replacemap: {}
replacemap:
{}
# Reference: FHIRReference # `FHIRReference` adds dereferencing capabilities

# Which class names are native to the language (or can be treated this way)
natives:
- bool
- int
- float
- str
- dict
- decimal.Decimal
- FHIRBase64Binary
- FHIRCode
- FHIRDate
- FHIRDateTime
- FHIRId
- FHIRInstant
- FHIRInt
- FHIROid
- FHIRPositiveInt
- FHIRRequiredString
- FHIRString
- FHIRTime
- FHIRUnsignedInt
- float
- int
- str

# Which classes are to be expected from JSON decoding
jsonmap:
str: str
int: int
bool: bool
decimal.Decimal: float
FHIRBase64Binary: str
FHIRCode: str
FHIRDate: str
FHIRDateTime: str
FHIRId: str
FHIRInstant: str
FHIRInt: str
FHIROid: str
FHIRPositiveInt: int
FHIRRequiredString: str
FHIRString: str
FHIRTime: str
FHIRUnsignedInt: str
float: float
int: int
str: str

jsonmap_default : dict
jsonmap_default: dict

# Properties that need to be renamed because of language keyword conflicts
reservedmap:
Expand All @@ -150,7 +173,6 @@ mapping_rules:
not: not_
pass: pass_


# For enum codes where a computer just cannot generate reasonable names
enum_map:
"=": eq
Expand All @@ -161,7 +183,6 @@ mapping_rules:
">=": gte
"*": max


# If you want to give specific names to enums based on their URI
enum_namemap:
# 'http://hl7.org/fhir/coverage-exception': 'CoverageExceptionCodes',
Expand All @@ -180,5 +201,6 @@ mapping_rules:
"http://terminology.hl7.org/CodeSystem/practitioner-role": "PractitionerRoleCodes"

# If certain CodeSystems don't need to generate an enum
enum_ignore: {}
# 'http://hl7.org/fhir/resource-type-link',
enum_ignore:
{}
# 'http://hl7.org/fhir/resource-type-link',
Loading