Пример #1
0
    def load(self, *args, **kwargs):
        """Load a the serialized profiles for the current set of monitors"""
        dpg_core.log_info("Loading save profile")
        monitor_set_id = self.get_or_create_monitor_set_id()
        dpg_core.log_debug(f"Monitor set ID: {monitor_set_id}")
        serialized_profiles = self._saved_profiles.get(monitor_set_id)
        if not serialized_profiles:
            dpg_core.log_debug(
                f"No serialized profile for monitor set ID {monitor_set_id}")
            return
        dpg_core.log_debug(
            f"Found serialized profile for monitor set ID {monitor_set_id}")
        self.show_status("Load Successful!")

        for label in self._profiles:
            dpg_core.delete_item(label)
        self._profiles = {}
        self._tab_number = 1

        for serialized_profile in serialized_profiles:
            profile = self.add_tab()
            # Ensure the plot width is accurate
            self.resize_callback(None, None)
            profile.load_dict(serialized_profile)

        self.resize_callback(None, None)
        dpg_core.log_info(
            f"Successfully loaded saved profile {monitor_set_id}")
Пример #2
0
    def get_or_create_monitor_set_id(self):
        """Get or create a monitor set id

        We assign each unique set of monitors their own ID so we can save different profiles to
        each set. A layout on one set of monitors does not perfectly translate to a good layout
        on another set so we give each unique set of monitors an id and it's own saved profile.

        Returns
        -------
        monitor_set_id : str
            UUID string for the monitor set
        """
        monitor_sets = self._saved_profiles.get("monitor_sets", {})
        current_monitor_ids = set(monitor.id for monitor in self._monitors)
        for monitor_set_id, monitor_ids in monitor_sets.items():
            if set(monitor_ids) == current_monitor_ids:
                dpg_core.log_debug(
                    f"Found existing monitor set: {monitor_set_id}")
                break
        else:
            monitor_set_id = str(uuid4())
            monitor_sets[monitor_set_id] = list(current_monitor_ids)
            self._saved_profiles["monitor_sets"] = monitor_sets
            dpg_core.log_info(f"Create new monitor set: {monitor_set_id}")
        return monitor_set_id
def connect_device_usb() -> NoReturn:
    global device, device_is_connected, device_name, device_android_version, device_user
    if not os.path.exists(cfg.adb_key_file_path):
        keygen(cfg.adb_key_file_path)
        log_info(f"[ADB] generated and stored a new adb-RSA-key (was missing)",
                 logger="debuglog")

    with open(cfg.adb_key_file_path) as f:
        priv = f.read()
    with open(cfg.adb_key_file_path + '.pub') as f:
        pub = f.read()
    signer = PythonRSASigner(pub, priv)
    try:
        device = AdbDeviceUsb(
        )  # TODO: there can be more than one phone, determine with "available", "list" or similar
    except UsbDeviceNotFoundError:
        device = None
        log_error(
            f"[ADB] is the device connected and ADB activated on device?",
            logger="debuglog")
    except DevicePathInvalidError:
        device = None
        log_error(
            "[ADB] installation seems incomplete, adb-shell[usb] is missing (or not working as intended) or adb-server is still running on your system",
            logger="debuglog")
    if device is not None:
        device.connect(rsa_keys=[signer], auth_timeout_s=30)
    if not is_device_available():
        return
    device_is_connected = True
    log_info(f"[ADB] connected to USB-Device", logger="debuglog")
    update_device_properties()
Пример #4
0
def add_info_message(message: str):
    """Log message for info.

    Args:
        message (str): message to display
    """
    core.log_info(message, logger='##log_message')
 def __init__(self,
              port,
              baudrate,
              maxlen=100,
              log_window="",
              raw_log_window="") -> None:
     super().__init__()
     self.port = port
     self.baudrate = baudrate
     self.maxlen = maxlen
     self.log_window = log_window
     self.raw_log_window = raw_log_window
     self.data = {
         "plots": {},
         "raw": deque([], maxlen=5000),
         "messages": deque([], maxlen=500),
         "errors": deque([], maxlen=500),
         "warnings": deque([], maxlen=500)
     }
     # self.buff = []
     self.running = True
     log_info(f"Starting serial interface at {port} with baud {baudrate}",
              logger=self.log_window)
     self.ser = serial.Serial(port, baudrate)  # opens the serial port
     log_info(f"serial interface started", logger=self.log_window)
     self.ser.flushInput()  # clears the buffers to start clean
     self.serial_listener_thread = self.serial_listener(
     )  # starts the serial listener
     self.buf = bytearray()
