Spaces:
Running
Running
| """ | |
| ComplexMoGAPI Calculator | |
| ======================== | |
| Implements the Total Greenness Score (TGS) for Complex Modified GAPI. | |
| Scoring scale (per article Table 1): | |
| Green = 3 pts (best / lowest environmental impact) | |
| Yellow = 2 pts | |
| Red = 1 pt (worst / highest environmental impact) | |
| TGS = sum of all parameter scores. | |
| Max TGS = 21 params × 3 = 63 (all-green) | |
| Min TGS = 21 params × 1 = 21 (all-red) | |
| """ | |
| from typing import Dict, Any | |
| # ─── PRE-ANALYSIS PARAMETERS ────────────────────────────────────────────────── | |
| PRE_ANALYSIS_PARAMS = [ | |
| { | |
| "id": "pre_yield", | |
| "label": "Yield / Selectivity", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": ">89%", "score": 3, "color": "green"}, | |
| {"label": "70–89%", "score": 2, "color": "yellow"}, | |
| {"label": "<70%", "score": 1, "color": "red"}, | |
| {"label": "Not applicable", "score": 0, "color": "muted"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_temp_time", | |
| "label": "Temperature / Time", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "Room temperature, <1 h", "score": 3, "color": "green"}, | |
| {"label": "Room temperature, >1 h OR Heating/Cooling, <1 h OR Cooling to 0°C", "score": 2, "color": "yellow"}, | |
| {"label": "Heating, >1 h OR Cooling <0°C", "score": 1, "color": "red"}, | |
| {"label": "Not applicable", "score": 0, "color": "muted"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_green_economy", | |
| "label": "Relation to Green Economy (rules met)", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "5–6 rules met", "score": 3, "color": "green"}, | |
| {"label": "3–4 rules met", "score": 2, "color": "yellow"}, | |
| {"label": "1–2 rules met", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_health_hazard", | |
| "label": "Reagents Health Hazard (NFPA)", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "NFPA health 0–1 (slightly toxic/irritant)", "score": 3, "color": "green"}, | |
| {"label": "NFPA health 2–3 (moderately toxic)", "score": 2, "color": "yellow"}, | |
| {"label": "NFPA health 4 (serious injury / carcinogen)", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_safety_hazard", | |
| "label": "Reagents Safety Hazard (NFPA flammability)", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "NFPA flammability/instability 0–1, no special hazards", "score": 3, "color": "green"}, | |
| {"label": "NFPA flammability/instability 2–3, or special hazard", "score": 2, "color": "yellow"}, | |
| {"label": "NFPA flammability/instability 4", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_instrument", | |
| "label": "Technical Setup (Instrumentation)", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "Common setup", "score": 3, "color": "green"}, | |
| {"label": "Additional/semi-advanced instruments", "score": 2, "color": "yellow"}, | |
| {"label": "Pressure >1 atm or glove box", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_energy", | |
| "label": "Energy Consumption", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "≤0.1 kWh per sample", "score": 3, "color": "green"}, | |
| {"label": "≤1.5 kWh per sample", "score": 2, "color": "yellow"}, | |
| {"label": ">1.5 kWh per sample", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_occupational", | |
| "label": "Occupational Hazard", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "Hermetization of the process", "score": 3, "color": "green"}, | |
| {"label": "—", "score": 2, "color": "yellow"}, | |
| {"label": "Emission of vapours to atmosphere", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_workup", | |
| "label": "Workup and Purification", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": "None or simple processes", "score": 3, "color": "green"}, | |
| {"label": "Standard purification techniques", "score": 2, "color": "yellow"}, | |
| {"label": "Advanced purification techniques", "score": 1, "color": "red"}, | |
| {"label": "Not applicable", "score": 0, "color": "muted"}, | |
| ], | |
| }, | |
| { | |
| "id": "pre_purity", | |
| "label": "Purity of End Product", | |
| "block": "Pre-Analysis", | |
| "options": [ | |
| {"label": ">98%", "score": 3, "color": "green"}, | |
| {"label": "97–98%", "score": 2, "color": "yellow"}, | |
| {"label": "<97%", "score": 1, "color": "red"}, | |
| {"label": "Not applicable", "score": 0, "color": "muted"}, | |
| ], | |
| }, | |
| ] | |
| # ─── ANALYTICAL PROCEDURE PARAMETERS ────────────────────────────────────────── | |
| ANALYTICAL_PARAMS = [ | |
| # Sample Preparation | |
| { | |
| "id": "an_collection", | |
| "label": "Collection (1)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "In-line", "score": 3, "color": "green"}, | |
| {"label": "On-line or at-line", "score": 2, "color": "yellow"}, | |
| {"label": "Off-line", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_preservation", | |
| "label": "Preservation (2)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "None", "score": 3, "color": "green"}, | |
| {"label": "Chemical or physical", "score": 2, "color": "yellow"}, | |
| {"label": "Physicochemical", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_transport", | |
| "label": "Transport (3)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "None", "score": 3, "color": "green"}, | |
| {"label": "Required", "score": 2, "color": "yellow"}, | |
| {"label": "—", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_storage", | |
| "label": "Storage (4)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "None", "score": 3, "color": "green"}, | |
| {"label": "Under normal conditions", "score": 2, "color": "yellow"}, | |
| {"label": "Under special conditions", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_method_type", | |
| "label": "Type of method: direct/indirect (5)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "No sample preparation", "score": 3, "color": "green"}, | |
| {"label": "Simple procedures (filtration, decantation)", "score": 2, "color": "yellow"}, | |
| {"label": "Extraction required", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_extraction_scale", | |
| "label": "Scale of extraction (6)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "Nanoextraction", "score": 3, "color": "green"}, | |
| {"label": "Microextraction", "score": 2, "color": "yellow"}, | |
| {"label": "Macroextraction", "score": 1, "color": "red"}, | |
| {"label": "Not applicable", "score": 0, "color": "muted"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_solvents_type", | |
| "label": "Solvents/Reagents used (7)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "Solvent-free methods", "score": 3, "color": "green"}, | |
| {"label": "Green solvents/reagents", "score": 2, "color": "yellow"}, | |
| {"label": "Non-green solvents/reagents", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_additional_treatments", | |
| "label": "Additional treatments (8)", | |
| "block": "Sample Preparation", | |
| "options": [ | |
| {"label": "None", "score": 3, "color": "green"}, | |
| {"label": "Simple (extract clean-up, solvent removal)", "score": 2, "color": "yellow"}, | |
| {"label": "Advanced (derivatization, mineralization)", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| # Reagents and Solvents | |
| { | |
| "id": "an_amount", | |
| "label": "Amount of reagents/solvents (9)", | |
| "block": "Reagents & Solvents", | |
| "options": [ | |
| {"label": "<10 mL or <10 g", "score": 3, "color": "green"}, | |
| {"label": "10–100 mL or 10–100 g", "score": 2, "color": "yellow"}, | |
| {"label": ">100 mL or >100 g", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_health_hazard", | |
| "label": "Health hazard (10)", | |
| "block": "Reagents & Solvents", | |
| "options": [ | |
| {"label": "NFPA health 0–1 (slightly toxic/irritant)", "score": 3, "color": "green"}, | |
| {"label": "NFPA health 2–3 (moderately toxic)", "score": 2, "color": "yellow"}, | |
| {"label": "NFPA health 4 (serious injury / carcinogen)", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_safety_hazard", | |
| "label": "Safety hazard (11)", | |
| "block": "Reagents & Solvents", | |
| "options": [ | |
| {"label": "NFPA flammability/instability 0–1, no special hazards", "score": 3, "color": "green"}, | |
| {"label": "NFPA flammability/instability 2–3, or special hazard", "score": 2, "color": "yellow"}, | |
| {"label": "NFPA flammability/instability 4", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| # Instrumentation | |
| { | |
| "id": "an_energy", | |
| "label": "Energy (12)", | |
| "block": "Instrumentation", | |
| "options": [ | |
| {"label": "≤0.1 kWh per sample", "score": 3, "color": "green"}, | |
| {"label": "≤1.5 kWh per sample", "score": 2, "color": "yellow"}, | |
| {"label": ">1.5 kWh per sample", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_occupational", | |
| "label": "Occupational hazard (13)", | |
| "block": "Instrumentation", | |
| "options": [ | |
| {"label": "Hermetization of the analytical process", "score": 3, "color": "green"}, | |
| {"label": "—", "score": 2, "color": "yellow"}, | |
| {"label": "Emission of vapours to atmosphere", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_waste", | |
| "label": "Waste (14)", | |
| "block": "Instrumentation", | |
| "options": [ | |
| {"label": "<1 mL or <1 g", "score": 3, "color": "green"}, | |
| {"label": "1–10 mL or 1–10 g", "score": 2, "color": "yellow"}, | |
| {"label": ">10 mL or >10 g", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| { | |
| "id": "an_waste_treatment", | |
| "label": "Waste treatment (15)", | |
| "block": "Instrumentation", | |
| "options": [ | |
| {"label": "Recycling", "score": 3, "color": "green"}, | |
| {"label": "Degradation or passivation", "score": 2, "color": "yellow"}, | |
| {"label": "No treatment", "score": 1, "color": "red"}, | |
| ], | |
| }, | |
| ] | |
| ALL_PARAMS = PRE_ANALYSIS_PARAMS + ANALYTICAL_PARAMS | |
| # Quantification mark: 5 pts (yes, oval shown) or 1 pt (no, qualification only) per article | |
| QUANT_PTS_YES = 5 | |
| QUANT_PTS_NO = 1 | |
| BASE_MAX_TGS = 80 # 25 params * 3 (75) + 5 (Quantification) | |
| class ComplexMoGAPICalculator: | |
| """Calculates the Total Greenness Score (TGS) for ComplexMoGAPI.""" | |
| def score_param(self, param_id: str, option_index: int) -> Dict[str, Any]: | |
| """Score a single parameter given its id and the chosen option index (0=green,1=yellow,2=red).""" | |
| param = next((p for p in ALL_PARAMS if p["id"] == param_id), None) | |
| if param is None: | |
| return {"score": 1, "color": "red", "label": "Unknown", "option": "N/A"} | |
| idx = max(0, min(option_index, len(param["options"]) - 1)) | |
| opt = param["options"][idx] | |
| return { | |
| "param_id": param_id, | |
| "param_label": param["label"], | |
| "block": param["block"], | |
| "option_index": idx, | |
| "option_label": opt["label"], | |
| "score": opt["score"], | |
| "color": opt["color"], | |
| } | |
| def calculate(self, selections: Dict[str, int], has_quantification: bool = True, | |
| e_factor: float = 0.0) -> Dict[str, Any]: | |
| """ | |
| Calculate TGS from a dict of {param_id: option_index}. | |
| option_index: 0 = Green (3 pts), 1 = Yellow (2 pts), 2 = Red (1 pt). | |
| has_quantification: True = oval shown = 5 pts, False = 1 pt (per ComplexGAPI article). | |
| e_factor: numeric only; displayed but NOT included in TGS. | |
| Returns structured results including breakdown, TGS, and greenness verdict. | |
| """ | |
| breakdown = [] | |
| tgs = 0 | |
| zeros = 0 | |
| for param in ALL_PARAMS: | |
| pid = param["id"] | |
| idx = selections.get(pid, 0) # Default to green if not provided | |
| result = self.score_param(pid, int(idx)) | |
| breakdown.append(result) | |
| if result["score"] == 0: | |
| zeros += 1 | |
| else: | |
| tgs += result["score"] | |
| # Quantification mark (per article: oval = 5 pts, no oval = 1 pt) | |
| quant_pts = QUANT_PTS_YES if has_quantification else QUANT_PTS_NO | |
| tgs += quant_pts | |
| # Calculate max_score dynamically based strictly on the original paper implementation | |
| max_tgs = BASE_MAX_TGS - (3 * zeros) | |
| # Greenness percentage is calculated directly as (tgs / max_tgs) * 100 | |
| tgs_pct = (tgs / max_tgs) * 100 if max_tgs > 0 else 0 | |
| if tgs_pct >= 75: | |
| verdict = "Excellent Greenness" | |
| verdict_color = "green" | |
| elif tgs_pct >= 50: | |
| verdict = "Good Greenness" | |
| verdict_color = "yellow_high" | |
| elif tgs_pct >= 25: | |
| verdict = "Moderate Greenness" | |
| verdict_color = "yellow" | |
| else: | |
| verdict = "Poor Greenness" | |
| verdict_color = "red" | |
| color_counts = { | |
| "green": sum(1 for b in breakdown if b["color"] == "green"), | |
| "yellow": sum(1 for b in breakdown if b["color"] == "yellow"), | |
| "red": sum(1 for b in breakdown if b["color"] == "red"), | |
| } | |
| pre_breakdown = [b for b in breakdown if b["param_id"].startswith("pre_")] | |
| an_breakdown = [b for b in breakdown if b["param_id"].startswith("an_")] | |
| return { | |
| "tgs": tgs, | |
| "tgs_max": max_tgs, | |
| "tgs_pct": round(tgs_pct, 1), | |
| "verdict": verdict, | |
| "verdict_color": verdict_color, | |
| "color_counts": color_counts, | |
| "breakdown": breakdown, | |
| "pre_analysis_breakdown": pre_breakdown, | |
| "analytical_breakdown": an_breakdown, | |
| "has_quantification": has_quantification, | |
| "quant_pts": quant_pts, | |
| "e_factor": e_factor, | |
| "param_definitions": ALL_PARAMS, | |
| } | |
| def get_param_definitions() -> list: | |
| """Returns the full parameter definitions for frontend rendering.""" | |
| return ALL_PARAMS | |