2 Module : Gargantext.Database.Flow
3 Description : Database Flow
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
12 {-# LANGUAGE QuasiQuotes #-}
13 {-# LANGUAGE Arrows #-}
15 module Gargantext.Database.Action.Flow.Pairing
19 import Control.Lens (_Just, (^.))
20 import Data.Map (Map, fromList, fromListWith)
21 import Data.Maybe (catMaybes, fromMaybe)
23 import Data.Text (Text)
24 import Gargantext.API.Ngrams.Tools
25 import Gargantext.API.Prelude (GargNoServer)
26 import Gargantext.Core.Types (TableResult(..), Term)
27 import Gargantext.Core.Types.Main
28 import Gargantext.Database
29 import Gargantext.Database.Action.Metrics.NgramsByNode (getNodesByNgramsOnlyUser)
30 import Gargantext.Database.Admin.Config
31 import Gargantext.Database.Admin.Config (nodeTypeId)
32 import Gargantext.Database.Admin.Types.Hyperdata -- (HyperdataContact(..))
33 import Gargantext.Database.Admin.Types.Node -- (AnnuaireId, CorpusId, ListId, DocId, ContactId, NodeId)
34 import Gargantext.Database.Prelude (Cmd, runOpaQuery)
35 import Gargantext.Database.Query.Prelude (leftJoin2, returnA, queryNodeNodeTable)
36 import Gargantext.Database.Query.Table.Node.Children (getAllContacts)
37 import Gargantext.Database.Query.Table.Node.Select (selectNodesWithUsername)
38 import Gargantext.Database.Query.Table.Node (defaultList)
39 import Gargantext.Database.Query.Table.NodeNode (insertNodeNode)
40 import Gargantext.Database.Schema.Ngrams -- (NgramsType(..))
41 import Gargantext.Database.Schema.Node
42 import Gargantext.Prelude hiding (sum)
45 import qualified Data.List as List
46 import qualified Data.Map as Map
47 import qualified Data.Set as Set
48 import qualified Data.Text as DT
52 -- All NodeAnnuaire paired with a Corpus of NodeId nId:
53 -- isPairedWith NodeAnnuaire corpusId
54 isPairedWith :: NodeId -> NodeType -> Cmd err [NodeId]
55 isPairedWith nId nt = runOpaQuery (selectQuery nt nId)
57 selectQuery :: NodeType -> NodeId -> Query (Column PGInt4)
58 selectQuery nt' nId' = proc () -> do
59 (node, node_node) <- queryJoin -< ()
60 restrict -< (node^.node_typename) .== (pgInt4 $ nodeTypeId nt')
61 restrict -< (node_node^.nn_node1_id) .== (toNullable $ pgNodeId nId')
62 returnA -< node^.node_id
64 queryJoin :: Query (NodeRead, NodeNodeReadNull)
65 queryJoin = leftJoin2 queryNodeTable queryNodeNodeTable cond
67 cond (node, node_node) = node^.node_id .== node_node^. nn_node2_id
69 -----------------------------------------------------------------------
70 pairing :: AnnuaireId -> CorpusId -> Maybe ListId -> GargNoServer Int
73 Nothing -> defaultList c
75 dataPaired <- dataPairing a (c,l,Authors) takeName takeName
76 r <- insertDB $ prepareInsert dataPaired
77 _ <- insertNodeNode [ NodeNode c a Nothing Nothing]
81 dataPairing :: AnnuaireId
82 -> (CorpusId, ListId, NgramsType)
83 -> (ContactName -> Projected)
84 -> (DocAuthor -> Projected)
85 -> GargNoServer (Map ContactId (Set DocId))
86 dataPairing aId (cId, lId, ngt) fc fa = do
87 mc <- getNgramsContactId aId
88 md <- getNgramsDocId cId lId ngt
90 printDebug "ngramsContactId" mc
91 printDebug "ngramsDocId" md
93 from = projectionFrom (Set.fromList $ Map.keys mc) fc
94 to = projectionTo (Set.fromList $ Map.keys md) fa
96 pure $ fusion mc $ align from to md
100 prepareInsert :: Map ContactId (Set DocId) -> [NodeNode]
101 prepareInsert m = map (\(n1,n2) -> NodeNode n1 n2 Nothing Nothing)
103 $ map (\(contactId, setDocIds)
105 -> (contactId, setDocId)
106 ) $ Set.toList setDocIds
110 ------------------------------------------------------------------------
111 type ContactName = Text
112 type DocAuthor = Text
113 type Projected = Text
115 projectionFrom :: Set ContactName -> (ContactName -> Projected) -> Map ContactName Projected
116 projectionFrom ss f = fromList $ map (\s -> (s, f s)) (Set.toList ss)
118 projectionTo :: Set DocAuthor -> (DocAuthor -> Projected) -> Map Projected (Set DocAuthor)
119 projectionTo ss f = fromListWith (<>) $ map (\s -> (f s, Set.singleton s)) (Set.toList ss)
120 ------------------------------------------------------------------------
121 takeName :: Term -> Term
122 takeName texte = DT.toLower texte'
124 texte' = maybe texte (\x -> if DT.length x > 3 then x else texte)
126 lastName' = lastMay . DT.splitOn " "
129 ------------------------------------------------------------------------
130 align :: Map ContactName Projected
131 -> Map Projected (Set DocAuthor)
132 -> Map DocAuthor (Set DocId)
133 -> Map ContactName (Set DocId)
134 align mc ma md = fromListWith (<>)
135 $ map (\c -> (c, getProjection md $ testProjection c mc ma))
138 getProjection :: Map DocAuthor (Set DocId) -> Set DocAuthor -> Set DocId
139 getProjection ma' sa' =
142 else Set.unions $ sets ma' sa'
144 sets ma'' sa'' = Set.map (\s -> lookup s ma'') sa''
145 lookup s' ma''= fromMaybe Set.empty (Map.lookup s' ma'')
147 testProjection :: ContactName
148 -> Map ContactName Projected
149 -> Map Projected (Set DocAuthor)
151 testProjection cn' mc' ma' = case Map.lookup cn' mc' of
153 Just c -> case Map.lookup c ma' of
157 fusion :: Map ContactName (Set ContactId)
158 -> Map ContactName (Set DocId)
159 -> Map ContactId (Set DocId)
160 fusion mc md = Map.fromListWith (<>)
162 $ [ (,) <$> Just cId <*> Map.lookup cn md
163 | (cn, setContactId) <- Map.toList mc
164 , cId <- Set.toList setContactId
166 ------------------------------------------------------------------------
168 getNgramsContactId :: AnnuaireId
169 -> Cmd err (Map ContactName (Set NodeId))
170 getNgramsContactId aId = do
171 contacts <- getAllContacts aId
172 pure $ fromListWith (<>)
174 $ map (\contact -> (,) <$> contact^.(node_hyperdata . hc_who . _Just . cw_lastName)
175 <*> Just ( Set.singleton (contact^.node_id))
179 getNgramsDocId :: CorpusId
182 -> GargNoServer (Map DocAuthor (Set NodeId))
183 getNgramsDocId cId lId nt = do
185 lIds <- selectNodesWithUsername NodeList userMaster
186 let ngs = filterListWithRoot MapTerm $ mapTermListRoot [lId] nt repo
188 groupNodesByNgrams ngs
189 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)