Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ A named set of functional unit(s) and LCIA method(s) used to run LCA / multi-LCA

A Life Cycle Impact Assessment method (characterization factors for elementary flows). In Brightway, methods are keyed tuples; AB exposes them in impact-category UI.

### AB impact-category file (AB LCIA format)

Activity Browser’s multi–impact-category interchange for import/export: characterization factors plus per–impact-category unit and description. Method keys and elementary-flow identities use `::` (variable-length Brightway tuples / categories). Excel uses sheets `CFs` and `Impact categories`; CSV uses a sibling pair `*.cfs.csv` + `*.metadata.csv`. Distinct from ecoinvent’s LCIA implementation workbook (fixed three-part names; separate name/compartment/subcompartment columns) and from the **bw2io impact-category file**.
_Avoid_: Indicators sheet (when meaning AB’s impact-category metadata table), AB ecoinvent format

### bw2io impact-category file (bw2io LCIA format)

bw2io’s Excel/CSV LCIA CF template: **one impact category per CF file/sheet** (`name`, `categories` with `::`, `amount`, optional uncertainty). AB may add a `metadata` sheet (xlsx) or `metadata.csv` sidecar for method/unit/description/`filename`; stock bw2io only needs the CF table.
_Avoid_: one-shot, bw2io native (as a product name), AB impact-category file

### Characterization factor (CF)

A factor that converts an elementary flow amount into an impact-category score for a given method.
Expand Down Expand Up @@ -120,4 +130,6 @@ Extensibility mechanism for third-party AB features. **Architecture TBD** — do
| “table of processes” | database / activity |
| “flow” without kind | intermediate (technosphere) or elementary (biosphere) flow; exchanges is another synonym |
| “impact method” only | LCIA method / impact category (as used in UI) |
| “Indicators” (AB LCIA metadata sheet/file) | Impact categories (Excel sheet) / `.metadata.csv` (AB CSV sidecar) |
| “one-shot” / “bw2io native” (LCIA file) | bw2io impact-category file |
| “global app settings file” ad hoc | `app.settings` |
14 changes: 9 additions & 5 deletions activity_browser/app/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from .activity.activity_modify import ActivityModify
from .activity.activity_new_process import ActivityNewProcess
from .activity.activity_new_product import ActivityNewProduct
from .activity.new_elementary_flow import NewElementaryFlow
from .activity.edit_elementary_flow import EditElementaryFlow
from .activity.delete_elementary_flow import DeleteElementaryFlow
from .activity.elementary_flow_new import NewElementaryFlow
from .activity.elementary_flow_edit import EditElementaryFlow
from .activity.elementary_flow_delete import DeleteElementaryFlow
from .activity.activity_open import ActivityOpen
from .activity.activity_relink import ActivityRelink
from .activity.activity_sdf_to_clipboard import ActivitySDFToClipboard
Expand Down Expand Up @@ -59,8 +59,12 @@
from .method.method_meta_modify import MethodMetaModify
from .method.method_new import MethodNew

from .method.importer.method_importer_ecoinvent import MethodImporterEcoinvent
from .method.importer.method_importer_bw2io import MethodImporterBW2IO
from .method.method_import_ecoinvent import MethodImportEcoinvent
from .method.method_import_ab import MethodImportAB
from .method.method_import_bw2io import MethodImportBW2IO
from .method.method_export_ab import MethodExportAB
from .method.method_export_bw2io import MethodExportBW2IO
from .method.method_get_template import MethodGetTemplate

from .method.cf_uncertainty_modify import CFUncertaintyModify
from .method.cf_amount_modify import CFAmountModify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from qtpy import QtWidgets

