From 04533163374c8a7451b4c18916e021553d877a63 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Przemys=C5=82aw=20Kaminski?= <pk@intrepidus.pl>
Date: Fri, 14 Apr 2023 13:00:28 +0200
Subject: [PATCH] [NLP] add support for arbitrary languages in INI file

Also, added a basic GraphQL endpoint.
---
 gargantext.cabal                              |  8 +++--
 package.yaml                                  |  3 +-
 src/Gargantext/API/GraphQL.hs                 |  3 ++
 src/Gargantext/API/GraphQL/NLP.hs             | 29 ++++++++++++++++++
 src/Gargantext/API/Node/Corpus/New.hs         |  2 +-
 src/Gargantext/API/Node/Corpus/Searx.hs       |  5 ++++
 src/Gargantext/API/Node/Types.hs              |  2 +-
 src/Gargantext/Core.hs                        | 30 +++++++++++++++----
 src/Gargantext/Core/NLP.hs                    | 10 ++++---
 src/Gargantext/Core/Text/Learn.hs             | 13 +++++---
 .../Core/Text/Samples/{CH.hs => CN.hs}        |  5 ++--
 .../Core/Text/Samples/{SP.hs => ES.hs}        |  8 ++---
 src/Gargantext/Core/Text/Samples/PL.hs        | 22 ++++++++++++++
 src/Gargantext/Utils/JohnSnowNLP.hs           | 21 +++++++++++++
 stack.yaml                                    |  4 +--
 15 files changed, 136 insertions(+), 29 deletions(-)
 create mode 100644 src/Gargantext/API/GraphQL/NLP.hs
 rename src/Gargantext/Core/Text/Samples/{CH.hs => CN.hs} (93%)
 rename src/Gargantext/Core/Text/Samples/{SP.hs => ES.hs} (83%)
 create mode 100644 src/Gargantext/Core/Text/Samples/PL.hs

diff --git a/gargantext.cabal b/gargantext.cabal
index cbb5bf8e..2b3554b3 100644
--- a/gargantext.cabal
+++ b/gargantext.cabal
@@ -5,7 +5,7 @@ cabal-version: 1.12
 -- see: https://github.com/sol/hpack
 
 name:           gargantext
-version: 0.0.6.9.8.6
+version:        0.0.6.9.8.6
 synopsis:       Search, map, share
 description:    Please see README.md
 category:       Data
@@ -126,6 +126,7 @@ library
       Gargantext.API.GraphQL.AsyncTask
       Gargantext.API.GraphQL.Context
       Gargantext.API.GraphQL.IMT
+      Gargantext.API.GraphQL.NLP
       Gargantext.API.GraphQL.Node
       Gargantext.API.GraphQL.Team
       Gargantext.API.GraphQL.TreeFirstLevel
@@ -213,11 +214,12 @@ library
       Gargantext.Core.Text.Metrics.FrequentItemSet
       Gargantext.Core.Text.Metrics.SpeGen.IncExc
       Gargantext.Core.Text.Metrics.Utils
-      Gargantext.Core.Text.Samples.CH
+      Gargantext.Core.Text.Samples.CN
       Gargantext.Core.Text.Samples.DE
       Gargantext.Core.Text.Samples.EN
+      Gargantext.Core.Text.Samples.ES
       Gargantext.Core.Text.Samples.FR
-      Gargantext.Core.Text.Samples.SP
+      Gargantext.Core.Text.Samples.PL
       Gargantext.Core.Text.Terms.Mono.Stem
       Gargantext.Core.Text.Terms.Mono.Stem.En
       Gargantext.Core.Text.Terms.Mono.Token
diff --git a/package.yaml b/package.yaml
index 1c0caedc..5101fc75 100644
--- a/package.yaml
+++ b/package.yaml
@@ -389,7 +389,7 @@ executables:
       - split
       - unordered-containers
       - cryptohash
-      - time 
+      - time
 
   gargantext-import:
     main: Main.hs
@@ -559,4 +559,3 @@ tests:
 #    - OverloadedStrings
 #    - RankNTypes
 #
