-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathdetermine_mvd_scope.py
175 lines (141 loc) · 5.41 KB
/
determine_mvd_scope.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
import functools
import itertools
import re
import ifcopenshell
import ifcopenshell.mvd as mvd
from ifcopenshell.express import parse as express_parse
from collections import defaultdict
W = ifcopenshell.ifcopenshell_wrapper
def run(schema_fn, mvdxml_fn, concept_subset=None, additional=None):
entities = set()
types = set()
causes = defaultdict(set)
def collect_entities(cause, ent):
entities.add(ent)
causes[ent].add(cause)
def collect_entities_mvdxml(cause, rule, parent):
if rule.tag == "EntityRule":
collect_entities(cause, rule.attribute)
def wrap_try(fn, default=None):
def inner(*args):
try:
return fn(*args)
except:
return default
return inner
"""
visited_templates = set()
concept_roots = list(mvd.concept_root.parse(mvdxml_fn))
for cr in concept_roots:
if concept_subset is not None and cr.name not in concept_subset:
continue
entities.add(cr.entity)
causes[cr.entity].add('root')
for c in cr.concepts():
ref = c.concept_node.getElementsByTagNameNS("*","Template")[0].attributes['ref'].value
if ref in visited_templates:
continue
visited_templates.add(ref)
tmpl = c.template()
entities.add(tmpl.entity)
tmpl.traverse(functools.partial(collect_entities, c.name))
"""
dom = mvd.parse(mvdxml_fn)
class collector:
def template(self, id, visited):
for node in dom.getElementsByTagNameNS("*", "ConceptTemplate"):
if node.attributes["uuid"].value == id:
t = mvd.template(self, node)
t.parse(visited=visited)
return t
templs = list(
map(
functools.partial(mvd.template, collector()),
filter(
lambda root: root.attributes.get("applicableEntity"),
dom.getElementsByTagNameNS("*", "ConceptTemplate"),
),
)
)
builder = express_parse(schema_fn)
ifcopenshell.register_schema(builder)
S = ifcopenshell.ifcopenshell_wrapper.schema_by_name(builder.schema_name)
for t in templs:
if re.sub(r'[^a-zA-Z0-9]', "", t.name) in concept_subset:
t.root = t.root.cloneNode(deep=True)
try:
t.root.removeChild(t.root.getElementsByTagName("SubTemplates")[0])
except IndexError as e:
pass
t.parse()
t.traverse(functools.partial(collect_entities_mvdxml, t.name))
if additional and (bindings := additional.get(t.name.replace(" ", ""))):
for x in set(filter(wrap_try(S.declaration_by_name), itertools.chain.from_iterable(b.values() for b in bindings))):
collect_entities(t.name, x)
def yield_supertypes(en):
yield en.name()
if en.supertype():
yield from yield_supertypes(en.supertype())
entities, pass1 = set(), sorted(entities)
for en in pass1:
decl = S.declaration_by_name(en)
if isinstance(decl, W.entity):
for st in yield_supertypes(decl):
entities.add(st)
if st != en:
causes[st].add(en)
def visit_typedecl(ty, cause=None):
if isinstance(ty, W.named_type):
visit_typedecl(ty.declared_type(), cause=cause)
elif isinstance(ty, W.type_declaration):
types.add(ty.name())
causes[ty.name()].add(cause)
elif isinstance(ty, W.aggregation_type):
visit_typedecl(ty.type_of_element(), cause=cause)
elif isinstance(ty, W.entity):
visit_entity(ty, cause=cause)
elif isinstance(ty, W.enumeration_type):
types.add(ty.name())
causes[ty.name()].add(cause)
elif isinstance(ty, W.simple_type):
pass
elif isinstance(ty, W.select_type):
# @nb this is important, select types do *not* result
# in broadening the mvd scope. Templates need explicitly
# incorporate selected subtypes for them to be in scope.
# causes[ty.name()].add(cause)
# for dd in ty.select_list():
# visit_typedecl(dd, cause=ty.name())
pass
else:
breakpoint()
visited = set()
def visit_entity(en, cause=None):
if en.name() in visited:
return
visited.add(en.name())
entities.add(en.name())
causes[en.name()].add(cause)
# print(en.name())
for attr in en.attributes():
# print(attr.name())
visit_typedecl(attr.type_of_attribute(), f"{en.name()}.{attr.name()}")
for en in list(entities):
decl = S.declaration_by_name(en)
visit_entity(decl)
# for en in sorted(entities):
# print(en)
return {k: sorted(filter(None, v)) for k, v in causes.items()}
if __name__ == "__main__":
import sys
import json
schema_fn, mvdxml_fn = sys.argv[1:]
with open("xmi_mvd_concepts.json", "r") as f:
mvds = json.load(f)
with open("xmi_concepts.json", "r") as f:
additional = json.load(f)["GeneralUsage"]
usage = {}
for nm, concepts in mvds.items():
usage[nm] = run(schema_fn, mvdxml_fn, concepts, additional)
with open("mvd_entity_usage.json", "w") as f:
json.dump(usage, f, indent=1)