#/usr/bin/perl

#Le programme BAO2_etiquetage_rss réalise le travail demandé dans la BAO 2 à l'aide de la bibliothèque XML::RSS (https://metacpan.org/pod/XML::RSS)
#-----------------------------------------------------------------------------------------------------------------------------------------------------------
#On a parcouru toute l'arborescence pour extraire uniquement les contenus textuels qui nous intéressent
#ICI, on veut appliquer à ces contenus un étiquetage automatique (TreeTagger/UDpipe)
#Enfin, on procèdera à des recherches et extractions syntaxiques sur ces contenus
#-----------------------------------------------------------------------------------------------------------------------------------------------------------
#Entrée : nom du répertoire contenant l'arborescence de fichiers (2020) + nom de la rubrique choisie (ex. pour la rubrique "cinéma" 3476)
#Résultat : pour une rubrique, un fichier étiqueté avec UDpipe au format ConLL et un fichier étiqueté avec TreeTagger converti directement au format xml
#Remarque : le fichier de sortie UDpipe sera converti ultérieurement grâce à un script spécifique udpipe2xml.pl
#-----------------------------------------------------------------------------------------------------------------------------------------------------------
#Commande pour lancer le programme : perl BAO2__etiquetage_rss.pl (nom du répertoire contenant l'arborescence de fichiers) (nom de la rubrique choisie)
#Exemple : perl BAO2__etiquetage_rss.pl 2020 3476
#-----------------------------------------------------------------------------------------------------------------------------------------------------------

use strict;
use utf8;
use XML::RSS;
use Data::Dumper;
use Data::Dump qw(dump);
binmode(STDOUT,":utf8"); #Pour que tout ce qui sera affiché sur la sortie standard (écran) soit encodé en UTF-8

#On recupère dans des variables les paramètres passés par l'utilisateur en ligne de commande : nom du répertoire à parcourir et nom de la rubrique sélectionnée
#On crée un objet de type XML::RSS
my $dir="$ARGV[0]";
my $rubrique="$ARGV[1]";
my $rss=new XML::RSS; # créé l'objet XML::RSS

#On vérifie que le nom du répertoire ne se termine pas par un "/" et s'il y en a un on l'élimine
$dir=~s/[\/]$//;

#On crée deux fichiers de sortie : un au format txt et un au format xml
#Pour le fichier de sortie xml, on ajoute directement l'en-tête xml et la balise racine dans laquelle sera précisé le code de la rubrique de travail
open my $output, ">:encoding(UTF-8)","2020-udpipe-rss-$rubrique.txt";
open my $output2, ">:encoding(UTF-8)","2020-treetagger-rss-$rubrique.xml";
print $output2 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
print $output2 "<corpus rubrique=\"$rubrique\">\n";

my %dico_des_titres=();
my $numberItem=0;
my $nbFile=1;

#On appelle un sous-programme qui parcourt l'arborescence des fichiers de manière récursive 
#On va ainsi voir quels éléments constituent le dossier dans lequel on se trouve (répertoire, dossier, fichier...) et agir ensuite en conséquence du type de l'élément
&parcoursarborescencefichiers($dir); #recurse!

print $output2 "</corpus>\n";
close $output;
close $output2;
print "Nb item : $numberItem \n";

