Exemplo n.º 1
0
    def delete_files(cls, labbook: LabBook, section: str,
                     relative_paths: List[str]) -> None:
        """Delete file (or directory) from inside lb section.

        The list of paths is deleted in series. Only provide "parent" nodes in the file tree. This is because deletes
        on directories will remove all child objects, so subsequent deletes of individual files will then fail.


        Args:
            labbook(LabBook): Subject LabBook
            section(str): Section name (code, input, output)
            relative_paths(list(str)): a list of relative paths from labbook root to target

        Returns:
            None
        """
        labbook.validate_section(section)

        if not isinstance(relative_paths, list):
            raise ValueError("Must provide list of paths to remove")

        for file_path in relative_paths:
            relative_path = LabBook.make_path_relative(file_path)
            target_path = os.path.join(labbook.root_dir, section,
                                       relative_path)
            if not os.path.exists(target_path):
                raise ValueError(
                    f"Attempted to delete non-existent path at `{target_path}`"
                )
            else:
                labbook.git.remove(target_path, force=True, keep_file=False)
                if os.path.exists(target_path):
                    raise IOError(f"Failed to delete path: {target_path}")
        labbook.sweep_uncommitted_changes(show=True)
Exemplo n.º 2
0
    def put_file(cls,
                 labbook: LabBook,
                 section: str,
                 src_file: str,
                 dst_path: str,
                 txid: Optional[str] = None) -> Dict[str, Any]:
        """Move the file at `src_file` to `dst_dir`. Filename removes
        upload ID if present. This operation does NOT commit or create an
        activity record.

        Args:
            labbook: Subject LabBook
            section: Section name (code, input, output)
            src_file: Full path of file to insert into
            dst_path: Path within section to insert `src_file`
            txid: Optional transaction id

        Returns:
           Full path to inserted file.
        """
        if not os.path.abspath(src_file):
            raise ValueError(f"Source file `{src_file}` not an absolute path")

        if not os.path.isfile(src_file):
            raise ValueError(f"Source file does not exist at `{src_file}`")

        labbook.validate_section(section)
        r = call_subprocess(
            ['git', 'check-ignore',
             os.path.basename(dst_path)],
            cwd=labbook.root_dir,
            check=False)
        if dst_path and r and os.path.basename(dst_path) in r:
            logger.warning(f"File {dst_path} matches gitignore; "
                           f"not put into {str(labbook)}")
            raise FileOperationsException(f"`{dst_path}` matches "
                                          f"ignored pattern")

        mdst_dir = _make_path_relative(dst_path)
        full_dst = os.path.join(labbook.root_dir, section, mdst_dir)
        full_dst = full_dst.replace('..', '')
        full_dst = full_dst.replace('~', '')

        # Force overwrite if file already exists
        if os.path.isfile(os.path.join(full_dst, os.path.basename(src_file))):
            os.remove(os.path.join(full_dst, os.path.basename(src_file)))

        if not os.path.isdir(os.path.dirname(full_dst)):
            os.makedirs(os.path.dirname(full_dst), exist_ok=True)

        fdst = shutil.move(src_file, full_dst)
        relpath = fdst.replace(os.path.join(labbook.root_dir, section), '')
        return cls.get_file_info(labbook, section, relpath)
Exemplo n.º 3
0
    def walkdir(cls,
                labbook: LabBook,
                section: str,
                show_hidden: bool = False) -> List[Dict[str, Any]]:
        """Return a list of all files and directories in a section of the labbook. Never includes the .git or
         .gigantum directory.

        Args:
            labbook: Subject LabBook
            section(str): The labbook section (code, input, output) to walk
            show_hidden(bool): If True, include hidden directories (EXCLUDING .git and .gigantum)

        Returns:
            List[Dict[str, str]]: List of dictionaries containing file and directory metadata
        """
        labbook.validate_section(section)

        keys: List[str] = list()
        # base_dir is the root directory to search, to account for relative paths inside labbook.
        base_dir = os.path.join(labbook.root_dir, section)
        if not os.path.isdir(base_dir):
            raise ValueError(
                f"Labbook walkdir base_dir {base_dir} not an existing directory"
            )

        for root, dirs, files in os.walk(base_dir):
            # Remove directories we ignore so os.walk does not traverse into them during future iterations
            if '.git' in dirs:
                del dirs[dirs.index('.git')]
            if '.gigantum' in dirs:
                del dirs[dirs.index('.gigantum')]

            # For more deterministic responses, sort resulting paths alphabetically.
            # Store directories then files, so pagination loads things in an intuitive order
            dirs.sort()
            keys.extend(
                sorted([
                    os.path.join(root.replace(base_dir, ''), d) for d in dirs
                ]))
            keys.extend(
                sorted([
                    os.path.join(root.replace(base_dir, ''), f) for f in files
                ]))

        # Create stats
        stats: List[Dict[str, Any]] = list()
        for f_p in keys:
            if not show_hidden and any(
                [len(p) and p[0] == '.' for p in f_p.split(os.path.sep)]):
                continue
            stats.append(cls.get_file_info(labbook, section, f_p))

        return stats
