Statistics
| Branch: | Tag: | Revision:

root / snf-django-lib / snf_django / management / utils.py @ 4267cb32

History | View | Annotate | Download (8.3 kB)

1 91c788ec Christos Stavrakakis
# Copyright 2012 - 2014 GRNET S.A. All rights reserved.
2 225cea18 Christos Stavrakakis
#
3 225cea18 Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 225cea18 Christos Stavrakakis
# without modification, are permitted provided that the following
5 225cea18 Christos Stavrakakis
# conditions are met:
6 225cea18 Christos Stavrakakis
#
7 225cea18 Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 225cea18 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 225cea18 Christos Stavrakakis
#      disclaimer.
10 225cea18 Christos Stavrakakis
#
11 225cea18 Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 225cea18 Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 225cea18 Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 225cea18 Christos Stavrakakis
#      provided with the distribution.
15 225cea18 Christos Stavrakakis
#
16 225cea18 Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 225cea18 Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 225cea18 Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 225cea18 Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 225cea18 Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 225cea18 Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 225cea18 Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 225cea18 Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 225cea18 Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 225cea18 Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 225cea18 Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 225cea18 Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 225cea18 Christos Stavrakakis
#
29 225cea18 Christos Stavrakakis
# The views and conclusions contained in the software and
30 225cea18 Christos Stavrakakis
# documentation are those of the authors and should not be
31 225cea18 Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 225cea18 Christos Stavrakakis
# or implied, of GRNET S.A.
33 225cea18 Christos Stavrakakis
34 b0e7f310 Christos Stavrakakis
import json
35 f105d79d Christos Stavrakakis
import csv
36 48233747 Christos Stavrakakis
import operator
37 91c788ec Christos Stavrakakis
import locale
38 91c788ec Christos Stavrakakis
import unicodedata
39 91c788ec Christos Stavrakakis
40 225cea18 Christos Stavrakakis
from datetime import datetime
41 225cea18 Christos Stavrakakis
from django.utils.timesince import timesince, timeuntil
42 48233747 Christos Stavrakakis
from django.db.models.query import QuerySet
43 91c788ec Christos Stavrakakis
from django.utils.encoding import smart_unicode, smart_str
44 91c788ec Christos Stavrakakis
45 91c788ec Christos Stavrakakis
46 91c788ec Christos Stavrakakis
def smart_locale_unicode(s, **kwargs):
47 91c788ec Christos Stavrakakis
    """Wrapper around 'smart_unicode' using user's preferred encoding."""
48 91c788ec Christos Stavrakakis
    encoding = locale.getpreferredencoding()
49 91c788ec Christos Stavrakakis
    return smart_unicode(s, encoding=encoding, **kwargs)
50 91c788ec Christos Stavrakakis
51 91c788ec Christos Stavrakakis
52 91c788ec Christos Stavrakakis
def smart_locale_str(s, errors='replace', **kwargs):
53 91c788ec Christos Stavrakakis
    """Wrapper around 'smart_str' using user's preferred encoding."""
54 91c788ec Christos Stavrakakis
    encoding = locale.getpreferredencoding()
55 91c788ec Christos Stavrakakis
    return smart_str(s, encoding=encoding, errors=errors, **kwargs)
56 91c788ec Christos Stavrakakis
57 91c788ec Christos Stavrakakis
58 91c788ec Christos Stavrakakis
def safe_string(s):
59 91c788ec Christos Stavrakakis
    """Escape control characters from unicode and string objects."""
60 91c788ec Christos Stavrakakis
    if not isinstance(s, basestring):
61 91c788ec Christos Stavrakakis
        return s
62 91c788ec Christos Stavrakakis
    if isinstance(s, unicode):
63 91c788ec Christos Stavrakakis
        return "".join(ch.encode("unicode_escape")
64 91c788ec Christos Stavrakakis
                       if unicodedata.category(ch)[0] == "C" else
65 91c788ec Christos Stavrakakis
                       ch for ch in s)
66 91c788ec Christos Stavrakakis
    return s.encode("string-escape")
67 225cea18 Christos Stavrakakis
68 225cea18 Christos Stavrakakis
69 225cea18 Christos Stavrakakis
def parse_bool(value, strict=True):
70 225cea18 Christos Stavrakakis
    """Convert a string to boolen value.
71 225cea18 Christos Stavrakakis

72 ec5ebdf5 Giorgos Korfiatis
    If strict is True, then ValueError will be raised, if the string can not be
73 225cea18 Christos Stavrakakis
    converted to boolean. Otherwise the string will be returned as is.
74 225cea18 Christos Stavrakakis

75 225cea18 Christos Stavrakakis
    """
