Skip to content
This repository was archived by the owner on Nov 24, 2024. It is now read-only.

Commit becb375

Browse files
authored
Feature work schedule (IfcOpenShell#1419)
* ifcopenshell.api modules Persons and Organisations account for IFC4 SCHEMA CHANGE * New Feature to manpiulate Work Plans
1 parent a777a55 commit becb375

9 files changed

Lines changed: 305 additions & 1 deletion

File tree

src/blenderbim/blenderbim/bim/module/sequence/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,23 @@
1111
operator.RemoveWorkPlan,
1212
operator.EnableEditingWorkPlan,
1313
operator.DisableEditingWorkPlan,
14+
operator.LoadWorkSchedules,
15+
operator.DisableWorkScheduleEditingUI,
16+
operator.AddWorkSchedule,
17+
operator.EditWorkSchedule,
18+
operator.RemoveWorkSchedule,
19+
operator.EnableEditingWorkSchedule,
20+
operator.DisableEditingWorkSchedule,
1421
prop.WorkPlan,
1522
prop.BIMWorkPlanProperties,
23+
prop.WorkSchedule,
24+
prop.BIMWorkScheduleProperties,
1625
prop.Task,
1726
prop.BIMTaskProperties,
1827
ui.BIM_PT_work_plans,
1928
ui.BIM_UL_work_plans,
29+
ui.BIM_PT_work_schedules,
30+
ui.BIM_UL_work_schedules,
2031
ui.BIM_PT_tasks,
2132
ui.BIM_UL_tasks,
2233
)
@@ -25,8 +36,10 @@
2536
def register():
2637
bpy.types.Scene.BIMTaskProperties = bpy.props.PointerProperty(type=prop.BIMTaskProperties)
2738
bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties)
39+
bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties)
2840

2941

3042
def unregister():
3143
del bpy.types.Scene.BIMTaskProperties
3244
del bpy.types.Scene.BIMWorkPlanProperties
45+
del bpy.types.Scene.BIMWorkScheduleProperties

