Newer
Older
api-wos / REST / rWosRetrieve.sh
#!/bin/bash


# Initialisation des variables pour les options
programme=$(basename $0)
substitut=$(echo $programme | sed 's/./ /g')
version='0.5.3'
modif='25/07/2022'

function usage
{
echo "Usage : $programme -q queryId -n nb_notices -r (résultats|-) [ -o offset ] "
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 queryId -n nb_notices -r (résultats|-) [ -o offset ] 
    $substitut [ -f champs ]* [ -t CH:(A|D)[,CH:(A|D)]* ] 
    $substitut [ -j cookie_jar ] [ -ix ] 
    $programme -h

Options
=======
    -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)
    -h  affiche cette aide
    -i  ajoute dans la réponse de l’API la liste des identifiants WoS des
        notices retournées
    -j  indique le nom du fichier qui recevra les cookies (“cookies.txt”
        par défaut)
    -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  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
    -x  indique que les notices doivent être en XML au lieu de JSON, le format 
        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:hij:n: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 "Attention : offset supérieur au nombre de notices" >&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 $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

# 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 $cookiejar -c $cookiejar -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 $cookiejar -c $cookiejar -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