comparison libervia/backend/tools/common/utils.py @ 4071:4b842c1fb686

refactoring: renamed `sat` package to `libervia.backend`
author Goffi <goffi@goffi.org>
date Fri, 02 Jun 2023 11:49:51 +0200
parents sat/tools/common/utils.py@524856bd7b19
children e11b13418ba6
comparison
equal deleted inserted replaced
4070:d10748475025 4071:4b842c1fb686
1 #!/usr/bin/env python3
2
3 # Libervia: an XMPP client
4 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
5
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU Affero General Public License for more details.
15
16 # You should have received a copy of the GNU Affero General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 """Misc utils for both backend and frontends"""
20
21 import collections.abc
22 size_units = {
23 "b": 1,
24 "kb": 1000,
25 "mb": 1000**2,
26 "gb": 1000**3,
27 "tb": 1000**4,
28 "pb": 1000**5,
29 "eb": 1000**6,
30 "zb": 1000**7,
31 "yb": 1000**8,
32 "o": 1,
33 "ko": 1000,
34 "mo": 1000**2,
35 "go": 1000**3,
36 "to": 1000**4,
37 "po": 1000**5,
38 "eo": 1000**6,
39 "zo": 1000**7,
40 "yo": 1000**8,
41 "kib": 1024,
42 "mib": 1024**2,
43 "gib": 1024**3,
44 "tib": 1024**4,
45 "pib": 1024**5,
46 "eib": 1024**6,
47 "zib": 1024**7,
48 "yib": 1024**8,
49 "kio": 1024,
50 "mio": 1024**2,
51 "gio": 1024**3,
52 "tio": 1024**4,
53 "pio": 1024**5,
54 "eio": 1024**6,
55 "zio": 1024**7,
56 "yio": 1024**8,
57 }
58
59
60 def per_luminance(red, green, blue):
61 """Caculate the perceived luminance of a RGB color
62
63 @param red(int): 0-1 normalized value of red
64 @param green(int): 0-1 normalized value of green
65 @param blue(int): 0-1 normalized value of blue
66 @return (float): 0-1 value of luminance (<0.5 is dark, else it's light)
67 """
68 # cf. https://stackoverflow.com/a/1855903, thanks Gacek
69
70 return 0.299 * red + 0.587 * green + 0.114 * blue
71
72
73 def recursive_update(ori: dict, update: dict):
74 """Recursively update a dictionary"""
75 # cf. https://stackoverflow.com/a/3233356, thanks Alex Martelli
76 for k, v in update.items():
77 if isinstance(v, collections.abc.Mapping):
78 ori[k] = recursive_update(ori.get(k, {}), v)
79 else:
80 ori[k] = v
81 return ori
82
83 class OrderedSet(collections.abc.MutableSet):
84 """A mutable sequence which doesn't keep duplicates"""
85 # TODO: complete missing set methods
86
87 def __init__(self, values=None):
88 self._dict = {}
89 if values is not None:
90 self.update(values)
91
92 def __len__(self):
93 return len(self._dict)
94
95 def __iter__(self):
96 return iter(self._dict.keys())
97
98 def __contains__(self, item):
99 return item in self._dict
100
101 def add(self, item):
102 self._dict[item] = None
103
104 def discard(self, item):
105 try:
106 del self._dict[item]
107 except KeyError:
108 pass
109
110 def update(self, items):
111 self._dict.update({i: None for i in items})
112
113
114 def parse_size(size):
115 """Parse a file size with optional multiple symbole"""
116 try:
117 return int(size)
118 except ValueError:
119 number = []
120 symbol = []
121 try:
122 for c in size:
123 if c == " ":
124 continue
125 if c.isdigit():
126 number.append(c)
127 elif c.isalpha():
128 symbol.append(c)
129 else:
130 raise ValueError("unexpected char in size: {c!r} (size: {size!r})")
131 number = int("".join(number))
132 symbol = "".join(symbol)
133 if symbol:
134 try:
135 multiplier = size_units[symbol.lower()]
136 except KeyError:
137 raise ValueError(
138 "unknown size multiplier symbole: {symbol!r} (size: {size!r})")
139 else:
140 return number * multiplier
141 return number
142 except Exception as e:
143 raise ValueError(f"invalid size: {e}")
144
145
146 def get_size_multiplier(size, suffix="o"):
147 """Get multiplier of a file size"""
148 size = int(size)
149 #  cf. https://stackoverflow.com/a/1094933 (thanks)
150 for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
151 if abs(size) < 1024.0:
152 return size, f"{unit}{suffix}"
153 size /= 1024.0
154 return size, f"Yi{suffix}"
155
156
157 def get_human_size(size, suffix="o", sep=" "):
158 size, symbol = get_size_multiplier(size, suffix)
159 return f"{size:.2f}{sep}{symbol}"