Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Folgezettel tree improvements #248

Merged
merged 4 commits into from
Jun 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions guide/2011503.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# Graph view

A zettelkasten is a [directed graph](https://en.wikipedia.org/wiki/Directed_graph), and <2017401> is a subset of this graph established by having zettels branch off to other zettels.
A zettelkasten is a [directed graph](https://en.wikipedia.org/wiki/Directed_graph). Neuron also has the notion of <2017401>, which is a subset of this graph established by having zettels "branch off" to other zettels.

## z-index

The z-index page (at `/z-index.html`; also linked in the footer) displays your Zettelkasten folgezettel graph. Neuron detects if there are any cycles in the graph (use `cf` to resolve cycles). Then, it detects all <2012301> in the graph, and displays the <2017401> for each cluster.
The z-index page (at `/z-index.html`; also linked in the header) displays the zettelkasten <2017401> for all <2012301> in the zettelkasten graph.

## Uplinks and Backlinks

Uplinks are a kind of backlinks. Specifically an **uplink tree** of a zettel is the subset of the <2017401> which branch off to the zettel. Uplink tree is displayed above the zettel; other backlinks are displayed below.
A backlink of a zettel is a zettel that links to it. If that link is a folgezettel link, it is called an "uplink".

An **uplink tree** of a zettel is the subset of the <2017401> which branch off to the zettel. Uplink tree is displayed above the zettel; other backlinks are displayed below.

Use `?cf` (see <2011504?cf>) to eject something out of a zettel's uplink tree.
17 changes: 13 additions & 4 deletions guide/2011504.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,22 @@ linked zettel.

If your link is merely a reference to another zettel, and you do not wish it to
be part of the <2017401?cf>, but only the graph connection, use the `cf`[^cf] query
flag (eg: `<ef3dke98?cf>`). Neuron will link the zettels, but the link would be
ignored when building the <2017401?cf>. This is especially useful to resolve
cycles and ensure sensible clusters.
flag (eg: `<ef3dke98?cf>`).

```markdown
This is a zettel file, which refers to another zettel without
strongly linking to it:

* <ef3dke98?cf>
```

Neuron will link the zettels, but the link would be
ignored when building the <2017401?cf>. This is especially useful to ensure sensible
clusters.

## Advanced linking

* [[2011506]]
* <2011506>

[^cf]:
> The abbreviation cf. (short for the Latin: *confer/conferatur*, both meaning "compare") is used in writing to refer the reader to other material to make a comparison with the topic being discussed. [Wikipedia](https://en.wikipedia.org/wiki/Cf.)
Expand Down
2 changes: 1 addition & 1 deletion guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

## Cerveau

[Cerveau](https://www.cerveau.app) is the upcoming web and mobile app ([see screenshot](https://lh3.googleusercontent.com/-aBX5ePS0mD6Ey8NQUFhRmX8SHhxr0aHBE9rX4aGPU_i4_NloQsJsSP2Ug0D8oJGrQeyz2H3MYrnyoJC83mGOsoHOkfB_z6kqDJUa7bbO2xHATw9pw-cKvvMkNpMnW1ww7Z-gpCaM4nm7yTFLSa9T7YQYdWOGJVnBy-vPBRX6d_3kJ0JZj9WoOBQtYnOUiLD85tEjF03PtVVw9mSvhpqrTPLicThyAHj3koYEJAXtdWqP_X7s4lSHvVqtod9NfvM8WFNSsITZJmBHbP8bfGBs1aVy2zCM7cU63bsMw5jVAA5wXm6Ef9v9Lf2gqY-SgRimzlqnUX4eAfEnhvmAkGEOiQgyJ8QJSSgm99TW9l_ve7ZQPZmXgnfUHHCjYwIoqc72VXjTEMn-RRhRJDD8txb3mRQU05t-nF7U4dW8XtWuyXFxB-7Vc7lkwofSEWh_3dJiPZwIT221qFCxz8pr8krMEPrzgf_-YSFoGo804PTXGpp0N8_gM4ElTt4KXx3jfyYiGx4OUD9Lm_n-FaLFyWas7ryIKmTDmx78t1PhycCjwoBT3k3BwWLk-RIcSV9KvCKpkhC2p2wf8qC5ekPgaGaj6J2Rh-OrAI5zF9-erRCzwVw6NF2E-xQFg_8VmIp4Lpvl1ygwB4pX9xKlq4qrGyxVtxO2bIcwnrELqx81Yi_Oah8Y2ik0_VVAdEkrzeDOX4=w3381-h2269-no?authuser=0)) for easy editing and publishing of Neuron notes backed by [Git](https://guides.github.com/introduction/git-handbook/) as storage.
[Cerveau](https://www.cerveau.app) is the upcoming web and mobile app for easy editing and publishing of Neuron notes backed by [Git](https://guides.github.com/introduction/git-handbook/) as storage.

## External links

Expand Down
2 changes: 1 addition & 1 deletion neuron/neuron.cabal
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cabal-version: 2.4
name: neuron
-- This version must be in sync with what's in Default.dhall
version: 0.5.6.3
version: 0.5.7.0
license: AGPL-3.0-only
copyright: 2020 Sridhar Ratnakumar
maintainer: srid@srid.ca
Expand Down
3 changes: 3 additions & 0 deletions neuron/src/lib/Data/Graph/Labelled.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Data.Graph.Labelled
mkGraphFrom,

-- * Querying
getGraph,
findVertex,
getVertices,
hasEdge,
Expand All @@ -22,6 +23,8 @@ module Data.Graph.Labelled
clusters,
dfsForestFrom,
dfsForestBackwards,
bfsForestFrom,
bfsForestBackwards,
obviateRootUnlessForest,
induceOnEdge,
)
Expand Down
12 changes: 12 additions & 0 deletions neuron/src/lib/Data/Graph/Labelled/Algorithm.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import qualified Data.Set as Set
import Data.Tree (Forest, Tree (..))
import Relude

{-# INLINE getGraph #-}
getGraph :: LabelledGraph v e -> LAM.AdjacencyMap e (VertexID v)
getGraph (LabelledGraph g _) = g

findVertex :: Ord (VertexID v) => VertexID v -> LabelledGraph v e -> Maybe v
findVertex v lg@(LabelledGraph g _) = do
guard $ LAM.hasVertex v g
Expand Down Expand Up @@ -88,6 +92,14 @@ dfsForestBackwards :: (Monoid e, Vertex v, Ord (VertexID v)) => v -> LabelledGra
dfsForestBackwards fromV (LabelledGraph g' v') =
dfsForestFrom [fromV] $ LabelledGraph (LAM.transpose g') v'

bfsForestBackwards :: (Monoid e, Vertex v, Ord (VertexID v)) => v -> LabelledGraph v e -> Forest v
bfsForestBackwards fromV (LabelledGraph g' v') =
bfsForestFrom [fromV] $ LabelledGraph (LAM.transpose g') v'

bfsForestFrom :: (Vertex v, Ord (VertexID v)) => [v] -> LabelledGraph v e -> Forest v
bfsForestFrom (fmap vertexID -> vs) g =
fmap (fmap $ getVertex g) $ Algo.bfsForest vs $ LAM.skeleton $ graph g

--------------------------
--- More general utilities
--------------------------
Expand Down
35 changes: 19 additions & 16 deletions neuron/src/lib/Neuron/Web/ZIndex.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,27 @@ import Relude hiding ((&))
-- All heavy graph computations are decoupled from rendering, producing this
-- value, that is in turn used for instant rendering.
data ZIndex = ZIndex
{ -- | Topological ordering of the folgezettel graph. Left value indicates
-- that it is cyclic.
zIndexTopSort :: Either (NonEmpty Zettel) [Zettel],
-- | Clusters on the folgezettel graph.
{ -- | Clusters on the folgezettel graph.
zIndexClusters :: [Forest (Zettel, [Zettel])],
-- | All zettel errors
zIndexErrors :: Map ZettelID ZettelError
zIndexErrors :: Map ZettelID ZettelError,
zIndexStats :: Stats
}

data Stats = Stats
{ statsZettelCount :: Int,
statsZettelConnectionCount :: Int
}
deriving (Eq, Show)

buildZIndex :: ZettelGraph -> Map ZettelID ZettelError -> ZIndex
buildZIndex graph errors =
let clusters = G.categoryClusters graph
clusters' :: [Forest (Zettel, [Zettel])] =
flip fmap clusters $ \(zs :: [Tree Zettel]) ->
G.backlinksMulti Folgezettel zs graph
topSort = G.topSort graph
in ZIndex topSort (fmap sortCluster clusters') errors
stats = Stats (length $ G.getZettels graph) (G.connectionCount graph)
in ZIndex (fmap sortCluster clusters') errors stats
where
-- TODO: Either optimize or get rid of this (or normalize the sorting somehow)
sortCluster fs =
Expand All @@ -70,14 +74,13 @@ renderZIndex (Theme.semanticColor -> themeColor) ZIndex {..} = do
elClass "h1" "header" $ text "Zettel Index"
renderErrors zIndexErrors
divClass "z-index" $ do
case zIndexTopSort of
Left (toList -> cyc) -> divClass "ui orange segment" $ do
el "h2" $ text "Cycle detected"
forM_ cyc $ \zettel ->
el "li" $ QueryView.renderZettelLink Nothing def zettel
_ -> blank
el "p" $ do
text $ "There " <> countNounBe "cluster" "clusters" (length zIndexClusters) <> " in the Zettelkasten folgezettel graph. "
text $
"The zettelkasten has "
<> countNounBe "zettel" "zettels" (statsZettelCount zIndexStats)
<> " and "
<> countNounBe "link" "links" (statsZettelConnectionCount zIndexStats)
text $ ". It has " <> countNounBe "cluster" "clusters" (length zIndexClusters) <> " in its folgezettel graph. "
text "Each cluster's "
elAttr "a" ("href" =: "https://neuron.zettel.page/2017401.html") $ text "folgezettel heterarchy"
text " is rendered as a forest."
Expand All @@ -86,8 +89,8 @@ renderZIndex (Theme.semanticColor -> themeColor) ZIndex {..} = do
el "ul" $ renderForest forest
where
countNounBe noun nounPlural = \case
1 -> "is 1 " <> noun
n -> "are " <> show n <> " " <> nounPlural
1 -> "1 " <> noun
n -> show n <> " " <> nounPlural

renderErrors :: DomBuilder t m => Map ZettelID ZettelError -> NeuronWebT t m ()
renderErrors errors = do
Expand Down
11 changes: 8 additions & 3 deletions neuron/src/lib/Neuron/Zettelkasten/Graph.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ module Neuron.Zettelkasten.Graph
backlinksMulti,
clusters,
categoryClusters,
connectionCount,
)
where

import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM
import Data.Default
import Data.Foldable (maximum)
import qualified Data.Graph.Labelled as G
Expand All @@ -37,13 +39,13 @@ import Relude
frontlinkForest :: Connection -> Zettel -> ZettelGraph -> Forest Zettel
frontlinkForest conn z =
G.obviateRootUnlessForest z
. G.dfsForestFrom [z]
. G.bfsForestFrom [z]
. G.induceOnEdge (== Just conn)

backlinkForest :: Connection -> Zettel -> ZettelGraph -> Forest Zettel
backlinkForest conn z =
G.obviateRootUnlessForest z
. G.dfsForestBackwards z
. G.bfsForestBackwards z
. G.induceOnEdge (== Just conn)

backlinks :: Connection -> Zettel -> ZettelGraph -> [Zettel]
Expand All @@ -67,7 +69,7 @@ backlinksMulti conn zs g =
categoryClusters :: ZettelGraph -> [Forest Zettel]
categoryClusters (categoryGraph -> g) =
let cs :: [[Zettel]] = sortMothers $ clusters g
in flip fmap cs $ \zs -> G.dfsForestFrom zs g
in flip fmap cs $ \zs -> G.bfsForestFrom zs g
where
-- Sort clusters with newer mother zettels appearing first.
sortMothers :: [NonEmpty Zettel] -> [[Zettel]]
Expand Down Expand Up @@ -96,3 +98,6 @@ getZettel = G.findVertex
-- | If no connection exists, this returns Nothing.
getConnection :: Zettel -> Zettel -> ZettelGraph -> Maybe Connection
getConnection z1 z2 g = fmap (fromMaybe def) $ G.edgeLabel g z1 z2

connectionCount :: ZettelGraph -> Int
connectionCount = LAM.edgeCount . G.getGraph
10 changes: 5 additions & 5 deletions neuron/test/Neuron/VersionSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ spec = do
"0.4" `isLesserOrEqual` olderThan
it "full versions" $ do
"0.6.1.2" `isGreater` olderThan
"0.5.8" `isGreater` olderThan
"0.5.6.8" `isGreater` olderThan
"0.5.6.0" `isLesserOrEqual` olderThan -- This is current version
"0.5.9" `isGreater` olderThan
"0.5.7.8" `isGreater` olderThan
"0.5.7.0" `isLesserOrEqual` olderThan -- This is current version
"0.3.1.0" `isLesserOrEqual` olderThan
it "within same major version" $ do
"0.5.6.8" `isGreater` olderThan
"0.5.6.0" `isLesserOrEqual` olderThan -- This is current version
"0.5.7.8" `isGreater` olderThan
"0.5.7.0" `isLesserOrEqual` olderThan -- This is current version