python-materialsdb 0.3.1__py3-none-any.whl → 0.4.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/classes.py +1 -1
- materialsdb/config.py +17 -0
- materialsdb/gui/listener.py +113 -16
- materialsdb/gui/server.py +561 -5
- materialsdb/gui/static/app-constructions.js +8 -19
- materialsdb/gui/static/app.js +308 -35
- materialsdb/gui/static/constructions.html +1 -5
- materialsdb/gui/static/index.html +20 -5
- materialsdb/gui/static/settings.html +74 -0
- materialsdb/gui/static/settings.js +643 -0
- materialsdb/ifc/material_builder.py +91 -48
- materialsdb/schemes.py +196 -0
- materialsdb/store.py +101 -0
- materialsdb/utils.py +5 -0
- {python_materialsdb-0.3.1.dist-info → python_materialsdb-0.4.0.dist-info}/METADATA +17 -2
- python_materialsdb-0.4.0.dist-info/RECORD +36 -0
- python_materialsdb-0.3.1.dist-info/RECORD +0 -33
- {python_materialsdb-0.3.1.dist-info → python_materialsdb-0.4.0.dist-info}/WHEEL +0 -0
- {python_materialsdb-0.3.1.dist-info → python_materialsdb-0.4.0.dist-info}/entry_points.txt +0 -0
- {python_materialsdb-0.3.1.dist-info → python_materialsdb-0.4.0.dist-info}/licenses/LICENSE.md +0 -0
- {python_materialsdb-0.3.1.dist-info → python_materialsdb-0.4.0.dist-info}/top_level.txt +0 -0
materialsdb/classes.py
CHANGED
|
@@ -93,7 +93,7 @@ class Guid(str):
|
|
|
93
93
|
|
|
94
94
|
class Brackguid(str):
|
|
95
95
|
xs_type: str = "simpleType"
|
|
96
|
-
xml_pattern:str = "
|
|
96
|
+
xml_pattern:str = "\\{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\\}"
|
|
97
97
|
xml_name: str = "Brackguid"
|
|
98
98
|
|
|
99
99
|
|
materialsdb/config.py
CHANGED
|
@@ -50,6 +50,23 @@ def set_country(country: str):
|
|
|
50
50
|
set_param("country", country)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
def get_scheme() -> str:
|
|
54
|
+
return str(get_base_config().get("scheme") or "")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def set_scheme(name: str) -> None:
|
|
58
|
+
set_param("scheme", str(name))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_ignore_producer_color() -> bool:
|
|
62
|
+
value = get_base_config().get("ignore_producer_color", "false")
|
|
63
|
+
return str(value).strip().lower() in ("true", "1", "yes", "on")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def set_ignore_producer_color(value: bool) -> None:
|
|
67
|
+
set_param("ignore_producer_color", "true" if value else "false")
|
|
68
|
+
|
|
69
|
+
|
|
53
70
|
def main():
|
|
54
71
|
lang = get_lang()
|
|
55
72
|
set_lang(lang)
|
materialsdb/gui/listener.py
CHANGED
|
@@ -5,7 +5,8 @@ MaterialBuilder would create (one entry per IfcMaterial); the Bonsai add-on
|
|
|
5
5
|
maps the payload onto its open file without importing this library."""
|
|
6
6
|
|
|
7
7
|
from materialsdb import config, utils
|
|
8
|
-
from materialsdb.ifc.material_builder import PSETS, get_value
|
|
8
|
+
from materialsdb.ifc.material_builder import PSETS, get_value, style_for
|
|
9
|
+
from materialsdb.store import FINGERPRINT_SCHEME
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def _resolved_psets(layer, country) -> dict:
|
|
@@ -45,11 +46,21 @@ def build_add_materials_payload(store_, items, country=None, lang=None):
|
|
|
45
46
|
if summary is None or material is None:
|
|
46
47
|
missing.append(material_id)
|
|
47
48
|
continue
|
|
49
|
+
fingerprint = store_.material_fingerprint(material_id)
|
|
48
50
|
wanted = {str(g) for g in item.get("layer_ids") or []} or None
|
|
49
51
|
name = str(utils.get_material_name(material, lang))
|
|
50
52
|
description = str(utils.get_material_description(material, lang))
|
|
51
53
|
category = str(getattr(material.information, "group", "") or "")
|
|
52
54
|
color = getattr(material.information, "color", None)
|
|
55
|
+
color = int(color) if color else None
|
|
56
|
+
# Resolve the surface style server-side: the add-on runs in Blender's
|
|
57
|
+
# interpreter without access to the materialsdb colour scheme.
|
|
58
|
+
style_name, style_rgb = style_for(
|
|
59
|
+
color,
|
|
60
|
+
category,
|
|
61
|
+
scheme=config.get_scheme(),
|
|
62
|
+
ignore_producer_color=config.get_ignore_producer_color(),
|
|
63
|
+
)
|
|
53
64
|
entries = []
|
|
54
65
|
for layer in utils.get_material_layers(material):
|
|
55
66
|
if wanted is not None and str(layer.id) not in wanted:
|
|
@@ -63,12 +74,20 @@ def build_add_materials_payload(store_, items, country=None, lang=None):
|
|
|
63
74
|
"name": name,
|
|
64
75
|
"description": description,
|
|
65
76
|
"category": category,
|
|
66
|
-
"color":
|
|
77
|
+
"color": color,
|
|
78
|
+
"style_name": style_name,
|
|
79
|
+
"style_color": [round(component, 6) for component in style_rgb],
|
|
67
80
|
"identity": {
|
|
68
81
|
"material_id": material_id,
|
|
69
82
|
"company_id": str(summary.company_id or ""),
|
|
70
83
|
"company": str(summary.company or ""),
|
|
84
|
+
"fingerprint": fingerprint,
|
|
85
|
+
"fingerprint_scheme": FINGERPRINT_SCHEME,
|
|
71
86
|
},
|
|
87
|
+
"mode": item.get("mode", "skip"),
|
|
88
|
+
"replaces": item.get("replaces"),
|
|
89
|
+
"update": item.get("update"),
|
|
90
|
+
"force": bool(item.get("force")),
|
|
72
91
|
"psets": _resolved_psets(layer, country),
|
|
73
92
|
"layer": {"layer_id": str(layer.id), "thick_m": thick / 1000} if thick else None,
|
|
74
93
|
}
|
|
@@ -80,6 +99,55 @@ def build_add_materials_payload(store_, items, country=None, lang=None):
|
|
|
80
99
|
return {"action": "add_materials", "materials": materials}, missing
|
|
81
100
|
|
|
82
101
|
|
|
102
|
+
def _layer_thick_m(layer, country) -> float | None:
|
|
103
|
+
"""The layer's thickness in metres for `country`, or None when absent."""
|
|
104
|
+
thick = getattr(utils.get_by_country(layer.geometry or (), country), "thick", None)
|
|
105
|
+
return thick / 1000 if thick is not None else None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _resolve_construction_layer(material, design_thickness_m, country, lang) -> tuple[object | None, str | None]:
|
|
109
|
+
"""Map a construction layer's design thickness onto a materialsdb Layer.
|
|
110
|
+
|
|
111
|
+
Exact thickness match wins; a single-layer material resolves to its only
|
|
112
|
+
layer whatever the design thickness; multi-layer mismatches fall back to
|
|
113
|
+
the first layer with a warning. Returns (layer_or_None, warning_or_None)."""
|
|
114
|
+
layers = list(utils.get_material_layers(material))
|
|
115
|
+
if not layers:
|
|
116
|
+
name = str(utils.get_material_name(material, lang))
|
|
117
|
+
return None, f"{name}: material has no layer"
|
|
118
|
+
for layer in layers:
|
|
119
|
+
thick_m = _layer_thick_m(layer, country)
|
|
120
|
+
if thick_m is not None and abs(thick_m - design_thickness_m) <= utils.THICKNESS_EPS_M:
|
|
121
|
+
return layer, None
|
|
122
|
+
if len(layers) == 1:
|
|
123
|
+
return layers[0], None
|
|
124
|
+
name = str(utils.get_material_name(material, lang))
|
|
125
|
+
return layers[0], f"{name}: design thickness {round(design_thickness_m * 1000)} mm matches no layer"
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _raw_decision_layer(raw_layers, material_id, thickness_m) -> dict:
|
|
129
|
+
"""The raw body layer carrying the decision for this validated layer.
|
|
130
|
+
|
|
131
|
+
``validate_construction`` can drop or reorder malformed layers, so the
|
|
132
|
+
validated index does not address ``raw_layers``. Match on the pair
|
|
133
|
+
(material_id, thickness) instead; when no exact match is unambiguous the
|
|
134
|
+
decision fields are omitted, i.e. the layer is treated as ``skip`` rather
|
|
135
|
+
than inheriting another layer's mode/update/replaces."""
|
|
136
|
+
matches = []
|
|
137
|
+
for raw in raw_layers:
|
|
138
|
+
if not isinstance(raw, dict):
|
|
139
|
+
continue
|
|
140
|
+
if str(raw.get("material_id")) != str(material_id):
|
|
141
|
+
continue
|
|
142
|
+
try:
|
|
143
|
+
raw_thickness = float(raw.get("thickness_m"))
|
|
144
|
+
except (TypeError, ValueError):
|
|
145
|
+
continue
|
|
146
|
+
if abs(raw_thickness - thickness_m) <= utils.THICKNESS_EPS_M:
|
|
147
|
+
matches.append(raw)
|
|
148
|
+
return matches[0] if len(matches) == 1 else {}
|
|
149
|
+
|
|
150
|
+
|
|
83
151
|
_DESIGN_USAGE_TO_TYPES = {
|
|
84
152
|
"consDesignForWall": ["IfcWallType"],
|
|
85
153
|
"consDesignForFloor": ["IfcSlabType"],
|
|
@@ -91,8 +159,9 @@ GENERIC_TYPES = ["IfcWallType", "IfcSlabType", "IfcRoofType"]
|
|
|
91
159
|
def build_add_construction_payload(store_, body, country=None, lang=None):
|
|
92
160
|
"""Resolve a construction body into a self-sufficient add_construction
|
|
93
161
|
payload (or (None, problems) when validation fails). Materials travel
|
|
94
|
-
|
|
95
|
-
find-or-create on the add-on side keys on
|
|
162
|
+
resolved — identity/name/category/style plus their layer's materialsdb
|
|
163
|
+
psets — and find-or-create on the add-on side keys on
|
|
164
|
+
(material_id, org_layer layer_id)."""
|
|
96
165
|
from materialsdb import construction as cm
|
|
97
166
|
|
|
98
167
|
body = dict(body or {})
|
|
@@ -103,7 +172,11 @@ def build_add_construction_payload(store_, body, country=None, lang=None):
|
|
|
103
172
|
return None, problems
|
|
104
173
|
country = country or config.get_country()
|
|
105
174
|
lang = lang or config.get_lang()
|
|
175
|
+
raw_layers: list = []
|
|
176
|
+
if isinstance(body.get("layers"), list):
|
|
177
|
+
raw_layers = body["layers"]
|
|
106
178
|
layers = []
|
|
179
|
+
warnings = []
|
|
107
180
|
for layer in construction.layers:
|
|
108
181
|
if layer.placeholder is not None:
|
|
109
182
|
layers.append(
|
|
@@ -119,23 +192,46 @@ def build_add_construction_payload(store_, body, country=None, lang=None):
|
|
|
119
192
|
if summary is None or material is None:
|
|
120
193
|
problems.append(f"unknown material id: {layer.material_id}")
|
|
121
194
|
continue
|
|
195
|
+
resolved_layer, warning = _resolve_construction_layer(material, layer.thickness_m, country, lang)
|
|
196
|
+
if warning is not None:
|
|
197
|
+
warnings.append(warning)
|
|
198
|
+
raw_layer = _raw_decision_layer(raw_layers, layer.material_id, layer.thickness_m)
|
|
122
199
|
color = getattr(material.information, "color", None)
|
|
200
|
+
color = int(color) if color else None
|
|
201
|
+
category = str(getattr(material.information, "group", "") or "")
|
|
202
|
+
style_name, style_rgb = style_for(
|
|
203
|
+
color,
|
|
204
|
+
category,
|
|
205
|
+
scheme=config.get_scheme(),
|
|
206
|
+
ignore_producer_color=config.get_ignore_producer_color(),
|
|
207
|
+
)
|
|
208
|
+
material_entry = {
|
|
209
|
+
"source_id": layer.material_id,
|
|
210
|
+
"name": str(utils.get_material_name(material, lang)),
|
|
211
|
+
"description": str(utils.get_material_description(material, lang)),
|
|
212
|
+
"category": category,
|
|
213
|
+
"color": color,
|
|
214
|
+
"style_name": style_name,
|
|
215
|
+
"style_color": [round(component, 6) for component in style_rgb],
|
|
216
|
+
"identity": {
|
|
217
|
+
"material_id": layer.material_id,
|
|
218
|
+
"company_id": str(summary.company_id or ""),
|
|
219
|
+
"company": str(summary.company or ""),
|
|
220
|
+
"fingerprint": store_.material_fingerprint(layer.material_id),
|
|
221
|
+
"fingerprint_scheme": FINGERPRINT_SCHEME,
|
|
222
|
+
},
|
|
223
|
+
"mode": raw_layer.get("mode", "skip"),
|
|
224
|
+
"replaces": raw_layer.get("replaces"),
|
|
225
|
+
"update": raw_layer.get("update"),
|
|
226
|
+
"force": bool(raw_layer.get("force")),
|
|
227
|
+
}
|
|
228
|
+
if resolved_layer is not None:
|
|
229
|
+
material_entry["psets"] = _resolved_psets(resolved_layer, country)
|
|
123
230
|
layers.append(
|
|
124
231
|
{
|
|
125
232
|
"material_id": layer.material_id,
|
|
126
233
|
"thickness_m": layer.thickness_m,
|
|
127
|
-
"material":
|
|
128
|
-
"source_id": layer.material_id,
|
|
129
|
-
"name": str(utils.get_material_name(material, lang)),
|
|
130
|
-
"description": str(utils.get_material_description(material, lang)),
|
|
131
|
-
"category": str(getattr(material.information, "group", "") or ""),
|
|
132
|
-
"color": int(color) if color else None,
|
|
133
|
-
"identity": {
|
|
134
|
-
"material_id": layer.material_id,
|
|
135
|
-
"company_id": str(summary.company_id or ""),
|
|
136
|
-
"company": str(summary.company or ""),
|
|
137
|
-
},
|
|
138
|
-
},
|
|
234
|
+
"material": material_entry,
|
|
139
235
|
}
|
|
140
236
|
)
|
|
141
237
|
if problems:
|
|
@@ -151,5 +247,6 @@ def build_add_construction_payload(store_, body, country=None, lang=None):
|
|
|
151
247
|
cls: u for cls, u in cm.u_values_by_type(construction, store_, types).items() if u is not None
|
|
152
248
|
},
|
|
153
249
|
"layers": layers,
|
|
250
|
+
"warnings": warnings,
|
|
154
251
|
},
|
|
155
252
|
}, []
|