Fix unfinished time/date/duration handling in edit task time, work plan, work schedule, and finally refactor attribute i/o like I promised

This commit is contained in:
Dion Moult
2021-05-06 17:14:28 +10:00
parent a13b257452
commit 8a3e023408
6 changed files with 177 additions and 403 deletions
+54 -145
View File
@@ -1,153 +1,62 @@
import bpy
import json
import math import math
import ifcopenshell
import ifcopenshell.util.attribute
from mathutils import geometry from mathutils import geometry
from mathutils import Vector from mathutils import Vector
import bpy from blenderbim.bim.ifc import IfcStore
# TODO: Deprecate this in favour of ifcopenshell.util.unit def import_attributes(ifc_class, props, data, callback=None):
for attribute in IfcStore.get_schema().declaration_by_name(ifc_class).all_attributes():
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
is_handled_by_callback = callback(attribute.name(), new, data) if callback else False
if is_handled_by_callback:
pass # Our job is done
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class SIUnitHelper: def export_attributes(props, callback=None):
prefixes = { attributes = {}
"EXA": 1e18, for attribute in props:
"PETA": 1e15, is_handled_by_callback = callback(attributes, attribute) if callback else False
"TERA": 1e12, if attribute.is_null:
"GIGA": 1e9, attributes[attribute.name] = None
"MEGA": 1e6, elif is_handled_by_callback:
"KILO": 1e3, pass # Our job is done
"HECTO": 1e2, elif attribute.data_type == "string":
"DECA": 1e1, attributes[attribute.name] = attribute.string_value
"DECI": 1e-1, elif attribute.data_type == "boolean":
"CENTI": 1e-2, attributes[attribute.name] = attribute.bool_value
"MILLI": 1e-3, elif attribute.data_type == "integer":
"MICRO": 1e-6, attributes[attribute.name] = attribute.int_value
"NANO": 1e-9, elif attribute.data_type == "float":
"PICO": 1e-12, attributes[attribute.name] = attribute.float_value
"FEMTO": 1e-15, elif attribute.data_type == "enum":
"ATTO": 1e-18, attributes[attribute.name] = attribute.enum_value
} return attributes
unit_names = [
"AMPERE",
"BECQUEREL",
"CANDELA",
"COULOMB",
"CUBIC_METRE",
"DEGREE CELSIUS",
"FARAD",
"GRAM",
"GRAY",
"HENRY",
"HERTZ",
"JOULE",
"KELVIN",
"LUMEN",
"LUX",
"MOLE",
"NEWTON",
"OHM",
"PASCAL",
"RADIAN",
"SECOND",
"SIEMENS",
"SIEVERT",
"SQUARE METRE",
"METRE",
"STERADIAN",
"TESLA",
"VOLT",
"WATT",
"WEBER",
]
si_conversions = {
"inch": 0.0254,
"foot": 0.3048,
"yard": 0.914,
"mile": 1609,
"square inch": 0.0006452,
"square foot": 0.09290304,
"square yard": 0.83612736,
"acre": 4046.86,
"square mile": 2588881,
"cubic inch": 0.00001639,
"cubic foot": 0.02831684671168849,
"cubic yard": 0.7636,
"litre": 0.001,
"fluid ounce UK": 0.0000284130625,
"fluid ounce US": 0.00002957353,
"pint UK": 0.000568,
"pint US": 0.000473,
"gallon UK": 0.004546,
"gallon US": 0.003785,
"degree": math.pi / 180,
"ounce": 0.02835,
"pound": 0.454,
"ton UK": 1016.0469088,
"ton US": 907.18474,
"lbf": 4.4482216153,
"kip": 4448.2216153,
"psi": 6894.7572932,
"ksi": 6894757.2932,
"minute": 60,
"hour": 3600,
"day": 86400,
"btu": 1055.056,
}
@staticmethod
def get_prefix(text):
for prefix in SIUnitHelper.prefixes.keys():
if prefix in text.upper():
return prefix
@staticmethod # TODO: migrate the below helper functions into the drawing module, since it is specific to that module
def get_prefix_multiplier(text):
if not text:
return 1
prefix = SIUnitHelper.get_prefix(text)
if prefix:
return SIUnitHelper.prefixes[prefix]
return 1
@staticmethod
def get_unit_name(text):
for name in SIUnitHelper.unit_names:
if name in text.upper().replace("METER", "METRE"):
return name
@staticmethod
def convert(value, from_prefix, from_unit, to_prefix, to_unit):
"""Converts between length, area, and volume units
:param value: The numeric value you want to convert
:type value: float
:param from_prefix: A prefix from IfcSIPrefix. Can be None.
:type from_prefix: string
:param from_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type from_unit: string
:param to_prefix: A prefix from IfcSIPrefix. Can be None.
:type to_prefix: string
:param to_unit: IfcSIUnitName or IfcConversionBasedUnit.Name
:type to_unit: string
"""
if from_unit in SIUnitHelper.si_conversions:
value *= SIUnitHelper.si_conversions[from_unit]
elif from_prefix:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
if "SQUARE" in from_unit:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
elif "CUBIC" in from_unit:
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
value *= SIUnitHelper.get_prefix_multiplier(from_prefix)
if to_unit in SIUnitHelper.si_conversions:
return value * (1 / SIUnitHelper.si_conversions[to_unit])
elif to_prefix:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
if "SQUARE" in from_unit:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
elif "CUBIC" in from_unit:
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
value *= 1 / SIUnitHelper.get_prefix_multiplier(to_prefix)
return value
# This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py # This function stolen from https://github.com/kevancress/MeasureIt_ARCH/blob/dcf607ce0896aa2284463c6b4ae9cd023fc54cbe/measureit_arch_baseclass.py
@@ -302,7 +211,7 @@ def parse_diagram_scale(camera):
def get_project_collection(scene): def get_project_collection(scene):
"""Get main project collection""" """Get main project collection"""
colls = [c for c in scene.collection.children if c.name.startswith('IfcProject')] colls = [c for c in scene.collection.children if c.name.startswith("IfcProject")]
if len(colls) != 1: if len(colls) != 1:
raise RuntimeError("project collection missing or not unique") raise RuntimeError("project collection missing or not unique")
return colls[0] return colls[0]
@@ -315,7 +224,7 @@ def get_active_drawing(scene):
return None, None return None, None
try: try:
drawing = props.drawings[props.active_drawing_index] drawing = props.drawings[props.active_drawing_index]
return scene.collection.children['Views'].children[f"IfcGroup/{drawing.name}"], drawing.camera return scene.collection.children["Views"].children[f"IfcGroup/{drawing.name}"], drawing.camera
except (KeyError, IndexError): except (KeyError, IndexError):
raise RuntimeError("missing drawing collection") raise RuntimeError("missing drawing collection")
@@ -333,8 +242,8 @@ def ortho_view_frame(camera, margin=0.015):
""" """
aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x aspect = camera.BIMCameraProperties.raster_y / camera.BIMCameraProperties.raster_x
size = camera.ortho_scale size = camera.ortho_scale
hwidth = size * .5 hwidth = size * 0.5
hheight = size * .5 * aspect hheight = size * 0.5 * aspect
scale = parse_diagram_scale(camera) scale = parse_diagram_scale(camera)
xmarg = margin * scale xmarg = margin * scale
ymarg = margin * scale * aspect ymarg = margin * scale * aspect
@@ -1,3 +1,5 @@
import isodate
from dateutil import parser
from ifcopenshell.api.sequence.data import Data from ifcopenshell.api.sequence.data import Data
@@ -18,3 +20,19 @@ def derive_date(ifc_definition_id, attribute_name, date=None, is_earliest=False,
if current_date and (date is None or current_date > date): if current_date and (date is None or current_date > date):
date = current_date date = current_date
return date return date
def parse_datetime(value):
try:
return parser.isoparse(value)
except:
try:
return parser.parse(value, dayfirst=True, fuzzy=True)
except:
return None
def parse_duration(value):
try:
return isodate.parse_duration(value)
except:
return None
@@ -8,6 +8,7 @@ import pystache
import webbrowser import webbrowser
import ifcopenshell.api import ifcopenshell.api
import ifcopenshell.util.date import ifcopenshell.util.date
import blenderbim.bim.helper
import blenderbim.bim.module.sequence.helper as helper import blenderbim.bim.module.sequence.helper as helper
from datetime import datetime from datetime import datetime
from datetime import timedelta from datetime import timedelta
@@ -33,15 +34,7 @@ class EditWorkPlan(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkPlanProperties props = context.scene.BIMWorkPlanProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.work_plan_attributes, self.export_attributes)
for attribute in props.work_plan_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_plan", "sequence.edit_work_plan",
@@ -52,6 +45,14 @@ class EditWorkPlan(bpy.types.Operator):
bpy.ops.bim.disable_editing_work_plan() bpy.ops.bim.disable_editing_work_plan()
return {"FINISHED"} return {"FINISHED"}
def export_attributes(self, attributes, prop):
if "Date" in prop.name or "Time" in prop.name:
attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True
elif prop.name == "Duration" or prop.name == "TotalFloat":
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
class RemoveWorkPlan(bpy.types.Operator): class RemoveWorkPlan(bpy.types.Operator):
bl_idname = "bim.remove_work_plan" bl_idname = "bim.remove_work_plan"
@@ -77,27 +78,17 @@ class EnableEditingWorkPlan(bpy.types.Operator):
data = Data.work_plans[self.work_plan] data = Data.work_plans[self.work_plan]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkPlan").all_attributes(): blenderbim.bim.helper.import_attributes("IfcWorkPlan", props.work_plan_attributes, data, self.import_attributes)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.work_plan_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if attribute.name() in ["CreationDate", "StartTime", "FinishTime"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_work_plan_id = self.work_plan props.active_work_plan_id = self.work_plan
props.editing_type = "ATTRIBUTES" props.editing_type = "ATTRIBUTES"
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data):
if name in ["CreationDate", "StartTime", "FinishTime"]:
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
class DisableEditingWorkPlan(bpy.types.Operator): class DisableEditingWorkPlan(bpy.types.Operator):
bl_idname = "bim.disable_editing_work_plan" bl_idname = "bim.disable_editing_work_plan"
@@ -176,15 +167,7 @@ class EditWorkSchedule(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.work_schedule_attributes, self.export_attributes)
for attribute in props.work_schedule_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_schedule", "sequence.edit_work_schedule",
@@ -195,6 +178,14 @@ class EditWorkSchedule(bpy.types.Operator):
bpy.ops.bim.disable_editing_work_schedule() bpy.ops.bim.disable_editing_work_schedule()
return {"FINISHED"} return {"FINISHED"}
def export_attributes(self, attributes, prop):
if "Date" in prop.name or "Time" in prop.name:
attributes[prop.name] = helper.parse_datetime(prop.string_value)
return True
elif prop.name == "Duration" or prop.name == "TotalFloat":
attributes[prop.name] = helper.parse_duration(prop.string_value)
return True
class RemoveWorkSchedule(bpy.types.Operator): class RemoveWorkSchedule(bpy.types.Operator):
bl_idname = "bim.remove_work_schedule" bl_idname = "bim.remove_work_schedule"
@@ -227,23 +218,14 @@ class EnableEditingWorkSchedule(bpy.types.Operator):
def enable_editing_work_schedule(self): def enable_editing_work_schedule(self):
data = Data.work_schedules[self.work_schedule] data = Data.work_schedules[self.work_schedule]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkSchedule").all_attributes(): blenderbim.bim.helper.import_attributes(
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) "IfcWorkSchedule", self.props.work_schedule_attributes, data, self.import_attributes
if data_type == "entity": )
continue
new = self.props.work_schedule_attributes.add() def import_attributes(self, name, prop, data):
new.name = attribute.name() if name in ["CreationDate", "StartTime", "FinishTime"]:
new.is_null = data[attribute.name()] is None prop.string_value = "" if prop.is_null else data[name].isoformat()
new.is_optional = attribute.optional() return True
new.data_type = data_type
if attribute.name() in ["CreationDate", "StartTime", "FinishTime"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class EnableEditingTasks(bpy.types.Operator): class EnableEditingTasks(bpy.types.Operator):
@@ -439,39 +421,21 @@ class EnableEditingTaskTime(bpy.types.Operator):
data = Data.task_times[task_time_id] data = Data.task_times[task_time_id]
for attribute in IfcStore.get_schema().declaration_by_name("IfcTaskTime").all_attributes(): blenderbim.bim.helper.import_attributes("IfcTaskTime", props.task_time_attributes, data, self.import_attributes)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.task_time_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
if isinstance(data[attribute.name()], datetime):
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif isinstance(data[attribute.name()], isodate.Duration):
new.string_value = (
""
if new.is_null
else ifcopenshell.util.date.datetime2ifc(data[attribute.name()], "IfcDuration")
)
else:
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_task_time_id = task_time_id props.active_task_time_id = task_time_id
props.active_task_id = self.task props.active_task_id = self.task
props.editing_task_type = "TASKTIME" props.editing_task_type = "TASKTIME"
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data):
if prop.data_type == "string":
if isinstance(data[name], datetime):
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
elif isinstance(data[name], isodate.Duration):
prop.string_value = "" if prop.is_null else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
return True
def add_task_time(self): def add_task_time(self):
task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.file.by_id(self.task)) task_time = ifcopenshell.api.run("sequence.add_task_time", self.file, task=self.file.by_id(self.task))
Data.load(IfcStore.get_file()) Data.load(IfcStore.get_file())
@@ -484,20 +448,7 @@ class EditTaskTime(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.task_time_attributes, self.export_attributes)
for attribute in props.task_time_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "boolean":
attributes[attribute.name] = attribute.bool_value
elif attribute.data_type == "float":
attributes[attribute.name] = attribute.float_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
attributes = self.convert_strings_to_date_times(attributes) attributes = self.convert_strings_to_date_times(attributes)
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
@@ -511,21 +462,13 @@ class EditTaskTime(bpy.types.Operator):
bpy.ops.bim.load_task_properties(task=props.active_task_id) bpy.ops.bim.load_task_properties(task=props.active_task_id)
return {"FINISHED"} return {"FINISHED"}
def convert_strings_to_date_times(self, attributes): def export_attributes(self, attributes, prop):
for key, value in attributes.items(): if "Start" in prop.name or "Finish" in prop.name or prop.name == "StatusTime":
if not value: attributes[prop.name] = helper.parse_datetime(value)
continue return True
if "Start" in key or "Finish" in key or key == "StatusTime": elif prop.name == "ScheduleDuration":
try: attributes[prop.name] = helper.parse_duration(value)
attributes[key] = parser.isoparse(value) return True
except:
try:
attributes[key] = parser.parse(value, dayfirst=True, fuzzy=True)
except:
attributes[key] = None
elif key == "ScheduleDuration":
attributes[key] = isodate.parse_duration(value)
return attributes
class EnableEditingTask(bpy.types.Operator): class EnableEditingTask(bpy.types.Operator):
@@ -540,25 +483,7 @@ class EnableEditingTask(bpy.types.Operator):
data = Data.tasks[self.task] data = Data.tasks[self.task]
for attribute in IfcStore.get_schema().declaration_by_name("IfcTask").all_attributes(): blenderbim.bim.helper.import_attributes("IfcTask", props.task_attributes, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = props.task_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "boolean":
new.bool_value = False if new.is_null else data[attribute.name()]
elif data_type == "integer":
new.int_value = 0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
props.active_task_id = self.task props.active_task_id = self.task
props.editing_task_type = "ATTRIBUTES" props.editing_task_type = "ATTRIBUTES"
return {"FINISHED"} return {"FINISHED"}
@@ -580,19 +505,7 @@ class EditTask(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.export_attributes(props.task_attributes)
for attribute in props.task_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "boolean":
attributes[attribute.name] = attribute.bool_value
elif attribute.data_type == "integer":
attributes[attribute.name] = attribute.int_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_task", self.file, **{"task": self.file.by_id(props.active_task_id), "attributes": attributes} "sequence.edit_task", self.file, **{"task": self.file.by_id(props.active_task_id), "attributes": attributes}
@@ -778,15 +691,7 @@ class EditWorkCalendar(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkCalendarProperties props = context.scene.BIMWorkCalendarProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.work_calendar_attributes)
for attribute in props.work_calendar_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_calendar", "sequence.edit_work_calendar",
@@ -824,21 +729,7 @@ class EnableEditingWorkCalendar(bpy.types.Operator):
data = Data.work_calendars[self.work_calendar] data = Data.work_calendars[self.work_calendar]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkCalendar").all_attributes(): blenderbim.bim.helper.import_attributes("IfcWorkCalendar", self.props.work_calendar_attributes, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.work_calendar_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
self.props.active_work_calendar_id = self.work_calendar self.props.active_work_calendar_id = self.work_calendar
self.props.editing_type = "ATTRIBUTES" self.props.editing_type = "ATTRIBUTES"
return {"FINISHED"} return {"FINISHED"}
@@ -936,29 +827,20 @@ class EnableEditingWorkTime(bpy.types.Operator):
data = Data.work_times[self.work_time] data = Data.work_times[self.work_time]
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkTime").all_attributes(): blenderbim.bim.helper.import_attributes(
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) "IfcWorkTime", self.props.work_time_attributes, data, self.import_attributes
if data_type == "entity": )
continue
new = self.props.work_time_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if attribute.name() in ["Start", "Finish"]:
new.string_value = "" if new.is_null else data[attribute.name()].isoformat()
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
self.initialise_recurrence_components() self.initialise_recurrence_components()
self.load_recurrence_pattern_data(data) self.load_recurrence_pattern_data(data)
self.props.active_work_time_id = self.work_time self.props.active_work_time_id = self.work_time
return {"FINISHED"} return {"FINISHED"}
def import_attributes(self, name, prop, data):
if name in ["Start", "Finish"]:
prop.string_value = "" if prop.is_null else data[name].isoformat()
return True
def initialise_recurrence_components(self): def initialise_recurrence_components(self):
if len(self.props.day_components) == 0: if len(self.props.day_components) == 0:
for i in range(0, 31): for i in range(0, 31):
@@ -1014,15 +896,7 @@ class EditWorkTime(bpy.types.Operator):
def execute(self, context): def execute(self, context):
self.props = context.scene.BIMWorkCalendarProperties self.props = context.scene.BIMWorkCalendarProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(self.props.work_time_attributes)
for attribute in self.props.work_time_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_work_time", "sequence.edit_work_time",
@@ -1251,21 +1125,7 @@ class EnableEditingSequenceAttributes(bpy.types.Operator):
def enable_editing_sequence_attributes(self): def enable_editing_sequence_attributes(self):
data = Data.sequences[self.sequence] data = Data.sequences[self.sequence]
for attribute in IfcStore.get_schema().declaration_by_name("IfcRelSequence").all_attributes(): blenderbim.bim.helper.import_attributes("IfcRelSequence", self.props.sequence_attributes, data)
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
if data_type == "entity":
continue
new = self.props.sequence_attributes.add()
new.name = attribute.name()
new.is_null = data[attribute.name()] is None
new.is_optional = attribute.optional()
new.data_type = data_type
if data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class EnableEditingSequenceTimeLag(bpy.types.Operator): class EnableEditingSequenceTimeLag(bpy.types.Operator):
@@ -1285,34 +1145,24 @@ class EnableEditingSequenceTimeLag(bpy.types.Operator):
def enable_editing_attributes(self): def enable_editing_attributes(self):
data = Data.lag_times[self.lag_time] data = Data.lag_times[self.lag_time]
for attribute in IfcStore.get_schema().declaration_by_name("IfcLagTime").all_attributes(): blenderbim.bim.helper.import_attributes(
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute) "IfcLagTime", self.props.time_lag_attributes, data, self.import_attributes
if data_type == "entity": )
continue
new = self.props.time_lag_attributes.add() def import_attributes(self, name, prop, data):
new.name = attribute.name() if name == "LagValue":
new.is_null = data[attribute.name()] is None if isinstance(data[name], isodate.Duration):
new.is_optional = attribute.optional() prop.data_type = "string"
new.data_type = data_type prop.string_value = (
if attribute.name() == "LagValue": ""
if isinstance(data[attribute.name()], isodate.Duration): if prop.is_null
new.data_type = "string" else ifcopenshell.util.date.datetime2ifc(data[name], "IfcDuration")
new.string_value = ( )
"" return True
if new.is_null else:
else ifcopenshell.util.date.datetime2ifc(data[attribute.name()], "IfcDuration") prop.data_type = "float"
) prop.float_value = 0.0 if prop.is_null else data[name]
else: return True
new.data_type = "float"
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "string":
new.string_value = "" if new.is_null else data[attribute.name()]
elif data_type == "float":
new.float_value = 0.0 if new.is_null else data[attribute.name()]
elif data_type == "enum":
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
if data[attribute.name()]:
new.enum_value = data[attribute.name()]
class UnassignLagTime(bpy.types.Operator): class UnassignLagTime(bpy.types.Operator):
@@ -1351,15 +1201,7 @@ class EditSequenceAttributes(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.sequence_attributes)
for attribute in props.sequence_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_sequence", "sequence.edit_sequence",
@@ -1378,17 +1220,7 @@ class EditSequenceTimeLag(bpy.types.Operator):
def execute(self, context): def execute(self, context):
props = context.scene.BIMWorkScheduleProperties props = context.scene.BIMWorkScheduleProperties
attributes = {} attributes = blenderbim.bim.helper.export_attributes(props.time_lag_attributes)
for attribute in props.time_lag_attributes:
if attribute.is_null:
attributes[attribute.name] = None
else:
if attribute.data_type == "string":
attributes[attribute.name] = attribute.string_value
elif attribute.data_type == "float":
attributes[attribute.name] = attribute.float_value
elif attribute.data_type == "enum":
attributes[attribute.name] = attribute.enum_value
self.file = IfcStore.get_file() self.file = IfcStore.get_file()
ifcopenshell.api.run( ifcopenshell.api.run(
"sequence.edit_lag_time", "sequence.edit_lag_time",
@@ -10,10 +10,9 @@ class Usecase:
def execute(self): def execute(self):
for name, value in self.settings["attributes"].items(): for name, value in self.settings["attributes"].items():
if "Start" in name or "Finish" in name or name == "StatusTime": if value:
if value: if "Start" in name or "Finish" in name or name == "StatusTime":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
if name == "ScheduleDuration": elif name == "ScheduleDuration":
if value:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration") value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["task_time"], name, value) setattr(self.settings["task_time"], name, value)
@@ -1,3 +1,6 @@
import ifcopenshell.util.date
class Usecase: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file self.file = file
@@ -7,4 +10,9 @@ class Usecase:
def execute(self): def execute(self):
for name, value in self.settings["attributes"].items(): for name, value in self.settings["attributes"].items():
if value:
if "Date" in name or "Time" in name:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif name == "Duration" or name == "TotalFloat":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["work_plan"], name, value) setattr(self.settings["work_plan"], name, value)
@@ -1,3 +1,6 @@
import ifcopenshell.util.date
class Usecase: class Usecase:
def __init__(self, file, **settings): def __init__(self, file, **settings):
self.file = file self.file = file
@@ -7,4 +10,9 @@ class Usecase:
def execute(self): def execute(self):
for name, value in self.settings["attributes"].items(): for name, value in self.settings["attributes"].items():
if value:
if "Date" in name or "Time" in name:
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDateTime")
elif name == "Duration" or name == "TotalFloat":
value = ifcopenshell.util.date.datetime2ifc(value, "IfcDuration")
setattr(self.settings["work_schedule"], name, value) setattr(self.settings["work_schedule"], name, value)