1+ from tqdm import tqdm
2+ from logging import getLogger
13from qtpy import QtWidgets , QtGui , QtCore
24
3- from bw2data .project import projects
5+ import bw2data as bd
6+ import pandas as pd
47
5- from activity_browser import application
8+ from activity_browser import application , signals
69from activity_browser .actions .base import ABAction , exception_dialogs
10+ from activity_browser .bwutils import AB_metadata
711from activity_browser .ui .icons import qicons
812from activity_browser .ui .threading import ABThread
913
14+ log = getLogger (__name__ )
15+
1016
1117class ProjectMigrate25 (ABAction ):
1218 """
@@ -23,16 +29,16 @@ class ProjectMigrate25(ABAction):
2329 @exception_dialogs
2430 def run (name : str = None ):
2531 if name is None :
26- name = projects .current
32+ name = bd . projects .current
2733
2834 dialog = MigrateDialog (name , application .main_window )
2935 dialog .exec_ ()
3036
3137 if dialog .result () == dialog .DialogCode .Rejected :
3238 return
3339
34- if name != projects .current :
35- projects .set_current (name , update = False )
40+ if name != bd . projects .current :
41+ bd . projects .set_current (name , update = False )
3642
3743 # setup dialog
3844 progress = QtWidgets .QProgressDialog (
@@ -51,6 +57,7 @@ def run(name: str = None):
5157 thread = MigrateThread (application )
5258 thread .finished .connect (lambda : progress .deleteLater ())
5359 thread .start ()
60+ thread .connect_progress_dialog (progress )
5461
5562
5663class MigrateDialog (QtWidgets .QDialog ):
@@ -85,6 +92,74 @@ def __init__(self, project_name: str, parent=None):
8592
8693class MigrateThread (ABThread ):
8794 def run_safely (self ):
88- projects .migrate_project_25 ()
89- projects .set_current (projects .current )
95+ self .pre_process_methods ()
96+
97+ log .info ("Updating and processing all datasets in the project" )
98+ bd .projects .set_current (bd .projects .current )
99+
100+ for db_name in bd .databases :
101+ self .update_database_activity_types (db_name )
102+
103+ # set the bw25 flag in the project dataset
104+ bd .projects .dataset .data ["25" ] = True
105+ bd .projects .dataset .save ()
106+
107+ # reloading project to ensure all changes are applied
108+ bd .projects .set_current (bd .projects .current )
109+
110+ @classmethod
111+ def pre_process_methods (cls ):
112+ log .info ("Pre-processing methods for migration to bw25" )
113+ data = {m : bd .Method (m ).load () for m in bd .methods }
114+ df = pd .DataFrame ([(k , v [0 ][0 ], v [0 ][1 ], v [1 ])
115+ for k , values in data .items () for v in values
116+ if isinstance (v [0 ], tuple ) and len (v ) == 2 and len (v [0 ]) == 2 ],
117+ columns = ["method" , "database" , "code" , "value" ])
118+
119+ df = df .merge (AB_metadata .dataframe ["id" ], left_on = ["database" , "code" ], right_index = True )
120+
121+ signals .method .blockSignals (True )
122+ signals .meta .blockSignals (True )
123+
124+ for name in tqdm (df ["method" ].unique (), desc = "Pre-processing methods" , unit = "method" , total = len (df ["method" ].unique ())):
125+ method_df = df [df ["method" ] == name ][["id" , "value" ]]
126+ method_list = list (method_df .itertuples (index = False , name = None ))
127+ bd .Method (name ).write (method_list , process = False )
128+
129+ signals .method .blockSignals (False )
130+ signals .meta .blockSignals (False )
131+
132+ return
133+
134+ @classmethod
135+ def update_database_activity_types (cls , db_name : str ):
136+ database = bd .Database (db_name )
137+ write = False
138+
139+ if not isinstance (database , bd .backends .SQLiteBackend ):
140+ return
141+
142+ log .info (f"Updating activity types in { db_name } " )
143+ raw = database .load ()
144+
145+ for key , ds in tqdm (raw .items (), desc = f"Updating activity types in { db_name } " , unit = "activity" , total = len (raw )):
146+ if cls .activity_is_processwithreferenceproduct (ds ):
147+ write = True
148+ ds ["type" ] = "processwithreferenceproduct"
149+
150+ if write :
151+ database .write (raw )
152+
153+ @staticmethod
154+ def activity_is_processwithreferenceproduct (ds : dict ) -> bool :
155+ production = [exc for exc in ds .get ("exchanges" , []) if exc .get ("type" ) == "production" ]
156+ return (
157+ ds .get ("type" ) in ["process" , "processwithreferenceproduct" ] and
158+ (
159+ len (production ) == 0 or
160+ production [0 ].get ("input" ) == (ds ["database" ], ds ["code" ])
161+ )
162+ )
163+
164+
90165
0 commit comments