Revert "Squashed commit of the following:"

This reverts commit f07cf1d1ef.
This commit is contained in:
Gorgious
2022-10-31 19:53:19 +01:00
parent 84dde4c5c0
commit 0c3269d9e1
34 changed files with 350 additions and 19582 deletions
+31 -171
View File
@@ -18,9 +18,6 @@
import json
from pathlib import Path
from pprint import pprint
import copy
import ifcopenshell
try:
import glob
@@ -42,17 +39,15 @@ SCHEMA_FILES = {
"IFC2X3": {
"entities": BASE_MODULE_PATH / "schema/ifc2x3_entities.json",
"properties": BASE_MODULE_PATH / "schema/ifc2x3_properties.json",
"types": BASE_MODULE_PATH / "schema/ifc2x3_types.json",
},
"IFC4": {
"entities": BASE_MODULE_PATH / "schema/ifc4_entities.json",
"properties": BASE_MODULE_PATH / "schema/ifc4_properties.json",
"types": BASE_MODULE_PATH / "schema/ifc4_types.json",
},
}
db = None
schema_by_name = {"IFC2X3": None, "IFC4": None}
def get_db(version):
global db
@@ -70,35 +65,17 @@ def get_db(version):
db[ifc_version][data_type] = json.load(fi)
return db.get(version)
def get_schema_by_name(version):
global schema_by_name
if not schema_by_name[version]:
schema_by_name[version] = ifcopenshell.ifcopenshell_wrapper.schema_by_name(version)
return schema_by_name[version]
def get_entity_doc(version, entity_name, recursive=True):
def get_entity_doc(version, entity):
db = get_db(version)
if db:
entity = copy.deepcopy(db["entities"].get(entity_name))
if not recursive:
return entity
ifc_schema = get_schema_by_name(version)
ifc_entity = ifc_schema.declaration_by_name(entity_name)
ifc_supertype = ifc_entity.supertype()
if ifc_entity.supertype():
parent_entity = get_entity_doc(version, ifc_supertype.name(), recursive=True)
if 'attributes' not in entity:
entity['attributes'] = dict()
for parent_attr in parent_entity["attributes"]:
entity['attributes'][parent_attr] = parent_entity["attributes"][parent_attr]
return entity
return db["entities"].get(entity)
def get_attribute_doc(version, entity, attribute, recursive=True):
def get_attribute_doc(version, entity, attribute):
db = get_db(version)
if db:
entity = get_entity_doc(version, entity, recursive)
entity = db["entities"].get(entity)
if entity:
return entity["attributes"].get(attribute)
@@ -114,6 +91,7 @@ def get_property_set_doc(version, pset):
if db:
return db["properties"].get(pset)
def get_property_doc(version, pset, prop):
db = get_db(version)
if db:
@@ -121,10 +99,6 @@ def get_property_doc(version, pset, prop):
if pset:
return pset["properties"].get(prop)
def get_type_doc(version, ifc_type):
db = get_db(version)
if db:
return db["types"].get(ifc_type)
class DocExtractor:
def extract_ifc2x3(self):
@@ -143,10 +117,10 @@ class DocExtractor:
# probably due domains on the website being from 4_0
# example (property set / github domain / website domain):
# Pset_AirTerminalBoxPHistory IfcControlExtension IfcHvacDomain
self.extract_ifc2x3_property_sets_site_domains()
self.extract_ifc2x3_entities()
self.extract_ifc2x3_property_sets()
self.extract_ifc2x3_types()
def extract_ifc2x3_property_sets_site_domains(self):
property_sets_domains = dict()
@@ -212,14 +186,14 @@ class DocExtractor:
# because BeautifulSoup wrongly assume we confused
# html code for filepath and gives warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=MarkupResemblesLocatorWarning)
warnings.simplefilter("ignore", category=MarkupResemblesLocatorWarning)
for html_attr in bs_tree.find_all("docattribute"):
attr_name = html_attr["name"]
if attr_name == "PredefinedType":
# get references to all predefined types
defined_type = html_attr["definedtype"]
enum_path = xml_path.parents[2] / "Types" / defined_type / "DocEnumeration.xml"
enum_path = xml_path.parents[2] / "Types" / defined_type / 'DocEnumeration.xml'
with open(enum_path, "r", encoding="utf-8") as fi:
enum_bs_tree = BeautifulSoup(fi.read(), features="lxml")
hrefs = [i["href"] for i in enum_bs_tree.find_all("docconstant")]
@@ -257,6 +231,7 @@ class DocExtractor:
attr_description = attr_description.strip().rstrip(">").strip()
entity_attrs[attr_name] = attr_description
if entity_attrs:
entities_dict[entity_name]["attributes"] = entity_attrs
@@ -278,6 +253,7 @@ class DocExtractor:
def extract_ifc2x3_property_sets(self):
property_sets_dict = dict()
property_sets_references = dict()
property_sets_spec_urls = dict()
# extract lists of properties and theirs references for each property set
parsed_paths = [
@@ -292,27 +268,9 @@ class DocExtractor:
for property_set_path in glob.iglob(f"{parse_folder_path}/**/"):
property_set_path = Path(property_set_path)
property_set_name = property_set_path.stem
property_set_dict = dict()
property_references = list()
xml_path = property_set_path / "DocPropertySet.xml"
md_path = property_set_path / "Documentation.md"
if md_path.is_file():
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
property_set_description = BeautifulSoup(html, features="lxml").find("p").text
property_set_description = property_set_description.replace("\n", " ")
property_set_description = property_set_description.split("HISTORY:", 1)[0]
property_set_description = property_set_description.strip()
property_set_dict["description"] = property_set_description
else:
print(
f"WARNING. Property set {property_set_name} has no Documentation.md, "
f"property set will be left without description."
)
with open(xml_path, "r", encoding="utf-8") as fi:
bs_tree = BeautifulSoup(fi.read(), features="lxml")
for html_attr in bs_tree.find_all("docproperty"):
@@ -324,8 +282,7 @@ class DocExtractor:
"https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML"
f"/psd/{property_set_domain}/{property_set_name}.xml"
)
property_set_dict["spec_url"] = spec_url
property_sets_dict[property_set_name] = property_set_dict
property_sets_spec_urls[property_set_name] = spec_url
# setup references look up tables to convert property hrefs to actual data paths
references_paths_lookup = self.setup_ifc2x3_reference_lookup()
@@ -388,54 +345,16 @@ class DocExtractor:
for property_reference in property_sets_references[property_set_name]:
property_name, property_dict = get_property_info_by_href(property_reference)
properties_dict[property_name] = property_dict
property_sets_dict[property_set_name]["properties"] = properties_dict
property_sets_dict[property_set_name] = {
"properties": properties_dict,
"spec_url": property_sets_spec_urls[property_set_name],
}
# export property sets data
with open(BASE_MODULE_PATH / "schema/ifc2x3_properties.json", "w", encoding="utf-8") as fo:
print(f"{len(property_sets_dict)} property sets parsed")
json.dump(property_sets_dict, fo, sort_keys=True, indent=4)
def extract_ifc2x3_types(self):
types_dict = dict()
# search
types_paths = [
filepath for filepath in glob.iglob(f"{IFC2x3_DOCS_LOCATION}/Sections/**/Types", recursive=True)
]
for parse_folder_path in types_paths:
for type_path in glob.iglob(f"{parse_folder_path}/**/"):
type_path = Path(type_path)
type_name = type_path.stem
types_dict[type_name] = dict()
md_path = type_path / "Documentation.md"
# utf-8-sig because of \ufeff occcurs - meaning it's utf bom encoded
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
type_description = BeautifulSoup(html, features="lxml").find("p").text
type_description = type_description.replace("\n", " ")
type_description = type_description.replace("\u00a0", " ")
type_description = type_description.replace("Definition from ISO/CD 10303-46:1992: ", "")
type_description = type_description.replace("Definition from ISO/CD 10303-42:1992 ", "")
type_description = type_description.replace("Definition from ISO/CD 10303-42:1992: ", "")
type_description = type_description.replace("Definition from ISO/CD 10303-41:1992: ", "")
type_description = type_description.strip()
if type_description:
types_dict[type_name]['description'] = type_description
spec_url = (
"https://standards.buildingsmart.org/IFC/RELEASE/IFC2x3/TC1/HTML/"
f"{md_path.parents[2].name.lower()}/lexical/{type_name.lower()}.htm"
)
types_dict[type_name]['spec_url'] = spec_url
# export entities data
with open(BASE_MODULE_PATH / "schema/ifc2x3_types.json", "w", encoding="utf-8") as fo:
print(f"{len(types_dict)} ifc types parsed")
json.dump(types_dict, fo, sort_keys=True, indent=4)
def extract_ifc4(self):
print("Parsing data for Ifc4.0.2.1")
if not IFC4_DOCS_LOCATION.is_dir():
@@ -455,7 +374,6 @@ class DocExtractor:
self.extract_ifc4_property_sets_site_domains()
self.extract_ifc4_entities()
self.extract_ifc4_property_sets()
self.extract_ifc4_types()
def extract_ifc4_property_sets_site_domains(self):
property_sets_domains = dict()
@@ -516,12 +434,12 @@ class DocExtractor:
entity_name = entity_path.stem
entities_dict[entity_name] = dict()
# utf-8-sig because of \ufeff occcurs - meaning it's utf bom encoded
md_path = entity_path / "Documentation.md"
xml_path = entity_path / "DocEntity.xml"
md_url_part = urllib.parse.quote(str(md_path.relative_to(Path(__file__).parent).as_posix()))
github_md_url = f"https://github.com/buildingSMART/IFC/blob/{md_url_part}"
# utf-8-sig because of \ufeff occcurs - meaning it's utf bom encoded
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
@@ -541,13 +459,12 @@ class DocExtractor:
# html code for filepath and gives warnings
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=MarkupResemblesLocatorWarning)
for html_attr in bs_tree.find_all("docattribute"):
attr_name = html_attr["name"]
if attr_name == "PredefinedType":
# get references to all predefined types
defined_type = html_attr["definedtype"]
enum_path = xml_path.parents[2] / "Types" / defined_type / "DocEnumeration.xml"
enum_path = xml_path.parents[2] / "Types" / defined_type / 'DocEnumeration.xml'
with open(enum_path, "r", encoding="utf-8") as fi:
enum_bs_tree = BeautifulSoup(fi.read(), features="lxml")
hrefs = [i["href"] for i in enum_bs_tree.find_all("docconstant")]
@@ -603,6 +520,7 @@ class DocExtractor:
# function parses both property and quantity sets
property_sets_dict = dict()
property_sets_references = dict()
property_sets_spec_urls = dict()
# extract lists of properties and theirs references for each property set
parsed_paths = [
@@ -621,27 +539,10 @@ class DocExtractor:
for property_set_path in glob.iglob(f"{parse_folder_path}/**/"):
property_set_path = Path(property_set_path)
property_set_name = property_set_path.stem
property_set_dict = dict()
property_references = list()
property_quantity = property_set_path.parents[0].name == "QuantitySets"
xml_path = property_set_path / ("DocQuantitySet.xml" if property_quantity else "DocPropertySet.xml")
md_path = property_set_path / "Documentation.md"
if md_path.is_file():
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read())
property_set_description = BeautifulSoup(html, features="lxml").find("p").text
property_set_description = property_set_description.replace("\n", " ")
property_set_description = property_set_description.split("HISTORY:", 1)[0]
property_set_description = property_set_description.strip()
property_set_dict["description"] = property_set_description
else:
print(
f"WARNING. Property set {property_set_name} has no Documentation.md, "
f"property set will be left without description."
)
with open(xml_path, "r", encoding="utf-8") as fi:
bs_tree = BeautifulSoup(fi.read(), features="lxml")
@@ -660,11 +561,10 @@ class DocExtractor:
spec_url = (
"https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML"
f"/schema/{property_set_domain}"
f"/{'qset' if property_quantity else 'pset'}"
f'/{"qset" if property_quantity else "pset"}'
f"/{property_set_name.lower()}.htm"
)
property_set_dict["spec_url"] = spec_url
property_sets_dict[property_set_name] = property_set_dict
property_sets_spec_urls[property_set_name] = spec_url
# setup references look up tables to convert property hrefs to actual data paths
references_paths_lookup = self.setup_ifc4_reference_lookup()
@@ -729,61 +629,25 @@ class DocExtractor:
for property_reference in property_sets_references[property_set_name]:
property_name, property_dict = get_property_info_by_href(property_reference)
properties_dict[property_name] = property_dict
property_sets_dict[property_set_name]["properties"] = properties_dict
property_sets_dict[property_set_name] = {"properties": properties_dict}
if property_set_name in property_sets_spec_urls:
spec_url = property_sets_spec_urls[property_set_name]
property_sets_dict[property_set_name]["spec_url"] = spec_url
# export property sets data
with open(BASE_MODULE_PATH / "schema/ifc4_properties.json", "w", encoding="utf-8") as fo:
print(f"{len(property_sets_dict)} property sets parsed")
json.dump(property_sets_dict, fo, sort_keys=True, indent=4)
def extract_ifc4_types(self):
types_dict = dict()
# search
types_paths = [
filepath for filepath in glob.iglob(f"{IFC4_DOCS_LOCATION}/Sections/**/Types", recursive=True)
]
for parse_folder_path in types_paths:
for type_path in glob.iglob(f"{parse_folder_path}/**/"):
type_path = Path(type_path)
type_name = type_path.stem
types_dict[type_name] = dict()
md_path = type_path / "Documentation.md"
# utf-8-sig because of \ufeff occcurs - meaning it's utf bom encoded
with open(md_path, "r", encoding="utf-8-sig") as fi:
# convert markdown to html for easier parsing
html = markdown(fi.read().replace("{ .extDef}", ""))
type_description = BeautifulSoup(html, features="lxml").find("p").text
type_description = type_description.replace("\n", " ")
type_description = type_description.replace("\u00a0", " ")
type_description = type_description.replace("{ .extDef}", "")
type_description = type_description.replace("NOTE Definition according to ISO/CD 10303-41:1992 ", "")
type_description = type_description.replace("Definition from ISO/CD 10303-41:1992: ", "")
type_description = type_description.strip()
if type_description:
types_dict[type_name]['description'] = type_description
spec_url = (
"https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/schema/"
f"{md_path.parents[2].name.lower()}/lexical/{type_name.lower()}.htm"
)
types_dict[type_name]['spec_url'] = spec_url
# export entities data
with open(BASE_MODULE_PATH / "schema/ifc4_types.json", "w", encoding="utf-8") as fo:
print(f"{len(types_dict)} ifc types parsed")
json.dump(types_dict, fo, sort_keys=True, indent=4)
def run_doc_api_examples():
print("Entities (with parent entities attributes included):")
print(get_entity_doc("IFC2X3", "IfcWindow"))
print(get_entity_doc("IFC4", "IfcWindow"))
print("Entities:")
print(get_entity_doc("IFC2X3", "IfcActionRequest"))
print(get_entity_doc("IFC4", "IfcActionRequest"))
print("Entity attributes (with parent entities attributes included):")
print(get_attribute_doc("IFC2X3", "IfcWindow", "OwnerHistory"))
print(get_attribute_doc("IFC4", "IfcWindow", "OwnerHistory"))
print("Entity attributes:")
print(get_attribute_doc("IFC2X3", "IfcActionRequest", "RequestID"))
print(get_attribute_doc("IFC4", "IfcActionRequest", "LongDescription"))
print("Entity predefined types:")
print(get_predefined_type_doc("IFC2X3", "IfcControllerType", "FLOATING"))
@@ -797,10 +661,6 @@ def run_doc_api_examples():
print(get_property_doc("IFC2X3", "Pset_ZoneCommon", "Category"))
print(get_property_doc("IFC4", "Pset_ZoneCommon", "NetPlannedArea"))
print("Types:")
print(get_type_doc("IFC2X3", "IfcIsothermalMoistureCapacityMeasure"))
print(get_type_doc("IFC4", "IfcDuration"))
if __name__ == "__main__":
extractor = DocExtractor()