python-materialsdb 0.0.2__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- materialsdb/__init__.py +3 -1
- materialsdb/cache.py +5 -6
- materialsdb/classes.py +14 -6
- materialsdb/config.py +3 -6
- materialsdb/construction.py +441 -0
- materialsdb/gui/__init__.py +0 -0
- materialsdb/gui/__main__.py +32 -0
- materialsdb/gui/discovery.py +28 -0
- materialsdb/gui/listener.py +151 -0
- materialsdb/gui/server.py +730 -0
- materialsdb/gui/static/app-constructions.js +639 -0
- materialsdb/gui/static/app.js +451 -0
- materialsdb/gui/static/constructions.html +107 -0
- materialsdb/gui/static/index.html +56 -0
- materialsdb/gui/static/picker-core.js +111 -0
- materialsdb/ifc/material_builder.py +295 -0
- materialsdb/ifc/material_psets.json +594 -580
- materialsdb/ifc/project_library.py +77 -212
- materialsdb/query.py +24 -0
- materialsdb/schema/MaterialsDBIndex100.xsd +60 -0
- materialsdb/schema/materialsdb102.xsd +820 -0
- materialsdb/schema/materialsdb103.xsd +938 -0
- materialsdb/serialiser.py +45 -18
- materialsdb/store.py +293 -0
- materialsdb/summary.py +83 -0
- materialsdb/utils.py +15 -21
- python_materialsdb-0.2.0.dist-info/METADATA +170 -0
- python_materialsdb-0.2.0.dist-info/RECORD +33 -0
- {python_materialsdb-0.0.2.dist-info → python_materialsdb-0.2.0.dist-info}/WHEEL +1 -1
- python_materialsdb-0.2.0.dist-info/entry_points.txt +2 -0
- python_materialsdb-0.0.2.dist-info/METADATA +0 -68
- python_materialsdb-0.0.2.dist-info/RECORD +0 -14
- {python_materialsdb-0.0.2.dist-info → python_materialsdb-0.2.0.dist-info/licenses}/LICENSE.md +0 -0
- {python_materialsdb-0.0.2.dist-info → python_materialsdb-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -1,63 +1,33 @@
|
|
|
1
1
|
import uuid
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
from pathlib import Path
|
|
2
|
+
|
|
5
3
|
import ifcopenshell
|
|
6
4
|
import ifcopenshell.api
|
|
7
5
|
|
|
8
|
-
from materialsdb.serialiser import XmlDeserialiser
|
|
9
6
|
from materialsdb import config, utils
|
|
10
7
|
from materialsdb.classes import (
|
|
11
8
|
Materials,
|
|
12
|
-
Material,
|
|
13
9
|
)
|
|
10
|
+
from materialsdb.ifc.material_builder import (
|
|
11
|
+
CATEGORIES,
|
|
12
|
+
MATERIALSDB_PSET,
|
|
13
|
+
PSETS,
|
|
14
|
+
MaterialBuilder,
|
|
15
|
+
clean_psets,
|
|
16
|
+
get_value,
|
|
17
|
+
)
|
|
18
|
+
from materialsdb.serialiser import XmlDeserialiser
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
|
|
26
|
-
"Stone": {"hatch": "", "color": (0, 0, 255)},
|
|
27
|
-
"Composite": {"hatch": "", "color": (112, 141, 35)},
|
|
28
|
-
"Films": {"hatch": "", "color": (0, 0, 0)},
|
|
29
|
-
"Render": {"hatch": "", "color": (0, 0, 0)},
|
|
30
|
-
"Covering": {"hatch": "", "color": (0, 0, 0)},
|
|
31
|
-
"Glas": {"hatch": "", "color": (27, 79, 8)},
|
|
32
|
-
"Soil": {"hatch": "", "color": (142, 84, 52)},
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def clean_psets(psets):
|
|
37
|
-
new_dict = {}
|
|
38
|
-
for pset_name, props in psets.items():
|
|
39
|
-
pset_dict = {}
|
|
40
|
-
for prop_name, definition in props.items():
|
|
41
|
-
if definition["path"]:
|
|
42
|
-
pset_dict[prop_name] = definition
|
|
43
|
-
if pset_dict:
|
|
44
|
-
new_dict[pset_name] = pset_dict
|
|
45
|
-
return new_dict
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
PSETS = json.loads(Path(__file__).with_name("material_psets.json").read_text("utf-8"))
|
|
49
|
-
PSETS = clean_psets(PSETS)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def get_value(layer, definition, country=None):
|
|
53
|
-
value = layer
|
|
54
|
-
for attrib in definition["path"]:
|
|
55
|
-
value = getattr(value, attrib)
|
|
56
|
-
if isinstance(value, list):
|
|
57
|
-
value = utils.get_by_country(value, country)
|
|
58
|
-
if not value:
|
|
59
|
-
return None
|
|
60
|
-
return value
|
|
20
|
+
# Re-exports: external code may import these from project_library (historical API).
|
|
21
|
+
__all__ = [
|
|
22
|
+
"CATEGORIES",
|
|
23
|
+
"MATERIALSDB_PSET",
|
|
24
|
+
"PSETS",
|
|
25
|
+
"MaterialBuilder",
|
|
26
|
+
"ProjectLibrary",
|
|
27
|
+
"clean_psets",
|
|
28
|
+
"create_project_library_from_xml",
|
|
29
|
+
"get_value",
|
|
30
|
+
]
|
|
61
31
|
|
|
62
32
|
|
|
63
33
|
class ProjectLibrary:
|
|
@@ -68,6 +38,7 @@ class ProjectLibrary:
|
|
|
68
38
|
self.lang = config.get_lang()
|
|
69
39
|
self.country = config.get_country()
|
|
70
40
|
self.owner_history = None
|
|
41
|
+
self.builder = MaterialBuilder(self.file, country=self.country, lang=self.lang)
|
|
71
42
|
|
|
72
43
|
def create_application(self):
|
|
73
44
|
file = self.file
|
|
@@ -94,198 +65,92 @@ class ProjectLibrary:
|
|
|
94
65
|
ApplicationIdentifier="ifcopenshell",
|
|
95
66
|
)
|
|
96
67
|
|
|
97
|
-
def create_project_library(self,
|
|
68
|
+
def create_project_library(self, company: str, companyid: str, ver: int, crd, role: str = "MANUFACTURER"):
|
|
98
69
|
file = self.file
|
|
99
70
|
|
|
100
|
-
|
|
101
|
-
role = file.createIfcActorRole(role)
|
|
102
|
-
|
|
103
|
-
# https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/link/ifcperson.htm
|
|
71
|
+
role_entity = file.createIfcActorRole(role)
|
|
104
72
|
person = file.createIfcPerson(
|
|
105
73
|
Identification=str(uuid.uuid4()),
|
|
106
74
|
FamilyName="Unknown",
|
|
107
75
|
GivenName="Unknown",
|
|
108
|
-
Roles=[
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
# https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/link/ifcorganization.htm
|
|
112
|
-
organisation = file.createIfcOrganization(
|
|
113
|
-
Identification=source.companyid, Name=source.company, Roles=[role]
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
# https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/link/ifcpersonandorganization.htm
|
|
117
|
-
person_and_organisation = file.createIfcPersonAndOrganization(
|
|
118
|
-
person, organisation, Roles=[role]
|
|
76
|
+
Roles=[role_entity],
|
|
119
77
|
)
|
|
120
|
-
|
|
121
|
-
|
|
78
|
+
organisation = file.createIfcOrganization(Identification=str(companyid), Name=str(company), Roles=[role_entity])
|
|
79
|
+
person_and_organisation = file.createIfcPersonAndOrganization(person, organisation, Roles=[role_entity])
|
|
122
80
|
owner_history = file.createIfcOwnerHistory(
|
|
123
81
|
OwningUser=person_and_organisation,
|
|
124
82
|
OwningApplication=self.application,
|
|
125
|
-
CreationDate=max(int(utils.date_from_xml(
|
|
83
|
+
CreationDate=max(int(utils.date_from_xml(crd).timestamp()), -2147483648),
|
|
126
84
|
)
|
|
127
85
|
self.owner_history = owner_history
|
|
128
|
-
|
|
129
|
-
# https://standards.buildingsmart.org/IFC/RELEASE/IFC4/ADD2_TC1/HTML/link/ifcprojectlibrary.htm
|
|
130
86
|
file.createIfcProjectLibrary(
|
|
131
87
|
GlobalId=ifcopenshell.guid.new(),
|
|
132
88
|
OwnerHistory=owner_history,
|
|
133
|
-
Name=
|
|
134
|
-
Description=f"Material library converted from materialsdb xml for company {
|
|
89
|
+
Name=str(company),
|
|
90
|
+
Description=f"Material library converted from materialsdb xml for company {company}",
|
|
135
91
|
ObjectType="MaterialLibrary",
|
|
136
|
-
LongName=f"{
|
|
92
|
+
LongName=f"{company} version {ver}",
|
|
137
93
|
)
|
|
138
94
|
|
|
139
95
|
def create_materials(self, source: Materials):
|
|
140
|
-
file = self.file
|
|
141
|
-
context = file.createIfcRepresentationContext()
|
|
142
96
|
for material in utils.get_materials(source, self.country):
|
|
97
|
+
created = self.builder.build(material)
|
|
98
|
+
if not created:
|
|
99
|
+
continue
|
|
143
100
|
name = utils.get_material_name(material, self.lang)
|
|
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
|
-
"IfcPropertySingleValue",
|
|
172
|
-
Name=prop_name,
|
|
173
|
-
NominalValue=file.create_entity(
|
|
174
|
-
primary_measure_type, value * unit_factor
|
|
175
|
-
),
|
|
176
|
-
)
|
|
177
|
-
)
|
|
178
|
-
if not properties:
|
|
179
|
-
continue
|
|
180
|
-
pset = file.create_entity(
|
|
181
|
-
"IfcMaterialProperties",
|
|
182
|
-
Name=pset_name,
|
|
183
|
-
Properties=properties,
|
|
184
|
-
Material=ifc_material,
|
|
185
|
-
)
|
|
186
|
-
geometry = utils.get_by_country(layer.geometry, self.country)
|
|
187
|
-
if getattr(geometry, "thick", None):
|
|
188
|
-
element_name = f"{name} | {geometry.thick}mm"
|
|
189
|
-
ifc_layer = file.create_entity(
|
|
190
|
-
"IfcMaterialLayer",
|
|
191
|
-
Material=ifc_material,
|
|
192
|
-
LayerThickness=geometry.thick / 1000,
|
|
193
|
-
Name=element_name,
|
|
194
|
-
)
|
|
195
|
-
assigned_material = file.create_entity(
|
|
196
|
-
"IfcMaterialLayerSet",
|
|
197
|
-
MaterialLayers=[ifc_layer],
|
|
198
|
-
LayerSetName=element_name,
|
|
199
|
-
)
|
|
200
|
-
else:
|
|
201
|
-
element_name = name
|
|
202
|
-
assigned_material = ifc_material
|
|
203
|
-
if material.information.wall:
|
|
204
|
-
wall = file.create_entity(
|
|
205
|
-
"IfcWallType",
|
|
206
|
-
GlobalId=ifcopenshell.guid.new(),
|
|
207
|
-
Name=element_name,
|
|
208
|
-
)
|
|
209
|
-
ifcopenshell.api.run(
|
|
210
|
-
"material.assign_material",
|
|
211
|
-
file,
|
|
212
|
-
product=wall,
|
|
213
|
-
material=assigned_material,
|
|
214
|
-
)
|
|
215
|
-
if material.information.roof:
|
|
216
|
-
roof = file.create_entity(
|
|
217
|
-
"IfcRoofType",
|
|
218
|
-
GlobalId=ifcopenshell.guid.new(),
|
|
219
|
-
Name=element_name,
|
|
220
|
-
)
|
|
221
|
-
ifcopenshell.api.run(
|
|
222
|
-
"material.assign_material",
|
|
223
|
-
file,
|
|
224
|
-
product=roof,
|
|
225
|
-
material=assigned_material,
|
|
226
|
-
)
|
|
227
|
-
if material.information.floor:
|
|
228
|
-
slab = file.create_entity(
|
|
229
|
-
"IfcRoofType",
|
|
230
|
-
GlobalId=ifcopenshell.guid.new(),
|
|
231
|
-
Name=element_name,
|
|
232
|
-
)
|
|
233
|
-
ifcopenshell.api.run(
|
|
234
|
-
"material.assign_material",
|
|
235
|
-
file,
|
|
236
|
-
product=slab,
|
|
237
|
-
material=assigned_material,
|
|
238
|
-
)
|
|
239
|
-
if material.information.door:
|
|
240
|
-
door = file.create_entity(
|
|
241
|
-
"IfcDoorType",
|
|
242
|
-
GlobalId=ifcopenshell.guid.new(),
|
|
243
|
-
Name=element_name,
|
|
244
|
-
)
|
|
245
|
-
ifcopenshell.api.run(
|
|
246
|
-
"material.assign_material",
|
|
247
|
-
file,
|
|
248
|
-
product=door,
|
|
249
|
-
material=assigned_material,
|
|
250
|
-
)
|
|
101
|
+
for layer, ifc_material in zip(utils.get_material_layers(material), created):
|
|
102
|
+
geometry = utils.get_by_country(layer.geometry or (), self.country)
|
|
103
|
+
thick = getattr(geometry, "thick", None)
|
|
104
|
+
element_name = f"{name} | {thick}mm" if thick else name
|
|
105
|
+
assigned_material = self._assigned_material(ifc_material, thick, element_name, name)
|
|
106
|
+
self._create_usage_types(material, assigned_material, element_name)
|
|
107
|
+
|
|
108
|
+
def _assigned_material(self, ifc_material, thick, element_name, name):
|
|
109
|
+
if not thick:
|
|
110
|
+
return ifc_material
|
|
111
|
+
# MaterialBuilder.build already created the layer + layer set for this
|
|
112
|
+
# layer; reuse them so batch export does not duplicate entities.
|
|
113
|
+
for rel in self.file.get_inverse(ifc_material):
|
|
114
|
+
if rel.is_a("IfcMaterialLayer"):
|
|
115
|
+
for layer_set in rel.ToMaterialLayerSet or ():
|
|
116
|
+
return layer_set
|
|
117
|
+
ifc_layer = self.file.create_entity(
|
|
118
|
+
"IfcMaterialLayer",
|
|
119
|
+
Material=ifc_material,
|
|
120
|
+
LayerThickness=thick / 1000,
|
|
121
|
+
Name=str(element_name),
|
|
122
|
+
)
|
|
123
|
+
return self.file.create_entity(
|
|
124
|
+
"IfcMaterialLayerSet",
|
|
125
|
+
MaterialLayers=[ifc_layer],
|
|
126
|
+
LayerSetName=str(element_name),
|
|
127
|
+
)
|
|
251
128
|
|
|
252
|
-
def
|
|
129
|
+
def _create_usage_types(self, material, assigned_material, element_name):
|
|
253
130
|
file = self.file
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
SurfaceColour=self.file.createIfcColourRgb(
|
|
268
|
-
None, *CATEGORIES[category]["color"]
|
|
269
|
-
),
|
|
270
|
-
)
|
|
271
|
-
for surface_style in file.by_type("IfcSurfaceStyle"):
|
|
272
|
-
if surface_style.Name == name:
|
|
273
|
-
return surface_style
|
|
274
|
-
return file.createIfcSurfaceStyle(Name=name, Side="BOTH", Styles=[style])
|
|
131
|
+
kind_map = (("wall", "IfcWallType"), ("roof", "IfcRoofType"), ("floor", "IfcRoofType"), ("door", "IfcDoorType"))
|
|
132
|
+
for flag, entity_type in kind_map:
|
|
133
|
+
if getattr(material.information, flag, None):
|
|
134
|
+
product = file.create_entity(entity_type, GlobalId=ifcopenshell.guid.new(), Name=str(element_name))
|
|
135
|
+
ifcopenshell.api.run(
|
|
136
|
+
"material.assign_material",
|
|
137
|
+
file,
|
|
138
|
+
products=[product],
|
|
139
|
+
material=assigned_material,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def get_surface_style(self, color, category):
|
|
143
|
+
return self.builder.get_surface_style(color, category)
|
|
275
144
|
|
|
276
145
|
def color_xml_to_ifc(self, color: int):
|
|
277
|
-
|
|
278
|
-
See: https://stackoverflow.com/a/2262152/4098083"""
|
|
279
|
-
return self.file.createIfcColourRgb(
|
|
280
|
-
Blue=color & 255, Green=(color >> 8) & 255, Red=(color >> 16) & 255
|
|
281
|
-
)
|
|
146
|
+
return self.builder.color_xml_to_ifc(color)
|
|
282
147
|
|
|
283
148
|
|
|
284
149
|
def create_project_library_from_xml(xml_path):
|
|
285
150
|
library = ProjectLibrary()
|
|
286
151
|
deserialiser = XmlDeserialiser()
|
|
287
152
|
source = deserialiser.from_xml(str(xml_path))
|
|
288
|
-
library.create_project_library(source)
|
|
153
|
+
library.create_project_library(company=source.company, companyid=source.companyid, ver=source.ver, crd=source.crd)
|
|
289
154
|
library.create_materials(source)
|
|
290
155
|
return library.file
|
|
291
156
|
|
materialsdb/query.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Convenience facade over the SQLite material store."""
|
|
2
|
+
|
|
3
|
+
from functools import lru_cache
|
|
4
|
+
|
|
5
|
+
from materialsdb.classes import Material
|
|
6
|
+
from materialsdb.store import MaterialStore, Report
|
|
7
|
+
from materialsdb.summary import MaterialSummary
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@lru_cache(maxsize=1)
|
|
11
|
+
def get_store() -> MaterialStore:
|
|
12
|
+
return MaterialStore()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_material(material_id: str) -> Material | None:
|
|
16
|
+
return get_store().get(material_id)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def search(text: str, **filters) -> list[MaterialSummary]:
|
|
20
|
+
return get_store().summaries(text=text, **filters)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def refresh(force: bool = False) -> Report:
|
|
24
|
+
return get_store().refresh(force=force)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>
|
|
2
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
|
3
|
+
targetNamespace="http://www.materialsDB.org"
|
|
4
|
+
xmlns="http://www.materialsDB.org" elementFormDefault="qualified" version="1.00">
|
|
5
|
+
<!--
|
|
6
|
+
An XML XSD for Open Materials database (OMaterialsDB 1.00)
|
|
7
|
+
DOCTYPE MaterialsDBIndex
|
|
8
|
+
SYSTEM http://www.materialsdb.org/schemas/MaterialsDBIndex100.xsd
|
|
9
|
+
See the documentation and examples at http://www.materialsdb.org/
|
|
10
|
+
(c) 2007-2013 E4tech Software SA, Edouard Westphal
|
|
11
|
+
released under the GNU Public License (GPL)
|
|
12
|
+
-->
|
|
13
|
+
<!--
|
|
14
|
+
1.00 : 2013.04.17 first version
|
|
15
|
+
-->
|
|
16
|
+
<xs:simpleType name="TDateTime">
|
|
17
|
+
<xs:annotation>
|
|
18
|
+
<xs:documentation>
|
|
19
|
+
The integral part of a TDateTime value is the number of days that have passed since 12/30/1899.
|
|
20
|
+
The fractional part of the TDateTime value is fraction of a 24 hour day that has elapsed.
|
|
21
|
+
</xs:documentation>
|
|
22
|
+
</xs:annotation>
|
|
23
|
+
<xs:restriction base="xs:float"/>
|
|
24
|
+
</xs:simpleType>
|
|
25
|
+
<xs:simpleType name="guid">
|
|
26
|
+
<xs:restriction base="xs:string">
|
|
27
|
+
<xs:pattern value="[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"/>
|
|
28
|
+
</xs:restriction>
|
|
29
|
+
</xs:simpleType>
|
|
30
|
+
<xs:element name='MaterialsDBIndex'>
|
|
31
|
+
<xs:complexType>
|
|
32
|
+
<xs:sequence>
|
|
33
|
+
<xs:element ref="company" maxOccurs="unbounded"/>
|
|
34
|
+
</xs:sequence>
|
|
35
|
+
<xs:attribute name="verXML" type="xs:unsignedInt" use="required"/>
|
|
36
|
+
</xs:complexType>
|
|
37
|
+
</xs:element>
|
|
38
|
+
<xs:element name="company">
|
|
39
|
+
<xs:complexType>
|
|
40
|
+
<xs:simpleContent>
|
|
41
|
+
<xs:extension base="xs:string">
|
|
42
|
+
<xs:attribute name="id" type="guid" use="required"/>
|
|
43
|
+
<xs:attribute name="name" type="xs:string" use="required"/>
|
|
44
|
+
<xs:attribute name="href" type="xs:anyURI" use="required"/>
|
|
45
|
+
<xs:attribute name="LastKnownDate" type="TDateTime" use="required"/>
|
|
46
|
+
<xs:attribute name="KnownVersion" type="xs:unsignedInt" use="required"/>
|
|
47
|
+
<xs:attribute name="KnownCRD" type="TDateTime" use="required"/>
|
|
48
|
+
</xs:extension>
|
|
49
|
+
</xs:simpleContent>
|
|
50
|
+
</xs:complexType>
|
|
51
|
+
</xs:element>
|
|
52
|
+
</xs:schema>
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|