Skip to content

Commit e0a431b

Browse files
author
Marin Visscher
committed
Updating bw-functional datasets
1 parent 949bf1b commit e0a431b

3 files changed

Lines changed: 92 additions & 8 deletions

File tree

bw_functional/__init__.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
__all__ = (
2-
"__version__",
32
"allocation_strategies",
43
"generic_allocation",
54
"Process",
@@ -9,11 +8,12 @@
98
"FunctionalSQLiteDatabase",
109
"property_allocation",
1110
"convert_sqlite_to_functional_sqlite",
12-
"convert_functional_sqlite_to_sqlite"
11+
"convert_functional_sqlite_to_sqlite",
12+
"update",
1313
)
14-
# import os
15-
__version__ = "0.0.2"
1614

15+
from importlib import metadata
16+
# import os
1717
from logging import getLogger
1818

1919
from bw2data import labels
@@ -24,6 +24,7 @@
2424
from .node_classes import Process, Product
2525
from .edge_classes import MFExchange, MFExchanges
2626
from .convert import convert_sqlite_to_functional_sqlite, convert_functional_sqlite_to_sqlite
27+
from .update import update, latest
2728

2829
log = getLogger(__name__)
2930

@@ -38,9 +39,10 @@
3839

3940
# make sure allocation happens on parameter changes
4041
def _init_signals():
41-
from bw2data.signals import on_activity_parameter_recalculate
42+
from bw2data.signals import on_activity_parameter_recalculate, project_changed
4243

4344
on_activity_parameter_recalculate.connect(_check_parameterized_exchange_for_allocation)
45+
project_changed.connect(_check_and_update)
4446

4547
def _check_parameterized_exchange_for_allocation(_, name):
4648
import bw2data as bd
@@ -65,4 +67,18 @@ def _check_parameterized_exchange_for_allocation(_, name):
6567
continue
6668
process.allocate()
6769

70+
def _check_and_update(dataset):
71+
if dataset.data is None:
72+
dataset.data = {}
73+
dataset.save()
74+
75+
current = dataset.data.get("bw_functional_version")
76+
77+
if current != latest:
78+
log.info(f"Updating {dataset.name} to latest bw_functional datastructure version {latest}")
79+
dataset.data["bw_functional_version"] = update(current)
80+
dataset.save()
81+
82+
return
83+
6884
_init_signals()

bw_functional/node_classes.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,13 @@
1111
log = getLogger(__name__)
1212

1313

14+
INHERITED_FIELDS = [
15+
"database",
16+
"location",
17+
"name",
18+
]
19+
20+
1421
class MFActivity(Activity):
1522
"""
1623
A class representing an activity of the functional_sqlite backend.
@@ -192,9 +199,19 @@ def save(self, signal: bool = True, data_already_set: bool = False, force_insert
192199

193200
super().save(signal, data_already_set, force_insert)
194201

195-
if not created and old.data.get("allocation") != self.get("allocation"):
202+
if created:
203+
return
204+
205+
if old.data.get("allocation") != self.get("allocation"):
196206
self.allocate()
197207

208+
if changed_fields := [field for field in INHERITED_FIELDS if self.get(field) != old.data.get(field)]:
209+
log.info(f"Updating inherited fields {changed_fields} for products of process {self}")
210+
for product in self.products():
211+
for field in changed_fields:
212+
product._set_inherited(field, self.get(field))
213+
product.save()
214+
198215
def copy(self, *args, **kwargs):
199216
"""
200217
Create a copy of the process.
@@ -245,9 +262,16 @@ def new_product(self, type="product", **kwargs):
245262
Returns:
246263
Product: A new product.
247264
"""
265+
if kwargs.get("reference product") is None:
266+
267+
kwargs["reference product"] = kwargs.get("product", kwargs.get("name", f"Unnamed {type}"))
268+
248269
kwargs["type"] = type
249270
kwargs["processor"] = self.key
250-
kwargs["database"] = self["database"]
271+
272+
for field in INHERITED_FIELDS:
273+
kwargs[field] = self.get(field)
274+
251275
kwargs["properties"] = {p: self.property_template(p) for p in self.available_properties()}
252276
return Product(**kwargs)
253277

@@ -288,7 +312,7 @@ def property_template(self, name: str, amount=1.0) -> dict:
288312
"normalize": normalize.pop() if normalize else True
289313
}
290314

291-
def products(self):
315+
def products(self) -> list["Product"]:
292316
"""
293317
Retrieve the products (products or wastes) associated with the process.
294318
@@ -367,6 +391,18 @@ class Product(MFActivity):
367391
products, including saving, deleting, and validating them, as well as handling
368392
processing edges and substitution.
369393
"""
394+
def __setitem__(self, key, value):
395+
if key in INHERITED_FIELDS:
396+
raise KeyError(f"Field '{key}' is inherited from the processor and cannot be set directly.")
397+
if key in ["product", "reference product"]:
398+
# explicit synonyms
399+
super().__setitem__("product", value)
400+
super().__setitem__("reference product", value)
401+
return
402+
super().__setitem__(key, value)
403+
404+
def _set_inherited(self, key, value):
405+
super().__setitem__(key, value)
370406

371407
def save(self, signal: bool = True, data_already_set: bool = False, force_insert: bool = False):
372408
"""

bw_functional/update.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
latest = "0b89"
3+
4+
def update(version: str | None) -> str:
5+
if version is None:
6+
update_0b89()
7+
return "0b89"
8+
return version
9+
10+
def update_0b89():
11+
import bw2data as bd
12+
import tqdm
13+
from .node_classes import Process
14+
15+
bf_db_names = [name for name, dict in bd.databases.items() if dict.get("backend") == "functional_sqlite"]
16+
for db_name in bf_db_names:
17+
database = bd.Database(db_name)
18+
19+
for process in tqdm.tqdm(database, desc=f"Updating {db_name} to bw_functional 0b89", total=len(database)):
20+
if not isinstance(process, Process):
21+
continue
22+
23+
for product in process.products():
24+
if not product.get("product"):
25+
product["product"] = product["name"]
26+
27+
product._set_inherited("name", process["name"])
28+
product._set_inherited("database", process["database"])
29+
product._set_inherited("location", process["location"])
30+
31+
product.save()
32+

0 commit comments

Comments
 (0)