#Description du sous-programme qui parcourt l'arborescence des fichiers de manière récursive
#On récupère dans une variable l'argument passé au sous-programme lors de son appel (répertoire contenant l'arborescence des fichiers - au premier appel, 2020)
#On indique à la fonction opendir()ce qu'on veut ouvrir selon deux paramètres : son descripteur dans le programme, et son nom, ici la valeur de la variable $path
#On passe le descripteur du répertoire en paramètre à readdir() pour récupérer la liste des fichiers qu'il contient 
#On passe le descripteur du répertoire en paramètre à closedir() pour le fermer
#Pour ne pas tomber dans une boucle infinie, 
#on utilise next if pour passer directement à l'itération suivante si la condition est vérifiée et on élimine le cas du répertoire courant . et du répertoire parent .. par regexp
#Condition 1 ( if (-d $file) ): $file est un répertoire, alors on relance le parcours mais au niveau du fichier courant
#Condition 2 ( if (-f $file) ): $file est un fichier 
#Attention, une fois au niveau du fichier, il faut encore filtrer : seuls les fichiers au format xml nous intéressent
sub parcoursarborescencefichiers {
    my $path = shift(@_); 
    opendir(my $DIRhandle, $path) or die "can't open $path: $!\n";
    my @files = readdir($DIRhandle);
    closedir($DIRhandle);
    foreach my $file (@files) {
        next if $file =~ /^\.\.?$/;
        $file = $path."/".$file;
        if (-d $file) { #L'élément courant n'est pas un fichier, on relance le parcours récursif
	    &parcoursarborescencefichiers($file);
        }
        if (-f $file) { #L'élément courant est un fichier, on vérifie s'il est au format xml
        	if ($file =~/$rubrique.+\.xml$/) { #L'élément courant est un fichier xml, on procède à l'extraction des contenus textuels qui nous intéressent
        		print $nbFile++," Traitement du fichier : ",$file,"\n"; #On affiche à l'écran le numéro du fichier courant et son nom
				eval {$rss->parsefile($file); };
				if( $@ ) {
					$@ =~ s/at \/.*?$//s;               # remove module line number
					print STDERR "\nERROR in '$file':\n$@\n";
				} 
				else {
					foreach my $item (@{$rss->{'items'}}) { #On récupère les contenus textuels titre/description uniquement
						my $titre=$item->{'title'};
						my $description=$item->{'description'};
						$numberItem++; #On incrémente le compteur du nombre d'items pour afficher le compte total à l'écran en fin de traitement 
						if (!(exists $dico_des_titres{$titre})) { #On filtre les possibles doublons en vérifiant qu'un titre n'est pas encore dans le dico pour l'implémenter
							$dico_des_titres{$titre}=$description;
							($titre,$description)=&nettoyage($titre,$description);
							#Appel du sous-programme de nettoyage 
							my ($titre_nettoye,$description_nettoyee)=&nettoyage($titre,$description); #On appelle un sous-programme qui nettoie les contenus textuels
							#On appelle un sous-programme qui applique l'étiquetage TreeTagger sur les contenus textuels nettoyés
							my ($titre_tagged,$description_tagged)=&etiquetageTT($titre_nettoye,$description_nettoyee);
                            #On appelle un sous-programme qui applique l'étiquetage UDpipe sur le contenus textuels nettoyés
							my ($titre_UD,$description_UD)=&etiquetageUD($titre_nettoye,$description_nettoyee);
							#On écrit les résultats dans deux fichiers de sortie : un fichier étiqueté UDpipe au format ConLL et un fichier étiqueté TreeTagger au format xml
							print $output $titre_UD,"\n";
							print $output $description_UD,"\n";
							print $output "--------------------\n";
							print $output2 "<item><titre>\n";
                            print $output2 "<article>\n$titre_tagged</article>\n";
                            print $output2 "</titre><description>\n";
                            print $output2 "<article>\n$description_tagged</article>\n";
                            print $output2 "</description></item>\n";

						}
					}
				}
			}
		}
	}
}

#Description du sous-programme qui nettoie les contenus textuels sélectionnés (titre et description)
#On récupère dans des variables les arguments passés au sous-programme lors de son appel (titre, description)
#On supprime les marques CDATA qui servent à signaler un contenu textuel dans un fichier xml
#On supprime les balises du fichier xml
#On traite les guillemets simples et doubles
#On remplace toutes les variantes de "&" par "et"
#On normalise les apostrophes (obligatoire pour obtenir un bon étiquetage UDpipe)
#Dans le titre uniquement, on ajoute un point pour marquer la fin, et si cela entraîne un double point alors on s'assure de n'en garder qu'un seul
sub nettoyage {
    my $titre = $_[0];
    my $description = $_[1];
    $titre=~s/^<!\[CDATA\[//;
    $titre=~s/\]\]>$//;
    $description=~s/^<!\[CDATA\[//;
    $description=~s/\]\]>$//;
    $description=~s/&lt;.+?&gt;//g;
    $description=~s/&#38;#39;/'/g;
    $description=~s/&#38;#34;/"/g;
    $description=~s/&amp/et/g;
    $description=~s/&amp;/et/g;
    $description=~s/&/et/g;
    $description=~s/amp;/et/g;
    $description=~s/’/'/g;
    $titre=~s/&lt;.+?&gt;//g;
    $titre=~s/&#38;#39;/'/g;
    $titre=~s/&#38;#34;/"/g;
    $titre=~s/&amp/et/g;
    $titre=~s/&amp;/et/g;
    $titre=~s/&/et/g;
    $titre=~s/amp;/et/g;
    $titre=~s/’/'/g;
    $titre=~s/$/\./g;
    $titre=~s/\.+$/\./g;
    return $titre,$description;
}

