Esempio n. 1
0
def test_rearrange_selfreference(fs):
    doc = 'I link to [myself](one.md).'
    fs.create_file('/notes/one.md', contents=doc)
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(edits_for_rearrange(repo, {'/notes/one.md': '/notes/two.md'}))
    assert not Path('/notes/one.md').exists()
    assert Path('/notes/two.md').exists()
    assert Path('/notes/two.md').read_text() == 'I link to [myself](two.md).'

    doc3 = 'I link to [a section of myself](#foo)'
    fs.create_file('/notes/three.md', contents=doc3)
    repo.change(
        edits_for_rearrange(repo, {'/notes/three.md': '/notes/four.md'}))
    assert Path('/notes/four.md').read_text(
    ) == 'I link to [a section of myself](#foo)'
Esempio n. 2
0
def test_rearrange_absolute_paths(fs):
    docs = [
        'I am being moved but link to something that is [not](/notes/two.md)',
        'I am not being moved',
        'I am not being moved but link to something that [is](/notes/one.md)',
        'I am being moved and so is what I link [to](/notes/one.md)'
    ]
    paths = [
        '/notes/one.md', '/notes/two.md', '/notes/three.md', '/notes/four.md'
    ]
    for i in range(4):
        fs.create_file(paths[i], contents=docs[i])
    Path('/notes/newdir').mkdir()
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(
        edits_for_rearrange(repo, {
            paths[0]: '/notes/newdir/new1.md',
            paths[3]: '/notes/newdir/new4.md'
        }))
    assert not Path(paths[0]).exists()
    assert not Path(paths[3]).exists()
    assert Path(paths[1]).read_text() == docs[1]
    assert Path(paths[2]).read_text(
    ) == 'I am not being moved but link to something that [is](newdir/new1.md)'
    assert Path('/notes/newdir/new4.md').read_text(
    ) == 'I am being moved and so is what I link [to](new1.md)'
    assert Path('/notes/newdir/new1.md').read_text() == docs[0]
Esempio n. 3
0
def test_rearrange_swap(fs):
    doc1 = 'I link to [two](two.md).'
    doc2 = 'I link to [one](one.md).'
    fs.create_file('/notes/one.md', contents=doc1)
    fs.create_file('/notes/two.md', contents=doc2)
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(
        edits_for_rearrange(repo, {
            '/notes/one.md': '/notes/two.md',
            '/notes/two.md': '/notes/one.md'
        }))
    assert Path('/notes/one.md').exists()
    assert Path('/notes/two.md').exists()
    assert Path('/notes/one.md').read_text() == 'I link to [one](two.md).'
    assert Path('/notes/two.md').read_text() == 'I link to [two](one.md).'
Esempio n. 4
0
    def move(self, moves: Dict[str, str], *, into_dirs=True, check_exists=True,
             create_parents=False, delete_empty_parents=False) -> Dict[str, str]:
        """Moves files/directories and updates references to/from them appropriately.

        moves is a dict where the keys are source paths that should be moved, and the values are the destinations.
        If a destination is a directory and into_dirs is True, the source will be moved into it,
        using the source's filename; otherwise, the source is renamed to the destination.

        This method tries not to overwrite files; if a destination path already exists, a shortened UUID
        will be appended to the path. You can disable that behavior by setting check_exists=False.

        It's OK for a path to occur as a key and also another key's value. For example,
        ``{'foo': 'bar', 'bar': 'foo'}`` will swap the two files.

        If create_parents is True, any directories in a destination path that do not yet exist will be
        created.

        If delete_empty_parents is True, after moving files out of a directory, if the directory or any of its parent
        directories are empty, they will be deleted. (The root folder or current working directory will not be
        deleted regardless.)

        Returns a dict mapping paths of files that were moved, to their final paths.
        """
        moves = {k: v for k, v in moves.items() if not k == v}
        if not moves:
            return {}

        final_moves = {}
        unavailable = set()
        for src, dest in moves.items():
            if not os.path.exists(src):
                raise FileNotFoundError(f'File does not exist: {src}')
            if os.path.isdir(dest) and into_dirs:
                srcname = os.path.split(src)[1]
                dest = os.path.join(dest, srcname)

            dest = find_available_name(dest, unavailable, src) if check_exists else dest
            final_moves[src] = dest
            unavailable.add(dest)

        edits = list(edits_for_rearrange(self.repo, final_moves))
        for edit in edits:
            if isinstance(edit, MoveCmd):
                edit.create_parents = create_parents
                edit.delete_empty_parents = delete_empty_parents
        self.repo.change(edits)

        return final_moves
