diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libervia/backend/tools/common/utils.py	Fri Jun 02 11:49:51 2023 +0200
@@ -0,0 +1,159 @@
+#!/usr/bin/env python3
+
+# Libervia: an XMPP client
+# Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""Misc utils for both backend and frontends"""
+
+import collections.abc
+size_units = {
+    "b": 1,
+    "kb": 1000,
+    "mb": 1000**2,
+    "gb": 1000**3,
+    "tb": 1000**4,
+    "pb": 1000**5,
+    "eb": 1000**6,
+    "zb": 1000**7,
+    "yb": 1000**8,
+    "o": 1,
+    "ko": 1000,
+    "mo": 1000**2,
+    "go": 1000**3,
+    "to": 1000**4,
+    "po": 1000**5,
+    "eo": 1000**6,
+    "zo": 1000**7,
+    "yo": 1000**8,
+    "kib": 1024,
+    "mib": 1024**2,
+    "gib": 1024**3,
+    "tib": 1024**4,
+    "pib": 1024**5,
+    "eib": 1024**6,
+    "zib": 1024**7,
+    "yib": 1024**8,
+    "kio": 1024,
+    "mio": 1024**2,
+    "gio": 1024**3,
+    "tio": 1024**4,
+    "pio": 1024**5,
+    "eio": 1024**6,
+    "zio": 1024**7,
+    "yio": 1024**8,
+}
+
+
+def per_luminance(red, green, blue):
+    """Caculate the perceived luminance of a RGB color
+
+    @param red(int): 0-1 normalized value of red
+    @param green(int): 0-1 normalized value of green
+    @param blue(int): 0-1 normalized value of blue
+    @return (float): 0-1 value of luminance (<0.5 is dark, else it's light)
+    """
+    # cf. https://stackoverflow.com/a/1855903, thanks Gacek
+
+    return 0.299 * red + 0.587 * green + 0.114 * blue
+
+
+def recursive_update(ori: dict, update: dict):
+    """Recursively update a dictionary"""
+    # cf. https://stackoverflow.com/a/3233356, thanks Alex Martelli
+    for k, v in update.items():
+        if isinstance(v, collections.abc.Mapping):
+            ori[k] = recursive_update(ori.get(k, {}), v)
+        else:
+            ori[k] = v
+    return ori
+
+class OrderedSet(collections.abc.MutableSet):
+    """A mutable sequence which doesn't keep duplicates"""
+    # TODO: complete missing set methods
+
+    def __init__(self, values=None):
+        self._dict = {}
+        if values is not None:
+            self.update(values)
+
+    def __len__(self):
+        return len(self._dict)
+
+    def __iter__(self):
+        return iter(self._dict.keys())
+
+    def __contains__(self, item):
+        return item in self._dict
+
+    def add(self, item):
+        self._dict[item] = None
+
+    def discard(self, item):
+        try:
+            del self._dict[item]
+        except KeyError:
+            pass
+
+    def update(self, items):
+        self._dict.update({i: None for i in items})
+
+
+def parse_size(size):
+    """Parse a file size with optional multiple symbole"""
+    try:
+        return int(size)
+    except ValueError:
+        number = []
+        symbol = []
+        try:
+            for c in size:
+                if c == " ":
+                    continue
+                if c.isdigit():
+                    number.append(c)
+                elif c.isalpha():
+                    symbol.append(c)
+                else:
+                    raise ValueError("unexpected char in size: {c!r} (size: {size!r})")
+            number = int("".join(number))
+            symbol = "".join(symbol)
+            if symbol:
+                try:
+                    multiplier = size_units[symbol.lower()]
+                except KeyError:
+                    raise ValueError(
+                        "unknown size multiplier symbole: {symbol!r} (size: {size!r})")
+                else:
+                    return number * multiplier
+            return number
+        except Exception as e:
+            raise ValueError(f"invalid size: {e}")
+
+
+def get_size_multiplier(size, suffix="o"):
+    """Get multiplier of a file size"""
+    size = int(size)
+    #  cf. https://stackoverflow.com/a/1094933 (thanks)
+    for unit in ["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"]:
+        if abs(size) < 1024.0:
+            return size, f"{unit}{suffix}"
+        size /= 1024.0
+    return size, f"Yi{suffix}"
+
+
+def get_human_size(size, suffix="o", sep=" "):
+    size, symbol = get_size_multiplier(size, suffix)
+    return f"{size:.2f}{sep}{symbol}"