@@ -216,8 +216,114 @@ def _should_include_field_by_default(self, field_name: str, field_info: Dict[str
216216
217217 return False
218218
219+ def _score_field_importance (self , field_name : str , field_info : Dict [str , Any ]) -> int :
220+ """Score field importance for smart default selection.
221+
222+ Args:
223+ field_name: Name of the field
224+ field_info: Field metadata from fields_get()
225+
226+ Returns:
227+ Importance score (higher = more important)
228+ """
229+ # Tier 1: Essential fields (always included)
230+ if field_name in {"id" , "name" , "display_name" , "active" }:
231+ return 1000
232+
233+ # Exclude system/technical fields by prefix
234+ exclude_prefixes = ("_" , "message_" , "activity_" , "website_message_" )
235+ if field_name .startswith (exclude_prefixes ):
236+ return 0
237+
238+ # Exclude specific technical fields
239+ exclude_fields = {
240+ "write_date" ,
241+ "create_date" ,
242+ "write_uid" ,
243+ "create_uid" ,
244+ "__last_update" ,
245+ "access_token" ,
246+ "access_warning" ,
247+ "access_url" ,
248+ }
249+ if field_name in exclude_fields :
250+ return 0
251+
252+ score = 0
253+
254+ # Tier 2: Required fields are very important
255+ if field_info .get ("required" ):
256+ score += 500
257+
258+ # Tier 3: Field type importance
259+ field_type = field_info .get ("type" , "" )
260+ type_scores = {
261+ "char" : 200 ,
262+ "boolean" : 180 ,
263+ "selection" : 170 ,
264+ "integer" : 160 ,
265+ "float" : 160 ,
266+ "monetary" : 140 ,
267+ "date" : 150 ,
268+ "datetime" : 150 ,
269+ "many2one" : 120 , # Relations useful but not primary
270+ "text" : 80 ,
271+ "one2many" : 40 ,
272+ "many2many" : 40 , # Heavy relations
273+ "binary" : 10 ,
274+ "html" : 10 ,
275+ "image" : 10 , # Heavy content
276+ }
277+ score += type_scores .get (field_type , 50 )
278+
279+ # Tier 4: Storage and searchability bonuses
280+ if field_info .get ("store" , True ):
281+ score += 80
282+ if field_info .get ("searchable" , True ):
283+ score += 40
284+
285+ # Tier 5: Business-relevant field patterns (bonus)
286+ business_patterns = [
287+ "state" ,
288+ "status" ,
289+ "stage" ,
290+ "priority" ,
291+ "company" ,
292+ "currency" ,
293+ "amount" ,
294+ "total" ,
295+ "date" ,
296+ "user" ,
297+ "partner" ,
298+ "email" ,
299+ "phone" ,
300+ "address" ,
301+ "street" ,
302+ "city" ,
303+ "country" ,
304+ "code" ,
305+ "ref" ,
306+ "number" ,
307+ ]
308+ if any (pattern in field_name .lower () for pattern in business_patterns ):
309+ score += 60
310+
311+ # Exclude expensive computed fields (non-stored)
312+ if field_info .get ("compute" ) and not field_info .get ("store" , True ):
313+ score = min (score , 30 ) # Cap computed fields at low score
314+
315+ # Exclude large field types completely
316+ if field_type in ("binary" , "image" , "html" ):
317+ return 0
318+
319+ # Exclude one2many and many2many fields (can be large)
320+ if field_type in ("one2many" , "many2many" ):
321+ return 0
322+
323+ return max (score , 0 )
324+
219325 def _get_smart_default_fields (self , model : str ) -> Optional [List [str ]]:
220- """Get smart default fields for a model.
326+ """Get smart default fields for a model using field importance scoring .
221327
222328 Args:
223329 model: The Odoo model name
@@ -229,26 +335,41 @@ def _get_smart_default_fields(self, model: str) -> Optional[List[str]]:
229335 # Get all field definitions
230336 fields_info = self .connection .fields_get (model )
231337
232- # Apply smart filtering
233- default_fields = [
234- field_name
235- for field_name , field_info in fields_info .items ()
236- if self ._should_include_field_by_default (field_name , field_info )
237- ]
238-
239- # Ensure we have at least some fields
240- if not default_fields :
241- default_fields = ["id" , "name" , "display_name" ]
242-
243- # Sort fields for consistent output
244- # Priority order: id, name, display_name, then alphabetical
245- priority_fields = ["id" , "name" , "display_name" , "active" ]
246- other_fields = sorted (f for f in default_fields if f not in priority_fields )
247-
248- final_fields = [f for f in priority_fields if f in default_fields ] + other_fields
338+ # Score all fields by importance
339+ field_scores = []
340+ for field_name , field_info in fields_info .items ():
341+ score = self ._score_field_importance (field_name , field_info )
342+ if score > 0 : # Only include fields with positive scores
343+ field_scores .append ((field_name , score ))
344+
345+ # Sort by score (highest first)
346+ field_scores .sort (key = lambda x : x [1 ], reverse = True )
347+
348+ # Select top N fields based on configuration
349+ max_fields = self .config .max_smart_fields
350+ selected_fields = [field_name for field_name , _ in field_scores [:max_fields ]]
351+
352+ # Ensure essential fields are always included
353+ essential_fields = ["id" , "name" , "display_name" , "active" ]
354+ for field in essential_fields :
355+ if field in fields_info and field not in selected_fields :
356+ selected_fields .append (field )
357+
358+ # Remove duplicates while preserving order
359+ final_fields = []
360+ seen = set ()
361+ for field in selected_fields :
362+ if field not in seen :
363+ final_fields .append (field )
364+ seen .add (field )
365+
366+ # Ensure we have at least essential fields
367+ if not final_fields :
368+ final_fields = [f for f in essential_fields if f in fields_info ]
249369
250370 logger .debug (
251- f"Smart default fields for { model } : { len (final_fields )} of { len (fields_info )} fields"
371+ f"Smart default fields for { model } : { len (final_fields )} of { len (fields_info )} fields "
372+ f"(max configured: { max_fields } )"
252373 )
253374 return final_fields
254375
0 commit comments