src/blenderbim/blenderbim/bim/module/sequence/operator.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,118 @@ class DisableEditingWorkPlan(bpy.types.Operator):
114114
def execute(self, context):
115115
context.scene.BIMWorkPlanProperties.active_work_plan_id = 0
116116
return {"FINISHED"}
117+
118+
class LoadWorkSchedules(bpy.types.Operator):
119+
bl_idname = "bim.load_work_schedules"
120+
bl_label = "Load Work Schedules"
121+
122+
def execute(self, context):
123+
props = context.scene.BIMWorkScheduleProperties
124+
while len(props.work_schedules) > 0:
125+
props.work_schedules.remove(0)
126+
for ifc_definition_id, work_schedule in Data.work_schedules.items():
127+
new = props.work_schedules.add()
128+
new.ifc_definition_id = ifc_definition_id or "Unnamed"
129+
new.name = work_schedule["Name"]
130+
props.is_editing = True
131+
bpy.ops.bim.disable_editing_work_schedule()
132+
return {"FINISHED"}
133+
134+
class DisableWorkScheduleEditingUI(bpy.types.Operator):
135+
bl_idname = "bim.disable_work_schedule_editing_ui"
136+
bl_label = "Disable WorkSchedule Editing UI"
137+
138+
def execute(self, context):
139+
context.scene.BIMWorkScheduleProperties.is_editing = False
140+
return {"FINISHED"}
141+
142+
143+
class AddWorkSchedule(bpy.types.Operator):
144+
bl_idname = "bim.add_work_schedule"
145+
bl_label = "Add Work Schedule"
146+
147+
def execute(self, context):
148+
result = ifcopenshell.api.run("sequence.add_work_schedule", IfcStore.get_file())
149+
Data.load(IfcStore.get_file())
150+
bpy.ops.bim.load_work_schedules()
151+
return {"FINISHED"}
152+
153+
154+
class EditWorkSchedule(bpy.types.Operator):
155+
bl_idname = "bim.edit_work_schedule"
156+
bl_label = "Edit Work Schedule"
157+
158+
def execute(self, context):
159+
props = context.scene.BIMWorkScheduleProperties
160+
attributes = {}
161+
for attribute in props.work_schedule_attributes:
162+
if attribute.is_null:
163+
attributes[attribute.name] = None
164+
else:
165+
if attribute.data_type == "string":
166+
attributes[attribute.name] = attribute.string_value
167+
elif attribute.data_type == "enum":
168+
attributes[attribute.name] = attribute.enum_value
169+
self.file = IfcStore.get_file()
170+
ifcopenshell.api.run("sequence.edit_work_schedule", self.file, **{
171+
"work_schedule": self.file.by_id(props.active_work_schedule_id),
172+
"attributes": attributes
173+
})
174+
Data.load(IfcStore.get_file())
175+
bpy.ops.bim.load_work_schedules()
176+
return {"FINISHED"}
177+
178+
class RemoveWorkSchedule(bpy.types.Operator):
179+
bl_idname = "bim.remove_work_schedule"
180+
bl_label = "Remove Work Schedule"
181+
work_schedule: bpy.props.IntProperty()
182+
183+
def execute(self, context):
184+
props = context.scene.BIMWorkScheduleProperties
185+
self.file = IfcStore.get_file()
186+
ifcopenshell.api.run("sequence.remove_work_schedule", self.file, **{"work_schedule": self.file.by_id(self.work_schedule)})
187+
Data.load(IfcStore.get_file())
188+
bpy.ops.bim.load_work_schedules()
189+
return {"FINISHED"}
190+
191+
class EnableEditingWorkSchedule(bpy.types.Operator):
192+
bl_idname = "bim.enable_editing_work_schedule"
193+
bl_label = "Enable Editing Work Schedule"
194+
work_schedule: bpy.props.IntProperty()
195+
196+
def execute(self, context):
197+
props = context.scene.BIMWorkScheduleProperties
198+
while len(props.work_schedule_attributes) > 0:
199+
props.work_schedule_attributes.remove(0)
200+
201+
data = Data.work_schedules[self.work_schedule]
202+
203+
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkSchedule").all_attributes():
204+
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
205+
if data_type == "entity":
206+
continue
207+
new = props.work_schedule_attributes.add()
208+
new.name = attribute.name()
209+
new.is_null = data[attribute.name()] is None
210+
new.is_optional = attribute.optional()
211+
new.data_type = data_type
212+
if data_type == "string":
213+
new.string_value = "" if new.is_null else data[attribute.name()]
214+
elif data_type == "enum":
215+
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
216+
if data[attribute.name()]:
217+
new.enum_value = data[attribute.name()]
218+
props.active_work_schedule_id = self.work_schedule
219+
return {"FINISHED"}
220+
221+
222+
class DisableEditingWorkSchedule(bpy.types.Operator):
223+
bl_idname = "bim.disable_editing_work_schedule"
224+
bl_label = "Disable Editing Work Schedule"
225+
226+
def execute(self, context):
227+
context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
228+
return {"FINISHED"}
117229
return {"FINISHED"}
118230

119231

src/blenderbim/blenderbim/bim/module/sequence/prop.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,17 @@ class BIMWorkPlanProperties(PropertyGroup):
3535
is_editing: BoolProperty(name="Is Editing", default=False)
3636
work_plans: CollectionProperty(name="Work Plans", type=WorkPlan)
3737
active_work_plan_index: IntProperty(name="Active Work Plan Index")
38-
active_work_plan_id: IntProperty(name="Active Work Plan Id")
38+
active_work_plan_id: IntProperty(name="Active Work Plan Id")
39+
40+
41+
class WorkSchedule(PropertyGroup):
42+
name: StringProperty(name="Name")
43+
ifc_definition_id: IntProperty(name="IFC Definition ID")
44+
45+
46+
class BIMWorkScheduleProperties(PropertyGroup):
47+
work_schedule_attributes: CollectionProperty(name="Work Schedule Attributes", type=Attribute)
48+
is_editing: BoolProperty(name="Is Editing", default=False)
49+
work_schedules: CollectionProperty(name="Work Schedules", type=WorkSchedule)
50+
active_work_schedule_index: IntProperty(name="Active Work Schedules Index")
51+
active_work_schedule_id: IntProperty(name="Active Work Schedules Id")

