|
3 | 3 | from dataclasses import dataclass
|
4 | 4 | from enum import Enum
|
5 | 5 | from numbers import Number
|
6 |
| -from typing import List, Union |
| 6 | +from typing import List, Union, Optional |
7 | 7 |
|
8 | 8 | from blinker import signal
|
9 | 9 | from bw2data import Database, databases
|
10 |
| -from bw2data.backends import Exchange |
| 10 | +from bw2data.backends import Exchange, Node |
11 | 11 | from bw2data.project import ProjectDataset, projects
|
12 | 12 |
|
13 | 13 | from . import allocation_strategies
|
@@ -87,111 +87,129 @@ def list_available_properties(database_label: str):
|
87 | 87 | return results
|
88 | 88 |
|
89 | 89 |
|
90 |
| -def check_property_for_allocation( |
91 |
| - database_label: str, property_label: str |
| 90 | +def check_property_for_process_allocation( |
| 91 | + process: Node, property_label: str, messages: Optional[List[PropertyMessage]] = None |
92 | 92 | ) -> Union[bool, List[PropertyMessage]]:
|
93 | 93 | """
|
94 |
| - Check that the given property is present for all functional edges in `multifunctional` |
95 |
| - processes. |
| 94 | + Check that the given property is present for all functional edges in a given process. |
96 | 95 |
|
97 |
| - `database_label`: String label of an existing database. |
| 96 | + `process`: Multifunctional process `Node`. |
98 | 97 | `property_label`: String label of the property to be used for allocation.
|
99 | 98 |
|
100 | 99 | If all the needed data is present, returns `True`.
|
101 | 100 |
|
102 | 101 | If there is missing data, returns a list of `PropertyMessage` objects.
|
103 | 102 | """
|
104 |
| - if database_label not in databases: |
105 |
| - raise ValueError(f"Database `{database_label}` not defined in this project") |
| 103 | + if messages is None: |
| 104 | + messages = [] |
106 | 105 |
|
107 |
| - db = Database(database_label) |
108 |
| - messages = [] |
| 106 | + if process['type'] != "multifunctional": |
| 107 | + return True |
109 | 108 |
|
110 |
| - for ds in filter(lambda x: x["type"] == "multifunctional", db): |
111 |
| - for edge in filter(lambda x: x.get("functional"), ds.exchanges()): |
112 |
| - properties = _get_unified_properties(edge) |
113 |
| - if ( |
114 |
| - property_label not in properties |
115 |
| - and edge.input["type"] != "readonly_process" |
116 |
| - ): |
117 |
| - messages.append( |
118 |
| - PropertyMessage( |
119 |
| - level=logging.WARNING, |
120 |
| - process_id=ds.id, |
121 |
| - product_id=edge.input.id, |
122 |
| - message_type=MessageType.MISSING_PRODUCT_PROPERTY, |
123 |
| - message=f"""Product is missing a property value for `{property_label}`. |
124 |
| - Missing values are treated as zeros. |
125 |
| - Please define this property for the product: |
126 |
| - {edge.input} |
127 |
| - Referenced by multifunctional process: |
128 |
| - {ds} |
| 109 | + for edge in filter(lambda x: x.get("functional"), process.exchanges()): |
| 110 | + properties = _get_unified_properties(edge) |
| 111 | + if ( |
| 112 | + property_label not in properties |
| 113 | + and edge.input["type"] != "readonly_process" |
| 114 | + ): |
| 115 | + messages.append( |
| 116 | + PropertyMessage( |
| 117 | + level=logging.WARNING, |
| 118 | + process_id=process.id, |
| 119 | + product_id=edge.input.id, |
| 120 | + message_type=MessageType.MISSING_PRODUCT_PROPERTY, |
| 121 | + message=f"""Product is missing a property value for `{property_label}`. |
| 122 | +Missing values are treated as zeros. |
| 123 | +Please define this property for the product: |
| 124 | + {edge.input} |
| 125 | +Referenced by multifunctional process: |
| 126 | + {process} |
129 | 127 |
|
130 | 128 | """,
|
131 |
| - ) |
132 | 129 | )
|
133 |
| - elif property_label not in properties: |
134 |
| - messages.append( |
135 |
| - PropertyMessage( |
136 |
| - level=logging.WARNING, |
137 |
| - process_id=ds.id, |
138 |
| - product_id=edge.input.id, |
139 |
| - message_type=MessageType.MISSING_EDGE_PROPERTY, |
140 |
| - message=f"""Functional edge is missing a property value for `{property_label}`. |
141 |
| - Missing values are treated as zeros. |
142 |
| - Please define this property for the edge: |
143 |
| - {edge} |
144 |
| - Found in multifunctional process: |
145 |
| - {ds} |
| 130 | + ) |
| 131 | + elif property_label not in properties: |
| 132 | + messages.append( |
| 133 | + PropertyMessage( |
| 134 | + level=logging.WARNING, |
| 135 | + process_id=process.id, |
| 136 | + product_id=edge.input.id, |
| 137 | + message_type=MessageType.MISSING_EDGE_PROPERTY, |
| 138 | + message=f"""Functional edge is missing a property value for `{property_label}`. |
| 139 | +Missing values are treated as zeros. |
| 140 | +Please define this property for the edge: |
| 141 | + {edge} |
| 142 | +Found in multifunctional process: |
| 143 | + {process} |
146 | 144 |
|
147 | 145 | """,
|
148 |
| - ) |
149 | 146 | )
|
150 |
| - elif ( |
151 |
| - not isinstance(properties[property_label], Number) |
152 |
| - or isinstance(properties[property_label], bool) |
153 |
| - and edge.input["type"] != "readonly_process" |
154 |
| - ): |
155 |
| - messages.append( |
156 |
| - PropertyMessage( |
157 |
| - level=logging.CRITICAL, |
158 |
| - process_id=ds.id, |
159 |
| - product_id=edge.input.id, |
160 |
| - message_type=MessageType.NONNUMERIC_PRODUCT_PROPERTY, |
161 |
| - message=f"""Found non-numeric value `{properties[property_label]}` in property `{property_label}`. |
162 |
| - Please redefine this property for the product: |
163 |
| - {edge.input} |
164 |
| - Referenced by multifunctional process: |
165 |
| - {ds} |
| 147 | + ) |
| 148 | + elif ( |
| 149 | + not isinstance(properties[property_label], Number) |
| 150 | + or isinstance(properties[property_label], bool) |
| 151 | + and edge.input["type"] != "readonly_process" |
| 152 | + ): |
| 153 | + messages.append( |
| 154 | + PropertyMessage( |
| 155 | + level=logging.CRITICAL, |
| 156 | + process_id=process.id, |
| 157 | + product_id=edge.input.id, |
| 158 | + message_type=MessageType.NONNUMERIC_PRODUCT_PROPERTY, |
| 159 | + message=f"""Found non-numeric value `{properties[property_label]}` in property `{property_label}`. |
| 160 | +Please redefine this property for the product: |
| 161 | + {edge.input} |
| 162 | +Referenced by multifunctional process: |
| 163 | + {process} |
166 | 164 |
|
167 | 165 | """,
|
168 |
| - ) |
169 | 166 | )
|
170 |
| - elif not isinstance(properties[property_label], Number) or isinstance( |
171 |
| - properties[property_label], bool |
172 |
| - ): |
173 |
| - messages.append( |
174 |
| - PropertyMessage( |
175 |
| - level=logging.CRITICAL, |
176 |
| - process_id=ds.id, |
177 |
| - product_id=edge.input.id, |
178 |
| - message_type=MessageType.NONNUMERIC_EDGE_PROPERTY, |
179 |
| - message=f"""Found non-numeric value `{properties[property_label]}` in property `{property_label}`. |
180 |
| - Please redefine this property for the edge: |
181 |
| - {edge} |
182 |
| - Found in multifunctional process: |
183 |
| - {ds} |
| 167 | + ) |
| 168 | + elif not isinstance(properties[property_label], Number) or isinstance( |
| 169 | + properties[property_label], bool |
| 170 | + ): |
| 171 | + messages.append( |
| 172 | + PropertyMessage( |
| 173 | + level=logging.CRITICAL, |
| 174 | + process_id=process.id, |
| 175 | + product_id=edge.input.id, |
| 176 | + message_type=MessageType.NONNUMERIC_EDGE_PROPERTY, |
| 177 | + message=f"""Found non-numeric value `{properties[property_label]}` in property `{property_label}`. |
| 178 | +Please redefine this property for the edge: |
| 179 | + {edge} |
| 180 | +Found in multifunctional process: |
| 181 | + {process} |
184 | 182 |
|
185 | 183 | """,
|
186 |
| - ) |
187 |
| - ) |
188 |
| - else: |
189 |
| - print( |
190 |
| - "No problem with:", |
191 |
| - properties, |
192 |
| - property_label, |
193 |
| - isinstance(properties[property_label], Number), |
194 | 184 | )
|
| 185 | + ) |
| 186 | + |
| 187 | + return messages or True |
| 188 | + |
| 189 | + |
| 190 | +def check_property_for_allocation( |
| 191 | + database_label: str, property_label: str |
| 192 | +) -> Union[bool, List[PropertyMessage]]: |
| 193 | + """ |
| 194 | + Check that the given property is present for all functional edges in `multifunctional` |
| 195 | + processes. |
| 196 | +
|
| 197 | + `database_label`: String label of an existing database. |
| 198 | + `property_label`: String label of the property to be used for allocation. |
| 199 | +
|
| 200 | + If all the needed data is present, returns `True`. |
| 201 | +
|
| 202 | + If there is missing data, returns a list of `PropertyMessage` objects. |
| 203 | + """ |
| 204 | + if database_label not in databases: |
| 205 | + raise ValueError(f"Database `{database_label}` not defined in this project") |
| 206 | + |
| 207 | + db = Database(database_label) |
| 208 | + messages = [] |
| 209 | + |
| 210 | + for ds in filter(lambda x: x["type"] == "multifunctional", db): |
| 211 | + check_property_for_process_allocation(ds, property_label, messages) |
| 212 | + |
195 | 213 | return messages or True
|
196 | 214 |
|
197 | 215 |
|
|
0 commit comments