Merge branch 'v0.7.0' of https://github.com/c4rlosdias/IfcOpenShell into v0.7.0

This commit is contained in:
c4rlosdias
2023-10-02 13:49:32 -03:00
21 changed files with 1874 additions and 105 deletions
@@ -0,0 +1,23 @@
ISO-10303-21;
HEADER;
FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
FILE_NAME('','2023-09-28T23:00:50',(),(),'IfcOpenShell v0.7.0-fbd8ea1ed','IfcOpenShell v0.7.0-fbd8ea1ed','');
FILE_SCHEMA(('IFC4'));
ENDSEC;
DATA;
#1=IFCPROPERTYSETTEMPLATE('1TLQmgUKf0I8Zi07bm9Jqn',$,'ePSet_ProjectedCRS','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#2,#3,#4,#5,#6,#7));
#2=IFCSIMPLEPROPERTYTEMPLATE('2cH53PtVT3v88uf_uGFblG',$,'Name','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.);
#3=IFCSIMPLEPROPERTYTEMPLATE('3JsReA6vXDqR7aNC$haRYW',$,'Description','',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.);
#4=IFCSIMPLEPROPERTYTEMPLATE('0u0uEsT5HFtuX5GvozO9ba',$,'GeodeticDatum','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
#5=IFCSIMPLEPROPERTYTEMPLATE('1JgEX9Fsf0$ggxYltdkqiY',$,'VerticalDatum','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
#6=IFCSIMPLEPROPERTYTEMPLATE('3ahTZjYrD0MAOo5IH6QuGH',$,'MapProjection','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
#7=IFCSIMPLEPROPERTYTEMPLATE('3hqJR2ghD0GAti7q4sXKj7',$,'MapZone','',.P_SINGLEVALUE.,'IfcIdentifier',$,$,$,$,$,.READWRITE.);
#8=IFCPROPERTYSETTEMPLATE('1jGHTycOj0vA8xHfRl4TNs',$,'ePSet_MapConversion','',.PSET_OCCURRENCEDRIVEN.,'IfcProject',(#9,#10,#11,#12,#13,#15));
#9=IFCSIMPLEPROPERTYTEMPLATE('0nxsULXo17JRrAvNteYtNR',$,'Eastings','',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
#10=IFCSIMPLEPROPERTYTEMPLATE('26RfaGJs5CqemN0Xin12XL',$,'Northings','',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
#11=IFCSIMPLEPROPERTYTEMPLATE('0ziCn3nZv3m850VEUwWH42',$,'OrthogonalHeight','',.P_SINGLEVALUE.,'IfcLengthMeasure',$,$,$,$,$,.READWRITE.);
#12=IFCSIMPLEPROPERTYTEMPLATE('3d4BOt4In8WR119ms1SaAe',$,'XAxisAbscissa','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#13=IFCSIMPLEPROPERTYTEMPLATE('1Q4Voqj2j84RQRI8l67m28',$,'XAxisOrdinate','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
#15=IFCSIMPLEPROPERTYTEMPLATE('0O1Hc0$XnDNOG_jPBmbW5r',$,'Scale','',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.);
ENDSEC;
END-ISO-10303-21;
@@ -748,7 +748,6 @@ class EditOpenings(Operator, tool.Ifc.Operator):
building_objs = set()
model = tool.Ifc.get()
all_openings = model.by_type("IfcOpeningElement")
similar_openings = []
for obj in context.selected_objects:
element = tool.Ifc.get_entity(obj)
@@ -756,9 +755,7 @@ class EditOpenings(Operator, tool.Ifc.Operator):
continue
openings = [r.RelatedOpeningElement for r in element.HasOpenings]
for opening in openings:
for all_opening in all_openings:
if all_opening.ObjectPlacement == opening.ObjectPlacement:
similar_openings.append(all_opening)
similar_openings = [o for o in all_openings if o.ObjectPlacement == opening.ObjectPlacement]
opening_obj = tool.Ifc.get_object(opening)
if opening_obj:
if tool.Ifc.is_edited(opening_obj):
@@ -81,8 +81,9 @@ def update_simple_openings(element, opening_width, opening_height):
has_replaced_opening_representation = True
tool.Model.reload_body_representation(voided_objs)
with bpy.context.temp_override(selected_objects=[tool.Ifc.get_object(f) for f in fillings]):
bpy.ops.bim.recalculate_fill()
if fillings:
with bpy.context.temp_override(selected_objects=[tool.Ifc.get_object(f) for f in fillings]):
bpy.ops.bim.recalculate_fill()
def update_window_modifier_representation(context, obj):
@@ -37,6 +37,7 @@ classes = (
operator.LoadProjectElements,
operator.NewProject,
operator.RefreshLibrary,
operator.RevertProject,
operator.RewindLibrary,
operator.SaveLibraryFile,
operator.SelectLibraryFile,
@@ -676,6 +676,24 @@ class UnloadProject(bpy.types.Operator):
return {"FINISHED"}
class RevertProject(bpy.types.Operator, IFCFileSelector):
bl_idname = "bim.revert_project"
bl_label = "Revert IFC Project"
bl_options = {"REGISTER", "UNDO"}
bl_description = "Reload currently opened IFC project discarding all unsaved changes"
@classmethod
def poll(cls, context):
if not context.scene.BIMProperties.ifc_file:
cls.poll_message_set("IFC project need to be loaded and saved on the disk.")
return False
return True
def execute(self, context):
bpy.ops.bim.load_project(should_start_fresh_session=True, filepath=context.scene.BIMProperties.ifc_file)
return {"FINISHED"}
class LoadProjectElements(bpy.types.Operator):
bl_idname = "bim.load_project_elements"
bl_label = "Load Project Elements"
@@ -73,6 +73,8 @@ def file_menu(self, context):
op = self.layout.operator("export_ifc.bim", text="Save IFC Project As...")
op.should_save_as = True
self.layout.separator()
self.layout.operator("bim.revert_project")
self.layout.separator()
class BIM_PT_project(Panel):
@@ -147,14 +147,12 @@ class RemoveOpening(bpy.types.Operator, tool.Ifc.Operator):
for building_element in decomposed_building_elements:
building_obj = tool.Ifc.get_object(building_element)
if building_obj and building_obj.data:
body = ifcopenshell.util.representation.get_representation(
building_element, "Model", "Body", "MODEL_VIEW"
)
representation = tool.Geometry.get_active_representation(building_obj)
blenderbim.core.geometry.switch_representation(
tool.Ifc,
tool.Geometry,
obj=building_obj,
representation=body,
representation=representation,
should_reload=True,
is_global=True,
should_sync_changes_first=False,
+2 -2
View File
@@ -212,7 +212,7 @@ class FileAssociate(bpy.types.Operator):
self.layout.label(text="On the next step to create file association ")
self.layout.label(text="the system console will be opened ")
self.layout.label(text=f"and you will be asked to type command")
self.layout.label(text=f'"{command}"')
self.layout.label(text=f"{command}")
self.layout.label(text="to create an association.")
def invoke(self, context, event):
@@ -241,7 +241,7 @@ class FileAssociate(bpy.types.Operator):
ps_script_path = os.path.join(src_dir, "windows_bbim_association.ps1")
# NOTE: call powershell with RunAs to get admin rights from user
subprocess.run(["powershell", "-file", ps_script_path, binary_path], shell=True)
subprocess.run(["powershell", "-ExecutionPolicy", "Bypass", "-File", ps_script_path, binary_path], shell=True)
def install_desktop_linux(self, src_dir=None, destdir="/tmp", binary_path="/usr/bin/blender"):
"""Creates linux file assocations and launcher icon"""
+5 -3
View File
@@ -125,7 +125,9 @@ def select_decomposed_elements(spatial):
#HERE STARTS SPATIAL TOOL
def generate_spaces_from_walls(ifc, spatial, collector):
container, active_obj = spatial.get_container_and_active_obj()
active_obj = bpy.context.active_object
element = ifc.get_entity(active_obj)
container = spatial.get_container(element)
if not active_obj:
self.report({"ERROR"}, "No active object. Please select a wall")
@@ -147,12 +149,12 @@ def generate_spaces_from_walls(ifc, spatial, collector):
h = active_obj.dimensions.z
selected_objects = bpy.context.selected_objects
union = spatial.get_union_shape_from_selected_objects(selected_objects)
union = spatial.get_union_shape_from_selected_objects()
for i, linear_ring in enumerate(union.interiors):
poly = spatial.get_buffered_poly_from_linear_ring(linear_ring)
bm = spatial.get_bmesh_from_polygon(poly, mat, h)
bm = spatial.get_bmesh_from_polygon(poly, h)
name = "Space" + str(i)
mesh = bpy.data.meshes.new(name=name)
+1 -2
View File
@@ -810,8 +810,7 @@ class Spatial:
def set_relative_object_matrix(cls, target_obj, relative_to_obj, matrix): pass
def show_scene_objects(cls): pass
#HERE STARTS SPATIAL TOOL
# def get_container_and_active_obj(cls): pass
def get_union_shape_from_selected_objects(cls, selected_objects): pass
def get_union_shape_from_selected_objects(cls): pass
def get_boundary_elements(cls, selected_objects): pass
def get_polygons(cls, boundary_elements): pass
def get_obj_base_points(cls, obj): pass
+1 -1
View File
@@ -416,7 +416,7 @@ class Drawing(blenderbim.core.tool.Drawing):
@classmethod
def get_drawing_group(cls, drawing):
for rel in drawing.HasAssignments or []:
if rel.is_a("IfcRelAssignsToGroup"):
if rel.is_a("IfcRelAssignsToGroup") and rel.RelatingGroup.ObjectType == "DRAWING":
return rel.RelatingGroup
@classmethod
+37 -3
View File
@@ -20,6 +20,8 @@ import csv
import ifcopenshell
import ifcopenshell.api
import ifcopenshell.util.unit
import ifcopenshell.util.selector
import ifcopenshell.util.element
import locale
@@ -67,11 +69,21 @@ class Csv2Ifc:
identification = row[self.headers["Identification"]] if "Identification" in self.headers else None
quantity = row[self.headers["Quantity"]]
unit = row[self.headers["Unit"]]
if not self.is_schedule_of_rates:
assignments = {
"PropertyName": row[self.headers["Property"]],
"Query": row[self.headers["Query"]],
}
else:
assignments = {
"PropertyName": None,
"Query": None,
}
if self.has_categories:
cost_values = {
k: locale.atof(row[v])
for k, v in self.headers.items()
if k not in ["Hierarchy", "Identification", "Name", "Quantity", "Unit", "Subtotal"] and row[v]
if k not in ["Hierarchy", "Identification", "Name", "Quantity", "Unit", "Subtotal", "Property", "Query"] and row[v]
}
else:
cost_values = row[self.headers["Value"]]
@@ -82,6 +94,7 @@ class Csv2Ifc:
"Quantity": float(quantity) if quantity else None,
"Unit": str(unit) if unit else None,
"CostValues": cost_values,
"assignments": assignments,
"children": [],
}
@@ -107,7 +120,7 @@ class Csv2Ifc:
cost_item["ifc"].Name = cost_item["Name"]
cost_item["ifc"].Identification = cost_item["Identification"]
if not cost_item["CostValues"]:
if not cost_item["CostValues"] and cost_item["children"]:
if not self.is_schedule_of_rates:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.Category = "*"
@@ -115,7 +128,11 @@ class Csv2Ifc:
for category, value in cost_item["CostValues"].items():
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcMonetaryMeasure(value)
cost_value.Category = category
if category != "Rate" or category != "Price":
if "Rate" in category or"Price" in category:
category = category.replace("Rate","")
category = category.strip()
cost_value.Category = category
else:
cost_value = ifcopenshell.api.run("cost.add_cost_value", self.file, parent=cost_item["ifc"])
cost_value.AppliedValue = self.file.createIfcMonetaryMeasure(cost_item["CostValues"])
@@ -144,6 +161,15 @@ class Csv2Ifc:
"cost.add_cost_item_quantity", self.file, cost_item=cost_item["ifc"], ifc_class=quantity_class
)
quantity[3] = cost_item["Quantity"]
if cost_item["assignments"]["PropertyName"] and cost_item["assignments"]["Query"]:
print("for query",cost_item["assignments"]["Query"])
results = ifcopenshell.util.selector.filter_elements(self.file, cost_item["assignments"]["Query"])
results = [r for r in results if has_property(self.file, r, cost_item["assignments"]["PropertyName"])]
if results:
ifcopenshell.api.run("cost.assign_cost_item_quantity", self.file, cost_item=cost_item["ifc"], products=results, prop_name=cost_item["assignments"]["PropertyName"])
self.create_cost_items(cost_item["children"], cost_item["ifc"])
def create_unit(self, symbol):
@@ -158,3 +184,11 @@ class Csv2Ifc:
def create_boilerplate_ifc(self):
self.file = ifcopenshell.file(schema="IFC4")
def has_property(self, product, property_name):
qtos = ifcopenshell.util.element.get_psets(product, qtos_only=True)
for qset, quantities in qtos.items():
for quantity, value in quantities.items():
if quantity == property_name:
return True
return False
+147
View File
@@ -0,0 +1,147 @@
Hierarchy,Identification,Name,Quantity,Unit,Contract,Rate,Material Rate,Labor Rate,Subtotal,Property,Query
1,DB,Design and build,,,,,,,,,
2,DB.1,Design,,,,,,,,,
3,DB.1.1,Architecte,1,unit,40000,,,,40000,,
3,DB.1.2,Bet structure,1,unit,10000,,,,10000,,
3,DB.1.3,Bet lots techniques,1,unit,8000,,,,8000,,
2,DB.2,Construction,,,,,,,,,
3,DB.2.1,Go & etancheite,,,,,,,,,
4,DB.2.1.1,Gros oeuvre,,,,,,,,,
4,DB.2.1.2,Etancheite,,,,,,,,,
5,DB.2.1.2.1,Forme de pente et chape de lissage,,,,97,,,,NetArea,"IfcSlab, location=Roof"
5,DB.2.1.2.2,Ecran pare-vapeur et isolation thermique,,,,47,,,,NetArea,"IfcSlab, location=Roof"
5,DB.2.1.2.3,Systeme detancheite bicouche,,,,23,,,,NetArea,"IfcCovering, type=Etancheite_H"
5,DB.2.1.2.4,Reliefs detancheite bicouche,,,,7,,,,Length,"IfcWall, type=”BLK150”, location=Roof"
5,DB.2.1.2.5,Protection horizontales detancheite par carreaux de ciment,,,,,,,,NetArea,"IfcCovering, type=Etancheite_H"
5,DB.2.1.2.6,Protection des reliefs detancheite,,,,,,,,,
5,DB.2.1.2.7,Etancheite legere,,,,,,,,,
5,DB.2.1.2.8,Fourniture et pose de gargouilles y compris crapaudines,3,unit,,,,,,,
,,,,,,,,,,,
4,DB.2.1.3,Gros oeuvre piscine,,,,,,,,,
5,DB.2.1.3.1,Fouilles en pleine masse dans tous terrains y compris rocher,,,,,,,,,
5,DB.2.1.3.2,Remblaiement ou evacuation aux decharges publiques,,,,,,,,,
5,DB.2.1.3.3,Beton de proprete,,,,,,,,,
5,DB.2.1.3.4,Beton hydrofuge pour tous les ouvrages en fondation et elevation,,,,,,,,,
5,DB.2.1.3.5,Aciers a haute limite elastique fe500 pour les ouvrages en fondation et elevation,,,,,,,,,
,,,,,,,,,,,
4,DB.2.1.4,Maconnerie et briquetage en elevation,,,,,,,,,
5,DB.2.1.5.1,Cloisons en briques creuses 7t,,,,,,,,,
5,DB.2.1.5.2,Doubles cloisons en briques creuses 7t+vide10+7t,,,,,,,,,
,,,,,,,,,,,
,,,,,,,,,,,
4,DB.2.1.5,Enduits interieurs et exterieurs,,,,,,,,,
5,DB.2.1.5.1,"Enduit interieur au mortier de ciment sur murs et plafonds y
Compris baguettes d'angles",,,,,,,,,
5,DB.2.1.5.1.1,Enduit interieur au platre,,,,,,,,,
5,DB.2.1.5.1.2,"Enduit projete exterieur au mortier de ciment y compris baguettes
D'angles",,,,,,,,,
5,DB.2.1.5.1.3,Enduit de couronnement d'acroteres y compris facon de larmier,,,,,,,,,
,,,,,,,,,,,
4,DB.2.1.5,Reseaux sous dallage,,,,,,,,,
5,DB.2.1.5.1,Canalisation en buse pvc type assainissement serie i,,,,,,,,,
6,DB.2.1.5.1.1,Tranchee ,,,,,,,,,
6,DB.2.1.5.1.2,P.v.c diametre de 200 mm,,,,,,,,,
,,,,,,,,,,,
5,DB.2.1.5.2,Regards en beton arme ,,,,,,,,,
6,DB.2.1.5.2.1,Regards visitable de 50x50 cm,,,,,,,,,
6,DB.2.1.5.2.2,Regards non visitable de 40x40 cm,,,,,,,,,
4,DB.2.1.5,Divers,,,,,,,,,
5,DB.2.1.5.1,Gaine technique daeration et habillage des descentes des eaux ,,,,,,,,,
,,,,,,,,,,,
3,DB.2.2,Fluides,,,,,,,,,
4,DB.2.2.1,Plomberie,,,,,,,,,
4,DB.2.2.2,Climatisation,,,,,,,,,
4,DB.2.2.3,Sanitaire,,,,,,,,,
3,DB.2.3,Electricite,,,,,,,,,
4,DB.2.3.1,Eclairage et appareillage,,,,,,,,,
4,DB.2.3.2,Panneaux solaires,,,,,,,,,
3,DB.2.4,Architecture interieure,,,,,,,,,
4,DB.2.4.1,Menuiserie metallique,,,,,,,,,
5,DB.2.4.1.1,G.c 1 en vitrage bord àbord 10*10*2(1unite) rail en alu encastre,,,,,,,,,
5,DB.2.4.1.2,G.c 2 en vitrage bord àbord 10*10*2(1unite) rail en alu encastre,,,,,,,,,
,,,,,,,,,,,
4,DB.2.4.2,Menuiserie bois,,,,,,,,,
5,DB.2.4.2.1,Portes,,,,,,,,,
6,DB.2.4.2.1.1,P1,,,,,,,,Count,"IfcDoor, type=P1"
6,DB.2.4.2.1.2,P2,,,,,,,,Count,"IfcDoor, type=P2-LEFT + IfcDoor, type=P2-RIGHT"
6,DB.2.4.2.1.3,P3,,,,,,,,Count,"IfcDoor, type=P3-LEFT + IfcDoor, type=P3-RIGHT"
,,,,,,,,,,,
5,DB.2.4.2.2,Placards et habillage,,,,,,,,,
6,DB.2.4.2.2.1,Fourniture placard type 1,,,,,,,,,
6,DB.2.4.2.2.2,Fourniture placard type 2,,,,,,,,,
6,DB.2.4.2.2.3,Fourniture placard type 3 ,,,,,,,,,
6,DB.2.4.2.2.3,Fourniture placard type 4,,,,,,,,,
,,,,,,,,,,,
4,DB.2.4.3,Revetement sol parquet,,,,,,,,,
5,DB.2.4.3.1,Fourniture parquet contrecolle 18mm dont epaisseur noyer americain 3mm,,,,,300,,,NetArea,"IfcCovering, type=RS-2"
5,DB.2.4.3.2,Fournitur basic underlay,,,,,50,,,NetArea,"IfcCovering, type=RS-2"
5,DB.2.4.3.3,"Fourniture profilpas finition laiton 2,5mm",,,,,20,,,Width,IfcDoor
5,DB.2.4.3.4,"Pose du parquet y compris isolant acoustique, chappe de ciment, sous-couche et barre de seuil",,,,,,74,,NetArea,"IfcCovering, type=RS-2"
,,,,,,,,,,,
4,DB.2.4.4,Revetement sol marbre,,,,,,,,,
5,DB.2.4.4.1,Fourniture marbre sols,,,,,,,,NetArea,"IfcCovering, type=RS-5"
5,DB.2.4.4.2,Fourniture marbre- sols sdb,,,,,,,,NetArea,"IfcCovering, type=RS-2"
5,DB.2.4.4.3,Fourniture marbre murs sdb ,,,,,,,,,
5,DB.2.4.4.4,Fourniture marbre marche escalier y/c nez,,,,,,,,,
5,DB.2.4.4.5,Pose marbre sols y compris chappe de ciment,,,,,,,,,
5,DB.2.4.4.6,Pose marbre murs,,,,,,,,,
5,DB.2.4.4.7,Pose marbre marches,,,,,,,,,
,,,,,,,,,,,
4,DB.2.4.5,Revetement carreaux,,,,,,,,,
5,DB.2.4.5.1,Revetements,,,,,,,,,
6,DB.2.4.5.1.1,Revêtement sol: cuisine - carreaux - gres cerame (30*60cm) - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage,,,,,,,,,
6,DB.2.4.5.1.2,Revêtement sol: salle de jeux - carreaux - gres cerame (30*60cm) - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage,,,,,,,,,
6,DB.2.4.5.1.3,Revêtement sol: (reserve) - gres cerame (30*60cm) - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage - ,,,,,,,,,
6,DB.2.4.5.1.4,Revêtement mural cuisine - carreaux - effet vitrage noir,,,,,,,,,
5,DB.2.4.5.2,Plinthes,,,,,,,,,
6,DB.2.4.5.2.1,Plinthes - duropolymer orac decor ref : sx157,,,,,,,,,
6,DB.2.4.5.2.2,Plinthes marches - - duropolymer orac decor ref : sx157,,,,,,,,,
,,,,,,,,,,,
4,DB.2.4.6,Peinture,,,,,,,,,
5,DB.2.4.6.1,Fourniture et pose peinture murs - marmorex astral y/c preparation (5 couches),,,,,,,,,
5,DB.2.4.6.2,Fourniture et pose peinture contremarche escalier - marmorex astral y/c preparation (5 couches,,,,,,,,,
5,DB.2.4.6.3,Fourniture et pose peinture murs cuisine - alpha tacto astral y/c preparation (5 couches),,,,,,,,,
5,DB.2.4.6.4,Foruniture et pose peinture faux-plafond - jotun acrylique mate blanc y/c preparation (5 couches),,,,,,,,,
5,DB.2.4.6.5,Fourniture et pose peinture murs sdb - jotun acrylique semi-brillant blanc special piece humide y/c preparation (5 couches),,,,,,,,,
5,DB.2.4.6.6,Fourniture et pose peinture faux-plafond sdb + cuisine - jotun acrylique semi-brillant blanc special piece humide y/c preparation (5 couches),,,,,,,,,
,,,,,,,,,,,
4,DB.2.4.7,Faux plafonds,,,,,,,,,
5,DB.2.4.7.1,Type 1,,,,,,,,,
5,DB.2.4.7.2,Type 2,,,,,,,,,
5,DB.2.4.7.3,Type 3 hydrofuge exterieur,,,,,,,,,
5,DB.2.4.7.4,Trappes dacces,,,,,,,,,
,,,,,,,,,,,
4,DB.2.4.8,Cuisine,1,unit,,,,,,,
,,,,,,,,,,,
3,DB.2.5,Facade,,,,,,,,,
4,DB.2.5.1,Revetement exterieur ,,,,,,,,,
5,DB.2.5.1.1,Revetement mural: plus value enduit mural hydrofuge (monocouche),,,,,,,,,
5,DB.2.5.1.2,Revetement mural: pierre volcanique grise ,,,,,,,,,
5,DB.2.5.1.3,Revetement mural: bardage en bois iroko ou tole alu section carre ou rectangulaire avec habillage bois technowood (voir detail architecte),,,,,,,,,
5,DB.2.5.1.4,Revêtement sol: terrasses et cour - tech - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage ,,,,,,,,,
5,DB.2.5.1.5,Revetement sol: seuill entree en pierre de taza,,,,,,,,,
5,DB.2.5.1.6,Revetement sol: sous bassement perimetre villa en pierre de taza ,,,,,,,,,
,,,,,,,,,,,
4,DB.2.5.2,Menuiserie aluminium,,,,,,,,,
5,DB.2.5.2.1,Fenetres,,,,,,,,,
6,DB.2.5.2.1.1,F1,,,,,,,,,
6,DB.2.5.2.1.2,F2,,,,,,,,,
,,,,,,,,,,,
5,DB.2.5.2.2,Chassis ouvrants,,,,,,,,,
6,DB.2.5.2.2.1,Ch1,,,,,,,,,
6,DB.2.5.2.2.2,Ch2,,,,,,,,,
6,DB.2.5.2.2.3,Ch3,,,,,,,,,
,,,,,,,,,,,
5,DB.2.5.2.3,Portes coulissantes/ mur rideau,,,,,,,,,
6,DB.2.5.2.3.1,P4,,,,,,,,,
6,DB.2.5.2.3.2,P5,,,,,,,,,
6,DB.2.5.2.3.3,P6,,,,,,,,,
,,,,,,,,,,,
5,DB.2.5.2.4,Volets roulants,,,,,,,,,
6,DB.2.5.2.4.1,Type 1,,,,,,,,,
6,DB.2.5.2.4.2,Type 2,,,,,,,,,
6,DB.2.5.2.4.3,Type 3,,,,,,,,,
3,DB.2.6,Exterieur,,,,,,,,,
4,DB.2.6.1,Cloture,,,,,,,,,
4,DB.2.6.2,Piscine,,,,,,,,,
4,DB.2.6.3,Jardin,,,,,,,,,
1 Hierarchy Identification Name Quantity Unit Contract Rate Material Rate Labor Rate Subtotal Property Query
2 1 DB Design and build
3 2 DB.1 Design
4 3 DB.1.1 Architecte 1 unit 40000 40000
5 3 DB.1.2 Bet structure 1 unit 10000 10000
6 3 DB.1.3 Bet lots techniques 1 unit 8000 8000
7 2 DB.2 Construction
8 3 DB.2.1 Go & etancheite
9 4 DB.2.1.1 Gros oeuvre
10 4 DB.2.1.2 Etancheite
11 5 DB.2.1.2.1 Forme de pente et chape de lissage 97 NetArea IfcSlab, location=Roof
12 5 DB.2.1.2.2 Ecran pare-vapeur et isolation thermique 47 NetArea IfcSlab, location=Roof
13 5 DB.2.1.2.3 Systeme d’etancheite bicouche 23 NetArea IfcCovering, type=Etancheite_H
14 5 DB.2.1.2.4 Reliefs d’etancheite bicouche 7 Length IfcWall, type=”BLK150”, location=Roof
15 5 DB.2.1.2.5 Protection horizontales d’etancheite par carreaux de ciment NetArea IfcCovering, type=Etancheite_H
16 5 DB.2.1.2.6 Protection des reliefs d’etancheite
17 5 DB.2.1.2.7 Etancheite legere
18 5 DB.2.1.2.8 Fourniture et pose de gargouilles y compris crapaudines 3 unit
19
20 4 DB.2.1.3 Gros oeuvre piscine
21 5 DB.2.1.3.1 Fouilles en pleine masse dans tous terrains y compris rocher
22 5 DB.2.1.3.2 Remblaiement ou evacuation aux decharges publiques
23 5 DB.2.1.3.3 Beton de proprete
24 5 DB.2.1.3.4 Beton hydrofuge pour tous les ouvrages en fondation et elevation
25 5 DB.2.1.3.5 Aciers a haute limite elastique fe500 pour les ouvrages en fondation et elevation
26
27 4 DB.2.1.4 Maconnerie et briquetage en elevation
28 5 DB.2.1.5.1 Cloisons en briques creuses 7t
29 5 DB.2.1.5.2 Doubles cloisons en briques creuses 7t+vide10+7t
30
31
32 4 DB.2.1.5 Enduits interieurs et exterieurs
33 5 DB.2.1.5.1 Enduit interieur au mortier de ciment sur murs et plafonds y Compris baguettes d'angles
34 5 DB.2.1.5.1.1 Enduit interieur au platre
35 5 DB.2.1.5.1.2 Enduit projete exterieur au mortier de ciment y compris baguettes D'angles
36 5 DB.2.1.5.1.3 Enduit de couronnement d'acroteres y compris facon de larmier
37
38 4 DB.2.1.5 Reseaux sous dallage
39 5 DB.2.1.5.1 Canalisation en buse pvc type assainissement serie i
40 6 DB.2.1.5.1.1 Tranchee
41 6 DB.2.1.5.1.2 P.v.c diametre de 200 mm
42
43 5 DB.2.1.5.2 Regards en beton arme
44 6 DB.2.1.5.2.1 Regards visitable de 50x50 cm
45 6 DB.2.1.5.2.2 Regards non visitable de 40x40 cm
46 4 DB.2.1.5 Divers
47 5 DB.2.1.5.1 Gaine technique d’aeration et habillage des descentes des eaux
48
49 3 DB.2.2 Fluides
50 4 DB.2.2.1 Plomberie
51 4 DB.2.2.2 Climatisation
52 4 DB.2.2.3 Sanitaire
53 3 DB.2.3 Electricite
54 4 DB.2.3.1 Eclairage et appareillage
55 4 DB.2.3.2 Panneaux solaires
56 3 DB.2.4 Architecture interieure
57 4 DB.2.4.1 Menuiserie metallique
58 5 DB.2.4.1.1 G.c 1 en vitrage bord àbord 10*10*2(1unite) rail en alu encastre
59 5 DB.2.4.1.2 G.c 2 en vitrage bord àbord 10*10*2(1unite) rail en alu encastre
60
61 4 DB.2.4.2 Menuiserie bois
62 5 DB.2.4.2.1 Portes
63 6 DB.2.4.2.1.1 P1 Count IfcDoor, type=P1
64 6 DB.2.4.2.1.2 P2 Count IfcDoor, type=P2-LEFT + IfcDoor, type=P2-RIGHT
65 6 DB.2.4.2.1.3 P3 Count IfcDoor, type=P3-LEFT + IfcDoor, type=P3-RIGHT
66
67 5 DB.2.4.2.2 Placards et habillage
68 6 DB.2.4.2.2.1 Fourniture placard type 1
69 6 DB.2.4.2.2.2 Fourniture placard type 2
70 6 DB.2.4.2.2.3 Fourniture placard type 3
71 6 DB.2.4.2.2.3 Fourniture placard type 4
72
73 4 DB.2.4.3 Revetement sol parquet
74 5 DB.2.4.3.1 Fourniture parquet contrecolle 18mm dont epaisseur noyer americain 3mm 300 NetArea IfcCovering, type=RS-2
75 5 DB.2.4.3.2 Fournitur basic underlay 50 NetArea IfcCovering, type=RS-2
76 5 DB.2.4.3.3 Fourniture profilpas finition laiton 2,5mm 20 Width IfcDoor
77 5 DB.2.4.3.4 Pose du parquet y compris isolant acoustique, chappe de ciment, sous-couche et barre de seuil 74 NetArea IfcCovering, type=RS-2
78
79 4 DB.2.4.4 Revetement sol marbre
80 5 DB.2.4.4.1 Fourniture marbre sols NetArea IfcCovering, type=RS-5
81 5 DB.2.4.4.2 Fourniture marbre- sols sdb NetArea IfcCovering, type=RS-2
82 5 DB.2.4.4.3 Fourniture marbre murs sdb
83 5 DB.2.4.4.4 Fourniture marbre marche escalier y/c nez
84 5 DB.2.4.4.5 Pose marbre sols y compris chappe de ciment
85 5 DB.2.4.4.6 Pose marbre murs
86 5 DB.2.4.4.7 Pose marbre marches
87
88 4 DB.2.4.5 Revetement carreaux
89 5 DB.2.4.5.1 Revetements
90 6 DB.2.4.5.1.1 Revêtement sol: cuisine - carreaux - gres cerame (30*60cm) - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage
91 6 DB.2.4.5.1.2 Revêtement sol: salle de jeux - carreaux - gres cerame (30*60cm) - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage
92 6 DB.2.4.5.1.3 Revêtement sol: (reserve) - gres cerame (30*60cm) - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage -
93 6 DB.2.4.5.1.4 Revêtement mural cuisine - carreaux - effet vitrage noir
94 5 DB.2.4.5.2 Plinthes
95 6 DB.2.4.5.2.1 Plinthes - duropolymer orac decor ref : sx157
96 6 DB.2.4.5.2.2 Plinthes marches - - duropolymer orac decor ref : sx157
97
98 4 DB.2.4.6 Peinture
99 5 DB.2.4.6.1 Fourniture et pose peinture murs - marmorex astral y/c preparation (5 couches)
100 5 DB.2.4.6.2 Fourniture et pose peinture contremarche escalier - marmorex astral y/c preparation (5 couches
101 5 DB.2.4.6.3 Fourniture et pose peinture murs cuisine - alpha tacto astral y/c preparation (5 couches)
102 5 DB.2.4.6.4 Foruniture et pose peinture faux-plafond - jotun acrylique mate blanc y/c preparation (5 couches)
103 5 DB.2.4.6.5 Fourniture et pose peinture murs sdb - jotun acrylique semi-brillant blanc special piece humide y/c preparation (5 couches)
104 5 DB.2.4.6.6 Fourniture et pose peinture faux-plafond sdb + cuisine - jotun acrylique semi-brillant blanc special piece humide y/c preparation (5 couches)
105
106 4 DB.2.4.7 Faux plafonds
107 5 DB.2.4.7.1 Type 1
108 5 DB.2.4.7.2 Type 2
109 5 DB.2.4.7.3 Type 3 – hydrofuge – exterieur
110 5 DB.2.4.7.4 Trappes d’acces
111
112 4 DB.2.4.8 Cuisine 1 unit
113
114 3 DB.2.5 Facade
115 4 DB.2.5.1 Revetement exterieur
116 5 DB.2.5.1.1 Revetement mural: plus value enduit mural hydrofuge (monocouche)
117 5 DB.2.5.1.2 Revetement mural: pierre volcanique grise
118 5 DB.2.5.1.3 Revetement mural: bardage en bois iroko ou tole alu section carre ou rectangulaire avec habillage bois technowood (voir detail architecte)
119 5 DB.2.5.1.4 Revêtement sol: terrasses et cour - tech - choix sous validation d'echantillons par l'architecte et le maitre d'ouvrage
120 5 DB.2.5.1.5 Revetement sol: seuill entree en pierre de taza
121 5 DB.2.5.1.6 Revetement sol: sous bassement perimetre villa en pierre de taza
122
123 4 DB.2.5.2 Menuiserie aluminium
124 5 DB.2.5.2.1 Fenetres
125 6 DB.2.5.2.1.1 F1
126 6 DB.2.5.2.1.2 F2
127
128 5 DB.2.5.2.2 Chassis ouvrants
129 6 DB.2.5.2.2.1 Ch1
130 6 DB.2.5.2.2.2 Ch2
131 6 DB.2.5.2.2.3 Ch3
132
133 5 DB.2.5.2.3 Portes coulissantes/ mur rideau
134 6 DB.2.5.2.3.1 P4
135 6 DB.2.5.2.3.2 P5
136 6 DB.2.5.2.3.3 P6
137
138 5 DB.2.5.2.4 Volets roulants
139 6 DB.2.5.2.4.1 Type 1
140 6 DB.2.5.2.4.2 Type 2
141 6 DB.2.5.2.4.3 Type 3
142 3 DB.2.6 Exterieur
143 4 DB.2.6.1 Cloture
144 4 DB.2.6.2 Piscine
145 4 DB.2.6.3 Jardin
Binary file not shown.
+21 -4
View File
@@ -57,7 +57,7 @@ class Parser:
else:
self.config = preset
def parse(self, ifc_file):
def parse(self, ifc_file, name=None):
for category_name, category_config in self.config["categories"].items():
self.categories.setdefault(category_name, {})
for element in category_config["get_category_elements"](ifc_file):
@@ -81,12 +81,29 @@ class Parser:
data.update(custom_data)
if data:
key = data["key"]
del data["key"]
key = "-".join([str(data[k]) for k in category_config["keys"]])
if key in self.categories[category_name]:
self.duplicate_keys.append((self.categories[category_name][key], data))
self.categories[category_name][key] = data
def federate(self, paths):
for path in paths:
spreadsheet = pd.ExcelFile(path)
sheet_names = spreadsheet.sheet_names
for category_name, category_config in self.config["categories"].items():
self.categories.setdefault(category_name, {})
if category_name not in sheet_names:
continue
self.categories.setdefault(category_name, {})
df = pd.read_excel(spreadsheet, sheet_name=category_name, keep_default_na=False)
for _, row in df.iterrows():
key = "-".join([str(row[k]) for k in category_config["keys"]])
if key in self.categories[category_name]:
continue
self.categories[category_name][key] = row.to_dict()
def exclude_categories(self, names):
for name in names:
if name in self.config["categories"]:
@@ -148,7 +165,7 @@ class Writer:
if isinstance(value, str):
convert = lambda text: int(text) if text.isdigit() else text.lower()
return [convert(c) for c in re.split("([0-9]+)", value)]
return value
return [str(value)]
# Sort least important keys first, then more important keys.
# https://stackoverflow.com/questions/11476371/sort-by-multiple-keys-using-different-orderings
+7 -7
View File
@@ -61,7 +61,6 @@ def get_systems(ifc_file):
def get_facility_data(ifc_file, element):
return {
"key": element.Name,
"Name": element.Name,
"ProjectName": ifc_file.by_type("IfcProject")[0].Name,
"SiteName": getattr(get_facility_parent(element, "IfcSite"), "Name", None),
@@ -80,7 +79,6 @@ def get_facility_data(ifc_file, element):
def get_storey_data(ifc_file, element):
return {
"key": element.Name,
"Name": element.Name,
"Category": "Level",
"AuthorOrganizationName": get_owner_name(element),
@@ -95,7 +93,6 @@ def get_storey_data(ifc_file, element):
def get_space_data(ifc_file, element):
psets = ifcopenshell.util.element.get_psets(element)
return {
"key": element.Name,
"Name": element.Name,
"Description": element.LongName,
"Category": get_classification(element),
@@ -112,7 +109,6 @@ def get_space_data(ifc_file, element):
def get_zone_data(ifc_file, element):
zone, space = element
return {
"key": (zone.Name or "Unnamed") + (space.Name or "Unnamed"),
"Name": zone.Name,
"SpaceName": space.Name,
"AuthorOrganizationName": get_owner_name(zone),
@@ -125,7 +121,6 @@ def get_zone_data(ifc_file, element):
def get_element_type_data(ifc_file, element):
psets = ifcopenshell.util.element.get_psets(element)
return {
"key": element.Name,
"Name": element.Name,
"Description": element.Description,
"Category": get_classification(element),
@@ -150,7 +145,6 @@ def get_element_data(ifc_file, element):
system = systems[0].Name if systems else None
psets = ifcopenshell.util.element.get_psets(element)
return {
"key": element.Name,
"Name": element.Name,
"TypeName": ifcopenshell.util.element.get_type(element).Name,
"SpaceName": space_name,
@@ -173,7 +167,6 @@ def get_element_data(ifc_file, element):
def get_system_data(ifc_file, element):
return {
"key": element.Name,
"Name": element.Name,
"Description": element.Description,
"Category": get_classification(element),
@@ -241,6 +234,7 @@ config = {
},
"categories": {
"Facilities": {
"keys": ["Name"],
"headers": [
"Name",
"ProjectName",
@@ -262,6 +256,7 @@ config = {
"get_element_data": get_facility_data,
},
"Storeys": {
"keys": ["Name"],
"headers": [
"Name",
"Category",
@@ -278,6 +273,7 @@ config = {
"get_element_data": get_storey_data,
},
"Spaces": {
"keys": ["Name"],
"headers": [
"Name",
"Description",
@@ -296,6 +292,7 @@ config = {
"get_element_data": get_space_data,
},
"Zones": {
"keys": ["Name", "SpaceName"],
"headers": ["Name", "SpaceName", "AuthorOrganizationName", "AuthorDate", "ModelSoftware", "ModelID"],
"colours": "prreee",
"sort": [{"name": "Name", "order": "ASC"}],
@@ -303,6 +300,7 @@ config = {
"get_element_data": get_zone_data,
},
"ElementTypes": {
"keys": ["Name"],
"headers": [
"Name",
"Description",
@@ -325,6 +323,7 @@ config = {
"get_element_data": get_element_type_data,
},
"Elements": {
"keys": ["Name"],
"headers": [
"Name",
"TypeName",
@@ -350,6 +349,7 @@ config = {
"get_element_data": get_element_data,
},
"Systems": {
"keys": ["Name"],
"headers": [
"Name",
"Description",
+68 -68
View File
@@ -35,7 +35,7 @@ import ifcopenshell.util.classification
def get_contacts(ifc_file):
return ifc_file.by_type("IfcPersonAndOrganization")
return ifc_file.by_type("IfcActor")
def get_facilities(ifc_file):
@@ -245,7 +245,6 @@ def get_attributes(ifc_file):
allowed_values = get_property_unit(props["id"], name)
data = {
"key": str(val(name)) + str(sheet_name) + str(val(element.Name)),
"Name": val(name),
"CreatedBy": pset_created_by,
"CreatedOn": pset_created_on,
@@ -265,51 +264,54 @@ def get_attributes(ifc_file):
def get_contact_data(ifc_file, element):
email = get_email_from_pao(element)
the_actor = element.Theactor
history = get_history(ifc_file)
if the_actor.is_a("IfcPerson"):
pao = None
person = the_actor
organization = None
elif the_actor.is_a("IfcOrganization"):
pao = None
person = None
organization = the_actor
elif the_actor.is_a("IfcPersonAndOrganization"):
pao = the_actor
person = the_actor.ThePerson
organization = the_actor.TheOrganization
roles = []
for actor in [element, element.ThePerson, element.TheOrganization]:
email = get_email_from_pao(person, organization)
roles = set()
for actor in [pao, person, organization]:
if not actor:
continue
for role in actor.Roles or []:
if role.Role == "USERDEFINED":
if role.UserDefinedRole:
roles.append(role.UserDefinedRole)
roles.add(role.UserDefinedRole)
else:
roles.append(role.Role)
organization = element.TheOrganization
person = element.ThePerson
department = get_pao_address(element, "InternalLocation")
if not department:
for rel in organization.Relates:
for org in rel.RelatedOrganizations:
if val(org.Name):
department = org.Name
roles.add(role.Role)
return {
"key": email,
"Email": email,
"CreatedBy": get_email_from_history(history) if history else None,
"CreatedOn": ifcopenshell.util.date.ifc2datetime(history.CreationDate).isoformat() if history else None,
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
"Category": ",".join(roles),
"Company": getattr(organization, "Name", None),
"Phone": get_pao_address(element, "TelephoneNumbers"),
"ExternalSystem": history.OwningApplication.ApplicationFullName if history else None,
"Phone": get_pao_address(person, organisation, "TelephoneNumbers"),
"ExternalSystem": get_external_system(element),
"ExternalObject": element.is_a(),
"ExternalIdentifier": email,
"Department": department,
"OrganizationCode": getattr(organization, "Id", getattr(organization, "Identification", None))
or organization.Name,
"ExternalIdentifier": element.GlobalId,
"Department": get_pao_address(person, organisation, "InternalLocation"),
"OrganizationCode": getattr(organization, "Id", getattr(organization, "Identification", None)),
"GivenName": getattr(person, "GivenName", None),
"FamilyName": getattr(person, "FamilyName", None),
"Street": get_pao_address(element, "AddressLines"),
"PostalBox": get_pao_address(element, "PostalBox"),
"Town": get_pao_address(element, "Town"),
"StateRegion": get_pao_address(element, "Region"),
"PostalCode": get_pao_address(element, "PostalCode"),
"Country": get_pao_address(element, "Country"),
"Street": get_pao_address(person, organisation, "AddressLines"),
"PostalBox": get_pao_address(person, organisation, "PostalBox"),
"Town": get_pao_address(person, organisation, "Town"),
"StateRegion": get_pao_address(person, organisation, "Region"),
"PostalCode": get_pao_address(person, organisation, "PostalCode"),
"Country": get_pao_address(person, organisation, "Country"),
}
@@ -338,7 +340,6 @@ def get_facility_data(ifc_file, element):
name = val(site.Name) or val(site.LongName)
return {
"key": name,
"Name": name,
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -394,7 +395,6 @@ def get_floor_data(ifc_file, element):
elevation = "" if elevation is None else str(elevation)
return {
"key": val(element.Name),
"Name": val(element.Name),
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -434,7 +434,6 @@ def get_space_data(ifc_file, element):
net_area = str(value)
return {
"key": val(element.Name),
"Name": val(element.Name),
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -458,7 +457,6 @@ def get_zone_data(ifc_file, element):
name, category = zone
history = get_history(ifc_file)
return {
"key": "-".join([str(name), str(category), str(space)]),
"Name": name,
"CreatedBy": get_email_from_history(history) if history else None,
"CreatedOn": ifcopenshell.util.date.ifc2datetime(history.CreationDate).isoformat() if history else None,
@@ -479,7 +477,6 @@ def get_zone_data(ifc_file, element):
space_name = val(space.Name) if space else None
return {
"key": "-".join([str(name), str(category), str(space_name)]),
"Name": name,
"CreatedBy": get_created_by(zone),
"CreatedOn": get_created_on(zone),
@@ -588,7 +585,6 @@ def get_type_data(ifc_file, element):
asset_type = "Moveable"
return {
"key": val(element.Name),
"Name": val(element.Name),
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -664,7 +660,6 @@ def get_component_data(ifc_file, element):
asset_identifier = str(value)
return {
"key": element.Name,
"Name": element.Name,
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -688,7 +683,6 @@ def get_system_data(ifc_file, element):
category = get_category(system)
component_name = val(component.Name)
return {
"key": str(val(system.Name)) + str(category) + str(component_name),
"Name": val(system.Name),
"CreatedBy": get_created_by(system),
"CreatedOn": get_created_on(system),
@@ -723,7 +717,6 @@ def get_assembly_data(ifc_file, element):
history = get_history(ifc_file)
return {
"key": str(name) + str(sheet_name) + str(parent_name),
"Name": name,
"CreatedBy": get_email_from_history(history) if history else None,
"CreatedOn": ifcopenshell.util.date.ifc2datetime(history.CreationDate).isoformat() if history else None,
@@ -749,7 +742,6 @@ def get_connection_data(ifc_file, element):
row_name1 = val(ifcopenshell.util.system.get_port_element(element.RelatingPort).Name)
row_name2 = val(ifcopenshell.util.system.get_port_element(element.RelatedPort).Name)
return {
"key": str(name) + str(connection_type) + str(row_name1) + str(row_name2),
"Name": name,
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -787,7 +779,6 @@ def get_spare_data(ifc_file, element):
part_number = str(value)
return {
"key": val(element),
"Name": val(element.Name),
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -805,7 +796,6 @@ def get_spare_data(ifc_file, element):
def get_resource_data(ifc_file, element):
return {
"key": val(element.Name),
"Name": val(element.Name),
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -866,7 +856,6 @@ def get_job_data(ifc_file, element):
priors = ",".join(priors) if priors else task_number
return {
"key": str(val(element.Name)) + str(type_name) + str(task_number),
"Name": val(element.Name),
"CreatedBy": get_created_by(element),
"CreatedOn": get_created_on(element),
@@ -908,7 +897,6 @@ def get_document_data(ifc_file, element):
sheet_name = get_sheet_name(related_object)
row_name = val(related_object.Name)
return {
"key": str(name) + str(stage) + str(sheet_name) + str(row_name),
"Name": name,
"CreatedBy": get_created_by(rel),
"CreatedOn": get_created_on(rel),
@@ -961,28 +949,23 @@ def get_created_by(element):
def get_email_from_history(element):
pao = element.OwningUser
if pao.is_a("IfcPersonAndOrganization"):
return get_email_from_pao(pao)
return get_email_from_pao(pao.ThePerson, pao.TheOrganization)
elif pao.is_a("IfcPerson"):
return get_email_from_pao(pao, None)
elif pao.is_a("IfcOrganization"):
return get_email_from_pao(None, pao)
def get_email_from_pao(pao):
for address in pao.ThePerson.Addresses or []:
if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses:
return address.ElectronicMailAddresses[0]
def get_email_from_pao(person, organization):
if organization:
for address in organization.Addresses or []:
if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses:
return address.ElectronicMailAddresses[0]
for address in pao.TheOrganization.Addresses or []:
if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses:
return address.ElectronicMailAddresses[0]
person_id = getattr(pao.ThePerson, "Identification", getattr(pao.ThePerson, "Id", None))
if person_id:
return person_id
organization_id = getattr(pao.TheOrganization, "Identification", getattr(pao.TheOrganization, "Id", None))
if organization_id:
return organization_id
if pao.ThePerson.GivenName and pao.ThePerson.FamilyName and pao.TheOrganization.Name:
return pao.ThePerson.GivenName + pao.ThePerson.FamilyName + "@" + pao.TheOrganization.Name + ".com"
if person:
for address in person.Addresses or []:
if address.is_a("IfcTelecomAddress") and address.ElectronicMailAddresses:
return address.ElectronicMailAddresses[0]
def get_owner_name(element):
@@ -1076,8 +1059,10 @@ def get_category(element):
return val(getattr(element, "ObjectType", None))
def get_pao_address(element, name):
for actor in [element.ThePerson, element.TheOrganization]:
def get_pao_address(person, organization, name):
for actor in [organization, person]:
if not actor:
continue
for address in actor.Addresses or []:
if hasattr(address, name) and getattr(address, name, None):
result = getattr(address, name)
@@ -1158,6 +1143,7 @@ config = {
"bool_false": "No",
"categories": {
"Contact": {
"keys": ["Email"],
"headers": [
"Email",
"CreatedBy",
@@ -1185,6 +1171,7 @@ config = {
"get_element_data": get_contact_data,
},
"Facility": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1215,6 +1202,7 @@ config = {
"get_element_data": get_facility_data,
},
"Floor": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1233,6 +1221,7 @@ config = {
"get_element_data": get_floor_data,
},
"Space": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1254,6 +1243,7 @@ config = {
"get_element_data": get_space_data,
},
"Zone": {
"keys": ["Name", "Category", "SpaceNames"],
"headers": [
"Name",
"CreatedBy",
@@ -1271,6 +1261,7 @@ config = {
"get_element_data": get_zone_data,
},
"Type": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1314,6 +1305,7 @@ config = {
"get_element_data": get_type_data,
},
"Component": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1341,6 +1333,7 @@ config = {
"get_element_data": get_component_data,
},
"System": {
"keys": ["Name", "Category", "ComponentNames"],
"headers": [
"Name",
"CreatedBy",
@@ -1358,6 +1351,7 @@ config = {
"get_element_data": get_system_data,
},
"Assembly": { # Note that this is technically "not required"
"keys": ["Name", "SheetName", "ParentName"],
"headers": [
"Name",
"CreatedBy",
@@ -1377,6 +1371,7 @@ config = {
"get_element_data": get_assembly_data,
},
"Connection": { # Note that this is technically "not required"
"keys": ["Name", "ConnectionType", "RowName1", "RowName2"],
"headers": [
"Name",
"CreatedBy",
@@ -1399,6 +1394,7 @@ config = {
"get_element_data": get_connection_data,
},
"Spare": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1419,6 +1415,7 @@ config = {
"get_element_data": get_spare_data,
},
"Resource": {
"keys": ["Name"],
"headers": [
"Name",
"CreatedBy",
@@ -1435,6 +1432,7 @@ config = {
"get_element_data": get_resource_data,
},
"Job": {
"keys": ["Name", "TypeName", "TaskNumber"],
"headers": [
"Name",
"CreatedBy",
@@ -1462,6 +1460,7 @@ config = {
"get_element_data": get_job_data,
},
"Document": {
"keys": ["Name", "Stage", "SheetName", "RowName"],
"headers": [
"Name",
"CreatedBy",
@@ -1485,6 +1484,7 @@ config = {
"get_element_data": get_document_data,
},
"Attribute": {
"keys": ["Name", "SheetName", "RowName"],
"headers": [
"Name",
"CreatedBy",
File diff suppressed because it is too large Load Diff
@@ -82,8 +82,8 @@ class Usecase:
# Create 2 ports, one for either end of both the duct and fitting.
duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting)
fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting)
# Connect the duct and fitting together. At this point, we have not
# yet determined the direction of the flow, so we leave direction as
@@ -52,8 +52,8 @@ class Usecase:
# Create 2 ports, one for either end of both the duct and fitting.
duct_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
duct_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=duct)
fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=duct)
fitting_port1 = ifcopenshell.api.run("system.add_port", model, element=fitting)
fitting_port2 = ifcopenshell.api.run("system.add_port", model, element=fitting)
# Connect the duct and fitting together. At this point, we have not
# yet determined the direction of the flow, so we leave direction as
@@ -1022,7 +1022,13 @@ def copy_deep(ifc_file, element, exclude=None, exclude_callback=None, copied_ent
elif exclude_callback and exclude_callback(attribute):
pass
else:
attribute = copy_deep(ifc_file, attribute, exclude=exclude, copied_entities=copied_entities)
attribute = copy_deep(
ifc_file,
attribute,
exclude=exclude,
copied_entities=copied_entities,
exclude_callback=exclude_callback,
)
elif isinstance(attribute, tuple) and attribute and isinstance(attribute[0], ifcopenshell.entity_instance):
if exclude and any([attribute[0].is_a(e) for e in exclude]):
pass