Skip to content

Commit 3c3bc6d

Browse files
committed
Merge branch 'major' into major_search
2 parents fecbcf2 + aeaef00 commit 3c3bc6d

10 files changed

Lines changed: 141 additions & 70 deletions

File tree

.github/ISSUE_TEMPLATE/bug_report.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Bug report
22
description: File a bug report for AB.
3-
labels: ["bug"]
3+
type: Bug
44
body:
55
- type: markdown
66
attributes:
@@ -35,9 +35,10 @@ body:
3535
options:
3636
- Windows 10
3737
- Windows 11
38-
- MacOS
38+
- MacOS (Apple Silicon)
39+
- MacOS (Intel)
3940
- Linux/Other (please specify above)
40-
default: 0
41+
default: 1
4142
- type: textarea
4243
id: conda-env
4344
attributes:

.github/ISSUE_TEMPLATE/feature_request.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Feature request
22
description: File a feature request for AB.
3-
labels: ["feature"]
3+
type: Feature
44
body:
55
- type: markdown
66
attributes:

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99

1010
# Activity Browser
1111

12+
13+
> [!TIP]
14+
> **Activity Browser** now has an open beta for Version 3🚀.
15+
>
16+
> The beta supports many new features such as Multi-functionality and uses Brightway 2.5 under the hood.
17+
> Help us by making Activity Browser even better by using and providing feedback on Activity Browser.
18+
>
19+
> Learn more about the beta
20+
> [here](https://lca-activitybrowser.github.io/activity-browser/beta.html).
21+
1222
<img src="https://user-images.githubusercontent.com/33026150/54299977-47a9f680-45bc-11e9-81c6-b99462f84d0b.png" width=100%/>
1323

1424
The **Activity Browser (AB) is an open source software for Life Cycle Assessment (LCA)** that builds on [Brightway2](https://brightway.dev).

activity_browser/actions/database/database_import_from_ecoinvent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import re
2+
import os
23
from logging import getLogger
34
from copy import deepcopy
45

@@ -223,7 +224,7 @@ def run_safely(self, release: ei.release, version: str, model: str):
223224
fix_version=False
224225
)
225226
path = str(path)
226-
if not path.endswith(".7z"):
227+
if not path.endswith(".7z") and os.path.exists(path + ".7z"):
227228
path = path + ".7z"
228229
self.download_ready.emit(path)
229230

activity_browser/actions/project/project_import.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import tarfile
44
from logging import getLogger
55

6+
import bw2data as bd
67
from qtpy import QtWidgets, QtCore
78
from bw2io import backup
89

@@ -95,7 +96,7 @@ def get_project_name(fp):
9596
for member in tar:
9697
if member.name[-17:] == "project-name.json":
9798
return json.load(reader(tar.extractfile(member)))["name"]
98-
raise ValueError("Couldn't find project name file in archive")
99+
return ""
99100

100101

101102
class ImportThread(ABThread):
@@ -105,5 +106,11 @@ def run_safely(self):
105106
f'\nPATH: {self.path}'
106107
f'\nNAME: {self.project_name}')
107108
backup.restore_project_directory(fp=self.path, project_name=self.project_name)
109+
110+
# fix wrong hashing
111+
ds = bd.project.ProjectDataset.get(name=self.project_name)
112+
ds.full_hash = False
113+
ds.save()
114+
108115
log.info(f"Project `{self.project_name}` imported.")
109116

activity_browser/bwutils/io/ecoinvent_importer.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,21 @@ class Ecoinvent7zImporter:
4242

4343
def __init__(self, archive_path: str) -> None:
4444
self.archive_path = archive_path
45+
self.is_compressed = archive_path.endswith('.7z')
4546