Exemplo n.º 4
0
    def listdir(cls,
                labbook: LabBook,
                section: str,
                base_path: Optional[str] = None,
                show_hidden: bool = False) -> List[Dict[str, Any]]:
        """Return a list of all files and directories in a directory. Never includes the .git or
         .gigantum directory.

        Args:
            labbook: Subject labbook
            section(str): the labbook section to start from
            base_path(str): Relative base path, if not listing from labbook's root.
            show_hidden(bool): If True, include hidden directories (EXCLUDING .git and .gigantum)

        Returns:
            List[Dict[str, str]]: List of dictionaries containing file and directory metadata
        """
        labbook.validate_section(section)
        # base_dir is the root directory to search, to account for relative paths inside labbook.
        base_dir = os.path.join(labbook.root_dir, section, base_path or '')
        if not os.path.isdir(base_dir):
            raise ValueError(
                f"Labbook listdir base_dir {base_dir} not an existing directory"
            )

        stats: List[Dict[str, Any]] = list()
        for item in os.listdir(base_dir):
            if item in ['.git', '.gigantum']:
                # Never include .git or .gigantum
                continue

            if not show_hidden and any(
                [len(p) and p[0] == '.' for p in item.split('/')]):
                continue

            # Create tuple (isDir, key)
            stats.append(
                cls.get_file_info(labbook, section,
                                  os.path.join(base_path or "", item)))

        # For more deterministic responses, sort resulting paths alphabetically.
        return sorted(stats, key=lambda a: a['key'])
Exemplo n.º 5
0
    def move_file(cls, labbook: LabBook, section: str, src_rel_path: str, dst_rel_path: str) \
            -> List[Dict[str, Any]]:
        """Move a file or directory within a labbook, but not outside of it. Wraps
        underlying "mv" call.

        Args:
            labbook: Subject LabBook
            section(str): Section name (code, input, output)
            src_rel_path(str): Source file or directory
            dst_rel_path(str): Target file name and/or directory
        """

        # Start with Validations
        labbook.validate_section(section)
        if not src_rel_path:
            raise ValueError("src_rel_path cannot be None or empty")

        if dst_rel_path is None:
            raise ValueError("dst_rel_path cannot be None or empty")

        src_rel_path = LabBook.make_path_relative(src_rel_path)
        dst_rel_path = LabBook.make_path_relative(dst_rel_path)

        src_abs_path = os.path.join(labbook.root_dir, section,
                                    src_rel_path.replace('..', ''))
        dst_abs_path = os.path.join(labbook.root_dir, section,
                                    dst_rel_path.replace('..', ''))

        if not os.path.exists(src_abs_path):
            raise ValueError(f"No src file exists at `{src_abs_path}`")

        try:
            src_type = 'directory' if os.path.isdir(src_abs_path) else 'file'
            logger.info(
                f"Moving {src_type} `{src_abs_path}` to `{dst_abs_path}`")
            labbook.git.remove(src_abs_path, keep_file=True)
            final_dest = shutil.move(src_abs_path, dst_abs_path)
            commit_msg = f"Moved {src_type} `{src_rel_path}` to `{dst_rel_path}`"
            cls._make_move_activity_record(labbook, section, dst_abs_path,
                                           commit_msg)

            if os.path.isfile(final_dest):
                t = final_dest.replace(os.path.join(labbook.root_dir, section),
                                       '')
                return [cls.get_file_info(labbook, section, t or "/")]
            else:
                moved_files = list()
                t = final_dest.replace(os.path.join(labbook.root_dir, section),
                                       '')
                moved_files.append(
                    cls.get_file_info(labbook, section, t or "/"))
                for root, dirs, files in os.walk(final_dest):
                    rt = root.replace(os.path.join(labbook.root_dir, section),
                                      '')
                    rt = _make_path_relative(rt)
                    for d in sorted(dirs):
                        dinfo = cls.get_file_info(labbook, section,
                                                  os.path.join(rt, d))
                        moved_files.append(dinfo)
                    for f in filter(lambda n: n != '.gitkeep', sorted(files)):
                        finfo = cls.get_file_info(labbook, section,
                                                  os.path.join(rt, f))
                        moved_files.append(finfo)
                return moved_files

        except Exception as e:
            logger.critical(
                "Failed moving file in labbook. Repository may be in corrupted state."
            )
            logger.exception(e)
            raise