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.Schema.Ngrams -- (NgramsType(..))
39 import Gargantext.Database.Schema.Node
40 import Gargantext.Prelude hiding (sum)
43 import qualified Data.List as List
44 import qualified Data.Map as Map
45 import qualified Data.Set as Set
46 import qualified Data.Text as DT
50 -- All NodeAnnuaire paired with a Corpus of NodeId nId:
51 -- isPairedWith NodeAnnuaire corpusId
52 isPairedWith :: NodeType -> NodeId -> Cmd err [NodeId]
53 isPairedWith nt nId = runOpaQuery (selectQuery nt nId)
55 selectQuery :: NodeType -> NodeId -> Query (Column PGInt4)
56 selectQuery nt' nId' = proc () -> do
57 (node, node_node) <- queryJoin -< ()
58 restrict -< (node^.node_typename) .== (pgInt4 $ nodeTypeId nt')
59 restrict -< (node_node^.nn_node1_id) .== (toNullable $ pgNodeId nId')
60 returnA -< node^.node_id
62 queryJoin :: Query (NodeRead, NodeNodeReadNull)
63 queryJoin = leftJoin2 queryNodeTable queryNodeNodeTable cond
65 cond (node, node_node) = node^.node_id .== node_node^. nn_node2_id
70 -----------------------------------------------------------------------
71 pairing :: AnnuaireId -> CorpusId -> ListId -> GargNoServer Int
73 dataPaired <- dataPairing a (c,l,Authors) takeName takeName
74 insertDB $ prepareInsert dataPaired
77 dataPairing :: AnnuaireId
78 -> (CorpusId, ListId, NgramsType)
79 -> (ContactName -> Projected)
80 -> (DocAuthor -> Projected)
81 -> GargNoServer (Map ContactId (Set DocId))
82 dataPairing aId (cId, lId, ngt) fc fa = do
83 mc <- getNgramsContactId aId
84 md <- getNgramsDocId cId lId ngt
86 printDebug "ngramsContactId" mc
87 printDebug "ngramsDocId" md
89 from = projectionFrom (Set.fromList $ Map.keys mc) fc
90 to = projectionTo (Set.fromList $ Map.keys md) fa
92 pure $ fusion mc $ align from to md
96 prepareInsert :: Map ContactId (Set DocId) -> [NodeNode]
97 prepareInsert m = map (\(n1,n2) -> NodeNode n1 n2 Nothing Nothing)
99 $ map (\(contactId, setDocIds)
101 -> (contactId, setDocId)
102 ) $ Set.toList setDocIds
106 ------------------------------------------------------------------------
107 type ContactName = Text
108 type DocAuthor = Text
109 type Projected = Text
111 projectionFrom :: Set ContactName -> (ContactName -> Projected) -> Map ContactName Projected
112 projectionFrom ss f = fromList $ map (\s -> (s, f s)) (Set.toList ss)
114 projectionTo :: Set DocAuthor -> (DocAuthor -> Projected) -> Map Projected (Set DocAuthor)
115 projectionTo ss f = fromListWith (<>) $ map (\s -> (f s, Set.singleton s)) (Set.toList ss)
116 ------------------------------------------------------------------------
117 takeName :: Term -> Term
118 takeName texte = DT.toLower texte'
120 texte' = maybe texte (\x -> if DT.length x > 3 then x else texte)
122 lastName' = lastMay . DT.splitOn " "
125 ------------------------------------------------------------------------
126 align :: Map ContactName Projected
127 -> Map Projected (Set DocAuthor)
128 -> Map DocAuthor (Set DocId)
129 -> Map ContactName (Set DocId)
130 align mc ma md = fromListWith (<>)
131 $ map (\c -> (c, getProjection md $ testProjection c mc ma))
134 getProjection :: Map DocAuthor (Set DocId) -> Set DocAuthor -> Set DocId
135 getProjection ma' sa' =
138 else Set.unions $ sets ma' sa'
140 sets ma'' sa'' = Set.map (\s -> lookup s ma'') sa''
141 lookup s' ma''= fromMaybe Set.empty (Map.lookup s' ma'')
143 testProjection :: ContactName
144 -> Map ContactName Projected
145 -> Map Projected (Set DocAuthor)
147 testProjection cn' mc' ma' = case Map.lookup cn' mc' of
149 Just c -> case Map.lookup c ma' of
153 fusion :: Map ContactName (Set ContactId)
154 -> Map ContactName (Set DocId)
155 -> Map ContactId (Set DocId)
156 fusion mc md = Map.fromListWith (<>)
158 $ [ (,) <$> Just cId <*> Map.lookup cn md
159 | (cn, setContactId) <- Map.toList mc
160 , cId <- Set.toList setContactId
162 ------------------------------------------------------------------------
164 getNgramsContactId :: AnnuaireId
165 -> Cmd err (Map ContactName (Set NodeId))
166 getNgramsContactId aId = do
167 contacts <- getAllContacts aId
168 pure $ fromListWith (<>)
170 $ map (\contact -> (,) <$> contact^.(node_hyperdata . hc_who . _Just . cw_lastName)
171 <*> Just ( Set.singleton (contact^.node_id))
175 getNgramsDocId :: CorpusId
178 -> GargNoServer (Map DocAuthor (Set NodeId))
179 getNgramsDocId cId lId nt = do
181 lIds <- selectNodesWithUsername NodeList userMaster
182 let ngs = filterListWithRoot MapTerm $ mapTermListRoot [lId] nt repo
184 groupNodesByNgrams ngs
185 <$> getNodesByNgramsOnlyUser cId (lIds <> [lId]) nt (Map.keys ngs)