-
diff --git a/src/Gargantext/API/GraphQL.hs b/src/Gargantext/API/GraphQL.hs
index 3e12217e..4ab8b29b 100644
--- a/src/Gargantext/API/GraphQL.hs
+++ b/src/Gargantext/API/GraphQL.hs
@@ -38,6 +38,7 @@ import qualified Gargantext.API.GraphQL.Annuaire as GQLA
 import qualified Gargantext.API.GraphQL.AsyncTask as GQLAT
 import qualified Gargantext.API.GraphQL.Context as GQLCTX
 import qualified Gargantext.API.GraphQL.IMT as GQLIMT
+import qualified Gargantext.API.GraphQL.NLP as GQLNLP
 import qualified Gargantext.API.GraphQL.Node as GQLNode
 import qualified Gargantext.API.GraphQL.User as GQLUser
 import qualified Gargantext.API.GraphQL.UserInfo as GQLUserInfo
@@ -70,6 +71,7 @@ data Query m
     , contexts_for_ngrams :: GQLCTX.ContextsForNgramsArgs -> m [GQLCTX.ContextGQL]
     , imt_schools         :: GQLIMT.SchoolsArgs -> m [GQLIMT.School]
     , job_logs            :: GQLAT.JobLogArgs -> m (Map Int JobLog)
+    , languages           :: GQLNLP.LanguagesArgs -> m [GQLNLP.Lang]
     , nodes               :: GQLNode.NodeArgs -> m [GQLNode.Node]
     , node_parent         :: GQLNode.NodeParentArgs -> m [GQLNode.Node]
     , user_infos          :: GQLUserInfo.UserInfoArgs -> m [GQLUserInfo.UserInfo]
@@ -112,6 +114,7 @@ rootResolver =
                             , contexts_for_ngrams = GQLCTX.resolveContextsForNgrams
                             , imt_schools         = GQLIMT.resolveSchools
                             , job_logs            = GQLAT.resolveJobLogs
+                            , languages           = GQLNLP.resolveLanguages
                             , nodes               = GQLNode.resolveNodes
                             , node_parent         = GQLNode.resolveNodeParent
                             , user_infos          = GQLUserInfo.resolveUserInfos
diff --git a/src/Gargantext/API/GraphQL/NLP.hs b/src/Gargantext/API/GraphQL/NLP.hs
new file mode 100644
index 00000000..34119c7c
--- /dev/null
+++ b/src/Gargantext/API/GraphQL/NLP.hs
@@ -0,0 +1,29 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+
+module Gargantext.API.GraphQL.NLP
+  ( Lang(..)
+  , LanguagesArgs(..)
+  , resolveLanguages
+  )
+  where
+
+import Data.Morpheus.Types
+  ( GQLType
+  , Resolver
+  , QUERY
+  )
+import Gargantext.API.Prelude (GargM, GargError)
+import Gargantext.Core (Lang(..), allLangs)
+import Gargantext.Prelude
+import GHC.Generics (Generic)
+
+data LanguagesArgs
+  = LanguagesArgs
+    { } deriving (Generic, GQLType)
+
+type GqlM e env = Resolver QUERY e (GargM env GargError)
+
+resolveLanguages
+  :: LanguagesArgs -> GqlM e env [Lang]
+resolveLanguages LanguagesArgs { } = pure $ allLangs
diff --git a/src/Gargantext/API/Node/Corpus/New.hs b/src/Gargantext/API/Node/Corpus/New.hs
index 384e2acd..8874bd00 100644
--- a/src/Gargantext/API/Node/Corpus/New.hs
+++ b/src/Gargantext/API/Node/Corpus/New.hs
@@ -43,7 +43,7 @@ import Gargantext.API.Node.Corpus.New.Types
 import Gargantext.API.Node.Corpus.Searx
 import Gargantext.API.Node.Corpus.Types
 import Gargantext.API.Node.Types
-import Gargantext.Core (Lang(..){-, allLangs-})
+import Gargantext.Core (Lang(..))
 import Gargantext.Core.Text.List.Social (FlowSocialListWith(..))
 import Gargantext.Core.Types.Individu (User(..))
 import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger)
