Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (7.7 kB)

1 225cea18 Christos Stavrakakis
# Copyright 2012 - 2013 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 f105d79d Christos Stavrakakis
import functools
37 48233747 Christos Stavrakakis
import operator
38 225cea18 Christos Stavrakakis
from datetime import datetime
39 225cea18 Christos Stavrakakis
from django.utils.timesince import timesince, timeuntil
40 48233747 Christos Stavrakakis
from django.db.models.query import QuerySet
41 225cea18 Christos Stavrakakis
42 225cea18 Christos Stavrakakis
from synnefo.util.text import uenc, udec
43 225cea18 Christos Stavrakakis
44 225cea18 Christos Stavrakakis
45 225cea18 Christos Stavrakakis
def parse_bool(value, strict=True):
46 225cea18 Christos Stavrakakis
    """Convert a string to boolen value.
47 225cea18 Christos Stavrakakis

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

51 225cea18 Christos Stavrakakis
    """
52 225cea18 Christos Stavrakakis
    if value.lower() in ("yes", "true", "t", "1"):
53 225cea18 Christos Stavrakakis
        return True
54 225cea18 Christos Stavrakakis
    if value.lower() in ("no", "false", "f", "0"):
55 225cea18 Christos Stavrakakis
        return False
56 225cea18 Christos Stavrakakis
57 225cea18 Christos Stavrakakis
    if strict:
58 ec5ebdf5 Giorgos Korfiatis
        raise ValueError("Cannot convert '%s' to boolean value" % value)
59 225cea18 Christos Stavrakakis
    else:
60 225cea18 Christos Stavrakakis
        return value
61 225cea18 Christos Stavrakakis
62 225cea18 Christos Stavrakakis
63 225cea18 Christos Stavrakakis
def format_bool(b):
64 225cea18 Christos Stavrakakis
    """Convert a boolean value to YES or NO."""
65 225cea18 Christos Stavrakakis
    return "YES" if b else "NO"
66 225cea18 Christos Stavrakakis
67 225cea18 Christos Stavrakakis
68 225cea18 Christos Stavrakakis
def format_date(d):
69 225cea18 Christos Stavrakakis
    if not d:
70 225cea18 Christos Stavrakakis
        return ""
71 225cea18 Christos Stavrakakis
72 225cea18 Christos Stavrakakis
    if d < datetime.now():
73 225cea18 Christos Stavrakakis
        return timesince(d) + " ago"
74 225cea18 Christos Stavrakakis
    else:
75 225cea18 Christos Stavrakakis
        return "in " + timeuntil(d)
76 225cea18 Christos Stavrakakis
77 225cea18 Christos Stavrakakis
78 48233747 Christos Stavrakakis
def filter_results(results, filters):
79 48233747 Christos Stavrakakis
    if isinstance(results, QuerySet):
80 48233747 Christos Stavrakakis
        return filter_queryset_results(results, filters)
81 48233747 Christos Stavrakakis
    elif isinstance(results, list):
82 48233747 Christos Stavrakakis
        return filter_object_results(results, filters)
83 48233747 Christos Stavrakakis
    else:
84 48233747 Christos Stavrakakis
        raise ValueError("Invalid type for results argument: %s", results)
85 48233747 Christos Stavrakakis
86 48233747 Christos Stavrakakis
87 48233747 Christos Stavrakakis
def parse_queryset_filters(filters):
88 225cea18 Christos Stavrakakis
    """Parse a string into lookup parameters for QuerySet.filter(**kwargs).
89 225cea18 Christos Stavrakakis

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

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

96 225cea18 Christos Stavrakakis
    """
97 48233747 Christos Stavrakakis
    OP_MAP = [
98 48233747 Christos Stavrakakis
        (">=", "__gte"),
99 48233747 Christos Stavrakakis
        ("=>", "__gte"),
100 48233747 Christos Stavrakakis
        (">",  "__gt"),
101 48233747 Christos Stavrakakis
        ("<=", "__lte"),
102 48233747 Christos Stavrakakis
        ("=<", "__lte"),
103 48233747 Christos Stavrakakis
        ("<", "__lt"),
104 48233747 Christos Stavrakakis
        ("=", ""),
105 48233747 Christos Stavrakakis
        ]
106 225cea18 Christos Stavrakakis
107 225cea18 Christos Stavrakakis
    filter_dict = {}
108 225cea18 Christos Stavrakakis
    exclude_dict = {}
109 48233747 Christos Stavrakakis
    for filter_str in filters.split(","):
110 48233747 Christos Stavrakakis
        if "!=" in filter_str:
111 48233747 Christos Stavrakakis
            key, val = filter_str.split("!=")
