#!/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|<message>(.+?)</message>|;' $out)
if [[ -n $msg ]]
then
code=$(perl -ne 'chomp; $l .= $_;}{print "$1" if $l =~ m|<code>(.+?)</code>|;' $out)
if [[ -n $code ]]
then
msg="$code: $msg"
fi
echo " "
echo "Erreur : “$msg” "
echo " "
exit 5
else
rec=$(perl -ne 'foreach $r (m|(<REC )|go) {$nb ++;}}{print $nb;' $out)
cat $out >> $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|(<REC .+?</REC>)|go) {
print "$rec\n";
}' $sortie |
(echo '<records>'; cat; echo '</records>') |
xmllint -format - > tmpWos$$.xml
mv tmpWos$$.xml $sortie
offset=$(perl -ne 'foreach $r (m|(<REC )|go) {$nb ++;}}{print $nb;' $sortie)
fi
amt=$(perl -ne 'print "$1" if /\bx-rec-amtperyear-remaining: (\d+)/io;' $err)
echo " " >&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