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

Commit 13d4a99

Browse files
authored
Feature to manipulate Work Calendars (IfcOpenShell#1420)
1 parent becb375 commit 13d4a99

11 files changed

Lines changed: 376 additions & 10 deletions

File tree

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
classes = (
55
operator.LoadWorkPlans,
66
operator.DisableWorkPlanEditingUI,
7-
operator.LoadTasks,
8-
operator.DisableTaskEditingUI,
97
operator.AddWorkPlan,
108
operator.EditWorkPlan,
119
operator.RemoveWorkPlan,
@@ -18,28 +16,41 @@
1816
operator.RemoveWorkSchedule,
1917
operator.EnableEditingWorkSchedule,
2018
operator.DisableEditingWorkSchedule,
19+
operator.LoadTasks,
20+
operator.DisableTaskEditingUI,
21+
operator.LoadWorkCalendars,
22+
operator.DisableWorkCalendarEditingUI,
23+
operator.AddWorkCalendar,
24+
operator.EditWorkCalendar,
25+
operator.RemoveWorkCalendar,
26+
operator.EnableEditingWorkCalendar,
27+
operator.DisableEditingWorkCalendar,
2128
prop.WorkPlan,
2229
prop.BIMWorkPlanProperties,
2330
prop.WorkSchedule,
2431
prop.BIMWorkScheduleProperties,
32+
prop.WorkCalendar,
33+
prop.BIMWorkCalendarProperties,
2534
prop.Task,
2635
prop.BIMTaskProperties,
2736
ui.BIM_PT_work_plans,
2837
ui.BIM_UL_work_plans,
2938
ui.BIM_PT_work_schedules,
3039
ui.BIM_UL_work_schedules,
40+
ui.BIM_PT_work_calendars,
41+
ui.BIM_UL_work_calendars,
3142
ui.BIM_PT_tasks,
3243
ui.BIM_UL_tasks,
3344
)
3445

35-
3646
def register():
3747
bpy.types.Scene.BIMTaskProperties = bpy.props.PointerProperty(type=prop.BIMTaskProperties)
3848
bpy.types.Scene.BIMWorkPlanProperties = bpy.props.PointerProperty(type=prop.BIMWorkPlanProperties)
3949
bpy.types.Scene.BIMWorkScheduleProperties = bpy.props.PointerProperty(type=prop.BIMWorkScheduleProperties)
40-
50+
bpy.types.Scene.BIMWorkCalendarProperties = bpy.props.PointerProperty(type=prop.BIMWorkCalendarProperties)
4151

4252
def unregister():
4353
del bpy.types.Scene.BIMTaskProperties
4454
del bpy.types.Scene.BIMWorkPlanProperties
4555
del bpy.types.Scene.BIMWorkScheduleProperties
56+
del bpy.types.Scene.BIMWorkCalendarProperties

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

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,118 @@ class DisableEditingWorkSchedule(bpy.types.Operator):
226226
def execute(self, context):
227227
context.scene.BIMWorkScheduleProperties.active_work_schedule_id = 0
228228
return {"FINISHED"}
229+
230+
231+
class LoadWorkCalendars(bpy.types.Operator):
232+
bl_idname = "bim.load_work_calendars"
233+
bl_label = "Load Work Calendars"
234+
235+
def execute(self, context):
236+
props = context.scene.BIMWorkCalendarProperties
237+
while len(props.work_calendars) > 0:
238+
props.work_calendars.remove(0)
239+
for ifc_definition_id, work_calendar in Data.work_calendars.items():
240+
new = props.work_calendars.add()
241+
new.ifc_definition_id = ifc_definition_id or "Unnamed"
242+
new.name = work_calendar["Name"]
243+
props.is_editing = True
244+
bpy.ops.bim.disable_editing_work_calendar()
245+
return {"FINISHED"}
246+
247+
class DisableWorkCalendarEditingUI(bpy.types.Operator):
248+
bl_idname = "bim.disable_work_calendar_editing_ui"
249+
bl_label = "Disable WorkCalendar Editing UI"
250+
251+
def execute(self, context):
252+
context.scene.BIMWorkCalendarProperties.is_editing = False
253+
return {"FINISHED"}
254+
255+
256+
class AddWorkCalendar(bpy.types.Operator):
257+
bl_idname = "bim.add_work_calendar"
258+
bl_label = "Add Work Calendar"
259+
260+
def execute(self, context):
261+
result = ifcopenshell.api.run("sequence.add_work_calendar", IfcStore.get_file())
262+
Data.load(IfcStore.get_file())
263+
bpy.ops.bim.load_work_calendars()
264+
return {"FINISHED"}
265+
266+
267+
class EditWorkCalendar(bpy.types.Operator):
268+
bl_idname = "bim.edit_work_calendar"
269+
bl_label = "Edit Work Calendar"
270+
271+
def execute(self, context):
272+
props = context.scene.BIMWorkCalendarProperties
273+
attributes = {}
274+
for attribute in props.work_calendar_attributes:
275+
if attribute.is_null:
276+
attributes[attribute.name] = None
277+
else:
278+
if attribute.data_type == "string":
279+
attributes[attribute.name] = attribute.string_value
280+
elif attribute.data_type == "enum":
281+
attributes[attribute.name] = attribute.enum_value
282+
self.file = IfcStore.get_file()
283+
ifcopenshell.api.run("sequence.edit_work_calendar", self.file, **{
284+
"work_calendar": self.file.by_id(props.active_work_calendar_id),
285+
"attributes": attributes
286+
})
287+
Data.load(IfcStore.get_file())
288+
bpy.ops.bim.load_work_calendars()
289+
return {"FINISHED"}
290+
291+
class RemoveWorkCalendar(bpy.types.Operator):
292+
bl_idname = "bim.remove_work_calendar"
293+
bl_label = "Remove Work Plan"
294+
work_calendar: bpy.props.IntProperty()
295+
296+
def execute(self, context):
297+
props = context.scene.BIMWorkCalendarProperties
298+
self.file = IfcStore.get_file()
299+
ifcopenshell.api.run("sequence.remove_work_calendar", self.file, **{"work_calendar": self.file.by_id(self.work_calendar)})
300+
Data.load(IfcStore.get_file())
301+
bpy.ops.bim.load_work_calendars()
302+
return {"FINISHED"}
303+
304+
class EnableEditingWorkCalendar(bpy.types.Operator):
305+
bl_idname = "bim.enable_editing_work_calendar"
306+
bl_label = "Enable Editing Work Plan"
307+
work_calendar: bpy.props.IntProperty()
308+
309+
def execute(self, context):
310+
props = context.scene.BIMWorkCalendarProperties
311+
while len(props.work_calendar_attributes) > 0:
312+
props.work_calendar_attributes.remove(0)
313+
314+
data = Data.work_calendars[self.work_calendar]
315+
316+
for attribute in IfcStore.get_schema().declaration_by_name("IfcWorkCalendar").all_attributes():
317+
data_type = ifcopenshell.util.attribute.get_primitive_type(attribute)
318+
if data_type == "entity":
319+
continue
320+
new = props.work_calendar_attributes.add()
321+
new.name = attribute.name()
322+
new.is_null = data[attribute.name()] is None
323+
new.is_optional = attribute.optional()
324+
new.data_type = data_type
325+
if data_type == "string":
326+
new.string_value = "" if new.is_null else data[attribute.name()]
327+
elif data_type == "enum":
328+
new.enum_items = json.dumps(ifcopenshell.util.attribute.get_enum_items(attribute))
329+
if data[attribute.name()]:
330+
new.enum_value = data[attribute.name()]
331+
props.active_work_calendar_id = self.work_calendar
332+
return {"FINISHED"}
333+
334+
335+
class DisableEditingWorkCalendar(bpy.types.Operator):
336+
bl_idname = "bim.disable_editing_work_calendar"
337+
bl_label = "Disable Editing Work Calendar"
338+
339+
def execute(self, context):
340+
context.scene.BIMWorkCalendarProperties.active_work_calendar_id = 0
229341
return {"FINISHED"}
230342

231343

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,17 @@ class BIMWorkScheduleProperties(PropertyGroup):
4848
is_editing: BoolProperty(name="Is Editing", default=False)
4949
work_schedules: CollectionProperty(name="Work Schedules", type=WorkSchedule)
5050
active_work_schedule_index: IntProperty(name="Active Work Schedules Index")
51-
active_work_schedule_id: IntProperty(name="Active Work Schedules Id")
51+
active_work_schedule_id: IntProperty(name="Active Work Schedules Id")
52+
53+
54+
class WorkCalendar(PropertyGroup):
55+
name: StringProperty(name="Name")
56+
ifc_definition_id: IntProperty(name="IFC Definition ID")
57+
58+
59+
class BIMWorkCalendarProperties(PropertyGroup):
60+
work_calendar_attributes: CollectionProperty(name="Work Calendar Attributes", type=Attribute)
61+
is_editing: BoolProperty(name="Is Editing", default=False)
62+
work_calendars: CollectionProperty(name="Work Calendar", type=WorkCalendar)
63+
active_work_calendar_index: IntProperty(name="Active Work Calendar Index")
64+
active_work_calendar_id: IntProperty(name="Active Work Calendar Id")

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

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,65 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
122122
op = row.operator("bim.enable_editing_work_schedule", text="", icon="GREASEPENCIL")
123123
op.work_schedule = item.ifc_definition_id
124124
row.operator("bim.remove_work_schedule", text="", icon="X").work_schedule = item.ifc_definition_id
125+
126+
class BIM_PT_work_calendars(Panel):
127+
bl_label = "IFC Work Calendars"
128+
bl_idname = "BIM_PT_work_calendars"
129+
bl_options = {"DEFAULT_CLOSED"}
130+
bl_space_type = "PROPERTIES"
131+
bl_region_type = "WINDOW"
132+
bl_context = "scene"
133+
134+
@classmethod
135+
def poll(cls, context):
136+
return IfcStore.get_file()
137+
138+
def draw(self, context):
139+
if not Data.is_loaded:
140+
Data.load(IfcStore.get_file())
141+
self.props = context.scene.BIMWorkCalendarProperties
142+
row = self.layout.row(align=True)
143+
row.label(text="{} Work Calendar Found".format(len(Data.work_calendars)), icon="TEXT")
144+
if self.props.is_editing:
145+
row.operator("bim.add_work_calendar", text="", icon="ADD")
146+
row.operator("bim.disable_work_calendar_editing_ui", text="", icon="CHECKMARK")
147+
else:
148+
row.operator("bim.load_work_calendars", text="", icon="GREASEPENCIL")
149+
150+
if self.props.is_editing:
151+
self.layout.template_list(
152+
"BIM_UL_work_calendars",
153+
"",
154+
self.props,
155+
"work_calendars",
156+
self.props,
157+
"active_work_calendar_index",
158+
)
159+
160+
if self.props.active_work_calendar_id:
161+
self.draw_editable_ui(context)
162+
163+
def draw_editable_ui(self, context):
164+
for attribute in self.props.work_calendar_attributes:
125165
row = self.layout.row(align=True)
126-
row.label(text=work_plan["Name"] or "Unnamed", icon="TEXT")
127-
row.operator("bim.add_work_plan", text="", icon="GREASEPENCIL")
128-
row.operator("bim.remove_work_plan", text="", icon="X").work_plan = work_plan_id
166+
row.prop(attribute, "string_value", text=attribute.name)
167+
if attribute.is_optional:
168+
row.prop(attribute, "is_null", icon="RADIOBUT_OFF" if attribute.is_null else "RADIOBUT_ON", text="")
169+
170+
class BIM_UL_work_calendars(UIList):
171+
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
172+
if item:
173+
row = layout.row(align=True)
174+
row.label(text=item.name)
175+
if context.scene.BIMWorkCalendarProperties.active_work_calendar_id == item.ifc_definition_id:
176+
row.operator("bim.edit_work_calendar", text="", icon="CHECKMARK")
177+
row.operator("bim.disable_editing_work_calendar", text="", icon="X")
178+
elif context.scene.BIMWorkCalendarProperties.active_work_calendar_id:
179+
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id
180+
else:
181+
op = row.operator("bim.enable_editing_work_calendar", text="", icon="GREASEPENCIL")
182+
op.work_calendar = item.ifc_definition_id
183+
row.operator("bim.remove_work_calendar", text="", icon="X").work_calendar = item.ifc_definition_id
129184

130185

131186
class BIM_PT_tasks(Panel):
@@ -175,4 +230,4 @@ def draw_item(self, context, layout, data, item, icon, active_data, active_propn
175230
row = layout.row(align=True)
176231
if item.identification:
177232
layout.label(text=item.identification)
178-
layout.label(text=item.name)
233+
layout.label(text=item.name)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import ifcopenshell.api
2+
import ifcopenshell.util.date
3+
from datetime import datetime, timedelta
4+
5+
6+
class Usecase:
7+
def __init__(self, file, **settings):
8+
self.file = file
9+
self.settings = {
10+
"name": "Unnamed",
11+
"predefined_type": "NOTDEFINED",
12+
"working_times":[],
13+
"exception_times":[]
14+
}
15+
for key, value in settings.items():
16+
self.settings[key] = value
17+
18+
def execute(self):
19+
work_calendar = ifcopenshell.api.run(
20+
"root.create_entity",
21+
self.file,
22+
ifc_class="IfcWorkCalendar",
23+
predefined_type=self.settings["predefined_type"],
24+
name=self.settings["name"],
25+
)
26+
working_time = self.file.create_entity("IfcWorkTime", **{"Name":"DefaultWorkingTime"})
27+
self.settings["working_times"].append(working_time)
28+
work_calendar.WorkingTimes = self.settings["working_times"]
29+
30+
exception_time = self.file.create_entity("IfcWorkTime", **{"Name":"DefaultExceptionTime"})
31+
self.settings["exception_times"].append(exception_time)
32+
work_calendar.ExceptionTimes = self.settings["exception_times"]
33+
34+
context = self.file.by_type("IfcContext")[0]
35+
if context.Declares:
36+
rel_declares = context.Declares[0]
37+
ifcopenshell.api.run("owner.update_owner_history", self.file, element=rel_declares)
38+
else:
39+
rel_declares = self.file.create_entity("IfcRelDeclares", **{
40+
"GlobalId": ifcopenshell.guid.new(),
41+
"OwnerHistory": ifcopenshell.api.run("owner.create_owner_history", self.file),
42+
"RelatingContext": context
43+
})
44+
related_definitions = list(rel_declares.RelatedDefinitions or [])
45+
related_definitions.append(work_calendar)
46+
rel_declares.RelatedDefinitions = related_definitions
47+
48+
return work_calendar
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import ifcopenshell
2+
import ifcopenshell.api
3+
import ifcopenshell.util.date
4+
from datetime import datetime, timedelta
5+
6+
7+
class Usecase:
8+
def __init__(self, file, **settings):
9+
self.file = file
10+
self.settings = {
11+
"object": None,
12+
"name":None,
13+
"recurrence_pattern": None,
14+
"start": datetime.now(),
15+
"finish": datetime.now() + timedelta(days=365)
16+
}
17+
for key, value in settings.items():
18+
self.settings[key] = value
19+
20+
def execute(self):
21+
working_time = self.file.create_entity("IfcWorkTime", **{
22+
"Name": self.settings["name"],
23+
"Start": ifcopenshell.util.date.datetime2ifc(self.settings["start"], "IfcTime"),
24+
"Finish": ifcopenshell.util.date.datetime2ifc(self.settings["finish"], "IfcTime")
25+
})
26+
27+
#TODO Implement user settings for recurrence pattern
28+
working_time.RecurrencePattern = ifcopenshell.api.run("time.add_recurrence_pattern", self.file)
29+
30+
if self.settings["object"].is_a('IfcWorkCalendar'):
31+
if self.settings["object"].WorkingTimes:
32+
working_times = list(self.settings["object"].WorkingTimes)
33+
working_times.append(working_time)
34+
self.settings["object"].WorkingTimes = working_times
35+
else:
36+
self.settings["object"].WorkingTimes = [working_time]
37+
return working_time

0 commit comments

Comments
 (0)