4647
def install_biosphere(self, biosphere_name: str = "biosphere3") -> None:
4748
"""
4849
Installs the biosphere that is bundled in the ecoinvent .7z. Simple extraction and installing through the
4950
standard bw2io importer is quick enough because we're talking about a single file.
5051
"""
51-
# extract the elementary exchanges to a temporary location
52-
with py7zr.SevenZipFile(self.archive_path, mode='r') as archive:
53-
temp = tempfile.gettempdir()
54-
archive.extract(temp, ["MasterData/ElementaryExchanges.xml"])
55-
bio_file = os.path.join(temp, "MasterData/ElementaryExchanges.xml")
52+
if self.is_compressed:
53+
# extract the elementary exchanges to a temporary location
54+
with py7zr.SevenZipFile(self.archive_path, mode='r') as archive:
55+
temp = tempfile.gettempdir()
56+
archive.extract(temp, ["MasterData/ElementaryExchanges.xml"])
57+
bio_file = os.path.join(temp, "MasterData", "ElementaryExchanges.xml")
58+
else:
59+
bio_file = os.path.join(self.archive_path, "MasterData", "ElementaryExchanges.xml")
5660

5761
# initiate the importer with the elementary exchanges file
5862
importer = Ecospold2BiosphereImporter(biosphere_name, None, bio_file)
@@ -71,11 +75,16 @@ def install_ecoinvent(self, db_name, biosphere_name: str = "biosphere3"):
7175
log.warning(f"Database already exists, overwriting {db_name}")
7276
bd.Database(db_name).delete(warn=False)
7377

74-
# load the spold files into memory
75-
spold_bytes = self.read_archive_to_bytes()
78+
if self.is_compressed:
79+
# load the spold files into memory
80+
spold_bytes = self.read_archive_to_bytes()
81+
82+
# process the in-memory data to a dict format and applying strategies
83+
db_data = self.process_bytes(spold_bytes, db_name)
84+
else:
85+
# if the archive is not compressed, we can use the standard importer
86+
db_data = Ecospold2DataExtractor.extract(os.path.join(self.archive_path, "datasets"), db_name)
7687

77-
# process the in-memory data to a dict format and applying strategies
78-
db_data = self.process_bytes(spold_bytes, db_name)
7988
db_data = self.apply_strategies(db_data, biosphere_name)
8089

8190
# rewrite into an ingestible format

activity_browser/docs/wiki/_Footer.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,12 @@ Activity Browser is a __community project__, we rely on __you__ for it to be awe
22

