Mercurial > libervia-backend
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}" |