Пример #1
0
def test_change(fs):
    doc = """---
title: An Examination of the Navel
---

#personal #book-draft
# Preface: Reasons for #journaling

As I have explained at length in [another note](../Another%20Note.md) and also
published about online (see [this article](http://example.com/blahblah) and
[this one](http://example.com/blah) among many others), ...
"""
    expected = """---
created: 2019-06-04 10:12:13-08:00
title: A Close Examination of the Navel
---

#personal #book-draft
# Preface: Reasons for #journaling

As I have explained at length in [another note](moved/another-note.md) and also
published about online (see [this article](http://example.com/blahblah) and
[this one](https://example.com/meh) among many others), ...
"""
    path = '/fakenotes/test.md'
    fs.create_file(path, contents=doc)
    acc = MarkdownAccessor(path)
    acc.edit(ReplaceHrefCmd(path, '../Another%20Note.md', 'moved/another-note.md'))
    acc.edit(ReplaceHrefCmd(path, 'http://example.com/blah', 'https://example.com/meh'))
    acc.edit(SetTitleCmd(path, 'A Close Examination of the Navel'))
    acc.edit(SetCreatedCmd(path, datetime(2019, 6, 4, 10, 12, 13, 0, timezone(timedelta(hours=-8)))))
    assert acc.save()
    assert Path(path).read_text() == expected
Пример #2
0
def test_change(fs):
    fs.cwd = '/notes'
    path1 = '/notes/one.md'
    path2 = '/notes/two.md'
    path3 = '/notes/moved.md'
    fs.create_file(path1, contents='[1](old)')
    fs.create_file(path2, contents='[2](foo)')
    edits = [SetTitleCmd(path1, 'New Title'),
             ReplaceHrefCmd(path1, 'old', 'new'),
             MoveCmd(path1, path3),
             ReplaceHrefCmd(path2, 'foo', 'bar')]
    repo = config().instantiate()
    repo.change(edits)
    assert not Path(path1).exists()
    assert Path(path3).read_text() == '---\ntitle: New Title\n...\n[1](new)'
    assert Path(path2).read_text() == '[2](bar)'
    assert repo.info(path1, FileInfoReq.full()) == FileInfo(path1)
    assert repo.info(path3, FileInfoReq.full()) == FileInfo(path3, title='New Title', links=[LinkInfo(path3, 'new')])
    assert repo.info(path2, FileInfoReq.full()) == FileInfo(path2, links=[LinkInfo(path2, 'bar')])
    assert repo.info('old', FileInfoReq.full()) == FileInfo('/notes/old')
    assert repo.info('foo', FileInfoReq.full()) == FileInfo('/notes/foo')
    assert repo.info('new', FileInfoReq.full()) == FileInfo('/notes/new', backlinks=[LinkInfo(path3, 'new')])
    assert repo.info('bar', FileInfoReq.full()) == FileInfo('/notes/bar', backlinks=[LinkInfo(path2, 'bar')])
    # regression test for bug where invalidate removed entries for files that were referred to
    # only by files that had not been changed
    repo.invalidate()
    assert repo.info('new', FileInfoReq.full()) == FileInfo('/notes/new', backlinks=[LinkInfo(path3, 'new')])
    assert repo.info('bar', FileInfoReq.full()) == FileInfo('/notes/bar', backlinks=[LinkInfo(path2, 'bar')])
Пример #3
0
def test_ignore_fenced_code_blocks(fs):
    doc = """#tag1 [link](link1.md)
```foo
#tag2 #tag3
text [link](link1.md)
```
  #tag3 [link](link2.md)

   ```
   [link](link3.md)
   ```"""
    path = '/fakenotes/text.md'
    fs.create_file(path, contents=doc)
    acc = MarkdownAccessor(path)
    info = acc.info()
    assert info.tags == {'tag1', 'tag3'}
    assert info.links == [
        LinkInfo(path, 'link1.md'),
        LinkInfo(path, 'link2.md')
    ]
    acc.edit(ReplaceHrefCmd(path, 'link1.md', 'CHANGED1'))
    acc.edit(ReplaceHrefCmd(path, 'link2.md', 'CHANGED2'))
    acc.edit(ReplaceHrefCmd(path, 'link3.md', 'CHANGED3'))
    acc.edit(DelTagCmd(path, 'tag3'))
    acc.edit(DelTagCmd(path, 'tag2'))
    acc.save()
    assert Path(path).read_text() == """#tag1 [link](CHANGED1)
Пример #4
0
def test_change(fs):
    doc = """<html>
    <head>
        <title>fdsalkhflsdakjsdhfaslkjdhfalkj</title>
        <meta name="created" content="2001-01-01 02:02:02 +0000" />
    </head>
    <body>
        <p>Hi! Here's a <a href="../Mediocre%20Note.md">link</a> and a <img src="http://example.com/foo.png" title="picture"/>.</p>

        <video controls src="media/something.weird">a video element</video>
        <audio controls src="media/something.weird">an audio element</audio>
        <audio controls><source src="media/something.weird">another audio element</audio>

        <p>Here's an <a href="../Mediocre%20Note.html">unaffected link</a>.</p>
    </body>
