Skip to content

Commit

Permalink
Explore connected components (#31)
Browse files Browse the repository at this point in the history
* πŸ“ Print program purpose

* ✨ Add `components` command

* πŸ§ͺ Update test for components command

* πŸ“ Document the component command

* πŸš€ Bump version to v0.3.0-beta.1

* 🐞 Fix string formatting and make deterministic

* πŸ“ Explain connected components

* πŸ“ Add new gif that explains components

* πŸ“ Add "Buy me a coffee" link

* πŸ“ Format README.md

* πŸš€ Bump version to v0.3.0

* πŸ“ Add link to newsletter.
  • Loading branch information
BasilPH authored Oct 14, 2020
1 parent dc6888b commit b1b8c71
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 16 deletions.
74 changes: 60 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ Vizel makes the following assumptions:

* The Zettel files have an `.md` or `.txt` extension.
* All Zettel are in one single directory.
* References use the `[[REFERENCE]]` or `[LABEL](REFERENCE)` format.
* References use the `[[REFERENCE]]` or `[LABEL](REFERENCE)` format.
* References of a Zettel pointing to itself are ignored.

Vizel was first developed for the format used by the [The Archive](https://zettelkasten.de/the-archive/).
Other formats are now supported as well, thanks to the help from the community.
Vizel was first developed for the format used by the
[The Archive](https://zettelkasten.de/the-archive/). Other formats are
now supported as well, thanks to the help from the community.

### Installing

Run `pip install vizel`

If you get an error about missing graphviz when running the `graph-pdf` command, you might need to install it with
If you get an error about missing graphviz when running the `graph-pdf`
command, you might need to install it with

` brew install graphviz` on OS X or

Expand All @@ -37,6 +39,7 @@ If you get an error about missing graphviz when running the `graph-pdf` command,
`vizel` has the following commands:

#### graph-pdf

```
vizel graph-pdf [OPTIONS] DIRECTORY
Expand All @@ -49,6 +52,7 @@ Options:
```

#### stats

```
Usage: vizel stats [OPTIONS] DIRECTORY
Expand All @@ -65,7 +69,28 @@ Options:
--help Show this message and exit.
```

##### A note on connected components

The fewer connected components your Zettelkasten has, the better. The
ideal number is 1. It means that you can reach any Zettel by following
links. This, in turn, should increase the likelihood of making new
semantic connections.

Connected components are a concept from graph theory. In the context of
a Zettelkasten and vizel, a connected component is a set of Zettel,
which can be reached from any other Zettel in the same component by
following links. Those links do not need to be direct but can pass
through other Zettel. The direction of the links also doesn't matter.

Two Zettel are not in the same component if there is no way to reach one
from the other through links.

Connected components will show up as separate clusters of Zettel when
using `graph-pdf`. Use the `components` command to get a list of your
components, and the Zettel contained in each.

#### unconnected

```
Usage: vizel unconnected [OPTIONS] DIRECTORY
Expand All @@ -76,22 +101,41 @@ Options:
--help Show this message and exit.
```

#### components

```
Usage: vizel components [OPTIONS] DIRECTORY
Lists the connected components and their Zettel in DIRECTORY.
Options:
--help Show this message and exit.
```

## Built With

* [NetworkX](https://networkx.github.io/): Network analysis in Python
* [click](https://click.palletsprojects.com): Python composable command-line interface toolkit
* [Graphviz](https://github.com/xflr6/graphviz): Simple Python interface for Graphviz

## Contributing
* [click](https://click.palletsprojects.com): Python composable
command-line interface toolkit
* [Graphviz](https://github.com/xflr6/graphviz): Simple Python interface
for Graphviz

Feel free to open issues and pull-requests.
## Updates & Contributing

If you've found vizel useful, please consider [sponsoring](https://github.com/sponsors/BasilPH) maintenance and further development.
Feel free to open issues and pull-requests. Subscribe to the
[vizel newsletter](https://tinyletter.com/vizel) to be informed about
new releases and features in development.

You can reach out to me for feedback or questions on
[Twitter](https://twitter.com/BasilPH) or through
[my website](https://interdimensional-television.com/).

If you've found vizel useful, please consider
[sponsoring](https://github.com/sponsors/BasilPH) maintenance and
further development. Or
[buying me a coffee](https://www.buymeacoffee.com/interdimension).


### Development install

The project uses [Poetry](https://python-poetry.org/).
Expand All @@ -107,8 +151,8 @@ Run `py.test` in the `tests` directory.

## Versioning

Vizel uses [SemVer](http://semver.org/) for versioning. For the
versions available, see the
Vizel uses [SemVer](http://semver.org/) for versioning. For the versions
available, see the
[tags on the repository](https://github.com/BasilPH/vizel/tags).

## Authors
Expand All @@ -122,5 +166,7 @@ This project is licensed under GNU GPLv3.
## Acknowledgments

* Thank you Christian Tietze and Sascha Fast for creating
[The Archive](https://zettelkasten.de/the-archive/) app and writing
a [book](https://zettelkasten.de/book/de/) (German only) on the Zettelkasten method.
[The Archive](https://zettelkasten.de/the-archive/) app and writing a
[book](https://zettelkasten.de/book/de/) (German only) on the
Zettelkasten method.

Binary file modified assets/vizel_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "vizel"
version = "0.2.1"
version = "0.3.0"
description = "Vizualise a Zettelkasten"
authors = ["Basil Philipp <basil@interdimensional-television.com>"]
license = "GPL-3.0-only"
Expand Down
25 changes: 25 additions & 0 deletions tests/test_vizel.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,28 @@ def test_unconnected(zettelkasten_directory, stderr_expected):
assert result.stdout == stdout_expected.format(ext=expected_file_ending)

assert result.stderr == stderr_expected.format(ext=expected_file_ending)


def test_components(zettelkasten_directory, stderr_expected):
runner = CliRunner(mix_stderr=False)
result = runner.invoke(main, ['components', zettelkasten_directory])

assert result.exit_code == 0

expected_file_ending = zettelkasten_directory.rpartition('_')[2].rstrip('/')

stdout_expected = (
'# Component 1\n'
'03242020003215-eda-explained.{ext}\n'
'03272020061037-electrodermal-activity.{ext}\n'
'202002241029_Broken_references_Zettel.{ext}\n'
'202002251025_This_is_the_first_test_zettel.{ext}\n'
'202003211727_This_is_the_second_test_zettel.{ext}\n\n'
'# Component 2\n'
'202005011017_All_by_myself.{ext}\n\n'
'# Component 3\n'
'202006112225_broken_utf8.{ext}\n\n'
)
assert result.stdout == stdout_expected.format(ext=expected_file_ending)

assert result.stderr == stderr_expected.format(ext=expected_file_ending)
39 changes: 38 additions & 1 deletion vizel/cli.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import glob
import os.path
import re
from operator import itemgetter

import click
import networkx as nx
Expand All @@ -10,7 +11,12 @@

@click.group()
def main():
# TODO Collect directory here and create digraph object
"""
See the stats and connections of your Zettelkasten.
\f
:return: None
"""
pass


Expand Down Expand Up @@ -214,3 +220,34 @@ def unconnected(directory):

for node in sorted(zero_degree_nodes):
click.echo('{}'.format(node))


@main.command(short_help='Connected components')
@click.argument('directory', type=click.Path(exists=True, dir_okay=True))
def components(directory):
"""
Lists the connected components and their Zettel in DIRECTORY.
\f
:param directory: Directory where all the Zettel are.
:return None
"""
digraph = _get_digraph(directory)
undirected_graph = digraph.to_undirected()

conn_components = nx.connected_components(undirected_graph)

# Sort the Zettel in each component
conn_components = [sorted(component) for component in conn_components]

# Sort the conn_components by their size and break ties with the name of their first Zettel
conn_components = sorted(conn_components, key=itemgetter(0))
conn_components = sorted(conn_components, key=len, reverse=True)

for i, component in enumerate(conn_components, start=1):
click.echo('# Component {}'.format(i))
for zettel in component:
click.echo(zettel)

click.echo()

0 comments on commit b1b8c71

Please sign in to comment.