source: watchlistplugin/0.12/tracwatchlist/util.py

Last change on this file was 15264, checked in by Ryan J Ollos, 8 years ago

Remove unnecessary svn:mime-type on py files

svn:mime-type was set to "plain" for many files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id URL Rev Author Date
File size: 9.8 KB
Line 
1# -*- coding: utf-8 -*-
2"""
3= Watchlist Plugin for Trac =
4Plugin Website:  http://trac-hacks.org/wiki/WatchlistPlugin
5Trac website:    http://trac.edgewall.org/
6
7Copyright (c) 2008-2010 by Martin Scharrer <martin@scharrer-online.de>
8All rights reserved.
9
10The i18n support was added by Steffen Hoffmann <hoff.st@web.de>.
11
12This program is free software: you can redistribute it and/or modify
13it under the terms of the GNU General Public License as published by
14the Free Software Foundation, either version 3 of the License, or
15(at your option) any later version.
16
17This program is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20GNU General Public License for more details.
21
22For a copy of the GNU General Public License see
23<http://www.gnu.org/licenses/>.
24
25$Id: util.py 15264 2016-02-11 04:22:34Z rjollos $
26"""
27
28__url__      = ur"$URL: //trac-hacks.org/svn/watchlistplugin/0.12/tracwatchlist/util.py $"[6:-2]
29__author__   = ur"$Author: rjollos $"[9:-2]
30__revision__ = int("0" + ur"$Rev: 15264 $"[6:-2].strip('M'))
31__date__     = ur"$Date: 2016-02-11 04:22:34 +0000 (Thu, 11 Feb 2016) $"[7:-2]
32
33from  trac.core          import  *
34from  genshi.builder     import  tag, Markup
35from  trac.util.datefmt  import  datetime, utc
36
37
38# Try to use babels format_datetime to localise date-times if possible.
39# A fall back to tracs implementation strips the unsupported `locale` argument.
40from  trac.util.datefmt      import  format_datetime as trac_format_datetime
41try:
42    from  babel.dates        import  format_datetime, LC_TIME
43except ImportError:
44    LC_TIME = None
45    def format_datetime(t=None, format='%x %X', tzinfo=None, locale=None):
46        return trac_format_datetime(t, format, tzinfo)
47
48
49def ldml_patterns( ldml_pattern ):
50    """Takes a LDML date/time format pattern and breaks it down into its
51       elements"""
52    last = None
53    num = 1
54    patterns = []
55    verbatim = False
56    verbtext = ''
57    for s in ldml_pattern:
58        if verbatim:
59            if last == "'":
60                if s == "'":
61                    # Inside quote (quote character already added)
62                    last = None
63                    continue
64                else:
65                    # Last character ended verbatim
66                    if verbtext != "'":
67                        verbtext = verbtext[:-1]
68                    patterns.append( [verbtext] )
69                    last = None
70                    verbatim = False
71            else:
72                verbtext += s
73                last = s
74                continue
75        if s == "'":
76            verbatim = True
77            verbtext = ''
78            if not last is None:
79                patterns.append( last * num )
80            num = 1
81            last = None
82        elif s == last:
83            num+=1
84        else:
85            if not last is None:
86                patterns.append( last * num )
87            num = 1
88            last = s
89    # Flush buffers
90    if verbatim:
91        if last == "'" and verbtext:
92            verbtext = verbtext[:-1]
93        patterns.append( [verbtext ] )
94    else:
95        if not last is None:
96            patterns.append( last * num )
97    return patterns
98
99_PATTERN_TRANSLATION = {
100    'h'     : '%l',
101    'hh'    : '%h',
102    'H'     : '%k',
103    'HH'    : '%H',
104    'K'     : '%l', # fuzzy
105    'KK'    : '%h', # fuzzy
106    'k'     : '%k', # fuzzy
107    'kk'    : '%H', # fuzzy
108    'j'     : '?',
109    'jj'    : '?',
110    'a'     : '%p',
111    'm'     : '%i', # fuzzy
112    'mm'    : '%i',
113    's'     : '%S',
114    'ss'    : '%S',
115    'S'     : '?',
116    'A'     : '?',
117    'z'     : '%@',
118    'zz'    : '%@',
119    'zzz'   : '%@',
120    'zzzz'  : '%@', # fuzzy
121    'Z'     : '%+',
122    'ZZ'    : '%+',
123    'ZZZ'   : '%+',
124    'ZZZZ'  : 'GMT%+',
125    'v'     : '%@', # fuzzy
126    'vvvv'  : '%@', # fuzzy
127    'V'     : '%@', # fuzzy
128    'VVVV'  : '%@', # fuzzy
129    'Q'     : '?', # quarter
130    'q'     : '?', # quarter
131    'yyyy'  : '%Y',
132    'yy'    : '%y',
133    'M'     : '%c',
134    'MM'    : '%m',
135    'MMM'   : '%b',
136    'MMMM'  : '%M',
137    'MMMMM' : '?',
138    'L'     : '%c',
139    'LL'    : '%m',
140    'LLL'   : '%b',
141    'LLLL'  : '%M',
142    'LLLLL' : '?',
143    'l'     : '*',
144    'w'     : '?', # week of the year
145    'ww'    : '??',
146    'W'     : '?', # week of the month
147    'd'     : '%e',
148    'dd'    : '%d',
149    'D'     : '?',
150    'DD'    : '??',
151    'DDD'   : '???',
152    'F'     : '%w',
153    'g'     : '?',
154    'E'     : '%a',
155    'EE'    : '%a',
156    'EEE'   : '%a',
157    'EEEE'  : '%W',
158    'EEEEE' : '?',
159    'e'     : '%w',
160    'ee'    : '%w',
161    'eee'   : '%a',
162    'eeee'  : '%W',
163    'eeeee' : '?',
164    'c'     : '%w',
165    'cc'    : '%w',
166    'ccc'   : '%a',
167    'cccc'  : '%W',
168    'ccccc' : '?',
169    'G'     : '%E', # Era abbreviation*
170    'GG'    : '%E', # Era abbreviation*
171    'GGG'   : '%E', # Era abbreviation*
172    'GGGG'  : '%E', # Era abbreviation*
173    'GGGGG' : '%E', # Era abbreviation*
174}
175
176
177
178def convert_LDML_to_MySQL( ldml_pattern ):
179    """Converts from LDML date/time format patterns to the one used by MySQL.
180       That is the same format used by the Any+Time datepicker currently used.
181       See http://www.ama3.com/anytime/#AnyTime.Converter.format
182       and http://unicode.org/reports/tr35/#Date_Format_Patterns .
183       """
184    import re
185    result = ''
186    for pattern in ldml_patterns(ldml_pattern):
187        if isinstance(pattern,list):
188            # Verbatim
189            result += ''.join(pattern).replace('%','%%')
190        else:
191            tp = _PATTERN_TRANSLATION.get(pattern,None)
192            if tp is None: # Replace unknown letters with '?'
193                tp = re.sub(r'[A-Za-z]','?', pattern)
194            result += tp
195    return result
196
197
198try:
199    from  babel.dates        import  get_datetime_format, get_date_format, get_time_format
200    def datetime_format(format='medium', locale=LC_TIME):
201        time_format = unicode(get_time_format(format, locale))
202        date_format = unicode(get_date_format(format, locale))
203        return convert_LDML_to_MySQL( get_datetime_format(format, locale)\
204                .replace('{0}', time_format)\
205                .replace('{1}', date_format) )
206except ImportError:
207    def datetime_format(format='medium', locale=LC_TIME):
208        return u"%Y-%m-%d %H:%i:%s"
209
210try:
211    from  trac.util.datefmt  import  to_utimestamp
212    def current_timestamp():
213        return to_utimestamp( datetime.now(utc) )
214except ImportError:
215    from  trac.util.datefmt  import  to_timestamp
216    def current_timestamp():
217        return to_timestamp( datetime.now(utc) )
218
219
220def moreless(text, length):
221    """Turns `text` into HTML code where everything behind `length` can be uncovered using a ''show more'' link
222       and later covered again with a ''show less'' link."""
223    try:
224        if len(text) <= length:
225            return tag(text)
226    except:
227        return tag(text)
228    return tag(tag.span(text[:length]),tag.a(' [', tag.strong(Markup('&hellip;')), ']', class_="more"),
229        tag.span(text[length:],class_="moretext"),tag.a(' [', tag.strong('-'), ']', class_="less"))
230
231
232def ensure_iter( var ):
233    """Ensures that variable is iterable. If it's not by itself, return it
234       as an element of a tuple"""
235    if getattr(var, '__iter__', False):
236        return var
237    return (var,)
238
239
240def ensure_tuple( var ):
241    """Ensures that variable is a tuple, even if its a scalar"""
242    if getattr(var, '__iter__', False):
243        return tuple(var)
244    return (var,)
245
246
247def ensure_string( var, sep=u',' ):
248    """Ensures that variable is a string"""
249    if getattr(var, '__iter__', False):
250        return unicode(sep.join(var))
251    else:
252        return var
253
254
255def decode_range( str ):
256    """Decodes given string with integer ranges like `a-b,c-d` and yields a list
257       of tuples: [(a,b),(c,d)] in this ranges."""
258    for irange in unicode(str).split(','):
259        irange = irange.strip()
260        try:
261            index = irange.index('-')
262        except:
263            if irange == '*':
264                a, b = 0, None
265            else:
266                a = b = irange
267        else:
268            b = irange[index+1:]
269            a = irange[:index]
270        try:
271            a = int(a)
272        except:
273            a = None
274        try:
275            b = int(b)
276        except:
277            b = None
278        if not (a is None and b is None):
279            yield (a,b)
280
281
282def decode_range_sql( str ):
283    """Decodes given string with ranges like `a-b,c-d` and returns a SQL
284       command fragment to much this ranges."""
285    cmd = []
286    for (a,b) in decode_range( str ):
287        if a is None:
288            if b is None:
289                continue
290            cmd.append( ' ( %%(var)s <= %i ) ' % (b) )
291        elif b is None:
292            cmd.append( ' ( %%(var)s >= %i ) ' % (a) )
293        else:
294            cmd.append( ' ( %%(var)s BETWEEN %i AND %i ) ' % (a,b) )
295    return ' OR '.join(cmd)
296
297
298def convert_to_sql_wildcards( pattern, sql_escape = '|' ):
299    r"""Converts wildcards '*' and '?' to SQL versions '%' and '_'.
300       A state machine is used to allow for using the backslash to
301       escape this characters:
302            t\e\s\t -> test
303            test -> test
304            test* -> test%
305            test\* -> test*
306            test\\* -> test\%
307            test\\\* -> test\*
308            test\\\\* -> test\\%
309            test\\\\\* -> test\\*
310            % -> \%
311            _ -> \_
312            test_test% -> test\_test\%
313            test__test% -> test\_\_test\%
314            test\_test\% -> test\_test\%
315            test\\_test\\% -> test\\_test\\%
316            test\\\_test\\\% -> test\\_test\\%
317            test\\\\_test\\\% -> test\\\_test\\%
318    """
319    pat = ''
320    esc = False
321    for p in pattern:
322        if not p in '*?\\':
323            esc = False
324            if p in '%_':
325                # Escaped for SQL
326                pat += sql_escape
327            pat += p
328        else:
329            if esc:
330                esc = False
331                pat += p
332            else:
333                if p == '\\':
334                    esc = True
335                elif p == '*':
336                    pat += '%'
337                elif p == '?':
338                    pat += '_'
339    return pat
340
341# EOF
Note: See TracBrowser for help on using the repository browser.