add missing copyright for ascii_graph
[deliverable/lttng-analyses.git] / ascii_graph / __init__.py
CommitLineData
846cf979 1#!/usr/bin/env python
2150109a
JD
2#
3# Copyright (c) 2012 Pierre-Francois Carpentier <carpentier.pf@gmail.com>
4#
5# https://github.com/kakwa/py-ascii-graph/
6#
7# Permission is hereby granted, free of charge, to any person obtaining
8# a copy of this software and associated documentation files (the
9# "Software"), to deal in the Software without restriction, including
10# without limitation the rights to use, copy, modify, merge, publish,
11# distribute, sublicense, and/or sell copies of the Software, and to
12# permit persons to whom the Software is furnished to do so, subject to
13# the following conditions:
14#
15# The above copyright notice and this permission notice shall be
16# included in all copies or substantial portions of the Software.
17#
18# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
846cf979
JD
25
26from __future__ import unicode_literals
27import sys
17818137 28import os
846cf979 29
846cf979 30
8054fc4c
SG
31class Pyasciigraph:
32 def __init__(self, line_length=79, min_graph_length=50,
33 separator_length=2):
846cf979 34 """Constructor of Pyasciigraph
8054fc4c 35
846cf979 36 :param int line_length: the max number of char on a line
8054fc4c 37 if any line cannot be shorter,
846cf979
JD
38 it will go over this limit
39 :param int min_graph_length: the min number of char used by the graph
40 :param int separator_length: the length of field separator
41 """
42 self.line_length = line_length
43 self.separator_length = separator_length
44 self.min_graph_length = min_graph_length
45
46 def _u(self, x):
47 if sys.version < '3':
48 import codecs
49 return codecs.unicode_escape_decode(x)[0]
50 else:
51 return x
52
53 def _get_maximum(self, data):
54 all_max = {}
55 all_max['value_max_length'] = 0
56 all_max['info_max_length'] = 0
57 all_max['max_value'] = 0
58
59 for (info, value) in data:
60 if value > all_max['max_value']:
61 all_max['max_value'] = value
62
63 if len(info) > all_max['info_max_length']:
64 all_max['info_max_length'] = len(info)
8054fc4c 65
846cf979
JD
66 if len(str(value)) > all_max['value_max_length']:
67 all_max['value_max_length'] = len(str(value))
68 return all_max
69
70 def _gen_graph_string(self, value, max_value, graph_length, start_value):
40fbd9cc
JD
71 if max_value == 0:
72 number_of_square = int(value * graph_length)
73 else:
74 number_of_square = int(value * graph_length / max_value)
846cf979
JD
75 number_of_space = int(start_value - number_of_square)
76 return '█' * number_of_square + self._u(' ') * number_of_space
77
17818137
JD
78 def _console_size(self):
79 TERMSIZE = 80
80 return int(os.environ.get('COLUMNS', TERMSIZE)) - 1
81
846cf979
JD
82 def _gen_info_string(self, info, start_info, line_length):
83 number_of_space = (line_length - start_info - len(info))
84 return info + self._u(' ') * number_of_space
85
bc9bd1f5 86 def _gen_value_string(self, value, start_value, start_info, unit):
846cf979 87 number_space = start_info -\
8054fc4c
SG
88 start_value -\
89 len(str(value)) -\
90 self.separator_length
846cf979 91
8054fc4c 92 return ' ' * number_space +\
bc9bd1f5 93 str(value) + str(unit) +\
8054fc4c 94 ' ' * self.separator_length
846cf979
JD
95
96 def _sanitize_string(self, string):
8054fc4c 97 # get the type of a unicode string
846cf979
JD
98 unicode_type = type(self._u('t'))
99 input_type = type(string)
100 if input_type is str:
ddefe9a6 101 if sys.version_info.major < 3: # pragma: no cover
846cf979 102 info = unicode(string)
8054fc4c 103 else:
846cf979
JD
104 info = string
105 elif input_type is unicode_type:
106 info = string
107 elif input_type is int or input_type is float:
ddefe9a6 108 if sys.version_info.major < 3: # pragma: no cover
846cf979
JD
109 info = unicode(string)
110 else:
111 info = str(string)
112 return info
113
114 def _sanitize_data(self, data):
115 ret = []
116 for item in data:
117 ret.append((self._sanitize_string(item[0]), item[1]))
118 return ret
119
bc9bd1f5 120 def graph(self, label, data, sort=0, with_value=True, unit=""):
846cf979 121 """function generating the graph
8054fc4c 122
846cf979
JD
123 :param string label: the label of the graph
124 :param iterable data: the data (list of tuple (info, value))
125 info must be "castable" to a unicode string
126 value must be an int or a float
127 :param int sort: flag sorted
128 0: not sorted (same order as given) (default)
129 1: increasing order
130 2: decreasing order
131 :param boolean with_value: flag printing value
132 True: print the numeric value (default)
133 False: don't print the numeric value
134 :rtype: a list of strings (each lines)
135
136 """
137 result = []
138 san_data = self._sanitize_data(data)
139 san_label = self._sanitize_string(label)
140
141 if sort == 1:
8054fc4c
SG
142 san_data = sorted(san_data, key=lambda value: value[1],
143 reverse=False)
846cf979 144 elif sort == 2:
8054fc4c
SG
145 san_data = sorted(san_data, key=lambda value: value[1],
146 reverse=True)
846cf979
JD
147
148 all_max = self._get_maximum(san_data)
8054fc4c 149
846cf979 150 real_line_length = max(self.line_length, len(label))
8054fc4c 151
846cf979 152 min_line_length = self.min_graph_length +\
8054fc4c
SG
153 2 * self.separator_length +\
154 all_max['value_max_length'] +\
155 all_max['info_max_length']
846cf979
JD
156
157 if min_line_length < real_line_length:
8054fc4c 158 # calcul of where to start info
846cf979 159 start_info = self.line_length -\
8054fc4c
SG
160 all_max['info_max_length']
161 # calcul of where to start value
846cf979 162 start_value = start_info -\
8054fc4c
SG
163 self.separator_length -\
164 all_max['value_max_length']
165 # calcul of where to end graph
846cf979 166 graph_length = start_value -\
8054fc4c 167 self.separator_length
846cf979 168 else:
8054fc4c 169 # calcul of where to start value
846cf979 170 start_value = self.min_graph_length +\
8054fc4c
SG
171 self.separator_length
172 # calcul of where to start info
846cf979 173 start_info = start_value +\
8054fc4c
SG
174 all_max['value_max_length'] +\
175 self.separator_length
176 # calcul of where to end graph
846cf979 177 graph_length = self.min_graph_length
8054fc4c 178 # calcul of the real line length
846cf979
JD
179 real_line_length = min_line_length
180
17818137 181 real_line_length = min(real_line_length, self._console_size())
846cf979 182 result.append(san_label)
8054fc4c 183 result.append(self._u('#') * real_line_length)
846cf979
JD
184
185 for item in san_data:
186 info = item[0]
187 value = item[1]
188
189 graph_string = self._gen_graph_string(
8054fc4c
SG
190 value,
191 all_max['max_value'],
192 graph_length,
193 start_value)
846cf979 194
452b4312
JD
195 if with_value:
196 value_string = self._gen_value_string(
197 value,
198 start_value,
199 start_info, unit)
200 else:
201 value_string = ""
846cf979
JD
202
203 info_string = self._gen_info_string(
8054fc4c
SG
204 info,
205 start_info,
206 real_line_length)
846cf979
JD
207 new_line = graph_string + value_string + info_string
208 result.append(new_line)
209
210 return result
211
212if __name__ == '__main__':
8054fc4c
SG
213 test = [('long_label', 423), ('sl', 1234), ('line3', 531),
214 ('line4', 200), ('line5', 834)]
846cf979 215 graph = Pyasciigraph()
8054fc4c 216 for line in graph.graph('test print', test):
846cf979 217 print(line)
This page took 0.032301 seconds and 5 git commands to generate.