BIM: Quantities support for nativeIFC objects (#18689)
* BIM: Quantities support for nativeIFC objects * BIM: Added nativeIFC support for schedules
This commit is contained in:
@@ -20,56 +20,119 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""This script retrieves a list of standard property sets from the IFC4 official documentation website
|
||||
and stores them into a pset_dfinitions.xml files in the current directory. Warning, this can take
|
||||
a certain time (there are more than 400 definitions to retrieve)"""
|
||||
"""This script retrieves a list of standard property sets from the IFC4 official
|
||||
documentation website and stores them into 1) a pset_definitions.csv and 2)
|
||||
a qto_definitions.csv files in the directory ../Presets."""
|
||||
|
||||
import codecs, os, re
|
||||
import os
|
||||
from zipfile import ZipFile
|
||||
from urllib.request import urlopen
|
||||
import xml.sax
|
||||
|
||||
MAXTRIES = 3
|
||||
IFC_DOCS_ROOT_URL = "https://standards.buildingsmart.org/IFC/DEV/IFC4_2/FINAL/HTML/"
|
||||
|
||||
# read the pset list
|
||||
print("Getting psets list...")
|
||||
u = urlopen(
|
||||
IFC_DOCS_ROOT_URL + "annex/annex-b/alphabeticalorder_psets.htm"
|
||||
)
|
||||
p = u.read().decode('utf-8')
|
||||
u.close()
|
||||
psets = re.findall(r">Pset_(.*?)</a>", p)
|
||||
URL = "https://ifc43-docs.standards.buildingsmart.org/IFC/RELEASE/IFC4x3/HTML/annex-a-psd.zip"
|
||||
|
||||
# retrieve xml data from each Pset type
|
||||
psetdefs = ""
|
||||
failed = []
|
||||
for i, pset in enumerate(psets):
|
||||
print(i + 1, "/", len(psets), ": Retrieving Pset", pset)
|
||||
for j in range(MAXTRIES):
|
||||
try:
|
||||
u = urlopen(
|
||||
IFC_DOCS_ROOT_URL + "psd/Pset_"
|
||||
+ pset
|
||||
+ ".xml"
|
||||
)
|
||||
p = u.read().decode('utf-8')
|
||||
u.close()
|
||||
except:
|
||||
print(" Connection failed. trying one more time...")
|
||||
QTO_TYPES = {
|
||||
"Q_AREA": "IfcQuantityArea",
|
||||
"Q_COUNT": "IfcQuantityCount",
|
||||
"Q_LENGTH": "IfcQuantityLength",
|
||||
"Q_NUMBER": "IfcQuantityNumber",
|
||||
"Q_TIME": "IfcQuantityTime",
|
||||
"Q_VOLUME": "IfcQuantityVolume",
|
||||
"Q_WEIGHT": "IfcQuantityWeight",
|
||||
}
|
||||
|
||||
class PropertyDefHandler(xml.sax.ContentHandler):
|
||||
"A XML handler to process pset definitions"
|
||||
|
||||
# this creates a dictionary where each key is a Pset name,
|
||||
# and each value is a list of [property,type] lists
|
||||
|
||||
def __init__(self, pset):
|
||||
super().__init__()
|
||||
self.line = pset.strip(".xml") + ";"
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
self.charbuffer = []
|
||||
self.writing = False
|
||||
self.prop = False
|
||||
self.qtotype = False
|
||||
|
||||
# Call when raw text is read (the property name)
|
||||
|
||||
def characters(self, data):
|
||||
if self.writing:
|
||||
self.charbuffer.append(data)
|
||||
|
||||
# Call when an element starts
|
||||
|
||||
def startElement(self, tag, attributes):
|
||||
if tag in ["PropertyDef", "QtoDef"]:
|
||||
self.prop = True
|
||||
elif tag == "Name":
|
||||
self.writing = True
|
||||
elif tag == "DataType":
|
||||
self.currenttype = attributes["type"]
|
||||
elif tag == "QtoType":
|
||||
self.qtotype = True
|
||||
self.writing = True
|
||||
|
||||
# Call when an elements ends
|
||||
|
||||
def endElement(self, tag):
|
||||
if tag in ["Name", "QtoType"]:
|
||||
if self.prop:
|
||||
self.currentprop = "".join(self.charbuffer)
|
||||
elif self.qtotype:
|
||||
self.currenttype = "".join(self.charbuffer)
|
||||
self.writing = False
|
||||
self.prop = False
|
||||
self.qtotype = False
|
||||
self.charbuffer = []
|
||||
elif tag in ["PropertyDef", "QtoDef"]:
|
||||
if self.currentprop and self.currenttype:
|
||||
if self.currenttype in QTO_TYPES:
|
||||
self.currenttype = QTO_TYPES[self.currenttype]
|
||||
self.line += self.currentprop + ";" + self.currenttype + ";"
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
|
||||
|
||||
|
||||
# MAIN
|
||||
|
||||
|
||||
print("Getting psets xml definitions...")
|
||||
|
||||
with open("psd.zip","wb") as f:
|
||||
u = urlopen(URL)
|
||||
p = u.read()
|
||||
f.write(p)
|
||||
|
||||
print("Reading xml definitions...")
|
||||
|
||||
psets = []
|
||||
qtos = []
|
||||
|
||||
with ZipFile("psd.zip", 'r') as z:
|
||||
for entry in z.namelist():
|
||||
print("Parsing",entry)
|
||||
xml_data = z.read(entry).decode(encoding="utf-8")
|
||||
handler = PropertyDefHandler(entry)
|
||||
xml.sax.parseString(xml_data, handler)
|
||||
if entry.startswith("Pset"):
|
||||
psets.append(handler.line)
|
||||
else:
|
||||
break
|
||||
else:
|
||||
print(" Unable to retrieve ", pset, ". Skipping...")
|
||||
failed.append(pset)
|
||||
psetdefs += p
|
||||
psetdefs = psetdefs.replace('<?xml version="1.0" encoding="utf-8"?>', "")
|
||||
psetdefs = '<?xml version="1.0" encoding="utf-8"?>\n<Root>\n' + psetdefs + "</Root>"
|
||||
qtos.append(handler.line)
|
||||
|
||||
f = codecs.open("pset_definitions.xml", "wb", "utf-8")
|
||||
f.write(psetdefs)
|
||||
f.close()
|
||||
print(
|
||||
"All done! writing "
|
||||
+ os.path.join(os.path.abspath(os.curdir), "pset_definitions.xml")
|
||||
)
|
||||
if failed:
|
||||
print("The following psets failed and were not retrieved:", failed)
|
||||
print("Saving files...")
|
||||
|
||||
with open("../Presets/pset_definitions.csv", "w") as f:
|
||||
for l in psets:
|
||||
f.write(l.strip(";") + "\n")
|
||||
|
||||
with open("../Presets/qto_definitions.csv", "w") as f:
|
||||
for l in qtos:
|
||||
f.write(l.strip(";") + "\n")
|
||||
|
||||
os.remove("psd.zip")
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2018 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * This program is free software; you can redistribute it and/or modify *
|
||||
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
||||
# * as published by the Free Software Foundation; either version 2 of *
|
||||
# * the License, or (at your option) any later version. *
|
||||
# * for detail see the LICENCE text file. *
|
||||
# * *
|
||||
# * This program is distributed in the hope that it will be useful, *
|
||||
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
# * GNU Library General Public License for more details. *
|
||||
# * *
|
||||
# * You should have received a copy of the GNU Library General Public *
|
||||
# * License along with this program; if not, write to the Free Software *
|
||||
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
# * USA *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
"""This script converts a xml file containing pset definitions to a csv file.
|
||||
Python3 only!! (py2 csv doesn't support utf8"""
|
||||
|
||||
import xml.sax, os
|
||||
|
||||
|
||||
class PropertyDefHandler(xml.sax.ContentHandler):
|
||||
"A XML handler to process pset definitions"
|
||||
|
||||
# this creates a dictionary where each key is a Pset name,
|
||||
# and each value is a list of [property,type] lists
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.psets = {}
|
||||
self.currentpset = None
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
self.currentlist = []
|
||||
self.charbuffer = []
|
||||
self.writing = False
|
||||
|
||||
# Call when raw text is read
|
||||
|
||||
def characters(self, data):
|
||||
if self.writing:
|
||||
self.charbuffer.append(data)
|
||||
|
||||
# Call when an element starts
|
||||
|
||||
def startElement(self, tag, attributes):
|
||||
if tag == "Name":
|
||||
self.writing = True
|
||||
if tag == "DataType":
|
||||
self.currenttype = attributes["type"]
|
||||
|
||||
# Call when an elements ends
|
||||
|
||||
def endElement(self, tag):
|
||||
if tag == "Name":
|
||||
if not self.currentpset:
|
||||
self.currentpset = "".join(self.charbuffer)
|
||||
else:
|
||||
if not self.currentprop:
|
||||
self.currentprop = "".join(self.charbuffer)
|
||||
self.writing = False
|
||||
self.charbuffer = []
|
||||
elif tag == "PropertyDef":
|
||||
if self.currentprop and self.currenttype:
|
||||
self.currentlist.append([self.currentprop, self.currenttype])
|
||||
self.currentprop = None
|
||||
self.currenttype = None
|
||||
elif tag == "PropertySetDef":
|
||||
if self.currentpset and self.currentlist:
|
||||
self.psets[self.currentpset] = self.currentlist
|
||||
self.currentpset = None
|
||||
self.currentlist = []
|
||||
|
||||
|
||||
defpath = "pset_definitions.xml"
|
||||
outpath = "pset_definitions.csv"
|
||||
|
||||
if os.path.exists(defpath):
|
||||
handler = PropertyDefHandler()
|
||||
parser = xml.sax.make_parser()
|
||||
# parser.setFeature(xml.sax.handler.feature_namespaces, 0)
|
||||
parser.setContentHandler(handler)
|
||||
parser.parse(defpath)
|
||||
psets = handler.psets
|
||||
|
||||
import csv
|
||||
|
||||
with open(outpath, "w", encoding="utf-8") as csvfile:
|
||||
csvfile = csv.writer(csvfile, delimiter=";")
|
||||
for key, values in psets.items():
|
||||
r = [key]
|
||||
for value in values:
|
||||
r.extend(value)
|
||||
csvfile.writerow(r)
|
||||
print("successfully exported ", outpath)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user