</html>"""
    expected = """<html>
    <head>
        <title>A Delightful Note</title>
        <meta name="created" content="2019-06-04 10:12:13 -0800" />
    </head>
    <body>
        <p>Hi! Here's a <a href="../archive/Mediocre%20Note.md">link</a> and a <img src="http://example.com/bar.png" title="picture"/>.</p>

        <video controls src="content/something.cool">a video element</video>
        <audio controls src="content/something.cool">an audio element</audio>
        <audio controls><source src="content/something.cool">another audio element</audio>

        <p>Here's an <a href="../Mediocre%20Note.html">unaffected link</a>.</p>
    </body>
</html>"""
    path = Path('/fakenotes/test.html')
    fs.create_file(path, contents=doc)
    acc = HTMLAccessor(str(path))
    acc.edit(SetTitleCmd(str(path), 'A Delightful Note'))
    acc.edit(
        SetCreatedCmd(
            str(path),
            datetime(2019, 6, 4, 10, 12, 13, 0,
                     timezone(timedelta(hours=-8)))))
    acc.edit(
        ReplaceHrefCmd(str(path), '../Mediocre%20Note.md',
                       '../archive/Mediocre%20Note.md'))
    acc.edit(
        ReplaceHrefCmd(str(path), 'http://example.com/foo.png',
                       'http://example.com/bar.png'))
    acc.edit(
        ReplaceHrefCmd(str(path), 'media/something.weird',
                       'content/something.cool'))
    assert acc.save()
    assert BeautifulSoup(
        path.read_text(),
        'lxml',
    ) == BeautifulSoup(expected, 'lxml')
Пример #5
0
def test_change(fs):
    fs.create_file('/notes/one.md', contents='[1](old)')
    fs.create_file('/notes/two.md', contents='[2](foo)')
    edits = [SetTitleCmd('/notes/one.md', 'New Title'),
             ReplaceHrefCmd('/notes/one.md', 'old', 'new'),
             MoveCmd('/notes/one.md', '/notes/moved.md'),
             ReplaceHrefCmd('/notes/two.md', 'foo', 'bar')]
    repo = DirectRepoConf(root_paths={'/notes'}).instantiate()
    repo.change(edits)
    assert not Path('/notes/one.md').exists()
    assert Path('/notes/moved.md').read_text() == '---\ntitle: New Title\n...\n[1](new)'
    assert Path('/notes/two.md').read_text() == '[2](bar)'
Пример #6
0
def edits_for_path_replacement(referrer: str, hrefs: Set[str],
                               replacement: str) -> Iterator[ReplaceHrefCmd]:
    """Yields commands to replace a file's links to a path with links to another path."""
    for href in hrefs:
        url = urlparse(href)
        newref = path_as_href(href_path(referrer, replacement), url)
        yield ReplaceHrefCmd(referrer, href, newref)
Пример #7
0
def edits_for_rearrange(store: Repo,
                        renames: Dict[str, str]) -> Iterator[FileEditCmd]:
    """Yields commands that will rename files and update links accordingly.

    The keys of the dictionary are the paths to be renamed, and the values
    are what they should be renamed to. (If a path appears as both a key and
    as a value, it will be moved to a temporary file as an intermediate step.)

    The given store is used to search for files that link to any of the paths that
    are keys in the dictionary, so that ReplaceHrefEditCmd instances can be generated for them.
    The files that are being renamed will also be checked for outbound links,
    and ReplaceRef edits will be generated for those too.

    Source paths may be directories; the directory as a whole will be moved, and links
    to/from all files/folders within it will be updated too.
    """
    to_move = {
        os.path.realpath(s): os.path.realpath(d)
        for s, d in renames.items()
    }
    all_moves = {}
    for src, dest in to_move.items():
        all_moves[src] = dest
        if os.path.isdir(src):
            for path in glob(os.path.join(src, '**', '*'), recursive=True):
                all_moves[path] = os.path.join(dest,
                                               os.path.relpath(path, src))

    for src, dest in all_moves.items():
        info = store.info(src,
                          FileInfoReq(path=True, links=True, backlinks=True))
        if info:
            for link in info.links:
                referent = link.referent()
                if not referent:
                    continue
                url = urlparse(link.href)
                if referent == src and url.path == '':
                    continue
                if referent in all_moves:
                    referent = all_moves[referent]
                elif os.path.isabs(url.path):
                    # Don't try to rewrite absolute paths, unless they refer to a file we're moving.
                    continue
                newhref = path_as_href(href_path(dest, referent), url)
                if not link.href == newhref:
                    yield ReplaceHrefCmd(src, link.href, newhref)
        for link in info.backlinks:
            if link.referrer in all_moves:
                continue
            # TODO either pass in all the hrefs at once, or change method to not take in a set
            yield from edits_for_path_replacement(link.referrer, {link.href},
                                                  dest)

    yield from edits_for_raw_moves(to_move)