#Description du sous-programme qui applique l'étiquetage TreeTagger sur les contenus textuels nettoyés
#On récupère dans des variables les arguments passés au sous-programme lors de son appel (titre nettoyé au préalable, description nettoyée au préalable)
#On traite séparément le titre et la description, mais on leur applique exactement le même traitement
#On crée un fichier temporaire où l'on place les arguments récupérés
#Traitement : tokenisation (tokenise-utf8.pl) puis application du programme TreeTagger avec le modèle français french-utf8.par fourni
#On utilise la fonction system() pour appliquer à ce fichier temporaire l'étiquetage TreeTagger, on envoie le résultat dans un fichier
#On utilise toujours system() pour lancer sur le fichier étiqueté le script treetaggerxml-utf8.pl (modifié en cours pour nos besoins) qui le transforme au format xml
#On récupère dans une variable le contenu du fichier étiqueté au format xml
#On supprime l'en-tête xml 
#On répète la même opération sur la description
#Retour : les contenus textuels nettoyés et désormais étiquetés avec TreeTagger
sub etiquetageTT {
    my $titre = $_[0];
    my $description = $_[1];
	
	#Traitement du titre
	open (ETT, ">:encoding(utf8)", "titre_temp.txt");
    print ETT $titre;
    close ETT;
    system ("perl -f tokenise-utf8.pl titre_temp.txt | ./tree-tagger french-utf8.par -token -lemma -no-unknown -sgml > titre_tokenise.txt");
    system ("perl treetagger2xml-utf8.pl titre_tokenise.txt utf8");
    $/=undef; #On passe au parcours du fichier en entier pour récupérer tout son contenu dans une variable
    open (TEMPT, "<:encoding(utf8)", "titre_tokenise.txt.xml");
    my $titre_tagged=<TEMPT>;
    $titre_tagged=~s/<\?xml version="1\.0" encoding="utf-8" standalone="no"\?>\r?\n?//;

    #Traitement de la description
    open (ETD, ">:encoding(utf8)", "description_temp.txt");
    print ETD $description;
    close ETD;
    system ("perl -f tokenise-utf8.pl description_temp.txt | ./tree-tagger french-utf8.par -token -lemma -no-unknown -sgml > description_tokenise.txt");
    system ("perl treetagger2xml-utf8.pl description_tokenise.txt utf8");
    $/=undef; #On passe au parcours du fichier en entier pour récupérer tout son contenu dans une variable
    open (TEMPD, "<:encoding(utf8)", "description_tokenise.txt.xml");
    my $description_tagged=<TEMPD>;
    $description_tagged =~s/<\?xml version="1\.0" encoding="utf-8" standalone="no"\?>\r?\n?//;
    $/="\n"; #On revient au parcours du fichier ligne par ligne pour récupérer les titres et descriptions au prochain appel du sous-programme
    return $titre_tagged,$description_tagged;
}

#Description du sous-programme qui applique l'étiquetage UDpipe sur le contenus textuels nettoyés
#On récupère dans des variables les arguments passés au sous-programme lors de son appel (titre nettoyé au préalable, description nettoyée au préalable)
#On traitement séparément le titre et la description, mais on leur applique exactement le même traitement
#On crée un fichier temporaire où l'on place les arguments récupérés
#Traitement : application du programme udpipe selon le modèle français fourni
#On utilise la fonction system() pour appliquer à ce fichier temporaire l'étiquetage UDpipe, on envoie le résultat dans un fichier
#On récupère dans une variable le contenu du fichier étiqueté
#On répète la même opération sur la description
#Retour : les contenus textuels nettoyés et désormais étiquetés avec UDpipe
#Remarque : la conversion au format xml sera effectuée à part ensuite à l'aide d'un script spécifique
sub etiquetageUD {
	my $titre = $_[0];
    my $description = $_[1];
	
	#Traitement du titre
	open (ETT, ">:encoding(utf8)", "titre_temp.txt");
    print ETT $titre;
    close ETT;
    system("./distrib-udpipe-1.2.0-bin/udpipe-1.2.0-bin/bin-linux64/udpipe --tokenize --tokenizer=presegmented --tag --parse ./distrib-udpipe-1.2.0-bin/modeles/french-gsd-ud-2.5-191206.udpipe titre_temp.txt > sortieudpipe-titre.txt");
    $/=undef; #On passe au parcours du fichier en entier pour récupérer tout son contenu dans une variable
    open (TEMPT, "<:encoding(utf8)", "sortieudpipe-titre.txt");
    my $titre_UD=<TEMPT>;

    #Traitement de la description
    open (ETD, ">:encoding(utf8)", "description_temp.txt");
    print ETD $description;
    close ETD; 
    system("./distrib-udpipe-1.2.0-bin/udpipe-1.2.0-bin/bin-linux64/udpipe --tokenize --tokenizer=presegmented --tag --parse ./distrib-udpipe-1.2.0-bin/modeles/french-gsd-ud-2.5-191206.udpipe description_temp.txt > sortieudpipe-description.txt");
    $/=undef; #On passe au parcours du fichier en entier pour récupérer tout son contenu dans une variable
    open (TEMPD, "<:encoding(utf8)", "sortieudpipe-description.txt");
    my $description_UD=<TEMPD>;
    $/="\n"; #On revient au parcours du fichier ligne par ligne pour récupérer les titres et descriptions au prochain appel du sous-programme
    return $titre_UD,$description_UD;
}