76 225cea18 Christos Stavrakakis
    if value.lower() in ("yes", "true", "t", "1"):
77 225cea18 Christos Stavrakakis
        return True
78 225cea18 Christos Stavrakakis
    if value.lower() in ("no", "false", "f", "0"):
79 225cea18 Christos Stavrakakis
        return False
80 225cea18 Christos Stavrakakis
81 225cea18 Christos Stavrakakis
    if strict:
82 ec5ebdf5 Giorgos Korfiatis
        raise ValueError("Cannot convert '%s' to boolean value" % value)
83 225cea18 Christos Stavrakakis
    else:
84 225cea18 Christos Stavrakakis
        return value
85 225cea18 Christos Stavrakakis
86 225cea18 Christos Stavrakakis
87 225cea18 Christos Stavrakakis
def format_bool(b):
88 225cea18 Christos Stavrakakis
    """Convert a boolean value to YES or NO."""
89 225cea18 Christos Stavrakakis
    return "YES" if b else "NO"
90 225cea18 Christos Stavrakakis
91 225cea18 Christos Stavrakakis
92 225cea18 Christos Stavrakakis
def format_date(d):
93 225cea18 Christos Stavrakakis
    if not d:
94 225cea18 Christos Stavrakakis
        return ""
95 225cea18 Christos Stavrakakis
96 225cea18 Christos Stavrakakis
    if d < datetime.now():
97 225cea18 Christos Stavrakakis
        return timesince(d) + " ago"
98 225cea18 Christos Stavrakakis
    else:
99 225cea18 Christos Stavrakakis
        return "in " + timeuntil(d)
100 225cea18 Christos Stavrakakis
101 225cea18 Christos Stavrakakis
102 48233747 Christos Stavrakakis
def filter_results(results, filters):
103 48233747 Christos Stavrakakis
    if isinstance(results, QuerySet):
104 48233747 Christos Stavrakakis
        return filter_queryset_results(results, filters)
105 48233747 Christos Stavrakakis
    elif isinstance(results, list):
106 48233747 Christos Stavrakakis
        return filter_object_results(results, filters)
107 48233747 Christos Stavrakakis
    else:
108 48233747 Christos Stavrakakis
        raise ValueError("Invalid type for results argument: %s", results)
109 48233747 Christos Stavrakakis
110 48233747 Christos Stavrakakis
111 48233747 Christos Stavrakakis
def parse_queryset_filters(filters):
112 225cea18 Christos Stavrakakis
    """Parse a string into lookup parameters for QuerySet.filter(**kwargs).
113 225cea18 Christos Stavrakakis

114 225cea18 Christos Stavrakakis
    This functions converts a string of comma-separated key 'cond' val triples
115 225cea18 Christos Stavrakakis
    to two dictionaries, containing lookup parameters to be used for filter
116 225cea18 Christos Stavrakakis
    and exclude functions of QuerySet.
117 225cea18 Christos Stavrakakis

118 225cea18 Christos Stavrakakis
    e.g. filter_by="foo>=2, baz!=4" -> ({"foo__gte": "2"}, {"baz": "4"})
119 225cea18 Christos Stavrakakis

120 225cea18 Christos Stavrakakis
    """
121 48233747 Christos Stavrakakis
    OP_MAP = [
122 48233747 Christos Stavrakakis
        (">=", "__gte"),
123 48233747 Christos Stavrakakis
        ("=>", "__gte"),
124 48233747 Christos Stavrakakis
        (">",  "__gt"),
125 48233747 Christos Stavrakakis
        ("<=", "__lte"),
126 48233747 Christos Stavrakakis
        ("=<", "__lte"),
127 48233747 Christos Stavrakakis
        ("<", "__lt"),
128 48233747 Christos Stavrakakis
        ("=", ""),
129 48233747 Christos Stavrakakis
        ]
130 225cea18 Christos Stavrakakis
131 225cea18 Christos Stavrakakis
    filter_dict = {}
132 225cea18 Christos Stavrakakis
    exclude_dict = {}
133 48233747 Christos Stavrakakis
    for filter_str in filters.split(","):
134 48233747 Christos Stavrakakis
        if "!=" in filter_str:
