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
3 changes: 2 additions & 1 deletion activity_browser/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
from .calculation_setup.cs_open import CSOpen

from .database.database_open import DatabaseOpen
from .database.database_export import DatabaseExport
from .database.database_export_excel import DatabaseExportExcel
from .database.database_export_bw2package import DatabaseExportBW2Package
from .database.database_new import DatabaseNew
from .database.database_delete import DatabaseDelete
from .database.database_duplicate import DatabaseDuplicate
Expand Down
75 changes: 46 additions & 29 deletions activity_browser/actions/database/database_delete.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import List

from qtpy import QtCore, QtWidgets

import bw2data as bd
Expand All @@ -12,72 +14,87 @@

class DatabaseDelete(ABAction):
"""
Deletes a specified database from the project after user confirmation.
Deletes one or more databases from the project after user confirmation.

This method performs the following steps:
- Displays a confirmation dialog to the user with the database name and record count.
- If the user confirms, deletes the database, its upstream exchanges, and associated parameters.
- Removes the database from the project settings.
- Displays a confirmation dialog to the user with the database name(s) and total record count.
- If the user confirms, deletes the database(s), their upstream exchanges, and associated parameters.
- Removes the database(s) from the project settings.

Args:
db_name (str): The name of the database to be deleted.
db_names (List[str]): The name(s) of the database(s) to be deleted.

Steps:
- Set the cursor to a waiting state while gathering data for large databases.
- Retrieve the record count for the specified database.
- Construct a warning message with the database name and record count.
- Retrieve the record count for the specified database(s).
- Construct a warning message with the database name(s) and record count.
- Display a confirmation dialog to the user.
- If the user cancels, exit the method.
- Set the cursor to a waiting state while performing the deletion.
- Delete upstream exchanges associated with the database.
- Remove the database from the Brightway2 project.
- Delete upstream exchanges associated with the database(s).
- Remove the database(s) from the Brightway2 project.
- Delete database parameters.
- Remove the database from the project settings.
- Remove the database(s) from the project settings.
- Restore the cursor to its default state.
"""

icon = qicons.delete
text = "Delete database"
tool_tip = "Delete this database from the project"
text = "Delete databases"
tool_tip = "Delete database(s) from the project"

@staticmethod
@exception_dialogs
def run(db_name: str):
def run(db_names: List[str]):
# gathering data will take time for large databases
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)

# get the record count from the database controller
db_name = db_name
n_records = AB_metadata.dataframe[AB_metadata.dataframe["database"] == db_name].shape[0]
# get the total record count from all databases
total_records = 0
for db_name in db_names:
n_records = AB_metadata.dataframe[AB_metadata.dataframe["database"] == db_name].shape[0]
total_records += n_records

# construct warning text
text = f"Are you sure you want to delete database '{db_name}'?"
if n_records:
text += f" It contains {n_records} activities"
if len(db_names) == 1:
text = f"Are you sure you want to delete database <b>'{db_names[0]}'</b>?"
if total_records:
text += f" It contains {total_records} activities."
else:
text = f"Are you sure you want to delete {len(db_names)} databases?"
if total_records:
text += f" They contain {total_records} activities in total."

# ask the user for confirmation
QtWidgets.QApplication.restoreOverrideCursor()
response = QtWidgets.QMessageBox.question(
application.main_window, "Delete database?", text
application.main_window, build_title(db_names), text
)

# return if the user cancels
if response != response.Yes:
if response != QtWidgets.QMessageBox.Yes:
return

# deleting data will take time for large databases
QtWidgets.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)

# delete upstream exchanges
ExchangeDataset.delete().where(ExchangeDataset.input_database == db_name).execute()
for db_name in db_names:
# delete upstream exchanges
ExchangeDataset.delete().where(ExchangeDataset.input_database == db_name).execute()

# instruct the DatabaseController to delete the database from the project.
del bd.databases[db_name]
# instruct the DatabaseController to delete the database from the project.
del bd.databases[db_name]

# delete database parameters
Group.delete().where(Group.name == db_name).execute()
# delete database parameters
Group.delete().where(Group.name == db_name).execute()

# remove database from project settings
settings.project_settings.remove_db(db_name)
# remove database from project settings
settings.project_settings.remove_db(db_name)

QtWidgets.QApplication.restoreOverrideCursor()


def build_title(db_names: List[str]) -> str:
"""Build an appropriate title for the confirmation dialog."""
if len(db_names) == 1:
return "Delete database?"
return "Delete databases?"
20 changes: 0 additions & 20 deletions activity_browser/actions/database/database_export.py

This file was deleted.

100 changes: 100 additions & 0 deletions activity_browser/actions/database/database_export_bw2package.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from logging import getLogger
from typing import List

from qtpy import QtWidgets

from activity_browser import application
from activity_browser.actions.base import ABAction, exception_dialogs
from activity_browser.ui import widgets, threading
from activity_browser.bwutils import exporters

log = getLogger(__name__)


class DatabaseExportBW2Package(ABAction):
"""
ABAction to export database(s) to BW2Package format (.bw2package).
"""