src/blenderbim/blenderbim/bim/module/sequence/ui.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,66 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
6262
op = row.operator("bim.enable_editing_work_plan", text="", icon="GREASEPENCIL")
6363
op.work_plan = item.ifc_definition_id
6464
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = item.ifc_definition_id
65+
66+
67+
class BIM_PT_work_schedules(Panel):
68+
bl_label = "IFC Work Schedules"
69+
bl_idname = "BIM_PT_work_schedules"
70+
bl_options = {"DEFAULT_CLOSED"}
71+
bl_space_type = "PROPERTIES"
72+
bl_region_type = "WINDOW"
73+
bl_context = "scene"
74+
75+
@classmethod
76+
def poll(cls, context):
77+
return IfcStore.get_file()
78+
79+
def draw(self, context):
80+
if not Data.is_loaded:
81+
Data.load(IfcStore.get_file())
82+
self.props = context.scene.BIMWorkScheduleProperties
83+
row = self.layout.row(align=True)
84+
row.label(text="{} Work Schedules Found".format(len(Data.work_schedules)), icon="TEXT")
85+
if self.props.is_editing:
86+
row.operator("bim.add_work_schedule", text="", icon="ADD")
87+
row.operator("bim.disable_work_schedule_editing_ui", text="", icon="CHECKMARK")
88+
else:
89+
row.operator("bim.load_work_schedules", text="", icon="GREASEPENCIL")
90+
91+
if self.props.is_editing:
92+
self.layout.template_list(
93+
"BIM_UL_work_schedules",
94+
"",
95+
self.props,
96+
"work_schedules",
97+
self.props,
98+
"active_work_schedule_index",
99+
)
100+
101+
if self.props.active_work_schedule_id:
102+
self.draw_editable_ui(context)
103+
104+
def draw_editable_ui(self, context):
105+
for attribute in self.props.work_schedule_attributes:
106+
row = self.layout.row(align=True)
107+
row.prop(attribute, "string_value", text=attribute.name)
108+
if attribute.is_optional:
109+
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
110+
111+
class BIM_UL_work_schedules(UIList):
112+
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
113+
if item:
114+
row = layout.row(align=True)
115+
row.label(text=item.name)
116+
if context.scene.BIMWorkScheduleProperties.active_work_schedule_id == item.ifc_definition_id:
117+
row.operator("bim.edit_work_schedule", text="", icon="CHECKMARK")
118+
row.operator("bim.disable_editing_work_schedule", text="", icon="X")
119+
elif context.scene.BIMWorkScheduleProperties.active_work_schedule_id:
120+
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = item.ifc_definition_id
121+
else:
122+
op = row.operator("bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL")
123+
op.work_schedule = item.ifc_definition_id
124+
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = item.ifc_definition_id
65125
row = self.layout.row(align=True)
66126
row.label(text=work_plan["Name"] or "Unnamed", icon="TEXT")
67127
row.operator("bim.add_work_plan", text="", icon="GREASEPENCIL")