Пример #6
0
    def __init__(self,
                 port,
                 baudrate,
                 maxlen=100,
                 log_window="",
                 raw_log_window="") -> None:
        super().__init__()
        self.port = port
        self.baudrate = baudrate
        self.maxlen = maxlen
        self.log_window = log_window
        self.raw_log_window = raw_log_window
        self.data = {
            "plots": {},
            "raw": deque([], maxlen=5000),
            "messages": deque([], maxlen=500),
            "errors": deque([], maxlen=500),
            "warnings": deque([], maxlen=500)
        }

        self.buff = []
        self.running = True

        log_info(f"Starting serial interface at {port} with baud {baudrate}",
                 logger=self.log_window)
        self.serial_listener_thread = self.serial_listener()

        log_info(f"serial interface started", logger=self.log_window)
Пример #7
0
    def input_callback(self, sender, data):
        """Callback when the input grids are changed"""
        dpg_core.log_info(
            f"Refreshing grid for monitor profile {self.monitor.name} as input changed"
        )
        # First remove each line from the plot
        for xline in self._xlines:
            dpg_core.delete_drag_line(self._plot_id, xline)
        for yline in self._ylines:
            dpg_core.delete_drag_line(self._plot_id, yline)

        # Ensure that the values are greater than or equal to 1
        rows, cols = dpg_core.get_value(self._input_id)
        rows = 1 if rows < 1 else rows
        cols = 1 if cols < 1 else cols
        dpg_core.set_value(self._input_id, [rows, cols])

        # Add horizontal lines to the plot
        ylines = []
        for row in range(1, rows):
            name = f"yline{row}-{self.id}"
            pos = int(self.monitor.work.top +
                      (row / rows) * self.monitor.work.height)
            dpg_core.add_drag_line(
                self._plot_id,
                name,
                y_line=True,
                show_label=False,
                default_value=pos,
                callback=self.line_callback,
            )
            ylines.append(name)

        # Add vertical lines to the plot
        xlines = []
        for col in range(1, cols):
            name = f"xline{col}-{self.id}"
            pos = int(self.monitor.work.left +
                      (col / cols) * self.monitor.work.width)
            dpg_core.add_drag_line(
                self._plot_id,
                name,
                y_line=False,
                show_label=False,
                default_value=pos,
                callback=self.line_callback,
            )
            xlines.append(name)

        self._xlines = xlines
        self._ylines = ylines

        # Reset the labels and application table to reflect the new grids
        self.set_labels()
        self._app_table.clear()
        self._app_table.set_rows(rows * cols)
        dpg_core.log_info(
            f"Refreshed grid for monitor profile {self.monitor.name} as input changed"
        )
def disconnect_device() -> NoReturn:
    global device_is_connected, device, device_packages
    device.close()
    device = None
    device_packages = pd.DataFrame(columns=cfg.package_columns +
                                   cfg.debloat_columns)
    device_is_connected = False
    log_info(f"[ADB] disconnected from Device", logger="debuglog")
def push_device_package_list_backup(_local_file_path: str,
                                    _remote_file_path: str) -> bool:
    global device
    if not os.path.exists(_local_file_path) or not is_device_available():
        return False
    device.push(_local_file_path, _remote_file_path)
    log_info(f"[ADB] pushed file to device ({_remote_file_path})",
             logger="debuglog")
    mode, size, mtime = device.stat(_remote_file_path)
    return size > 0