Пример #8
0
def test_relink(fs, capsys):
    nd_setup(fs)
    path1 = Path('/notes/foo.md')
    fs.create_file(path1,
                   contents='I link to [bar](/notes/subdir1/bar.md#section)')
    assert cli.main(['relink', '-p', '/notes/subdir1/bar.md',
                     '/blah/baz.md']) == 0
    out, err = capsys.readouterr()
    assert out == str(
        ReplaceHrefCmd(str(path1), '/notes/subdir1/bar.md#section',
                       '../blah/baz.md#section')) + '\n'
    assert cli.main(['relink', '/notes/subdir1/bar.md', '/blah/baz.md']) == 0
    assert path1.read_text() == 'I link to [bar](../blah/baz.md#section)'
Пример #9
0
def test_org_simple(fs, capsys, mocker):
    mocker.patch('shortuuid.uuid',
                 side_effect=(f'uuid{i}' for i in itertools.count(1)))
    nd_setup(fs,
             extra_conf="""
conf.path_organizer = lambda info: info.path.replace('hi', 'hello')
""")
    path1 = Path('/notes/cwd/one.md')
    path2 = Path('/notes/cwd/two.md')
    fs.create_file(path1)
    fs.create_file(path2, contents='I link to [hi](hi.md).')
    assert cli.main(['organize', '-j']) == 0
    assert path1.is_file()
    assert path2.is_file()
    out, err = capsys.readouterr()
    assert json.loads(out) == {}

    path3 = Path('/notes/cwd/hi.md')
    path4 = Path('/notes/cwd/hello.md')
    fs.create_file(path3, contents='I link to [one](one.md).')
    assert cli.main(['organize', '-p']) == 0
    assert path3.exists()
    assert not path4.exists()
    out, err = capsys.readouterr()
    assert out == (str(ReplaceHrefCmd(str(path2), 'hi.md', 'hello.md')) +
                   '\n' + str(
                       MoveCmd(str(path3),
                               str(path4),
                               create_parents=True,
                               delete_empty_parents=True)) + '\n')
    assert cli.main(['organize', '-j']) == 0
    assert path1.is_file()
    assert path2.read_text() == 'I link to [hi](hello.md).'
    assert not path3.exists()
    assert path4.read_text() == 'I link to [one](one.md).'
    capsys.readouterr()

    path5 = Path('/notes/cwd/hello_uuid1.md')
    fs.create_file(path3, contents='I am a duplicate name')
    assert cli.main(['organize', '-j']) == 0
    assert not path3.exists()
    assert path4.read_text() == 'I link to [one](one.md).'
    assert path5.read_text() == 'I am a duplicate name'
    out, err = capsys.readouterr()
    assert json.loads(out) == {'/notes/cwd/hi.md': '/notes/cwd/hello_uuid1.md'}
Пример #10
0
def test_mv_file(fs, capsys):
    nd_setup(fs)
    fs.create_file('/notes/cwd/subdir/old.md')
    fs.create_file('/notes/dir/referrer.md',
                   contents='I have a [link](../cwd/subdir/old.md).')
    assert cli.main(['mv', '-p', 'subdir/old.md', '../dir/new.md']) == 0
    assert Path('/notes/cwd/subdir/old.md').exists()
    assert Path('/notes/dir/referrer.md').read_text(
    ) == 'I have a [link](../cwd/subdir/old.md).'
    out, err = capsys.readouterr()
    assert out == (
        str(
            ReplaceHrefCmd('/notes/dir/referrer.md', '../cwd/subdir/old.md',
                           'new.md')) + '\n' +
        str(MoveCmd('/notes/cwd/subdir/old.md', '/notes/dir/new.md')) + '\n')

    assert cli.main(['mv', 'subdir/old.md', '../dir/new.md']) == 0
    assert not Path('/notes/cwd/subdir/old.md').exists()
    assert Path('/notes/dir/new.md').exists()
    assert Path(
        '/notes/dir/referrer.md').read_text() == 'I have a [link](new.md).'
    out, err = capsys.readouterr()
    assert not out
Пример #11
0
 def replace_href(self, path: str, original: str, replacement: str) -> None:
     """Convenience method equivalent to calling change with one :class:`notesdir.models.ReplaceRefCmd`"""
     self.change([ReplaceHrefCmd(path, original, replacement)])