33
| <picture><img alt="Activity Browser logo" src="./assets/activitybrowser.png" width="25"></picture> | ❓ Need help?<br/>[💬 Ask the community](https://github.com/LCA-ActivityBrowser/activity-browser/discussions?discussions_q=) | 💡 Ideas to improve?<br/>[💭 Request a feature](https://github.com/LCA-ActivityBrowser/activity-browser/issues/new?assignees=&labels=feature&projects=&template=feature_request.yml) | 🔥 Something Broken?<br/>[🪲 Start a bug report](https://github.com/LCA-ActivityBrowser/activity-browser/issues/new?assignees=&labels=bug&projects=&template=bug_report.yml) | ⚙️ Want to help out? <br/>[🛠️ Learn how to contribute](https://github.com/LCA-ActivityBrowser/activity-browser/blob/main/CONTRIBUTING.md) |
44
|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------|
5+
6+
> [!TIP]
7+
> **Activity Browser** now has an open beta for Version 3🚀.
8+
>
9+
> The beta supports many new features such as Multi-functionality and uses Brightway 2.5 under the hood.
10+
> Help us by making Activity Browser even better by using and providing feedback on Activity Browser.
11+
>
12+
> Learn more about the beta
13+
> [here](https://lca-activitybrowser.github.io/activity-browser/beta.html).

activity_browser/layouts/pages/calculation_setup/functional_unit_section.py

Lines changed: 4 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
from qtpy import QtWidgets
2-
from qtpy.QtCore import Qt
32

43
import bw2data as bd
54
import pandas as pd
65

76
from activity_browser import actions
8-
from activity_browser.ui import widgets, icons, delegates
7+
from activity_browser.ui import widgets, icons
98
from activity_browser.bwutils import AB_metadata
109

1110

@@ -47,7 +46,6 @@ def build_df(self):
4746
act_df = AB_metadata.get_metadata(keys, cols)
4847
act_df["amount"] = amounts
4948
act_df["_activity_key"] = keys
50-
act_df["_cs_name"] = self.calculation_setup_name
5149

5250
act_df["_processor_key"] = act_df["processor"]
5351
act_df["_processor_key"] = act_df["_processor_key"].fillna(act_df["_activity_key"])
@@ -75,15 +73,12 @@ def build_df(self):
7573
act_df.update(act_df["product"].rename("name"))
7674
act_df["product"] = act_df["name"]
7775

78-
cols = ["amount", "unit", "product", "process", "database", "location", "_processor_key", "_activity_key", "_cs_name"]
76+
cols = ["amount", "unit", "product", "process", "database", "location", "_processor_key", "_activity_key"]
7977

8078
return act_df[cols].reset_index(drop=True)
8179

8280

8381
class FunctionalUnitView(widgets.ABTreeView):
84-
defaultColumnDelegates = {
85-
"amount": delegates.AmountDelegate
86-
}
8782

8883
class ContextMenu(widgets.ABMenu):
8984
menuSetup = [
@@ -124,16 +119,10 @@ def mouseDoubleClickEvent(self, event) -> None:
124119
Args:
125120
event: The mouse double click event.
126121
"""
127-
index = self.indexAt(event.pos())
128-
if index.column() == 0:
129-
return super().mouseDoubleClickEvent(event)
130-
131122
if self.selectedIndexes():
132123
activities = [index.internalPointer()["_processor_key"] for index in self.selectedIndexes()]
133124
actions.ActivityOpen.run(list(set(activities)))
134125

135-
return None
136-
137126
def dragMoveEvent(self, event) -> None:
138127
pass
139128

@@ -142,7 +131,7 @@ def dragEnterEvent(self, event):
142131
keys: list = event.mimeData().retrievePickleData("application/bw-nodekeylist")
143132
for key in keys:
144133
act = bd.get_node(key=key)
145-
if act["type"] not in bd.labels.product_node_types + ["processwithreferenceproduct"]:
134+
if act["type"] not in bd.labels.product_node_types + ["processwithreferenceproduct", "process"]:
146135
keys.remove(key)
147136

148137
if not keys:
@@ -157,7 +146,7 @@ def dropEvent(self, event) -> None:
157146
keys: list = event.mimeData().retrievePickleData("application/bw-nodekeylist")
158147
for key in keys:
159148
act = bd.get_node(key=key)
160-
if act["type"] not in bd.labels.product_node_types + ["processwithreferenceproduct"]:
149+
if act["type"] not in bd.labels.product_node_types + ["processwithreferenceproduct", "process"]:
161150
keys.remove(key)
162151

163152
actions.CSAddFunctionalUnit.run(cs_name, keys)
@@ -169,45 +158,6 @@ def decorationData(self, col: int, key: str):
169158
return icons.qicons.product
170159
if key == "process":
171160
return icons.qicons.process
172-
return super().decorationData(col, key)
173-
174-
def flags(self, col: int, key: str):
175-
"""
176-
Returns the item flags for the given column and key.
177-
178-
Args:
179-
col (int): The column index.
180-
key (str): The key for which to return the flags.
181-
182-
Returns:
183-
QtCore.Qt.ItemFlags: The item flags.
184-
"""
185-
flags = super().flags(col, key)
186-
if key in ["amount"]:
187-
return flags | Qt.ItemFlag.ItemIsEditable
188-
return flags
189-
190-
def setData(self, col: int, key: str, value) -> bool:
191-
"""
192-
Sets the data for the given column and key.
193-
194-
Args:
195-
col (int): The column index.
196-
key (str): The key for which to set the data.
197-
value: The value to set.
198-
199-
Returns:
200-
bool: True if the data was set successfully, False otherwise.
201-
"""
202-
if key not in ["amount"]:
203-
return False
204-
205-
cs_name = self["_cs_name"]
206-
index = self.key()
207-
208-
actions.CSChangeFunctionalUnit.run(cs_name, index, value)
209-
210-
211161

212162

213163
class FunctionalUnitModel(widgets.ABItemModel):
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from qtpy import QtWidgets, QtCore
2+
from activity_browser import actions
3+
from activity_browser.ui import widgets
4+
5+
6+
class ImpactCategoryHeader(QtWidgets.QWidget):
7+
8+
def __init__(self, parent: QtWidgets.QWidget):
9+
"""
10+
Initializes the ActivityHeader widget.
11+
12+
Args:
13+
parent (QtWidgets.QWidget): The parent widget.
14+
"""
15+
super().__init__(parent)
16+
self.impact_category = parent.impact_category
17+
18+
layout = QtWidgets.QVBoxLayout()
19+
layout.setContentsMargins(0, 0, 0, 0)
20+
self.setLayout(layout)
21+
22+
def sync(self):
23+
"""
24+
Synchronizes the widget with the current state of the activity.
25+
"""
26+
self.clear_layout()
27+
self.layout().addLayout(self.build_grid())
28+
29+
def clear_layout(self, layout: QtWidgets.QLayout = None):
30+
layout = layout or self.layout()
31+
32+
if layout is None:
33+
return
34+
35+
while layout.count():
36+
item = layout.takeAt(0)
37+
widget = item.widget()
38+
if widget is not None:
39+
widget.deleteLater()
40+
elif item.layout() is not None:
41+
self.clear_layout(item.layout())
42+
43+
def build_grid(self) -> QtWidgets.QGridLayout:
44+
grid = QtWidgets.QGridLayout(self)
45+
grid.setContentsMargins(0, 5, 0, 5)
46+
grid.setSpacing(10)
47+
grid.setAlignment(QtCore.Qt.AlignmentFlag.AlignTop)
48+
49+
setup = [
50+
("Name:", QtWidgets.QLabel(str(self.impact_category.name)),),
51+
("Unit:", QtWidgets.QLabel(str(self.impact_category.metadata.get("unit", "Undefined"))),),
52+
]
53+
54+
# Arrange widgets for display as a grid
55+
for i, (title, widget) in enumerate(setup):
56+
grid.addWidget(widgets.ABLabel.demiBold(title, self), i, 1)
57+
grid.addWidget(widget, i, 2, 1, 4)
58+
59+
return grid
60+
61+
62+
class ImpactCategoryName(QtWidgets.QLineEdit):
63+
"""
64+
A widget that displays and edits the name of the activity.
65+
"""
66+
67+
def __init__(self, parent: ImpactCategoryHeader):
68+
"""
69+
Initializes the ActivityName widget.
70+
71+
Args:
72+
parent (ActivityHeader): The parent widget.
73+
"""
74+
super().__init__(str(parent.impact_category.name), parent)
75+
self.editingFinished.connect(self.change_name)
76+
77+
def change_name(self):
78+
"""
79+
Changes the name of the activity if it has been modified.
80+
"""
81+
if self.text() == self.parent().impact_category.name:
82+
return
83+
# actions.ActivityModify.run(self.parent().activity, "name", self.text())

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ classifiers = [
2525
"Programming Language :: Python :: 3",
2626
"Programming Language :: Python :: 3.10",
2727
"Programming Language :: Python :: 3.11",
28+
"Programming Language :: Python :: 3.12",
2829
"Topic :: Scientific/Engineering :: Information Analysis",
2930
"Topic :: Scientific/Engineering :: Mathematics",
3031
"Topic :: Scientific/Engineering :: Visualization",
3132
]
32-
requires-python = ">=3.10, <=3.13"
33+
requires-python = ">=3.10, <3.13"
3334
dependencies = [
3435
"arrow",
3536
"bw2analyzer>=0.11.5",

0 commit comments

Comments
 (0)