Пример #10
0
    def load_dict(self, serialized_monitor_profile: dict[str,
                                                         list[float]]) -> None:
        """Set the monitor profile from a serialized dictionary

        Parameters
        ----------
        serialized_monitor_profile : dict[str, list[float]]
            Dictionary to load
        """
        dpg_core.log_info(f"Loading monitor profile {self.id}")
        for xline in self._xlines:
            dpg_core.delete_drag_line(self._plot_id, xline)
        for yline in self._ylines:
            dpg_core.delete_drag_line(self._plot_id, yline)

        dpg_core.log_debug("Setting loaded ylines")
        ylines = []
        for row, value in enumerate(serialized_monitor_profile["ylines"]):
            dpg_core.log_debug("Loading y lines")
            yline = int(value * self.monitor.work.height)
            name = f"yline{row}-{self.id}"
            dpg_core.add_drag_line(
                self._plot_id,
                name,
                y_line=True,
                show_label=False,
                default_value=yline,
                callback=self.line_callback,
            )
            ylines.append(name)

        dpg_core.log_debug("Setting loaded xlines")
        xlines = []
        for col, value in enumerate(serialized_monitor_profile["xlines"]):
            yline = int(value * self.monitor.work.width)
            name = f"xline{col}-{self.id}"
            dpg_core.add_drag_line(
                self._plot_id,
                name,
                y_line=False,
                show_label=False,
                default_value=yline,
                callback=self.line_callback,
            )
            xlines.append(name)

        self._xlines = xlines
        self._ylines = ylines

        self.set_labels()
        rows, cols = (len(self._ylines) + 1), (len(self._xlines) + 1)
        dpg_core.set_value(self._input_id, [rows, cols])
        self._app_table.set_rows(rows * cols)
        dpg_core.log_info(f"Loaded monitor profile {self.id}")
Пример #11
0
def git_update() -> NoReturn:
    uad_path = "/".join(cfg.debloater_list_path.split("/")[0:-1])
    if not os.path.exists(uad_path):
        Repo.clone_from(
            "https://gitlab.com/W1nst0n/universal-android-debloater", uad_path)
        log_info(f"[GIT] cloned the repo 'universal android debloater'",
                 logger="debuglog")
    else:
        repo = Repo(uad_path)
        repo.git.pull()
        log_info(f"[GIT] updated local repo of debloat-scripts",
                 logger="debuglog")
Пример #12
0
 def save(self, *args, **kwargs):
     """Save the current layout to the profiles file for the set of monitors"""
     dpg_core.log_info("Saving configuration")
     serialized_profiles = [
         profile.to_dict() for profile in self._profiles.values()
     ]
     monitor_set_id = self.get_or_create_monitor_set_id()
     self._saved_profiles[monitor_set_id] = serialized_profiles
     with self.PROFILES_PATH.open("w") as stream:
         json.dump(self._saved_profiles, stream)
     dpg_core.log_info(f"Successfully saved configuration {monitor_set_id}")
     self.show_status("Save Successful!")
Пример #13
0
    def load_dict(self, serialized_profile: dict[str, dict[str, list[float]]]):
        """Load a serialized profile

        Parameters
        ----------
        serialized_profile : dict[str, dict[str, list[float]]]
            The serialized profile
        """
        dpg_core.log_info(f"Loading profile {self.id}")
        for monitor_id, serialized_monitor_profile in serialized_profile.items(
        ):
            monitor_profile = self._monitor_profiles[monitor_id]
            monitor_profile.load_dict(serialized_monitor_profile)
        dpg_core.log_info(f"Loaded profile {self.id}")
Пример #14
0
    def to_dict(self) -> dict[str, dict[str, list[float]]]:
        """Serialize profile to a dictionary

        Returns
        -------
        serialized_profile : dict[str, dict[str, list[float]]]
            The serialized profile
        """
        dpg_core.log_info(f"Serializing profile {self.id}")
        serialized_profile = {}
        for monitor_id, monitor_profile in self._monitor_profiles.items():
            serialized_profile[monitor_id] = monitor_profile.to_dict()
        dpg_core.log_info(f"Serialized profile {self.id}")
        return serialized_profile