112 225cea18 Christos Stavrakakis
            exclude_dict[key] = parse_bool(val, strict=False)
113 48233747 Christos Stavrakakis
            continue
114 78c79ac7 Giorgos Korfiatis
        for op, new_op in OP_MAP:
115 48233747 Christos Stavrakakis
            if op in filter_str:
116 48233747 Christos Stavrakakis
                key, val = filter_str.split(op)
117 225cea18 Christos Stavrakakis
                filter_dict[key + new_op] = parse_bool(val, strict=False)
118 48233747 Christos Stavrakakis
                break
119 48233747 Christos Stavrakakis
        else:
120 48233747 Christos Stavrakakis
            raise ValueError("Unknown filter expression: %s" % filter_str)
121 225cea18 Christos Stavrakakis
122 225cea18 Christos Stavrakakis
    return (filter_dict, exclude_dict)
123 225cea18 Christos Stavrakakis
124 225cea18 Christos Stavrakakis
125 48233747 Christos Stavrakakis
def filter_queryset_results(results, filters):
126 48233747 Christos Stavrakakis
    filter_dict, exclude_dict = parse_queryset_filters(filters)
127 48233747 Christos Stavrakakis
    return results.exclude(**exclude_dict).filter(**filter_dict)
128 48233747 Christos Stavrakakis
129 48233747 Christos Stavrakakis
130 48233747 Christos Stavrakakis
def parse_object_filters(filters):
131 48233747 Christos Stavrakakis
    OP_MAP = [
132 48233747 Christos Stavrakakis
        (">=", operator.ge),
133 48233747 Christos Stavrakakis
        ("=>", operator.ge),
134 48233747 Christos Stavrakakis
        (">",  operator.gt),
135 48233747 Christos Stavrakakis
        ("<=", operator.le),
136 48233747 Christos Stavrakakis
        ("=<", operator.le),
137 48233747 Christos Stavrakakis
        ("<", operator.lt),
138 48233747 Christos Stavrakakis
        ("!=", operator.ne),
139 48233747 Christos Stavrakakis
        ("=", operator.eq),
140 48233747 Christos Stavrakakis
    ]
141 48233747 Christos Stavrakakis
    filters = []
142 48233747 Christos Stavrakakis
    for filter_str in filters.split(","):
143 48233747 Christos Stavrakakis
        for op, op_func in OP_MAP:
144 48233747 Christos Stavrakakis
            if op in filter_str:
145 48233747 Christos Stavrakakis
                key, val = filter_str.split(op)
146 48233747 Christos Stavrakakis
                filters.append((key.strip(), op_func, val.strip()))
147 48233747 Christos Stavrakakis
                break
148 48233747 Christos Stavrakakis
        else:
149 48233747 Christos Stavrakakis
            raise ValueError("Unknown filter expression: %s" % filter_str)
150 48233747 Christos Stavrakakis
    return filters
151 48233747 Christos Stavrakakis
152 48233747 Christos Stavrakakis
153 48233747 Christos Stavrakakis
def filter_object_results(results, filters):
154 48233747 Christos Stavrakakis
    results = list(results)
155 48233747 Christos Stavrakakis
    if results is []:
156 48233747 Christos Stavrakakis
        return results
157 48233747 Christos Stavrakakis
    zero_result = results[0]
158 48233747 Christos Stavrakakis
    for key, op_func, val in parse_object_filters(filters):
159 48233747 Christos Stavrakakis
        val_type = type(getattr(zero_result, key))
160 48233747 Christos Stavrakakis
        results = filter(lambda x: op_func(getattr(x, key), val_type(val)),
161 48233747 Christos Stavrakakis
                         results)
162 48233747 Christos Stavrakakis
    return results
163 48233747 Christos Stavrakakis
164 48233747 Christos Stavrakakis
165 b0e7f310 Christos Stavrakakis
def pprint_table(out, table, headers=None, output_format='pretty',
166 b482fbcc Giorgos Korfiatis
                 separator=None, vertical=False, title=None):
167 225cea18 Christos Stavrakakis
    """Print a pretty, aligned string representation of table.
168 225cea18 Christos Stavrakakis

169 225cea18 Christos Stavrakakis
    Works by finding out the max width of each column and padding to data
170 225cea18 Christos Stavrakakis
    to this value.
171 225cea18 Christos Stavrakakis
    """
172 225cea18 Christos Stavrakakis
173 225cea18 Christos Stavrakakis
    assert(isinstance(table, (list, tuple))), "Invalid table type"
174 225cea18 Christos Stavrakakis
    if headers:
