mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-10 01:41:57 +00:00
You can now export Cost Schedules as CSV, ODS, XSLX with BlenderBIM or using the cli app
This commit is contained in:
@@ -30,6 +30,7 @@ classes = (
|
|||||||
operator.EnableEditingCostSchedule,
|
operator.EnableEditingCostSchedule,
|
||||||
operator.EnableEditingCostItems,
|
operator.EnableEditingCostItems,
|
||||||
operator.EnableEditingCostItem,
|
operator.EnableEditingCostItem,
|
||||||
|
operator.ExportCostSchedules,
|
||||||
operator.ExpandCostItems,
|
operator.ExpandCostItems,
|
||||||
operator.EnableEditingCostItemQuantities,
|
operator.EnableEditingCostItemQuantities,
|
||||||
operator.EnableEditingCostItemQuantity,
|
operator.EnableEditingCostItemQuantity,
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import ifcopenshell
|
|||||||
import ifcopenshell.util.cost
|
import ifcopenshell.util.cost
|
||||||
import ifcopenshell.util.element
|
import ifcopenshell.util.element
|
||||||
import blenderbim.tool as tool
|
import blenderbim.tool as tool
|
||||||
|
import blenderbim.bim.schema
|
||||||
|
from ifcopenshell.util.doc import get_entity_doc, get_predefined_type_doc
|
||||||
|
|
||||||
|
|
||||||
def refresh():
|
def refresh():
|
||||||
@@ -36,6 +38,8 @@ class CostSchedulesData:
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls):
|
def load(cls):
|
||||||
cls.data = {
|
cls.data = {
|
||||||
|
"predefined_types": cls.get_cost_schedule_types(),
|
||||||
|
"total_cost_schedules": cls.total_cost_schedules(),
|
||||||
"schedules": cls.schedules(),
|
"schedules": cls.schedules(),
|
||||||
"is_editing_rates": cls.is_editing_rates(),
|
"is_editing_rates": cls.is_editing_rates(),
|
||||||
"cost_items": cls.cost_items(),
|
"cost_items": cls.cost_items(),
|
||||||
@@ -45,6 +49,10 @@ class CostSchedulesData:
|
|||||||
}
|
}
|
||||||
cls.is_loaded = True
|
cls.is_loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def total_cost_schedules(cls):
|
||||||
|
return len(tool.Ifc.get().by_type("IfcCostSchedule"))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def schedules(cls):
|
def schedules(cls):
|
||||||
results = []
|
results = []
|
||||||
@@ -212,6 +220,21 @@ class CostSchedulesData:
|
|||||||
for t in tool.Ifc.schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
|
for t in tool.Ifc.schema().declaration_by_name("IfcPhysicalSimpleQuantity").subtypes()
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_cost_schedule_types(cls):
|
||||||
|
results = []
|
||||||
|
declaration = tool.Ifc().schema().declaration_by_name("IfcCostSchedule")
|
||||||
|
version = tool.Ifc.get_schema()
|
||||||
|
for attribute in declaration.attributes():
|
||||||
|
if attribute.name() == "PredefinedType":
|
||||||
|
results.extend(
|
||||||
|
[
|
||||||
|
(e, e, get_predefined_type_doc(version, "IfcCostSchedule", e))
|
||||||
|
for e in attribute.type_of_attribute().declared_type().enumeration_items()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
break
|
||||||
|
return results
|
||||||
|
|
||||||
class CostItemRatesData:
|
class CostItemRatesData:
|
||||||
data = {}
|
data = {}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class AddCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
bl_description = "Add a new cost schedule"
|
bl_description = "Add a new cost schedule"
|
||||||
|
|
||||||
def _execute(self, context):
|
def _execute(self, context):
|
||||||
core.add_cost_schedule(tool.Ifc)
|
core.add_cost_schedule(tool.Ifc, predefined_type = context.scene.BIMCostProperties.cost_schedule_predefined_types)
|
||||||
|
|
||||||
|
|
||||||
class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
class EditCostSchedule(bpy.types.Operator, tool.Ifc.Operator):
|
||||||
@@ -328,7 +328,7 @@ class DisableEditingCostItemQuantity(bpy.types.Operator):
|
|||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
bl_description = "Disable editing cost item quantity"
|
bl_description = "Disable editing cost item quantity"
|
||||||
|
|
||||||
def _execute(self, context):
|
def execute(self, context):
|
||||||
core.disable_editing_cost_item_quantity(tool.Cost)
|
core.disable_editing_cost_item_quantity(tool.Cost)
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
@@ -586,3 +586,22 @@ class CalculateCostItemResourceValue(bpy.types.Operator, tool.Ifc.Operator):
|
|||||||
core.calculate_cost_item_resource_value(tool.Ifc, cost_item=tool.Ifc.get().by_id(self.cost_item))
|
core.calculate_cost_item_resource_value(tool.Ifc, cost_item=tool.Ifc.get().by_id(self.cost_item))
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
|
class ExportCostSchedules(bpy.types.Operator):
|
||||||
|
bl_idname = "bim.export_cost_schedules"
|
||||||
|
bl_label = "Export Cost Schedule"
|
||||||
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
bl_description = "Export a cost schedule to a CSV, XSLX OR ODS file"
|
||||||
|
format: bpy.props.EnumProperty("Format", items=(("CSV", "CSV", ""), ("XLSX", "XLSX", ""), ("ODS", "ODS", "")))
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
core.export_cost_schedules(tool.Cost, format=self.format)
|
||||||
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
def invoke(self, context, event):
|
||||||
|
wm = context.window_manager
|
||||||
|
return wm.invoke_props_dialog(self)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
self.layout.label(text="Choose a format")
|
||||||
|
self.layout.prop(self, "format")
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ def update_cost_item_name(self, context):
|
|||||||
attribute.string_value = self.name
|
attribute.string_value = self.name
|
||||||
|
|
||||||
|
|
||||||
|
def get_schedule_predefined_types(self, context):
|
||||||
|
if not CostSchedulesData.is_loaded:
|
||||||
|
CostSchedulesData.load()
|
||||||
|
return CostSchedulesData.data["predefined_types"]
|
||||||
|
|
||||||
|
|
||||||
class CostItem(PropertyGroup):
|
class CostItem(PropertyGroup):
|
||||||
name: StringProperty(name="Name", update=update_cost_item_name)
|
name: StringProperty(name="Name", update=update_cost_item_name)
|
||||||
identification: StringProperty(name="Identification", update=update_cost_item_identification)
|
identification: StringProperty(name="Identification", update=update_cost_item_identification)
|
||||||
@@ -130,6 +136,9 @@ class CostItemType(PropertyGroup):
|
|||||||
|
|
||||||
|
|
||||||
class BIMCostProperties(PropertyGroup):
|
class BIMCostProperties(PropertyGroup):
|
||||||
|
cost_schedule_predefined_types: EnumProperty(
|
||||||
|
items=get_schedule_predefined_types, name="Predefined Type", default=None
|
||||||
|
)
|
||||||
is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True)
|
is_cost_update_enabled: BoolProperty(name="Is Cost Update Enabled", default=True)
|
||||||
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
|
cost_schedule_attributes: CollectionProperty(name="Cost Schedule Attributes", type=Attribute)
|
||||||
is_editing: StringProperty(name="Is Editing")
|
is_editing: StringProperty(name="Is Editing")
|
||||||
|
|||||||
@@ -42,9 +42,16 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
CostSchedulesData.load()
|
CostSchedulesData.load()
|
||||||
|
|
||||||
self.props = context.scene.BIMCostProperties
|
self.props = context.scene.BIMCostProperties
|
||||||
|
row = self.layout.row()
|
||||||
|
if CostSchedulesData.data["total_cost_schedules"]:
|
||||||
|
row.label(text=f"{CostSchedulesData.data['total_cost_schedules']} Cost Schedules Found", icon="TEXT")
|
||||||
|
row.operator("bim.export_cost_schedules", text="Export as spreadsheet", icon="EXPORT")
|
||||||
|
else:
|
||||||
|
row.label(text="No Cost Schedules Found found.", icon="COMMUNITY")
|
||||||
|
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
row.operator("bim.add_cost_schedule", icon="ADD")
|
row.prop(self.props, "cost_schedule_predefined_types")
|
||||||
|
row.operator("bim.add_cost_schedule", icon="ADD", text="Add new")
|
||||||
|
|
||||||
for schedule in CostSchedulesData.data["schedules"]:
|
for schedule in CostSchedulesData.data["schedules"]:
|
||||||
self.draw_cost_schedule_ui(schedule)
|
self.draw_cost_schedule_ui(schedule)
|
||||||
@@ -57,7 +64,6 @@ class BIM_PT_cost_schedules(Panel):
|
|||||||
op = row.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="")
|
op = row.operator("bim.select_cost_schedule_products", icon="RESTRICT_SELECT_OFF", text="")
|
||||||
op.cost_schedule = cost_schedule["id"]
|
op.cost_schedule = cost_schedule["id"]
|
||||||
row.prop(self.props, "should_show_column_ui", text="", icon="SHORTDISPLAY")
|
row.prop(self.props, "should_show_column_ui", text="", icon="SHORTDISPLAY")
|
||||||
row.operator("bim.export_cost_schedule", text="", icon="EXPORT").cost_schedule = cost_schedule["id"]
|
|
||||||
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
|
if self.props.is_editing == "COST_SCHEDULE_ATTRIBUTES":
|
||||||
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
|
row.operator("bim.edit_cost_schedule", text="", icon="CHECKMARK")
|
||||||
elif self.props.is_editing == "COST_ITEMS":
|
elif self.props.is_editing == "COST_ITEMS":
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
def add_cost_schedule(ifc):
|
def add_cost_schedule(ifc, predefined_type):
|
||||||
ifc.run("cost.add_cost_schedule")
|
ifc.run("cost.add_cost_schedule", predefined_type=predefined_type)
|
||||||
|
|
||||||
|
|
||||||
def edit_cost_schedule(ifc, cost, cost_schedule):
|
def edit_cost_schedule(ifc, cost, cost_schedule):
|
||||||
@@ -209,4 +209,7 @@ def contract_cost_item_rate(cost, cost_item):
|
|||||||
cost.contract_cost_item_rate(cost_item)
|
cost.contract_cost_item_rate(cost_item)
|
||||||
|
|
||||||
def calculate_cost_item_resource_value(ifc, cost_item):
|
def calculate_cost_item_resource_value(ifc, cost_item):
|
||||||
ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item)
|
ifc.run("cost.calculate_cost_item_resource_value", cost_item=cost_item)
|
||||||
|
|
||||||
|
def export_cost_schedules(cost, format):
|
||||||
|
cost.export_cost_schedules(format)
|
||||||
@@ -176,8 +176,8 @@ class Cost:
|
|||||||
def load_cost_schedule_tree(cls): pass
|
def load_cost_schedule_tree(cls): pass
|
||||||
def load_schedule_of_rates_tree(cls, schedule_of_rates): pass
|
def load_schedule_of_rates_tree(cls, schedule_of_rates): pass
|
||||||
def play_chaching_sound(cls): pass
|
def play_chaching_sound(cls): pass
|
||||||
def print_all_cost_items(cls, cost_schedule): pass
|
|
||||||
def remove_cost_column(cls, name): pass
|
def remove_cost_column(cls, name): pass
|
||||||
|
def export_cost_schedules(cls, format): pass
|
||||||
|
|
||||||
@interface
|
@interface
|
||||||
class Debug:
|
class Debug:
|
||||||
|
|||||||
@@ -279,9 +279,7 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
prop.float_value = (
|
prop.float_value = (
|
||||||
0.0
|
0.0
|
||||||
if prop.is_null
|
if prop.is_null
|
||||||
else ifcopenshell.util.cost.calculate_applied_value(
|
else cls.calculate_applied_value(tool.Ifc.get().by_id(props.active_cost_item_id), cost_value)
|
||||||
tool.Ifc.get().by_id(props.active_cost_item_id), cost_value
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
if name == "UnitBasis" and is_rates:
|
if name == "UnitBasis" and is_rates:
|
||||||
@@ -299,33 +297,11 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
prop.name = "UnitBasisUnit"
|
prop.name = "UnitBasisUnit"
|
||||||
prop.data_type = "enum"
|
prop.data_type = "enum"
|
||||||
prop.is_null = prop.is_optional = False
|
prop.is_null = prop.is_optional = False
|
||||||
units = {}
|
units = cls.get_units()
|
||||||
|
|
||||||
def format_unit(unit):
|
|
||||||
if unit.is_a("IfcContextDependentUnit"):
|
|
||||||
return f"{unit.UnitType} / {unit.Name}"
|
|
||||||
else:
|
|
||||||
name = unit.Name
|
|
||||||
if unit.get_info().get("Prefix", None):
|
|
||||||
name = f"{unit.Prefix} {name}"
|
|
||||||
return f"{unit.UnitType} / {name}"
|
|
||||||
|
|
||||||
for unit in tool.Ifc.get().by_type("IfcNamedUnit"):
|
|
||||||
if unit.get_info().get("UnitType", None) in [
|
|
||||||
"AREAUNIT",
|
|
||||||
"LENGTHUNIT",
|
|
||||||
"TIMEUNIT",
|
|
||||||
"VOLUMEUNIT",
|
|
||||||
"MASSUNIT",
|
|
||||||
"USERDEFINED",
|
|
||||||
]:
|
|
||||||
units[unit.id()] = format_unit(unit)
|
|
||||||
prop.enum_items = json.dumps(units)
|
prop.enum_items = json.dumps(units)
|
||||||
if data["UnitBasis"] and data["UnitBasis"].UnitComponent:
|
if data["UnitBasis"] and data["UnitBasis"].UnitComponent:
|
||||||
# prop.enum_value = prop.enum_items[data["UnitBasis"].UnitComponent.id()]
|
|
||||||
print(prop.enum_items)
|
|
||||||
for key, value in json.loads(prop.enum_items).items():
|
for key, value in json.loads(prop.enum_items).items():
|
||||||
if value == format_unit(data["UnitBasis"].UnitComponent):
|
if value == cls.format_unit(data["UnitBasis"].UnitComponent):
|
||||||
prop.enum_value = key
|
prop.enum_value = key
|
||||||
break
|
break
|
||||||
return True
|
return True
|
||||||
@@ -384,7 +360,6 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
"ValueComponent": prop.float_value or 1,
|
"ValueComponent": prop.float_value or 1,
|
||||||
"UnitComponent": cls.get_cost_value_unit_component(),
|
"UnitComponent": cls.get_cost_value_unit_component(),
|
||||||
}
|
}
|
||||||
print(attributes)
|
|
||||||
return True
|
return True
|
||||||
if prop.name == "UnitBasisUnit":
|
if prop.name == "UnitBasisUnit":
|
||||||
return True
|
return True
|
||||||
@@ -424,9 +399,6 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
def get_nested_cost_items(cls, cost_item):
|
def get_nested_cost_items(cls, cost_item):
|
||||||
return [obj for rel in cost_item.IsNestedBy for obj in rel.RelatedObjects]
|
return [obj for rel in cost_item.IsNestedBy for obj in rel.RelatedObjects]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def print_all_cost_items(cls, cost_schedule):
|
|
||||||
print(cls.get_schedule_cost_items(cost_schedule))
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_schedule_cost_items(cls, cost_schedule):
|
def get_schedule_cost_items(cls, cost_schedule):
|
||||||
@@ -526,3 +498,25 @@ class Cost(blenderbim.core.tool.Cost):
|
|||||||
for cost_item in rel.RelatedObjects
|
for cost_item in rel.RelatedObjects
|
||||||
]
|
]
|
||||||
props.is_cost_update_enabled = True
|
props.is_cost_update_enabled = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def export_cost_schedules(cls, format=None):
|
||||||
|
path = os.path.join(bpy.context.scene.BIMProperties.data_dir, "build", "cost_schedules")
|
||||||
|
if format == "CSV":
|
||||||
|
from ifc5d.ifc5Dspreadsheet import Ifc5DCsvWriter
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
writer = Ifc5DCsvWriter(file=tool.Ifc.get(), output=path)
|
||||||
|
writer.write()
|
||||||
|
elif format == "ODS":
|
||||||
|
from ifc5d.ifc5Dspreadsheet import Ifc5DOdsWriter
|
||||||
|
|
||||||
|
writer = Ifc5DOdsWriter(file=tool.Ifc.get(), output=path)
|
||||||
|
writer.write()
|
||||||
|
elif format == "XLSX":
|
||||||
|
from ifc5d.ifc5Dspreadsheet import Ifc5DXlsxWriter
|
||||||
|
|
||||||
|
writer = Ifc5DXlsxWriter(file=tool.Ifc.get(), output=path + ".xlsx")
|
||||||
|
writer.write()
|
||||||
|
|
||||||
|
|||||||
@@ -379,3 +379,29 @@ Scenario: Select cost item products
|
|||||||
And I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
|
And I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
|
||||||
When I press "bim.select_cost_item_products(cost_item={cost_item})"
|
When I press "bim.select_cost_item_products(cost_item={cost_item})"
|
||||||
Then nothing happens
|
Then nothing happens
|
||||||
|
|
||||||
|
Scenario: Select Cost Schedule Products
|
||||||
|
Given an empty IFC project
|
||||||
|
And I press "bim.add_cost_schedule"
|
||||||
|
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
|
||||||
|
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
|
||||||
|
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
|
||||||
|
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||||
|
And I add a cube
|
||||||
|
And the object "Cube" is selected
|
||||||
|
And I set "scene.BIMRootProperties.ifc_class" to "IfcWall"
|
||||||
|
And I press "bim.assign_class"
|
||||||
|
And the object "IfcWall/Cube" is selected
|
||||||
|
And I press "bim.assign_cost_item_quantity(cost_item={cost_item}, related_object_type='PRODUCT', prop_name='')"
|
||||||
|
When I press "bim.select_cost_schedule_products(cost_schedule={cost_schedule})"
|
||||||
|
Then nothing happens
|
||||||
|
Scenario: Load Cost Item Types
|
||||||
|
Given an empty IFC project
|
||||||
|
And I press "bim.add_cost_schedule"
|
||||||
|
And the variable "cost_schedule" is "{ifc}.by_type('IfcCostSchedule')[0].id()"
|
||||||
|
And I press "bim.enable_editing_cost_items(cost_schedule={cost_schedule})"
|
||||||
|
And I press "bim.add_summary_cost_item(cost_schedule={cost_schedule})"
|
||||||
|
And the variable "cost_item" is "{ifc}.by_type('IfcCostItem')[0].id()"
|
||||||
|
When I press "bim.add_cost_item(cost_item={cost_item})"
|
||||||
|
And I press "bim.load_cost_item_types"
|
||||||
|
Then nothing happens
|
||||||
|
|||||||
+42
-3
@@ -1,18 +1,57 @@
|
|||||||
# ifc5d
|
# ifc5d
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
Ifc5D is a collection of utilities of manipulating cost-related data to and from
|
Ifc5D is a collection of utilities of manipulating cost-related data to and from
|
||||||
formats, reports, and optimisation engines.
|
formats, reports, and optimisation engines.
|
||||||
|
|
||||||
Currently supported:
|
Currently supported:
|
||||||
|
|
||||||
- CSV to IFC
|
- CSV to IFC
|
||||||
|
- IFC to XLSX
|
||||||
|
- IFC to CSV
|
||||||
|
- IFC to ODS
|
||||||
|
|
||||||
|
|
||||||
Planned (would you like to contribute? Please reach out!):
|
Planned (would you like to contribute? Please reach out!):
|
||||||
|
|
||||||
- IFC to CSV
|
|
||||||
- IFC to PDF
|
- IFC to PDF
|
||||||
- IFC to ODS
|
|
||||||
- IFC to XLSX
|
|
||||||
- ODS to CSV
|
- ODS to CSV
|
||||||
- XLSX to CSV
|
- XLSX to CSV
|
||||||
- IFC to Graph
|
- IFC to Graph
|
||||||
|
|
||||||
|
## Usage CSV to IFC
|
||||||
|
|
||||||
|
## Usage IFC to CSV, ODS, XSLS
|
||||||
|
|
||||||
|
### CLI app for converting IFC files to CSV, ODS or XLSX format.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python ifc5Dspreadsheet.py input_file output_file [-l log_file] [-f format_type]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
input_file (str): The path to the input IFC file to process.
|
||||||
|
output_file (str): The output directory for CSV or filename for other formats.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-l, --log log_file (str): The path to the file where errors should be logged. Default is process.log.
|
||||||
|
-f, --format format_type (str): The output format to export in (csv/ods/xlsx). Default is csv.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
python ifc5Dspreadsheet.py "C:\Users\Username\Desktop\test_cost.ifc" rev_01_schedule -l error.log -f ods
|
||||||
|
python ifc5Dspreadsheet.py "C:\Users\Dev-Machine\Desktop\test_cost.ifc" "C:\Users\Dev-Machine\Desktop" -l error.log -f csv
|
||||||
|
### Scripting:
|
||||||
|
|
||||||
|
Example for ODS exports:
|
||||||
|
"""
|
||||||
|
import ifcopenshell
|
||||||
|
from ifc5d.ifc5Dspreadsheet import Ifc5DOdsWriter
|
||||||
|
|
||||||
|
file = "path_to_file/file.ifc"
|
||||||
|
|
||||||
|
path = "directory/cost_schedule"
|
||||||
|
writer = Ifc5DOdsWriter(file=file, output=path)
|
||||||
|
writer.write()
|
||||||
|
|
||||||
|
"""
|
||||||
Reference in New Issue
Block a user