def pull_file_from_device(_remote_file_path: str,
                          _local_file_path: str) -> int:
    global device
    if not is_device_available():
        return 0
    mode, size, mtime = device.stat(_remote_file_path)
    if size > 0:
        device.pull(_remote_file_path, _local_file_path)
        log_info(
            f"[ADB] pulled file '{_remote_file_path}' from device, size = {size} byte",
            logger="debuglog")
    else:
        log_error(f"[ADB] failed pulling file ({_remote_file_path})",
                  logger="debuglog")
    return size
Пример #16
0
 def snap(self, sender, data):
     """Snap each window to the selected grid"""
     dpg_core.log_info(
         f"Snapping windows in monitor profile {self.monitor.name}")
     for number, windows in self._app_table._grid_mapping.items():
         if number not in self._rectangle_mapping:
             continue
         rect = self._rectangle_mapping[number]
         for name in windows:
             window = AppTable.ACTIVE_WINDOWS[name]
             dpg_core.log_debug(f"Snapping window {window}")
             move_window(window.handle, int(rect.left), int(rect.top),
                         int(rect.width), int(rect.height))
     dpg_core.log_info(
         f"Snapped windows in monitor profile {self.monitor.name}")
Пример #17
0
def start_development_windows(logger: str):
    """
    Starts doc, debug, and logger window
    """
    add_doc_window(name="Core Documentation", x_pos=0, y_pos=800)
    end()

    show_logger()

    # for some reason the default logger doesn't show the first
    # log_info call so this clears that out.
    log_info("Clearing out initial issue with logger", logger=logger)

    add_debug_window(name="Debug", x_pos=0, y_pos=300)
    end()
Пример #18
0
def window_crud_maintenance(sender, data):
    log_info(f'Function: CRUD Maintenance Window, {sender}, {data}')
    if does_item_exist(f'{data}##window'):
        log_info(f'Already exist {data}##window')
        pass
    else:
        if data == 'Key Values':
            table_headers = ['Key', 'Value', 'Comment']
        elif data == 'Plex Shows':
            table_headers = ['Show Name', 'Show Id', 'Cleaned Show Name']
        elif data == 'Plex Episodes':
            table_headers = [
                'Show Name', 'Season', 'Episode', 'Date Watched',
                'TVM Updated', 'TVM Update Status'
            ]
        else:
            table_headers = ['Unknown']
        with window(name=f'{data}##window',
                    width=2130,
                    height=650,
                    x_pos=5,
                    y_pos=45):
            add_input_text(name=f'{data}_input',
                           no_spaces=True,
                           multiline=False,
                           decimal=False,
                           label=data,
                           width=200)
            add_same_line(spacing=10)
            add_button(name=f'Search##{data}',
                       callback=func_crud_search,
                       callback_data=data)
            if data == 'Key Values' or data == 'Plex Shows':
                add_same_line(spacing=10)
                add_button(name=f"Add New##{data}")
            add_same_line(spacing=10)
            add_button(name=f"Edit##{data}")
            if data == 'Key Values':
                add_same_line(spacing=10)
                add_button(name=f"Delete##{data}")
            add_same_line(spacing=10)
            add_button(name=f"Clear##{data}",
                       callback=func_crud_clear,
                       callback_data=f'Table##{data}')
            add_separator(name=f'##{data}SEP1')
            add_table(name=f'Table##{data}', headers=table_headers)
            add_separator(name=f'##{data}SEP1')
def update_package_data() -> NoReturn:
    global device_packages
    package_data1 = device_packages if (
        len(device_packages) > 0) else restore_device_package_list()
    package_data1["via_adb"] = False
    package_data2 = read_package_data()
    package_data3 = pd.concat([package_data1, package_data2],
                              ignore_index=True)
    package_data4 = package_data3.sort_values(
        by="via_adb",
        ascending=False).groupby("package").first().reset_index()
    device_packages = uad_fw.enrich_package_list(package_data4)
    log_info(
        f"[ADB] read package list ("
        f"{len(device_packages)} entries, "
        f"{len(device_packages[device_packages['via_adb'] == False])} via backup / not ADB)",
        logger="debuglog")