# icon = icons.qicons.export_db
text = "Export to .bw2package"
tool_tip = "Export database(s) to BW2Package format"

@classmethod
@exception_dialogs
def run(cls, db_names: List[str] = None):
if db_names is None:
import bw2data as bd
dialog = widgets.ABDatabaseSelectionDialog(
parent=application.main_window,
databases=sorted(bd.databases),
title="Select databases to export to BW2Package"
)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
db_names = dialog.get_selected_databases()
else:
return

# Get export directory or file from user
if len(db_names) == 1:
# Single database - suggest a filename
suggested_name = f"{db_names[0]}.bw2package"
path, _ = QtWidgets.QFileDialog.getSaveFileName(
parent=application.main_window,
caption=f'Export database "{db_names[0]}" to BW2Package',
directory=suggested_name,
filter='Brightway2 Database Package (*.bw2package);; All files (*.*)'
)
else:
# Multiple databases - ask for directory
path = QtWidgets.QFileDialog.getExistingDirectory(
parent=application.main_window,
caption=f'Select directory to export {len(db_names)} databases',
)

if not path:
return

# Show export dialog
context = {
"db_names": db_names,
"path": path,
}
export_dialog = ExportBW2PackageSetup(
parent=application.main_window,
title="Export to BW2Package",
context=context
)
export_dialog.show()


class ExportBW2PackageSetup(widgets.ABWizard):
"""Wizard for exporting databases to BW2Package format."""

class ExportPage(widgets.ABThreadedWizardPage):
"""Wizard page to export the selected database(s) to BW2Package."""
title = "Exporting Database(s)"
subtitle = "Exporting database(s) to .bw2package file(s)"

class Thread(threading.ABThread):
"""Thread to handle the export process."""

def run_safely(self, db_names: List[str], path: str):
"""Export the database(s) to BW2Package."""
for db_name in db_names:
try:
success = exporters.store_database_as_package(db_name, path)
if success:
log.info(f"Successfully exported database '{db_name}' to BW2Package")
else:
log.error(f"Failed to export database '{db_name}'")
raise RuntimeError(f"Database '{db_name}' not found")
except Exception as e:
log.error(f"Failed to export database '{db_name}': {e}")
raise

def initializePage(self, context: dict):
"""Start the export thread."""
self.thread.start(context["db_names"], context["path"])

pages = [ExportPage]
97 changes: 97 additions & 0 deletions activity_browser/actions/database/database_export_excel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
from logging import getLogger
from typing import List

from qtpy import QtWidgets

from activity_browser import application
from activity_browser.actions.base import ABAction, exception_dialogs
from activity_browser.ui import widgets, threading
from activity_browser.bwutils import exporters


log = getLogger(__name__)


class DatabaseExportExcel(ABAction):
"""
ABAction to export database(s) to Excel format (.xlsx).
"""

icon = application.style().standardIcon(QtWidgets.QStyle.SP_DriveHDIcon)
text = "Export to Excel (.xlsx)"
tool_tip = "Export database(s) to Excel format"

@classmethod
@exception_dialogs
def run(cls, db_names: List[str] = None):
if db_names is None:
import bw2data as bd
dialog = widgets.ABDatabaseSelectionDialog(
parent=application.main_window,
databases=sorted(bd.databases),
title="Select databases to export to Excel"
)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
db_names = dialog.get_selected_databases()
else:
return

# Get export directory or file from user
if len(db_names) == 1:
# Single database - suggest a filename
suggested_name = f"lci-{db_names[0]}.xlsx"
path, _ = QtWidgets.QFileDialog.getSaveFileName(
parent=application.main_window,
caption=f'Export database "{db_names[0]}" to Excel',
directory=suggested_name,
filter='Excel spreadsheet (*.xlsx);; All files (*.*)'
)
else:
# Multiple databases - ask for directory
path = QtWidgets.QFileDialog.getExistingDirectory(
parent=application.main_window,
caption=f'Select directory to export {len(db_names)} databases',
)

if not path:
return

# Show export dialog
context = {
"db_names": db_names,
"path": path,
}
export_dialog = ExportExcelSetup(
parent=application.main_window,
title="Export to Excel",
context=context
)
export_dialog.show()


class ExportExcelSetup(widgets.ABWizard):
"""Wizard for exporting databases to Excel format."""

class ExportPage(widgets.ABThreadedWizardPage):
"""Wizard page to export the selected database(s) to Excel."""
title = "Exporting Database(s)"
subtitle = "Exporting database(s) to Excel file(s)"

class Thread(threading.ABThread):
"""Thread to handle the export process."""

def run_safely(self, db_names: List[str], path: str):
"""Export the database(s) to Excel."""
for db_name in db_names:
try:
exporters.write_lci_excel(db_name, path)
log.info(f"Successfully exported database '{db_name}' to Excel")
except Exception as e:
log.error(f"Failed to export database '{db_name}': {e}")
raise

def initializePage(self, context: dict):
"""Start the export thread."""
self.thread.start(context["db_names"], context["path"])

pages = [ExportPage]
Loading