import datetime import locale import math import re import pytz from core.decorators import instance from core.dict_object import DictObject from core.text import Text @instance() class Util: budatime_full_regex = re.compile("^([0-9]+[a-z]+)+$", re.IGNORECASE) budatime_unit_regex = re.compile("([0-9]+)([a-z]+)", re.IGNORECASE) def __init__(self): # needed for self.format_number() to work properly locale.setlocale(locale.LC_NUMERIC, '') self.abilities = [ "Agility", "Intelligence", "Psychic", "Stamina", "Strength", "Sense" ] self.time_units = [ { "units": ["yr", "years", "year", "y"], "conversion_factor": 31536000 }, { "units": ["month", "months", "mo"], "conversion_factor": 2592000 }, { "units": ["week", "weeks", "w"], "conversion_factor": 604800 }, { "units": ["day", "days", "d"], "conversion_factor": 86400 }, { "units": ["hr", "hours", "hour", "hrs", "h"], "conversion_factor": 3600 }, { "units": ["min", "mins", "m"], "conversion_factor": 60 }, { "units": ["sec", "secs", "s"], "conversion_factor": 1 } ] def inject(self, registry): self.text: Text = registry.get_instance("text") def get_handler_name(self, handler): return handler.__module__ + "." + handler.__qualname__ def get_module_name(self, handler): handler_name = self.get_handler_name(handler) parts = handler_name.split(".") return parts[1] + "." + parts[2] def parse_time(self, budatime, default=0): unixtime = 0 if not self.budatime_full_regex.search(budatime): return default matches = self.budatime_unit_regex.finditer(budatime) for match in matches: for time_unit in self.time_units: if match.group(2).lower() in time_unit["units"]: unixtime += int(match.group(1)) * time_unit["conversion_factor"] continue return unixtime def time_to_readable(self, unixtime, min_unit="sec", max_unit="day", max_levels=2): if unixtime == 0: return "0 secs" # handle negative as positive, and add negative sign at the end is_negative = False if unixtime < 0: is_negative = True unixtime *= -1 found_max_unit = False time_shift = "" levels = 0 for time_unit in self.time_units: unit = time_unit["units"][0] if max_unit in time_unit["units"]: found_max_unit = True # continue to skip until we have found the max unit if not found_max_unit: continue unit_value = math.floor(unixtime / time_unit["conversion_factor"]) if unit_value == 0: # do not show units where unit_value is 0 pass elif unit_value == 1: # show singular where unit_value is 1 time_shift += str(unit_value) + " " + unit + " " else: # show plural where unit_value is greater than 1 time_shift += str(unit_value) + " " + unit + "s " unixtime %= time_unit["conversion_factor"] # record level after the first a unit has a length if levels or unit_value >= 1: levels += 1 if levels == max_levels: break # if we have reached the min unit, then break, unless we have no output, in which case we continue if time_shift and min_unit in time_unit["units"]: break return ("-" if is_negative else "") + time_shift.strip() def get_ability(self, ability_str): ability_str = ability_str.capitalize() for ability in self.abilities: if ability.startswith(ability_str): return ability return None def get_all_abilities(self): return self.abilities.copy() def get_title_level(self, level): if level < 5: return 0 elif level < 15: return 1 elif level < 50: return 2 elif level < 100: return 3 elif level < 150: return 4 elif level < 190: return 5 elif level < 205: return 6 else: return 7 def get_level_range_tl(self, tl): if tl == 0: return 0, 4 elif tl == 1: return 5, 14 elif tl == 2: return 15, 49 elif tl == 3: return 50, 99 elif tl == 4: return 100, 149 elif tl == 5: return 150, 189 elif tl == 6: return 190, 204 elif tl == 7: return 205, 300 else: return 0, 0 def format_number(self, number): return locale.format_string("%.*f", (0, number), grouping=True) def get_profession(self, search, long=True): search = search.lower() if search in ["adv", "advy", "adventurer"]: return "Adventurer" if long else "Adv" elif search in ["agent"]: return "Agent" if long else "Agent" elif search in ["crat", "bureaucrat"]: return "Bureaucrat" if long else "Crat" elif search in ["doc", "doctor"]: return "Doctor" if long else "Doc" elif search in ["enf", "enfo", "enforcer"]: return "Enforcer" if long else "Enf" elif search in ["eng", "engi", "engy", "engineer"]: return "Engineer" if long else "Eng" elif search in ["fix", "fixer"]: return "Fixer" if long else "Fix" elif search in ["keep", "keeper"]: return "Keeper" if long else "Keep" elif search in ["ma", "martial", "martialartist", "martial artist"]: return "Martial Artist" if long else "MA" elif search in ["mp", "meta", "metaphysicist", "meta-physicist"]: return "Meta-Physicist" if long else "MP" elif search in ["nt", "nano", "nanotechnician", "nano-technician"]: return "Nano-Technician" if long else "NT" elif search in ["sha", "shade"]: return "Shade" if long else "Shade" elif search in ["sol", "sold", "soldier"]: return "Soldier" if long else "Sold" elif search in ["tra", "trad", "trader"]: return "Trader" if long else "Trader" elif search in ["gen", "general"]: return "General" if long else "UKN" else: return None def get_prof_icon(self, search, large=False): large = "_LARGE" if large else "" search = self.get_profession(search) if search == "Adventurer": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_6", "tdb") elif search == "Agent": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_5", "tdb") elif search == "Bureaucrat": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_8", "tdb") elif search == "Doctor": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_10", "tdb") elif search == "Enforcer": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_9", "tdb") elif search == "Engineer": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_3", "tdb") elif search == "Fixer": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_4", "tdb") elif search == "Keeper": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_14", "tdb") elif search == "Martial Artist": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_2", "tdb") elif search == "Meta-Physicist": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_12", "tdb") elif search == "Nano-Technician": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_11", "tdb") elif search == "Shade": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_15", "tdb") elif search == "Soldier": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_1", "tdb") elif search == "Trader": return self.text.make_image(f"id:GFX_GUI_ICON_PROFESSION{large}_7", "tdb") else: return "" def get_all_professions(self): return ["Adventurer", "Agent", "Bureaucrat", "Doctor", "Enforcer", "Engineer", "Fixer", "Keeper", "Martial Artist", "Meta-Physicist", "Nano-Technician", "Shade", "Soldier", "Trader"] def format_date(self, timestamp): value = datetime.datetime.fromtimestamp(timestamp, tz=pytz.UTC) return value.strftime('%Y-%m-%d') def format_time(self, timestamp): value = datetime.datetime.fromtimestamp(timestamp, tz=pytz.UTC) return value.strftime('%H:%M:%S') def format_datetime(self, timestamp): value = datetime.datetime.fromtimestamp(timestamp, tz=pytz.UTC) return value.strftime('%Y-%m-%d %H:%M:%S %Z') def interpolate_value(self, interpolated_ql, interpolation_ranges, precision=0): min_val = None max_val = None for ql, v in interpolation_ranges.items(): # required to avoid division by zero if ql == interpolated_ql: return v if interpolated_ql >= ql and (not min_val or ql > min_val.ql): min_val = DictObject({"ql": ql, "val": v}) if interpolated_ql <= ql and (not max_val or ql < max_val.ql): max_val = DictObject({"ql": ql, "val": v}) if not min_val or not max_val: return None return round((max_val.val - min_val.val) / (max_val.ql - min_val.ql) * (interpolated_ql - min_val.ql) + min_val.val, precision) # noinspection PyMethodMayBeStatic def get_offset_limit(self, page_size, page_number): return (page_number - 1) * page_size, page_size