diff --git a/docs/source/paths.rst b/docs/source/paths.rst index bb7e95d..111bb42 100644 --- a/docs/source/paths.rst +++ b/docs/source/paths.rst @@ -159,6 +159,24 @@ Verify if application exists app.debug(app.path.exists_app(app_name)) +Path is file +------------ + +.. code-block:: python + + path = '/home/mau/myfile.ott' + app.msgbox(app.path.is_file(path)) + + +Path is dir +----------- + +.. code-block:: python + + path = '/home/mau' + app.msgbox(app.path.is_dir(path)) + + Make temporary file ------------------- @@ -251,3 +269,47 @@ Open in other path path_dir = app.paths.get_dir(path_tmp) app.debug(path_dir) + +Get path exists file +-------------------- + +Default open in user documents +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + path_file = app.path.get_file() + app.msgbox(path_file) + + +Change init dir +^^^^^^^^^^^^^^^ + +.. code-block:: python + + path = '/home/mau' + path_file = app.path.get_file(path) + app.msgbox(path_file) + + +Add filter or filters +^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + path_file = app.path.get_file(filters='ods') + + # or + + path_file = app.path.get_file(filters='ods,odt') + + +Can select multiple files +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code-block:: python + + path_files = app.path.get_file(multiple=True) + + + diff --git a/source/easymacro.py b/source/easymacro.py index ee4ec08..35e032e 100644 --- a/source/easymacro.py +++ b/source/easymacro.py @@ -1083,6 +1083,28 @@ class Paths(object): result = bool(shutil.which(name_app)) return result + @classmethod + def is_dir(cls, path: str): + """Validate if path is directory + + :param path: Path for validate + :type path: str + :return: True if path is directory, False if not. + :rtype: bool + """ + return Path(path).is_dir() + + @classmethod + def is_file(cls, path: str): + """Validate if path is a file + + :param path: Path for validate + :type path: str + :return: True if path is a file, False if not. + :rtype: bool + """ + return Path(path).is_file() + @classmethod def temp_file(self): """Make temporary file""" @@ -1128,8 +1150,6 @@ class Paths(object): :param init_dir: Initial default path :type init_dir: str - :param filters: Filter for show type files: 'xml' or 'txt,xml' - :type filters: str :return: Selected path :rtype: str """ @@ -1147,12 +1167,16 @@ class Paths(object): @classmethod def get_file(cls, init_dir: str='', filters: str='', multiple: bool=False): - """ - Get path file + """Get path exists file - init_folder: folder default open - filters: 'xml' or 'xml,txt' - multiple: True for multiple selected + :param init_dir: Initial default path + :type init_dir: str + :param filters: Filter for show type files: 'xml' or 'txt,xml' + :type filters: str + :param multiple: If user can selected multiple files + :type multiple: bool + :return: Selected path + :rtype: str """ if not init_dir: init_dir = cls.documents @@ -1164,10 +1188,8 @@ class Paths(object): file_picker.setMultiSelectionMode(multiple) if filters: - filters = [(f.upper(), f'*.{f.lower()}') for f in filters.split(',')] - file_picker.setCurrentFilter(filters[0][0]) - for f in filters: - file_picker.appendFilter(f[0], f[1]) + for f in filters.split(','): + file_picker.appendFilter(f.upper(), f'*.{f.lower()}') path = '' if file_picker.execute(): @@ -1177,6 +1199,125 @@ class Paths(object): path = path[0] return path + @classmethod + def files(cls, path: str, pattern: str='*'): + """Get all files in path + + :param path: Path with files + :type path: str + :param pattern: For filter files, default get all. + :type pattern: str + :return: Files in path + :rtype: list + """ + files = [str(p) for p in Path(path).glob(pattern) if p.is_file()] + return files + + @classmethod + def dirs(cls, path): + """Get directories in path + + :param path: Path to scan + :type path: str + :return: Directories in path + :rtype: list + """ + dirs = [str(p) for p in Path(path).iterdir() if p.is_dir()] + return dirs + + @classmethod + def walk(cls, path, filters=''): + """Get all files in path recursively + + :param path: Path with files + :type path: str + :param filters: For filter files, default get all. + :type filters: str + :return: Files in path + :rtype: list + """ + paths = [] + for folder, _, files in os.walk(path): + if filters: + pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE) + paths += [cls.join(folder, f) for f in files if pattern.search(f)] + else: + paths += [cls.join(folder, f) for f in files] + return paths + + @classmethod + def walk_dirs(cls, path, tree=False): + """Get directories recursively + + :param path: Path to scan + :type path: str + :param tree: get info in a tuple (ID_FOLDER, ID_PARENT, NAME) + :type tree: bool + :return: Directories in path + :rtype: list + """ + folders = [] + if tree: + i = 0 + p = 0 + parents = {path: 0} + for root, dirs, _ in os.walk(path): + for name in dirs: + i += 1 + rn = cls.join(root, name) + if not rn in parents: + parents[rn] = i + folders.append((i, parents[root], name)) + else: + for root, dirs, _ in os.walk(path): + folders += [cls.join(root, name) for name in dirs] + return folders + + @classmethod + def ext(cls, id_ext: str): + """Get path extension install from id + + :param id_ext: ID extension + :type id_ext: str + :return: Path extension + :rtype: str + """ + pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider') + path = Paths.to_system(pip.getPackageLocation(id_ext)) + return path + + @classmethod + def replace_ext(cls, path: str, new_ext: str): + """Replace extension in file path + + :param path: Path to file + :type path: str + :param new_ext: New extension + :type new_ext: str + :return: Path with new extension + :rtype: str + """ + p = Paths(path) + name = f'{p.name}.{new_ext}' + path = cls.join(p.path, name) + return path + + @classmethod + def open(cls, path: str): + """Open any file with default program in systema + + :param path: Path to file + :type path: str + :return: PID file, only Linux + :rtype: int + """ + pid = 0 + if IS_WIN: + os.startfile(path) + else: + pid = subprocess.Popen(['xdg-open', path]).pid + return pid + # ~ Save/read data @classmethod @@ -1265,6 +1406,118 @@ class Paths(object): data = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True) return cls.save(path, data) + @classmethod + def from_csv(cls, path: str, args: dict={}) -> tuple: + """Read CSV + + :param path: Path to file csv + :type path: str + :param args: Any argument support for Python library + :type args: dict + :return: Data csv like tuple + :rtype: tuple + + `See CSV Reader `_ + """ + with open(path) as f: + rows = tuple(csv.reader(f, **args)) + return rows + + @classmethod + def to_csv(cls, path: str, data: Any, args: dict={}): + """Write CSV + + :param path: Path to file write csv + :type path: str + :param data: Data to write + :type data: Iterable + :param args: Any argument support for Python library + :type args: dict + + `See CSV Writer `_ + """ + with open(path, 'w') as f: + writer = csv.writer(f, **args) + writer.writerows(data) + return + + @classmethod + def zip(cls, source: Union[str, tuple, list], target='') -> str: + path_zip = target + if not isinstance(source, (tuple, list)): + path, _, name, _ = _P(source).info + start = len(path) + 1 + if not target: + path_zip = f'{path}/{name}.zip' + + if isinstance(source, (tuple, list)): + files = [(f, f[len(_P(f).path)+1:]) for f in source] + elif _P.is_file(source): + files = ((source, source[start:]),) + else: + files = [(f, f[start:]) for f in _P.walk(source)] + + compression = zipfile.ZIP_DEFLATED + with zipfile.ZipFile(path_zip, 'w', compression=compression) as z: + for f in files: + z.write(f[0], f[1]) + return path_zip + + @classmethod + def unzip(cls, source, target='', members=None, pwd=None): + path = target + if not target: + path = _P(source).path + with zipfile.ZipFile(source) as z: + if not pwd is None: + pwd = pwd.encode() + if isinstance(members, str): + members = (members,) + z.extractall(path, members=members, pwd=pwd) + return + + @classmethod + def zip_content(cls, path: str): + with zipfile.ZipFile(path) as z: + names = z.namelist() + return names + + @classmethod + def merge_zip(cls, target, zips): + try: + with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t: + for path in zips: + with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s: + for name in s.namelist(): + t.writestr(name, s.open(name).read()) + except Exception as e: + error(e) + return False + + return True + + @classmethod + def kill(cls, path: str): + """Delete path + + :param path: Path to file or directory + :type path: str + :return: True if delete correctly + :rtype: bool + """ + p = Path(path) + try: + if p.is_file(): + p.unlink() + elif p.is_dir(): + shutil.rmtree(path) + result = True + except OSError as e: + log.error(e) + result = False + + return result + class Config(object): """Class for set and get configurations