135 48233747 Christos Stavrakakis
            key, val = filter_str.split("!=")
136 225cea18 Christos Stavrakakis
            exclude_dict[key] = parse_bool(val, strict=False)
137 48233747 Christos Stavrakakis
            continue
138 78c79ac7 Giorgos Korfiatis
        for op, new_op in OP_MAP:
139 48233747 Christos Stavrakakis
            if op in filter_str:
140 48233747 Christos Stavrakakis
                key, val = filter_str.split(op)
141 225cea18 Christos Stavrakakis
                filter_dict[key + new_op] = parse_bool(val, strict=False)
142 48233747 Christos Stavrakakis
                break
143 48233747 Christos Stavrakakis
        else:
144 48233747 Christos Stavrakakis
            raise ValueError("Unknown filter expression: %s" % filter_str)
145 225cea18 Christos Stavrakakis
146 225cea18 Christos Stavrakakis
    return (filter_dict, exclude_dict)
147 225cea18 Christos Stavrakakis
148 225cea18 Christos Stavrakakis
149 48233747 Christos Stavrakakis
def filter_queryset_results(results, filters):
150 48233747 Christos Stavrakakis
    filter_dict, exclude_dict = parse_queryset_filters(filters)
151 48233747 Christos Stavrakakis
    return results.exclude(**exclude_dict).filter(**filter_dict)
152 48233747 Christos Stavrakakis
153 48233747 Christos Stavrakakis
154 48233747 Christos Stavrakakis
def parse_object_filters(filters):
155 48233747 Christos Stavrakakis
    OP_MAP = [
156 48233747 Christos Stavrakakis
        (">=", operator.ge),
157 48233747 Christos Stavrakakis
        ("=>", operator.ge),
158 48233747 Christos Stavrakakis
        (">",  operator.gt),
159 48233747 Christos Stavrakakis
        ("<=", operator.le),
160 48233747 Christos Stavrakakis
        ("=<", operator.le),
161 48233747 Christos Stavrakakis
        ("<", operator.lt),
162 48233747 Christos Stavrakakis
        ("!=", operator.ne),
163 48233747 Christos Stavrakakis
        ("=", operator.eq),
164 48233747 Christos Stavrakakis
    ]
165 48233747 Christos Stavrakakis
    filters = []
166 48233747 Christos Stavrakakis
    for filter_str in filters.split(","):
167 48233747 Christos Stavrakakis
        for op, op_func in OP_MAP:
168 48233747 Christos Stavrakakis
            if op in filter_str:
169 48233747 Christos Stavrakakis
                key, val = filter_str.split(op)
170 48233747 Christos Stavrakakis
                filters.append((key.strip(), op_func, val.strip()))
171 48233747 Christos Stavrakakis
                break
172 48233747 Christos Stavrakakis
        else:
173 48233747 Christos Stavrakakis
            raise ValueError("Unknown filter expression: %s" % filter_str)
174 48233747 Christos Stavrakakis
    return filters
175 48233747 Christos Stavrakakis
176 48233747 Christos Stavrakakis
177 48233747 Christos Stavrakakis
def filter_object_results(results, filters):
178 48233747 Christos Stavrakakis
    results = list(results)
179 48233747 Christos Stavrakakis
    if results is []:
180 48233747 Christos Stavrakakis
        return results
181 48233747 Christos Stavrakakis
    zero_result = results[0]
182 48233747 Christos Stavrakakis
    for key, op_func, val in parse_object_filters(filters):
183 48233747 Christos Stavrakakis
        val_type = type(getattr(zero_result, key))
184 48233747 Christos Stavrakakis
        results = filter(lambda x: op_func(getattr(x, key), val_type(val)),
185 48233747 Christos Stavrakakis
                         results)
186 48233747 Christos Stavrakakis
    return results
187 48233747 Christos Stavrakakis
188 48233747 Christos Stavrakakis
189 b0e7f310 Christos Stavrakakis
def pprint_table(out, table, headers=None, output_format='pretty',
190 b482fbcc Giorgos Korfiatis
                 separator=None, vertical=False, title=None):
191 225cea18 Christos Stavrakakis
    """Print a pretty, aligned string representation of table.
192 225cea18 Christos Stavrakakis

193 225cea18 Christos Stavrakakis
    Works by finding out the max width of each column and padding to data
194 225cea18 Christos Stavrakakis
    to this value.
195 225cea18 Christos Stavrakakis
    """