Esempio n. 5
0
def test_rearrange_special_characters(fs):
    doc1 = 'I link to [two](second%20doc%21.md).'
    doc2 = 'I link to [one](first%20doc%21.md).'
    fs.create_file('/notes/first doc!.md', contents=doc1)
    fs.create_file('/notes/second doc!.md', contents=doc2)
    Path('/notes/subdir').mkdir()
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(
        edits_for_rearrange(
            repo, {'/notes/first doc!.md': '/notes/subdir/new loc!.md'}))
    assert not Path('/notes/first doc!.md').exists()
    assert Path('/notes/second doc!.md').exists()
    assert Path('/notes/subdir/new loc!.md').exists()
    assert Path('/notes/second doc!.md').read_text(
    ) == 'I link to [one](subdir/new%20loc%21.md).'
    assert (Path('/notes/subdir/new loc!.md').read_text() ==
            'I link to [two](../second%20doc%21.md).')
Esempio n. 6
0
def test_rearrange_folder(fs):
    doc1 = 'I link to [two](dir/two.md).'
    doc2 = 'I link to [three](subdir/three.md).'
    doc3 = 'I link to [one](../../one.md) and [the web](https://example.com).'
    fs.create_file('/notes/one.md', contents=doc1)
    fs.create_file('/notes/dir/two.md', contents=doc2)
    fs.create_file('/notes/dir/subdir/three.md', contents=doc3)
    Path('/notes/wrapper').mkdir()
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(
        edits_for_rearrange(repo, {'/notes/dir': '/notes/wrapper/newdir'}))
    assert not Path('/notes/dir').exists()
    assert Path('/notes/one.md').read_text(
    ) == 'I link to [two](wrapper/newdir/two.md).'
    assert Path('/notes/wrapper/newdir/two.md').read_text(
    ) == 'I link to [three](subdir/three.md).'
    assert (
        Path('/notes/wrapper/newdir/subdir/three.md').read_text() ==
        'I link to [one](../../../one.md) and [the web](https://example.com).')
Esempio n. 7
0
def test_rearrange_mutual_subdirs(fs):
    doc1 = 'I link to [two](../two.md).'
    doc2 = 'I link to [one](subdir1/one.md).'
    fs.create_file('/notes/subdir1/one.md', contents=doc1)
    fs.create_file('/notes/two.md', contents=doc2)
    Path('/notes/subdir2').mkdir()
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(
        edits_for_rearrange(
            repo, {
                '/notes/subdir1/one.md': '/notes/one.md',
                '/notes/two.md': '/notes/subdir2/two.md'
            }))
    assert not Path('/notes/subdir1/one.md').exists()
    assert not Path('/notes/two.md').exists()
    assert Path('/notes/one.md').exists()
    assert Path('/notes/subdir2/two.md').exists()
    assert Path(
        '/notes/one.md').read_text() == 'I link to [two](subdir2/two.md).'
    assert Path(
        '/notes/subdir2/two.md').read_text() == 'I link to [one](../one.md).'
Esempio n. 8
0
    def organize(self) -> Dict[str, str]:
        """Reorganizes files using the function set in :attr:`notesdir.conf.NotesdirConf.path_organizer`.

        For every file in your note directories (defined by :attr:`notesdir.conf.RepoConf.root_paths`), this
        method will call that function with the file's FileInfo, and move the file to the path the function returns.

        Note that the function will only be called for files, not directories. You cannot directly move a directory
        by this method, but you can effectively move one by moving all the files from it to the same new directory.

        This method deletes any empty directories that result from the moves it makes, and creates any directories
        it needs to.

        The FileInfo is retrieved using :meth:`notesdir.models.FileInfoReq.full`.
        """
        infos = self.repo.query('', FileInfoReq.full())
        moves = {}
        move_fns = {}
        info_map = {}
        unavailable = set()
        for info in infos:
            if not os.path.isfile(info.path):
                continue
            info_map[info.path] = info
            dest = self.conf.path_organizer(info)
            if isinstance(dest, DependentPathFn):
                move_fns[info.path] = dest
            else:
                dest = find_available_name(dest, unavailable, info.path)
                if info.path == dest:
                    continue
                moves[info.path] = dest
                unavailable.add(dest)

        def process_fn(src: str):
            dpfn = move_fns[src]
            determinant = dpfn.determinant
            dinfo = info_map.get(determinant, FileInfo(determinant))
            if determinant in move_fns:
                process_fn(determinant)
            if determinant in moves:
                dinfo = replace(dinfo, path=moves[determinant])
            srcdest = dpfn.fn(dinfo)
            del move_fns[src]
            srcdest = find_available_name(srcdest, unavailable, src)
            if src == srcdest:
                return
            moves[src] = srcdest
            unavailable.add(srcdest)

        while move_fns:
            process_fn(next(iter(move_fns)))

        if not moves:
            return {}

        edits = list(edits_for_rearrange(self.repo, moves))
        for edit in edits:
            if isinstance(edit, MoveCmd):
                edit.create_parents = True
                edit.delete_empty_parents = True
        self.repo.change(edits)

        return moves