Пример #20
0
 def remove_tab(self, *args, **kwargs):
     """Close a tab"""
     # Due to https://github.com/hoffstadt/DearPyGui/issues/429, when a tab is closed, we have to
     # search for the tab to remove the profile itself
     remove = set()
     for label in self._profiles:
         if dpg_core.is_item_shown(label):
             dpg_core.configure_item(label,
                                     closable=dpg_core.get_value(label))
         else:
             dpg_core.delete_item(label)
             remove.add(label)
     self._profiles = {
         label: self._profiles[label]
         for label in self._profiles if label not in remove
     }
     dpg_core.log_info(f"Profiles {remove} successfully closed")
Пример #21
0
def func_crud_search(sender, data):
    db = mariaDB()
    log_info(f'Searching CRUD {sender}, {data}')
    key = get_value(f'{data}_input')
    if data == 'Key Values':
        sql = f"select * from key_values where `key` like '%{key}%' order by `key`"
    elif data == 'Plex Shows':
        sql = f"select * from TVMazeDB.plex_shows where showname like '%{key}%' " \
              f"order by `showid`, 'cleaned_showname'"
    elif data == 'Plex Episodes':
        sql = f"select * from TVMazeDB.plex_episodes where showname like '%{key}%' " \
              f"order by `showname`, 'season', `episode`"
    else:
        sql = f'Should not happen, sql is ""'

    result = db.execute_sql(sqltype='Fetch', sql=sql)
    func_fill_a_table(f'Table##{data}', result)
Пример #22
0
    def set_rows(self, nrows):
        """ "Set the rows in the table

        Each row has two columns. The first column is the grid number. The second column is a list
        of applications to snap to the grid. We use a table to enable multiselect
        """
        dpg_core.log_info(f"Refreshing rows for table {self.id}")
        for row in range(1, nrows + 1):
            name = f"{self._id}_{row}"
            # If the row already exists, we don't need to do anything else
            if dpg_core.does_item_exist(name):
                continue

            with dpg_simple.managed_columns(name,
                                            len(self.HEADER),
                                            parent=self.parent):
                # The first column is the grid number
                dpg_core.add_input_int(
                    f"##{self._id}_{row}_number",
                    default_value=row,
                    readonly=True,
                    step=0,
                    parent=name,
                )

                # The second column is the table. Wrap in a collapsing header so the screen isn't
                # too full the entire time.
                with dpg_simple.collapsing_header(f"##{self._id}_{row}_header",
                                                  parent=name):
                    dpg_core.add_table(
                        f"{self._id}_{row}_table",
                        [""],  # no headers
                        callback=self.selected,
                        parent=f"##{self._id}_{row}_header",
                    )
                    # populate the table with the names of available windows
                    for window_name in sorted(self.ACTIVE_WINDOWS):
                        dpg_core.add_row(f"{self._id}_{row}_table",
                                         [window_name])

            # Separate each row with a line
            dpg_core.add_separator(name=f"{self._id}_{row}_sep", parent=name)

        self._nrows = nrows
        dpg_core.log_info(f"Refreshed rows for table {self.id}")