175 225cea18 Christos Stavrakakis
        assert(isinstance(headers, (list, tuple))), "Invalid headers type"
176 225cea18 Christos Stavrakakis
177 b0e7f310 Christos Stavrakakis
    sep = separator if separator else "  "
178 b0e7f310 Christos Stavrakakis
179 b0e7f310 Christos Stavrakakis
    def stringnify(obj):
180 225cea18 Christos Stavrakakis
        if isinstance(obj, (unicode, str)):
181 225cea18 Christos Stavrakakis
            return udec(obj)
182 225cea18 Christos Stavrakakis
        else:
183 225cea18 Christos Stavrakakis
            return str(obj)
184 225cea18 Christos Stavrakakis
185 f105d79d Christos Stavrakakis
    if headers:
186 f105d79d Christos Stavrakakis
        headers = map(stringnify, headers)
187 b0e7f310 Christos Stavrakakis
    table = [map(stringnify, row) for row in table]
188 b0e7f310 Christos Stavrakakis
189 b0e7f310 Christos Stavrakakis
    if output_format == "json":
190 f105d79d Christos Stavrakakis
        assert(headers is not None), "json output format requires headers"
191 b0e7f310 Christos Stavrakakis
        table = [dict(zip(headers, row)) for row in table]
192 b0e7f310 Christos Stavrakakis
        out.write(json.dumps(table, indent=4))
193 b0e7f310 Christos Stavrakakis
        out.write("\n")
194 b0e7f310 Christos Stavrakakis
    elif output_format == "csv":
195 f105d79d Christos Stavrakakis
        cw = csv.writer(out)
196 b0e7f310 Christos Stavrakakis
        if headers:
197 f105d79d Christos Stavrakakis
            table.insert(0, headers)
198 f105d79d Christos Stavrakakis
        table = map(functools.partial(map, uenc), table)
199 f105d79d Christos Stavrakakis
        cw.writerows(table)
200 b0e7f310 Christos Stavrakakis
    elif output_format == "pretty":
201 28578e52 Christos Stavrakakis
        if vertical:
202 28578e52 Christos Stavrakakis
            assert(len(table) == 1)
203 28578e52 Christos Stavrakakis
            row = table[0]
204 28578e52 Christos Stavrakakis
            max_key = max(map(len, headers))
205 28578e52 Christos Stavrakakis
            for row in table:
206 28578e52 Christos Stavrakakis
                for (k, v) in zip(headers, row):
207 28578e52 Christos Stavrakakis
                    k = uenc(k.ljust(max_key))
208 4571b863 Giorgos Korfiatis
                    v = uenc(v)
209 28578e52 Christos Stavrakakis
                    out.write("%s: %s\n" % (k, v))
210 28578e52 Christos Stavrakakis
        else:
211 28578e52 Christos Stavrakakis
            # Find out the max width of each column
212 28578e52 Christos Stavrakakis
            columns = [headers] + table if headers else table
213 28578e52 Christos Stavrakakis
            widths = [max(map(len, col)) for col in zip(*(columns))]
214 28578e52 Christos Stavrakakis
215 28578e52 Christos Stavrakakis
            t_length = sum(widths) + len(sep) * (len(widths) - 1)
216 b482fbcc Giorgos Korfiatis
            if title is not None:
217 d9f2a9e1 Christos Stavrakakis
                t_length = max(t_length, len(title))
218 a859190b Christos Stavrakakis
                out.write("-" * t_length + "\n")
219 b482fbcc Giorgos Korfiatis
                out.write(title.center(t_length) + "\n")
220 a859190b Christos Stavrakakis
                out.write("-" * t_length + "\n")
221 28578e52 Christos Stavrakakis
            if headers:
222 28578e52 Christos Stavrakakis
                # pretty print the headers
223 d546637f Christos Stavrakakis
                line = sep.join(uenc(v.rjust(w))
224 28578e52 Christos Stavrakakis
                                for v, w in zip(headers, widths))
225 28578e52 Christos Stavrakakis
                out.write(line + "\n")
226 28578e52 Christos Stavrakakis
                out.write("-" * t_length + "\n")
227 28578e52 Christos Stavrakakis
228 28578e52 Christos Stavrakakis
            # print the rest table
229 28578e52 Christos Stavrakakis
            for row in table:
230 28578e52 Christos Stavrakakis
                line = sep.join(uenc(v.rjust(w)) for v, w in zip(row, widths))
231 28578e52 Christos Stavrakakis
                out.write(line + "\n")
232 b0e7f310 Christos Stavrakakis
    else:
233 b0e7f310 Christos Stavrakakis
        raise ValueError("Unknown output format '%s'" % output_format)