From 18476ec7f32b2167087b0a776d6611a3283b85dd Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Fri, 20 Sep 2024 14:50:31 +0500 Subject: [PATCH] bsdd - update classes according to the latest API Also added yml_to_classes.py script that can extract the classes automatically --- src/bsdd/bsdd.py | 37 ++++++++++++++++++ src/bsdd/yml_to_classes.py | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/bsdd/yml_to_classes.py diff --git a/src/bsdd/bsdd.py b/src/bsdd/bsdd.py index 18ee8e65c5..4ab9a72b4b 100644 --- a/src/bsdd/bsdd.py +++ b/src/bsdd/bsdd.py @@ -35,13 +35,17 @@ ClassTypes = Literal["Class", "GroupOfProperties", "AlternativeUse", "Material"] # and should be typed with NotRequired. +# Use yml_to_classes.py to update classes. class DictionaryContractV1(TypedDict): + code: str uri: str name: str version: str organizationCodeOwner: str organizationNameOwner: str defaultLanguageCode: str + isLatestVersion: bool + isVerified: bool license: str licenseUrl: str qualityAssuranceProcedure: str @@ -50,6 +54,7 @@ class DictionaryContractV1(TypedDict): moreInfoUrl: str releaseDate: str lastUpdatedUtc: str + availableLanguages: list[CodeNameDto] class DictionaryResponseContractV1(TypedDict): @@ -66,16 +71,20 @@ class ClassListItemContractV1(TypedDict): classType: str referenceCode: str parentClassCode: str + descriptionPart: str children: list[ClassListItemContractV1] class DictionaryClassesResponseContractV1(TypedDict): + code: str uri: str name: str version: str organizationCodeOwner: str organizationNameOwner: str defaultLanguageCode: str + isLatestVersion: bool + isVerified: bool license: str licenseUrl: str qualityAssuranceProcedure: str @@ -94,15 +103,19 @@ class PropertyListItemContractV1(TypedDict): uri: str code: str name: str + descriptionPart: str class DictionaryPropertiesResponseContractV1(TypedDict): + code: str uri: str name: str version: str organizationCodeOwner: str organizationNameOwner: str defaultLanguageCode: str + isLatestVersion: bool + isVerified: bool license: str licenseUrl: str qualityAssuranceProcedure: str @@ -135,6 +148,7 @@ class ClassPropertyContractV1(TypedDict): name: str uri: str description: str + definition: str dataType: str dimension: str dimensionLength: int @@ -159,6 +173,7 @@ class ClassPropertyContractV1(TypedDict): predefinedValue: str propertyCode: str propertyDictionaryName: str + propertyDictionaryUri: str propertyUri: str propertySet: str propertyStatus: str @@ -319,6 +334,23 @@ class ClassContractV1(TypedDict): classProperties: list[ClassPropertyContractV1] classRelations: list[ClassRelationContractV1] childClassReferences: list[ClassReferenceContractV1] + reverseClassRelations: list[ClassReverseRelationContractV1] + hierarchy: list[HierarchyItemContractV1] + + +class ClassReverseRelationContractV1(TypedDict): + relationType: str + classUri: str + className: str + fraction: float + dictionaryUri: str + + +class HierarchyItemContractV1(TypedDict): + level: int + name: str + code: str + uri: str class SearchInDictionaryResponseContractV1(TypedDict): @@ -385,6 +417,11 @@ class UnitContractV1(TypedDict): qudtUri: str +class CodeNameDto(TypedDict): + code: str + name: str + + class OAuthReceiver(http.server.BaseHTTPRequestHandler): def do_GET(self): query = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query) diff --git a/src/bsdd/yml_to_classes.py b/src/bsdd/yml_to_classes.py new file mode 100644 index 0000000000..659b943699 --- /dev/null +++ b/src/bsdd/yml_to_classes.py @@ -0,0 +1,77 @@ +import yaml +import sys +from pathlib import Path +from typing import Any + + +type_to_python_type = { + "string": "str", + "boolean": "bool", + "array": "list", + "integer": "int", +} + + +def get_python_type(prop_data: dict[str, Any]) -> str: + prop_type = prop_data.get("type") + + if prop_type is None: + ref = format_class(prop_data["$ref"].split("/")[-1]) + return ref + elif prop_type == "number" and prop_data["format"] == "double": + python_type = "float" + else: + python_type = type_to_python_type[prop_type] + + if python_type == "list": + array_type = get_python_type(prop_data["items"]) + return f"{python_type}[{array_type}]" + return python_type + + +def format_class(class_str: str) -> str: + if "." not in class_str: + return class_str + class_name, version = class_str.split(".") + return f"{class_name}{version.upper()}" + + +if __name__ == "__main__": + if len(sys.argv) < 2: + print(f'Usage: python {Path(__file__).name} "path/to/bSDD OpenAPI.yaml"') + print( + ".yml file available in https://github.com/buildingSMART/bSDD/blob/master/Documentation/bSDD%20OpenAPI.yaml" + ) + exit() + + path = sys.argv[1] + with open(path, "r") as file: + yaml_file = yaml.safe_load(file) + + class_strings: dict[str, str] = {} + for schema_name, schema_data in yaml_file["components"]["schemas"].items(): + props = schema_data.get("properties", {}) + if not props: + continue + class_name = format_class(schema_name) + class_header = f"class {class_name}(TypedDict):" + required = schema_data.get("required", []) + props_str = "" + for prop_name, prop_data in props.items(): + python_type = get_python_type(prop_data) + # if prop_name not in required: + # python_type = f"NotRequired[{python_type}]" + props_str += f" {prop_name}: {python_type}\n" + class_strings[class_header] = props_str + + # Sort classes according to bsdd.py to reduce git diff noise. + bsdd_contents = Path(__file__).with_name("bsdd.py").read_text() + # Skipping not yet implemented classes for now. + filtered_classes = list(filter(lambda x: x in bsdd_contents, class_strings)) + for class_header in sorted( + filtered_classes, + key=lambda x: bsdd_contents.index(x), + ): + print(class_header) + print(class_strings[class_header]) + print(f"{len(filtered_classes)} classes printed ({len(class_strings)-len(filtered_classes)} skipped).")