Merge lp:~yann-papouin/ocb-addons/6.1-bug-1267845-product-and-category-search-improved-usability into lp:ocb-addons/6.1

Proposed by Yann Papouin
Status: Needs review
Proposed branch: lp:~yann-papouin/ocb-addons/6.1-bug-1267845-product-and-category-search-improved-usability
Merge into: lp:ocb-addons/6.1
Diff against target: 91 lines (+50/-3)
1 file modified
product/product.py (+50/-3)
To merge this branch: bzr merge lp:~yann-papouin/ocb-addons/6.1-bug-1267845-product-and-category-search-improved-usability
Reviewer Review Type Date Requested Status
Holger Brunn (Therp) Needs Information
Review via email: mp+201192@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

18: add spaces between operators

21: this seems to be one line too much

33: that's not safe against searching for ' / ' or the like, I'd wrap that into an
if category_names:

33, 36: shouldn't strip(product_category_separator + ' ') be enough? (the space just to be safe if we ever change product_category_separator)

42: if ids and classic_ids are nondistinct sets and there's a limit, you'll get less than limit ids, which is particularly annoying for name_search. I think you should pass limit+len(ids), remove duplicates in a manner that preserves class_ids' order and then shorten it as necessary

53: why is this necessary? And if so, why only if name is set?

63: can't you integrate that in the regex above? If you'll only have '[bla' trigger searching for the default code, but not 'bla]', adding a '?' after the closing square bracket would be enough. (and I can't really think of a use case for 'bla]').

64: if you don't agree with 63, .strip('[] ') should be enough here

review: Needs Fixing
6828. By Yann Papouin

Check category_names length before accessing by index
Simpler strip
classic_ids are not duplicates of ids anymore
Reorder only if classic_ids if not empty

6829. By Yann Papouin

Simpler strip for product name_search

Revision history for this message
Yann Papouin (yann-papouin) wrote :

Thank you for your advices.

There is no more duplicates since ids are filtered by postgresql.
I added a manual result reorder because OpenERP "read" function does not return results in the ids order. This reordering is only necessary when a concatenation exists (via classics_ids)

About the regex, I tried to add a '?' but it's not working, it always returns an empty value

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

Yes, letting postgres do that is simpler and faster I guess.

Concerning the regex:

>>> import re
>>> re.compile('(\[(.*?)\]?)').search('[bla')
<_sre.SRE_Match object at 0x7f0be1590558>

That seems to work for me.

Looks good otherwise!

Revision history for this message
Yann Papouin (yann-papouin) wrote :

I'm getting following results:

>>> re.compile('(\[(.*?)\]?)').search('[bla').group(1)
'['
>>> re.compile('(\[(.*?)\]?)').search('[bla').group(2)
''
>>> re.compile('(\[(.*?)\]?)').search('[bla] bob').group(1)
'['
>>> re.compile('(\[(.*?)\]?)').search('[bla] bob').group(2)
''
>>> re.compile('(\[(.*?)\])').search('[bla] bob').group(1)
'[bla]'
>>> re.compile('(\[(.*?)\])').search('[bla] bob').group(2)
'bla'

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

You're right. This one should cover them all:
re.compile('(\[([^]]+)\]?)')

Revision history for this message
Yann Papouin (yann-papouin) wrote :

Indeed, it covers previous cases, but it fails if a close bracket exists in 'default_code' or 'name'.

>>> re.compile('(\[([^]]+)\]?)').search('[Code[A][B][C]] Product name').group(2)
'Code[A'

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

which gives the same result as the original regex

>>> re.compile('(\[(.*?)\])').search('[Code[A][B][C]] Product name').group(2)
'Code[A'

and the code path you added would search for 'Code[A][B][C' which doesn't seem that much better to me.

Don't you think that's a quite theoretical case?

Revision history for this message
Holger Brunn (Therp) (hbrunn) wrote :

How about that one?

review: Needs Information
6830. By Yann Papouin

More generic regex for product name_search
Extended search based on name added if limit is not reached
Reorder if extended_ids is not empty

Revision history for this message
Yann Papouin (yann-papouin) wrote :

New commit with your regex in place.

To mimic the already existing behaviour, I've added an extended search based on name.

Note that contrary to the original code, I'm not using a python set because it is not ordered, I'm just using postgresql to filter the second query by removing previously found IDs. A sorted operation is done at the end, after the name_get.

Since this part of the code is really close to the existing code, maybe a mix between these two parts could be considered.

Unmerged revisions

6830. By Yann Papouin

More generic regex for product name_search
Extended search based on name added if limit is not reached
Reorder if extended_ids is not empty

6829. By Yann Papouin

Simpler strip for product name_search

6828. By Yann Papouin