diff --git a/src/Gargantext/API/Node/Corpus/Searx.hs b/src/Gargantext/API/Node/Corpus/Searx.hs
index 2f397d8e..ce3718c5 100644
--- a/src/Gargantext/API/Node/Corpus/Searx.hs
+++ b/src/Gargantext/API/Node/Corpus/Searx.hs
@@ -47,6 +47,11 @@ import qualified Gargantext.Database.Query.Table.Node.Document.Add  as Doc  (add
 langToSearx :: Lang -> Text
 langToSearx EN = "en-US"
 langToSearx FR = "fr-FR"
+langToSearx DE = "de-FR"
+langToSearx ES = "es-FR"
+langToSearx IT = "it-FR"
+langToSearx PL = "pl-FR"
+langToSearx CN = "cn-FR"
 langToSearx All = "en-US"
 
 data SearxResult = SearxResult
diff --git a/src/Gargantext/API/Node/Types.hs b/src/Gargantext/API/Node/Types.hs
index 804b1984..07598044 100644
--- a/src/Gargantext/API/Node/Types.hs
+++ b/src/Gargantext/API/Node/Types.hs
@@ -15,7 +15,7 @@ import GHC.Generics (Generic)
 import Servant.Job.Utils (jsonOptions)
 import Web.FormUrlEncoded          (FromForm, ToForm)
 
-import Gargantext.Core (Lang(..){-, allLangs-})
+import Gargantext.Core (Lang(..))
 import Gargantext.Core.Utils.Prefix (unPrefixSwagger)
 import Gargantext.Prelude
 import qualified Gargantext.Database.GargDB as GargDB
diff --git a/src/Gargantext/Core.hs b/src/Gargantext/Core.hs
index b4f15771..dec21062 100644
--- a/src/Gargantext/Core.hs
+++ b/src/Gargantext/Core.hs
@@ -39,19 +39,26 @@ import Servant.API
 --  ... add your language and help us to implement it (:
 
 -- | All languages supported
--- TODO : DE | SP | CH
-data Lang = EN | FR | All
-  deriving (Show, Eq, Ord, Bounded, Enum, Generic, GQLType)
+-- NOTE: Use international country codes
+-- https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
+data Lang = EN | FR | DE | IT | PL | ES | CN | All
+  deriving (Show, Eq, Ord, Enum, Bounded, Generic, GQLType)
 
 instance ToJSON Lang
 instance FromJSON Lang
-instance ToSchema Lang
+instance ToSchema Lang where
+  declareNamedSchema = genericDeclareNamedSchemaUnrestricted defaultSchemaOptions
 instance FromHttpApiData Lang
   where
     parseUrlPiece "EN"  = pure EN
     parseUrlPiece "FR"  = pure FR
+    parseUrlPiece "DE"  = pure DE
+    parseUrlPiece "ES"  = pure ES
+    parseUrlPiece "IT"  = pure IT
+    parseUrlPiece "PL"  = pure PL
+    parseUrlPiece "CN"  = pure CN
     parseUrlPiece "All" = pure All
-    parseUrlPiece _     = Left "Unexpected value of OrderBy"
+    parseUrlPiece _     = Left "Unexpected value of Lang"
 instance ToHttpApiData Lang where
   toUrlPiece = pack . show
 instance Hashable Lang
@@ -63,14 +70,27 @@ class HasDBid a where
   toDBid   :: a   -> Int
   fromDBid :: Int -> a
 
+-- NOTE: We try to use numeric codes for countries
+-- https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
+-- https://en.wikipedia.org/wiki/ISO_3166-1_numeric#004
 instance HasDBid Lang where
   toDBid All = 0
   toDBid FR  = 1
   toDBid EN  = 2
+  toDBid DE  = 276
+  toDBid ES  = 724
+  toDBid IT  = 380
+  toDBid PL  = 616
+  toDBid CN  = 156
 
   fromDBid 0 = All
   fromDBid 1 = FR
   fromDBid 2 = EN
+  fromDBid 276 = DE
+  fromDBid 724 = ES
+  fromDBid 380 = IT
+  fromDBid 616 = PL
+  fromDBid 156 = CN
   fromDBid _ = panic "HasDBid lang, not implemented"
 
 ------------------------------------------------------------------------
diff --git a/src/Gargantext/Core/NLP.hs b/src/Gargantext/Core/NLP.hs
index c677accb..d5103e92 100644
--- a/src/Gargantext/Core/NLP.hs
+++ b/src/Gargantext/Core/NLP.hs
@@ -4,7 +4,7 @@ import Control.Lens (Getter, at, non)
 import qualified Data.Map.Strict as Map
 import Data.Maybe (fromJust)
 import Network.URI (URI(..), parseURI)
-import Gargantext.Core (Lang(..), NLPServerConfig(..), PosTagAlgo(..))
+import Gargantext.Core (Lang(..), NLPServerConfig(..), PosTagAlgo(..), allLangs)
 import Gargantext.Prelude.NLP.Types (NLPConfig(..))
 import Gargantext.Utils.Tuple (uncurryMaybeSecond)
 import Protolude hiding (All)
@@ -53,6 +53,8 @@ nlpServerConfigFromURI _ = Nothing
 
 nlpServerMap :: NLPConfig -> NLPServerMap
 nlpServerMap (NLPConfig { .. }) =
-  Map.fromList $ catMaybes [ uncurryMaybeSecond (EN, nlpServerConfigFromURI _nlp_en)
-                           , uncurryMaybeSecond (FR, nlpServerConfigFromURI _nlp_fr)
-                           , uncurryMaybeSecond (All, nlpServerConfigFromURI _nlp_all) ]
+  Map.fromList $ catMaybes $
+    [ uncurryMaybeSecond (All, nlpServerConfigFromURI _nlp_all) ] ++
+    ((\lang ->
+        uncurryMaybeSecond (lang, Map.lookup (show lang) _nlp_languages >>= nlpServerConfigFromURI ))
+      <$> allLangs)
diff --git a/src/Gargantext/Core/Text/Learn.hs b/src/Gargantext/Core/Text/Learn.hs
index e28605a3..f354276e 100644
--- a/src/Gargantext/Core/Text/Learn.hs
+++ b/src/Gargantext/Core/Text/Learn.hs
@@ -39,11 +39,12 @@ import Gargantext.Core (Lang(..), allLangs)
 import Gargantext.Core.Text.Terms.Mono (words)
 import Gargantext.Core.Text.Metrics.Count (occurrencesWith)
 
-import qualified Gargantext.Core.Text.Samples.FR as FR
+import qualified Gargantext.Core.Text.Samples.CN as CN
+import qualified Gargantext.Core.Text.Samples.DE as DE
 import qualified Gargantext.Core.Text.Samples.EN as EN
---import qualified Gargantext.Core.Text.Samples.DE as DE
---import qualified Gargantext.Core.Text.Samples.SP as SP
---import qualified Gargantext.Core.Text.Samples.CH as CH
+import qualified Gargantext.Core.Text.Samples.ES as ES
+import qualified Gargantext.Core.Text.Samples.FR as FR
+import qualified Gargantext.Core.Text.Samples.PL as PL
 
 ------------------------------------------------------------------------
 data Candidate = Candidate { stop :: Double
@@ -112,6 +113,10 @@ detectLangDefault = detectCat 99 eventLang
     textSample :: Lang -> String
     textSample EN = EN.textSample
     textSample FR = FR.textSample
+    textSample DE = DE.textSample
+    textSample ES = ES.textSample
+    textSample CN = CN.textSample
+    textSample PL = PL.textSample
     textSample _  = panic "[G.C.T.L:detectLangDefault] not impl yet"
     --textSample DE = DE.textSample
     --textSample SP = SP.textSample
diff --git a/src/Gargantext/Core/Text/Samples/CH.hs b/src/Gargantext/Core/Text/Samples/CN.hs
similarity index 93%
rename from src/Gargantext/Core/Text/Samples/CH.hs
rename to src/Gargantext/Core/Text/Samples/CN.hs
index 2800dfa0..123d285f 100644
--- a/src/Gargantext/Core/Text/Samples/CH.hs
+++ b/src/Gargantext/Core/Text/Samples/CN.hs
@@ -1,5 +1,5 @@
 {-|
-Module      : Gargantext.Core.Text.Samples.CH
+Module      : Gargantext.Core.Text.Samples.CN
 Description : Sample of Chinese Text
 Copyright   : (c) CNRS, 2017 - present
 License     : AGPL + CECILL v3
@@ -14,10 +14,9 @@ Page  : text mining
 
 
 
-module Gargantext.Core.Text.Samples.CH where
+module Gargantext.Core.Text.Samples.CN where
 
 import Data.String (String)
 
 textSample :: String
 textSample = "文本挖掘有时也被称为文字探勘、文本数据挖掘等,大致相当于文字分析,一般指文本处理过程中产生高质量的信息。高质量的信息通常通过分类和预测来产生,如模式识别。文本挖掘通常涉及输入文本的处理过程(通常进行分析,同时加上一些衍生语言特征以及消除杂音,随后插入到数据库中) ,产生结构化数据,并最终评价和解释输出。'高品质'的文本挖掘通常是指某种组合的相关性,新颖性和趣味性。典型的文本挖掘方法包括文本分类,文本聚类,概念/实体挖掘,生产精确分类,观点分析,文档摘要和实体关系模型(即,学习已命名实体之间的关系) 。 文本分析包括了信息检索、词典分析来研究词语的频数分布、模式识别、标签 注释、信息抽取,数据挖掘技术包括链接和关联分析、可视化和预测分析。本质上,首要的任务是,通过自然语言处理和分析方法,将文本转化为数据进行分析"
-
diff --git a/src/Gargantext/Core/Text/Samples/SP.hs b/src/Gargantext/Core/Text/Samples/ES.hs
similarity index 83%
rename from src/Gargantext/Core/Text/Samples/SP.hs
rename to src/Gargantext/Core/Text/Samples/ES.hs
index 26535a1f..5588c2a6 100644
--- a/src/Gargantext/Core/Text/Samples/SP.hs
+++ b/src/Gargantext/Core/Text/Samples/ES.hs
@@ -1,5 +1,5 @@
 {-|
-Module      : Gargantext.Core.Text.Samples.SP
+Module      : Gargantext.Core.Text.Samples.ES
 Description : Sample of Spanish Text
 Copyright   : (c) CNRS, 2017 - present
 License     : AGPL + CECILL v3
@@ -13,9 +13,9 @@ Page  : text mining
 
 
 
-module Gargantext.Core.Text.Samples.SP where
+module Gargantext.Core.Text.Samples.ES where
 
 import Data.String (String)
 
-textMining :: String
-textMining = "La minería de textos se refiere al proceso de derivar información nueva de textos. A comienzos de los años ochenta surgieron los primeros esfuerzos de minería de textos que necesitaban una gran cantidad de esfuerzo humano, pero los avances tecnológicos han permitido que esta área progrese de manera rápida en la última década. La minería de textos es un área multidisciplinar basada en la recuperación de información, minería de datos, aprendizaje automático, estadísticas y la lingüística computacional. Como la mayor parte de la información (más de un 80%) se encuentra actualmente almacenada como texto, se cree que la minería de textos tiene un gran valor comercial."
+textSample :: String
+textSample = "La minería de textos se refiere al proceso de derivar información nueva de textos. A comienzos de los años ochenta surgieron los primeros esfuerzos de minería de textos que necesitaban una gran cantidad de esfuerzo humano, pero los avances tecnológicos han permitido que esta área progrese de manera rápida en la última década. La minería de textos es un área multidisciplinar basada en la recuperación de información, minería de datos, aprendizaje automático, estadísticas y la lingüística computacional. Como la mayor parte de la información (más de un 80%) se encuentra actualmente almacenada como texto, se cree que la minería de textos tiene un gran valor comercial."
diff --git a/src/Gargantext/Core/Text/Samples/PL.hs b/src/Gargantext/Core/Text/Samples/PL.hs
new file mode 100644
index 00000000..050f11a4
--- /dev/null
+++ b/src/Gargantext/Core/Text/Samples/PL.hs
@@ -0,0 +1,22 @@
+{-|
+Module      : Gargantext.Core.Text.Samples.PL
+Description : Sample of Polish Text
+Copyright   : (c) CNRS, 2017 - present
+License     : AGPL + CECILL v3
+Maintainer  : team@gargantext.org
+Stability   : experimental
+Portability : POSIX
+
+Source: Wikipedia
+Page  : text mining
+
+-}
+
+
+
+module Gargantext.Core.Text.Samples.PL where
+
+import Data.String (String)
+
+textSample :: String
+textSample = "Text mining (eksploracja tekstu) – ogólna nazwa metod eksploracji danych służących do wydobywania danych z tekstu i ich późniejszej obróbki. Metody text mining stosowane są np. do statystycznego przetwarzania: artykułów prasowych, wiadomości poczty elektronicznej, otwartych odpowiedzi na pytania ankietowe, opisów dolegliwości, podawanych przez pacjentów, komentarzy do sesji giełdowych i zdarzeń dotyczące spółek, życiorysów zawodowych i listów motywacyjnych, tekstów reklamacji konsumenckich. Text mining może polegać na znalezieniu kluczowych fraz, zdań, które zostają następnie zakodowane pod postacią zmiennych numerycznych. Później stosuje się metody statystyki i eksploracji danych w celu odkrycia zależności pomiędzy zmiennymi. Ze względu na to, że powstające zmienne są zwykle nominalne, szczególnie użyteczna jest analiza koszykowa."
diff --git a/src/Gargantext/Utils/JohnSnowNLP.hs b/src/Gargantext/Utils/JohnSnowNLP.hs
index 69072fdb..7c20433e 100644
--- a/src/Gargantext/Utils/JohnSnowNLP.hs
+++ b/src/Gargantext/Utils/JohnSnowNLP.hs
@@ -38,17 +38,38 @@ data JSSpell = JSPOS Lang | JSLemma Lang
 instance ToJSON JSSpell where
   toJSON (JSPOS EN)    = "en.pos"
   toJSON (JSPOS FR)    = "fr.pos"
+  toJSON (JSPOS DE)    = "de.pos"
+  toJSON (JSPOS ES)    = "es.pos"
+  toJSON (JSPOS IT)    = "it.pos"
+  toJSON (JSPOS PL)    = "pl.pos"
+  toJSON (JSPOS CN)    = "cn.pos"
   toJSON (JSPOS All)   = "pos"
+
   toJSON (JSLemma EN)  = "en.lemma"
   toJSON (JSLemma FR)  = "fr.lemma"
+  toJSON (JSLemma DE)  = "de.lemma"
+  toJSON (JSLemma ES)  = "es.lemma"
+  toJSON (JSLemma IT)  = "it.lemma"
+  toJSON (JSLemma PL)  = "pl.lemma"
+  toJSON (JSLemma CN)  = "cn.lemma"
   toJSON (JSLemma All) = "lemma"
 
 instance FromJSON JSSpell where
   parseJSON (String "en.pos")   = pure $ JSPOS EN
   parseJSON (String "fr.pos")   = pure $ JSPOS FR
+  parseJSON (String "de.pos")   = pure $ JSPOS DE
+  parseJSON (String "es.pos")   = pure $ JSPOS ES
+  parseJSON (String "it.pos")   = pure $ JSPOS IT
+  parseJSON (String "pl.pos")   = pure $ JSPOS PL
+  parseJSON (String "cn.pos")   = pure $ JSPOS CN
   parseJSON (String "pos")      = pure $ JSPOS All
   parseJSON (String "en.lemma") = pure $ JSLemma EN
   parseJSON (String "fr.lemma") = pure $ JSLemma FR
+  parseJSON (String "de.lemma") = pure $ JSLemma DE
+  parseJSON (String "es.lemma") = pure $ JSLemma ES
+  parseJSON (String "it.lemma") = pure $ JSLemma IT
+  parseJSON (String "pl.lemma") = pure $ JSLemma PL
+  parseJSON (String "cn.lemma") = pure $ JSLemma CN
   parseJSON (String "lemma")    = pure $ JSLemma All
   parseJSON s =
     prependFailure "parsing spell failed, "
diff --git a/stack.yaml b/stack.yaml
index 953c8556..3f65b0ca 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -40,7 +40,7 @@ extra-deps:
 #- git: https://gitlab.iscpif.fr/gargantext/haskell-gargantext-prelude.git
 #  commit: 791c2a7046a3760f8ae5fabdbd708f61caa63741
 - git: https://gitlab.iscpif.fr/cgenie/haskell-gargantext-prelude
-  commit: 175d4b295be2a0f56edc4eb6c7d8227d81bc2841
+  commit: 8f97fef4dfd941d773914ad058d8e02ce2bb1a3e  # e250af8a495191b4bd06c9f465656b87bea7bf7f
 - git: https://gitlab.iscpif.fr/gargantext/gargantext-graph.git
   commit: 588e104fe7593210956610cab0041fd16584a4ce
   # Data Mining Libs
@@ -94,7 +94,7 @@ extra-deps:
   #- git: https://github.com/np/patches-map
 - git: https://github.com/delanoe/patches-map
   commit: 76cae88f367976ff091e661ee69a5c3126b94694
-  #- git: https://gitlab.com/npouillard/patches-class.git  
+  #- git: https://gitlab.com/npouillard/patches-class.git
 #- git: https://gitlab.iscpif.fr/gargantext/patches-class.git
 #  commit: d3e971d4e78d1dfcc853f2fb86bde1995faf22ae
 - git: https://gitlab.iscpif.fr/cgenie/patches-class.git
-- 
2.21.0