diff --git a/REST/rWosQuery.sh b/REST/rWosQuery.sh new file mode 100755 index 0000000..638ae91 --- /dev/null +++ b/REST/rWosQuery.sh @@ -0,0 +1,487 @@ +#!/bin/bash + + +# Initialisation des variables pour les options +programme=$(basename $0) +substitut=$(echo $programme | sed 's/./ /g') +version='0.9.4' +modif='13/07/2022' + +function usage +{ +echo "Usage : $programme -q 'requête' [ -r (résultats|-) ] [ -n nb_notices ] " +echo " $substitut [ -[cmp] début:fin ] [ -l [0-9]+[DWMY] ] [ -b [WOS:]base ]* " +echo " $substitut [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] " +echo " $substitut [ -j cookie_jar ] [ -ix ] " +echo " $programme -h " +} + + +function aide +{ +cat << EOT + +Usage +===== + $programme -q 'requête' [ -r (résultats|-) ] [ -n nb_notices ] + $substitut [ -[cmp] début:fin ] [ -l [0-9]+[DWMY] ] [ -b [WOS:]base ]* + $substitut [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] + $substitut [ -j cookie_jar ] [ -i ] + $programme -h + +Options principales +=================== + -c limite la recherche dans la base de données aux dates de création + des notices indiquées sous la forme aaaa-mm-jj:aaaa-mm-jj (comme par + exemple, “2000-01-01:2020-07-31”) + -h affiche cette aide + -l limite la recherche dans la base de données aux dates de chargement + des notices indiquées sous la forme d’un nombre de jours (D), de + semaines (W), de mois (M) ou d’années (Y). Les valeurs acceptées vont + de “0D” à “6D”, de “1W” à “52W”, de “1M” à “12M” et de “0Y” à “10Y” + -m limite la recherche dans la base de données aux dates de modification + des notices indiquées sous la forme aaaa-mm-jj:aaaa-mm-jj (comme par + exemple, “2000-01-01:2020-07-31”) + -p limite la recherche dans la base de données aux dates de publication + indiquées sous la forme aaaa-mm-jj:aaaa-mm-jj (comme par exemple, + “2000-01-01:2020-07-31”) + -r indique le nom du fichier où placer les notices téléchargées (mettre + un tiret pour indiquer la sortie standard) + -q fournit la requête à envoyer (entre simples ou doubles quotes si + elle contient des espaces ou des caractères spéciaux) + -x indique que les notices doivent être en XML au lieu de JSON, le format + par défaut + +Autres options +============== + -b limite la recherche à une ou plusieurs bases (e.g. “WOS:SCI”). + Cette option est répétitive et “WOS:” est facultatif (par défaut, + la recherche se fait sur l’ensemble du WoS) + -f limite les champs présents dans les notices téléchargées. Cette + option est répétitive (par défaut, tous les champs sont présents) + -i ajoute dans la réponse de l’API la liste des identifiants WoS des + notices retournées (5 par défaut ou valeur de l’option “-n”) + -j indique le nom du fichier qui recevra les cookies (“cookies.txt” + par défaut) + -n indique le nombre de notices, entre 0 et 100, à retourner (5 par + défaut) + -t trie les résultats sur un champ (nom abrégé à 2 majuscules) de + façon croissante (A) ou décroissante (D) ou par pertinence (RS) + forcément décroissante + +N.B. : on ne peut utiliser au maximum qu’une seule des options “-c”, “-l”, + “-m” ou “-p” à la fois, sinon cela entraîne une erreur. + +EOT + +exit 0 +} + +function nettoie +{ +if [[ -f search$$.json ]] +then + rm -f search$$.json +fi +} + +# Déclaration explicite des tableaux “bases” et “champs” +declare -a bases +declare -a champs +declare -a tri + +format='json' + +# Options +while getopts b:c:f:hij:l:m:n:p:q:r:t:x i + do + case $i in + b) bases+=($OPTARG);; + c) cdate=$OPTARG;; + f) champs+=($OPTARG);; + h) aide;; + i) recid=1;; + j) cookiejar=$OPTARG;; + l) ldate=$OPTARG;; + m) mdate=$OPTARG;; + n) nrec=$OPTARG;; + p) pdate=$OPTARG;; + q) query=$OPTARG;; + r) sortie=$OPTARG;; + t) tri+=($OPTARG);; + x) format='xml';; + \?) echo >&2 + usage >&2 + exit 1;; + esac + done + +# Vérification des options +if [[ -z $query ]] +then + echo "Erreur : option(s) manquante(s)" >&2 + echo "" >&2 + usage >&2 + echo "" + exit 2 +fi + +if [[ -z $sortie ]] +then + sortie='/dev/null' +elif [[ $sortie = '-' ]] +then + sortie='/dev/fd/1' +fi + +if [[ -z $cookiejar ]] +then + cookiejar='cookies.txt' +fi + +# Vérification de la présence des programmes +# “curl”, “jq” et “xmllint” +for i in curl jq xmllint +do + prog=$(which $i 2> /dev/null) + if [[ -z $prog ]] + then + echo "" >&2 + echo "Erreur : programme “$i” introuvable. Vérifier \$PATH." >&2 + echo "" >&2 + exit 3 + fi +done + +# Définition des fichiers de sortie et de l’URL de l’API +out="WosApiOut.$format" +err='WosApiErr.txt' +url='https://wos-api.clarivate.com/api/wos' + +# Vérification de la variable “$max” +max=${nrec:-5} + +if [[ $max -lt 0 ]] +then + echo "Erreur : le nombre de notices par itération doit être compris entre 0 et 100" >&2 + exit 4 +elif [[ $max -gt 100 ]] +then + echo "Erreur : le nombre de notices par itération ne peut dépasser 100" >&2 + exit 4 +fi + +# Initialisation de la clé +if [[ -z $key ]] +then + read -t 60 -s -p "clé d‘authentification : " key + echo + if [ $? -gt 128 ] + then + echo "" >&2 + echo "Erreur : temps de réponse (60 s) dépassé " >&2 + exit 3 + fi +fi + +# Gestion des autres options +if [[ ${#champs[@]} -gt 0 ]] +then + viewField='WOS' + for item in ${champs[@]} + do + while [[ -n $item ]] + do + deb=${item%%,*} + fin=${item#*,} + viewField="$viewField+$deb" + if [[ $deb = $fin ]] + then + item="" + else + item=$fin + fi + done + done + extra="\"optionView\": \"FS\", \"viewField\": \"$viewField\"" +else + extra="\"optionView\": \"FR\"" +fi + +if [[ ${#bases[@]} -gt 0 ]] +then + for item in ${bases[@]} + do + while [[ -n $item ]] + do + deb=${item%%,*} + fin=${item#*,} + if [[ $deb =~ ^[A-Z]+:[A-Z]+$ ]] + then + coll=${deb%:*} + edit=${deb#*:} + else + coll='WOS' + edit=$deb + fi + if [[ -n $edition ]] + then + edition="$edition,$coll+$edit" + else + edition="$coll+$edit" + fi + if [[ $deb = $fin ]] + then + item="" + else + item=$fin + fi + done + done + extra="$extra, \"edition\": \"$edition\"" +fi + +if [[ -n $cdate || -n $mdate || -n $pdate ]] +then + if [[ -n $cdate ]] + then + if [[ -n $ldate || -n $mdate || -n $pdate ]] + then + erreur=$(( $erreur + 1 )) + else + label='createdTimeSpan' + option='-c' + span=$cdate + fi + fi + if [[ -n $mdate ]] + then + if [[ -n $ldate || -n $pdate ]] + then + erreur=$(( $erreur + 1 )) + else + label='modifiedTimeSpan' + option='-m' + span=$mdate + fi + fi + if [[ -n $pdate ]] + then + if [[ -n $ldate ]] + then + erreur=$(( $erreur + 1 )) + else + label='publishTimeSpan' + option='-p' + span=$pdate + fi + fi + if [[ $erreur -gt 0 ]] + then + echo " " >&2 + echo "Erreur : présence de plusieurs options indiquant une période" >&2 + echo " " >&2 + usage >&2 + echo " " >&2 + exit 4 + fi + regex="[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])" + if [[ $span =~ ^$regex:$regex$ ]] + then + begin=${span%:*} + end=${span#*:} + # Vérification de la validité de chaque date + for i in $begin $end + do + day=$(date -d $i 2> /dev/null) + if [[ -z $day ]] + then + echo "" + echo "Erreur : date “$i” invalide" >&2 + echo " " >&2 + exit 4 + fi + done + extra="$extra, \"$label\": \"$begin+$end\"" + else + echo " " >&2 + echo "Erreur : argument incorrect pour l’option “$option”" >&2 + echo " " >&2 + exit 4 + fi +fi + +if [[ -n $ldate ]] +then + if [[ $ldate =~ ^([0-6]D|([1-9]|[1-4][0-9]|5[0-2])W|([1-9]|1[0-2])M|([0-9]|10)Y)$ ]] + then + extra="$extra, \"loadTimeSpan\": \"$ldate\"" + else + echo "" + echo "Erreur : argument incorrect pour l’option “-l”" >&2 + echo " " >&2 + exit 4 + fi +fi + +if [[ ${#tri[@]} -gt 0 ]] +then + for item in ${tri[@]} + do + while [[ -n $item ]] + do + deb=${item%%,*} + fin=${item#*,} + if [[ ! $deb =~ ^(AU|CF|CG|CW|CV|LC|LD|PG|PY|SO|VL):[AD]$ && ! $deb =~ ^(RS|TC):D$ ]] + then + echo "Erreur : argument incorrect pour pour l’option “-t”" >&2 + exit 4 + fi + nom=${deb%%:*} + sens=${deb#*:} + if [[ -n $sortField ]] + then + sortField="$sortField,$nom+$sens" + else + sortField="$nom+$sens" + fi + + if [[ $deb = $fin ]] + then + item="" + else + item=$fin + fi + done + done + extra="$extra, \"sortField\": \"$sortField\"" +fi + +if [[ $recid -gt 0 ]] +then + extra="$extra, \"optionOther\": \"RI+On\"" +fi + +# Nettoyage en cas d’arrêt prématuré +trap nettoie HUP INT QUIT TERM + +# Lancement de la requête +requete="${query//\"/\\\"}" + +cat << EOT > search$$.json +{ + "databaseId": "WOS", + "lang": "en", + "usrQuery": "$requete", + "count": $max, + "firstRecord": 1, + $extra +} +EOT + +if [[ $format = 'json' ]] +then + curl -X POST -d @search$$.json -b $cookiejar -c $cookiejar -v "$url" \ + -H "Content-Type: application/json" \ + -H "X-ApiKey: $key" 2> $err | jq . 2>> $err > $out + + cr=$? + echo "Code retour “jq” = $cr" >> $err + + if [[ $cr -gt 0 ]] + then + echo "" + echo "Erreur : la réponse de l‘API est incorrecte [code jq = $cr]" >&2 + echo "" + nettoie + exit 6 + fi + + # Test de la réponse + msg=$(perl -ne 'print "$1" if /"message": "(.+?)"/o' $out) + if [[ -n $msg ]] + then + code=$(perl -ne 'print "$1" if /"code": "(.+?)"/o' $out) + if [[ -n $code ]] + then + msg="$code: $msg" + fi + + echo " " + echo "Erreur : “$msg” " + echo " " + nettoie + exit 5 + + else + qid=$(perl -ne 'print "$1" if /^ +"QueryID": (\d+)/o;' $out) + if [[ -n $qid ]] + then + rec=$(perl -ne 'print "$1" if /^ +"RecordsFound": (\d+)/o;;' $out) + top=$(perl -ne 'print "$1" if /^ +"RecordsSearched": (\d+)/o;;' $out) + + cat $out >> $sortie + fi + fi + +elif [[ $format = 'xml' ]] +then + curl -X POST -d @search$$.json -b $cookiejar -c $cookiejar -v "$url" \ + -H "Content-Type: application/json" \ + -H "Accept: application/xml" \ + -H "X-ApiKey: $key" 2> $err | xmllint -format - 2>> $err > $out + + cr=$? + echo "Code retour “xmllint” = $cr" >> $err + + if [[ $cr -gt 0 ]] + then + echo "" + echo "Erreur : la réponse de l‘API est incorrecte [code xmllint = $cr]" >&2 + echo "" + nettoie + exit 6 + fi + + # Test de la réponse + msg=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|(.+?)|;' $out) + if [[ -n $msg ]] + then + code=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|(.+?)|;' $out) + if [[ -n $code ]] + then + msg="$code: $msg" + fi + + echo " " + echo "Erreur : “$msg” " + echo " " + nettoie + exit 5 + + else + qid=$(perl -ne 'print "$1" if m|(\d+)|;' $out) + if [[ -n $qid ]] + then + rec=$(perl -ne 'print "$1" if m|(\d+)|;' $out) + top=$(perl -ne 'print "$1" if m|(\d+)|;' $out) + + cat $out >> $sortie + fi + fi + +fi + +amt=$(perl -ne 'print "$1" if /\bx-rec-amtperyear-remaining: (\d+)/io;' $err) + +echo " " >&2 +echo "Identifiant de la requête : $qid" >&2 +(echo "Nombre de notices trouvées : $rec / $top notices" + echo "Forfait restant : $amt" + echo "") | + perl -pe '1 while(s/(\d)(\d\d\d)\b/$1 $2/o);' >&2 + +nettoie + + +exit 0 diff --git a/REST/rWosRetrieve.sh b/REST/rWosRetrieve.sh new file mode 100755 index 0000000..28d3466 --- /dev/null +++ b/REST/rWosRetrieve.sh @@ -0,0 +1,412 @@ +#!/bin/bash + + +# Initialisation des variables pour les options +programme=$(basename $0) +substitut=$(echo $programme | sed 's/./ /g') +version='0.4.1' +modif='15/07/2022' + +function usage +{ +echo "Usage : $programme -q queryId -n nb_notices -r (résultats|-) [ -o offset ] " +echo " $substitut [ -n nb_notices ] [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] " +echo " $substitut [ -j cookie_jar ] [ -ix ] " +echo " $programme -h " +} + +function aide +{ +cat << EOT + +Usage +===== + $programme -q queryId -n nb_notices -r (résultats|-) [ -o offset ] [ -t taille ] + $programme -h + +Options +======= + -h affiche cette aide + -n indique le nombre total de notices WoS à télécharger (la valeur + maximale est donnée par le programme envoyant la requête à l’API + WoS) + -o indique à partir de quel numéro (offset), on veut télécharger les + notices, en particulier pour reprendre un téléchargement après un + problème de connexion (1 par défaut) + -q indique le numéro de la requête “queryId” dont on veut télécharger + les résultats + -r indique le nom du fichier résultat (pour la sortie standard, utiliser + le tiret “-” comme argument de l’option) + -t indique le nombre de notices à télécharger à chaque itération + (100 par défaut) + +N.B. : la variable d‘environnement WOS_DELAY permet de modifier le délai + entre deux itérations (1 seconde par défaut). Sa valeur doit être + un entier positif supérieur à 1, par ex. : “export WOS_DELAY=3” + +EOT + +exit 0 +} + +# Déclaration explicite des tableaux “bases” et “champs” +declare -a champs +declare -a tri + +format='json' + +# Options +while getopts f:hn:o:q:r:t:x i + do + case $i in + f) champs+=$OPTARG;; + h) aide;; + i) recid=1;; + j) cookiejar=$OPTARG;; + n) nrec=$OPTARG;; + o) offset=$OPTARG;; + q) qid=$OPTARG;; + r) sortie=$OPTARG;; + t) tri+=$OPTARG;; + x) format='xml';; + \?) echo >&2 + usage >&2 + exit 1;; + esac + done + +# Vérification des options +if [[ -z $qid || -z $nb || -z $sortie ]] +then + echo "Erreur : option(s) manquante(s)" >&2 + echo "" >&2 + usage >&2 + echo "" + exit 2 +fi + +if [[ ! $nb =~ ^[1-9][0-9]*$ ]] +then + echo "Erreur : le nombre de notices (option “-n”) doit être un entier positif non nul" >&2 + exit 2 +fi + +if [[ -z $offset ]] +then + offset=1 +else + if [[ $offset =~ ^[1-9][0-9]*$ ]] + then + if [[ $offset -gt $nb ]] + then + echo "Erreur : offset supérieur au nombre de notices" >&2 + exit 2 + fi + else + echo "Erreur : l’offset (option “-o”) doit être un entier positif non nul" >&2 + exit 2 + fi +fi + +if [[ $sortie = '-' ]] +then + sortie='/dev/fd/1' +fi + +if [[ -z $cookie ]] +then + cookie='cookies.txt' +fi + +# Vérification de la présence des programmes +# “curl” et “jq” +for i in curl jq xmllint +do + prog=$(which $i 2> /dev/null) + if [[ -z $prog ]] + then + echo "" >&2 + echo "Erreur : programme “$i” introuvable. Vérifier \$PATH." >&2 + echo "" >&2 + exit 3 + fi +done + +# Vérification de la variable '$WOS_DELAY' +if [[ -n $WOS_DELAY && ! $WOS_DELAY =~ ^[1-9][0-9]*$ ]] +then + echo "Erreur : la valeur de la variable \$WOS_DELAY doit être un entier supérieur à 1" >&2 + exit 2 +fi + + + +# Définition des fichiers de sortie et de l’URL de l’API +out='WosApiOut.$format' +err='WosApiErr.txt' +url='https://wos-api.clarivate.com/api/wos' + +# Initialisation de la clé +if [[ -z $key ]] +then + read -t 60 -s -p "clé d‘authentification : " key + echo + if [ $? -gt 128 ] + then + echo "" >&2 + echo "Erreur : temps de réponse (60 s) dépassé " >&2 + exit 3 + fi +fi + +# Gestion des autres options +if [[ ${#champs[@]} -gt 0 ]] +then + viewField='WOS' + for item in ${champs[@]} + do + while [[ -n $item ]] + do + deb=${item%%,*} + fin=${item#*,} + viewField="$viewField+$deb" + if [[ $deb = $fin ]] + then + item="" + else + item=$fin + fi + done + done + options="optionView=FS&viewField=$viewField" +else + options="optionView=FR" +fi + +if [[ ${#tri[@]} -gt 0 ]] +then + for item in ${tri[@]} + do + while [[ -n $item ]] + do + deb=${item%%,*} + fin=${item#*,} + if [[ ! $deb =~ ^(AU|CF|CG|CW|CV|LC|LD|PG|PY|SO|VL):[AD]$ && ! $deb =~ ^(RS|TC):D$ ]] + then + echo "Erreur : argument incorrect pour pour l’option “-t”" >&2 + exit 4 + fi + nom=${deb%%:*} + sens=${deb#*:} + if [[ -n $sortField ]] + then + sortField="$sortField,$nom+$sens" + else + sortField="$nom+$sens" + fi + + if [[ $deb = $fin ]] + then + item="" + else + item=$fin + fi + done + done + if [[ -n $options ]] + then + options="$options&sortField=$sortField" + else + options="sortField=$sortField" + fi +fi + +if [[ $recid -gt 0 ]] +then + if [[ -n $options ]] + then + options="$options&optionOther=RI+On" + else + options="optionOther=RI+On" + fi +fi + +# Lancement de la requête +function cherche +{ +end=$(( $1 + $2 - 1 )) +printf " -> %d - %d " $1 $end >&2 + +if [[ $3 = 'json' ]] +then + curl -X GET -b $cookie -c $cookie -v "$url/query/$qid?count=$2&firstRecord=$1&$options" \ + -H "X-ApiKey: $key" 2> $err | jq . 2>> $err > $out + + cr=$? + echo "Code retour “jq” = $cr" >> $err + + if [[ $cr -gt 0 ]] + then + echo "" + echo "Erreur : la réponse de l‘API est incorrecte [code jq = $cr]" >&2 + echo "" + exit 6 + fi + + # Test de la réponse + msg=$(perl -ne 'print "$1" if /"message": "(.+?)"/o' $out) + if [[ -n $msg ]] + then + cod=$(perl -ne 'print "$1" if /"code": "(.+?)"/o' $out) + if [[ -n $cod ]] + then + msg="$cod: $msg" + fi + + echo " " + echo "Erreur : “$msg” " + echo " " + exit 5 + else + rec=$(perl -ne '$nb ++ if /"UID": /o;}{print $nb;' $out) + cat $out >> $sortie + echo $rec + + fi + +elif [[ $3 = 'xml' ]] +then + curl -X GET -b $cookie -c $cookie -v "$url/query/$qid?count=$2&firstRecord=$1&$options" \ + -H "X-ApiKey: $key" -H "Accept: application/xml" 2> $err | xmllint -format - 2>> $err > $out + + cr=$? + echo "Code retour “xmllint” = $cr" >> $err + + if [[ $cr -gt 0 ]] + then + echo "" + echo "Erreur : la réponse de l‘API est incorrecte [code xmllint = $cr]" >&2 + echo "" + exit 6 + fi + + # Test de la réponse + msg=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|(.+?)|;' $out) + if [[ -n $msg ]] + then + code=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|(.+?)|;' $out) + if [[ -n $code ]] + then + msg="$code: $msg" + fi + + echo " " + echo "Erreur : “$msg” " + echo " " + exit 5 + + else + rec=$(perl -ne 'foreach $r (m|(> $sortie + echo $rec + + fi + +fi +} + +# Vérification de la variable “$max” +max=${taille:-100} +delai=${WOS_DELAY:-1} +total=$(( $nb - $offset + 1 )) + +if [[ $max -lt 1 ]] +then + echo "Erreur : le nombre de notices par itération doit être compris entre 1 et 100" >&2 + exit 4 +elif [[ $max -gt 100 ]] +then + echo "Erreur : le nombre de notices par itération ne peut dépasser 100" >&2 + exit 4 +fi + +while [[ $total -gt 0 ]] +do + if [[ $total -lt $max ]] + then + max=$total + fi + + SECONDS=0 + + rc=$(cherche "$offset" "$max" "$format") + + printf "\r \r" >&2 + + if [[ $rc -lt $max ]] + then + echo "Attention : $rc notices reçues pour $max notices attendues" >&2 + fi + + total=$(( $total - $max )) + offset=$(( $offset + $max )) + notices=$(( $notices + $rc )) + + while [[ $total -gt 0 && $SECONDS -lt $delai ]] + do + sleep 0.5 + done + +done + +printf "\r \r" >&2 + +if [[ $format = 'json' ]] +then + perl -ne 'if (not $m) { + if (/^( +)\{/o) { + $m = $1; + if ($ok) { + print ","; + $ok = 0; + } + print; + } + } + else { + if (/^$m/o) { + print; + $n ++; + } + else { + $ok ++ if $n; + $m = "" if /^\{/o; + } + }' $sortie | + (echo '{"Records": {"records": {"REC": ['; cat; echo ']}}}') | + jq . > tmpWos$$.json + + mv tmpWos$$.json $sortie + offset=$(perl -ne '$nb ++ if /"UID": /o;}{print $nb;' $sortie) +else + perl -ne 'foreach $rec (m|()|go) { + print "$rec\n"; + }' $sortie | + (echo ''; cat; echo '') | + xmllint -format - > tmpWos$$.xml + + mv tmpWos$$.xml $sortie + offset=$(perl -ne 'foreach $r (m|(&2 +(echo "Nombre de notices téléchargées : $notices notices" + echo "Nombre total de notices  : $offset notices" + echo "Forfait restant  : $amt" + echo "") | + perl -pe '1 while(s/(\d)(\d\d\d)\b/$1 $2/o);' >&2 + + +exit 0