src/ifcopenshell-python/ifcopenshell/api/aggregate/assign_object.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ def execute(self):
5050
"RelatingObject": self.settings["relating_object"],
5151
}
5252
)
53+
return is_decomposed_by
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import ifcopenshell.api
2+
import ifcopenshell.util.date
3+
from datetime import datetime
4+
5+
6+
class Usecase:
7+
def __init__(self, file, **settings):
8+
self.file = file
9+
self.settings = {"name": "Unnamed", "predefined_type": "NOTDEFINED", "start_time": datetime.now(), "work_plan":None}
10+
for key, value in settings.items():
11+
self.settings[key] = value
12+
13+
def execute(self):
14+
work_schedule = ifcopenshell.api.run(
15+
"root.create_entity",
16+
self.file,
17+
ifc_class="IfcWorkSchedule",
18+
predefined_type=self.settings["predefined_type"],
19+
name=self.settings["name"],
20+
)
21+
work_schedule.CreationDate = ifcopenshell.util.date.datetime2ifc(datetime.now(), "IfcDateTime")
22+
person = ifcopenshell.api.owner.settings.get_person(self.file)
23+
if person:
24+
work_schedule.Creators = [person]
25+
work_schedule.StartTime = ifcopenshell.util.date.datetime2ifc(self.settings["start_time"], "IfcDateTime")
26+
27+
if self.settings["work_plan"]:
28+
rel_aggregates = ifcopenshell.api.run("aggregate.assign_object", self.file, **{
29+
"product": work_schedule,
30+
"relating_object": self.settings["work_plan"]
31+
})
32+
else:
33+
context = self.file.by_type("IfcContext")[0]
34+
if context.Declares:
35+
rel_declares = context.Declares[0]
36+
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel_declares)
37+
else:
38+
rel_declares = self.file.create_entity("IfcRelDeclares", **{
39+
"GlobalId": ifcopenshell.guid.new(),
40+
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
41+
"RelatingContext": context
42+
})
43+
related_definitions = list(rel_declares.RelatedDefinitions or [])
44+
related_definitions.append(work_schedule)
45+
rel_declares.RelatedDefinitions = related_definitions
46+
47+
return work_schedule
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import ifcopenshell
2+
import ifcopenshell.api
3+
4+
class Usecase:
5+
def __init__(self, file, **settings):
6+
self.file = file
7+
self.settings = {
8+
"work_schedule": None,
9+
"work_plan":None,
10+
}
11+
for key, value in settings.items():
12+
self.settings[key] = value
13+
14+
def execute(self):
15+
###Project declaration should be removed if the workschedule is assigned to a project.####
16+
if self.settings["work_schedule"].HasContext:
17+
rel_declares = self.settings["work_schedule"].HasContext[0]
18+
related_definitions = list(rel_declares.RelatedDefinitions)
19+
related_definitions.remove(self.settings["work_schedule"])
20+
rel_declares.RelatedDefinitions = related_definitions
21+
if self.settings["work_plan"].IsDecomposedBy:
22+
rel_aggregates = self.settings["work_plan"].IsDecomposedBy[0]
23+
related_objects = list(rel_aggregates.RelatedObjects or [])
24+
related_objects.append(self.settings["work_schedule"])
25+
rel_aggregates.RelatedObjects = related_objects
26+
else:
27+
rel_aggregates = ifcopenshell.api.run("aggregate.assign_object", self.file, **{
28+
"product": self.settings["work_schedule"],
29+
"relating_object": self.settings["work_plan"]
30+
})
31+
return rel_aggregates
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Usecase:
2+
def __init__(self, file, **settings):
3+
self.file = file
4+
self.settings = {
5+
"work_schedule": None,
6+
"attributes": {}
7+
}
8+
for key, value in settings.items():
9+
self.settings[key] = value
10+
11+
def execute(self):
12+
for name, value in self.settings["attributes"].items():
13+
setattr(self.settings["work_schedule"], name, value)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class Usecase:
2+
def __init__(self, file, **settings):
3+
self.file = file
4+
self.settings = {"work_schedule": None}
5+
for key, value in settings.items():
6+
self.settings[key] = value
7+
8+
def execute(self):
9+
# TODO: do a deep purge
10+
if self.settings["work_schedule"].HasContext:
11+
rel_declares = self.settings["work_schedule"].HasContext[0]
12+
if len(rel_declares.RelatedDefinitions) == 1:
13+
self.file.remove(rel_declares)
14+
self.file.remove(self.settings["work_schedule"])

0 commit comments

Comments
 (0)