[NGRAMS-TABLE] prevent the creation of cycles

parent fe8e89af
...@@ -28,11 +28,11 @@ import Data.Foldable (class Foldable, foldMap, foldl, foldr) ...@@ -28,11 +28,11 @@ import Data.Foldable (class Foldable, foldMap, foldl, foldr)
import Data.FoldableWithIndex (class FoldableWithIndex, foldMapWithIndex, foldlWithIndex, foldrWithIndex) import Data.FoldableWithIndex (class FoldableWithIndex, foldMapWithIndex, foldlWithIndex, foldrWithIndex)
import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex) import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex)
import Data.Newtype (class Newtype) import Data.Newtype (class Newtype)
import Data.Lens (Iso', Lens', (%~), (.=), (^.), (^..), to) import Data.Lens (Iso', Lens', (?=), (%=), (%~), (.~), (^?), (^.), (^..), to, use, view)
import Data.Lens.Common (_Just) import Data.Lens.Common (_Just)
import Data.Lens.At (class At, at) import Data.Lens.At (class At, at)
import Data.Lens.Index (class Index, ix) import Data.Lens.Index (class Index, ix)
import Data.Lens.Fold (folded) import Data.Lens.Fold (folded, traverseOf_)
import Data.Lens.Record (prop) import Data.Lens.Record (prop)
import Data.Lens.Iso.Newtype (_Newtype) import Data.Lens.Iso.Newtype (_Newtype)
import Data.List as List import Data.List as List
...@@ -95,10 +95,13 @@ newtype NgramsElement = NgramsElement ...@@ -95,10 +95,13 @@ newtype NgramsElement = NgramsElement
, list :: TermList , list :: TermList
, occurrences :: Int , occurrences :: Int
, parent :: Maybe NgramsTerm , parent :: Maybe NgramsTerm
, root :: Maybe NgramsTerm
, children :: Set NgramsTerm , children :: Set NgramsTerm
} }
_parent = prop (SProxy :: SProxy "parent") _parent = prop (SProxy :: SProxy "parent")
_root = prop (SProxy :: SProxy "root")
_ngrams = prop (SProxy :: SProxy "ngrams")
_children :: forall row. Lens' { children :: Set NgramsTerm | row } (Set NgramsTerm) _children :: forall row. Lens' { children :: Set NgramsTerm | row } (Set NgramsTerm)
_children = prop (SProxy :: SProxy "children") _children = prop (SProxy :: SProxy "children")
_occurrences :: forall row. Lens' { occurrences :: Int | row } Int _occurrences :: forall row. Lens' { occurrences :: Int | row } Int
...@@ -118,9 +121,10 @@ instance decodeJsonNgramsElement :: DecodeJson NgramsElement where ...@@ -118,9 +121,10 @@ instance decodeJsonNgramsElement :: DecodeJson NgramsElement where
list <- obj .? "list" list <- obj .? "list"
occurrences <- obj .? "occurrences" occurrences <- obj .? "occurrences"
parent <- obj .?? "parent" parent <- obj .?? "parent"
root <- obj .?? "root"
children' <- obj .? "children" children' <- obj .? "children"
let children = Set.fromFoldable (children' :: Array NgramsTerm) let children = Set.fromFoldable (children' :: Array NgramsTerm)
pure $ NgramsElement {ngrams, list, occurrences, parent, children} pure $ NgramsElement {ngrams, list, occurrences, parent, root, children}
type Version = Int type Version = Int
...@@ -290,6 +294,7 @@ applyNgramsPatch (NgramsPatch p) (NgramsElement e) = NgramsElement ...@@ -290,6 +294,7 @@ applyNgramsPatch (NgramsPatch p) (NgramsElement e) = NgramsElement
, list: applyReplace p.patch_list e.list , list: applyReplace p.patch_list e.list
, occurrences: e.occurrences , occurrences: e.occurrences
, parent: e.parent , parent: e.parent
, root: e.root
, children: applyPatchSet p.patch_children e.children , children: applyPatchSet p.patch_children e.children
} }
...@@ -351,11 +356,22 @@ applyPatchMap applyPatchValue (PatchMap p) = mapWithIndex f ...@@ -351,11 +356,22 @@ applyPatchMap applyPatchValue (PatchMap p) = mapWithIndex f
type NgramsTablePatch = PatchMap NgramsTerm NgramsPatch type NgramsTablePatch = PatchMap NgramsTerm NgramsPatch
type RootParent = { root :: NgramsTerm, parent :: NgramsTerm }
type ReParent a = forall m. MonadState NgramsTable m => a -> m Unit type ReParent a = forall m. MonadState NgramsTable m => a -> m Unit
reParent :: Maybe NgramsTerm -> ReParent NgramsTerm reRootChildren :: NgramsTerm -> ReParent NgramsTerm
reParent parent child = reRootChildren root ngram = do
at child <<< _Just <<< _NgramsElement <<< _parent .= parent nre <- use (at ngram)
traverseOf_ (_Just <<< _NgramsElement <<< _children <<< folded) (\child -> do
at child <<< _Just <<< _NgramsElement <<< _root ?= root
reRootChildren root child) nre
reParent :: Maybe RootParent -> ReParent NgramsTerm
reParent mrp child = do
at child <<< _Just <<< _NgramsElement %= ((_parent .~ (view _parent <$> mrp)) <<<
(_root .~ (view _root <$> mrp)))
reRootChildren (maybe child identity (mrp ^? _Just <<< _root)) child
-- reParentNgramsPatch :: NgramsTerm -> ReParent NgramsPatch -- reParentNgramsPatch :: NgramsTerm -> ReParent NgramsPatch
-- ^ GHC would have accepted this type. Here reParentNgramsPatch checks but -- ^ GHC would have accepted this type. Here reParentNgramsPatch checks but
...@@ -363,8 +379,13 @@ reParent parent child = ...@@ -363,8 +379,13 @@ reParent parent child =
reParentNgramsPatch :: forall m. MonadState NgramsTable m reParentNgramsPatch :: forall m. MonadState NgramsTable m
=> NgramsTerm -> NgramsPatch -> m Unit => NgramsTerm -> NgramsPatch -> m Unit
reParentNgramsPatch parent (NgramsPatch {patch_children: PatchSet {rem, add}}) = do reParentNgramsPatch parent (NgramsPatch {patch_children: PatchSet {rem, add}}) = do
-- root_of_parent <- use (at parent <<< _Just <<< _NgramsElement <<< _root)
-- ^ TODO this does not type checks, we do the following two lines instead:
s <- use (at parent)
let root_of_parent = s ^. (_Just <<< _NgramsElement <<< _root)
let rp = { root: maybe parent identity root_of_parent, parent }
traverse_ (reParent Nothing) rem traverse_ (reParent Nothing) rem
traverse_ (reParent $ Just parent) add traverse_ (reParent $ Just rp) add
reParentNgramsTablePatch :: ReParent NgramsTablePatch reParentNgramsTablePatch :: ReParent NgramsTablePatch
reParentNgramsTablePatch = void <<< traverseWithIndex reParentNgramsPatch reParentNgramsTablePatch = void <<< traverseWithIndex reParentNgramsPatch
...@@ -373,8 +394,6 @@ applyNgramsTablePatch :: NgramsTablePatch -> NgramsTable -> NgramsTable ...@@ -373,8 +394,6 @@ applyNgramsTablePatch :: NgramsTablePatch -> NgramsTable -> NgramsTable
applyNgramsTablePatch p (NgramsTable m) = applyNgramsTablePatch p (NgramsTable m) =
execState (reParentNgramsTablePatch p) $ execState (reParentNgramsTablePatch p) $
NgramsTable $ applyPatchMap applyNgramsPatch p m NgramsTable $ applyPatchMap applyNgramsPatch p m
-- TODO: update the .root fields...
-- See ROOT-UPDATE
type State = type State =
{ ngramsTablePatch :: NgramsTablePatch { ngramsTablePatch :: NgramsTablePatch
...@@ -494,9 +513,9 @@ tableContainer { pageParams ...@@ -494,9 +513,9 @@ tableContainer { pageParams
<<< _NgramsElement <<< _NgramsElement
<<< _children <<< _children
%~ applyPatchSet (patchSetFromMap ngramsChildren) %~ applyPatchSet (patchSetFromMap ngramsChildren)
ngramsClick child ngramsClick {depth: 1, ngrams: child} =
| child == ngrams = Nothing Just $ dispatch $ ToggleChild false child
| otherwise = Just $ dispatch $ ToggleChild false child ngramsClick _ = Nothing
in in
[ p[] [text $ "Editing " <> ngrams] [ p[] [text $ "Editing " <> ngrams]
, renderNgramsTree { ngramsTable, ngrams, ngramsStyle: [], ngramsClick } , renderNgramsTree { ngramsTable, ngrams, ngramsStyle: [], ngramsClick }
...@@ -603,26 +622,22 @@ ngramsTableSpec = simpleSpec performAction render ...@@ -603,26 +622,22 @@ ngramsTableSpec = simpleSpec performAction render
loaderDispatch $ Loader.SetPath $ pageParams {params = params} loaderDispatch $ Loader.SetPath $ pageParams {params = params}
ngramsTable = applyNgramsTablePatch ngramsTablePatch initTable ngramsTable = applyNgramsTablePatch ngramsTablePatch initTable
rows = convertRow <$> Map.toUnfoldable (Map.filter displayRow (ngramsTable ^. _NgramsTable)) rows = convertRow <$> Map.toUnfoldable (Map.filter displayRow (ngramsTable ^. _NgramsTable))
isRoot (NgramsElement e) = e.parent == Nothing
-- TODO: There is a missing case where we display a row that we should not. ngramsParentRoot :: Maybe String
-- Assumptions: ngramsParentRoot =
-- * cats -> cat -> animal (\np -> ngramsTable ^. at np <<< _Just <<< _NgramsElement <<< _root) =<< ngramsParent
-- * We are editing cats: ngramsParent == Just "cats"
-- * animal should not be listed since this would create a cycle! displayRow (NgramsElement {ngrams, root}) =
displayRow e@(NgramsElement {ngrams, children}) = root == Nothing
isRoot e -- ^ Display only nodes without parents
-- ^ Display only nodes with parents && ngramsChildren ^. at ngrams /= Just true
&& (ngramsChildren ^. at ngrams /= Just true) -- ^ and which are not scheduled to be added already
-- ^ and which are not scheduled to be added already. && Just ngrams /= ngramsParent
&& (case ngramsParent of -- ^ and which are not our new parent
Just p -> && Just ngrams /= ngramsParentRoot
ngrams /= p && -- ^ and which are not the root of our new parent
-- ^ and which is not the node being currently edited.
not (Set.member p children)
-- ^ ... or one of its children.
Nothing -> true)
|| -- Unless they are scheduled to be removed. || -- Unless they are scheduled to be removed.
(ngramsChildren ^. at ngrams == Just false) ngramsChildren ^. at ngrams == Just false
convertRow (Tuple ngrams ngramsElement) = convertRow (Tuple ngrams ngramsElement) =
{ row: { row:
renderNgramsItem { ngramsTable, ngrams, ngramsParent, ngramsElement, dispatch } renderNgramsItem { ngramsTable, ngrams, ngramsParent, ngramsElement, dispatch }
...@@ -665,19 +680,22 @@ mainNgramsTableSpec = simpleSpec defaultPerformAction render ...@@ -665,19 +680,22 @@ mainNgramsTableSpec = simpleSpec defaultPerformAction render
, component: ngramsTableClass , component: ngramsTableClass
} ] } ]
type NgramsDepth = {ngrams :: NgramsTerm, depth :: Int}
type NgramsClick = NgramsDepth -> Maybe (Effect Unit)
tree :: { ngramsTable :: NgramsTable tree :: { ngramsTable :: NgramsTable
, ngramsStyle :: Array DOM.Props , ngramsStyle :: Array DOM.Props
, ngramsClick :: NgramsTerm -> Maybe (Effect Unit) , ngramsClick :: NgramsClick
} -> NgramsTerm -> ReactElement } -> NgramsDepth -> ReactElement
tree params@{ngramsTable, ngramsStyle, ngramsClick} label = tree params@{ngramsTable, ngramsStyle, ngramsClick} nd@{ngrams} =
li [ style {width : "100%"} ] li [ style {width : "100%"} ]
[ i icon [] [ i icon []
, tag [text $ " " <> label] , tag [text $ " " <> ngrams]
, forest cs , forest cs
] ]
where where
tag = tag =
case ngramsClick label of case ngramsClick nd of
Just effect -> Just effect ->
a (ngramsStyle <> [onClick $ const effect]) a (ngramsStyle <> [onClick $ const effect])
Nothing -> Nothing ->
...@@ -686,9 +704,11 @@ tree params@{ngramsTable, ngramsStyle, ngramsClick} label = ...@@ -686,9 +704,11 @@ tree params@{ngramsTable, ngramsStyle, ngramsClick} label =
icon = gray <> [className $ "fas fa-caret-" <> if open then "down" else "right"] icon = gray <> [className $ "fas fa-caret-" <> if open then "down" else "right"]
open = not leaf || false {- TODO -} open = not leaf || false {- TODO -}
gray = if leaf then [style {color: "#adb5bd"}] else [] gray = if leaf then [style {color: "#adb5bd"}] else []
cs = ngramsTable ^.. ix label <<< _NgramsElement <<< _children <<< folded cs = ngramsTable ^.. ix ngrams <<< _NgramsElement <<< _children <<< folded
forest = ul [] <<< map (tree params) <<< List.toUnfoldable forest =
let depth = nd.depth + 1 in
ul [] <<< map (\ngrams -> tree params {depth, ngrams}) <<< List.toUnfoldable
sumOccurrences' :: NgramsTable -> NgramsTerm -> Additive Int sumOccurrences' :: NgramsTable -> NgramsTerm -> Additive Int
sumOccurrences' ngramsTable label = sumOccurrences' ngramsTable label =
...@@ -701,11 +721,11 @@ sumOccurrences ngramsTable (NgramsElement {occurrences, children}) = ...@@ -701,11 +721,11 @@ sumOccurrences ngramsTable (NgramsElement {occurrences, children}) =
renderNgramsTree :: { ngrams :: NgramsTerm renderNgramsTree :: { ngrams :: NgramsTerm
, ngramsTable :: NgramsTable , ngramsTable :: NgramsTable
, ngramsStyle :: Array DOM.Props , ngramsStyle :: Array DOM.Props
, ngramsClick :: NgramsTerm -> Maybe (Effect Unit) , ngramsClick :: NgramsClick
} -> ReactElement } -> ReactElement
renderNgramsTree { ngramsTable, ngrams, ngramsStyle, ngramsClick } = renderNgramsTree { ngramsTable, ngrams, ngramsStyle, ngramsClick } =
ul [] [ ul [] [
span [className "tree"] [tree {ngramsTable, ngramsStyle, ngramsClick} ngrams] span [className "tree"] [tree {ngramsTable, ngramsStyle, ngramsClick} {ngrams, depth: 0}]
] ]
renderNgramsItem :: { ngrams :: NgramsTerm renderNgramsItem :: { ngrams :: NgramsTerm
...@@ -730,7 +750,7 @@ renderNgramsItem { ngramsTable, ngrams, ngramsElement, ngramsParent, dispatch } ...@@ -730,7 +750,7 @@ renderNgramsItem { ngramsTable, ngrams, ngramsElement, ngramsParent, dispatch }
Additive occurrences = sumOccurrences ngramsTable ngramsElement Additive occurrences = sumOccurrences ngramsTable ngramsElement
termList = ngramsElement ^. _NgramsElement <<< _list termList = ngramsElement ^. _NgramsElement <<< _list
ngramsStyle = [termStyle termList] ngramsStyle = [termStyle termList]
ngramsClick = Just <<< dispatch <<< SetParentResetChildren <<< Just ngramsClick = Just <<< dispatch <<< SetParentResetChildren <<< Just <<< view _ngrams
checkbox termList' = checkbox termList' =
let chkd = termList == termList' let chkd = termList == termList'
termList'' = if chkd then CandidateTerm else termList' termList'' = if chkd then CandidateTerm else termList'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment