Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (6 kB)

1
# Copyright 2012 - 2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
import json
35
import csv
36
import functools
37
from datetime import datetime
38
from django.utils.timesince import timesince, timeuntil
39

    
40
from synnefo.util.text import uenc, udec
41

    
42

    
43
def parse_bool(value, strict=True):
44
    """Convert a string to boolen value.
45

46
    If strict is True, then ValueError will be raised, if the string can not be
47
    converted to boolean. Otherwise the string will be returned as is.
48

49
    """
50
    if value.lower() in ("yes", "true", "t", "1"):
51
        return True
52
    if value.lower() in ("no", "false", "f", "0"):
53
        return False
54

    
55
    if strict:
56
        raise ValueError("Cannot convert '%s' to boolean value" % value)
57
    else:
58
        return value
59

    
60

    
61
def format_bool(b):
62
    """Convert a boolean value to YES or NO."""
63
    return "YES" if b else "NO"
64

    
65

    
66
def format_date(d):
67
    if not d:
68
        return ""
69

    
70
    if d < datetime.now():
71
        return timesince(d) + " ago"
72
    else:
73
        return "in " + timeuntil(d)
74

    
75

    
76
def parse_filters(filter_by):
77
    """Parse a string into lookup parameters for QuerySet.filter(**kwargs).
78

79
    This functions converts a string of comma-separated key 'cond' val triples
80
    to two dictionaries, containing lookup parameters to be used for filter
81
    and exclude functions of QuerySet.
82

83
    e.g. filter_by="foo>=2, baz!=4" -> ({"foo__gte": "2"}, {"baz": "4"})
84

85
    """
86

    
87
    filter_dict = {}
88
    exclude_dict = {}
89

    
90
    filter_list = filter_by.split(",")
91

    
92
    def map_field_type(query):
93
        if "!=" in query:
94
            key, val = query.split("!=")
95
            exclude_dict[key] = parse_bool(val, strict=False)
96
            return
97

    
98
        OP_MAP = {
99
            ">=": "__gte",
100
            "=>": "__gte",
101
            ">":  "__gt",
102
            "<=": "__lte",
103
            "=<": "__lte",
104
            "<":  "__lt",
105
            "=":  "",
106
        }
107

    
108
        for op, new_op in OP_MAP.items():
109
            if op in query:
110
                key, val = query.split(op)
111
                filter_dict[key + new_op] = parse_bool(val, strict=False)
112
                return
113

    
114
    map(lambda x: map_field_type(x), filter_list)
115

    
116
    return (filter_dict, exclude_dict)
117

    
118

    
119
def pprint_table(out, table, headers=None, output_format='pretty',
120
                 separator=None, vertical=False, title=None):
121
    """Print a pretty, aligned string representation of table.
122

123
    Works by finding out the max width of each column and padding to data
124
    to this value.
125
    """
126

    
127
    assert(isinstance(table, (list, tuple))), "Invalid table type"
128
    if headers:
129
        assert(isinstance(headers, (list, tuple))), "Invalid headers type"
130

    
131
    sep = separator if separator else "  "
132

    
133
    def stringnify(obj):
134
        if isinstance(obj, (unicode, str)):
135
            return udec(obj)
136
        else:
137
            return str(obj)
138

    
139
    if headers:
140
        headers = map(stringnify, headers)
141
    table = [map(stringnify, row) for row in table]
142

    
143
    if output_format == "json":
144
        assert(headers is not None), "json output format requires headers"
145
        table = [dict(zip(headers, row)) for row in table]
146
        out.write(json.dumps(table, indent=4))
147
        out.write("\n")
148
    elif output_format == "csv":
149
        cw = csv.writer(out)
150
        if headers:
151
            table.insert(0, headers)
152
        table = map(functools.partial(map, uenc), table)
153
        cw.writerows(table)
154
    elif output_format == "pretty":
155
        if vertical:
156
            assert(len(table) == 1)
157
            row = table[0]
158
            max_key = max(map(len, headers))
159
            for row in table:
160
                for (k, v) in zip(headers, row):
161
                    k = uenc(k.ljust(max_key))
162
                    v = uenc(v)
163
                    out.write("%s: %s\n" % (k, v))
164
        else:
165
            # Find out the max width of each column
166
            columns = [headers] + table if headers else table
167
            widths = [max(map(len, col)) for col in zip(*(columns))]
168

    
169
            t_length = sum(widths) + len(sep) * (len(widths) - 1)
170
            if title is not None:
171
                out.write(title.center(t_length) + "\n")
172
            if headers:
173
                # pretty print the headers
174
                line = sep.join(uenc(v.rjust(w))
175
                                for v, w in zip(headers, widths))
176
                out.write(line + "\n")
177
                out.write("-" * t_length + "\n")
178

    
179
            # print the rest table
180
            for row in table:
181
                line = sep.join(uenc(v.rjust(w)) for v, w in zip(row, widths))
182
                out.write(line + "\n")
183
    else:
184
        raise ValueError("Unknown output format '%s'" % output_format)