|
25 | 25 | import os, marshal, re, weakref, string, copy, time, shutil, logging |
26 | 26 |
|
27 | 27 | from roundup.anypy.dbm_ import anydbm, whichdb |
28 | | -from roundup.anypy.strings import b2s, bs2b, repr_export, eval_import |
| 28 | +from roundup.anypy.strings import b2s, bs2b, repr_export, eval_import, is_us |
29 | 29 |
|
30 | 30 | from roundup import hyperdb, date, password, roundupdb, security, support |
31 | 31 | from roundup.backends import locking |
@@ -1690,12 +1690,16 @@ def getnodeids(self, db=None, retired=None): |
1690 | 1690 | return res |
1691 | 1691 |
|
1692 | 1692 | def _filter(self, search_matches, filterspec, proptree, |
1693 | | - num_re = re.compile(r'^\d+$'), retired=False): |
| 1693 | + num_re = re.compile(r'^\d+$'), retired=False, |
| 1694 | + exact_match_spec={}): |
1694 | 1695 | """Return a list of the ids of the nodes in this class that |
1695 | 1696 | match the 'filter' spec, sorted by the group spec and then the |
1696 | 1697 | sort spec. |
1697 | 1698 |
|
1698 | 1699 | "filterspec" is {propname: value(s)} |
| 1700 | + same for "exact_match_spec". The latter specifies exact matching |
| 1701 | + for String type while String types in "filterspec" are searched |
| 1702 | + for as case insensitive substring match. |
1699 | 1703 |
|
1700 | 1704 | "sort" and "group" are (dir, prop) where dir is '+', '-' or None |
1701 | 1705 | and prop is a prop name or None |
@@ -1726,82 +1730,86 @@ def _filter(self, search_matches, filterspec, proptree, |
1726 | 1730 | INTERVAL = 'spec:interval' |
1727 | 1731 | OTHER = 'spec:other' |
1728 | 1732 |
|
1729 | | - for k, v in filterspec.items(): |
1730 | | - propclass = props[k] |
1731 | | - if isinstance(propclass, hyperdb.Link): |
1732 | | - if type(v) is not type([]): |
1733 | | - v = [v] |
1734 | | - u = [] |
1735 | | - for entry in v: |
| 1733 | + for exact, filtertype in enumerate((filterspec, exact_match_spec)): |
| 1734 | + for k, v in filtertype.items(): |
| 1735 | + propclass = props[k] |
| 1736 | + if isinstance(propclass, hyperdb.Link): |
| 1737 | + if type(v) is not type([]): |
| 1738 | + v = [v] |
| 1739 | + u = [] |
| 1740 | + for entry in v: |
| 1741 | + # the value -1 is a special "not set" sentinel |
| 1742 | + if entry == '-1': |
| 1743 | + entry = None |
| 1744 | + u.append(entry) |
| 1745 | + l.append((LINK, k, u)) |
| 1746 | + elif isinstance(propclass, hyperdb.Multilink): |
1736 | 1747 | # the value -1 is a special "not set" sentinel |
1737 | | - if entry == '-1': |
1738 | | - entry = None |
1739 | | - u.append(entry) |
1740 | | - l.append((LINK, k, u)) |
1741 | | - elif isinstance(propclass, hyperdb.Multilink): |
1742 | | - # the value -1 is a special "not set" sentinel |
1743 | | - if v in ('-1', ['-1']): |
1744 | | - v = [] |
1745 | | - elif type(v) is not type([]): |
1746 | | - v = [v] |
1747 | | - l.append((MULTILINK, k, v)) |
1748 | | - elif isinstance(propclass, hyperdb.String) and k != 'id': |
1749 | | - if type(v) is not type([]): |
1750 | | - v = [v] |
1751 | | - for v in v: |
1752 | | - # simple glob searching |
1753 | | - v = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', v) |
1754 | | - v = v.replace('?', '.') |
1755 | | - v = v.replace('*', '.*?') |
1756 | | - l.append((STRING, k, re.compile(v, re.I))) |
1757 | | - elif isinstance(propclass, hyperdb.Date): |
1758 | | - try: |
1759 | | - date_rng = propclass.range_from_raw(v, self.db) |
1760 | | - l.append((DATE, k, date_rng)) |
1761 | | - except ValueError: |
1762 | | - # If range creation fails - ignore that search parameter |
1763 | | - pass |
1764 | | - elif isinstance(propclass, hyperdb.Interval): |
1765 | | - try: |
1766 | | - intv_rng = date.Range(v, date.Interval) |
1767 | | - l.append((INTERVAL, k, intv_rng)) |
1768 | | - except ValueError: |
1769 | | - # If range creation fails - ignore that search parameter |
1770 | | - pass |
1771 | | - |
1772 | | - elif isinstance(propclass, hyperdb.Boolean): |
1773 | | - if type(v) == type(""): |
1774 | | - v = v.split(',') |
1775 | | - if type(v) != type([]): |
1776 | | - v = [v] |
1777 | | - bv = [] |
1778 | | - for val in v: |
1779 | | - if type(val) is type(''): |
1780 | | - bv.append(propclass.from_raw (val)) |
1781 | | - else: |
1782 | | - bv.append(val) |
1783 | | - l.append((OTHER, k, bv)) |
1784 | | - |
1785 | | - elif k == 'id': |
1786 | | - if type(v) != type([]): |
1787 | | - v = v.split(',') |
1788 | | - l.append((OTHER, k, [str(int(val)) for val in v])) |
1789 | | - |
1790 | | - elif isinstance(propclass, hyperdb.Number): |
1791 | | - if type(v) != type([]): |
1792 | | - try : |
| 1748 | + if v in ('-1', ['-1']): |
| 1749 | + v = [] |
| 1750 | + elif type(v) is not type([]): |
| 1751 | + v = [v] |
| 1752 | + l.append((MULTILINK, k, v)) |
| 1753 | + elif isinstance(propclass, hyperdb.String) and k != 'id': |
| 1754 | + if type(v) is not type([]): |
| 1755 | + v = [v] |
| 1756 | + for x in v: |
| 1757 | + if exact: |
| 1758 | + l.append((STRING, k, x)) |
| 1759 | + else: |
| 1760 | + # simple glob searching |
| 1761 | + x = re.sub(r'([\|\{\}\\\.\+\[\]\(\)])', r'\\\1', x) |
| 1762 | + x = x.replace('?', '.') |
| 1763 | + x = x.replace('*', '.*?') |
| 1764 | + l.append((STRING, k, re.compile(x, re.I))) |
| 1765 | + elif isinstance(propclass, hyperdb.Date): |
| 1766 | + try: |
| 1767 | + date_rng = propclass.range_from_raw(v, self.db) |
| 1768 | + l.append((DATE, k, date_rng)) |
| 1769 | + except ValueError: |
| 1770 | + # If range creation fails - ignore that search parameter |
| 1771 | + pass |
| 1772 | + elif isinstance(propclass, hyperdb.Interval): |
| 1773 | + try: |
| 1774 | + intv_rng = date.Range(v, date.Interval) |
| 1775 | + l.append((INTERVAL, k, intv_rng)) |
| 1776 | + except ValueError: |
| 1777 | + # If range creation fails - ignore that search parameter |
| 1778 | + pass |
| 1779 | + |
| 1780 | + elif isinstance(propclass, hyperdb.Boolean): |
| 1781 | + if type(v) == type(""): |
1793 | 1782 | v = v.split(',') |
1794 | | - except AttributeError : |
| 1783 | + if type(v) != type([]): |
1795 | 1784 | v = [v] |
1796 | | - l.append((OTHER, k, [float(val) for val in v])) |
| 1785 | + bv = [] |
| 1786 | + for val in v: |
| 1787 | + if type(val) is type(''): |
| 1788 | + bv.append(propclass.from_raw (val)) |
| 1789 | + else: |
| 1790 | + bv.append(val) |
| 1791 | + l.append((OTHER, k, bv)) |
1797 | 1792 |
|
1798 | | - elif isinstance(propclass, hyperdb.Integer): |
1799 | | - if type(v) != type([]): |
1800 | | - try : |
| 1793 | + elif k == 'id': |
| 1794 | + if type(v) != type([]): |
1801 | 1795 | v = v.split(',') |
1802 | | - except AttributeError : |
1803 | | - v = [v] |
1804 | | - l.append((OTHER, k, [int(val) for val in v])) |
| 1796 | + l.append((OTHER, k, [str(int(val)) for val in v])) |
| 1797 | + |
| 1798 | + elif isinstance(propclass, hyperdb.Number): |
| 1799 | + if type(v) != type([]): |
| 1800 | + try : |
| 1801 | + v = v.split(',') |
| 1802 | + except AttributeError : |
| 1803 | + v = [v] |
| 1804 | + l.append((OTHER, k, [float(val) for val in v])) |
| 1805 | + |
| 1806 | + elif isinstance(propclass, hyperdb.Integer): |
| 1807 | + if type(v) != type([]): |
| 1808 | + try : |
| 1809 | + v = v.split(',') |
| 1810 | + except AttributeError : |
| 1811 | + v = [v] |
| 1812 | + l.append((OTHER, k, [int(val) for val in v])) |
1805 | 1813 |
|
1806 | 1814 | filterspec = l |
1807 | 1815 |
|
@@ -1848,8 +1856,12 @@ def _filter(self, search_matches, filterspec, proptree, |
1848 | 1856 | elif t == STRING: |
1849 | 1857 | if nv is None: |
1850 | 1858 | nv = '' |
1851 | | - # RE search |
1852 | | - match = v.search(nv) |
| 1859 | + if is_us(v): |
| 1860 | + # Exact match |
| 1861 | + match = (nv == v) |
| 1862 | + else: |
| 1863 | + # RE search |
| 1864 | + match = v.search(nv) |
1853 | 1865 | elif t == DATE or t == INTERVAL: |
1854 | 1866 | if nv is None: |
1855 | 1867 | match = v is None |
|
0 commit comments