File size: 15,643 Bytes
fb5dc87
 
 
888355f
fb5dc87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
"""
Parameter Standardization Utility
Handles consistent naming and mapping of soil and leaf analysis parameters
across all modules in the CropDrive™ AI Assistant system.
"""

import re
from typing import Dict, List, Any, Optional

class ParameterStandardizer:
    """Centralized parameter standardization for soil and leaf analysis data"""
    
    def __init__(self):
        # Standard parameter names (canonical format)
        self.STANDARD_SOIL_PARAMS = {
            'pH': 'pH',
            'nitrogen': 'N (%)',
            'organic_carbon': 'Org. C (%)',
            'total_phosphorus': 'Total P (mg/kg)',
            'available_phosphorus': 'Avail P (mg/kg)',
            'exchangeable_potassium': 'Exch. K (meq/100 g)',
            'exchangeable_calcium': 'Exch. Ca (meq/100 g)',
            'exchangeable_magnesium': 'Exch. Mg (meq/100 g)',
            'cec': 'CEC (meq/100 g)'
        }
        
        self.STANDARD_LEAF_PARAMS = {
            'nitrogen': 'N (%)',
            'phosphorus': 'P (%)',
            'potassium': 'K (%)',
            'magnesium': 'Mg (%)',
            'calcium': 'Ca (%)',
            'boron': 'B (mg/kg)',
            'copper': 'Cu (mg/kg)',
            'zinc': 'Zn (mg/kg)'
        }
        
        # Comprehensive mapping of all possible parameter name variations
        self.PARAMETER_VARIATIONS = {
            # pH variations
            'pH': ['ph', 'pH', 'PH', 'p.h.', 'acidity', 'alkalinity'],
            
            # Nitrogen variations
            'N (%)': [
                'nitrogen', 'n', 'n%', 'n (%)', 'n_%', 'nitrogen%', 'nitrogen (%)', 
                'nitrogen_%', 'total n', 'total nitrogen', 'total n (%)', 'total nitrogen (%)',
                'Nitrogen (%)', 'Total Nitrogen (%)'
            ],
            
            # Organic Carbon variations
            'Org. C (%)': [
                'organic carbon', 'organic_carbon', 'carbon', 'c', 'c%', 'c (%)', 
                'c_%', 'organic_carbon_%', 'org. c (%)', 'org c (%)', 'org.c (%)',
                'Organic Carbon (%)', 'Org. C (%)'
            ],
            
            # Total Phosphorus variations
            'Total P (mg/kg)': [
                'total phosphorus', 'total p', 'total_p', 'total phosphorus mg/kg', 
                'total_p_mg_kg', 'total p (mg/kg)', 'total_p_(mg/kg)'
            ],
            
            # Available Phosphorus variations
            'Avail P (mg/kg)': [
                'available phosphorus', 'available p', 'available_p', 'avail p', 
                'avail_p', 'available phosphorus mg/kg', 'available_p_mg_kg',
                'avail p (mg/kg)', 'avail_p_(mg/kg)', 'available p (mg/kg)',
                'Available P (mg/kg)', 'Available Phosphorus (mg/kg)'
            ],
            
            # Exchangeable Potassium variations
            'Exch. K (meq/100 g)': [
                'exchangeable potassium', 'exch k', 'exch_k', 'exchangeable k',
                'exchangeable_k', 'k meq%', 'k_meq%', 'exchangeable_k_meq%',
                'exch. k (meq%)', 'exch k (meq%)', 'exch. k meq%', 'exch k meq%',
                'exch k (cmol/kg)', 'exch. k (cmol/kg)', 'exch k cmol/kg', 'exch. k cmol/kg',
                'k meq/100 g', 'k_meq/100 g', 'exchangeable_k_meq/100 g',
                'exch. k (meq/100 g)', 'exch k (meq/100 g)', 'exch. k meq/100 g', 'exch k meq/100 g'
            ],

            # Exchangeable Calcium variations
            'Exch. Ca (meq/100 g)': [
                'exchangeable calcium', 'exch ca', 'exch_ca', 'exchangeable ca',
                'exchangeable_ca', 'ca meq%', 'ca_meq%', 'exchangeable_ca_meq%',
                'exch. ca (meq%)', 'exch ca (meq%)', 'exch. ca meq%', 'exch ca meq%',
                'exch ca (cmol/kg)', 'exch. ca (cmol/kg)', 'exch ca cmol/kg', 'exch. ca cmol/kg',
                'ca meq/100 g', 'ca_meq/100 g', 'exchangeable_ca_meq/100 g',
                'exch. ca (meq/100 g)', 'exch ca (meq/100 g)', 'exch. ca meq/100 g', 'exch ca meq/100 g'
            ],

            # Exchangeable Magnesium variations
            'Exch. Mg (meq/100 g)': [
                'exchangeable magnesium', 'exch mg', 'exch_mg', 'exchangeable mg',
                'exchangeable_mg', 'mg meq%', 'mg_meq%', 'exchangeable_mg_meq%',
                'exch. mg (meq%)', 'exch mg (meq%)', 'exch. mg meq%', 'exch mg meq%',
                'exch mg (cmol/kg)', 'exch. mg (cmol/kg)', 'exch mg cmol/kg', 'exch. mg cmol/kg',
                'mg meq/100 g', 'mg_meq/100 g', 'exchangeable_mg_meq/100 g',
                'exch. mg (meq/100 g)', 'exch mg (meq/100 g)', 'exch. mg meq/100 g', 'exch mg meq/100 g'
            ],

            # CEC variations
            'CEC (meq/100 g)': [
                'cec', 'cation exchange capacity', 'c.e.c', 'cec meq%', 'cec_meq%',
                'cec (meq%)', 'c.e.c (meq%)', 'c.e.c meq%', 'cec (cmol/kg)', 'cec cmol/kg',
                'C.E.C (meq%)', 'CEC (meq%)', 'cec meq/100 g', 'cec_meq/100 g',
                'cec (meq/100 g)', 'c.e.c (meq/100 g)', 'c.e.c meq/100 g',
                'C.E.C (meq/100 g)', 'CEC (meq/100 g)'
            ],
            
            # Leaf parameter variations
            'P (%)': [
                'phosphorus', 'p', 'p%', 'p (%)', 'p_%', 'phosphorus%', 'phosphorus (%)',
                'leaf phosphorus', 'leaf p', 'leaf_p'
            ],
            
            'K (%)': [
                'potassium', 'k', 'k%', 'k (%)', 'k_%', 'potassium%', 'potassium (%)',
                'leaf potassium', 'leaf k', 'leaf_k'
            ],
            
            'Mg (%)': [
                'magnesium', 'mg', 'mg%', 'mg (%)', 'mg_%', 'magnesium%', 'magnesium (%)',
                'leaf magnesium', 'leaf mg', 'leaf_mg'
            ],
            
            'Ca (%)': [
                'calcium', 'ca', 'ca%', 'ca (%)', 'ca_%', 'calcium%', 'calcium (%)',
                'leaf calcium', 'leaf ca', 'leaf_ca'
            ],
            
            'B (mg/kg)': [
                'boron', 'b', 'b mg/kg', 'b_mg_kg', 'boron mg/kg', 'boron_mg_kg',
                'b (mg/kg)', 'boron (mg/kg)', 'leaf boron', 'leaf b', 'leaf_b'
            ],
            
            'Cu (mg/kg)': [
                'copper', 'cu', 'cu mg/kg', 'cu_mg_kg', 'copper mg/kg', 'copper_mg_kg',
                'cu (mg/kg)', 'copper (mg/kg)', 'leaf copper', 'leaf cu', 'leaf_cu'
            ],
            
            'Zn (mg/kg)': [
                'zinc', 'zn', 'zn mg/kg', 'zn_mg_kg', 'zinc mg/kg', 'zinc_mg_kg',
                'zn (mg/kg)', 'zinc (mg/kg)', 'leaf zinc', 'leaf zn', 'leaf_zn'
            ]
        }
        
        # Create reverse mapping for quick lookup
        self.variation_to_standard = {}
        for standard, variations in self.PARAMETER_VARIATIONS.items():
            for variation in variations:
                self.variation_to_standard[variation.lower()] = standard
    
    def standardize_parameter_name(self, param_name: str) -> Optional[str]:
        """
        Convert any parameter name variation to the standard format
        
        Args:
            param_name: The parameter name to standardize
            
        Returns:
            Standard parameter name or None if not found
        """
        if not param_name:
            return None
            
        # Clean the parameter name
        clean_name = param_name.strip().lower()
        clean_name = re.sub(r'\s+', ' ', clean_name)  # Normalize whitespace
        
        # Direct lookup
        if clean_name in self.variation_to_standard:
            return self.variation_to_standard[clean_name]
        
        # Fuzzy matching for partial matches (more precise)
        for variation, standard in self.variation_to_standard.items():
            # Only match if the variation is a significant part of the clean_name
            # Avoid matching single characters or very short variations
            if len(variation) >= 3 and (variation in clean_name or clean_name in variation):
                return standard
        
        return None
    
    def standardize_data_dict(self, data_dict: Dict[str, Any]) -> Dict[str, Any]:
        """
        Standardize all parameter names in a data dictionary
        
        Args:
            data_dict: Dictionary with potentially non-standard parameter names
            
        Returns:
            Dictionary with standardized parameter names
        """
        standardized = {}
        
        for key, value in data_dict.items():
            standard_key = self.standardize_parameter_name(key)
            if standard_key:
                standardized[standard_key] = value
            else:
                # Keep non-parameter keys as-is (like sample_id, lab_no, etc.)
                standardized[key] = value
        
        return standardized
    
    def standardize_samples_list(self, samples: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        Standardize parameter names in a list of sample dictionaries
        
        Args:
            samples: List of sample dictionaries
            
        Returns:
            List of samples with standardized parameter names
        """
        return [self.standardize_data_dict(sample) for sample in samples]
    
    def get_display_name_mapping(self, param_type: str = 'soil') -> Dict[str, str]:
        """
        Get mapping from standard parameter names to display names
        
        Args:
            param_type: 'soil' or 'leaf'
            
        Returns:
            Dictionary mapping standard names to display names
        """
        if param_type.lower() == 'soil':
            return {
                'pH': 'pH',
                'N (%)': 'Nitrogen (%)',
                'Org. C (%)': 'Organic Carbon (%)',
                'Total P (mg/kg)': 'Total P (mg/kg)',
                'Avail P (mg/kg)': 'Available P (mg/kg)',
                'Exch. K (meq/100 g)': 'Exchangeable K (meq/100 g)',
                'Exch. Ca (meq/100 g)': 'Exchangeable Ca (meq/100 g)',
                'Exch. Mg (meq/100 g)': 'Exchangeable Mg (meq/100 g)',
                'CEC (meq/100 g)': 'CEC (meq/100 g)'
            }
        else:  # leaf
            return {
                'N (%)': 'Nitrogen (%)',
                'P (%)': 'Phosphorus (%)',
                'K (%)': 'Potassium (%)',
                'Mg (%)': 'Magnesium (%)',
                'Ca (%)': 'Calcium (%)',
                'B (mg/kg)': 'Boron (mg/kg)',
                'Cu (mg/kg)': 'Copper (mg/kg)',
                'Zn (mg/kg)': 'Zinc (mg/kg)'
            }
    
    def validate_parameter_completeness(self, data_dict: Dict[str, Any], param_type: str = 'soil') -> Dict[str, Any]:
        """
        Validate that all expected parameters are present and add missing ones with default values
        
        Args:
            data_dict: Data dictionary to validate
            param_type: 'soil' or 'leaf'
            
        Returns:
            Dictionary with all expected parameters
        """
        if param_type.lower() == 'soil':
            expected_params = list(self.STANDARD_SOIL_PARAMS.values())
        else:
            expected_params = list(self.STANDARD_LEAF_PARAMS.values())
        
        validated_data = data_dict.copy()
        
        for param in expected_params:
            if param not in validated_data:
                validated_data[param] = 0.0  # Default value for missing parameters
        
        return validated_data
    
    @classmethod
    def get_parameter_variations_mapping(cls) -> Dict[str, List[str]]:
        """Get parameter variations mapping for text analysis"""
        return {
            # Soil Parameters (9)
            'ph': ['ph', 'ph level', 'soil ph', 'acidity', 'alkalinity'],
            'nitrogen': ['nitrogen', 'n', 'n%', 'n_%', 'nitrogen%', 'nitrogen_%', 'n (%)', 'n(%)'],
            'organic_carbon': ['organic carbon', 'organic_carbon', 'carbon', 'c', 'c%', 'c_%', 'organic_carbon_%', 'org. c (%)', 'org c (%)', 'org.c(%)'],
            'total_phosphorus': ['total phosphorus', 'total p', 'total_p', 'total phosphorus mg/kg', 'total_p_mg_kg', 'total p (mg/kg)', 'total p(mg/kg)'],
            'available_phosphorus': ['available phosphorus', 'available p', 'available_p', 'available phosphorus mg/kg', 'available_p_mg_kg', 'avail p (mg/kg)', 'avail p(mg/kg)'],
            'exchangeable_potassium': ['exchangeable potassium', 'exch k', 'exch_k', 'exchangeable k', 'exchangeable_k', 'k meq%', 'k_meq%', 'exchangeable_k_meq%', 'exch. k (meq%)', 'exch k (meq%)', 'exch.k(meq%)'],
            'exchangeable_calcium': ['exchangeable calcium', 'exch ca', 'exch_ca', 'exchangeable ca', 'exchangeable_ca', 'ca meq%', 'ca_meq%', 'exchangeable_ca_meq%', 'exch. ca (meq%)', 'exch ca (meq%)', 'exch.ca(meq%)'],
            'exchangeable_magnesium': ['exchangeable magnesium', 'exch mg', 'exch_mg', 'exchangeable mg', 'exchangeable_mg', 'mg meq%', 'mg_meq%', 'exchangeable_mg_meq%', 'exch. mg (meq%)', 'exch mg (meq%)', 'exch.mg(meq%)'],
            'cec': ['cec', 'cation exchange capacity', 'c.e.c', 'cec meq%', 'cec_meq%', 'cec (meq%)', 'cec(meq%)'],
            
            # Leaf Parameters (8)
            'leaf_nitrogen': ['leaf nitrogen', 'leaf n', 'leaf_n', 'n%', 'n_%', 'nitrogen%', 'nitrogen_%', 'n (%)', 'n(%)'],
            'leaf_phosphorus': ['leaf phosphorus', 'leaf p', 'leaf_p', 'p%', 'p_%', 'phosphorus%', 'phosphorus_%', 'p (%)', 'p(%)'],
            'leaf_potassium': ['leaf potassium', 'leaf k', 'leaf_k', 'k%', 'k_%', 'potassium%', 'potassium_%', 'k (%)', 'k(%)'],
            'leaf_magnesium': ['leaf magnesium', 'leaf mg', 'leaf_mg', 'mg%', 'mg_%', 'magnesium%', 'magnesium_%', 'mg (%)', 'mg(%)'],
            'leaf_calcium': ['leaf calcium', 'leaf ca', 'leaf_ca', 'ca%', 'ca_%', 'calcium%', 'calcium_%', 'ca (%)', 'ca(%)'],
            'leaf_boron': ['leaf boron', 'leaf b', 'leaf_b', 'b mg/kg', 'b_mg_kg', 'boron mg/kg', 'boron_mg_kg', 'b (mg/kg)', 'b(mg/kg)'],
            'leaf_copper': ['leaf copper', 'leaf cu', 'leaf_cu', 'cu mg/kg', 'cu_mg_kg', 'copper mg/kg', 'copper_mg_kg', 'cu (mg/kg)', 'cu(mg/kg)'],
            'leaf_zinc': ['leaf zinc', 'leaf zn', 'leaf_zn', 'zn mg/kg', 'zn_mg_kg', 'zinc mg/kg', 'zinc_mg_kg', 'zn (mg/kg)', 'zn(mg/kg)'],
            
            # Land & Yield Parameters
            'land_size': ['land size', 'land_size', 'farm size', 'farm_size', 'area', 'hectares', 'acres', 'square meters', 'square_meters'],
            'current_yield': ['current yield', 'current_yield', 'yield', 'production', 'tonnes/hectare', 'kg/hectare', 'tonnes/acre', 'kg/acre', 'yield per hectare', 'yield per acre'],
            'yield_forecast': ['yield forecast', 'yield_forecast', 'projected yield', 'projected_yield', 'future yield', 'future_yield', 'yield projection', 'yield_projection'],
            'economic_impact': ['economic impact', 'economic_impact', 'roi', 'return on investment', 'cost benefit', 'cost_benefit', 'profitability', 'revenue', 'income'],
            
            # Legacy mappings for backward compatibility
            'phosphorus': ['phosphorus', 'p', 'p%', 'p_%', 'phosphorus%', 'available p'],
            'potassium': ['potassium', 'k', 'k%', 'k_%', 'potassium%'],
            'calcium': ['calcium', 'ca', 'ca%', 'ca_%', 'calcium%'],
            'magnesium': ['magnesium', 'mg', 'mg%', 'mg_%', 'magnesium%'],
            'copper': ['copper', 'cu', 'cu mg/kg', 'cu_mg/kg', 'copper mg/kg'],
            'zinc': ['zinc', 'zn', 'zn mg/kg', 'zn_mg/kg', 'zinc mg/kg'],
            'boron': ['boron', 'b', 'b mg/kg', 'b_mg/kg', 'boron mg/kg']
        }

# Global instance for easy import
parameter_standardizer = ParameterStandardizer()