Verified Commit 8b0f558b authored by Przemyslaw Kaminski's avatar Przemyslaw Kaminski

Merge branch 'dev' into dev-epo-patents

parents a1f614f9 c41b6a37
Pipeline #4596 failed with stages
in 3 minutes and 30 seconds
...@@ -12,6 +12,7 @@ variables: ...@@ -12,6 +12,7 @@ variables:
stages: stages:
- stack - stack
- cabal - cabal
- bench
- test - test
stack: stack:
...@@ -34,7 +35,19 @@ cabal: ...@@ -34,7 +35,19 @@ cabal:
- .cabal/ - .cabal/
policy: pull-push policy: pull-push
script: script:
- nix-shell --run "./bin/update-cabal-project $CABAL_STORE_DIR && cabal --store-dir=$CABAL_STORE_DIR v2-build --ghc-options='-O0 -fclear-plugins'" - nix-shell --run "./bin/update-cabal-project $CABAL_STORE_DIR && cabal --store-dir=$CABAL_STORE_DIR v2-build --ghc-options='-O2 -fclear-plugins'"
allow_failure: false
bench:
stage: bench
cache:
key: cabal.project
paths:
- dist-newstyle/
- .cabal/
policy: pull-push
script:
- nix-shell --run "./bin/update-cabal-project $CABAL_STORE_DIR && cabal --store-dir=$CABAL_STORE_DIR v2-bench --ghc-options='-O2 -fclear-plugins'"
allow_failure: false allow_failure: false
test: test:
...@@ -59,14 +72,17 @@ test: ...@@ -59,14 +72,17 @@ test:
export TEST_NIX_PATH=$(nix-shell --run "echo -n \$PATH") export TEST_NIX_PATH=$(nix-shell --run "echo -n \$PATH")
echo $CABAL echo $CABAL
echo $TEST_NIX_PATH echo $TEST_NIX_PATH
git config --global --add safe.directory '*'
nix-shell --run "./bin/update-cabal-project $CABAL_STORE_DIR" nix-shell --run "./bin/update-cabal-project $CABAL_STORE_DIR"
mkdir -p /root/.cache/cabal/logs mkdir -p /root/.cache/cabal/logs
chown -R test:test /root/.cache/cabal/logs/ chown -R test:test /root/.cache/cabal/logs/
nix-shell --run "chown -R test:test /root/.config/ && su -m test -c \"export PATH=$PATH:$TEST_NIX_PATH && cd /builds/gargantext/haskell-gargantext && $CABAL --store-dir=$CABAL_STORE_DIR v2-test --test-show-details=streaming --ghc-options='-O0 -fclear-plugins'\"" chown -R test:test /root/.cache/cabal/packages/hackage.haskell.org/
nix-shell --run "chown -R test:test /root/.config/ && su -m test -c \"export PATH=$PATH:$TEST_NIX_PATH && cd /builds/gargantext/haskell-gargantext && $CABAL --store-dir=$CABAL_STORE_DIR v2-test --test-show-details=streaming --flags test-crypto --ghc-options='-O0 -fclear-plugins'\""
chown -R root:root dist-newstyle/ chown -R root:root dist-newstyle/
chown -R root:root /root/ chown -R root:root /root/
chown -R root:root $CABAL_STORE_DIR chown -R root:root $CABAL_STORE_DIR
chown -R root:root /root/.cache/cabal/logs/ chown -R root:root /root/.cache/cabal/logs/
chown -R root:root /root/.cache/cabal/packages/hackage.haskell.org/
#docs: #docs:
# stage: docs # stage: docs
......
## Version 0.0.6.9.9.7.6.3
* [BACK][TESTS] Make a start on benchmarking, add more tests
## Version 0.0.6.9.9.7.6.2
* [BACK][FIX] CI
* [FRONT][FEAT] Restoring APIs for tests
## Version 0.0.6.9.9.7.6.1
* [BACK][API/DATA] OpenAlex connection
* [BACK][DOC][README: Add note about libraries devlopment (#260)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/260)
## Version 0.0.6.9.9.7.6
* [BACK][FIX][[ArXiv][TextFlow] Ngrams Table empty (#258)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/258)
* [BACK][FIX][[Corpus HAL API] Abstract in both languages FR+EN instead of only 1 language (#244)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/244)
* [FRONT][ERGO][API key pubmed (#593)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/593)
* [FRONT][FACTO][Tree on the right (#511)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/511)
* [FRONT][FIX][Single document upload doesn't work when abstract is empty (#592)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/592)
* [FRONT][RENDER][[Node Phylo] Form render improvements (#581)](https://gitlab.iscpif.fr/gargantext/purescript-gargantext/issues/581)
## Version 0.0.6.9.9.7.5.1 ## Version 0.0.6.9.9.7.5.1
* [BACK][FIX][[Corpus HAL API] Abstract in both languages FR+EN instead of only 1 language (#244)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/244) * [BACK][FIX][[Corpus HAL API] Abstract in both languages FR+EN instead of only 1 language (#244)](https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/244)
......
...@@ -37,7 +37,7 @@ Disclaimer: since this project is still in development, this document remains in ...@@ -37,7 +37,7 @@ Disclaimer: since this project is still in development, this document remains in
git clone https://gitlab.iscpif.fr/gargantext/haskell-gargantext.git git clone https://gitlab.iscpif.fr/gargantext/haskell-gargantext.git
cd haskell-gargantext cd haskell-gargantext
``` ```
#### 1. Installation ### Installation
This project can be built with either Stack or Cabal. For historical reasons, we generate a `cabal.project` from the `stack.yaml`, and we do not commit the former to the repo, to have a single "source of truth". This project can be built with either Stack or Cabal. For historical reasons, we generate a `cabal.project` from the `stack.yaml`, and we do not commit the former to the repo, to have a single "source of truth".
However, it's always possible to generate a `cabal.project` thanks to [stack2cabal](https://hackage.haskell.org/package/stack2cabal). However, it's always possible to generate a `cabal.project` thanks to [stack2cabal](https://hackage.haskell.org/package/stack2cabal).
...@@ -64,7 +64,7 @@ nix-shell ...@@ -64,7 +64,7 @@ nix-shell
This will take a bit of time the first time. This will take a bit of time the first time.
#### Build: choose cabal (new) or stack (old) ### Build: choose cabal (new) or stack (old)
#### With Cabal (recommanded) #### With Cabal (recommanded)
...@@ -166,12 +166,29 @@ From the Backend root folder (haskell-gargantext): ...@@ -166,12 +166,29 @@ From the Backend root folder (haskell-gargantext):
``` shell ``` shell
./start ./start
# The start script runs following commands: # The start script runs following commands:
# - `./bin/install` to update and build the project
# - `docker compose up` to run the Docker for postgresql from devops/docker folder # - `docker compose up` to run the Docker for postgresql from devops/docker folder
# - `cabal run gargantext-server -- --ini gargantext.ini --run Prod` to run other services through `nix-shell` # - `cabal run gargantext-server -- --ini gargantext.ini --run Prod` to run other services through `nix-shell`
``` ```
For frontend development and compilation, see the [Frontend Readme.md](https://gitlab.iscpif.fr/gargantext/purescript-gargantext#dev) For frontend development and compilation, see the [Frontend Readme.md](https://gitlab.iscpif.fr/gargantext/purescript-gargantext#dev)
### Working on libraries
When a devlopment is needed on libraries (for instance, the HAL crawler in https://gitlab.iscpif.fr/gargantext/crawlers):
1. Ongoing devlopment (on local repo):
1. In `cabal.project`:
- add `../hal` to `packages:`
- turn off (temporarily) the `hal` in `source-repository-package`
2. When changes work and tests are OK, commit in repo `hal`
2. When changes are commited / merged:
1. Get the hash id, and edit `stack.yaml` with the **new commit id**
2. run `./bin/update-cabal-project`
- get an error that sha256 don't match, so update the `./bin/update-cabal-project` with new sha256 hash
- run again `./bin/update-cabal-project` (to make sure it's a fixed point now)
> Note: without `stack.yaml` we would have to only fix `cabal.project` -> `source-repository-package` commit id. Sha256 is there to make sure CI reruns the tests.
## Use Cases <a name="use-cases"></a> ## Use Cases <a name="use-cases"></a>
......
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Main where
import Gargantext.Core.Types.Individu
import Gargantext.Prelude.Crypto.Auth (createPasswordHash)
import Test.Tasty.Bench
main :: IO ()
main = defaultMain
[ bgroup "Benchmarks"
[ bgroup "User creation" [
bench "createPasswordHash" $ whnfIO (createPasswordHash "rabbit")
, bench "toUserHash" $
whnfIO (toUserHash $ NewUser "alfredo" "alfredo@well-typed.com" (GargPassword "rabbit"))
]
]
]
...@@ -18,6 +18,7 @@ module Main where ...@@ -18,6 +18,7 @@ module Main where
import Gargantext.API.Dev (withDevEnv, runCmdDev) import Gargantext.API.Dev (withDevEnv, runCmdDev)
import Gargantext.API.Prelude (GargError) import Gargantext.API.Prelude (GargError)
import Gargantext.Database.Action.User.New (newUsers) import Gargantext.Database.Action.User.New (newUsers)
import Gargantext.Database.Admin.Types.Node
import Gargantext.Database.Prelude (Cmd'') import Gargantext.Database.Prelude (Cmd'')
import Gargantext.Prelude import Gargantext.Prelude
import System.Environment (getArgs) import System.Environment (getArgs)
...@@ -28,6 +29,6 @@ main = do ...@@ -28,6 +29,6 @@ main = do
(iniPath:mails) <- getArgs (iniPath:mails) <- getArgs
withDevEnv iniPath $ \env -> do withDevEnv iniPath $ \env -> do
x <- runCmdDev env ((newUsers $ map cs mails) :: Cmd'' DevEnv GargError Int64) x <- runCmdDev env ((newUsers $ map cs mails) :: Cmd'' DevEnv GargError [UserId])
putStrLn $ show x putStrLn $ show x
pure () pure ()
...@@ -16,15 +16,19 @@ Script to start gargantext with different modes (Dev, Prod, Mock). ...@@ -16,15 +16,19 @@ Script to start gargantext with different modes (Dev, Prod, Mock).
{-# LANGUAGE Strict #-} {-# LANGUAGE Strict #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE TypeFamilies #-}
module Main where module Main where
import Data.String (String)
import Data.Text (unpack) import Data.Text (unpack)
import Data.Version (showVersion) import Data.Version (showVersion)
import Gargantext.API (startGargantext, Mode(..)) -- , startGargantextMock) import Gargantext.API (startGargantext) -- , startGargantextMock)
import Gargantext.API.Admin.EnvTypes
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.System.Logging
import Options.Generic import Options.Generic
import System.Exit (exitSuccess) import System.Exit (exitSuccess)
import qualified Paths_gargantext as PG -- cabal magic build module import qualified Paths_gargantext as PG -- cabal magic build module
...@@ -49,14 +53,26 @@ data MyOptions w = ...@@ -49,14 +53,26 @@ data MyOptions w =
instance ParseRecord (MyOptions Wrapped) instance ParseRecord (MyOptions Wrapped)
deriving instance Show (MyOptions Unwrapped) deriving instance Show (MyOptions Unwrapped)
-- | A plain logger in the IO monad, waiting for more serious logging solutions like
-- the one described in https://gitlab.iscpif.fr/gargantext/haskell-gargantext/issues/229
instance HasLogger IO where
data instance Logger IO = IOLogger
type instance LogInitParams IO = ()
type instance LogPayload IO = String
initLogger = \() -> pure IOLogger
destroyLogger = \_ -> pure ()
logMsg = \IOLogger lvl msg ->
let pfx = "[" <> show lvl <> "] "
in putStrLn $ pfx <> msg
logTxt lgr lvl msg = logMsg lgr lvl (unpack msg)
main :: IO () main :: IO ()
main = do main = withLogger () $ \ioLogger -> do
MyOptions myMode myPort myIniFile myVersion <- unwrapRecord MyOptions myMode myPort myIniFile myVersion <- unwrapRecord
"Gargantext server" "Gargantext server"
--------------------------------------------------------------- ---------------------------------------------------------------
if myVersion then do if myVersion then do
putStrLn $ "Version: " <> showVersion PG.version logMsg ioLogger INFO $ "Version: " <> showVersion PG.version
System.Exit.exitSuccess System.Exit.exitSuccess
else else
return () return ()
...@@ -73,6 +89,6 @@ main = do ...@@ -73,6 +89,6 @@ main = do
let start = case myMode of let start = case myMode of
Mock -> panic "[ERROR] Mock mode unsupported" Mock -> panic "[ERROR] Mock mode unsupported"
_ -> startGargantext myMode myPort' (unpack myIniFile') _ -> startGargantext myMode myPort' (unpack myIniFile')
putStrLn $ "Starting with " <> show myMode <> " mode." logMsg ioLogger INFO $ "Starting with " <> show myMode <> " mode."
start start
--------------------------------------------------------------- ---------------------------------------------------------------
...@@ -3,5 +3,5 @@ ...@@ -3,5 +3,5 @@
tmux new -d -s gargantext './server' \; \ tmux new -d -s gargantext './server' \; \
split-window -h -d 'cd ./purescript-gargantext ; ./server' \; \ split-window -h -d 'cd ./purescript-gargantext ; ./server' \; \
select-pane -t 1 \; \ select-pane -t 1 \; \
split-window -d 'cd deps/nlp/CoreNLP ; ./startServer.sh' \; \ split-window -d 'cd devops/docker/nlp/stanford/ ; docker-compose up' \; \
split-window -d 'cd deps/nlp/spacy-server ; docker-compose up' \; \ split-window -d 'cd deps/nlp/spacy-server ; docker-compose up' \; \
...@@ -7,10 +7,12 @@ STORE_DIR="${1:-$DEFAULT_STORE}" ...@@ -7,10 +7,12 @@ STORE_DIR="${1:-$DEFAULT_STORE}"
# README! # README!
# Every time you modify the `stack.yaml` and as result the relevant `cabal.project` # Every time you modify the `stack.yaml` and as result the relevant `cabal.project`
# changes, you have to make sure to update the `expected_cabal_projet_hash` with the # changes, you have to make sure to update the `expected_cabal_project_hash` and
# `sha256sum` result calculated on the `cabal.project`. This ensures the `cabal.project` # `expected_cabal_project_freeze_hash` with the
# stays deterministic so that CI cache can kick in. # `sha256sum` result calculated on the `cabal.project` and `cabal.project.freeze`.
expected_cabal_project_hash="720a064535707fc28b8c7b67b1560698d13610a4c1f8a79176b4c5bd40514979" # This ensures the files stay deterministic so that CI cache can kick in.
expected_cabal_project_hash="eb12c232115b3fffa1f81add7c83d921e5899c7712eddee6100ff8df7305088e"
expected_cabal_project_freeze_hash="b7acfd12c970323ffe2c6684a13130db09d8ec9fa5676a976afed329f1ef3436"
cabal --store-dir=$STORE_DIR v2-update 'hackage.haskell.org,2023-06-24T21:28:46Z' cabal --store-dir=$STORE_DIR v2-update 'hackage.haskell.org,2023-06-24T21:28:46Z'
...@@ -23,9 +25,16 @@ fi ...@@ -23,9 +25,16 @@ fi
stack2cabal --no-run-hpack -p '2023-06-24 21:28:46' stack2cabal --no-run-hpack -p '2023-06-24 21:28:46'
actual_cabal_project_hash=$(sha256sum cabal.project | awk '{printf "%s",$1}') actual_cabal_project_hash=$(sha256sum cabal.project | awk '{printf "%s",$1}')
actual_cabal_project_freeze_hash=$(sha256sum cabal.project.freeze | awk '{printf "%s",$1}')
if [[ $actual_cabal_project_hash != $expected_cabal_project_hash ]]; then if [[ $actual_cabal_project_hash != $expected_cabal_project_hash ]]; then
echo "ERROR! hash mismatch between expected cabal.project and the one computed by stack2cabal." echo "ERROR! hash mismatch between expected cabal.project and the one computed by stack2cabal."
exit 1 exit 1
else else
echo "cabal.project updated successfully." echo "cabal.project updated successfully."
fi fi
if [[ $actual_cabal_project_freeze_hash != $expected_cabal_project_freeze_hash ]]; then
echo "ERROR! hash mismatch between expected cabal.project.freeze and the one computed by stack2cabal."
exit 1
else
echo "cabal.project.freeze updated successfully."
fi
...@@ -66,11 +66,6 @@ source-repository-package ...@@ -66,11 +66,6 @@ source-repository-package
location: https://gitlab.iscpif.fr/amestanogullari/accelerate-utility.git location: https://gitlab.iscpif.fr/amestanogullari/accelerate-utility.git
tag: a3875fe652d3bb5acb522674c22c6c814c1b4ad0 tag: a3875fe652d3bb5acb522674c22c6c814c1b4ad0
source-repository-package
type: git
location: https://gitlab.iscpif.fr/cgenie/haskell-gargantext-prelude
tag: 8f97fef4dfd941d773914ad058d8e02ce2bb1a3e
source-repository-package source-repository-package
type: git type: git
location: https://gitlab.iscpif.fr/cgenie/patches-class.git location: https://gitlab.iscpif.fr/cgenie/patches-class.git
...@@ -79,7 +74,7 @@ source-repository-package ...@@ -79,7 +74,7 @@ source-repository-package
source-repository-package source-repository-package
type: git type: git
location: https://gitlab.iscpif.fr/gargantext/crawlers/arxiv-api.git location: https://gitlab.iscpif.fr/gargantext/crawlers/arxiv-api.git
tag: 2d7e5753cbbce248b860b571a0e9885415c846f7 tag: eb130c71fa17adaceed6ff66beefbccb13df51ba
source-repository-package source-repository-package
type: git type: git
...@@ -99,7 +94,7 @@ source-repository-package ...@@ -99,7 +94,7 @@ source-repository-package
source-repository-package source-repository-package
type: git type: git
location: https://gitlab.iscpif.fr/gargantext/crawlers/openalex.git location: https://gitlab.iscpif.fr/gargantext/crawlers/openalex.git
tag: dab07cb89e8ab8eaaff8619f5e21d944d9c526ab tag: 1cf872fb3bd0e3e44af31247833c4b6bb7d0dca5
source-repository-package source-repository-package
type: git type: git
...@@ -111,6 +106,11 @@ source-repository-package ...@@ -111,6 +106,11 @@ source-repository-package
location: https://gitlab.iscpif.fr/gargantext/gargantext-graph.git location: https://gitlab.iscpif.fr/gargantext/gargantext-graph.git
tag: 588e104fe7593210956610cab0041fd16584a4ce tag: 588e104fe7593210956610cab0041fd16584a4ce
source-repository-package
type: git
location: https://gitlab.iscpif.fr/gargantext/haskell-gargantext-prelude
tag: 8f97fef4dfd941d773914ad058d8e02ce2bb1a3e
source-repository-package source-repository-package
type: git type: git
location: https://gitlab.iscpif.fr/gargantext/haskell-igraph.git location: https://gitlab.iscpif.fr/gargantext/haskell-igraph.git
...@@ -151,7 +151,7 @@ source-repository-package ...@@ -151,7 +151,7 @@ source-repository-package
source-repository-package source-repository-package
type: git type: git
location: ssh://git@gitlab.iscpif.fr:20022/gargantext/crawlers/epo.git location: ssh://git@gitlab.iscpif.fr:20022/gargantext/crawlers/epo.git
tag: ac9f20b36e8659267d7525fe2c74c7286a0350cb tag: 4918c32679edaba87d05bed88ea1c3024813946d
allow-older: * allow-older: *
allow-newer: * allow-newer: *
......
...@@ -1205,12 +1205,12 @@ constraints: any.AC-Angle ==1.0, ...@@ -1205,12 +1205,12 @@ constraints: any.AC-Angle ==1.0,
any.hslua-module-text ==0.3.0.1, any.hslua-module-text ==0.3.0.1,
any.hsp ==0.10.0, any.hsp ==0.10.0,
any.hsparql ==0.3.8, any.hsparql ==0.3.8,
any.hspec ==2.7.10, any.hspec ==2.11.1,
any.hspec-attoparsec ==0.1.0.2, any.hspec-attoparsec ==0.1.0.2,
any.hspec-checkers ==0.1.0.2, any.hspec-checkers ==0.1.0.2,
any.hspec-contrib ==0.5.1, any.hspec-contrib ==0.5.1,
any.hspec-core ==2.7.10, any.hspec-core ==2.11.1,
any.hspec-discover ==2.7.10, any.hspec-discover ==2.11.1,
any.hspec-expectations ==0.8.3, any.hspec-expectations ==0.8.3,
any.hspec-expectations-json ==1.0.0.4, any.hspec-expectations-json ==1.0.0.4,
any.hspec-expectations-lifted ==0.10.0, any.hspec-expectations-lifted ==0.10.0,
...@@ -2385,7 +2385,7 @@ constraints: any.AC-Angle ==1.0, ...@@ -2385,7 +2385,7 @@ constraints: any.AC-Angle ==1.0,
any.tasty-focus ==1.0.1, any.tasty-focus ==1.0.1,
any.tasty-golden ==2.3.5, any.tasty-golden ==2.3.5,
any.tasty-hedgehog ==1.1.0.0, any.tasty-hedgehog ==1.1.0.0,
any.tasty-hspec ==1.1.6, any.tasty-hspec ==1.2.0.3,
any.tasty-hunit ==0.10.0.3, any.tasty-hunit ==0.10.0.3,
any.tasty-hunit-compat ==0.2.0.1, any.tasty-hunit-compat ==0.2.0.1,
any.tasty-inspection-testing ==0.1, any.tasty-inspection-testing ==0.1,
......
version: '3'
services:
corenlp:
#image: 'cgenie/corenlp-garg:latest'
image: 'cgenie/corenlp-garg:4.5.4'
ports:
- 9000:9000
volumes:
js-cache:
...@@ -265,14 +265,14 @@ CREATE INDEX ON public.nodes USING btree (user_id, typename, parent_id); ...@@ -265,14 +265,14 @@ CREATE INDEX ON public.nodes USING btree (user_id, typename, parent_id);
CREATE INDEX ON public.nodes USING btree (id, typename, date ASC); CREATE INDEX ON public.nodes USING btree (id, typename, date ASC);
CREATE INDEX ON public.nodes USING btree (id, typename, date DESC); CREATE INDEX ON public.nodes USING btree (id, typename, date DESC);
CREATE INDEX ON public.nodes USING btree (typename, id); CREATE INDEX ON public.nodes USING btree (typename, id);
CREATE UNIQUE INDEX ON public.nodes USING btree (hash_id); CREATE UNIQUE INDEX IF NOT EXISTS ON public.nodes USING btree (hash_id);
CREATE INDEX ON public.contexts USING gin (hyperdata); CREATE INDEX ON public.contexts USING gin (hyperdata);
CREATE INDEX ON public.contexts USING btree (user_id, typename, parent_id); CREATE INDEX ON public.contexts USING btree (user_id, typename, parent_id);
CREATE INDEX ON public.contexts USING btree (id, typename, date ASC); CREATE INDEX ON public.contexts USING btree (id, typename, date ASC);
CREATE INDEX ON public.contexts USING btree (id, typename, date DESC); CREATE INDEX ON public.contexts USING btree (id, typename, date DESC);
CREATE INDEX ON public.contexts USING btree (typename, id); CREATE INDEX ON public.contexts USING btree (typename, id);
CREATE UNIQUE INDEX ON public.contexts USING btree (hash_id); CREATE UNIQUE INDEX IF NOT EXISTS ON public.contexts USING btree (hash_id);
CREATE INDEX ON public.nodescontexts_nodescontexts USING btree (nodescontexts1, nodescontexts2); CREATE INDEX ON public.nodescontexts_nodescontexts USING btree (nodescontexts1, nodescontexts2);
-- CREATE UNIQUE INDEX ON public.nodes USING btree (((hyperdata ->> 'uniqId'::text))); -- CREATE UNIQUE INDEX ON public.nodes USING btree (((hyperdata ->> 'uniqId'::text)));
......
...@@ -5,7 +5,7 @@ cabal-version: 2.0 ...@@ -5,7 +5,7 @@ cabal-version: 2.0
-- see: https://github.com/sol/hpack -- see: https://github.com/sol/hpack
name: gargantext name: gargantext
version: 0.0.6.9.9.7.5.1 version: 0.0.6.9.9.7.6.3
synopsis: Search, map, share synopsis: Search, map, share
description: Please see README.md description: Please see README.md
category: Data category: Data
...@@ -33,6 +33,13 @@ data-files: ...@@ -33,6 +33,13 @@ data-files:
test-data/test_config.ini test-data/test_config.ini
.clippy.dhall .clippy.dhall
-- When enabled, it swaps the hashing algorithm
-- with a quicker (and less secure) version, which
-- runs faster in tests.
flag test-crypto
default: False
manual: True
library library
exposed-modules: exposed-modules:
Gargantext Gargantext
...@@ -106,6 +113,7 @@ library ...@@ -106,6 +113,7 @@ library
Gargantext.Core.Viz.Types Gargantext.Core.Viz.Types
Gargantext.Database.Action.Flow Gargantext.Database.Action.Flow
Gargantext.Database.Action.Flow.Types Gargantext.Database.Action.Flow.Types
Gargantext.Database.Action.User
Gargantext.Database.Action.User.New Gargantext.Database.Action.User.New
Gargantext.Database.Admin.Config Gargantext.Database.Admin.Config
Gargantext.Database.Admin.Trigger.Init Gargantext.Database.Admin.Trigger.Init
...@@ -116,9 +124,12 @@ library ...@@ -116,9 +124,12 @@ library
Gargantext.Database.Query.Table.Node Gargantext.Database.Query.Table.Node
Gargantext.Database.Query.Table.Node.Error Gargantext.Database.Query.Table.Node.Error
Gargantext.Database.Query.Table.Node.UpdateOpaleye Gargantext.Database.Query.Table.Node.UpdateOpaleye
Gargantext.Database.Query.Tree.Root
Gargantext.Database.Query.Table.User Gargantext.Database.Query.Table.User
Gargantext.Database.Schema.Ngrams Gargantext.Database.Schema.Ngrams
Gargantext.Database.Schema.User
Gargantext.Defaults Gargantext.Defaults
Gargantext.System.Logging
Gargantext.Utils.Jobs Gargantext.Utils.Jobs
Gargantext.Utils.Jobs.Internal Gargantext.Utils.Jobs.Internal
Gargantext.Utils.Jobs.Map Gargantext.Utils.Jobs.Map
...@@ -281,7 +292,6 @@ library ...@@ -281,7 +292,6 @@ library
Gargantext.Database.Action.Search Gargantext.Database.Action.Search
Gargantext.Database.Action.Share Gargantext.Database.Action.Share
Gargantext.Database.Action.TSQuery Gargantext.Database.Action.TSQuery
Gargantext.Database.Action.User
Gargantext.Database.Admin.Access Gargantext.Database.Admin.Access
Gargantext.Database.Admin.Bashql Gargantext.Database.Admin.Bashql
Gargantext.Database.Admin.Trigger.ContextNodeNgrams Gargantext.Database.Admin.Trigger.ContextNodeNgrams
...@@ -331,7 +341,6 @@ library ...@@ -331,7 +341,6 @@ library
Gargantext.Database.Query.Table.NodesNgramsRepo Gargantext.Database.Query.Table.NodesNgramsRepo
Gargantext.Database.Query.Tree Gargantext.Database.Query.Tree
Gargantext.Database.Query.Tree.Error Gargantext.Database.Query.Tree.Error
Gargantext.Database.Query.Tree.Root
Gargantext.Database.Schema.Context Gargantext.Database.Schema.Context
Gargantext.Database.Schema.ContextNodeNgrams Gargantext.Database.Schema.ContextNodeNgrams
Gargantext.Database.Schema.ContextNodeNgrams2 Gargantext.Database.Schema.ContextNodeNgrams2
...@@ -346,7 +355,6 @@ library ...@@ -346,7 +355,6 @@ library
Gargantext.Database.Schema.NodeNodeNgrams2 Gargantext.Database.Schema.NodeNodeNgrams2
Gargantext.Database.Schema.NodesNgramsRepo Gargantext.Database.Schema.NodesNgramsRepo
Gargantext.Database.Schema.Prelude Gargantext.Database.Schema.Prelude
Gargantext.Database.Schema.User
Gargantext.Database.Types Gargantext.Database.Types
Gargantext.Utils.Aeson Gargantext.Utils.Aeson
Gargantext.Utils.JohnSnowNLP Gargantext.Utils.JohnSnowNLP
...@@ -371,6 +379,8 @@ library ...@@ -371,6 +379,8 @@ library
RecordWildCards RecordWildCards
StrictData StrictData
ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wmissing-signatures -Wunused-binds -Wunused-imports -Werror -freduction-depth=300 -fplugin=Clippy ghc-options: -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Wmissing-signatures -Wunused-binds -Wunused-imports -Werror -freduction-depth=300 -fplugin=Clippy
if flag(test-crypto)
cpp-options: -DTEST_CRYPTO
build-depends: build-depends:
HSvm ^>= 0.1.1.3.22 HSvm ^>= 0.1.1.3.22
, KMP ^>= 0.2.0.0 , KMP ^>= 0.2.0.0
...@@ -868,9 +878,9 @@ executable gargantext-upgrade ...@@ -868,9 +878,9 @@ executable gargantext-upgrade
, text ^>= 1.2.4.1 , text ^>= 1.2.4.1
default-language: Haskell2010 default-language: Haskell2010
test-suite garg-test test-suite garg-test-tasty
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
main-is: Main.hs main-is: tasty/Main.hs
other-modules: other-modules:
Core.Text Core.Text
Core.Text.Corpus.Query Core.Text.Corpus.Query
...@@ -939,6 +949,7 @@ test-suite garg-test ...@@ -939,6 +949,7 @@ test-suite garg-test
, gargantext , gargantext
, gargantext-prelude , gargantext-prelude
, hspec ^>= 2.7.10 , hspec ^>= 2.7.10
, hspec-core
, hspec-expectations >= 0.8 && < 0.9 , hspec-expectations >= 0.8 && < 0.9
, http-client ^>= 0.6.4.1 , http-client ^>= 0.6.4.1
, http-client-tls ^>= 0.3.5.3 , http-client-tls ^>= 0.3.5.3
...@@ -961,9 +972,103 @@ test-suite garg-test ...@@ -961,9 +972,103 @@ test-suite garg-test
, tasty-hspec , tasty-hspec
, tasty-hunit , tasty-hunit
, tasty-quickcheck , tasty-quickcheck
, tasty-smallcheck
, text ^>= 1.2.4.1 , text ^>= 1.2.4.1
, time ^>= 1.9.3 , time ^>= 1.9.3
, tmp-postgres >= 1.34.1 && < 1.35 , tmp-postgres >= 1.34.1 && < 1.35
, unordered-containers ^>= 0.2.16.0 , unordered-containers ^>= 0.2.16.0
, validity ^>= 0.11.0.1 , validity ^>= 0.11.0.1
default-language: Haskell2010 default-language: Haskell2010
test-suite garg-test-hspec
type: exitcode-stdio-1.0
main-is: hspec/Main.hs
other-modules:
Database.Operations
Paths_gargantext
hs-source-dirs:
test
default-extensions:
DataKinds
DeriveGeneric
FlexibleContexts
FlexibleInstances
GADTs
GeneralizedNewtypeDeriving
ImportQualifiedPost
MultiParamTypeClasses
NamedFieldPuns
NoImplicitPrelude
OverloadedStrings
RankNTypes
RecordWildCards
StrictData
DataKinds
DeriveGeneric
FlexibleContexts
FlexibleInstances
GeneralizedNewtypeDeriving
ImportQualifiedPost
MultiParamTypeClasses
NoImplicitPrelude
OverloadedStrings
RankNTypes
ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
build-depends:
QuickCheck ^>= 2.14.2
, aeson ^>= 1.5.6.0
, async ^>= 2.2.4
, base ^>= 4.14.3.0
, boolexpr ^>= 0.2
, bytestring ^>= 0.10.12.0
, conduit ^>= 1.3.4.2
, containers ^>= 0.6.5.1
, crawlerArxiv
, duckling ^>= 0.2.0.0
, extra ^>= 1.7.9
, gargantext
, gargantext-prelude
, hspec ^>= 2.7.10
, hspec-core
, hspec-expectations >= 0.8 && < 0.9
, http-client ^>= 0.6.4.1
, http-client-tls ^>= 0.3.5.3
, lens >= 5.2.2 && < 5.3
, monad-control >= 1.0.3 && < 1.1
, mtl ^>= 2.2.2
, parsec ^>= 3.1.14.0
, patches-class ^>= 0.1.0.1
, patches-map ^>= 0.1.0.1
, postgres-options >= 0.2 && < 0.3
, postgresql-simple >= 0.6.4 && < 0.7
, quickcheck-instances ^>= 0.3.25.2
, raw-strings-qq
, recover-rtti >= 0.4 && < 0.5
, resource-pool >= 0.2.3.2 && < 0.2.4
, servant-job
, shelly >= 1.9 && < 2
, stm ^>= 2.5.0.1
, tasty ^>= 1.4.2.1
, tasty-hspec
, tasty-hunit
, tasty-quickcheck
, tasty-smallcheck
, text ^>= 1.2.4.1
, time ^>= 1.9.3
, tmp-postgres >= 1.34.1 && < 1.35
, unordered-containers ^>= 0.2.16.0
, validity ^>= 0.11.0.1
default-language: Haskell2010
benchmark garg-bench
main-is: Main.hs
hs-source-dirs: bench
type: exitcode-stdio-1.0
build-depends: base
, bytestring
, gargantext
, gargantext-prelude
, tasty-bench
ghc-options: "-with-rtsopts=-A32m"
if impl(ghc >= 8.6)
ghc-options: "-with-rtsopts=--nonmoving-gc"
...@@ -29,13 +29,14 @@ Pouillard (who mainly made it). ...@@ -29,13 +29,14 @@ Pouillard (who mainly made it).
{-# LANGUAGE BangPatterns #-} {-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
module Gargantext.API module Gargantext.API
where where
import Control.Concurrent import Control.Concurrent
import Control.Exception (catch, finally, SomeException{-, displayException, IOException-}) import Control.Exception (catch, finally, SomeException{-, displayException, IOException-})
import Control.Lens import Control.Lens hiding (Level)
import Control.Monad.Except import Control.Monad.Except
import Control.Monad.Reader (runReaderT) import Control.Monad.Reader (runReaderT)
import Data.Either import Data.Either
...@@ -46,9 +47,8 @@ import Data.Text.Encoding (encodeUtf8) ...@@ -46,9 +47,8 @@ import Data.Text.Encoding (encodeUtf8)
import Data.Text.IO (putStrLn) import Data.Text.IO (putStrLn)
import Data.Validity import Data.Validity
import GHC.Base (Applicative) import GHC.Base (Applicative)
import GHC.Generics (Generic)
import Gargantext.API.Admin.Auth.Types (AuthContext) import Gargantext.API.Admin.Auth.Types (AuthContext)
import Gargantext.API.Admin.EnvTypes (Env) import Gargantext.API.Admin.EnvTypes (Env, Mode(..))
import Gargantext.API.Admin.Settings (newEnv) import Gargantext.API.Admin.Settings (newEnv)
import Gargantext.API.Admin.Types (FireWall(..), PortNumber, cookieSettings, jwtSettings, settings) import Gargantext.API.Admin.Types (FireWall(..), PortNumber, cookieSettings, jwtSettings, settings)
import Gargantext.API.EKG import Gargantext.API.EKG
...@@ -69,14 +69,12 @@ import Servant ...@@ -69,14 +69,12 @@ import Servant
import System.FilePath import System.FilePath
import qualified Gargantext.Database.Prelude as DB import qualified Gargantext.Database.Prelude as DB
import qualified System.Cron.Schedule as Cron import qualified System.Cron.Schedule as Cron
import Gargantext.System.Logging
data Mode = Dev | Mock | Prod
deriving (Show, Read, Generic)
-- | startGargantext takes as parameters port number and Ini file. -- | startGargantext takes as parameters port number and Ini file.
startGargantext :: Mode -> PortNumber -> FilePath -> IO () startGargantext :: Mode -> PortNumber -> FilePath -> IO ()
startGargantext mode port file = do startGargantext mode port file = withLoggerHoisted mode $ \logger -> do
env <- newEnv port file env <- newEnv logger port file
runDbCheck env runDbCheck env
portRouteInfo port portRouteInfo port
app <- makeApp env app <- makeApp env
......
...@@ -2,15 +2,18 @@ ...@@ -2,15 +2,18 @@
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE LambdaCase #-}
module Gargantext.API.Admin.EnvTypes ( module Gargantext.API.Admin.EnvTypes (
GargJob(..) GargJob(..)
, Env(..) , Env(..)
, Mode(..)
, mkJobHandle , mkJobHandle
, env_logger , env_logger
, env_manager , env_manager
, env_self_url , env_self_url
, menv_firewall , menv_firewall
, dev_env_logger
, MockEnv(..) , MockEnv(..)
, DevEnv(..) , DevEnv(..)
...@@ -18,7 +21,7 @@ module Gargantext.API.Admin.EnvTypes ( ...@@ -18,7 +21,7 @@ module Gargantext.API.Admin.EnvTypes (
, ConcreteJobHandle -- opaque , ConcreteJobHandle -- opaque
) where ) where
import Control.Lens hiding ((:<)) import Control.Lens hiding (Level, (:<))
import Control.Monad.Except import Control.Monad.Except
import Control.Monad.Reader import Control.Monad.Reader
import Data.Pool (Pool) import Data.Pool (Pool)
...@@ -29,24 +32,62 @@ import Network.HTTP.Client (Manager) ...@@ -29,24 +32,62 @@ import Network.HTTP.Client (Manager)
import Servant.Client (BaseUrl) import Servant.Client (BaseUrl)
import Servant.Job.Async (HasJobEnv(..), Job) import Servant.Job.Async (HasJobEnv(..), Job)
import qualified Servant.Job.Async as SJ import qualified Servant.Job.Async as SJ
import System.Log.FastLogger
import qualified Servant.Job.Core import qualified Servant.Job.Core
import Gargantext.API.Admin.Types import Data.List ((\\))
import qualified Data.Text as T
import Gargantext.API.Admin.Orchestrator.Types import Gargantext.API.Admin.Orchestrator.Types
import Gargantext.API.Admin.Types
import Gargantext.API.Job import Gargantext.API.Job
import Gargantext.API.Prelude (GargM) import Gargantext.API.Prelude (GargM, GargError)
import Gargantext.Core.NodeStory
import Gargantext.Core.Mail.Types (HasMail, mailSettings) import Gargantext.Core.Mail.Types (HasMail, mailSettings)
import Gargantext.Core.NLP (NLPServerMap, HasNLPServer(..)) import Gargantext.Core.NLP (NLPServerMap, HasNLPServer(..))
import Gargantext.Core.NodeStory
import Gargantext.Database.Prelude (HasConnectionPool(..), HasConfig(..)) import Gargantext.Database.Prelude (HasConnectionPool(..), HasConfig(..))
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config (GargConfig(..)) import Gargantext.Prelude.Config (GargConfig(..))
import Gargantext.Prelude.Mail.Types (MailConfig) import Gargantext.Prelude.Mail.Types (MailConfig)
import Gargantext.System.Logging
import qualified System.Log.FastLogger as FL
import qualified Gargantext.Utils.Jobs.Monad as Jobs import qualified Gargantext.Utils.Jobs.Monad as Jobs
import Gargantext.Utils.Jobs.Map (LoggerM, J(..), jTask, rjGetLog) import Gargantext.Utils.Jobs.Map (LoggerM, J(..), jTask, rjGetLog)
data Mode = Dev | Mock | Prod
deriving (Show, Read, Generic)
-- | Given the 'Mode' the server is running in, it returns the list of
-- allowed levels. For example for production we ignore everything which
-- has priority lower than "warning".
modeToLoggingLevels :: Mode -> [LogLevel]
modeToLoggingLevels = \case
Dev -> [minBound .. maxBound]
Mock -> [minBound .. maxBound]
-- For production, accepts everything but DEBUG.
Prod -> [minBound .. maxBound] \\ [DEBUG]
instance MonadLogger (GargM Env GargError) where
getLogger = asks _env_logger
instance HasLogger (GargM Env GargError) where
data instance Logger (GargM Env GargError) =
GargLogger {
logger_mode :: Mode
, logger_set :: FL.LoggerSet
}
type instance LogInitParams (GargM Env GargError) = Mode
type instance LogPayload (GargM Env GargError) = FL.LogStr
initLogger = \mode -> do
logger_set <- liftIO $ FL.newStderrLoggerSet FL.defaultBufSize
pure $ GargLogger mode logger_set
destroyLogger = \GargLogger{..} -> liftIO $ FL.rmLoggerSet logger_set
logMsg = \(GargLogger mode logger_set) lvl msg -> do
let pfx = "[" <> show lvl <> "] "
when (lvl `elem` (modeToLoggingLevels mode)) $
liftIO $ FL.pushLogStrLn logger_set $ FL.toLogStr pfx <> msg
logTxt lgr lvl msg = logMsg lgr lvl (FL.toLogStr $ T.unpack msg)
data GargJob data GargJob
= TableNgramsJob = TableNgramsJob
| ForgotPasswordJob | ForgotPasswordJob
...@@ -72,7 +113,7 @@ data GargJob ...@@ -72,7 +113,7 @@ data GargJob
-- we need to remember to force the fields to WHNF at that point. -- we need to remember to force the fields to WHNF at that point.
data Env = Env data Env = Env
{ _env_settings :: ~Settings { _env_settings :: ~Settings
, _env_logger :: ~LoggerSet , _env_logger :: ~(Logger (GargM Env GargError))
, _env_pool :: ~(Pool Connection) , _env_pool :: ~(Pool Connection)
, _env_nodeStory :: ~NodeStoryEnv , _env_nodeStory :: ~NodeStoryEnv
, _env_manager :: ~Manager , _env_manager :: ~Manager
...@@ -186,6 +227,8 @@ instance Jobs.MonadJobStatus (GargM Env err) where ...@@ -186,6 +227,8 @@ instance Jobs.MonadJobStatus (GargM Env err) where
Just msg -> jobLogFailTotalWithMessage msg latest Just msg -> jobLogFailTotalWithMessage msg latest
) )
addMoreSteps steps jh = updateJobProgress jh (jobLogAddMore steps)
data MockEnv = MockEnv data MockEnv = MockEnv
{ _menv_firewall :: !FireWall { _menv_firewall :: !FireWall
} }
...@@ -193,9 +236,31 @@ data MockEnv = MockEnv ...@@ -193,9 +236,31 @@ data MockEnv = MockEnv
makeLenses ''MockEnv makeLenses ''MockEnv
instance MonadLogger (GargM DevEnv GargError) where
getLogger = asks _dev_env_logger
instance HasLogger (GargM DevEnv GargError) where
data instance Logger (GargM DevEnv GargError) =
GargDevLogger {
dev_logger_mode :: Mode
, dev_logger_set :: FL.LoggerSet
}
type instance LogInitParams (GargM DevEnv GargError) = Mode
type instance LogPayload (GargM DevEnv GargError) = FL.LogStr
initLogger = \mode -> do
dev_logger_set <- liftIO $ FL.newStderrLoggerSet FL.defaultBufSize
pure $ GargDevLogger mode dev_logger_set
destroyLogger = \GargDevLogger{..} -> liftIO $ FL.rmLoggerSet dev_logger_set
logMsg = \(GargDevLogger mode logger_set) lvl msg -> do
let pfx = "[" <> show lvl <> "] "
when (lvl `elem` (modeToLoggingLevels mode)) $
liftIO $ FL.pushLogStrLn logger_set $ FL.toLogStr pfx <> msg
logTxt lgr lvl msg = logMsg lgr lvl (FL.toLogStr $ T.unpack msg)
data DevEnv = DevEnv data DevEnv = DevEnv
{ _dev_env_settings :: !Settings { _dev_env_settings :: !Settings
, _dev_env_config :: !GargConfig , _dev_env_config :: !GargConfig
, _dev_env_logger :: !(Logger (GargM DevEnv GargError))
, _dev_env_pool :: !(Pool Connection) , _dev_env_pool :: !(Pool Connection)
, _dev_env_nodeStory :: !NodeStoryEnv , _dev_env_nodeStory :: !NodeStoryEnv
, _dev_env_mail :: !MailConfig , _dev_env_mail :: !MailConfig
...@@ -229,6 +294,8 @@ instance Jobs.MonadJobStatus (GargM DevEnv err) where ...@@ -229,6 +294,8 @@ instance Jobs.MonadJobStatus (GargM DevEnv err) where
markFailed _ _ = pure () markFailed _ _ = pure ()
addMoreSteps _ _ = pure ()
instance HasConfig DevEnv where instance HasConfig DevEnv where
hasConfig = dev_env_config hasConfig = dev_env_config
......
...@@ -37,12 +37,12 @@ import System.Directory ...@@ -37,12 +37,12 @@ import System.Directory
-- import System.FileLock (tryLockFile, unlockFile, SharedExclusive(Exclusive)) -- import System.FileLock (tryLockFile, unlockFile, SharedExclusive(Exclusive))
import System.IO (FilePath, hClose) import System.IO (FilePath, hClose)
import System.IO.Temp (withTempFile) import System.IO.Temp (withTempFile)
import System.Log.FastLogger
import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as L
import Gargantext.API.Admin.EnvTypes import Gargantext.API.Admin.EnvTypes
import Gargantext.API.Admin.Types import Gargantext.API.Admin.Types
import Gargantext.API.Prelude
-- import Gargantext.API.Ngrams.Types (NgramsRepo, HasRepo(..), RepoEnv(..), r_version, initRepo, renv_var, renv_lock) -- import Gargantext.API.Ngrams.Types (NgramsRepo, HasRepo(..), RepoEnv(..), r_version, initRepo, renv_var, renv_lock)
import Gargantext.Core.NLP (nlpServerMap) import Gargantext.Core.NLP (nlpServerMap)
import Gargantext.Database.Prelude (databaseParameters, hasConfig) import Gargantext.Database.Prelude (databaseParameters, hasConfig)
...@@ -54,6 +54,7 @@ import qualified Gargantext.Utils.Jobs as Jobs ...@@ -54,6 +54,7 @@ import qualified Gargantext.Utils.Jobs as Jobs
import qualified Gargantext.Utils.Jobs.Monad as Jobs import qualified Gargantext.Utils.Jobs.Monad as Jobs
import qualified Gargantext.Utils.Jobs.Queue as Jobs import qualified Gargantext.Utils.Jobs.Queue as Jobs
import qualified Gargantext.Utils.Jobs.Settings as Jobs import qualified Gargantext.Utils.Jobs.Settings as Jobs
import Gargantext.System.Logging
devSettings :: FilePath -> IO Settings devSettings :: FilePath -> IO Settings
devSettings jwkFile = do devSettings jwkFile = do
...@@ -176,8 +177,8 @@ readRepoEnv repoDir = do ...@@ -176,8 +177,8 @@ readRepoEnv repoDir = do
devJwkFile :: FilePath devJwkFile :: FilePath
devJwkFile = "dev.jwk" devJwkFile = "dev.jwk"
newEnv :: PortNumber -> FilePath -> IO Env newEnv :: Logger (GargM Env GargError) -> PortNumber -> FilePath -> IO Env
newEnv port file = do newEnv logger port file = do
!manager_env <- newTlsManager !manager_env <- newTlsManager
!settings' <- devSettings devJwkFile <&> appPort .~ port -- TODO read from 'file' !settings' <- devSettings devJwkFile <&> appPort .~ port -- TODO read from 'file'
when (port /= settings' ^. appPort) $ when (port /= settings' ^. appPort) $
...@@ -200,7 +201,6 @@ newEnv port file = do ...@@ -200,7 +201,6 @@ newEnv port file = do
& Jobs.l_jsJobTimeout .~ (fromIntegral $ config_env ^. hasConfig ^. gc_js_job_timeout) & Jobs.l_jsJobTimeout .~ (fromIntegral $ config_env ^. hasConfig ^. gc_js_job_timeout)
& Jobs.l_jsIDTimeout .~ (fromIntegral $ config_env ^. hasConfig ^. gc_js_id_timeout) & Jobs.l_jsIDTimeout .~ (fromIntegral $ config_env ^. hasConfig ^. gc_js_id_timeout)
!jobs_env <- Jobs.newJobEnv jobs_settings prios' manager_env !jobs_env <- Jobs.newJobEnv jobs_settings prios' manager_env
!logger <- newStderrLoggerSet defaultBufSize
!config_mail <- Mail.readConfig file !config_mail <- Mail.readConfig file
!nlp_env <- nlpServerMap <$> NLP.readConfig file !nlp_env <- nlpServerMap <$> NLP.readConfig file
......
...@@ -29,16 +29,17 @@ import qualified Gargantext.Prelude.Mail as Mail ...@@ -29,16 +29,17 @@ import qualified Gargantext.Prelude.Mail as Mail
import qualified Gargantext.Prelude.NLP as NLP import qualified Gargantext.Prelude.NLP as NLP
import Servant import Servant
import System.IO (FilePath) import System.IO (FilePath)
import Gargantext.System.Logging
type IniPath = FilePath type IniPath = FilePath
------------------------------------------------------------------- -------------------------------------------------------------------
withDevEnv :: IniPath -> (DevEnv -> IO a) -> IO a withDevEnv :: IniPath -> (DevEnv -> IO a) -> IO a
withDevEnv iniPath k = do withDevEnv iniPath k = withLoggerHoisted Dev $ \logger -> do
env <- newDevEnv env <- newDevEnv logger
k env -- `finally` cleanEnv env k env -- `finally` cleanEnv env
where where
newDevEnv = do newDevEnv logger = do
cfg <- readConfig iniPath cfg <- readConfig iniPath
dbParam <- databaseParameters iniPath dbParam <- databaseParameters iniPath
--nodeStory_env <- readNodeStoryEnv (_gc_repofilepath cfg) --nodeStory_env <- readNodeStoryEnv (_gc_repofilepath cfg)
...@@ -49,6 +50,7 @@ withDevEnv iniPath k = do ...@@ -49,6 +50,7 @@ withDevEnv iniPath k = do
nlp_config <- NLP.readConfig iniPath nlp_config <- NLP.readConfig iniPath
pure $ DevEnv pure $ DevEnv
{ _dev_env_pool = pool { _dev_env_pool = pool
, _dev_env_logger = logger
, _dev_env_nodeStory = nodeStory_env , _dev_env_nodeStory = nodeStory_env
, _dev_env_settings = setts , _dev_env_settings = setts
, _dev_env_config = cfg , _dev_env_config = cfg
......
...@@ -31,7 +31,7 @@ addErrorEvent message = addEvent "ERROR" message ...@@ -31,7 +31,7 @@ addErrorEvent message = addEvent "ERROR" message
jobLogProgress :: Int -> JobLog -> JobLog jobLogProgress :: Int -> JobLog -> JobLog
jobLogProgress n jl = over (scst_succeeded . _Just) (+ n) $ jobLogProgress n jl = over (scst_succeeded . _Just) (+ n) $
over (scst_remaining . _Just) (\x -> x - n) jl over (scst_remaining . _Just) (\x -> max 0 (x - n)) jl
-- | Mark a job as completely done, by adding the 'remaining' into 'succeeded'. -- | Mark a job as completely done, by adding the 'remaining' into 'succeeded'.
-- At the end 'scst_remaining' will be 0, and 'scst_succeeded' will be 'oldvalue + remaining'. -- At the end 'scst_remaining' will be 0, and 'scst_succeeded' will be 'oldvalue + remaining'.
...@@ -41,6 +41,9 @@ jobLogComplete jl = ...@@ -41,6 +41,9 @@ jobLogComplete jl =
in jl & over scst_succeeded (Just . maybe remainingNow ((+) remainingNow)) in jl & over scst_succeeded (Just . maybe remainingNow ((+) remainingNow))
& over scst_remaining (const (Just 0)) & over scst_remaining (const (Just 0))
jobLogAddMore :: Int -> JobLog -> JobLog
jobLogAddMore moreSteps jl = jl & over (scst_remaining . _Just) (+ moreSteps)
jobLogFailures :: Int -> JobLog -> JobLog jobLogFailures :: Int -> JobLog -> JobLog
jobLogFailures n jl = over (scst_failed . _Just) (+ n) $ jobLogFailures n jl = over (scst_failed . _Just) (+ n) $
over (scst_remaining . _Just) (\x -> x - n) jl over (scst_remaining . _Just) (\x -> x - n) jl
......
...@@ -21,20 +21,20 @@ module Gargantext.API.Node.Corpus.New ...@@ -21,20 +21,20 @@ module Gargantext.API.Node.Corpus.New
import Conduit import Conduit
import Control.Lens hiding (elements, Empty) import Control.Lens hiding (elements, Empty)
import Control.Monad
import Data.Aeson import Data.Aeson
import Data.Aeson.TH (deriveJSON) import Data.Aeson.TH (deriveJSON)
import qualified Data.ByteString.Base64 as BSB64 import Data.ByteString.Base64 qualified as BSB64
import Data.Conduit.Internal (zipSources) import Data.Conduit.Internal (zipSources)
import Data.Either import Data.Either
import Data.Swagger import Data.Swagger
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import Data.Text qualified as T
import EPO qualified
import GHC.Generics (Generic) import GHC.Generics (Generic)
import Servant import Servant
import Servant.Job.Utils (jsonOptions) import Servant.Job.Utils (jsonOptions)
-- import Servant.Multipart -- import Servant.Multipart
import qualified Data.Text.Encoding as TE import Data.Text.Encoding qualified as TE
-- import Test.QuickCheck (elements) -- import Test.QuickCheck (elements)
import Test.QuickCheck.Arbitrary import Test.QuickCheck.Arbitrary
...@@ -59,14 +59,14 @@ import Gargantext.Database.Admin.Types.Node (CorpusId, NodeType(..)) ...@@ -59,14 +59,14 @@ import Gargantext.Database.Admin.Types.Node (CorpusId, NodeType(..))
import Gargantext.Database.Prelude (hasConfig) import Gargantext.Database.Prelude (hasConfig)
import Gargantext.Database.Query.Table.Node (getNodeWith) import Gargantext.Database.Query.Table.Node (getNodeWith)
import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata) import Gargantext.Database.Query.Table.Node.UpdateOpaleye (updateHyperdata)
import Gargantext.Database.Query.Table.User (getUserPubmedAPIKey)
import Gargantext.Database.Schema.Node (node_hyperdata) import Gargantext.Database.Schema.Node (node_hyperdata)
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config (gc_max_docs_parsers) import Gargantext.Prelude.Config (gc_max_docs_parsers)
import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..)) import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..))
import qualified Gargantext.Core.Text.Corpus.API as API import Gargantext.Core.Text.Corpus.API qualified as API
import qualified Gargantext.Core.Text.Corpus.Parsers as Parser (FileType(..), parseFormatC) import Gargantext.Core.Text.Corpus.Parsers qualified as Parser (FileType(..), parseFormatC)
import qualified Gargantext.Database.GargDB as GargDB import Gargantext.Database.GargDB qualified as GargDB
import Gargantext.System.Logging
------------------------------------------------------------------------ ------------------------------------------------------------------------
{- {-
...@@ -145,12 +145,30 @@ data WithQuery = WithQuery ...@@ -145,12 +145,30 @@ data WithQuery = WithQuery
, _wq_lang :: !Lang , _wq_lang :: !Lang
, _wq_node_id :: !Int , _wq_node_id :: !Int
, _wq_flowListWith :: !FlowSocialListWith , _wq_flowListWith :: !FlowSocialListWith
, _wq_pubmedAPIKey :: !(Maybe Text)
, _wq_epoAuthKey :: !(Maybe EPO.AuthKey)
} }
deriving (Show, Eq, Generic) deriving (Show, Eq, Generic)
makeLenses ''WithQuery makeLenses ''WithQuery
instance FromJSON WithQuery where instance FromJSON WithQuery where
parseJSON = genericParseJSON $ jsonOptions "_wq_" parseJSON = genericParseJSON $ jsonOptions "_wq_"
-- parseJSON = withObject "WithQuery" $ \o -> do
-- _wq_query <- o .: "query"
-- _wq_databases <- o .: "databases"
-- _wq_datafield <- o .:? "datafield"
-- _wq_lang <- o .: "lang"
-- _wq_node_id <- o .: "node_id"
-- _wq_flowListWith <- o .: "flowListWith"
-- _wq_pubmedAPIKey <- o .:? "pubmedAPIKey"
-- _wq_epoAuthKey' <- o .:? "epoAuthKey" -- this is in form consumerKey:secretKey
-- let _wq_epoAuthKey = case (T.splitOn ":" <$> _wq_epoAuthKey') of
-- Just [consumer, secret] -> Just $ EPO.AuthKey { consumerKey = consumer
-- , secretKey = secret }
-- _ -> Nothing
-- pure $ WithQuery { .. }
instance ToJSON WithQuery where instance ToJSON WithQuery where
toJSON = genericToJSON $ jsonOptions "_wq_" toJSON = genericToJSON $ jsonOptions "_wq_"
instance ToSchema WithQuery where instance ToSchema WithQuery where
...@@ -163,6 +181,8 @@ instance Arbitrary WithQuery where ...@@ -163,6 +181,8 @@ instance Arbitrary WithQuery where
<*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary
<*> arbitrary
<*> arbitrary
------------------------------------------------------------------------ ------------------------------------------------------------------------
...@@ -200,17 +220,19 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q ...@@ -200,17 +220,19 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q
, _wq_databases = dbs , _wq_databases = dbs
, _wq_datafield = datafield , _wq_datafield = datafield
, _wq_lang = l , _wq_lang = l
, _wq_flowListWith = flw }) maybeLimit jobHandle = do , _wq_flowListWith = flw
, _wq_pubmedAPIKey = mPubmedAPIKey
, _wq_epoAuthKey = mEPOAuthKey }) maybeLimit jobHandle = do
-- TODO ... -- TODO ...
-- printDebug "[addToCorpusWithQuery] (cid, dbs)" (cid, dbs) $(logLocM) DEBUG $ T.pack $ "(cid, dbs) " <> show (cid, dbs)
-- printDebug "[addToCorpusWithQuery] datafield" datafield $(logLocM) DEBUG $ T.pack $ "datafield " <> show datafield
-- printDebug "[addToCorpusWithQuery] flowListWith" flw $(logLocM) DEBUG $ T.pack $ "flowListWith " <> show flw
addLanguageToCorpus cid l addLanguageToCorpus cid l
case datafield of case datafield of
Just Web -> do Just Web -> do
-- printDebug "[addToCorpusWithQuery] processing web request" datafield $(logLocM) DEBUG $ T.pack $ "processing web request " <> show datafield
markStarted 1 jobHandle markStarted 1 jobHandle
...@@ -225,21 +247,21 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q ...@@ -225,21 +247,21 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q
-- TODO if cid is folder -> create Corpus -- TODO if cid is folder -> create Corpus
-- if cid is corpus -> add to corpus -- if cid is corpus -> add to corpus
-- if cid is root -> create corpus in Private -- if cid is root -> create corpus in Private
-- printDebug "[G.A.N.C.New] getDataText with query" q $(logLocM) DEBUG $ T.pack $ "getDataText with query: " <> show q
let db = database2origin dbs let db = database2origin dbs
mPubmedAPIKey <- getUserPubmedAPIKey user -- mPubmedAPIKey <- getUserPubmedAPIKey user
-- printDebug "[addToCorpusWithQuery] mPubmedAPIKey" mPubmedAPIKey -- printDebug "[addToCorpusWithQuery] mPubmedAPIKey" mPubmedAPIKey
eTxt <- getDataText db (Multi l) q mPubmedAPIKey maybeLimit eTxt <- getDataText db (Multi l) q mPubmedAPIKey mEPOAuthKey maybeLimit
-- printDebug "[G.A.N.C.New] lTxts" lTxts -- printDebug "[G.A.N.C.New] lTxts" lTxts
case eTxt of case eTxt of
Right txt -> do Right txt -> do
-- TODO Sum lenghts of each txt elements -- TODO Sum lenghts of each txt elements
$(logLocM) DEBUG "Processing dataText results"
markProgress 1 jobHandle markProgress 1 jobHandle
void $ flowDataText user txt (Multi l) cid (Just flw) jobHandle corpusId <- flowDataText user txt (Multi l) cid (Just flw) jobHandle
-- printDebug "corpus id" cids $(logLocM) DEBUG $ T.pack $ "corpus id " <> show corpusId
-- printDebug "sending email" ("xxxxxxxxxxxxxxxxxxxxx" :: Text) -- printDebug "sending email" ("xxxxxxxxxxxxxxxxxxxxx" :: Text)
sendMail user sendMail user
-- TODO ... -- TODO ...
...@@ -247,6 +269,7 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q ...@@ -247,6 +269,7 @@ addToCorpusWithQuery user cid (WithQuery { _wq_query = q
Left err -> do Left err -> do
-- printDebug "Error: " err -- printDebug "Error: " err
$(logLocM) ERROR (T.pack $ show err)
markFailed (Just $ T.pack (show err)) jobHandle markFailed (Just $ T.pack (show err)) jobHandle
type AddWithForm = Summary "Add with FormUrlEncoded to corpus endpoint" type AddWithForm = Summary "Add with FormUrlEncoded to corpus endpoint"
......
...@@ -93,7 +93,7 @@ documentUpload nId doc = do ...@@ -93,7 +93,7 @@ documentUpload nId doc = do
Just c -> c Just c -> c
Nothing -> panic $ T.pack $ "[G.A.N.DU] Node has no corpus parent: " <> show nId Nothing -> panic $ T.pack $ "[G.A.N.DU] Node has no corpus parent: " <> show nId
(theFullDate, (year, month, day)) <- liftBase $ dateSplit EN (theFullDate, (year, month, day)) <- liftBase $ dateSplit
$ Just $ Just
$ view du_date doc $ view du_date doc
......
...@@ -49,6 +49,7 @@ import Servant ...@@ -49,6 +49,7 @@ import Servant
import Servant.Job.Async import Servant.Job.Async
import Servant.Job.Core (HasServerError(..), serverError) import Servant.Job.Core (HasServerError(..), serverError)
import qualified Servant.Job.Types as SJ import qualified Servant.Job.Types as SJ
import Gargantext.System.Logging
class HasJoseError e where class HasJoseError e where
_JoseError :: Prism' e Jose.Error _JoseError :: Prism' e Jose.Error
...@@ -88,7 +89,7 @@ type GargServerC env err m = ...@@ -88,7 +89,7 @@ type GargServerC env err m =
type GargServerT env err m api = GargServerC env err m => ServerT api m type GargServerT env err m api = GargServerC env err m => ServerT api m
type GargServer api = forall env err m. GargServerT env err m api type GargServer api = forall env err m. MonadLogger m => GargServerT env err m api
-- This is the concrete monad. It needs to be used as little as possible. -- This is the concrete monad. It needs to be used as little as possible.
type GargM env err = ReaderT env (ExceptT err IO) type GargM env err = ReaderT env (ExceptT err IO)
......
...@@ -17,6 +17,7 @@ module Gargantext.Core ...@@ -17,6 +17,7 @@ module Gargantext.Core
import Data.Aeson import Data.Aeson
import Data.Either(Either(Left)) import Data.Either(Either(Left))
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Data.LanguageCodes qualified as ISO639
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Morpheus.Types (GQLType) import Data.Morpheus.Types (GQLType)
import Data.Swagger import Data.Swagger
...@@ -45,6 +46,7 @@ import qualified Data.Map as Map ...@@ -45,6 +46,7 @@ import qualified Data.Map as Map
-- | All languages supported -- | All languages supported
-- NOTE: Use international country codes -- NOTE: Use international country codes
-- https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes -- https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
-- TODO This should be deprecated in favor of iso-639 library
data Lang = All data Lang = All
| DE | DE
| EL | EL
...@@ -93,6 +95,28 @@ instance Hashable Lang ...@@ -93,6 +95,28 @@ instance Hashable Lang
instance Arbitrary Lang where instance Arbitrary Lang where
arbitrary = arbitraryBoundedEnum arbitrary = arbitraryBoundedEnum
toISO639 :: Lang -> Maybe ISO639.ISO639_1
toISO639 DE = Just ISO639.DE
toISO639 EL = Just ISO639.EL
toISO639 EN = Just ISO639.EN
toISO639 ES = Just ISO639.ES
toISO639 FR = Just ISO639.FR
toISO639 IT = Just ISO639.IT
toISO639 PL = Just ISO639.PL
toISO639 PT = Just ISO639.PT
toISO639 RU = Just ISO639.RU
toISO639 UK = Just ISO639.UK
toISO639 ZH = Just ISO639.ZH
toISO639 All = Nothing
toISO639EN :: Lang -> ISO639.ISO639_1
toISO639EN l = fromMaybe ISO639.EN $ toISO639 l
iso639ToText :: ISO639.ISO639_1 -> Text
iso639ToText la = pack [a, b]
where
(a, b) = ISO639.toChars la
-- | https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes -- | https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
toISO639Lang :: Lang -> Maybe Text toISO639Lang :: Lang -> Maybe Text
toISO639Lang All = Nothing toISO639Lang All = Nothing
...@@ -108,6 +132,20 @@ toISO639Lang RU = Just "ru" ...@@ -108,6 +132,20 @@ toISO639Lang RU = Just "ru"
toISO639Lang UK = Just "uk" toISO639Lang UK = Just "uk"
toISO639Lang ZH = Just "zh" toISO639Lang ZH = Just "zh"
toGargLang :: ISO639.ISO639_1 -> Maybe Lang
toGargLang ISO639.DE = Just DE
toGargLang ISO639.EL = Just EL
toGargLang ISO639.EN = Just EN
toGargLang ISO639.ES = Just ES
toGargLang ISO639.FR = Just FR
toGargLang ISO639.IT = Just IT
toGargLang ISO639.PL = Just PL
toGargLang ISO639.PT = Just PT
toGargLang ISO639.RU = Just RU
toGargLang ISO639.UK = Just UK
toGargLang ISO639.ZH = Just ZH
toGargLang _ = Nothing
allLangs :: [Lang] allLangs :: [Lang]
allLangs = [minBound .. maxBound] allLangs = [minBound .. maxBound]
......
...@@ -19,13 +19,15 @@ module Gargantext.Core.Text.Corpus.API ...@@ -19,13 +19,15 @@ module Gargantext.Core.Text.Corpus.API
) where ) where
import Conduit import Conduit
import Control.Monad.Except
import Data.Bifunctor import Data.Bifunctor
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.LanguageCodes qualified as ISO639
import Data.Maybe import Data.Maybe
import Data.Text qualified as T import Data.Text qualified as T
import EPO qualified as EPO import EPO qualified
import Gargantext.API.Admin.Orchestrator.Types (ExternalAPIs(..), externalAPIs) import Gargantext.API.Admin.Orchestrator.Types (ExternalAPIs(..), externalAPIs)
import Gargantext.Core (Lang(..)) import Gargantext.Core (Lang(..), toISO639)
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..)) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..))
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Core.Text.Corpus.API.Arxiv qualified as Arxiv import Gargantext.Core.Text.Corpus.API.Arxiv qualified as Arxiv
...@@ -56,20 +58,27 @@ get :: ExternalAPIs ...@@ -56,20 +58,27 @@ get :: ExternalAPIs
-- -> IO [HyperdataDocument] -- -> IO [HyperdataDocument]
-> IO (Either GetCorpusError (Maybe Integer, ConduitT () HyperdataDocument IO ())) -> IO (Either GetCorpusError (Maybe Integer, ConduitT () HyperdataDocument IO ()))
get externalAPI la q mPubmedAPIKey mEPOAuthKey limit = do get externalAPI la q mPubmedAPIKey mEPOAuthKey limit = do
case Corpus.parseQuery q of -- For PUBMED, HAL, IsTex, Isidore and OpenAlex, we want to send the query as-it.
Left err -> pure $ Left $ InvalidInputQuery q (T.pack err) -- For Arxiv we parse the query into a structured boolean query we submit over.
Right corpusQuery -> case externalAPI of case externalAPI of
OpenAlex -> first ExternalAPIError <$> PubMed ->
OpenAlex.get (fromMaybe "" Nothing {- email -}) q la limit first ExternalAPIError <$> PUBMED.get (fromMaybe "" mPubmedAPIKey) q limit
PubMed -> first ExternalAPIError <$> OpenAlex ->
PUBMED.get (fromMaybe "" mPubmedAPIKey) corpusQuery limit first ExternalAPIError <$> OpenAlex.get (fromMaybe "" Nothing {- email -}) q langISO639 limit
--docs <- PUBMED.get q default_limit -- EN only by default Arxiv -> runExceptT $ do
--pure (Just $ fromIntegral $ length docs, yieldMany docs) corpusQuery <- ExceptT (pure parse_query)
Arxiv -> Right <$> Arxiv.get la corpusQuery limit ExceptT $ fmap Right (Arxiv.get la corpusQuery limit)
HAL -> first ExternalAPIError <$> HAL.getC la (Corpus.getRawQuery q) (Corpus.getLimit <$> limit) HAL ->
IsTex -> do docs <- ISTEX.get la (Corpus.getRawQuery q) (Corpus.getLimit <$> limit) first ExternalAPIError <$> HAL.getC langISO639 (Corpus.getRawQuery q) (Corpus.getLimit <$> limit)
pure $ Right (Just $ fromIntegral $ length docs, yieldMany docs) IsTex -> do
Isidore -> do docs <- ISIDORE.get la (Corpus.getLimit <$> limit) (Just $ Corpus.getRawQuery q) Nothing docs <- ISTEX.get la (Corpus.getRawQuery q) (Corpus.getLimit <$> limit)
pure $ Right (Just $ fromIntegral $ length docs, yieldMany docs) pure $ Right (Just $ fromIntegral $ length docs, yieldMany docs)
EPO -> first ExternalAPIError <$> Isidore -> do
EPOAPI.get mEPOAuthKey q la limit docs <- ISIDORE.get la (Corpus.getLimit <$> limit) (Just $ Corpus.getRawQuery q) Nothing
pure $ Right (Just $ fromIntegral $ length docs, yieldMany docs)
EPO -> do
(count, docsC) <- EPOAPI.get mEPOAuthKey q (fromMaybe ISO639.EN langISO639) limit
pure $ Right (Just count, docsC)
where
parse_query = first (InvalidInputQuery q . T.pack) $ Corpus.parseQuery q
langISO639 = toISO639 la
...@@ -18,7 +18,7 @@ import Data.LanguageCodes qualified as ISO639 ...@@ -18,7 +18,7 @@ import Data.LanguageCodes qualified as ISO639
import Data.Map.Strict qualified as Map import Data.Map.Strict qualified as Map
import Data.Maybe import Data.Maybe
import Data.Text (Text, pack, intercalate) import Data.Text (Text, pack, intercalate)
import Gargantext.Core (Lang(..)) -- import Gargantext.Core (Lang(..), toISO639Lang)
import Gargantext.Core.Text.Corpus.Parsers.Date qualified as Date import Gargantext.Core.Text.Corpus.Parsers.Date qualified as Date
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..)) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..))
import Gargantext.Defaults qualified as Defaults import Gargantext.Defaults qualified as Defaults
...@@ -28,40 +28,29 @@ import HAL.Client qualified as HAL ...@@ -28,40 +28,29 @@ import HAL.Client qualified as HAL
import HAL.Doc.Corpus qualified as HAL import HAL.Doc.Corpus qualified as HAL
import Servant.Client (ClientError) import Servant.Client (ClientError)
toLang :: Lang -> Maybe ISO639.ISO639_1 get :: Maybe ISO639.ISO639_1 -> Text -> Maybe Int -> IO [HyperdataDocument]
toLang DE = Just ISO639.DE
toLang EL = Just ISO639.EL
toLang EN = Just ISO639.EN
toLang ES = Just ISO639.ES
toLang FR = Just ISO639.FR
toLang IT = Just ISO639.IT
toLang PL = Just ISO639.PL
toLang PT = Just ISO639.PT
toLang RU = Just ISO639.RU
toLang UK = Just ISO639.UK
toLang ZH = Just ISO639.ZH
toLang All = Nothing
get :: Lang -> Text -> Maybe Int -> IO [HyperdataDocument]
get la q ml = do get la q ml = do
eDocs <- HAL.getMetadataWith [q] (Just 0) (fromIntegral <$> ml) (toLang la) eDocs <- HAL.getMetadataWith [q] (Just 0) (fromIntegral <$> ml) la
either (panic . pack . show) (\d -> mapM (toDoc' la) $ HAL._docs d) eDocs either (panic . pack . show) (\d -> mapM (toDoc' la) $ HAL._docs d) eDocs
getC :: Lang -> Text -> Maybe Int -> IO (Either ClientError (Maybe Integer, ConduitT () HyperdataDocument IO ())) getC :: Maybe ISO639.ISO639_1
-> Text
-> Maybe Int
-> IO (Either ClientError (Maybe Integer, ConduitT () HyperdataDocument IO ()))
getC la q ml = do getC la q ml = do
eRes <- HAL.getMetadataWithC [q] (Just 0) (fromIntegral <$> ml) (toLang la) eRes <- HAL.getMetadataWithC [q] (Just 0) (fromIntegral <$> ml) la
pure $ (\(len, docsC) -> (len, docsC .| mapMC (toDoc' la))) <$> eRes pure $ (\(len, docsC) -> (len, docsC .| mapMC (toDoc' la))) <$> eRes
-- case eRes of -- case eRes of
-- Left err -> panic $ pack $ show err -- Left err -> panic $ pack $ show err
-- Right (len, docsC) -> pure (len, docsC .| mapMC (toDoc' la)) -- Right (len, docsC) -> pure (len, docsC .| mapMC (toDoc' la))
toDoc' :: Lang -> HAL.Corpus -> IO HyperdataDocument toDoc' :: Maybe ISO639.ISO639_1 -> HAL.Corpus -> IO HyperdataDocument
toDoc' la (HAL.Corpus { .. }) = do toDoc' la (HAL.Corpus { .. }) = do
-- printDebug "[toDoc corpus] h" h -- printDebug "[toDoc corpus] h" h
(utctime, (pub_year, pub_month, pub_day)) <- (utctime, (pub_year, pub_month, pub_day)) <-
Date.dateSplit la (maybe (Just $ pack $ show Defaults.year) Just _corpus_date) Date.dateSplit (maybe (Just $ pack $ show Defaults.year) Just _corpus_date)
let abstractDefault = intercalate " " _corpus_abstract let abstractDefault = intercalate " " _corpus_abstract
let abstract = case toLang la of let abstract = case la of
Nothing -> abstractDefault Nothing -> abstractDefault
Just l -> fromMaybe abstractDefault (intercalate " " <$> Map.lookup l _corpus_abstract_lang_map) Just l -> fromMaybe abstractDefault (intercalate " " <$> Map.lookup l _corpus_abstract_lang_map)
pure HyperdataDocument { _hd_bdd = Just "Hal" pure HyperdataDocument { _hd_bdd = Just "Hal"
......
...@@ -35,13 +35,13 @@ get la l q a = do ...@@ -35,13 +35,13 @@ get la l q a = do
let let
printErr (DecodeFailure e _) = panic e printErr (DecodeFailure e _) = panic e
printErr e = panic (cs $ show e) printErr e = panic (cs $ show e)
toIsidoreDocs :: Reply -> [IsidoreDoc] toIsidoreDocs :: Reply -> [IsidoreDoc]
toIsidoreDocs (ReplyOnly r) = [r] toIsidoreDocs (ReplyOnly r) = [r]
toIsidoreDocs (Replies rs) = rs toIsidoreDocs (Replies rs) = rs
iDocs <- either printErr _content <$> Isidore.get l q a iDocs <- either printErr _content <$> Isidore.get l q a
hDocs <- mapM (\d -> isidoreToDoc la d) (toIsidoreDocs iDocs) hDocs <- mapM (\d -> isidoreToDoc la d) (toIsidoreDocs iDocs)
pure hDocs pure hDocs
...@@ -58,7 +58,7 @@ isidoreToDoc l (IsidoreDoc t a d u s as) = do ...@@ -58,7 +58,7 @@ isidoreToDoc l (IsidoreDoc t a d u s as) = do
author :: Author -> Text author :: Author -> Text
author (Author fn ln) = (_name fn) <> ", " <> (_name ln) author (Author fn ln) = (_name fn) <> ", " <> (_name ln)
author (Authors aus) = Text.intercalate ". " $ map author aus author (Authors aus) = Text.intercalate ". " $ map author aus
creator2text :: Creator -> Text creator2text :: Creator -> Text
creator2text (Creator au) = author au creator2text (Creator au) = author au
creator2text (Creators aus') = Text.intercalate ". " $ map author aus' creator2text (Creators aus') = Text.intercalate ". " $ map author aus'
...@@ -67,9 +67,9 @@ isidoreToDoc l (IsidoreDoc t a d u s as) = do ...@@ -67,9 +67,9 @@ isidoreToDoc l (IsidoreDoc t a d u s as) = do
langText (LangText _l t1) = t1 langText (LangText _l t1) = t1
langText (OnlyText t2 ) = t2 langText (OnlyText t2 ) = t2
langText (ArrayText ts ) = Text.intercalate " " $ map langText ts langText (ArrayText ts ) = Text.intercalate " " $ map langText ts
(utcTime, (pub_year, pub_month, pub_day)) <- Date.dateSplit l (maybe (Just $ Text.pack $ show Defaults.year) (Just) d) (utcTime, (pub_year, pub_month, pub_day)) <- Date.dateSplit (maybe (Just $ Text.pack $ show Defaults.year) (Just) d)
pure HyperdataDocument pure HyperdataDocument
{ _hd_bdd = Just "Isidore" { _hd_bdd = Just "Isidore"
, _hd_doi = Nothing , _hd_doi = Nothing
...@@ -91,5 +91,3 @@ isidoreToDoc l (IsidoreDoc t a d u s as) = do ...@@ -91,5 +91,3 @@ isidoreToDoc l (IsidoreDoc t a d u s as) = do
, _hd_publication_second = Nothing , _hd_publication_second = Nothing
, _hd_language_iso2 = Just $ (Text.pack . show) l , _hd_language_iso2 = Just $ (Text.pack . show) l
} }
...@@ -82,7 +82,7 @@ toDoc :: Lang -> ISTEX.Document -> IO HyperdataDocument ...@@ -82,7 +82,7 @@ toDoc :: Lang -> ISTEX.Document -> IO HyperdataDocument
toDoc la (ISTEX.Document i t a ab d s) = do toDoc la (ISTEX.Document i t a ab d s) = do
--printDebug "ISTEX date" d --printDebug "ISTEX date" d
(utctime, (pub_year, pub_month, pub_day)) <- (utctime, (pub_year, pub_month, pub_day)) <-
Date.dateSplit la (maybe (Just $ pack $ show Defaults.year) (Just . pack . show) d) Date.dateSplit (maybe (Just $ pack $ show Defaults.year) (Just . pack . show) d)
--printDebug "toDoc Istex" (utctime, (pub_year, pub_month, pub_day)) --printDebug "toDoc Istex" (utctime, (pub_year, pub_month, pub_day))
pure $ HyperdataDocument { _hd_bdd = Just "Istex" pure $ HyperdataDocument { _hd_bdd = Just "Istex"
, _hd_doi = Just i , _hd_doi = Just i
......
...@@ -10,8 +10,9 @@ Portability : POSIX ...@@ -10,8 +10,9 @@ Portability : POSIX
module Gargantext.Core.Text.Corpus.API.OpenAlex where module Gargantext.Core.Text.Corpus.API.OpenAlex where
import Conduit import Conduit
import Data.LanguageCodes qualified as ISO639
import Data.Text qualified as T import Data.Text qualified as T
import Gargantext.Core (Lang, toISO639Lang) import Gargantext.Core (iso639ToText)
import Gargantext.Core.Text.Corpus.Query as Corpus import Gargantext.Core.Text.Corpus.Query as Corpus
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..)) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataDocument(..))
import Protolude import Protolude
...@@ -22,12 +23,12 @@ import Servant.Client (ClientError) ...@@ -22,12 +23,12 @@ import Servant.Client (ClientError)
get :: Text get :: Text
-> Corpus.RawQuery -> Corpus.RawQuery
-> Lang -> Maybe ISO639.ISO639_1
-> Maybe Limit -> Maybe Limit
-> IO (Either ClientError (Maybe Integer, ConduitT () HyperdataDocument IO ())) -> IO (Either ClientError (Maybe Integer, ConduitT () HyperdataDocument IO ()))
get _email q lang mLimit = do get _email q lang mLimit = do
let limit = getLimit $ fromMaybe 10000 mLimit let limit = getLimit $ fromMaybe 1000 mLimit
let mFilter = (\l -> "language:" <> l) <$> toISO639Lang lang let mFilter = (\l -> "language:" <> iso639ToText l) <$> lang
eRes <- OA.fetchWorksC Nothing mFilter $ Just $ Corpus.getRawQuery q eRes <- OA.fetchWorksC Nothing mFilter $ Just $ Corpus.getRawQuery q
pure $ (\(len, docsC) -> (len, docsC .| takeC limit .| mapC toDoc)) <$> eRes pure $ (\(len, docsC) -> (len, docsC .| takeC limit .| mapC toDoc)) <$> eRes
......
...@@ -45,6 +45,15 @@ import PUBMED.Types (Config(..)) ...@@ -45,6 +45,15 @@ import PUBMED.Types (Config(..))
-- | A pubmed query. -- | A pubmed query.
-- See: https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.ESearch -- See: https://www.ncbi.nlm.nih.gov/books/NBK25499/#chapter4.ESearch
-- The documentation for PUBMED says:
-- Values for query keys may also be provided in term if they are preceeded by a
-- '#' (%23 in the URL). While only one query_key parameter can be provided to ESearch,
-- any number of query keys can be combined in term. Also, if query keys are provided in term,
-- they can be combined with OR or NOT in addition to AND.
-- Example:
-- esearch.fcgi?db=pubmed&term=%231+AND+asthma&WebEnv=<webenv string>&usehistory=y
--
-- Therefore, we can pretty-print our 'Query' back into something that PubMed could understand.
newtype ESearch = ESearch { _ESearch :: [EscapeItem] } newtype ESearch = ESearch { _ESearch :: [EscapeItem] }
deriving stock (Show, Eq) deriving stock (Show, Eq)
deriving newtype (Semigroup, Monoid) deriving newtype (Semigroup, Monoid)
...@@ -86,24 +95,15 @@ convertQuery q = ESearch (interpretQuery q transformAST) ...@@ -86,24 +95,15 @@ convertQuery q = ESearch (interpretQuery q transformAST)
BConst (Negative (Term term)) BConst (Negative (Term term))
-> [QN "NOT+", QE (TE.encodeUtf8 term)] -> [QN "NOT+", QE (TE.encodeUtf8 term)]
-- | TODO put default pubmed query in gargantext.ini
-- by default: 10K docs
get :: Text get :: Text
-> Corpus.Query -> Corpus.RawQuery
-> Maybe Limit -> Maybe Limit
-> IO (Either ClientError (Maybe Integer, ConduitT () HyperdataDocument IO ())) -> IO (Either ClientError (Maybe Integer, ConduitT () HyperdataDocument IO ()))
get apiKey q l = do get apiKey q l = do
-- The documentation for PUBMED says: -- NOTE(adinapoli): For now we do not interpret the PUBMED query into something
-- Values for query keys may also be provided in term if they are preceeded by a -- more structured, like an 'ESearch' term, but we could, in the future.
-- '#' (%23 in the URL). While only one query_key parameter can be provided to ESearch,
-- any number of query keys can be combined in term. Also, if query keys are provided in term,
-- they can be combined with OR or NOT in addition to AND.
-- Example:
-- esearch.fcgi?db=pubmed&term=%231+AND+asthma&WebEnv=<webenv string>&usehistory=y
--
-- Therefore, we can pretty-print our 'Query' back into something that PubMed could understand.
eRes <- runReaderT PubMed.getMetadataWithC (Config { apiKey = Just apiKey eRes <- runReaderT PubMed.getMetadataWithC (Config { apiKey = Just apiKey
, query = getESearch $ convertQuery q , query = getRawQuery q
, perPage = Just 200 , perPage = Just 200
, mWebEnv = Nothing }) , mWebEnv = Nothing })
let takeLimit = case l of let takeLimit = case l of
......
...@@ -236,7 +236,7 @@ toDoc ff d = do ...@@ -236,7 +236,7 @@ toDoc ff d = do
let dateToParse = DT.replace " " "" <$> lookup "PY" d -- <> Just " " <> lookup "publication_date" d let dateToParse = DT.replace " " "" <$> lookup "PY" d -- <> Just " " <> lookup "publication_date" d
-- printDebug "[G.C.T.C.Parsers] dateToParse" dateToParse -- printDebug "[G.C.T.C.Parsers] dateToParse" dateToParse
(utcTime, (pub_year, pub_month, pub_day)) <- Date.dateSplit lang dateToParse (utcTime, (pub_year, pub_month, pub_day)) <- Date.dateSplit dateToParse
let hd = HyperdataDocument { _hd_bdd = Just $ DT.pack $ show ff let hd = HyperdataDocument { _hd_bdd = Just $ DT.pack $ show ff
, _hd_doi = lookup "doi" d , _hd_doi = lookup "doi" d
......
...@@ -48,10 +48,10 @@ import qualified Data.List as List ...@@ -48,10 +48,10 @@ import qualified Data.List as List
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | Parse date to Ints -- | Parse date to Ints
-- TODO add hours, minutes and seconds -- TODO add hours, minutes and seconds
dateSplit :: Lang -> Maybe Text -> IO (Maybe UTCTime, (Maybe Year, Maybe Month, Maybe Day)) dateSplit :: Maybe Text -> IO (Maybe UTCTime, (Maybe Year, Maybe Month, Maybe Day))
dateSplit _ Nothing = pure (Nothing, (Nothing, Nothing, Nothing)) dateSplit Nothing = pure (Nothing, (Nothing, Nothing, Nothing))
dateSplit l (Just txt) = do dateSplit (Just txt) = do
utcTime <- parse l txt utcTime <- parse txt
let (y, m, d) = split' utcTime let (y, m, d) = split' utcTime
pure (Just utcTime, (Just y, Just m,Just d)) pure (Just utcTime, (Just y, Just m,Just d))
...@@ -72,15 +72,15 @@ type Day = Int ...@@ -72,15 +72,15 @@ type Day = Int
-- 1900-04-01 19:00:00 UTC -- 1900-04-01 19:00:00 UTC
-- >>> parse EN (pack "April 1 1900") -- >>> parse EN (pack "April 1 1900")
-- 1900-04-01 00:00:00 UTC -- 1900-04-01 00:00:00 UTC
parse :: Lang -> Text -> IO UTCTime parse :: Text -> IO UTCTime
parse lang s = do parse s = do
-- printDebug "Date: " s -- printDebug "Date: " s
let result = dateFlow (DucklingFailure s) let result = dateFlow (DucklingFailure s)
--printDebug "Date': " dateStr' --printDebug "Date': " dateStr'
case result of case result of
DateFlowSuccess ok -> pure ok DateFlowSuccess ok -> pure ok
DateFlowFailure -> (withDebugMode (DebugMode True) DateFlowFailure -> (withDebugMode (DebugMode True)
"[G.C.T.P.T.Date parse]" (lang,s) "[G.C.T.P.T.Date parse]" s
$ getCurrentTime) $ getCurrentTime)
_ -> panic "[G.C.T.C.Parsers.Date] parse: Should not happen" _ -> panic "[G.C.T.C.Parsers.Date] parse: Should not happen"
...@@ -206,4 +206,3 @@ parseDateWithDuckling lang input options = do ...@@ -206,4 +206,3 @@ parseDateWithDuckling lang input options = do
--pure $ parseAndResolve (rulesFor (locale ctx) (HashSet.fromList [(This Time)])) input ctx --pure $ parseAndResolve (rulesFor (locale ctx) (HashSet.fromList [(This Time)])) input ctx
-- TODO check/test Options False or True -- TODO check/test Options False or True
analyze input contxt options $ HashSet.fromList [(Seal Time)] analyze input contxt options $ HashSet.fromList [(Seal Time)]
{-| {-|
Module : Gargantext.Core.Text.Corpus.Parsers.Wikidata Module : Gargantext.Core.Text.Corpus.Parsers.Wikidata
<<<<<<< HEAD <<<<<<< HEAD
Description : To query Wikidata Description : To query Wikidata
======= =======
Description : To query Wikidata Description : To query Wikidata
>>>>>>> dev-clustering >>>>>>> dev-clustering
...@@ -68,7 +68,7 @@ wikiPageToDocument m wr = do ...@@ -68,7 +68,7 @@ wikiPageToDocument m wr = do
source = Nothing source = Nothing
abstract = Just $ concat $ take m sections abstract = Just $ concat $ take m sections
(date, (year, month, day)) <- dateSplit EN $ head (date, (year, month, day)) <- dateSplit $ head
$ catMaybes $ catMaybes
[ wr ^. wr_yearStart [ wr ^. wr_yearStart
, wr ^. wr_yearEnd , wr ^. wr_yearEnd
...@@ -130,4 +130,3 @@ wikidataQuery n = List.unlines ...@@ -130,4 +130,3 @@ wikidataQuery n = List.unlines
," }" ," }"
," LIMIT " <> (cs $ show n) ," LIMIT " <> (cs $ show n)
] ]
...@@ -22,11 +22,13 @@ Source: https://en.wikipedia.org/wiki/Part-of-speech_tagging ...@@ -22,11 +22,13 @@ Source: https://en.wikipedia.org/wiki/Part-of-speech_tagging
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeOperators #-} {-# LANGUAGE TypeOperators #-}
module Gargantext.Core.Text.Terms.Multi.PosTagging module Gargantext.Core.Text.Terms.Multi.PosTagging where
where
import Control.Exception (catch, throwIO)
import Data.Aeson import Data.Aeson
import Data.ByteString.Lazy.Char8 qualified as BSL
import Data.ByteString.Lazy.Internal (ByteString) import Data.ByteString.Lazy.Internal (ByteString)
import Data.Map qualified as Map
import Data.Set (fromList) import Data.Set (fromList)
import Data.Text (Text, splitOn, pack, toLower) import Data.Text (Text, splitOn, pack, toLower)
import Gargantext.Core (Lang(..)) import Gargantext.Core (Lang(..))
...@@ -35,8 +37,6 @@ import Gargantext.Core.Types ...@@ -35,8 +37,6 @@ import Gargantext.Core.Types
import Gargantext.Prelude import Gargantext.Prelude
import Network.HTTP.Simple import Network.HTTP.Simple
import Network.URI (URI(..)) import Network.URI (URI(..))
import qualified Data.ByteString.Lazy.Char8 as BSL
import qualified Data.Map as Map
-- import qualified Gargantext.Utils.SpacyNLP as SpacyNLP -- import qualified Gargantext.Utils.SpacyNLP as SpacyNLP
...@@ -82,7 +82,15 @@ corenlp' uri lang txt = do ...@@ -82,7 +82,15 @@ corenlp' uri lang txt = do
req <- parseRequest $ req <- parseRequest $
"POST " <> show (uri { uriQuery = "?properties=" <> (BSL.unpack $ encode $ toJSON $ Map.fromList properties) }) "POST " <> show (uri { uriQuery = "?properties=" <> (BSL.unpack $ encode $ toJSON $ Map.fromList properties) })
-- curl -XPOST 'http://localhost:9000/?properties=%7B%22annotators%22:%20%22tokenize,ssplit,pos,ner%22,%20%22outputFormat%22:%20%22json%22%7D' -d 'hello world, hello' | jq . -- curl -XPOST 'http://localhost:9000/?properties=%7B%22annotators%22:%20%22tokenize,ssplit,pos,ner%22,%20%22outputFormat%22:%20%22json%22%7D' -d 'hello world, hello' | jq .
httpJSON $ setRequestBodyLBS (cs txt) req -- printDebug "[corenlp] sending body" $ (cs txt :: ByteString)
catch (httpJSON $ setRequestBodyLBS (cs txt) req) $ \e ->
case e of
JSONParseException _req res _err -> do
let body = getResponseBody res
printDebug "[corenlp'] request text" (cs txt :: ByteString)
printDebug "[corenlp'] response body (error)" body
throwIO e
JSONConversionException _req _res _err -> throwIO e
where where
properties_ :: [(Text, Text)] properties_ :: [(Text, Text)]
properties_ = case lang of properties_ = case lang of
......
{-# LANGUAGE CPP #-}
{-| {-|
Module : Gargantext.Core.Types.Individu Module : Gargantext.Core.Types.Individu
Description : Short description Description : Short description
...@@ -15,11 +17,11 @@ Individu defintions ...@@ -15,11 +17,11 @@ Individu defintions
module Gargantext.Core.Types.Individu module Gargantext.Core.Types.Individu
where where
import Data.Aeson
import Control.Monad.IO.Class (MonadIO) import Control.Monad.IO.Class (MonadIO)
import GHC.Generics (Generic) import Data.Aeson
import Data.Swagger import Data.Swagger
import Data.Text (Text, pack, reverse) import Data.Text (Text, pack, reverse)
import GHC.Generics (Generic)
import Gargantext.Database.Admin.Types.Node (NodeId, UserId) import Gargantext.Database.Admin.Types.Node (NodeId, UserId)
import Gargantext.Prelude hiding (reverse) import Gargantext.Prelude hiding (reverse)
import qualified Gargantext.Prelude.Crypto.Auth as Auth import qualified Gargantext.Prelude.Crypto.Auth as Auth
...@@ -68,8 +70,15 @@ toUserHash :: MonadIO m ...@@ -68,8 +70,15 @@ toUserHash :: MonadIO m
=> NewUser GargPassword => NewUser GargPassword
-> m (NewUser HashPassword) -> m (NewUser HashPassword)
toUserHash (NewUser u m (GargPassword p)) = do toUserHash (NewUser u m (GargPassword p)) = do
h <- Auth.createPasswordHash p salt <- Auth.newSalt
let h = Auth.hashPasswordWithSalt params salt (Auth.mkPassword p)
pure $ NewUser u m h pure $ NewUser u m h
where
#if TEST_CRYPTO
params = Auth.defaultParams { Auth.argon2MemoryCost = 4096 }
#else
params = Auth.defaultParams
#endif
-- TODO remove -- TODO remove
arbitraryUsersHash :: MonadIO m arbitraryUsersHash :: MonadIO m
......
...@@ -55,21 +55,34 @@ module Gargantext.Database.Action.Flow -- (flowDatabase, ngrams2list) ...@@ -55,21 +55,34 @@ module Gargantext.Database.Action.Flow -- (flowDatabase, ngrams2list)
import Conduit import Conduit
import Control.Lens hiding (elements, Indexed) import Control.Lens hiding (elements, Indexed)
import Control.Monad (void)
import Data.Aeson.TH (deriveJSON) import Data.Aeson.TH (deriveJSON)
import Data.Conduit qualified as C
import Data.Conduit.Internal (zipSources) import Data.Conduit.Internal (zipSources)
import Data.Conduit.List qualified as CL
import Data.Conduit.List qualified as CList
import Data.Either import Data.Either
import Data.Foldable (for_)
import Data.HashMap.Strict (HashMap) import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HashMap
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Data.List (concat) import Data.List (concat)
import Data.List qualified as List
import Data.Map.Strict (Map, lookup) import Data.Map.Strict (Map, lookup)
import Data.Map.Strict qualified as Map
import Data.Maybe (catMaybes) import Data.Maybe (catMaybes)
import Data.Monoid import Data.Monoid
import Data.Proxy import Data.Proxy
import Data.Set (Set) import Data.Set (Set)
import Data.Set qualified as Set
import Data.Swagger import Data.Swagger
import Data.Text qualified as T
import Data.Tuple.Extra (first, second) import Data.Tuple.Extra (first, second)
import EPO qualified
import GHC.Generics (Generic) import GHC.Generics (Generic)
import GHC.Num (fromInteger)
import Gargantext.API.Ngrams.Tools (getTermsWith) import Gargantext.API.Ngrams.Tools (getTermsWith)
import Gargantext.API.Ngrams.Types qualified as NT
import Gargantext.Core (Lang(..), PosTagAlgo(..)) import Gargantext.Core (Lang(..), PosTagAlgo(..))
import Gargantext.Core (withDefaultLanguage) import Gargantext.Core (withDefaultLanguage)
import Gargantext.Core.Ext.IMTUser (readFile_Annuaire) import Gargantext.Core.Ext.IMTUser (readFile_Annuaire)
...@@ -77,6 +90,7 @@ import Gargantext.Core.Flow.Types ...@@ -77,6 +90,7 @@ import Gargantext.Core.Flow.Types
import Gargantext.Core.NLP (nlpServerGet) import Gargantext.Core.NLP (nlpServerGet)
import Gargantext.Core.NodeStory (HasNodeStory) import Gargantext.Core.NodeStory (HasNodeStory)
import Gargantext.Core.Text import Gargantext.Core.Text
import Gargantext.Core.Text.Corpus.API qualified as API
import Gargantext.Core.Text.Corpus.Parsers (parseFile, FileFormat, FileType, splitOn) import Gargantext.Core.Text.Corpus.Parsers (parseFile, FileFormat, FileType, splitOn)
import Gargantext.Core.Text.List (buildNgramsLists) import Gargantext.Core.Text.List (buildNgramsLists)
import Gargantext.Core.Text.List.Group.WithStem ({-StopSize(..),-} GroupParams(..)) import Gargantext.Core.Text.List.Group.WithStem ({-StopSize(..),-} GroupParams(..))
...@@ -90,6 +104,7 @@ import Gargantext.Core.Types.Main ...@@ -90,6 +104,7 @@ import Gargantext.Core.Types.Main
import Gargantext.Core.Types.Query (Limit) import Gargantext.Core.Types.Query (Limit)
import Gargantext.Core.Utils (addTuples) import Gargantext.Core.Utils (addTuples)
import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger) import Gargantext.Core.Utils.Prefix (unPrefix, unPrefixSwagger)
import Gargantext.Data.HashMap.Strict.Utils qualified as HashMap
import Gargantext.Database.Action.Flow.List import Gargantext.Database.Action.Flow.List
import Gargantext.Database.Action.Flow.Types import Gargantext.Database.Action.Flow.Types
import Gargantext.Database.Action.Flow.Utils (insertDocNgrams, DocumentIdWithNgrams(..)) import Gargantext.Database.Action.Flow.Utils (insertDocNgrams, DocumentIdWithNgrams(..))
...@@ -97,11 +112,12 @@ import Gargantext.Database.Action.Metrics (updateNgramsOccurrences, updateContex ...@@ -97,11 +112,12 @@ import Gargantext.Database.Action.Metrics (updateNgramsOccurrences, updateContex
import Gargantext.Database.Action.Search (searchDocInDatabase) import Gargantext.Database.Action.Search (searchDocInDatabase)
import Gargantext.Database.Admin.Config (userMaster, corpusMasterName) import Gargantext.Database.Admin.Config (userMaster, corpusMasterName)
import Gargantext.Database.Admin.Types.Hyperdata import Gargantext.Database.Admin.Types.Hyperdata
import Gargantext.Database.Admin.Types.Node -- (HyperdataDocument(..), NodeType(..), NodeId, UserId, ListId, CorpusId, RootId, MasterCorpusId, MasterUserId) import Gargantext.Database.Admin.Types.Node hiding (DEBUG) -- (HyperdataDocument(..), NodeType(..), NodeId, UserId, ListId, CorpusId, RootId, MasterCorpusId, MasterUserId)
import Gargantext.Database.Prelude import Gargantext.Database.Prelude
import Gargantext.Database.Query.Table.ContextNodeNgrams2 import Gargantext.Database.Query.Table.ContextNodeNgrams2
import Gargantext.Database.Query.Table.Ngrams import Gargantext.Database.Query.Table.Ngrams
import Gargantext.Database.Query.Table.Node import Gargantext.Database.Query.Table.Node
import Gargantext.Database.Query.Table.Node.Document.Add qualified as Doc (add)
import Gargantext.Database.Query.Table.Node.Document.Insert -- (insertDocuments, ReturnId(..), addUniqIdsDoc, addUniqIdsContact, ToDbData(..)) import Gargantext.Database.Query.Table.Node.Document.Insert -- (insertDocuments, ReturnId(..), addUniqIdsDoc, addUniqIdsContact, ToDbData(..))
import Gargantext.Database.Query.Table.Node.Error (HasNodeError(..)) import Gargantext.Database.Query.Table.Node.Error (HasNodeError(..))
import Gargantext.Database.Query.Table.NodeContext (selectDocNodes) import Gargantext.Database.Query.Table.NodeContext (selectDocNodes)
...@@ -113,26 +129,16 @@ import Gargantext.Database.Schema.Node (node_hyperdata) ...@@ -113,26 +129,16 @@ import Gargantext.Database.Schema.Node (node_hyperdata)
import Gargantext.Database.Types import Gargantext.Database.Types
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Crypto.Hash (Hash) import Gargantext.Prelude.Crypto.Hash (Hash)
import Gargantext.System.Logging
import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..)) import Gargantext.Utils.Jobs (JobHandle, MonadJobStatus(..))
import PUBMED.Types qualified as PUBMED
import System.FilePath (FilePath) import System.FilePath (FilePath)
import qualified Data.Conduit as C
import qualified Data.Conduit.List as CL
import qualified Data.Conduit.List as CList
import qualified Data.HashMap.Strict as HashMap
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Gargantext.API.Ngrams.Types as NT
import qualified Gargantext.Core.Text.Corpus.API as API
import qualified Gargantext.Data.HashMap.Strict.Utils as HashMap
import qualified Gargantext.Database.Query.Table.Node.Document.Add as Doc (add)
import qualified PUBMED.Types as PUBMED
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- Imports for upgrade function -- Imports for upgrade function
import Gargantext.Database.Query.Tree.Root (getRootId) import Gargantext.Database.Query.Tree.Root (getRootId)
import Gargantext.Database.Query.Tree (findNodesId) import Gargantext.Database.Query.Tree (findNodesId)
import qualified Data.List as List
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- TODO use internal with API name (could be old data) -- TODO use internal with API name (could be old data)
data DataOrigin = InternalOrigin { _do_api :: API.ExternalAPIs } data DataOrigin = InternalOrigin { _do_api :: API.ExternalAPIs }
...@@ -166,12 +172,13 @@ getDataText :: FlowCmdM env err m ...@@ -166,12 +172,13 @@ getDataText :: FlowCmdM env err m
-> TermType Lang -> TermType Lang
-> API.RawQuery -> API.RawQuery
-> Maybe PUBMED.APIKey -> Maybe PUBMED.APIKey
-> Maybe EPO.AuthKey
-> Maybe API.Limit -> Maybe API.Limit
-> m (Either API.GetCorpusError DataText) -> m (Either API.GetCorpusError DataText)
getDataText (ExternalOrigin api) la q mPubmedAPIKey li = do getDataText (ExternalOrigin api) la q mPubmedAPIKey mEPOAuthKey li = do
eRes <- liftBase $ API.get api (_tt_lang la) q mPubmedAPIKey li eRes <- liftBase $ API.get api (_tt_lang la) q mPubmedAPIKey mEPOAuthKey li
pure $ DataNew <$> eRes pure $ DataNew <$> eRes
getDataText (InternalOrigin _) _la q _ _li = do getDataText (InternalOrigin _) _la q _ _ _li = do
(_masterUserId, _masterRootId, cId) <- getOrMk_RootWithCorpus (_masterUserId, _masterRootId, cId) <- getOrMk_RootWithCorpus
(UserName userMaster) (UserName userMaster)
(Left "") (Left "")
...@@ -186,7 +193,7 @@ getDataText_Debug :: FlowCmdM env err m ...@@ -186,7 +193,7 @@ getDataText_Debug :: FlowCmdM env err m
-> Maybe API.Limit -> Maybe API.Limit
-> m () -> m ()
getDataText_Debug a l q li = do getDataText_Debug a l q li = do
result <- getDataText a l q Nothing li result <- getDataText a l q Nothing Nothing li
case result of case result of
Left err -> liftBase $ putStrLn $ show err Left err -> liftBase $ putStrLn $ show err
Right res -> liftBase $ printDataText res Right res -> liftBase $ printDataText res
...@@ -205,12 +212,15 @@ flowDataText :: forall env err m. ...@@ -205,12 +212,15 @@ flowDataText :: forall env err m.
-> JobHandle m -> JobHandle m
-> m CorpusId -> m CorpusId
flowDataText u (DataOld ids) tt cid mfslw _ = do flowDataText u (DataOld ids) tt cid mfslw _ = do
$(logLocM) DEBUG $ T.pack $ "Found " <> show (length ids) <> " old node IDs"
(_userId, userCorpusId, listId) <- createNodes u (Right [cid]) corpusType (_userId, userCorpusId, listId) <- createNodes u (Right [cid]) corpusType
_ <- Doc.add userCorpusId ids _ <- Doc.add userCorpusId ids
flowCorpusUser (_tt_lang tt) u userCorpusId listId corpusType mfslw flowCorpusUser (_tt_lang tt) u userCorpusId listId corpusType mfslw
where where
corpusType = (Nothing :: Maybe HyperdataCorpus) corpusType = (Nothing :: Maybe HyperdataCorpus)
flowDataText u (DataNew (mLen, txtC)) tt cid mfslw jobHandle = flowDataText u (DataNew (mLen, txtC)) tt cid mfslw jobHandle = do
$(logLocM) DEBUG $ T.pack $ "Found " <> show mLen <> " new documents to process"
for_ (mLen <&> fromInteger) (`addMoreSteps` jobHandle)
flowCorpus u (Right [cid]) tt mfslw (mLen, (transPipe liftBase txtC)) jobHandle flowCorpus u (Right [cid]) tt mfslw (mLen, (transPipe liftBase txtC)) jobHandle
------------------------------------------------------------------------ ------------------------------------------------------------------------
...@@ -279,59 +289,23 @@ flow :: forall env err m a c. ...@@ -279,59 +289,23 @@ flow :: forall env err m a c.
flow c u cn la mfslw (mLength, docsC) jobHandle = do flow c u cn la mfslw (mLength, docsC) jobHandle = do
(_userId, userCorpusId, listId) <- createNodes u cn c (_userId, userCorpusId, listId) <- createNodes u cn c
-- TODO if public insertMasterDocs else insertUserDocs -- TODO if public insertMasterDocs else insertUserDocs
_ <- runConduit $ zipSources (yieldMany [1..]) docsC runConduit $ zipSources (yieldMany [1..]) docsC
.| CList.chunksOf 100 .| CList.chunksOf 100
.| mapMC insertDocs' .| mapM_C (\docs -> void $ insertDocs' docs >>= Doc.add userCorpusId)
.| mapM_C (\ids' -> do .| sinkNull
_ <- Doc.add userCorpusId ids'
pure ()) $(logLocM) DEBUG "Calling flowCorpusUser"
.| sinkList flowCorpusUser (la ^. tt_lang) u userCorpusId listId c mfslw
_ <- flowCorpusUser (la ^. tt_lang) u userCorpusId listId c mfslw
-- ids <- traverse (\(idx, doc) -> do
-- id <- insertMasterDocs c la doc
-- logStatus JobLog { _scst_succeeded = Just $ 1 + idx
-- , _scst_failed = Just 0
-- , _scst_remaining = Just $ length docs - idx
-- , _scst_events = Just []
-- }
-- pure id
-- ) (zip [1..] docs)
--printDebug "[flow] calling flowCorpusUser" (0 :: Int)
pure userCorpusId
--flowCorpusUser (la ^. tt_lang) u cn c ids mfslw
where where
insertDocs' :: [(Integer, a)] -> m [NodeId] insertDocs' :: [(Integer, a)] -> m [NodeId]
insertDocs' [] = pure [] insertDocs' [] = pure []
insertDocs' docs = do insertDocs' docs = do
-- printDebug "[flow] calling insertDoc, ([idx], mLength) = " (fst <$> docs, mLength) $(logLocM) DEBUG $ T.pack $ "calling insertDoc, ([idx], mLength) = " <> show (fst <$> docs, mLength)
ids <- insertMasterDocs c la (snd <$> docs) ids <- insertMasterDocs c la (snd <$> docs)
let maxIdx = maximum (fst <$> docs) markProgress (length docs) jobHandle
case mLength of
Nothing -> pure ()
Just _len -> do
let succeeded = fromIntegral (1 + maxIdx)
-- let remaining = fromIntegral (len - maxIdx)
-- Reconstruct the correct update state by using 'markStarted' and the other primitives.
-- We do this slightly awkward arithmetic such that when we call 'markProgress' we reduce
-- the number of 'remaining' of exactly '1 + maxIdx', and we will end up with a 'JobLog'
-- looking like this:
-- JobLog
-- { _scst_succeeded = Just $ fromIntegral $ 1 + maxIdx
-- , _scst_failed = Just 0
-- , _scst_remaining = Just $ fromIntegral $ len - maxIdx
-- , _scst_events = Just []
-- }
-- markStarted (remaining + succeeded) jobHandle
markProgress succeeded jobHandle
pure ids pure ids
------------------------------------------------------------------------ ------------------------------------------------------------------------
createNodes :: ( FlowCmdM env err m createNodes :: ( FlowCmdM env err m
, MkCorpus c , MkCorpus c
......
...@@ -30,6 +30,7 @@ import Gargantext.Database.Query.Table.Node.Error (HasNodeError) ...@@ -30,6 +30,7 @@ import Gargantext.Database.Query.Table.Node.Error (HasNodeError)
import Gargantext.Database.Prelude (CmdM) import Gargantext.Database.Prelude (CmdM)
import Gargantext.Database.Query.Table.Node.Document.Insert import Gargantext.Database.Query.Table.Node.Document.Insert
import Gargantext.Database.Query.Tree.Error (HasTreeError) import Gargantext.Database.Query.Tree.Error (HasTreeError)
import Gargantext.System.Logging
type FlowCmdM env err m = type FlowCmdM env err m =
( CmdM env err m ( CmdM env err m
...@@ -37,6 +38,7 @@ type FlowCmdM env err m = ...@@ -37,6 +38,7 @@ type FlowCmdM env err m =
, HasNodeError err , HasNodeError err
, HasInvalidError err , HasInvalidError err
, HasTreeError err , HasTreeError err
, MonadLogger m
) )
type FlowCorpus a = ( AddUniqId a type FlowCorpus a = ( AddUniqId a
......
...@@ -16,7 +16,7 @@ module Gargantext.Database.Action.User ...@@ -16,7 +16,7 @@ module Gargantext.Database.Action.User
import Data.Text (Text) import Data.Text (Text)
import Gargantext.Core.Types.Individu (User(..)) import Gargantext.Core.Types.Individu (User(..))
import Gargantext.Database.Admin.Types.Node import Gargantext.Database.Admin.Types.Node
import Gargantext.Database.Prelude (Cmd, DBCmd) import Gargantext.Database.Prelude (DBCmd)
import Gargantext.Database.Query.Table.Node import Gargantext.Database.Query.Table.Node
import Gargantext.Database.Query.Table.User import Gargantext.Database.Query.Table.User
import Gargantext.Database.Query.Table.Node.Error import Gargantext.Database.Query.Table.Node.Error
...@@ -24,14 +24,14 @@ import Gargantext.Database.Schema.Node ...@@ -24,14 +24,14 @@ import Gargantext.Database.Schema.Node
import Gargantext.Prelude import Gargantext.Prelude
------------------------------------------------------------------------ ------------------------------------------------------------------------
getUserLightWithId :: HasNodeError err => Int -> Cmd err UserLight getUserLightWithId :: HasNodeError err => UserId -> DBCmd err UserLight
getUserLightWithId i = do getUserLightWithId i = do
candidates <- head <$> getUsersWithId (UserDBId i) candidates <- head <$> getUsersWithId (UserDBId i)
case candidates of case candidates of
Nothing -> nodeError NoUserFound Nothing -> nodeError NoUserFound
Just u -> pure u Just u -> pure u
getUserLightDB :: HasNodeError err => User -> Cmd err UserLight getUserLightDB :: HasNodeError err => User -> DBCmd err UserLight
getUserLightDB u = do getUserLightDB u = do
userId <- getUserId u userId <- getUserId u
userLight <- getUserLightWithId userId userLight <- getUserLightWithId userId
......
...@@ -28,6 +28,7 @@ import Gargantext.Core.Mail ...@@ -28,6 +28,7 @@ import Gargantext.Core.Mail
import Gargantext.Core.Mail.Types (HasMail, mailSettings) import Gargantext.Core.Mail.Types (HasMail, mailSettings)
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import Gargantext.Database.Action.Flow (getOrMkRoot) import Gargantext.Database.Action.Flow (getOrMkRoot)
import Gargantext.Database.Admin.Types.Node
import Gargantext.Database.Prelude import Gargantext.Database.Prelude
import Gargantext.Database.Query.Table.Node.Error (HasNodeError(..), nodeError, NodeError(..)) import Gargantext.Database.Query.Table.Node.Error (HasNodeError(..), nodeError, NodeError(..))
import Gargantext.Database.Query.Table.User import Gargantext.Database.Query.Table.User
...@@ -41,13 +42,13 @@ import qualified Data.Text as Text ...@@ -41,13 +42,13 @@ import qualified Data.Text as Text
-- be valid (i.e. a valid username needs to be inferred via 'guessUsername'). -- be valid (i.e. a valid username needs to be inferred via 'guessUsername').
newUser :: (CmdM env err m, MonadRandom m, HasNodeError err, HasMail env) newUser :: (CmdM env err m, MonadRandom m, HasNodeError err, HasMail env)
=> EmailAddress => EmailAddress
-> m Int64 -> m UserId
newUser emailAddress = do newUser emailAddress = do
cfg <- view mailSettings cfg <- view mailSettings
pwd <- gargPass pwd <- gargPass
let nur = mkNewUser emailAddress (GargPassword pwd) let nur = mkNewUser emailAddress (GargPassword pwd)
affectedRows <- new_user nur new_user_id <- new_user nur
withNotification (SendEmail True) cfg Invitation $ pure (affectedRows, nur) withNotification (SendEmail True) cfg Invitation $ pure (new_user_id, nur)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | A DB-specific action to create a single user. -- | A DB-specific action to create a single user.
...@@ -56,8 +57,12 @@ newUser emailAddress = do ...@@ -56,8 +57,12 @@ newUser emailAddress = do
-- use 'newUser' instead for standard Gargantext code. -- use 'newUser' instead for standard Gargantext code.
new_user :: HasNodeError err new_user :: HasNodeError err
=> NewUser GargPassword => NewUser GargPassword
-> DBCmd err Int64 -> DBCmd err UserId
new_user = new_users . (:[]) new_user rq = do
ur <- new_users [rq]
case head ur of
Nothing -> nodeError MkNode
Just uid -> pure uid
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | A DB-specific action to bulk-create users. -- | A DB-specific action to bulk-create users.
...@@ -67,17 +72,16 @@ new_user = new_users . (:[]) ...@@ -67,17 +72,16 @@ new_user = new_users . (:[])
new_users :: HasNodeError err new_users :: HasNodeError err
=> [NewUser GargPassword] => [NewUser GargPassword]
-- ^ A list of users to create. -- ^ A list of users to create.
-> DBCmd err Int64 -> DBCmd err [UserId]
new_users us = do new_users us = do
us' <- liftBase $ mapM toUserHash us us' <- liftBase $ mapM toUserHash us
r <- insertUsers $ map toUserWrite us' void $ insertUsers $ map toUserWrite us'
_ <- mapM getOrMkRoot $ map (\u -> UserName (_nu_username u)) us mapM (fmap fst . getOrMkRoot) $ map (\u -> UserName (_nu_username u)) us
pure r
------------------------------------------------------------------------ ------------------------------------------------------------------------
newUsers :: (CmdM env err m, MonadRandom m, HasNodeError err, HasMail env) newUsers :: (CmdM env err m, MonadRandom m, HasNodeError err, HasMail env)
=> [EmailAddress] => [EmailAddress]
-> m Int64 -> m [UserId]
newUsers us = do newUsers us = do
config <- view $ mailSettings config <- view $ mailSettings
us' <- mapM (\ea -> mkNewUser ea . GargPassword <$> gargPass) us us' <- mapM (\ea -> mkNewUser ea . GargPassword <$> gargPass) us
...@@ -102,14 +106,14 @@ guessUserName n = case splitOn "@" n of ...@@ -102,14 +106,14 @@ guessUserName n = case splitOn "@" n of
------------------------------------------------------------------------ ------------------------------------------------------------------------
newUsers' :: HasNodeError err newUsers' :: HasNodeError err
=> MailConfig -> [NewUser GargPassword] -> Cmd err Int64 => MailConfig -> [NewUser GargPassword] -> Cmd err [UserId]
newUsers' cfg us = do newUsers' cfg us = do
us' <- liftBase $ mapM toUserHash us us' <- liftBase $ mapM toUserHash us
r <- insertUsers $ map toUserWrite us' void $ insertUsers $ map toUserWrite us'
_ <- mapM getOrMkRoot $ map (\u -> UserName (_nu_username u)) us urs <- mapM (fmap fst . getOrMkRoot) $ map (\u -> UserName (_nu_username u)) us
_ <- mapM (\u -> mail cfg (Invitation u)) us _ <- mapM (\u -> mail cfg (Invitation u)) us
-- printDebug "newUsers'" us -- printDebug "newUsers'" us
pure r pure urs
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | Updates a user's password, notifying the user via email, if necessary. -- | Updates a user's password, notifying the user via email, if necessary.
......
...@@ -195,7 +195,7 @@ getDocumentsWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeDocument ...@@ -195,7 +195,7 @@ getDocumentsWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeDocument
getListsModelWithParentId :: HasDBid NodeType => NodeId -> Cmd err [Node HyperdataModel] getListsModelWithParentId :: HasDBid NodeType => NodeId -> Cmd err [Node HyperdataModel]
getListsModelWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeModel) getListsModelWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeModel)
getCorporaWithParentId :: HasDBid NodeType => NodeId -> Cmd err [Node HyperdataCorpus] getCorporaWithParentId :: HasDBid NodeType => NodeId -> DBCmd err [Node HyperdataCorpus]
getCorporaWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeCorpus) getCorporaWithParentId n = runOpaQuery $ selectNodesWith' n (Just NodeCorpus)
------------------------------------------------------------------------ ------------------------------------------------------------------------
...@@ -271,7 +271,7 @@ getNodeWith nId _ = do ...@@ -271,7 +271,7 @@ getNodeWith nId _ = do
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- | Sugar to insert Node with NodeType in Database -- | Sugar to insert Node with NodeType in Database
insertDefaultNode :: HasDBid NodeType insertDefaultNode :: HasDBid NodeType
=> NodeType -> ParentId -> UserId -> Cmd err [NodeId] => NodeType -> ParentId -> UserId -> DBCmd err [NodeId]
insertDefaultNode nt p u = insertNode nt Nothing Nothing p u insertDefaultNode nt p u = insertNode nt Nothing Nothing p u
insertDefaultNodeIfNotExists :: HasDBid NodeType insertDefaultNodeIfNotExists :: HasDBid NodeType
...@@ -382,7 +382,7 @@ data CorpusType = CorpusDocument | CorpusContact ...@@ -382,7 +382,7 @@ data CorpusType = CorpusDocument | CorpusContact
class MkCorpus a class MkCorpus a
where where
mk :: HasDBid NodeType => Maybe Name -> Maybe a -> ParentId -> UserId -> Cmd err [NodeId] mk :: HasDBid NodeType => Maybe Name -> Maybe a -> ParentId -> UserId -> DBCmd err [NodeId]
instance MkCorpus HyperdataCorpus instance MkCorpus HyperdataCorpus
where where
......
...@@ -39,6 +39,13 @@ add pId ns = runPGSQuery queryAdd (Only $ Values fields inputData) ...@@ -39,6 +39,13 @@ add pId ns = runPGSQuery queryAdd (Only $ Values fields inputData)
fields = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes fields = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes
inputData = prepare pId ns inputData = prepare pId ns
-- | Adds a single document. Useful for debugging purposes, but
-- not as efficient as adding documents in bulk via 'add'.
add_one :: CorpusId -> ContextId -> Cmd err [Only Int]
add_one pId ctxId = runPGSQuery queryAdd (Only $ Values fields [InputData pId ctxId])
where
fields = map (\t-> QualifiedIdentifier Nothing t) inputSqlTypes
add_debug :: CorpusId -> [ContextId] -> Cmd err ByteString add_debug :: CorpusId -> [ContextId] -> Cmd err ByteString
add_debug pId ns = formatPGSQuery queryAdd (Only $ Values fields inputData) add_debug pId ns = formatPGSQuery queryAdd (Only $ Values fields inputData)
where where
......
...@@ -58,7 +58,7 @@ import Control.Lens (set, view) ...@@ -58,7 +58,7 @@ import Control.Lens (set, view)
import Control.Lens.Cons import Control.Lens.Cons
import Control.Lens.Prism import Control.Lens.Prism
import Data.Aeson (toJSON, ToJSON) import Data.Aeson (toJSON, ToJSON)
import Data.Char (isAlpha) import Data.Char (isAlphaNum)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text) import Data.Text (Text)
-- import Data.ByteString (ByteString) -- import Data.ByteString (ByteString)
...@@ -233,7 +233,7 @@ instance UniqParameters (Node a) ...@@ -233,7 +233,7 @@ instance UniqParameters (Node a)
filterText :: Text -> Text filterText :: Text -> Text
filterText = DT.toLower . (DT.filter isAlpha) filterText = DT.toLower . (DT.filter isAlphaNum)
instance (UniqParameters a, ToJSON a, HasDBid NodeType) => AddUniqId (Node a) instance (UniqParameters a, ToJSON a, HasDBid NodeType) => AddUniqId (Node a)
......
...@@ -26,7 +26,7 @@ import Gargantext.Database.Prelude (Cmd, mkCmd, DBCmd) ...@@ -26,7 +26,7 @@ import Gargantext.Database.Prelude (Cmd, mkCmd, DBCmd)
import Gargantext.Database.Query.Table.Node import Gargantext.Database.Query.Table.Node
import Gargantext.Database.Query.Table.Node.Error import Gargantext.Database.Query.Table.Node.Error
import Debug.Trace (trace) -- import Debug.Trace (trace)
updateHyperdata :: HyperdataC a => NodeId -> a -> DBCmd err Int64 updateHyperdata :: HyperdataC a => NodeId -> a -> DBCmd err Int64
updateHyperdata i h = mkCmd $ \c -> putStrLn "before runUpdate_" >> updateHyperdata i h = mkCmd $ \c -> putStrLn "before runUpdate_" >>
...@@ -34,7 +34,7 @@ updateHyperdata i h = mkCmd $ \c -> putStrLn "before runUpdate_" >> ...@@ -34,7 +34,7 @@ updateHyperdata i h = mkCmd $ \c -> putStrLn "before runUpdate_" >>
putStrLn "after runUpdate_" >> return res putStrLn "after runUpdate_" >> return res
updateHyperdataQuery :: HyperdataC a => NodeId -> a -> Update Int64 updateHyperdataQuery :: HyperdataC a => NodeId -> a -> Update Int64
updateHyperdataQuery i h = seq h' $ trace "updateHyperdataQuery: encoded JSON" $ Update updateHyperdataQuery i h = seq h' $ {- trace "updateHyperdataQuery: encoded JSON" $ -} Update
{ uTable = nodeTable { uTable = nodeTable
, uUpdateWith = updateEasy (\ (Node { .. }) , uUpdateWith = updateEasy (\ (Node { .. })
-> Node { _node_hyperdata = h', .. } -> Node { _node_hyperdata = h', .. }
......
...@@ -72,7 +72,7 @@ import Gargantext.Database.Admin.Config (nodeTypeId) ...@@ -72,7 +72,7 @@ import Gargantext.Database.Admin.Config (nodeTypeId)
------------------------------------------------------------------------ ------------------------------------------------------------------------
-- TODO: on conflict, nice message -- TODO: on conflict, nice message
insertUsers :: [UserWrite] -> DBCmd err Int64 insertUsers :: [UserWrite] -> DBCmd err Int64
insertUsers us = mkCmd $ \c -> runInsert_ c insert insertUsers us = mkCmd $ \c -> runInsert c insert
where where
insert = Insert userTable us rCount Nothing insert = Insert userTable us rCount Nothing
......
...@@ -23,7 +23,7 @@ import Gargantext.Database.Action.User (getUserId, getUsername) ...@@ -23,7 +23,7 @@ import Gargantext.Database.Action.User (getUserId, getUsername)
import Gargantext.Database.Admin.Config import Gargantext.Database.Admin.Config
import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser) import Gargantext.Database.Admin.Types.Hyperdata (HyperdataUser)
import Gargantext.Database.Admin.Types.Node import Gargantext.Database.Admin.Types.Node
import Gargantext.Database.Prelude (Cmd, runOpaQuery, DBCmd) import Gargantext.Database.Prelude (runOpaQuery, DBCmd)
import Gargantext.Database.Query.Table.Node import Gargantext.Database.Query.Table.Node
import Gargantext.Database.Query.Table.Node.Error import Gargantext.Database.Query.Table.Node.Error
import Gargantext.Database.Query.Table.User (queryUserTable, UserPoly(..)) import Gargantext.Database.Query.Table.User (queryUserTable, UserPoly(..))
...@@ -34,7 +34,7 @@ import Opaleye (restrict, (.==), Select) ...@@ -34,7 +34,7 @@ import Opaleye (restrict, (.==), Select)
import Opaleye.SqlTypes (sqlStrictText, sqlInt4) import Opaleye.SqlTypes (sqlStrictText, sqlInt4)
getRootId :: (HasNodeError err) => User -> Cmd err NodeId getRootId :: (HasNodeError err) => User -> DBCmd err NodeId
getRootId u = do getRootId u = do
maybeRoot <- head <$> getRoot u maybeRoot <- head <$> getRoot u
case maybeRoot of case maybeRoot of
...@@ -66,7 +66,7 @@ getOrMk_RootWithCorpus :: (HasNodeError err, MkCorpus a) ...@@ -66,7 +66,7 @@ getOrMk_RootWithCorpus :: (HasNodeError err, MkCorpus a)
=> User => User
-> Either CorpusName [CorpusId] -> Either CorpusName [CorpusId]
-> Maybe a -> Maybe a
-> Cmd err (UserId, RootId, CorpusId) -> DBCmd err (UserId, RootId, CorpusId)
getOrMk_RootWithCorpus user cName c = do getOrMk_RootWithCorpus user cName c = do
(userId, rootId) <- getOrMkRoot user (userId, rootId) <- getOrMkRoot user
corpusId'' <- if user == UserName userMaster corpusId'' <- if user == UserName userMaster
......
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Gargantext.System.Logging (
LogLevel(..)
, HasLogger(..)
, MonadLogger(..)
, logM
, logLocM
, withLogger
, withLoggerHoisted
) where
import Language.Haskell.TH hiding (Type)
import Control.Exception.Lifted (bracket)
import Control.Monad.IO.Class
import Control.Monad.Trans.Control
import Data.Kind (Type)
import Prelude
import qualified Data.Text as T
import qualified Language.Haskell.TH.Syntax as TH
data LogLevel =
-- | Debug messages
DEBUG
-- | Information
| INFO
-- | Normal runtime conditions
| NOTICE
-- | General Warnings
| WARNING
-- | General Errors
| ERROR
-- | Severe situations
| CRITICAL
-- | Take immediate action
| ALERT
-- | System is unusable
| EMERGENCY
deriving (Show, Eq, Ord, Enum, Bounded)
-- | This is a barebore logging interface which we
-- can extend to plug a proper logging library, without
-- the details of the logger cropping up everywhere in
-- the rest of the codebase.
class HasLogger m where
data family Logger m :: Type
type family LogInitParams m :: Type
type family LogPayload m :: Type
initLogger :: LogInitParams m -> (forall m1. MonadIO m1 => m1 (Logger m))
destroyLogger :: Logger m -> (forall m1. MonadIO m1 => m1 ())
logMsg :: Logger m -> LogLevel -> LogPayload m -> m ()
logTxt :: Logger m -> LogLevel -> T.Text -> m ()
-- | Separate typeclass to get hold of a 'Logger' from within a monad.
-- We keey 'HasLogger' and 'MonadLogger' separate to enforce compositionality,
-- i.e. we can still give instances to 'HasLogger' for things like 'IO' without
-- having to force actually acquiring a logger for those monads.
class HasLogger m => MonadLogger m where
getLogger :: m (Logger m)
-- | A variant of 'logTxt' that doesn't require passing an explicit 'Logger'.
logM :: (Monad m, MonadLogger m) => LogLevel -> T.Text -> m ()
logM level msg = do
logger <- getLogger
logTxt logger level msg
-- | Like 'logM', but it automatically adds the file and line number to
-- the output log.
logLocM :: ExpQ
logLocM = [| \level msg ->
let loc = $(getLocTH)
in logM level (formatWithLoc loc msg)
|]
formatWithLoc :: Loc -> T.Text -> T.Text
formatWithLoc loc msg = "[" <> locationToText <> "] " <> msg
where
locationToText :: T.Text
locationToText = T.pack $ (loc_filename loc) ++ ':' : (line loc) ++ ':' : (char loc)
where
line = show . fst . loc_start
char = show . snd . loc_start
getLocTH :: ExpQ
getLocTH = [| $(location >>= liftLoc) |]
liftLoc :: Loc -> Q Exp
liftLoc (Loc a b c (d1, d2) (e1, e2)) = [|Loc
$(TH.lift a)
$(TH.lift b)
$(TH.lift c)
($(TH.lift d1), $(TH.lift d2))
($(TH.lift e1), $(TH.lift e2))
|]
-- | exception-safe combinator that creates and destroys a logger.
-- Think about it like a 'bracket' function from 'Control.Exception'.
withLogger :: (MonadBaseControl IO m, MonadIO m, HasLogger m)
=> LogInitParams m
-> (Logger m -> m a)
-> m a
withLogger params = bracket (initLogger params) destroyLogger
-- | Like 'withLogger', but it allows creating a 'Logger' that can run in
-- a different monad from within an 'IO' action.
withLoggerHoisted :: (MonadBaseControl IO m, HasLogger m)
=> LogInitParams m
-> (Logger m -> IO a)
-> IO a
withLoggerHoisted params act = bracket (initLogger params) destroyLogger act
...@@ -212,3 +212,6 @@ class MonadJobStatus m where ...@@ -212,3 +212,6 @@ class MonadJobStatus m where
-- | Finish tracking a job by marking all the remaining steps as failed. Attach an optional -- | Finish tracking a job by marking all the remaining steps as failed. Attach an optional
-- message to the failure. -- message to the failure.
markFailed :: Maybe T.Text -> JobHandle m -> m () markFailed :: Maybe T.Text -> JobHandle m -> m ()
-- | Add 'n' more steps to the running computation, they will be marked as remaining.
addMoreSteps :: MonadJobStatus m => Int -> JobHandle m -> m ()
...@@ -21,7 +21,7 @@ nix: ...@@ -21,7 +21,7 @@ nix:
allow-newer: true allow-newer: true
extra-deps: extra-deps:
- git: https://gitlab.iscpif.fr/cgenie/haskell-gargantext-prelude - git: https://gitlab.iscpif.fr/gargantext/haskell-gargantext-prelude
commit: 8f97fef4dfd941d773914ad058d8e02ce2bb1a3e commit: 8f97fef4dfd941d773914ad058d8e02ce2bb1a3e
- git: https://gitlab.iscpif.fr/gargantext/gargantext-graph.git - git: https://gitlab.iscpif.fr/gargantext/gargantext-graph.git
commit: 588e104fe7593210956610cab0041fd16584a4ce commit: 588e104fe7593210956610cab0041fd16584a4ce
...@@ -58,11 +58,11 @@ extra-deps: ...@@ -58,11 +58,11 @@ extra-deps:
- git: https://gitlab.iscpif.fr/gargantext/crawlers/isidore.git - git: https://gitlab.iscpif.fr/gargantext/crawlers/isidore.git
commit: 3db385e767d2100d8abe900833c6e7de3ac55e1b commit: 3db385e767d2100d8abe900833c6e7de3ac55e1b
- git: https://gitlab.iscpif.fr/gargantext/crawlers/arxiv-api.git - git: https://gitlab.iscpif.fr/gargantext/crawlers/arxiv-api.git
commit: 2d7e5753cbbce248b860b571a0e9885415c846f7 commit: eb130c71fa17adaceed6ff66beefbccb13df51ba
- git: https://gitlab.iscpif.fr/gargantext/crawlers/openalex.git - git: https://gitlab.iscpif.fr/gargantext/crawlers/openalex.git
commit: dab07cb89e8ab8eaaff8619f5e21d944d9c526ab commit: dab07cb89e8ab8eaaff8619f5e21d944d9c526ab
- git: ssh://git@gitlab.iscpif.fr:20022/gargantext/crawlers/epo.git - git: ssh://git@gitlab.iscpif.fr:20022/gargantext/crawlers/epo.git
commit: ac9f20b36e8659267d7525fe2c74c7286a0350cb commit: dd45fb91eca6150c2d8f8930ec87bf23f081b2e2
# NP libs # NP libs
- git: https://github.com/alpmestan/servant-job.git - git: https://github.com/alpmestan/servant-job.git
commit: b4182487cfe479777c11ca19f3c0d47840b376f6 commit: b4182487cfe479777c11ca19f3c0d47840b376f6
...@@ -118,6 +118,9 @@ extra-deps: ...@@ -118,6 +118,9 @@ extra-deps:
- hgal-2.0.0.2@sha256:13d58afd0668b9cb881c612eff8488a0e289edd4bbffa893df4beee60cfeb73b,653 - hgal-2.0.0.2@sha256:13d58afd0668b9cb881c612eff8488a0e289edd4bbffa893df4beee60cfeb73b,653
- hsparql-0.3.8 - hsparql-0.3.8
- hstatistics-0.3.1 - hstatistics-0.3.1
- hspec-2.11.1
- hspec-core-2.11.1
- hspec-discover-2.11.1
- hspec-expectations-0.8.3 - hspec-expectations-0.8.3
- json-stream-0.4.2.4@sha256:8b7f17d54a6e1e6311756270f8bcf51e91bab4300945400de66118470dcf51b9,4716 - json-stream-0.4.2.4@sha256:8b7f17d54a6e1e6311756270f8bcf51e91bab4300945400de66118470dcf51b9,4716
- located-base-0.1.1.1@sha256:7c6395f2b6fbf2d5f76c3514f774423838c0ea94e1c6a5530dd3c94b30c9d1c8,1904 - located-base-0.1.1.1@sha256:7c6395f2b6fbf2d5f76c3514f774423838c0ea94e1c6a5530dd3c94b30c9d1c8,1904
...@@ -135,6 +138,7 @@ extra-deps: ...@@ -135,6 +138,7 @@ extra-deps:
- stemmer-0.5.2@sha256:823aec56249ec2619f60a2c0d1384b732894dbbbe642856d337ebfe9629a0efd,4082 - stemmer-0.5.2@sha256:823aec56249ec2619f60a2c0d1384b732894dbbbe642856d337ebfe9629a0efd,4082
- taggy-0.2.1@sha256:7bc55ddba178971dc6052163597f0445a0a2b5b0ca0e84ce651d53d722e3c265,4662 - taggy-0.2.1@sha256:7bc55ddba178971dc6052163597f0445a0a2b5b0ca0e84ce651d53d722e3c265,4662
- taggy-lens-0.1.2@sha256:091ca81d02bd3d7fb493dce0148e1a38f25eb178a1ebd751043a23239e5e3265,3009 - taggy-lens-0.1.2@sha256:091ca81d02bd3d7fb493dce0148e1a38f25eb178a1ebd751043a23239e5e3265,3009
- tasty-hspec-1.2.0.3
- tmp-postgres-1.34.1.0 - tmp-postgres-1.34.1.0
- vector-0.12.3.0@sha256:0ae2c1ba86f0077910be242ec6802cc3d7725fe7b2bea6987201aa3737b239b5,7953 - vector-0.12.3.0@sha256:0ae2c1ba86f0077910be242ec6802cc3d7725fe7b2bea6987201aa3737b239b5,7953
- xmlbf-0.6.1@sha256:57867fcb39e0514d17b3328ff5de8d241a18482fc89bb742d9ed820a6a2a5187,1540 - xmlbf-0.6.1@sha256:57867fcb39e0514d17b3328ff5de8d241a18482fc89bb742d9ed820a6a2a5187,1540
......
...@@ -226,7 +226,7 @@ testPubMedCovid_01 getPubmedKey = do ...@@ -226,7 +226,7 @@ testPubMedCovid_01 getPubmedKey = do
case mb_key of case mb_key of
Nothing -> pure () Nothing -> pure ()
Just k -> withValidQuery "\"Covid\"" $ \query -> do Just k -> withValidQuery "\"Covid\"" $ \query -> do
res <- Pubmed.get (_PubmedApiKey k) query (Just 1) res <- Pubmed.get (_PubmedApiKey k) (renderQuery query) (Just 1)
case res of case res of
Left err -> fail (show err) Left err -> fail (show err)
Right (_, cnd) -> do Right (_, cnd) -> do
...@@ -241,7 +241,7 @@ testPubMedCovid_02 getPubmedKey = do ...@@ -241,7 +241,7 @@ testPubMedCovid_02 getPubmedKey = do
case mb_key of case mb_key of
Nothing -> pure () Nothing -> pure ()
Just k -> withValidQuery "\"Covid\" AND \"Alzheimer\"" $ \query -> do Just k -> withValidQuery "\"Covid\" AND \"Alzheimer\"" $ \query -> do
res <- Pubmed.get (_PubmedApiKey k) query (Just 1) res <- Pubmed.get (_PubmedApiKey k) (renderQuery query) (Just 1)
case res of case res of
Left err -> fail (show err) Left err -> fail (show err)
Right (_, cnd) -> do Right (_, cnd) -> do
......
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-} {-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-} {-# OPTIONS_GHC -Wno-orphans #-}
module Database.Operations where module Database.Operations (
tests
) where
import Control.Exception import Control.Exception hiding (assert)
import Control.Lens import Control.Lens hiding (elements)
import Control.Monad.Except import Control.Monad.Except
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.Trans.Control import Control.Monad.Trans.Control
import Data.IORef
import Data.Pool hiding (withResource) import Data.Pool hiding (withResource)
import Data.String import Data.String
import Database.PostgreSQL.Simple
import Gargantext.Core.Types.Individu import Gargantext.Core.Types.Individu
import Gargantext.Database.Action.User
import Gargantext.Database.Action.User.New import Gargantext.Database.Action.User.New
import Gargantext.Database.Prelude import Gargantext.Database.Prelude
import Gargantext.Database.Query.Table.Node.Error import Gargantext.Database.Query.Table.Node.Error
import Gargantext.Prelude import Gargantext.Prelude
import Gargantext.Prelude.Config import Gargantext.Prelude.Config
import Prelude import Prelude
import Shelly hiding (FilePath) import Shelly hiding (FilePath, run)
import Test.Tasty import Test.QuickCheck.Monadic
import Test.Tasty.HUnit import Test.Hspec
import Test.Tasty.Hspec import Test.Tasty.HUnit hiding (assert)
import Test.Tasty.QuickCheck
import qualified Data.Pool as Pool import qualified Data.Pool as Pool
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding as TE
import qualified Database.PostgreSQL.Simple as PG import qualified Database.PostgreSQL.Simple as PG
import qualified Database.PostgreSQL.Simple.Options as Client import qualified Database.PostgreSQL.Simple.Options as Client
import qualified Database.Postgres.Temp as Tmp import qualified Database.Postgres.Temp as Tmp
import qualified Shelly as SH
import Paths_gargantext import Paths_gargantext
-- | Keeps a log of usernames we have already generated, so that our
-- roundtrip tests won't fail.
uniqueArbitraryNewUser :: Int -> Gen (NewUser GargPassword)
uniqueArbitraryNewUser currentIx = do
ur <- (`mappend` (T.pack (show currentIx) <> "-")) <$> ascii_txt
let email = ur <> "@foo.com"
NewUser <$> pure ur <*> pure email <*> elements arbitraryPassword
where
ascii_txt :: Gen T.Text
ascii_txt = fmap (T.pack . getPrintableString) arbitrary
-- | Test DB settings. -- | Test DB settings.
dbUser, dbPassword, dbName :: String dbUser, dbPassword, dbName :: String
dbUser = "gargantua" dbUser = "gargantua"
dbPassword = "gargantua_test" dbPassword = "gargantua_test"
dbName = "gargandbV5" dbName = "gargandb_test"
newtype Counter = Counter { _Counter :: IORef Int }
deriving Eq
instance Show Counter where
show (Counter _) = "Counter"
emptyCounter :: IO Counter
emptyCounter = Counter <$> newIORef 0
nextCounter :: Counter -> IO Int
nextCounter (Counter ref) = atomicModifyIORef' ref (\old -> (succ old, old))
data TestEnv = TestEnv { data TestEnv = TestEnv {
test_db :: !DBHandle test_db :: !DBHandle
, test_config :: !GargConfig , test_config :: !GargConfig
, test_usernameGen :: !Counter
} }
newtype TestMonad a = TestMonad { runTestMonad :: ReaderT TestEnv IO a } newtype TestMonad a = TestMonad { runTestMonad :: ReaderT TestEnv IO a }
...@@ -52,7 +82,7 @@ newtype TestMonad a = TestMonad { runTestMonad :: ReaderT TestEnv IO a } ...@@ -52,7 +82,7 @@ newtype TestMonad a = TestMonad { runTestMonad :: ReaderT TestEnv IO a }
, MonadBaseControl IO , MonadBaseControl IO
) )
data DBHandle = DBHandle { data DBHandle = DBHandle {
_DBHandle :: Pool PG.Connection _DBHandle :: Pool PG.Connection
, _DBTmp :: Tmp.DB , _DBTmp :: Tmp.DB
} }
...@@ -72,9 +102,6 @@ fakeIniPath = getDataFileName "test-data/test_config.ini" ...@@ -72,9 +102,6 @@ fakeIniPath = getDataFileName "test-data/test_config.ini"
gargDBSchema :: IO FilePath gargDBSchema :: IO FilePath
gargDBSchema = getDataFileName "devops/postgres/schema.sql" gargDBSchema = getDataFileName "devops/postgres/schema.sql"
gargDBExtensionsSchema :: IO FilePath
gargDBExtensionsSchema = getDataFileName "devops/postgres/extensions.sql"
teardown :: TestEnv -> IO () teardown :: TestEnv -> IO ()
teardown TestEnv{..} = do teardown TestEnv{..} = do
destroyAllResources $ _DBHandle test_db destroyAllResources $ _DBHandle test_db
...@@ -87,7 +114,7 @@ bootstrapDB tmpDB pool _cfg = Pool.withResource pool $ \conn -> do ...@@ -87,7 +114,7 @@ bootstrapDB tmpDB pool _cfg = Pool.withResource pool $ \conn -> do
schemaPath <- gargDBSchema schemaPath <- gargDBSchema
let connString = Tmp.toConnectionString tmpDB let connString = Tmp.toConnectionString tmpDB
(res,ec) <- shelly $ silently $ escaping False $ do (res,ec) <- shelly $ silently $ escaping False $ do
result <- run "psql" ["-d", "\"" <> TE.decodeUtf8 connString <> "\"", "<", fromString schemaPath] result <- SH.run "psql" ["-d", "\"" <> TE.decodeUtf8 connString <> "\"", "<", fromString schemaPath]
(result,) <$> lastExitCode (result,) <$> lastExitCode
unless (ec == 0) $ throwIO (userError $ show ec <> ": " <> T.unpack res) unless (ec == 0) $ throwIO (userError $ show ec <> ": " <> T.unpack res)
...@@ -107,26 +134,75 @@ setup = do ...@@ -107,26 +134,75 @@ setup = do
Right db -> do Right db -> do
gargConfig <- fakeIniPath >>= readConfig gargConfig <- fakeIniPath >>= readConfig
pool <- createPool (PG.connectPostgreSQL (Tmp.toConnectionString db)) pool <- createPool (PG.connectPostgreSQL (Tmp.toConnectionString db))
(PG.close) (PG.close) 2 60 2
2
60
2
bootstrapDB db pool gargConfig bootstrapDB db pool gargConfig
pure $ TestEnv (DBHandle pool db) gargConfig ugen <- emptyCounter
pure $ TestEnv (DBHandle pool db) gargConfig ugen
withTestDB :: (TestEnv -> IO ()) -> IO ()
withTestDB = bracket setup teardown
tests :: Spec
tests = sequential $ aroundAll withTestDB $ describe "Database" $ do
describe "Read/Writes" $
describe "User creation" $ do
it "Simple write/read" writeRead01
it "Simple duplicate" mkUserDup
it "Read/Write roundtrip" prop_userCreationRoundtrip
data ExpectedActual a =
Expected a
| Actual a
deriving Show
tests :: TestTree instance Eq a => Eq (ExpectedActual a) where
tests = withResource setup teardown $ (Expected a) == (Actual b) = a == b
\getEnv -> testGroup "Database" [unitTests getEnv] (Actual a) == (Expected b) = a == b
_ == _ = False
unitTests :: IO TestEnv -> TestTree
unitTests getEnv = testGroup "Read/Writes"
[ testCase "Simple write" (write01 getEnv)
]
write01 :: IO TestEnv -> Assertion writeRead01 :: TestEnv -> Assertion
write01 getEnv = do writeRead01 env = do
env <- getEnv
flip runReaderT env $ runTestMonad $ do flip runReaderT env $ runTestMonad $ do
let nur = mkNewUser "alfredo@well-typed.com" (GargPassword "my_secret") let nur1 = mkNewUser "alfredo@well-typed.com" (GargPassword "my_secret")
x <- new_user nur let nur2 = mkNewUser "paul@acme.com" (GargPassword "my_secret")
liftBase $ x `shouldBe` 1
\ No newline at end of file uid1 <- new_user nur1
uid2 <- new_user nur2
liftBase $ uid1 `shouldBe` 1
liftBase $ uid2 `shouldBe` 2
-- Getting the users by username returns the expected IDs
uid1' <- getUserId (UserName "alfredo")
uid2' <- getUserId (UserName "paul")
liftBase $ uid1' `shouldBe` 1
liftBase $ uid2' `shouldBe` 2
mkUserDup :: TestEnv -> Assertion
mkUserDup env = do
let x = flip runReaderT env $ runTestMonad $ do
-- This should fail, because user 'alfredo' exists already.
let nur = mkNewUser "alfredo@well-typed.com" (GargPassword "my_secret")
new_user nur
--
-- SqlError {sqlState = "23505", sqlExecStatus = FatalError
-- , sqlErrorMsg = "duplicate key value violates unique constraint \"auth_user_username_idx1\""
-- , sqlErrorDetail = "Key (username)=(alfredo) already exists.", sqlErrorHint = ""
-- }
--
-- Postgres increments the underlying SERIAL for the user even if the request fails, see
-- https://stackoverflow.com/questions/37204749/serial-in-postgres-is-being-increased-even-though-i-added-on-conflict-do-nothing
-- This means that the next available ID is '3'.
x `shouldThrow` (\SqlError{..} -> sqlErrorDetail == "Key (username)=(alfredo) already exists.")
runEnv :: TestEnv -> TestMonad a -> PropertyM IO a
runEnv env act = run (flip runReaderT env $ runTestMonad act)
prop_userCreationRoundtrip :: TestEnv -> Property
prop_userCreationRoundtrip env = monadicIO $ do
nextAvailableCounter <- run (nextCounter $ test_usernameGen env)
nur <- pick (uniqueArbitraryNewUser nextAvailableCounter)
uid <- runEnv env (new_user nur)
ur' <- runEnv env (getUserId (UserName $ _nu_username nur))
run (Expected uid `shouldBe` Actual ur')
...@@ -18,7 +18,6 @@ module Parsers.Date where ...@@ -18,7 +18,6 @@ module Parsers.Date where
import Test.Hspec import Test.Hspec
import Test.QuickCheck import Test.QuickCheck
import Control.Applicative ((<*>))
import Data.Either (Either(..)) import Data.Either (Either(..))
import Data.Time (ZonedTime(..)) import Data.Time (ZonedTime(..))
import Data.Text (pack, Text) import Data.Text (pack, Text)
......
...@@ -25,7 +25,6 @@ import Test.QuickCheck.Instances () ...@@ -25,7 +25,6 @@ import Test.QuickCheck.Instances ()
import Text.Parsec.Pos import Text.Parsec.Pos
import Text.Parsec.Error (ParseError, Message(..), newErrorMessage) import Text.Parsec.Error (ParseError, Message(..), newErrorMessage)
import Data.Time.LocalTime (ZonedTime (..), TimeZone (..), TimeOfDay(..), LocalTime(..)) import Data.Time.LocalTime (ZonedTime (..), TimeZone (..), TimeOfDay(..), LocalTime(..))
import Data.Eq (Eq(..))
import Data.Either (Either(..)) import Data.Either (Either(..))
deriving instance Eq ZonedTime deriving instance Eq ZonedTime
......
...@@ -228,6 +228,7 @@ instance MonadJobStatus MyDummyMonad where ...@@ -228,6 +228,7 @@ instance MonadJobStatus MyDummyMonad where
markFailure steps mb_msg jh = MyDummyMonad (markFailure steps mb_msg jh) markFailure steps mb_msg jh = MyDummyMonad (markFailure steps mb_msg jh)
markComplete jh = MyDummyMonad (markComplete jh) markComplete jh = MyDummyMonad (markComplete jh)
markFailed mb_msg jh = MyDummyMonad (markFailed mb_msg jh) markFailed mb_msg jh = MyDummyMonad (markFailed mb_msg jh)
addMoreSteps steps jh = MyDummyMonad (addMoreSteps steps jh)
runMyDummyMonad :: Env -> MyDummyMonad a -> IO a runMyDummyMonad :: Env -> MyDummyMonad a -> IO a
runMyDummyMonad env m = do runMyDummyMonad env m = do
......
module Main where
import Gargantext.Prelude
import qualified Database.Operations as DB
import Test.Hspec
-- It's especially important to use Hspec for DB tests, because,
-- unlike 'tasty', 'Hspec' has explicit control over parallelism,
-- and it's important that DB tests are run according to a very
-- precise order, as they are not independent from each other.
-- Unfortunately it's not possibly to use the 'tasty-hspec' adapter
-- because by the time we get a 'TestTree' out of the adapter library,
-- the information about parallelism is lost.
main :: IO ()
main = hspec DB.tests
{-| {--|
Module : Main.hs Module : Main.hs
Description : Main for Gargantext Tests Description : Main for Gargantext Tasty Tests
Copyright : (c) CNRS, 2017-Present Copyright : (c) CNRS, 2017-Present
License : AGPL + CECILL v3 License : AGPL + CECILL v3
Maintainer : team@gargantext.org Maintainer : team@gargantext.org
...@@ -8,6 +8,7 @@ Stability : experimental ...@@ -8,6 +8,7 @@ Stability : experimental
Portability : POSIX Portability : POSIX
-} -}
module Main where
import Gargantext.Prelude import Gargantext.Prelude
...@@ -44,5 +45,4 @@ main = do ...@@ -44,5 +45,4 @@ main = do
, NgramsQuery.tests , NgramsQuery.tests
, CorpusQuery.tests , CorpusQuery.tests
, JSON.tests , JSON.tests
, DB.tests
] ]
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