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.
Files changed (34) hide show
  1. materialsdb/__init__.py +3 -1
  2. materialsdb/cache.py +5 -6
  3. materialsdb/classes.py +14 -6
  4. materialsdb/config.py +3 -6
  5. materialsdb/construction.py +441 -0
  6. materialsdb/gui/__init__.py +0 -0
  7. materialsdb/gui/__main__.py +32 -0
  8. materialsdb/gui/discovery.py +28 -0
  9. materialsdb/gui/listener.py +151 -0
  10. materialsdb/gui/server.py +730 -0
  11. materialsdb/gui/static/app-constructions.js +639 -0
  12. materialsdb/gui/static/app.js +451 -0
  13. materialsdb/gui/static/constructions.html +107 -0
  14. materialsdb/gui/static/index.html +56 -0
  15. materialsdb/gui/static/picker-core.js +111 -0
  16. materialsdb/ifc/material_builder.py +295 -0
  17. materialsdb/ifc/material_psets.json +594 -580
  18. materialsdb/ifc/project_library.py +77 -212
  19. materialsdb/query.py +24 -0
  20. materialsdb/schema/MaterialsDBIndex100.xsd +60 -0
  21. materialsdb/schema/materialsdb102.xsd +820 -0
  22. materialsdb/schema/materialsdb103.xsd +938 -0
  23. materialsdb/serialiser.py +45 -18
  24. materialsdb/store.py +293 -0
  25. materialsdb/summary.py +83 -0
  26. materialsdb/utils.py +15 -21
  27. python_materialsdb-0.2.0.dist-info/METADATA +170 -0
  28. python_materialsdb-0.2.0.dist-info/RECORD +33 -0
  29. {python_materialsdb-0.0.2.dist-info → python_materialsdb-0.2.0.dist-info}/WHEEL +1 -1
  30. python_materialsdb-0.2.0.dist-info/entry_points.txt +2 -0
  31. python_materialsdb-0.0.2.dist-info/METADATA +0 -68
  32. python_materialsdb-0.0.2.dist-info/RECORD +0 -14
  33. {python_materialsdb-0.0.2.dist-info → python_materialsdb-0.2.0.dist-info/licenses}/LICENSE.md +0 -0
  34. {python_materialsdb-0.0.2.dist-info → python_materialsdb-0.2.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,151 @@
1
+ """Resolved push payloads for the listener channel (slice 1: add_materials).
2
+
3
+ The server resolves picker selections into exactly the entity list
4
+ MaterialBuilder would create (one entry per IfcMaterial); the Bonsai add-on
5
+ maps the payload onto its open file without importing this library."""
6
+
7
+ from materialsdb import config, utils
8
+ from materialsdb.ifc.material_builder import PSETS, get_value
9
+
10
+
11
+ def _resolved_psets(layer, country) -> dict:
12
+ resolved = {}
13
+ for pset_name, props in PSETS.items():
14
+ values = {}
15
+ for prop_name, definition in props.items():
16
+ if not definition["primary_measure_type"]:
17
+ continue
18
+ value = get_value(layer, definition, country)
19
+ if value:
20
+ values[prop_name] = value * (definition.get("unit_factor") or 1)
21
+ if values:
22
+ resolved[pset_name] = values
23
+ return resolved
24
+
25
+
26
+ def build_add_materials_payload(store_, items, country=None, lang=None):
27
+ """Resolve picker items [{id, layer_ids?}] into
28
+ ({"action": "add_materials", "materials": [...]}, missing_ids).
29
+
30
+ One materials[] entry per IfcMaterial, mirroring MaterialBuilder.build():
31
+ a 2-layer material yields 2 same-named entries, each carrying its own
32
+ layer's resolved psets. Ids that fail to resolve (unknown, or no
33
+ resolvable layers) land in missing_ids."""
34
+ country = country or config.get_country()
35
+ lang = lang or config.get_lang()
36
+ materials = []
37
+ missing = []
38
+ for item in items:
39
+ if not isinstance(item, dict):
40
+ missing.append(str(item))
41
+ continue
42
+ material_id = str(item.get("id") or "")
43
+ summary = store_.get_summary(material_id)
44
+ material = store_.get(material_id)
45
+ if summary is None or material is None:
46
+ missing.append(material_id)
47
+ continue
48
+ wanted = {str(g) for g in item.get("layer_ids") or []} or None
49
+ name = str(utils.get_material_name(material, lang))
50
+ description = str(utils.get_material_description(material, lang))
51
+ category = str(getattr(material.information, "group", "") or "")
52
+ color = getattr(material.information, "color", None)
53
+ entries = []
54
+ for layer in utils.get_material_layers(material):
55
+ if wanted is not None and str(layer.id) not in wanted:
56
+ continue
57
+ geometry = utils.get_by_country(layer.geometry or (), country)
58
+ thick = getattr(geometry, "thick", None)
59
+ entries.append(
60
+ {
61
+ "source_id": material_id,
62
+ "layer_id": str(layer.id),
63
+ "name": name,
64
+ "description": description,
65
+ "category": category,
66
+ "color": int(color) if color else None,
67
+ "identity": {
68
+ "material_id": material_id,
69
+ "company_id": str(summary.company_id or ""),
70
+ "company": str(summary.company or ""),
71
+ },
72
+ "psets": _resolved_psets(layer, country),
73
+ "layer": {"layer_id": str(layer.id), "thick_m": thick / 1000} if thick else None,
74
+ }
75
+ )
76
+ if not entries:
77
+ missing.append(material_id)
78
+ continue
79
+ materials.extend(entries)
80
+ return {"action": "add_materials", "materials": materials}, missing
81
+
82
+
83
+ _DESIGN_USAGE_TO_TYPES = {
84
+ "consDesignForWall": ["IfcWallType"],
85
+ "consDesignForFloor": ["IfcSlabType"],
86
+ "consDesignForRoof": ["IfcRoofType"],
87
+ }
88
+ GENERIC_TYPES = ["IfcWallType", "IfcSlabType", "IfcRoofType"]
89
+
90
+
91
+ def build_add_construction_payload(store_, body, country=None, lang=None):
92
+ """Resolve a construction body into a self-sufficient add_construction
93
+ payload (or (None, problems) when validation fails). Materials travel
94
+ minimal — identity/name/category/color, to_ifc_layer_set-style — and
95
+ find-or-create on the add-on side keys on the identity pset."""
96
+ from materialsdb import construction as cm
97
+
98
+ body = dict(body or {})
99
+ if body.get("design_usage") not in (None, *_DESIGN_USAGE_TO_TYPES):
100
+ body["design_usage"] = None # unknown usage -> generic types (validate_construction would reject it)
101
+ construction, problems = cm.validate_construction(body, store_)
102
+ if problems:
103
+ return None, problems
104
+ country = country or config.get_country()
105
+ lang = lang or config.get_lang()
106
+ layers = []
107
+ for layer in construction.layers:
108
+ if layer.placeholder is not None:
109
+ layers.append(
110
+ {
111
+ "material_id": None,
112
+ "thickness_m": layer.thickness_m,
113
+ "placeholder": dict(layer.placeholder),
114
+ }
115
+ )
116
+ continue
117
+ summary = store_.get_summary(layer.material_id)
118
+ material = store_.get(layer.material_id)
119
+ if summary is None or material is None:
120
+ problems.append(f"unknown material id: {layer.material_id}")
121
+ continue
122
+ color = getattr(material.information, "color", None)
123
+ layers.append(
124
+ {
125
+ "material_id": layer.material_id,
126
+ "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
+ },
139
+ }
140
+ )
141
+ if problems:
142
+ return None, problems
143
+ return {
144
+ "action": "add_construction",
145
+ "construction": {
146
+ "name": construction.name,
147
+ "design_usage": construction.design_usage,
148
+ "types": _DESIGN_USAGE_TO_TYPES.get(construction.design_usage or "", GENERIC_TYPES),
149
+ "layers": layers,
150
+ },
151
+ }, []