196 225cea18 Christos Stavrakakis
197 225cea18 Christos Stavrakakis
    assert(isinstance(table, (list, tuple))), "Invalid table type"
198 225cea18 Christos Stavrakakis
    if headers:
199 225cea18 Christos Stavrakakis
        assert(isinstance(headers, (list, tuple))), "Invalid headers type"
200 225cea18 Christos Stavrakakis
201 b0e7f310 Christos Stavrakakis
    sep = separator if separator else "  "
202 b0e7f310 Christos Stavrakakis
203 f105d79d Christos Stavrakakis
    if headers:
204 01c660b6 Christos Stavrakakis
        headers = map(smart_unicode, headers)
205 8c911970 Christos Stavrakakis
    table = [map(safe_string, row) for row in table]
206 4267cb32 Christos Stavrakakis
    table = [map(smart_unicode, row) for row in table]
207 b0e7f310 Christos Stavrakakis
208 b0e7f310 Christos Stavrakakis
    if output_format == "json":
209 f105d79d Christos Stavrakakis
        assert(headers is not None), "json output format requires headers"
210 b0e7f310 Christos Stavrakakis
        table = [dict(zip(headers, row)) for row in table]
211 b0e7f310 Christos Stavrakakis
        out.write(json.dumps(table, indent=4))
212 b0e7f310 Christos Stavrakakis
        out.write("\n")
213 b0e7f310 Christos Stavrakakis
    elif output_format == "csv":
214 f105d79d Christos Stavrakakis
        cw = csv.writer(out)
215 b0e7f310 Christos Stavrakakis
        if headers:
216 f105d79d Christos Stavrakakis
            table.insert(0, headers)
217 f105d79d Christos Stavrakakis
        cw.writerows(table)
218 b0e7f310 Christos Stavrakakis
    elif output_format == "pretty":
219 28578e52 Christos Stavrakakis
        if vertical:
220 28578e52 Christos Stavrakakis
            assert(len(table) == 1)
221 28578e52 Christos Stavrakakis
            row = table[0]
222 28578e52 Christos Stavrakakis
            max_key = max(map(len, headers))
223 28578e52 Christos Stavrakakis
            for row in table:
224 28578e52 Christos Stavrakakis
                for (k, v) in zip(headers, row):
225 01c660b6 Christos Stavrakakis
                    k = k.ljust(max_key)
226 28578e52 Christos Stavrakakis
                    out.write("%s: %s\n" % (k, v))
227 28578e52 Christos Stavrakakis
        else:
228 28578e52 Christos Stavrakakis
            # Find out the max width of each column
229 28578e52 Christos Stavrakakis
            columns = [headers] + table if headers else table
230 28578e52 Christos Stavrakakis
            widths = [max(map(len, col)) for col in zip(*(columns))]
231 28578e52 Christos Stavrakakis
232 28578e52 Christos Stavrakakis
            t_length = sum(widths) + len(sep) * (len(widths) - 1)
233 b482fbcc Giorgos Korfiatis
            if title is not None:
234 d9f2a9e1 Christos Stavrakakis
                t_length = max(t_length, len(title))
235 a859190b Christos Stavrakakis
                out.write("-" * t_length + "\n")
236 b482fbcc Giorgos Korfiatis
                out.write(title.center(t_length) + "\n")
237 a859190b Christos Stavrakakis
                out.write("-" * t_length + "\n")
238 28578e52 Christos Stavrakakis
            if headers:
239 28578e52 Christos Stavrakakis
                # pretty print the headers
240 01c660b6 Christos Stavrakakis
                line = sep.join(v.rjust(w)
241 28578e52 Christos Stavrakakis
                                for v, w in zip(headers, widths))
242 28578e52 Christos Stavrakakis
                out.write(line + "\n")
243 28578e52 Christos Stavrakakis
                out.write("-" * t_length + "\n")
244 28578e52 Christos Stavrakakis
245 28578e52 Christos Stavrakakis
            # print the rest table
246 28578e52 Christos Stavrakakis
            for row in table:
247 01c660b6 Christos Stavrakakis
                line = sep.join(v.rjust(w) for v, w in zip(row, widths))
248 28578e52 Christos Stavrakakis
                out.write(line + "\n")
249 b0e7f310 Christos Stavrakakis
    else:
250 b0e7f310 Christos Stavrakakis
        raise ValueError("Unknown output format '%s'" % output_format)