Пример #23
0
    def add_tab(self, *args, **kwargs):
        """Add a profile tab"""
        dpg_core.log_debug("Adding profile tab...")
        label = f"{self._tab_number}##MainWindow-tab{self._tab_number}"
        with dpg_simple.tab(label,
                            parent="##MainWindow-tabbar",
                            closable=False,
                            no_tooltip=True):
            profile = Profile(label, self._monitors)
            self._profiles[label] = profile
        self._tab_number += 1

        # If we previously only had one tab then we need to make the first tab closable
        if len(self._profiles) == 2:
            label = list(self._profiles)[0]  # get the label of the first tab
            dpg_core.configure_item(label, closable=dpg_core.get_value(label))

        dpg_core.log_info(f"Profile {label} successfully added")
        return profile
    def parse_point(self, s: str):
        t = time.time()
        t_str = time.strftime("%T", time.localtime(t))

        s = s.strip()
        raw_str = f"{t_str}: {s}"
        if (self.raw_log_window != ""):
            log(raw_str, logger=self.raw_log_window)
        self.data["raw"].append(raw_str)
        if s[0] == "#":
            # parse as a number
            a = s.strip("#;")
            a = a.split(":")
            name = a[0]
            val = float(a[1])
            if name in self.data["plots"]:
                if len(self.data["plots"][name]["x"]) >= self.maxlen:
                    self.data["plots"][name]["x"].pop(0)
                    self.data["plots"][name]["t"].pop(0)
                self.data["plots"][name]["x"].append(val)
                self.data["plots"][name]["t"].append(t)
            else:
                self.data["plots"][name] = {
                    "x": (self.maxlen - 1) * [0.] + [val],
                    "t": self.maxlen * [t]
                }

        elif s[0] == "!":
            # parse as error
            error_msg = f"{t_str}: {s.strip('!;')}"
            log_error(error_msg, logger=self.log_window)
            self.data["errors"].append(error_msg)

        elif s[0] == "?":
            # parse as warning
            warning_msg = f"{t_str}: {s.strip('?;')}"
            log_warning(warning_msg, logger=self.log_window)
            self.data["warnings"].append(warning_msg)

        else:
            info_msg = f"{t_str}: {s.strip(';')}"
            log_info(info_msg, logger=self.log_window)
            self.data["messages"].append(info_msg)
def example_two():
    """
    Commands to put into input
    """
    with s.window("Hello World", autosize=True):
        c.add_text("Hello world!")

    c.show_logger()
    c.log_info("Foo")
    c.log_info("Check it out ma - no IDE!")

    with s.window("Canvas", x_pos=0, y_pos=300, autosize=True):
        c.add_drawing("Draw", width=300, height=300)
        c.draw_circle(
            "Draw",
            center=[150, 150],
            radius=50,
            color=[125, 125, 125],
            fill=[125, 125, 200],
        )
Пример #26
0
    def to_dict(self) -> dict[str, list[float]]:
        """Serialize the monitor profile

        Returns
        -------
        serialized_monitor_profile : dict[str, list[float]]
            Serialized monitor profile
        """
        dpg_core.log_info(f"Serializing monitor profile: {self.id}")
        serialized_monitor_profile = {
            "xlines":
            sorted(
                dpg_core.get_value(xline) / self.monitor.work.width
                for xline in self._xlines),
            "ylines":
            sorted(
                dpg_core.get_value(yline) / self.monitor.work.height
                for yline in self._ylines),
        }
        dpg_core.log_info(f"Serialized monitor profile: {self.id}")
        return serialized_monitor_profile
def update_device_properties() -> int:
    global device, device_name, device_android_sdk, device_android_version, device_user
    if not is_device_available():
        return 0
    manufacturer = device.shell("getprop ro.product.manufacturer").strip(
        "\n\r")
    product_name = device.shell("getprop ro.product.device").strip("\n\r")
    device_name = manufacturer + " " + product_name
    device_android_version = device.shell(
        "getprop ro.build.version.release").strip("\n\r")
    device_android_sdk = int(
        device.shell("getprop ro.build.version.sdk").strip("\n\r"))
    log_info(
        f"[ADB] read device properties, '{device_name}', android {device_android_version}, sdk={device_android_sdk}",
        logger="debuglog")
    users = get_device_users()
    if len(users) > 1:
        log_info(
            "[ADB] NOTE - there are several users, will choose first one in list!",
            logger="debuglog")
        log_info(str(users), logger="debuglog")
    device_user = users["index"].iloc[0]
    if device_android_sdk < 26:
        log_error("[ADB] Your android version is old (< 8.0).",
                  logger="debuglog")
        log_error("[ADB] Uninstalled packages can't be restored.",
                  logger="debuglog")
        log_error("[ADB] The GUI won't stop you from doing so.",
                  logger="debuglog")
    return device_android_sdk
