99from ..schemas .job import JobStatusEnum
1010from ..schemas .mcda import Goal , Alternative
1111from ..utils .logger import get_logger
12- from ..utils .data_loaders import get_goal_weights_for_perspective , load_mcda_config
12+ from ..utils .data_loaders import get_goal_weights_for_perspective , load_mcda_config , normalize_goal_weights
1313
1414# Initialize logger
1515logger = get_logger (__name__ )
@@ -26,19 +26,11 @@ class McdaQualitativeJob:
2626 4. Saves structured MCDA input and output snapshots
2727 """
2828
29- @staticmethod
30- def _normalize_goal_weights (goal_weights : Dict [str , float ]) -> Dict [str , float ]:
31- """Normalize all goal weight keys to lower-case."""
32- return {
33- goal_name : weight
34- for goal_name , weight in goal_weights .items ()
35- }
36-
3729 @staticmethod
3830 def _normalize_goal_scores (goal_scores : Dict [str , float ]) -> Dict [str , float ]:
3931 """Normalize all goal score keys to lower-case."""
4032 return {
41- goal_name : score
33+ goal_name : score
4234 for goal_name , score in goal_scores .items ()
4335 }
4436
@@ -108,7 +100,8 @@ def build_alternatives(config: Dict[str, Any]) -> list[Alternative]:
108100
109101 alternatives = []
110102 for activity_key , activity_goal_scores in goals_score .items ():
111- values = McdaQualitativeJob ._normalize_goal_scores (activity_goal_scores )
103+ values = McdaQualitativeJob ._normalize_goal_scores (
104+ activity_goal_scores )
112105 alternative = Alternative (
113106 name = activity_labels .get (activity_key , activity_key ),
114107 values = values
@@ -136,11 +129,6 @@ def build_goals(alternatives: list[Alternative], goal_weights: Optional[Dict[str
136129 if not alternatives :
137130 return []
138131
139- normalized_goal_weights = (
140- McdaQualitativeJob ._normalize_goal_weights (goal_weights )
141- if goal_weights else None
142- )
143-
144132 goal_names = list (alternatives [0 ].values .keys ())
145133 if not goal_names :
146134 return []
@@ -150,7 +138,7 @@ def build_goals(alternatives: list[Alternative], goal_weights: Optional[Dict[str
150138
151139 for goal_name in goal_names :
152140 weight = McdaQualitativeJob ._get_weight_by_goal (
153- goal_name , normalized_goal_weights , default_weight )
141+ goal_name , goal_weights , default_weight )
154142
155143 min_value , max_value = McdaQualitativeJob ._get_min_max_values_per_goal (
156144 goal_name , alternatives )
@@ -196,7 +184,7 @@ def get_goal_weights(perspective: Optional[str]) -> Optional[Dict[str, float]]:
196184 goal_weights = get_goal_weights_for_perspective (perspective )
197185 logger .info (
198186 f"Using qualitative goal weights for perspective: { perspective } " )
199- return McdaQualitativeJob . _normalize_goal_weights ( goal_weights )
187+ return goal_weights
200188 except ValueError as e :
201189 logger .warning (
202190 f"Failed to load perspective weights: { e } . Using equal weights." )
@@ -227,9 +215,16 @@ def run(job_id: str, db: Session, params: Optional[Dict] = None) -> None:
227215 )
228216
229217 perspective = params .get ("perspective" ) if params else None
218+ analysis_name = params .get ("name" ) if params else None
219+ personalized_goal_weights = params .get (
220+ "goals_weights" ) if params else None
230221 logger .debug (
231222 "Qualitative MCDA analysis parameters" ,
232- extra = {"perspective" : perspective }
223+ extra = {
224+ "perspective" : perspective ,
225+ "analysis_name" : analysis_name ,
226+ "personalized_goal_weights" : bool (personalized_goal_weights )
227+ }
233228 )
234229
235230 config = load_mcda_config ()
@@ -238,16 +233,25 @@ def run(job_id: str, db: Session, params: Optional[Dict] = None) -> None:
238233 "perspectives" : config .get ("perspectives" , {}),
239234 "business_activities" : config .get ("business_activities" , {})
240235 }
241- job_repo .update_job_data (job_id = job_id , input_data = input_data_snapshot )
236+ job_repo .update_job_data (
237+ job_id = job_id , input_data = input_data_snapshot )
238+
239+ if perspective == "user_personalized" and personalized_goal_weights :
240+ goal_weights = normalize_goal_weights (
241+ personalized_goal_weights )
242+ logger .info ("Using user-personalized qualitative goal weights" )
243+ else :
244+ goal_weights = McdaQualitativeJob .get_goal_weights (perspective )
242245
243- goal_weights = McdaQualitativeJob .get_goal_weights (perspective )
244246 alternatives = McdaQualitativeJob .build_alternatives (config )
245247 goals = McdaQualitativeJob .build_goals (alternatives , goal_weights )
246248
247249 if not alternatives :
248- raise Exception ("No business activities were found. Cannot proceed with qualitative MCDA." )
250+ raise Exception (
251+ "No business activities were found. Cannot proceed with qualitative MCDA." )
249252 if not goals :
250- raise Exception ("No goals were built from business activity scores. Cannot proceed with qualitative MCDA." )
253+ raise Exception (
254+ "No goals were built from business activity scores. Cannot proceed with qualitative MCDA." )
251255
252256 mcda_analyzer = PrometheeGaiaAnalyzer (
253257 goals = goals ,
@@ -256,22 +260,26 @@ def run(job_id: str, db: Session, params: Optional[Dict] = None) -> None:
256260
257261 mcda_input_data_snapshot = {
258262 "perspective" : perspective ,
263+ "name" : analysis_name ,
259264 "goals" : [goal .model_dump () for goal in goals ],
260265 "alternatives" : [alt .model_dump () for alt in alternatives ],
261266 "timestamp" : datetime .utcnow ().isoformat ()
262267 }
263268 input_data_snapshot .update (mcda_input_data_snapshot )
264- job_repo .update_job_data (job_id = job_id , input_data = input_data_snapshot )
269+ job_repo .update_job_data (
270+ job_id = job_id , input_data = input_data_snapshot )
265271
266272 mcda_output = mcda_analyzer .run_analysis (run_visualizations = False )
267273
268274 output_data_snapshot = {
275+ "name" : analysis_name ,
269276 "kpi_impact_results" : [],
270277 "kpi_impact_errors" : [],
271278 "mcda_results" : mcda_output .model_dump (),
272279 "timestamp" : datetime .utcnow ().isoformat ()
273280 }
274- job_repo .update_job_data (job_id = job_id , output_data = output_data_snapshot )
281+ job_repo .update_job_data (
282+ job_id = job_id , output_data = output_data_snapshot )
275283
276284 top_alt_key = mcda_output .ranking [0 ] if mcda_output .ranking else "N/A"
277285 top_alt_name = mcda_output .alternative_labels .get (
@@ -291,7 +299,8 @@ def run(job_id: str, db: Session, params: Optional[Dict] = None) -> None:
291299 message = success_message ,
292300 completed_at = datetime .utcnow ()
293301 )
294- logger .info (f"Qualitative MCDA analysis job completed successfully: { job_id } " )
302+ logger .info (
303+ f"Qualitative MCDA analysis job completed successfully: { job_id } " )
295304
296305 except Exception as e :
297306 error_message = f"McdaQualitativeJob failed: { str (e )} "
0 commit comments