diff --git a/src/ifcblenderexport/Makefile b/src/ifcblenderexport/Makefile index 6938080a42..476c859c64 100644 --- a/src/ifcblenderexport/Makefile +++ b/src/ifcblenderexport/Makefile @@ -157,12 +157,14 @@ endif cp -r dist/working/*.xsd dist/blenderbim/libs/site/packages/bcfplugin/schemas/ rm -rf dist/working + # Required by IFCCSV and ifcopenshell.util.selector mkdir dist/working cd dist/working && wget https://files.pythonhosted.org/packages/18/4d/8d522136c37d9e1ea74062b41b8d5e1318ebf45063ae46ce72ed60af223b/lark-parser-0.8.5.tar.gz cd dist/working && tar -xzvf lark-parser* cp -r dist/working/lark-parser-0.8.5/lark dist/blenderbim/libs/site/packages/ rm -rf dist/working + # Required by IFCClash ifeq ($(PLATFORM), linux) mkdir dist/working cd dist/working && wget https://files.pythonhosted.org/packages/0c/fa/00d85f893b02289e2942849d97f8818dde8e2111182e825fb3a735677791/python_fcl-0.0.12-cp37-cp37m-manylinux1_x86_64.whl @@ -171,6 +173,7 @@ ifeq ($(PLATFORM), linux) rm -rf dist/working endif + # Required by IFCClash ifeq ($(PLATFORM), win) mkdir dist/working cd dist/working && wget https://files.pythonhosted.org/packages/19/e6/6e9f33fb59e8f27c0e1592bd26e644bc92b85c942b072b2d3854105d5887/python_fcl_win32-0.0.12.post3-py3-none-win_amd64.whl @@ -179,6 +182,7 @@ ifeq ($(PLATFORM), win) rm -rf dist/working endif + # Provides IFCClash functionality cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcclash/collision.py cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcclash/ifcclash.py @@ -203,7 +207,7 @@ endif cd dist/working/parse_type-0.5.2/ && cp -r parse_type ../../blenderbim/libs/site/packages/ rm -rf dist/working - # Supplies BIMTester functionality + # Provides BIMTester functionality cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/bimtester.py mkdir dist/blenderbim/libs/site/packages/features/ mkdir dist/blenderbim/libs/site/packages/features/steps/ @@ -211,7 +215,34 @@ endif cd dist/blenderbim/libs/site/packages/features/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/template.html cd dist/blenderbim/libs/site/packages/features/steps/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcbimtester/features/steps/steps.py + # Required by IFCCOBie for XLSX support + mkdir dist/working + cd dist/working && wget https://files.pythonhosted.org/packages/0c/bc/82d6783f83f65f56d8b77d052773c4a2f952fa86385f0cd54e1e006658d7/XlsxWriter-1.2.9.tar.gz + cd dist/working && tar -xzvf XlsxWriter* + cd dist/working/XlsxWriter-1.2.9/ && cp -r xlsxwriter ../../blenderbim/libs/site/packages/ + rm -rf dist/working + + # Required by IFCCOBie for ODS support + mkdir dist/working + cd dist/working && wget https://files.pythonhosted.org/packages/97/73/8ade73f6749177003f7ce3304f524774adda96e6aaab30ea79fd8fda7934/odfpy-1.4.1.tar.gz + cd dist/working && tar -xzvf odfpy* + cd dist/working/odfpy-1.4.1/ && cp -r odf ../../blenderbim/libs/site/packages/ + rm -rf dist/working + + # Required by odfpy + mkdir dist/working + cd dist/working && wget https://files.pythonhosted.org/packages/a4/5f/f8aa58ca0cf01cbcee728abc9d88bfeb74e95e6cb4334cfd5bed5673ea77/defusedxml-0.6.0.tar.gz + cd dist/working && tar -xzvf defusedxml* + cd dist/working/defusedxml-0.6.0/ && cp -r defusedxml ../../blenderbim/libs/site/packages/ + rm -rf dist/working + + # Provides IFCCOBie functionality + cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifccobie/cobie.py + + # Provides IFCDiff functionality cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifcdiff/ifcdiff.py + + # Provides IFCCSV functionality cd dist/blenderbim/libs/site/packages/ && wget https://raw.githubusercontent.com/IfcOpenShell/IfcOpenShell/v0.6.0/src/ifccsv/ifccsv.py cd dist/blenderbim && sed -i "s/999999/$(VERSION)/" __init__.py diff --git a/src/ifcblenderexport/blenderbim/bim/__init__.py b/src/ifcblenderexport/blenderbim/bim/__init__.py index 256f699259..15bffab43c 100644 --- a/src/ifcblenderexport/blenderbim/bim/__init__.py +++ b/src/ifcblenderexport/blenderbim/bim/__init__.py @@ -163,6 +163,8 @@ if bpy is not None: operator.GuessQuantity, operator.ExecuteBIMTester, operator.BIMTesterPurge, + operator.SelectCobieIfcFile, + operator.ExecuteIfcCobie, prop.StrProperty, prop.Variable, prop.Role, @@ -222,6 +224,7 @@ if bpy is not None: ui.BIM_PT_library, ui.BIM_PT_gis, ui.BIM_PT_diff, + ui.BIM_PT_cobie, ui.BIM_PT_mvd, ui.BIM_PT_material, ui.BIM_PT_mesh, diff --git a/src/ifcblenderexport/blenderbim/bim/operator.py b/src/ifcblenderexport/blenderbim/bim/operator.py index bbb65f5c87..7bd939fc75 100644 --- a/src/ifcblenderexport/blenderbim/bim/operator.py +++ b/src/ifcblenderexport/blenderbim/bim/operator.py @@ -672,7 +672,7 @@ class OpenBcfBimSnippetReference(bpy.types.Operator): if bpy.context.scene.BCFProperties.topic_snippet_is_external: webbrowser.open(bpy.context.scene.BCFProperties.topic_snippet_reference) return {'FINISHED'} - webbrowser.open('file:///' + os.path.join( + webbrowser.open('file://' + os.path.join( bcfplugin.util.getBcfDir(), self.topic_guid, bpy.context.scene.BCFProperties.topic_snippet_reference @@ -693,7 +693,7 @@ class OpenBcfDocumentReference(bpy.types.Operator): if doc.is_external: webbrowser.open(uri) return {'FINISHED'} - webbrowser.open('file:///' + os.path.join( + webbrowser.open('file://' + os.path.join( bcfplugin.util.getBcfDir(), topic_guid, uri)) return {'FINISHED'} @@ -1289,6 +1289,55 @@ class SelectExternalMaterialDir(bpy.types.Operator): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} + +class SelectCobieIfcFile(bpy.types.Operator): + bl_idname = "bim.select_cobie_ifc_file" + bl_label = "Select COBie IFC File" + filepath: bpy.props.StringProperty(subtype="FILE_PATH") + + def execute(self, context): + bpy.context.scene.BIMProperties.cobie_ifc_file = self.filepath + return {'FINISHED'} + + def invoke(self, context, event): + context.window_manager.fileselect_add(self) + return {'RUNNING_MODAL'} + + +class ExecuteIfcCobie(bpy.types.Operator): + bl_idname = 'bim.execute_ifc_cobie' + bl_label = 'Execute IFCCOBie' + file_format: bpy.props.StringProperty() + + def execute(self, context): + from cobie import IfcCobieParser + output_dir = os.path.dirname(bpy.context.scene.BIMProperties.cobie_ifc_file) + output = os.path.join(output_dir, 'output') + log = os.path.join(output_dir, 'cobie.log') + + logging.basicConfig( + filename=log, filemode='a', level=logging.DEBUG) + logger = logging.getLogger('IFCtoCOBie') + parser = IfcCobieParser(logger) + parser.parse(bpy.context.scene.BIMProperties.cobie_ifc_file) + if self.file_format == 'xlsx': + from cobie import CobieXlsWriter + writer = CobieXlsWriter(parser, output) + writer.write() + webbrowser.open('file://' + output + '.' + self.file_format) + elif self.file_format == 'ods': + from cobie import CobieOdsWriter + writer = CobieOdsWriter(parser, output) + writer.write() + webbrowser.open('file://' + output + '.' + self.file_format) + else: + from cobie import CobieCsvWriter + writer = CobieCsvWriter(parser, output_dir) + writer.write() + webbrowser.open('file://' + output_dir) + return {'FINISHED'} + + class SelectDiffJsonFile(bpy.types.Operator): bl_idname = "bim.select_diff_json_file" bl_label = "Select Diff JSON File" @@ -1947,7 +1996,7 @@ class OpenView(bpy.types.Operator): bl_label = 'Open View' def execute(self, context): - webbrowser.open('file:///' + os.path.join( + webbrowser.open('file://' + os.path.join( bpy.context.scene.BIMProperties.data_dir, 'diagrams', bpy.context.scene.DocProperties.available_views + '.svg')) return {'FINISHED'} @@ -2094,7 +2143,7 @@ class OpenSheet(bpy.types.Operator): bl_label = 'Open Sheet' def execute(self, context): - webbrowser.open('file:///' + os.path.join( + webbrowser.open('file://' + os.path.join( bpy.context.scene.BIMProperties.data_dir, 'sheets', bpy.context.scene.DocProperties.available_sheets + '.svg')) return {'FINISHED'} @@ -2105,7 +2154,7 @@ class OpenCompiledSheet(bpy.types.Operator): bl_label = 'Open Compiled Sheet' def execute(self, context): - webbrowser.open('file:///' + os.path.join( + webbrowser.open('file://' + os.path.join( bpy.context.scene.BIMProperties.data_dir, 'build', bpy.context.scene.DocProperties.available_sheets, bpy.context.scene.DocProperties.available_sheets + '.svg')) diff --git a/src/ifcblenderexport/blenderbim/bim/prop.py b/src/ifcblenderexport/blenderbim/bim/prop.py index c121824e24..4f207ca36c 100644 --- a/src/ifcblenderexport/blenderbim/bim/prop.py +++ b/src/ifcblenderexport/blenderbim/bim/prop.py @@ -1032,6 +1032,7 @@ class BIMProperties(PropertyGroup): features_dir: StringProperty(default='', name="Features Directory", update=refreshFeaturesFiles) features_file: EnumProperty(items=getFeaturesFiles, name="Features File", update=refreshScenarios) scenario: EnumProperty(items=getScenarios, name="Scenario") + cobie_ifc_file: StringProperty(default='', name="COBie IFC File") diff_json_file: StringProperty(default='', name="Diff JSON File") diff_old_file: StringProperty(default='', name="Diff Old IFC File") diff_new_file: StringProperty(default='', name="Diff New IFC File") diff --git a/src/ifcblenderexport/blenderbim/bim/ui.py b/src/ifcblenderexport/blenderbim/bim/ui.py index 2ccfac69d7..a8601f567d 100644 --- a/src/ifcblenderexport/blenderbim/bim/ui.py +++ b/src/ifcblenderexport/blenderbim/bim/ui.py @@ -1403,6 +1403,7 @@ class BIM_PT_library(Panel): layout.row().prop(scene.BIMLibrary, 'version_date') layout.row().prop(scene.BIMLibrary, 'description') + class BIM_PT_diff(Panel): bl_label = "IFC Diff" bl_idname = "BIM_PT_diff" @@ -1439,6 +1440,34 @@ class BIM_PT_diff(Panel): row.operator('bim.execute_ifc_diff') +class BIM_PT_cobie(Panel): + bl_label = "IFC COBie" + bl_idname = "BIM_PT_cobie" + bl_options = {'DEFAULT_CLOSED'} + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "scene" + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + + scene = context.scene + props = scene.BIMProperties + + row = layout.row(align=True) + row.prop(props, "cobie_ifc_file") + row.operator("bim.select_cobie_ifc_file", icon="FILE_FOLDER", text="") + + row = layout.row() + op = row.operator('bim.execute_ifc_cobie', text='CSV') + op.file_format = 'csv' + op = row.operator('bim.execute_ifc_cobie', text='ODS') + op.file_format = 'ods' + op = row.operator('bim.execute_ifc_cobie', text='XLSX') + op.file_format = 'xlsx' + + class BIM_PT_mvd(Panel): bl_label = "Model View Definitions (MVD)" bl_idname = "BIM_PT_mvd" diff --git a/src/ifccobie/cobie.py b/src/ifccobie/cobie.py index b4a8d0c0ed..dfb2032eb0 100644 --- a/src/ifccobie/cobie.py +++ b/src/ifccobie/cobie.py @@ -1,5 +1,6 @@ # This can be packaged with `pyinstaller --onefile --clean --icon=icon.ico bimtester.py` +import os import time import argparse import datetime @@ -1095,20 +1096,12 @@ class CobieCsvWriter(CobieWriter): import csv super().write() for sheet, data in self.sheet_data.items(): - with open('{}.csv'.format(sheet), 'w', newline='', encoding='utf-8') as file: + with open(os.path.join(self.filename, '{}.csv'.format(sheet)), 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) writer.writerow(data['headers']) for row in data['rows']: writer.writerow(row) - def write_file_old(self, csv_name, data, primary_key, fieldnames): - with open('{}.csv'.format(csv_name), 'w', newline='', encoding='utf-8') as file: - writer = csv.DictWriter(file, fieldnames=fieldnames) - writer.writeheader() - for name, row in data.items(): - row[primary_key] = name - writer.writerow(row) - class CobieXlsWriter(CobieWriter): def write(self): @@ -1187,6 +1180,7 @@ if __name__ == '__main__': parser = argparse.ArgumentParser( description='Converts COBie IFC MVD into its spreadsheet equivalent') parser.add_argument('input', type=str, help='Specify an IFC file to process') + parser.add_argument('output', type=str, help='The output directory for CSV or filename for other formats') parser.add_argument( '-l', '--log', @@ -1199,12 +1193,6 @@ if __name__ == '__main__': type=str, help='Choose which format to export in (csv/xlsx)', default='csv') - parser.add_argument( - '-o', - '--output', - type=str, - help='The output filename excluding the extension. Not required for CSV.', - default='output') args = vars(parser.parse_args()) print('Processing IFC file ...') @@ -1225,7 +1213,7 @@ if __name__ == '__main__': elif args['format'] == 'ods': writer = CobieOdsWriter(parser, args['output']) else: - writer = CobieCsvWriter(parser) + writer = CobieCsvWriter(parser, args['output']) writer.write() logger.info('Finished conversion in {}s'.format(time.time() - start))