Пример #28
0
def debug_get_window_pos(sender, data):
    """
    Debug setup for getting location configs of windows
    """
    log_info(sender, logger=data)
    window_name_to_search = get_value("Window Name##input")
    show_invisible = get_value("Print invisible")
    log_info(window_name_to_search, logger=data)
    log_info(show_invisible, logger=data)
    window_list = list()

    if window_name_to_search:
        log_debug(window_name_to_search, logger=data)
        for window_name in get_windows():
            if window_name_to_search.lower() in window_name.lower():
                window_list.append(window_name)
    else:
        window_list = get_windows()

    for window_name in window_list:
        config = get_item_configuration(window_name)
        if show_invisible is False:
            if config.get("show") is False:
                continue

        x_pos = config.get("x_pos", 0)
        y_pos = config.get("y_pos", 0)
        name = config.get("name")

        if len(name) > 6:
            name = name[:5]
        log_debug(f"{name} : {x_pos}, {y_pos}", logger=data)
def get_device_users() -> pd.DataFrame:
    global device
    user_columns = ["index", "name", "unknown"]
    if not is_device_available():
        return pd.DataFrame(columns=user_columns)
    response = device.shell("pm list users")
    user_list = list([])
    for line in response.splitlines():
        if "UserInfo{" not in line:
            continue
        if "} running" not in line:
            continue
        start = line.find("{") + 1
        end = line.find("}")
        user_list.append(
            pd.DataFrame([line[start:end].split(":")], columns=user_columns))
    log_info(f"[ADB] read user list ({len(user_list)} entries)",
             logger="debuglog")
    if len(user_list) > 0:
        user_df = pd.concat(user_list, ignore_index=True).sort_values(
            by="index", ascending=True).reset_index(drop=True)
    else:
        user_df = pd.DataFrame(columns=user_columns)
    return user_df
def parse_debloat_lists(debug: bool = False) -> NoReturn:
    global debloat_data
    file_items = [
        x for x in os.scandir(cfg.debloater_list_path) if x.is_file()
    ]  # is List[os.DirEntry]
    package_list = list([])
    for file in file_items:
        item_ext = file.name.split(".")[-1]
        if item_ext.find(cfg.debloater_list_extension) < 0:
            continue
        if debug:
            print(f"-> will parse '{file.name}'")
        with open(file, "r", encoding="utf8") as metafile:
            # TODO: encoding had to be specified, because of unusual characters in LG.sh, line 135
            data = metafile.readlines()
            data = [
                date.replace("\t", "").replace("\r", "").replace("\n", "")
                for date in data
            ]
            data_len = len(data)
            for data_index in range(data_len):
                # filter for lines with format: text1 "text2" text3 -> text2 is alphanum with .dot, without spaces or /
                fragments = data[data_index].split("\"")
                if (len(fragments) > 3) and debug:
                    print(
                        f"NOTE: filtered out '{fragments}', because of too many >>\"<< in '{file.name}'"
                    )
                if len(fragments) != 3:
                    continue
                package_name = fragments[1]
                is_safe = fragments[0].find("#") < 0
                if (package_name.find(".") <
                        0) or (package_name.find(" ") >
                               0) or (package_name.find("/") > 0):
                    if debug:
                        print(
                            f"NOTE: filtered out '{package_name}' package -> invalid name-format in '{file.name}'"
                        )
                    continue
                # determine start and end of description
                line_min = max(0, data_index - 6)
                for min_index in range(data_index - 1, line_min, -1):
                    if len(data[min_index]) < 2:
                        line_min = min_index + 1
                        break
                line_max = min(data_len - 1, data_index + 10)
                for max_index in range(data_index + 1, line_max, 1):
                    if len(data[max_index]) < 2:
                        line_max = max_index
                        break
                data_row = [
                    package_name, True, is_safe, file.name, data_index,
                    range(line_min, line_max), data[line_min:line_max]
                ]
                package_list.append(
                    pd.DataFrame([data_row],
                                 columns=[cfg.package_columns[0]] +
                                 cfg.debloat_columns))
    log_info(f"[UAD] parsed debloat lists ({len(package_list)} entries)",
             logger="debuglog")
    packages = pd.concat(package_list,
                         ignore_index=True).sort_values(by="package",
                                                        ascending=True)
    packages.loc[:, "duplicate"] = packages.duplicated(subset=["package"],
                                                       keep=False)
    packages = packages.reset_index(drop=True)
    debloat_data = packages