2 Module : Gargantext.API.Ngrams
3 Description : Server API
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
13 get ngrams filtered by NgramsType
18 {-# LANGUAGE ConstraintKinds #-}
19 {-# LANGUAGE DataKinds #-}
20 {-# LANGUAGE DeriveGeneric #-}
21 {-# LANGUAGE NoImplicitPrelude #-}
22 {-# LANGUAGE OverloadedStrings #-}
23 {-# LANGUAGE ScopedTypeVariables #-}
24 {-# LANGUAGE TemplateHaskell #-}
25 {-# LANGUAGE TypeOperators #-}
26 {-# LANGUAGE FlexibleContexts #-}
27 {-# LANGUAGE FlexibleInstances #-}
28 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
29 {-# LANGUAGE MultiParamTypeClasses #-}
30 {-# LANGUAGE RankNTypes #-}
31 {-# LANGUAGE TypeFamilies #-}
32 {-# OPTIONS -fno-warn-orphans #-}
34 module Gargantext.API.Ngrams
37 import Prelude (Enum, Bounded, Semigroup(..), minBound, maxBound {-, round-}, error)
38 -- import Gargantext.Database.Schema.User (UserId)
39 import Data.Functor (($>))
40 import Data.Patch.Class (Replace, replace, Action(act), Applicable(..),
41 Composable(..), Transformable(..),
42 PairPatch(..), Patched, ConflictResolution,
43 ConflictResolutionReplace)
44 import qualified Data.Map.Strict.Patch as PM
46 --import Data.Semigroup
48 -- import Data.Maybe (isJust)
49 -- import Data.Tuple.Extra (first)
50 import qualified Data.Map.Strict as Map
51 import Data.Map.Strict (Map)
52 --import qualified Data.Set as Set
53 import Control.Category ((>>>))
54 import Control.Concurrent
55 import Control.Lens (makeLenses, makePrisms, Getter, Prism', prism', Iso', iso, from, (^..), (.~), (#), to, {-withIndex, folded, ifolded,-} view, (^.), (+~), (%~), at, _Just, Each(..), dropping, taking, itraverse_, (.=), both)
56 import Control.Monad (guard)
57 import Control.Monad.Error.Class (MonadError, throwError)
58 import Control.Monad.Reader
59 import Control.Monad.State
60 import Data.Aeson hiding ((.=))
61 import Data.Aeson.TH (deriveJSON)
62 import Data.Either(Either(Left))
63 -- import Data.Map (lookup)
64 import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
65 import Data.Swagger hiding (version, patch)
66 import Data.Text (Text)
68 import GHC.Generics (Generic)
69 import Gargantext.Core.Utils.Prefix (unPrefix)
70 import Gargantext.Database.Schema.Node (defaultList, HasNodeError)
71 -- import Gargantext.Database.Schema.Ngrams (NgramsTypeId, ngramsTypeId, NgramsTableData(..))
72 import Gargantext.Database.Schema.Ngrams (NgramsType)
73 import qualified Gargantext.Database.Schema.Ngrams as Ngrams
74 -- import Gargantext.Database.Schema.NodeNgram hiding (Action)
75 import Gargantext.Database.Utils (CmdM)
76 import Gargantext.Prelude
77 -- import Gargantext.Core.Types (ListTypeId, listTypeId)
78 import Gargantext.Core.Types (ListType(..), ListId, CorpusId, Limit, Offset)
79 import Servant hiding (Patch)
80 import Test.QuickCheck (elements)
81 import Test.QuickCheck.Arbitrary (Arbitrary, arbitrary)
83 ------------------------------------------------------------------------
84 --data FacetFormat = Table | Chart
85 data TabType = Docs | Terms | Sources | Authors | Institutes | Trash
87 deriving (Generic, Enum, Bounded)
89 instance FromHttpApiData TabType
91 parseUrlPiece "Docs" = pure Docs
92 parseUrlPiece "Terms" = pure Terms
93 parseUrlPiece "Sources" = pure Sources
94 parseUrlPiece "Institutes" = pure Institutes
95 parseUrlPiece "Authors" = pure Authors
96 parseUrlPiece "Trash" = pure Trash
98 parseUrlPiece "Contacts" = pure Contacts
100 parseUrlPiece _ = Left "Unexpected value of TabType"
102 instance ToParamSchema TabType
103 instance ToJSON TabType
104 instance FromJSON TabType
105 instance ToSchema TabType
106 instance Arbitrary TabType
108 arbitrary = elements [minBound .. maxBound]
110 newtype MSet a = MSet (Map a ())
111 deriving (Eq, Ord, Show, Generic, Arbitrary, Semigroup, Monoid)
113 instance ToJSON a => ToJSON (MSet a) where
114 toJSON (MSet m) = toJSON (Map.keys m)
115 toEncoding (MSet m) = toEncoding (Map.keys m)
117 mSetFromSet :: Set a -> MSet a
118 mSetFromSet = MSet . Map.fromSet (const ())
120 mSetFromList :: Ord a => [a] -> MSet a
121 mSetFromList = MSet . Map.fromList . map (\x -> (x, ()))
123 instance (Ord a, FromJSON a) => FromJSON (MSet a) where
124 parseJSON = fmap mSetFromList . parseJSON
126 instance (ToJSONKey a, ToSchema a) => ToSchema (MSet a) where
129 ------------------------------------------------------------------------
130 type NgramsTerm = Text
133 NgramsElement { _ne_ngrams :: NgramsTerm
134 , _ne_list :: ListType
135 , _ne_occurrences :: Int
136 , _ne_parent :: Maybe NgramsTerm
137 , _ne_children :: MSet NgramsTerm
139 deriving (Ord, Eq, Show, Generic)
141 deriveJSON (unPrefix "_ne_") ''NgramsElement
142 makeLenses ''NgramsElement
144 instance ToSchema NgramsElement
145 instance Arbitrary NgramsElement where
146 arbitrary = elements [NgramsElement "sport" GraphList 1 Nothing mempty]
148 ------------------------------------------------------------------------
149 newtype NgramsTable = NgramsTable [NgramsElement]
150 deriving (Ord, Eq, Generic, ToJSON, FromJSON, Show)
152 makePrisms ''NgramsTable
154 instance Each NgramsTable NgramsTable NgramsElement NgramsElement where
155 each = _NgramsTable . each
158 -- | TODO Check N and Weight
160 toNgramsElement :: [NgramsTableData] -> [NgramsElement]
161 toNgramsElement ns = map toNgramsElement' ns
163 toNgramsElement' (NgramsTableData _ p t _ lt w) = NgramsElement t lt' (round w) p' c'
167 Just x -> lookup x mapParent
168 c' = maybe mempty identity $ lookup t mapChildren
169 lt' = maybe (panic "API.Ngrams: listypeId") identity lt
171 mapParent :: Map Int Text
172 mapParent = Map.fromListWith (<>) $ map (\(NgramsTableData i _ t _ _ _) -> (i,t)) ns
174 mapChildren :: Map Text (Set Text)
175 mapChildren = Map.mapKeys (\i -> (maybe (panic "API.Ngrams.mapChildren: ParentId with no Terms: Impossible") identity $ lookup i mapParent))
176 $ Map.fromListWith (<>)
177 $ map (first fromJust)
178 $ filter (isJust . fst)
179 $ map (\(NgramsTableData _ p t _ _ _) -> (p, Set.singleton t)) ns
182 mockTable :: NgramsTable
183 mockTable = NgramsTable
184 [ NgramsElement "animal" GraphList 1 Nothing (mSetFromList ["dog", "cat"])
185 , NgramsElement "cat" GraphList 1 (Just "animal") mempty
186 , NgramsElement "cats" StopList 4 Nothing mempty
187 , NgramsElement "dog" GraphList 3 (Just "animal")(mSetFromList ["dogs"])
188 , NgramsElement "dogs" StopList 4 (Just "dog") mempty
189 , NgramsElement "fox" GraphList 1 Nothing mempty
190 , NgramsElement "object" CandidateList 2 Nothing mempty
191 , NgramsElement "nothing" StopList 4 Nothing mempty
192 , NgramsElement "organic" GraphList 3 Nothing (mSetFromList ["flower"])
193 , NgramsElement "flower" GraphList 3 (Just "organic") mempty
194 , NgramsElement "moon" CandidateList 1 Nothing mempty
195 , NgramsElement "sky" StopList 1 Nothing mempty
198 instance Arbitrary NgramsTable where
199 arbitrary = pure mockTable
201 instance ToSchema NgramsTable
203 ------------------------------------------------------------------------
204 type NgramsTableMap = Map NgramsTerm NgramsElement
206 ------------------------------------------------------------------------
207 -- On the Client side:
208 --data Action = InGroup NgramsId NgramsId
209 -- | OutGroup NgramsId NgramsId
210 -- | SetListType NgramsId ListType
212 data PatchSet a = PatchSet
216 deriving (Eq, Ord, Show, Generic)
218 makeLenses ''PatchSet
219 makePrisms ''PatchSet
221 instance ToJSON a => ToJSON (PatchSet a) where
222 toJSON = genericToJSON $ unPrefix "_"
223 toEncoding = genericToEncoding $ unPrefix "_"
225 instance (Ord a, FromJSON a) => FromJSON (PatchSet a) where
226 parseJSON = genericParseJSON $ unPrefix "_"
229 instance (Ord a, Arbitrary a) => Arbitrary (PatchSet a) where
230 arbitrary = PatchSet <$> arbitrary <*> arbitrary
232 type instance Patched (PatchSet a) = Set a
234 type ConflictResolutionPatchSet a = SimpleConflictResolution' (Set a)
235 type instance ConflictResolution (PatchSet a) = ConflictResolutionPatchSet a
237 instance Ord a => Semigroup (PatchSet a) where
238 p <> q = PatchSet { _rem = (q ^. rem) `Set.difference` (p ^. add) <> p ^. rem
239 , _add = (q ^. add) `Set.difference` (p ^. rem) <> p ^. add
242 instance Ord a => Monoid (PatchSet a) where
243 mempty = PatchSet mempty mempty
245 instance Ord a => Group (PatchSet a) where
246 invert (PatchSet r a) = PatchSet a r
248 instance Ord a => Composable (PatchSet a) where
249 composable _ _ = undefined
251 instance Ord a => Action (PatchSet a) (Set a) where
252 act p source = (source `Set.difference` (p ^. rem)) <> p ^. add
254 instance Applicable (PatchSet a) (Set a) where
255 applicable _ _ = mempty
257 instance Ord a => Validity (PatchSet a) where
258 validate p = check (Set.disjoint (p ^. rem) (p ^. add)) "_rem and _add should be dijoint"
260 instance Ord a => Transformable (PatchSet a) where
261 transformable = undefined
263 conflicts _p _q = undefined
265 transformWith conflict p q = undefined conflict p q
267 instance ToSchema a => ToSchema (PatchSet a)
270 type AddRem = Replace (Maybe ())
272 remPatch, addPatch :: AddRem
273 remPatch = replace (Just ()) Nothing
274 addPatch = replace Nothing (Just ())
276 isRem :: Replace (Maybe ()) -> Bool
277 isRem = (== remPatch)
279 type PatchMap = PM.PatchMap
281 newtype PatchMSet a = PatchMSet (PatchMap a AddRem)
282 deriving (Eq, Show, Generic, Validity, Semigroup, Monoid,
283 Transformable, Composable)
285 type ConflictResolutionPatchMSet a = a -> ConflictResolutionReplace (Maybe ())
286 type instance ConflictResolution (PatchMSet a) = ConflictResolutionPatchMSet a
288 -- TODO this breaks module abstraction
289 makePrisms ''PM.PatchMap
291 makePrisms ''PatchMSet
293 _PatchMSetIso :: Ord a => Iso' (PatchMSet a) (PatchSet a)
294 _PatchMSetIso = _PatchMSet . _PatchMap . iso f g . from _PatchSet
296 f :: Ord a => Map a (Replace (Maybe ())) -> (Set a, Set a)
297 f = Map.partition isRem >>> both %~ Map.keysSet
299 g :: Ord a => (Set a, Set a) -> Map a (Replace (Maybe ()))
300 g (rems, adds) = Map.fromSet (const remPatch) rems
301 <> Map.fromSet (const addPatch) adds
303 instance Ord a => Action (PatchMSet a) (MSet a) where
304 act (PatchMSet p) (MSet m) = MSet $ act p m
306 instance Ord a => Applicable (PatchMSet a) (MSet a) where
307 applicable (PatchMSet p) (MSet m) = applicable p m
309 instance (Ord a, ToJSON a) => ToJSON (PatchMSet a) where
310 toJSON = toJSON . view _PatchMSetIso
311 toEncoding = toEncoding . view _PatchMSetIso
313 instance (Ord a, FromJSON a) => FromJSON (PatchMSet a) where
314 parseJSON = fmap (_PatchMSetIso #) . parseJSON
316 instance (Ord a, Arbitrary a) => Arbitrary (PatchMSet a) where
317 arbitrary = (PatchMSet . PM.fromMap) <$> arbitrary
319 instance ToSchema a => ToSchema (PatchMSet a) where
321 declareNamedSchema _ = undefined
323 type instance Patched (PatchMSet a) = MSet a
325 instance (Eq a, Arbitrary a) => Arbitrary (Replace a) where
326 arbitrary = uncurry replace <$> arbitrary
327 -- If they happen to be equal then the patch is Keep.
329 instance ToSchema a => ToSchema (Replace a) where
330 declareNamedSchema (_ :: proxy (Replace a)) = do
331 -- TODO Keep constructor is not supported here.
332 aSchema <- declareSchemaRef (Proxy :: Proxy a)
333 return $ NamedSchema (Just "Replace") $ mempty
334 & type_ .~ SwaggerObject
336 InsOrdHashMap.fromList
340 & required .~ [ "old", "new" ]
343 NgramsPatch { _patch_children :: PatchMSet NgramsTerm
344 , _patch_list :: Replace ListType -- TODO Map UserId ListType
346 deriving (Eq, Show, Generic)
348 deriveJSON (unPrefix "_") ''NgramsPatch
349 makeLenses ''NgramsPatch
351 instance ToSchema NgramsPatch
353 instance Arbitrary NgramsPatch where
354 arbitrary = NgramsPatch <$> arbitrary <*> (replace <$> arbitrary <*> arbitrary)
356 type NgramsPatchIso = PairPatch (PatchMSet NgramsTerm) (Replace ListType)
358 _NgramsPatch :: Iso' NgramsPatch NgramsPatchIso
359 _NgramsPatch = iso (\(NgramsPatch c l) -> c :*: l) (\(c :*: l) -> NgramsPatch c l)
361 instance Semigroup NgramsPatch where
362 p <> q = _NgramsPatch # (p ^. _NgramsPatch <> q ^. _NgramsPatch)
364 instance Monoid NgramsPatch where
365 mempty = _NgramsPatch # mempty
367 instance Validity NgramsPatch where
368 validate p = p ^. _NgramsPatch . to validate
370 instance Transformable NgramsPatch where
371 transformable p q = transformable (p ^. _NgramsPatch) (q ^. _NgramsPatch)
373 conflicts p q = conflicts (p ^. _NgramsPatch) (q ^. _NgramsPatch)
375 transformWith conflict p q = (_NgramsPatch # p', _NgramsPatch # q')
377 (p', q') = transformWith conflict (p ^. _NgramsPatch) (q ^. _NgramsPatch)
379 type ConflictResolutionNgramsPatch =
380 ( ConflictResolutionPatchMSet NgramsTerm
381 , ConflictResolutionReplace ListType
383 type instance ConflictResolution NgramsPatch =
384 ConflictResolutionNgramsPatch
386 type PatchedNgramsPatch = (Set NgramsTerm, ListType)
387 -- ~ Patched NgramsPatchIso
388 type instance Patched NgramsPatch = PatchedNgramsPatch
390 instance Applicable NgramsPatch (Maybe NgramsElement) where
391 applicable p Nothing = check (p == mempty) "NgramsPatch should be empty here"
392 applicable p (Just ne) =
393 -- TODO how to patch _ne_parent ?
394 applicable (p ^. patch_children) (ne ^. ne_children) <>
395 applicable (p ^. patch_list) (ne ^. ne_list)
397 instance Action NgramsPatch NgramsElement where
398 act p = (ne_children %~ act (p ^. patch_children))
399 . (ne_list %~ act (p ^. patch_list))
401 instance Action NgramsPatch (Maybe NgramsElement) where
404 newtype NgramsTablePatch = NgramsTablePatch (PatchMap NgramsTerm NgramsPatch)
405 deriving (Eq, Show, Generic, ToJSON, FromJSON, Semigroup, Monoid, Validity, Transformable)
407 --instance (Ord k, Action pv (Maybe v)) => Action (PatchMap k pv) (Map k v) where
409 type instance ConflictResolution NgramsTablePatch =
410 NgramsTerm -> ConflictResolutionNgramsPatch
412 type PatchedNgramsTablePatch = Map NgramsTerm PatchedNgramsPatch
413 -- ~ Patched (PatchMap NgramsTerm NgramsPatch)
414 type instance Patched NgramsTablePatch = PatchedNgramsTablePatch
416 makePrisms ''NgramsTablePatch
417 instance ToSchema (PatchMap NgramsTerm NgramsPatch)
418 instance ToSchema NgramsTablePatch
420 instance Applicable NgramsTablePatch (Maybe NgramsTableMap) where
421 applicable p = applicable (p ^. _NgramsTablePatch)
423 instance Action NgramsTablePatch (Maybe NgramsTableMap) where
425 fmap (execState (reParentNgramsTablePatch p)) .
426 act (p ^. _NgramsTablePatch)
428 instance Arbitrary NgramsTablePatch where
429 arbitrary = NgramsTablePatch <$> PM.fromMap <$> arbitrary
431 -- Should it be less than an Lens' to preserve PatchMap's abstraction.
432 -- ntp_ngrams_patches :: Lens' NgramsTablePatch (Map NgramsTerm NgramsPatch)
433 -- ntp_ngrams_patches = _NgramsTablePatch . undefined
435 type ReParent a = forall m. MonadState NgramsTableMap m => a -> m ()
437 reParent :: Maybe NgramsTerm -> ReParent NgramsTerm
438 reParent parent child = at child . _Just . ne_parent .= parent
440 reParentAddRem :: NgramsTerm -> NgramsTerm -> ReParent AddRem
441 reParentAddRem parent child p =
442 reParent (if isRem p then Nothing else Just parent) child
444 reParentNgramsPatch :: NgramsTerm -> ReParent NgramsPatch
445 reParentNgramsPatch parent ngramsPatch =
446 itraverse_ (reParentAddRem parent) (ngramsPatch ^. patch_children . _PatchMSet . _PatchMap)
447 -- TODO FoldableWithIndex/TraversableWithIndex for PatchMap
449 reParentNgramsTablePatch :: ReParent NgramsTablePatch
450 reParentNgramsTablePatch p = itraverse_ reParentNgramsPatch (p ^. _NgramsTablePatch. _PatchMap)
451 -- TODO FoldableWithIndex/TraversableWithIndex for PatchMap
453 ------------------------------------------------------------------------
454 ------------------------------------------------------------------------
457 data Versioned a = Versioned
458 { _v_version :: Version
462 deriveJSON (unPrefix "_v_") ''Versioned
463 makeLenses ''Versioned
464 instance ToSchema a => ToSchema (Versioned a)
465 instance Arbitrary a => Arbitrary (Versioned a) where
466 arbitrary = Versioned 1 <$> arbitrary -- TODO 1 is constant so far
469 -- TODO sequencs of modifications (Patchs)
470 type NgramsIdPatch = Patch NgramsId NgramsPatch
472 ngramsPatch :: Int -> NgramsPatch
473 ngramsPatch n = NgramsPatch (DM.fromList [(1, StopList)]) (Set.fromList [n]) Set.empty
475 toEdit :: NgramsId -> NgramsPatch -> Edit NgramsId NgramsPatch
476 toEdit n p = Edit n p
477 ngramsIdPatch :: Patch NgramsId NgramsPatch
478 ngramsIdPatch = fromList $ catMaybes $ reverse [ replace (1::NgramsId) (Just $ ngramsPatch 1) Nothing
479 , replace (1::NgramsId) Nothing (Just $ ngramsPatch 2)
480 , replace (2::NgramsId) Nothing (Just $ ngramsPatch 2)
483 -- applyPatchBack :: Patch -> IO Patch
484 -- isEmptyPatch = Map.all (\x -> Set.isEmpty (add_children x) && Set.isEmpty ... )
486 ------------------------------------------------------------------------
487 ------------------------------------------------------------------------
488 ------------------------------------------------------------------------
490 type TableNgramsApiGet = Summary " Table Ngrams API Get"
491 :> QueryParam "ngramsType" TabType
492 :> QueryParam "list" ListId
493 :> QueryParam "limit" Limit
494 :> QueryParam "offset" Offset
495 :> Get '[JSON] (Versioned NgramsTable)
497 type TableNgramsApi = Summary " Table Ngrams API Change"
498 :> QueryParam "ngramsType" TabType
499 :> QueryParam "list" ListId
500 :> ReqBody '[JSON] (Versioned NgramsTablePatch)
501 :> Put '[JSON] (Versioned NgramsTablePatch)
503 data NgramError = UnsupportedVersion
506 class HasNgramError e where
507 _NgramError :: Prism' e NgramError
509 instance HasNgramError ServantErr where
510 _NgramError = prism' make match
512 err = err500 { errBody = "NgramError: Unsupported version" }
513 make UnsupportedVersion = err
514 match e = guard (e == err) $> UnsupportedVersion
516 ngramError :: (MonadError e m, HasNgramError e) => NgramError -> m a
517 ngramError nne = throwError $ _NgramError # nne
520 -- TODO: Replace.old is ignored which means that if the current list
521 -- `GraphList` and that the patch is `Replace CandidateList StopList` then
522 -- the list is going to be `StopList` while it should keep `GraphList`.
523 -- However this should not happen in non conflicting situations.
524 mkListsUpdate :: NgramsType -> NgramsTablePatch -> [(NgramsTypeId, NgramsTerm, ListTypeId)]
525 mkListsUpdate nt patches =
526 [ (ngramsTypeId nt, ng, listTypeId lt)
527 | (ng, patch) <- patches ^.. ntp_ngrams_patches . ifolded . withIndex
528 , lt <- patch ^.. patch_list . new
531 mkChildrenGroups :: (PatchSet NgramsTerm -> Set NgramsTerm)
534 -> [(NgramsTypeId, NgramsParent, NgramsChild)]
535 mkChildrenGroups addOrRem nt patches =
536 [ (ngramsTypeId nt, parent, child)
537 | (parent, patch) <- patches ^.. ntp_ngrams_patches . ifolded . withIndex
538 , child <- patch ^.. patch_children . to addOrRem . folded
542 ngramsTypeFromTabType :: Maybe TabType -> NgramsType
543 ngramsTypeFromTabType maybeTabType =
544 let lieu = "Garg.API.Ngrams: " :: Text in
546 Nothing -> panic (lieu <> "Indicate the Table")
547 Just tab -> case tab of
548 Sources -> Ngrams.Sources
549 Authors -> Ngrams.Authors
550 Institutes -> Ngrams.Institutes
551 Terms -> Ngrams.NgramsTerms
552 _ -> panic $ lieu <> "No Ngrams for this tab"
554 ------------------------------------------------------------------------
556 { _r_version :: Version
559 -- ^ first patch in the list is the most recent
564 initRepo :: Monoid s => Repo s p
565 initRepo = Repo 1 mempty []
567 type NgramsState = Map ListId (Map NgramsType NgramsTableMap)
568 type NgramsStatePatch = PatchMap ListId (PatchMap NgramsType NgramsTablePatch)
569 type NgramsRepo = Repo NgramsState NgramsStatePatch
571 initMockRepo :: NgramsRepo
572 initMockRepo = Repo 1 s []
575 $ Map.singleton Ngrams.NgramsTerms
577 [ (n ^. ne_ngrams, n) | n <- mockTable ^. _NgramsTable ]
579 class HasRepoVar env where
580 repoVar :: Getter env (MVar NgramsRepo)
582 instance HasRepoVar (MVar NgramsRepo) where
585 type RepoCmdM env err m =
590 ------------------------------------------------------------------------
592 listTypeConflictResolution :: ListType -> ListType -> ListType
593 listTypeConflictResolution _ _ = undefined -- TODO Use Map User ListType
595 ngramsStatePatchConflictResolution
596 :: ListId -> NgramsType -> NgramsTerm
597 -> ConflictResolutionNgramsPatch
598 ngramsStatePatchConflictResolution _listId _ngramsType _ngramsTerm
599 = (undefined {- TODO think this through -}, listTypeConflictResolution)
601 class HasInvalidError e where
602 _InvalidError :: Prism' e Validation
604 instance HasInvalidError ServantErr where
605 _InvalidError = undefined {-prism' make match
607 err = err500 { errBody = "InvalidError" }
609 match e = guard (e == err) $> UnsupportedVersion-}
611 assertValid :: (MonadError e m, HasInvalidError e) => Validation -> m ()
612 assertValid v = when (not $ validationIsValid v) $ throwError $ _InvalidError # v
615 -- Insertions are not considered as patches,
616 -- they do not extend history,
617 -- they do not bump version.
618 insertNewOnly :: a -> Maybe a -> Maybe a
619 insertNewOnly a = maybe (Just a) (const $ error "insertNewOnly: impossible")
620 -- TODO error handling
622 insertNewListOfNgramsElements :: RepoCmdM env err m => ListId
623 -> Map NgramsType [NgramsElement] -> m ()
624 insertNewListOfNgramsElements listId m = do
626 liftIO $ modifyMVar_ var $ pure . (r_state . at listId %~ insertNewOnly m')
628 m' = (Map.fromList . fmap (\n -> (n ^. ne_ngrams, n))) <$> m
630 -- Apply the given patch to the DB and returns the patch to be applied on the
633 -- In this perliminary version the OT aspect is missing, therefore the version
634 -- number is always 1 and the returned patch is always empty.
635 tableNgramsPatch :: (HasNgramError err, HasNodeError err, HasInvalidError err,
637 => CorpusId -> Maybe TabType -> Maybe ListId
638 -> Versioned NgramsTablePatch
639 -> m (Versioned NgramsTablePatch)
640 tableNgramsPatch corpusId maybeTabType maybeList (Versioned p_version p_table) = do
641 let ngramsType = ngramsTypeFromTabType maybeTabType
642 listId <- maybe (defaultList corpusId) pure maybeList
643 let (p0, p0_validity) = PM.singleton ngramsType p_table
644 let (p, p_validity) = PM.singleton listId p0
646 assertValid p0_validity
647 assertValid p_validity
650 (p'_applicable, vq') <- liftIO $ modifyMVar var $ \r ->
652 q = mconcat $ take (r ^. r_version - p_version) (r ^. r_history)
653 (p', q') = transformWith ngramsStatePatchConflictResolution p q
654 r' = r & r_version +~ 1
656 & r_history %~ (p' :)
657 q'_table = q' ^. _PatchMap . at listId . _Just . _PatchMap . at ngramsType . _Just
658 p'_applicable = applicable p' (r ^. r_state)
660 pure (r', (p'_applicable, Versioned (r' ^. r_version) q'_table))
661 assertValid p'_applicable
665 when (version /= 1) $ ngramError UnsupportedVersion
666 updateNodeNgrams $ NodeNgramsUpdate
667 { _nnu_user_list_id = listId
668 , _nnu_lists_update = mkListsUpdate ngramsType patch
669 , _nnu_rem_children = mkChildrenGroups _rem ngramsType patch
670 , _nnu_add_children = mkChildrenGroups _add ngramsType patch
672 pure $ Versioned 1 mempty
675 -- | TODO Errors management
676 -- TODO: polymorphic for Annuaire or Corpus or ...
677 getTableNgrams :: RepoCmdM env err m
678 => CorpusId -> Maybe TabType
679 -> Maybe ListId -> Maybe Limit -> Maybe Offset
680 -- -> Maybe MinSize -> Maybe MaxSize
682 -- -> Maybe Text -- full text search
683 -> m (Versioned NgramsTable)
684 getTableNgrams cId maybeTabType maybeListId mlimit moffset = do
685 let ngramsType = ngramsTypeFromTabType maybeTabType
686 listId <- maybe (defaultList cId) pure maybeListId
689 defaultLimit = 10 -- TODO
690 limit_ = maybe defaultLimit identity mlimit
691 offset_ = maybe 0 identity moffset
694 repo <- liftIO $ readMVar v
696 let ngrams = repo ^.. r_state
698 . at ngramsType . _Just
699 . taking limit_ (dropping offset_ each)
701 pure $ Versioned (repo ^. r_version) (NgramsTable ngrams)
705 Ngrams.getNgramsTableDb NodeDocument ngramsType (Ngrams.NgramsTableParam listId cId) limit_ offset_
707 -- printDebug "ngramsTableDatas" ngramsTableDatas
709 pure $ Versioned 1 $ NgramsTable (toNgramsElement ngramsTableDatas)