from activity_browser import app
from activity_browser.app.actions.activity.new_elementary_flow import ElementaryFlowDialog
from activity_browser.app.actions.activity.elementary_flow_new import ElementaryFlowDialog
from activity_browser.app.actions.base import ABAction, exception_dialogs
from activity_browser.bwutils.commontasks import (
get_writable_databases,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from activity_browser.ui import widgets, icons
from activity_browser.app.actions.base import ABAction, exception_dialogs
from activity_browser.bwutils.io.ecoinvent_importer import Ecoinvent7zImporter
from activity_browser.bwutils.io.ecoinvent_lcia_importer import EcoinventLCIAImporter
from activity_browser.bwutils.impact_categories import EcoinventLCIAImporter
from activity_browser.mod.bw2io.migrations import ab_create_core_migrations
from activity_browser.ui.core import threading

Expand Down

This file was deleted.

121 changes: 121 additions & 0 deletions activity_browser/app/actions/method/method_export_ab.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Export impact categories to an AB LCIA file (.xlsx/.csv)."""
from __future__ import annotations

from typing import List, Optional, Sequence

from loguru import logger
from qtpy import QtWidgets
from qtpy.QtCore import Signal, SignalInstance

from activity_browser import app
from activity_browser.app import application
from activity_browser.app.actions.base import ABAction, exception_dialogs
from activity_browser.app.panes.impact_categories import resolve_methods_for_export
from activity_browser.bwutils.impact_categories import (
CancelledError,
export_methods_ab_csv_pair,
export_methods_ab_xlsx,
)
from activity_browser.ui.core import threading
from activity_browser.app.dialogs import run_thread_with_progress


class MethodExportAB(ABAction):
"""Export selected (or all) impact categories to an AB LCIA Excel workbook."""

icon = application.style().standardIcon(QtWidgets.QStyle.SP_DialogSaveButton)
text = "To AB LCIA file (.xlsx/.csv)…"
tool_tip = "Export impact categories to Activity Browser spreadsheet format"

@classmethod
@exception_dialogs
def run(cls, method_names: Optional[List[tuple]] = None):
method_names = resolve_methods_for_export(method_names)
if not method_names:
return

path, selected_filter = QtWidgets.QFileDialog.getSaveFileName(
parent=app.main_window,
caption="Export impact categories (AB impact-category file)",
directory="ab-impact-categories.xlsx",
filter="Excel spreadsheet (*.xlsx);;CSV pair (*.cfs.csv);; All files (*.*)",
)
if not path:
return

as_csv = "CSV" in selected_filter or path.lower().endswith(".cfs.csv")
if as_csv and path.lower().endswith(".xlsx"):
path = path[:-5]
elif not as_csv and not path.lower().endswith(".xlsx"):
path = path + ".xlsx"

export_ab_with_progress(method_names, path, as_csv=as_csv)


class ExportABThread(threading.ABThread):
done: SignalInstance = Signal(str)
failed: SignalInstance = Signal(str)

method_names: list
path: str
as_csv: bool

def run_safely(self):
try:
if self.ab_cancel_requested():
return
cancel = lambda: self.ab_cancel_requested()
if self.as_csv:
cfs_path, ic_path = export_methods_ab_csv_pair(
self.method_names, self.path, cancel_check=cancel
)
message = f"{cfs_path}\n{ic_path}"
else:
export_methods_ab_xlsx(
self.method_names, self.path, cancel_check=cancel
)
message = self.path
except CancelledError:
self.request_ab_cancel()
return
except Exception as exc:
self.failed.emit(str(exc))
return
if self.ab_cancel_requested():
return
self.done.emit(message)


def export_ab_with_progress(
method_names: Sequence[tuple],
path: str,
*,
as_csv: bool,
) -> None:
thread = ExportABThread(app.application)
thread.method_names = list(method_names)
thread.path = path
thread.as_csv = as_csv

def done(message: str):
logger.info(
f"Exported {len(method_names)} impact categories to {message.replace(chr(10), ' and ')}"
)
QtWidgets.QMessageBox.information(
app.main_window,
"Export complete",
f"Exported {len(method_names)} impact categories to:\n{message}",
)

def failed(message: str):
QtWidgets.QMessageBox.warning(app.main_window, "Export impact categories", message)

thread.done.connect(done)
thread.failed.connect(failed)
run_thread_with_progress(
"Exporting impact categories",
thread,
on_cancelled=lambda: QtWidgets.QMessageBox.information(
app.main_window, "Export cancelled", "Export cancelled."
),
)
131 changes: 131 additions & 0 deletions activity_browser/app/actions/method/method_export_bw2io.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""Export impact categories as bw2io LCIA files."""
from __future__ import annotations

from pathlib import Path
from typing import Sequence

from qtpy import QtWidgets
from qtpy.QtCore import Signal, SignalInstance

from activity_browser import app
from activity_browser.app import application
from activity_browser.app.actions.base import ABAction, exception_dialogs
from activity_browser.app.panes.impact_categories import resolve_methods_for_export
from activity_browser.bwutils.impact_categories import (
CancelledError,
method_name_to_filename_stem,
raise_if_cancelled,
)
from activity_browser.bwutils.impact_categories.bw2io_lcia_file import (
export_method_bw2io_xlsx,
export_methods_bw2io_csv_batch,
)
from activity_browser.ui.core import threading
from activity_browser.app.dialogs import run_thread_with_progress


class MethodExportBW2IO(ABAction):
icon = application.style().standardIcon(QtWidgets.QStyle.SP_DialogSaveButton)
text = "To bw2io LCIA file (.xlsx/.csv)…"
tool_tip = "Export impact categories as bw2io LCIA files"

@classmethod
@exception_dialogs
def run(cls, method_names: list[tuple] | None = None):
method_names = resolve_methods_for_export(method_names)
if not method_names:
return

fmt, ok = QtWidgets.QInputDialog.getItem(
app.main_window,
"bw2io export format",
"Format:",
["Excel (.xlsx, one file per impact category)", "CSV (folder + metadata.csv)"],
0,
False,
)
if not ok:
return

directory = QtWidgets.QFileDialog.getExistingDirectory(
app.main_window,
"Select folder for bw2io Excel files"
if fmt.startswith("Excel")
else "Select folder for bw2io CSV files",
)
if not directory:
return

export_bw2io_with_progress(
method_names, directory, as_excel=fmt.startswith("Excel")
)


class ExportBW2IOThread(threading.ABThread):
done: SignalInstance = Signal(str)
failed: SignalInstance = Signal(str)

method_names: list
directory: str
as_excel: bool

def run_safely(self):
try:
directory = Path(self.directory)
cancel = lambda: self.ab_cancel_requested()
if self.as_excel:
import tqdm

for name in tqdm.tqdm(
self.method_names,
desc="Exporting bw2io Excel",
total=len(self.method_names),
):
raise_if_cancelled(cancel)
path = directory / f"{method_name_to_filename_stem(name)}.xlsx"
export_method_bw2io_xlsx(name, path)
message = (
f"Wrote {len(self.method_names)} Excel file(s) to:\n{directory}"
)
else:
written = export_methods_bw2io_csv_batch(
self.method_names, directory, cancel_check=cancel
)
message = f"Wrote {len(written)} file(s) to:\n{directory}"
except CancelledError:
self.request_ab_cancel()
return
except Exception as exc:
self.failed.emit(str(exc))
return
if self.ab_cancel_requested():
return
self.done.emit(message)


def export_bw2io_with_progress(
method_names: Sequence[tuple],
directory: str,
*,
as_excel: bool,
) -> None:
thread = ExportBW2IOThread(app.application)
thread.method_names = list(method_names)
thread.directory = directory
thread.as_excel = as_excel

def done(message: str):
QtWidgets.QMessageBox.information(app.main_window, "Export complete", message)

def failed(message: str):
QtWidgets.QMessageBox.warning(app.main_window, "Export impact categories", message)

thread.done.connect(done)
thread.failed.connect(failed)
run_thread_with_progress(
"Exporting impact categories",
thread,
on_cancelled=lambda: QtWidgets.QMessageBox.information(
app.main_window, "Export cancelled", "Export cancelled."
),
)
Loading
Loading