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
1=== modified file 'product/product.py'
2--- product/product.py 2013-11-20 01:17:39 +0000
3+++ product/product.py 2014-03-03 13:12:34 +0000
4@@ -193,6 +193,8 @@
5 #----------------------------------------------------------
6 class product_category(osv.osv):
7
8+ product_category_separator = ' / '
9+
10 def name_get(self, cr, uid, ids, context=None):
11 if not len(ids):
12 return []
13@@ -201,9 +203,45 @@
14 for record in reads:
15 name = record['name']
16 if record['parent_id']:
17- name = record['parent_id'][1]+' / '+name
18+ name = record['parent_id'][1] + self.product_category_separator + name
19 res.append((record['id'], name))
20 return res
21+
22+ def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
23+ if not args:
24+ args = []
25+ classic_ids = []
26+ if name:
27+ clean_name = False
28+ category_args = []
29+ if self.product_category_separator in name:
30+ # Get a list of each category names and remove extra spaces
31+ category_names = [x for x in name.split(self.product_category_separator) if x.strip()]
32+ # Use only latest value and remove interferences
33+ if category_names:
34+ clean_name = category_names[-1].strip(self.product_category_separator + ' ')
35+ # Extract parent name for better accuracy
36+ if len(category_names) > 1:
37+ parent_name = category_names[-2].strip(self.product_category_separator + ' ')
38+ category_args = [('parent_id.name', '=', parent_name)]
39+
40+ ids = self.search(cr, user, [('name', operator, clean_name or name)] + category_args + args,
41+ limit=limit, context=context)
42+ # Get remaining (classic) results (using clean name if exists) and avoid duplicates
43+ if not limit or len(ids) < limit:
44+ classic_ids = self.search(cr, user, [('name', operator, clean_name or name), ('id', 'not in', ids)],
45+ limit=(limit-len(ids) if limit else limit), context=context)
46+ if classic_ids:
47+ ids = ids + classic_ids
48+ else:
49+ ids = self.search(cr, user, args, limit=limit, context=context)
50+
51+ # Get named values
52+ result = self.name_get(cr, user, ids, context=context)
53+ # Re-order data by search priority since name_get lost our order (ids first, classic_ids afterward)
54+ if classic_ids:
55+ result = sorted(result, lambda x,y: cmp(ids.index(x[0]), ids.index(y[0])))
56+ return result
57
58 def _name_get_fnc(self, cr, uid, ids, prop, unknow_none, context=None):
59 res = self.name_get(cr, uid, ids, context=context)
60@@ -600,6 +638,7 @@
61 def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100):
62 if not args:
63 args = []
64+ extended_ids = []
65 if name:
66 ids = self.search(cr, user, [('default_code','=',name)]+ args, limit=limit, context=context)
67 if not ids:
68@@ -616,13 +655,21 @@
69 ids.update(self.search(cr, user, args + [('name',operator,name)], limit=(limit-len(ids) if limit else limit) , context=context))
70 ids = list(ids)
71 if not ids:
72- ptrn = re.compile('(\[(.*?)\])')
73+ ptrn = re.compile('(\[([^]]+)\]?)')
74 res = ptrn.search(name)
75 if res:
76- ids = self.search(cr, user, [('default_code','=', res.group(2))] + args, limit=limit, context=context)
77+ ids = self.search(cr, user, [('default_code', '=', res.group(2))] + args, limit=limit, context=context)
78+ if not limit or len(ids) < limit:
79+ res = [x for x in ptrn.split(name) if x]
80+ if res:
81+ 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)
82+ ids += extended_ids
83 else:
84 ids = self.search(cr, user, args, limit=limit, context=context)
85 result = self.name_get(cr, user, ids, context=context)
86+ # Re-order data by search priority since name_get lost our order (ids first, extended_ids afterward)
87+ if extended_ids:
88+ result = sorted(result, lambda x,y: cmp(ids.index(x[0]), ids.index(y[0])))
89 return result
90
91 #

Subscribers

People subscribed via source and target branches