misc_utils.py
Python script, ASCII text executable
1""" 2This module provides some more general utilities useful in various parts 3of the application. 4 5Roundabout - git hosting for everyone <https://roundabout-host.com> 6Copyright (C) 2023-2025 Roundabout developers <root@roundabout-host.com> 7 8This program is free software: you can redistribute it and/or modify 9it under the terms of the GNU Affero General Public License as published by 10the Free Software Foundation, either version 3 of the License, or 11(at your option) any later version. 12 13This program is distributed in the hope that it will be useful, 14but WITHOUT ANY WARRANTY; without even the implied warranty of 15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16GNU Affero General Public License for more details. 17 18You should have received a copy of the GNU Affero General Public License 19along with this program. If not, see <http://www.gnu.org/licenses/>. 20""" 21 22from common import * 23 24__all__ = ["git_command", "only_chars", "get_permission_level", "get_visibility", "get_favourite", "human_size", 25"guess_mime", "convert_to_html", "js_to_bool", "get_commit_identity"] 26 27import subprocess 28import os 29import magic 30 31def only_chars(string, chars): 32chars = set(chars) 33all_chars = set(string) 34return all_chars.issubset(chars) 35 36 37def get_permission_level(logged_in, username, repository): 38from models import User, Repo, RepoAccess 39user = User.query.filter_by(username=logged_in).first() 40repo = Repo.query.filter_by(route=f"/{username}/{repository}").first() 41 42if user and repo: 43permission = RepoAccess.query.filter_by(user=user, repo=repo).first() 44if permission: 45return permission.access_level 46 47return None 48 49 50def get_visibility(username, repository): 51from models import Repo 52repo = Repo.query.filter_by(route=f"/{username}/{repository}").first() 53 54if repo: 55return repo.visibility 56 57return None 58 59 60def get_favourite(logged_in, username, repository): 61from models import RepoFavourite 62relationship = RepoFavourite.query.filter_by(user_username=logged_in, 63repo_route=f"/{username}/{repository}").first() 64return relationship 65 66 67def human_size(value, decimals=2, scale=1024, 68units=("B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB")): 69for unit in units: 70if value < scale: 71break 72value /= scale 73if int(value) == value: 74# do not return decimals if the value is already round 75return int(value), unit 76return round(value * 10 ** decimals) / 10 ** decimals, unit 77 78 79def guess_mime(path): 80if os.path.isdir(path): 81mimetype = "inode/directory" 82elif magic.from_file(path, mime=True): 83mimetype = magic.from_file(path, mime=True) 84else: 85mimetype = "application/octet-stream" 86return mimetype 87 88 89def convert_to_html(path): 90with open(path, "r") as f: 91contents = f.read() 92return contents 93 94 95def js_to_bool(js): 96return js.lower() == "true" if isinstance(js, str) else bool(js) 97 98 99def get_commit_identity(identity, pusher, repo): 100from models import User, RepoAccess 101email = identity.rpartition("<")[2].rpartition(">")[0].strip() 102# If the email is not valid, attribute the commit to the pusher. 103if not email: 104return pusher 105email_users = User.query.filter_by(email=email).all() 106# If no user has the email, attribute the commit to the pusher. 107if not email_users: 108return pusher 109 110# If only one user has the email, attribute the commit to them. 111if len(email_users) == 1: 112return email_users[0] 113 114# If it's ambiguous, attribute the commit to an user with a higher permission level. 115for user in email_users: 116if repo.owner == user: 117return user 118 119for user in email_users: 120relationship = RepoAccess.query.filter_by(user=user, repo=repo).first() 121if relationship.permission_level == 2: 122return user 123 124for user in email_users: 125relationship = RepoAccess.query.filter_by(user=user, repo=repo).first() 126if relationship.permission_level == 1: 127return user 128 129# If no user has a higher permission level, attribute the commit to the pusher :( 130return pusher 131 132