Check category_names length before accessing by index
Simpler strip
classic_ids are not duplicates of ids anymore
Reorder only if classic_ids if not empty

6827. By Yann Papouin

Trim extra characters when searching for product and product categories for better usability

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'product/product.py'
--- product/product.py 2013-11-20 01:17:39 +0000
+++ product/product.py 2014-03-03 13:12:34 +0000
@@ -193,6 +193,8 @@
193#----------------------------------------------------------193#----------------------------------------------------------
194class product_category(osv.osv):194class product_category(osv.osv):
195195
196 product_category_separator = ' / '
197
196 def name_get(self, cr, uid, ids, context=None):198 def name_get(self, cr, uid, ids, context=None):
197 if not len(ids):199 if not len(ids):
198 return []200 return []
@@ -201,9 +203,45 @@
201 for record in reads:203 for record in reads:
202 name = record['name']204 name = record['name']
203 if record['parent_id']:205 if record['parent_id']:
204 name = record['parent_id'][1]+' / '+name206 name = record['parent_id'][1] + self.product_category_separator + name
205 res.append((record['id'], name))207 res.append((record['id'], name))
206 return res208 return res
209
210 def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
211 if not args:
212 args = []
213 classic_ids = []
214 if name:
215 clean_name = False
216 category_args = []
217 if self.product_category_separator in name:
218 # Get a list of each category names and remove extra spaces
219 category_names = [x for x in name.split(self.product_category_separator) if x.strip()]
220 # Use only latest value and remove interferences
221 if category_names:
222 clean_name = category_names[-1].strip(self.product_category_separator + ' ')
223 # Extract parent name for better accuracy
224 if len(category_names) > 1:
225 parent_name = category_names[-2].strip(self.product_category_separator + ' ')
226 category_args = [('parent_id.name', '=', parent_name)]
227
228 ids = self.search(cr, user, [('name', operator, clean_name or name)] + category_args + args,
229 limit=limit, context=context)
230 # Get remaining (classic) results (using clean name if exists) and avoid duplicates
231 if not limit or len(ids) < limit:
232 classic_ids = self.search(cr, user, [('name', operator, clean_name or name), ('id', 'not in', ids)],
233 limit=(limit-len(ids) if limit else limit), context=context)
234 if classic_ids:
235 ids = ids + classic_ids
236 else:
237 ids = self.search(cr, user, args, limit=limit, context=context)
238
239 # Get named values
240 result = self.name_get(cr, user, ids, context=context)
241 # Re-order data by search priority since name_get lost our order (ids first, classic_ids afterward)
242 if classic_ids:
243 result = sorted(result, lambda x,y: cmp(ids.index(x[0]), ids.index(y[0])))
244 return result
207245
208 def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):246 def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
209 res = self.name_get(cr, uid, ids, context=context)247 res = self.name_get(cr, uid, ids, context=context)
@@ -600,6 +638,7 @@
600 def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):638 def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
601 if not args:639 if not args:
602 args = []640 args = []
641 extended_ids = []
603 if name:642 if name:
604 ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context)643 ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context)
605 if not ids:644 if not ids:
@@ -616,13 +655,21 @@
616 ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit-len(ids) if limit else limit) , context=context))655 ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit-len(ids) if limit else limit) , context=context))
617 ids = list(ids)656 ids = list(ids)
618 if not ids:657 if not ids:
619 ptrn = re.compile('(\[(.*?)\])')658 ptrn = re.compile('(\[([^]]+)\]?)')
620 res = ptrn.search(name)659 res = ptrn.search(name)
621 if res:660 if res:
622 ids = self.search(cr, user, [('default_code','=', res.group(2))] + args, limit=limit, context=context)661 ids = self.search(cr, user, [('default_code', '=', res.group(2))] + args, limit=limit, context=context)
662 if not limit or len(ids) < limit:
663 res = [x for x in ptrn.split(name) if x]
664 if res:
665 extended_ids = self.search(cr, user, [('name', operator, res[-1].strip('[] ')), ('id', 'not in', ids)] + args, limit=(limit-len(ids) if limit else limit), context=context)
666 ids += extended_ids
623 else:667 else:
624 ids = self.search(cr, user, args, limit=limit, context=context)668 ids = self.search(cr, user, args, limit=limit, context=context)
625 result = self.name_get(cr, user, ids, context=context)669 result = self.name_get(cr, user, ids, context=context)
670 # Re-order data by search priority since name_get lost our order (ids first, extended_ids afterward)
671 if extended_ids:
672 result = sorted(result, lambda x,y: cmp(ids.index(x[0]), ids.index(y[0])))
626 return result673 return result
627674
628 #675 #

Subscribers

People subscribed via source and target branches