Source code for AutoBibPlus


import sys, os, ctypes, win32gui, subprocess, requests
from datetime import datetime
from pathlib import Path

# Utilisation de QT pour la création de l'Interface Homme-Machine (IHM ou HMI en anglais)
from PySide6.QtWidgets import QApplication, QMainWindow, QLineEdit, QVBoxLayout, QWidget, QPlainTextEdit, QMessageBox, QToolBar, QFileDialog
from PySide6.QtGui import QFont, QIcon, QFontMetrics, QAction
from PySide6.QtCore import Qt, Slot

# Importations locales
from Include.Front import ExitBox, CustomTextEdit, LoadingDialog, AchievedMessageBox, ReconfigMessageBox, InfoAPI, Info, Timer

# Appliquer une feuille de style CSS pour le texte en couleur
text_style_warning = '"color: #D35230"'
text_style_parameter = '"color: #AA1C4F"'
text_style_question = '"color: #0C5E31"'

[docs] def check_create_config(console: QPlainTextEdit, response: str, keys: list = None, first_time: bool = False): """Assure l'existence du fichier de configuration pybliometrics avant de poursuivre. :param console: composant console utilise pour afficher les invites et messages. :param response: saisie brute retournee par l'utilisateur via la console. :param keys: paire cle API / insttoken deja enregistree. :param first_time: indique si l'application s'execute pour la premiere fois. :return: tuple (validation, liste des cles normalisees). """ import configparser from Include.pybliometrics.utils.constants import CONFIG_FILE from Include.pybliometrics.utils.create_config import create_config # Read/create config file (with fixture for RTFD.io) config = configparser.ConfigParser() config.optionxform = str # Fichier de configuration existant? if not CONFIG_FILE.exists(): # Clef et token API déjà fournis? if keys == None: # Affiche dans un premier temps le message de bienvenu (permet aussi de garder la couleur noire sur les textes et tableaux par défaut) if first_time: console.append("Bienvenue sur <b>AutoBib+</b>, le logiciel qui vous permez de générer automatiquement les rapports d'analyses bibliométriques et de collaborations de l'ÉTS!") console.append("") console.append('<p style={}>☼ Veuillez entrer votre clef API ainsi que votre token (séparés respectivement par une virgule) pour Scopus et SciVal.</p>'.format(text_style_parameter)) # Rien ne se passe si la zone de texte rentrée est vide if response == '': return False, None # Sépare la zone de texte rentrée avec la virgule comme séparateur elements = response.split(',') # Check le nombre d'éléments et si le deuxième élément n'est pas nul if len(elements) < 2 or elements[1] == '': console.append('<p style={}>! Manque la clef et/ou le token</p>'.format(text_style_warning)) console.append('') console.append('<p style={}>☼ Veuillez entrer votre clef API ainsi que votre token (séparés respectivement par une virgule) pour Scopus et SciVal.</p>'.format(text_style_parameter)) return False, None # Suppression des espaces avant et après les éléments elements = [element.strip() for element in elements] # Test de la clef et du token par envoie d'une requête (API Scopus / AuthorRetrival sur Mohamed Cheriet) try: # Envoi de la requête à l'API Scopus response = requests.get("https://api.elsevier.com/content/author/author_id/56216876600", params={"apiKey": elements[0], "insttoken": elements[1], "httpAccept": "application/json"}) except requests.exceptions.RequestException as e: error_message = "Une erreur s'est produite!\n\nSi l'erreur persiste, veuillez contacter le service technique de votre établissement.\n\nDétails:\n" + str(e) error_dialog = QMessageBox(QMessageBox.Critical, "Erreur", error_message, QMessageBox.Ok) error_dialog.exec() # Vérification du statut de la réponse if response.status_code != 200: console.append('<p style={}>! Clef et/ou Token pour les API invalides (statut de la requête : {})</p>'.format(text_style_warning, response.status_code)) console.append('') console.append('<p style={}>☼ Veuillez entrer votre clef API ainsi que votre token (séparés respectivement par une virgule) pour Scopus et SciVal :</p>'.format(text_style_parameter)) return False, None # On peut passer à l'étape suivante : rentrer un chemin d'accès valide à un répertoire pour enregistrer les rapports par défaut console.append("\n\n") console.append("<p style={}>☼ Veuillez entrer le chemin d'accès ENTIER du répertoire où vous voulez enregistrer les rapports par défault (ex : C:\\Users\\Name\\Documents).</p>".format(text_style_parameter)) return False, elements # Rien ne se passe si la zone de texte rentrée est vide if response == '': return False, keys # Supprimer les espaces de la zone de texte rentrée, en faire un vrai chemin d'accès et vérifier s'il existe en local sur la machine docs_path = Path(response.strip()) if not docs_path.is_dir(): console.append("<p style={}>! Chemin d'accès à un dossier non-valide</p>".format(text_style_warning)) console.append('') console.append("<p style={}>☼ Veuillez entrer le chemin d'accès ENTIER du répertoire où vous voulez enregistrer les rapports par défault (ex : C:\\Users\\Name\\Documents).</p>".format(text_style_parameter)) return False, keys # Suppression du fichier de configuration non-abouti puis création du fichier complet subprocess.run("del %userprofile%\\.config\\pybliometrics.cfg", shell=True) config = create_config(keys=[keys[0]], insttoken=keys[1], docs_path=response.strip()) # Gestion de l'affichage de la zone de text (effacage puis affichage des diverses informations) console.setPlainText('') console.append(f"Le fichier de configuration a été créé avec succès au chemin d'accès : {CONFIG_FILE}.") console.append("Pour plus de détails, veuillez consulter https://pybliometrics.rtfd.io/en/stable/configuration.html.") console.append('') console.append("""Bienvenue sur <b>AutoBib+</b>, le logiciel qui vous permez de générer automatiquement les rapports d'analyses bibliométriques et de collaborations de l'ÉTS! <br><br>Les commandes suivantes pourraient vous aider : <br>&nbsp;&nbsp;- <b>Touche &lt;Entrée/Enter&gt;</b> :&nbsp;&nbsp;permet de sélectionner les paramètres <b>par défaut</b> <br>&nbsp;&nbsp;- <b>Séparateur de valeurs</b> :&nbsp;&nbsp;utilisez la virgule <br><br>La barre d'outils en rouge peut être déplacée à l'aide de sa ligne de points à son extrémité. <br><br><b>Tapez vos commandes dans la barre d'entrée de texte tout en bas de la page</b>, puis validez-les en appuyant sur la touche &lt;Entrée/Enter&gt; de votre clavier.<br><br>""") # console.append('<span style="color: #0C5E31">● Veuillez entrer le nom puis le prénom de la personne (nom, prénom).</span>') # Affiche la première interrogation dans la console console.append('<span style="color: #0C5E31">● Quel type de document souhaitez-vous produire? </span>') # Affiche la première interrogation dans la console console.append('<span style="color: Black"> 1. Fiche bibliométrique </span>') console.append('<span style="color: Black"> 2. Rapport de collaboration </span>') return True, keys
[docs] class ConsoleWindow(QMainWindow): """Fenetre principale d'AutoBib+ pilotant le deroulement conversationnel. Elle orchestre la logique de generation des rapports bibliometriques et de collaboration. """ def __init__(self): """Initialise les widgets, les menus et les variables d'etat de la console.""" super().__init__() # Permet de récupérer le constructeur de la classe mère: QMainWindow script_path = os.getcwd() self.setWindowTitle("AutoBib+ : Logiciel d'automatisation des rapports d'analyses bibliométriques et de collaborations de l'ÉTS") # Définie le nom de la fenêtre self.setWindowIcon(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/ETS_Logo.png")) # Définit le logo # Définie la zone de texte (console) self.console = CustomTextEdit(self) # Instanciation # Définie la zone d'entrée de commandes de l'utilisateur self.input_box = QLineEdit(self) # Instanciation self.input_box.setStyleSheet("background-color: #DEDEDE; color: black; font-family: Consolas; font-size: 14pt; border: 2px solid black;") # Définie le CSS self.input_box.returnPressed.connect(self.handle_input) # Touche ENTER connectée à la méthode handle_input de la même classe # self.input_box.returnPressed.connect(self.printOK) # Touche ENTER connectée à la méthode handle_input de la même classe # Définie la barre d'outils toolbar = QToolBar('Toolbar', self) # Définition du style QSS pour la barre d'outils toolbar.setStyleSheet(""" QToolBar { border: 0px solid black; }; background-color: #EF3635; """) self.addToolBar(Qt.TopToolBarArea, toolbar) # Ajout action retour actRedo = QAction(QIcon(script_path + "/Logos/Back.svg"), "&Retour", self) # actRedo = QAction(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/Back.svg"), "&Retour", self) actRedo.setShortcut('Ctrl+Z') actRedo.triggered.connect(self.retour) toolbar.addAction(actRedo) # Ajout d'un séparateur toolbar.addSeparator() # Ajout action raz actReset = QAction(QIcon(script_path + "/Logos/Reset.svg"), "&Remise à zéro", self) # actReset = QAction(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/Reset.svg"), "&Remise à zéro", self) actReset.setShortcut('Ctrl+R') actReset.triggered.connect(self.raz) toolbar.addAction(actReset) toolbar.addSeparator() # Ajout action feuille blanche actResetALL = QAction(QIcon(script_path + "/Logos/WhiteSheet.svg"), "&Page blanche", self) # actResetALL = QAction(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/WhiteSheet.svg"), "&Page blanche", self) actResetALL.setShortcut('Ctrl+N') actResetALL.triggered.connect(self.whitesheet) toolbar.addAction(actResetALL) toolbar.addSeparator() toolbar.addSeparator() # Ajout action reconfiguration actReconfig = QAction(QIcon(script_path + "/Logos/Config.svg"), "&Reconfiguration", self) # actReconfig = QAction(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/Config.svg"), "&Reconfiguration", self) actReconfig.setShortcut('F1') actReconfig.triggered.connect(self.reconfig) toolbar.addAction(actReconfig) toolbar.addSeparator() toolbar.addSeparator() # Ajout action Infos API actAPI = QAction(QIcon(script_path + "/Logos/API.svg"), "&Infos API", self) # actAPI = QAction(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/API.svg"), "&Infos API", self) actAPI.setShortcut('F2') actAPI.triggered.connect(self.API) toolbar.addAction(actAPI) toolbar.addSeparator() toolbar.addSeparator() # Ajout action Informations actInfos = QAction(QIcon(script_path + "/Logos/Infos.svg"), "&Informations", self) # actInfos = QAction(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/Infos.svg"), "&Informations", self) actInfos.setShortcut('F12') actInfos.triggered.connect(self.infos) toolbar.addAction(actInfos) # Définir la LoadingBox self.loading_dialog = LoadingDialog() # Organisation de la fenêtre Vertical Box et on met les deux éléments à la suite dans le bon ordre! layout = QVBoxLayout() layout.addWidget(self.console) layout.addWidget(self.input_box) # Créé la widget et ajoute le layout principal container = QWidget() container.setLayout(layout) container.setStyleSheet("background-color: #DEDEDE") # Couleur d'arrière-plan de la widget principale # Set le widget sur la fenêtre principale self.setCentralWidget(container) # Initialisation self.text_style_question = '"color: #0C5E31"' self.input_box.setPlaceholderText("Entrez votre texte ici...") self.input_box.setFocus() # Instanciation du Timer self.timer = Timer() # Tableau pour les valeurs sur les clefs API self.infos_API = {'CitationOverview': {'Date': 'None', 'X-RateLimit-Limit': 'None', 'X-RateLimit-Remaining': 'None', 'X-RateLimit-Reset': 'None'}, \ 'AuthorRetrieval': {'Date': 'None', 'X-RateLimit-Limit': 'None', 'X-RateLimit-Remaining': 'None', 'X-RateLimit-Reset': 'None'},\ 'AuthorSearch': {'Date': 'None', 'X-RateLimit-Limit': 'None', 'X-RateLimit-Remaining': 'None', 'X-RateLimit-Reset': 'None'},\ 'AuthorLookup': {'Date': 'None', 'X-RateLimit-Limit': 'None', 'X-RateLimit-Remaining': 'None', 'X-RateLimit-Reset': 'None'}} # Exécuter le fichier .bat de refresh du genpy pour une recherche plus rapide et éviter de futurs bugs liés aux fichiers de ce répertoire subprocess.run(os.path.dirname(os.path.abspath(__file__)) + "/maj_gen_py.bat", shell=True) # Backend: self.first_time = True # Permet de savoir si c'est la première fois que l'on rentre dans la fonction _affichageQuestions self.classeur = None # Variable qui va contenir l'instance du classeur Excel self.keys_valid = None # Variable pour stocker l'état courant de la machine à états validation, _ = check_create_config(self.console, '', first_time=True) self.state = 0 if validation else -1 # Frontend '''console.append('<span style="color: Black"> 1. Fiche bibliométrique </span>') console.append('<span style="color: Black"> 2. Rapport de collaboration </span>')''' self.df_doc_type_selected = [0,0] self.tableauQuestions = [ '<span style={}>● Quel type de document souhaitez-vous produire? </span><br><span style="color: Black"> 1. Fiche bibliométrique </span><br><span style="color: Black"> 2. Rapport de collaboration </span>'.format(self.text_style_question), '<span style={}>● Veuillez entrer le nom puis le prénom de la personne (nom, prénom).</span>'.format(self.text_style_question), "<span style={}>● Quel.s type.s de documents souhaitez-vous exclure de la liste? (Par défaut : aucun)[Entrez le ou les numéro.s de l'index]</span>".format(self.text_style_question), "<span style={}>● Quelle est la plage d'années que vous choisissez? (Par défaut : {}, {} (c-à-d : 3ans et 5ans))(période carrière ajoutée par défaut, si aucune 3ème valeur n'est spécifiée)</span>".format(self.text_style_question, 'NONE', 'NONE'), "<span style={}>● Quels sont les 2 types de publications que vous voulez mettre en valeur? (Par défaut : {}, {} (c-à-d : {}, {}))[Entrez les numéros de l'index]</span><br><span style={}>--Option de combinaison sous le format [n1; n2] : syntaxe permettant de combiner des types de publications sous le nom du 1er type (n1).</span>".format(self.text_style_question, "0", "1", "NONE", "NONE", '"color: #75163F"'), 'NULL', 'NULL', 'NULL', 'NULL', 'NULL', 'NULL', '<span style={}>● Vous souhaitez produire un rapport de collaboration entre deux entités A et B. Veuillez choisir l\'entité A dans cette liste : </span><br><span style="color: Black"> 1. Chercheur ou groupe de chercheurs </span><br><span style="color: Black"> 2. Établissement ou groupe d\'établissements </span>'.format(self.text_style_question), '<span style={}>● Veuillez choisir l\'entité A dans cette liste : </span><br><span style="color: Black"> 1. Liste des ORN </span><br><span style="color: Black"> 2. Réseau UQ </span><br><span style="color: Black"> 3. Réseau ETS </span><br><span style="color: Black"> 4. Autres </span>'.format(self.text_style_question), '<span style={}>● Veuillez choisir l\'entité A dans cette liste : </span><br><span style="color: Black"> 1. Liste des professeurs de l\'ETS </span><br><span style="color: Black"> 2. Autres </span>'.format(self.text_style_question), '<span style={}>● Veuillez entrer l\'identifiant (ou la liste des identifiants) Scopus de l\'entité A. <br> Si l\'entité A est un chercheur, vous pouvez aussi saisir son nom. <br> Utilisez la virgule comme séparateur si plusieurs identifiants à rentrer (ID1, ID2, ...)</span>'.format(self.text_style_question), '<span style={}>● Veuillez choisir l\'entité B dans cette liste : </span><br><span style="color: Black"> 1. Chercheur ou groupe de chercheurs </span><br><span style="color: Black"> 2. Établissement ou groupe d\'établissements </span><br><span style="color: Black"> 3. Pays </span>'.format(self.text_style_question), '<span style={}>● Veuillez choisir l\'entité B dans cette liste : </span><br><span style="color: Black"> 1. Liste des ORN </span><br><span style="color: Black"> 2. Réseau UQ </span><br><span style="color: Black"> 3. Réseau ETS </span><br><span style="color: Black"> 4. Autres </span>'.format(self.text_style_question), '<span style={}>● Veuillez entrer l\'identifiant (ou la liste des identifiants) Scopus de l\'entité B. <br> Si l\'entité B est un chercheur, vous pouvez aussi saisir son nom. <br> Utilisez la virgule comme séparateur si plusieurs identifiants à rentrer (ID1, ID2, ...)</span>'.format(self.text_style_question), "<span style={}>● Quelle est la plage d'années que vous choisissez? (Par défaut : {}, {})</span>".format(self.text_style_question, 'NONE', 'NONE'), 'NULL', 'NULL' ]
[docs] def handle_input(self): """Traite la saisie courante et fait avancer la machine a etats.""" # print('dans handle_input') # Stock la réponse de l'utilisateur, efface la zone d'entrée de texte et affiche la réponse sur la zone de texte self.response = self.input_box.text() self.input_box.clear() self.console.append('<span style="color: blue">{}</span>'.format("► " + self.response)) # Afficher l'entrée de l'utilisateur # Si l'état courant est à -1, la création du fichier de configuration se fait alors if self.state == -1: # Check fichier de configuration déjà créé ou non validation, keys = check_create_config(self.console, self.response, self.keys_valid) if not validation: self.keys_valid = keys return self.state = 0 return from Include.Tools import homonyme, selection_homonyme, tab_graph_Collab, \ selection_types_de_documents, donnees_documents_graph_citations, selection_plages_annees, tab_graph_citations, \ tab_graph_publications, Excel_part1, Excel_part2, valeurs_encadre, selection_2_types_docs, tab_graph_SNIP, \ collaborationExtract, getSelectedYears, load_ETS_profs, findFuzzyMatches, countAuthorsInCollab, \ countInstitutionsInCollab, countEntityAuthorsInCollab, add_affiliation_ids_to_list, load_UQ, load_ORN, load_ETS,\ get_country_in_english, get_country_in_french, get_country_for_request,saveInter,count_document_types,\ findOthersEtsAffiliations, findCollabCountryAffiliations, getEntityProfile, Excel_collabs_ETS_pays \ # Excel_autres_collabs from Include.pybliometrics.scopus.author_search import AuthorSearch import pandas as pd # Afficher le message de chargement self.loading_dialog.show() # Forcer le rafraîchissement de l'interface utilisateur QApplication.processEvents() # Obtient la largeur d'un caractère (qui est fixe car nous sommes en police monospace) font_metrics = QFontMetrics(QFont("Consolas", 11)) width_char = font_metrics.averageCharWidth() # Test l'exécution du script de l'état courant et test la validité des transitions possibles sinon message d'erreur try: # Machine à états match self.state: # État initial : recherche d'une personne par son nom et son prénom # État initial : choix du type de rapport à produire case 0: # self.console.append('<p style={}>● Quel type de document souhaitez-vous produire? </p>'.format(text_style_question)) if self.response == '1': self.state += 1 self._affichageQuestions(1) elif self.response == '2': self._affichageQuestions(11) self.state = 11 else : self.console.append('<p style={}>! Veuillez choisir un type de document dans la liste proposée</p>'.format(text_style_warning)) # Fermer le message de chargement self.loading_dialog.close() return case 1: # Validation du format de la requête de l'utilisateur if ',' in self.response and not self.response.split(",")[1] =='': last_name = self.response.split(",")[0] first_name = self.response.split(",")[1] else: self.console.append('<p style={}>! Manque du séparateur (virgule) et/ou du prénom du personne</p>'.format(text_style_warning)) self.console.append('') self.console.append('<p style={}>● Veuillez entrer le nom puis le prénom de la personne (nom, prénom).</p>'.format(text_style_question)) # Fermer le message de chargement self.loading_dialog.close() return # Lancement du timer self.timer.start() # Recherche de la personne self.search = AuthorSearch('AUTHLAST(' + last_name + ') and AUTHFIRST(' + first_name + ')', refresh=True) self.infos_API['AuthorSearch'].update({key: self.search._header[key] for key in self.search._header if key in self.infos_API['AuthorSearch']}) # Incrémentation en fonction du nombre d'homonyme self.state += homonyme(self.search, self.console, int(self.width()/width_char)-10) # self.state += 2 # S'il n'y a pas d'homonymes self._rechercheSurChercheur() if self.state == 3 else None # État 1 : cas où la recherche a mené à des homonymes, il faut alors choisir l'un d'entre eux case 2: # Vérifie si le ou les numéros d'index sont correctes if selection_homonyme(self.response, self.search, self.console): self.state += 1 self._rechercheSurChercheur(choix=int(self.response)) # État 2 : certains documents doivent être exclus et export des premières données vers le doc Excel case 3: # Séparer les types de documents sélectionnés par l'utilisateur selected_types = self.response.split(',') selected_types = [element.strip() for element in selected_types] # Vérifie la conformité de la commande de l'utilisateur if len(selected_types) == 1 and selected_types[0] == "": self.index_list = self.df_doc_type.index.tolist() elif selection_types_de_documents(selected_types, len(self.df_doc_type), self.console): # Obtenir une liste d'entier puis mettre à jour la liste selected_types = [int(x) for x in selected_types] self.index_list = [x for x in self.df_doc_type.index.tolist() if x not in selected_types] else: self._affichageQuestions(2) return self.state += 1 # Calcul, mise en forme des données pour le graphique des citations self.docs_list, self.selected_eids_list, self.years = donnees_documents_graph_citations(self.au_retrieval, self.index_list, self.df_doc_type, self.console) self.df, self.nom_prenom, header = tab_graph_citations(self.au_retrieval, self.selected_eids_list, self.docs_list, self.console, int(self.width()/width_char)-10) # Mise à jour du dictionnaire des données sur les API self.infos_API['CitationOverview'].update({key: header[key] for key in header if key in self.infos_API['CitationOverview']}) # Calcul, mise en forme des données pour les valeurs de l'encadré self.en_tete, self.annee_10y_adapt, header = valeurs_encadre(self.authorEID, self.years) # Mise à jour du dictionnaire des données sur les API self.infos_API['AuthorLookup'].update({key: header[key] for key in header if key in self.infos_API['AuthorLookup']}) self.excel, self.classeur = Excel_part1(self.df, self.nom_prenom, self.en_tete, self.annee_10y_adapt) self._affichageQuestions(3) # État 3 : les plages d'années des histogrammes sont choisies ici case 4: validation, self.years_list, self.df_doc_type_selected = selection_plages_annees(self.response, self.years, self.index_list, self.df_doc_type, self.console) if validation: self.state += 1 self._affichageQuestions(4) else: self._affichageQuestions(3) # État 4 : les 2 types de publications mis en avant sur le graphique des Pubications se fait ici AINSI que l'envoie de toutes les autres données pour # le doc Excel ainsi que l'appel des routines VBA pour réaliser la mise en forme des données et la création de la fiche bibliométrique Word case 5: validation, self.type_list = selection_2_types_docs(self.response, self.df_doc_type_selected, self.console) if validation: self.state = 1 self.df_pub = tab_graph_publications(self.au_retrieval, self.selected_eids_list, self.years_list, self.type_list, self.console, int(self.width()/width_char)-10) self.years_list = [[int(item) for item in sublist] for sublist in self.years_list] self.df_SNIP, header = tab_graph_SNIP(console=self.console, author_id=self.authorEID, years_list=self.years_list, window_width=int(self.width()/width_char)-10) self.df_Collab, header = tab_graph_Collab(console=self.console, author_id=self.authorEID, years_list=self.years_list, window_width=int(self.width()/width_char)-10) self.infos_API['AuthorLookup'].update({key: header[key] for key in header if key in self.infos_API['AuthorLookup']}) nom_classeur = self.classeur.Name nom_classeur = nom_classeur.split('.')[0] Excel_part2(self.excel, self.classeur, self.df_pub, self.df_SNIP, self.df_Collab) self.classeur = None # Fermer le message de chargement self.loading_dialog.close() self.timer.stop() achieved_msg = AchievedMessageBox(time=self.timer.get_elapsed_time()) achieved_msg.exec() self.timer.reset() self.console.append("\n") self.console.append("<b>Rapport d'analyse bibliométrique créé avec succès!</b>") self.console.append("\n\n") # Mettre la fenêtre Word en premier plan try: win32gui.SetForegroundWindow(win32gui.FindWindow(None, nom_classeur + " - Word")) except: win32gui.SetForegroundWindow(win32gui.FindWindow(None, nom_classeur + '.docx' + " - Word")) self._affichageQuestions(1) ##### else: self._affichageQuestions(4) case 11: import configparser from Include.pybliometrics.utils.constants import CONFIG_FILE # Lancement du timer self.timer.start() self.fileNamePartA = '' self.fileNamePartB = '' self.indexAuteur = 0 if self.response == '1': self.entiteA = self.response self.state += 2 self._affichageQuestions(13) elif self.response == '2': self.entiteA = self.response self.state += 1 self._affichageQuestions(12) else : self.console.append('<p style={}>! Veuillez choisir un ensemble dans la liste proposée</p>'.format(text_style_warning)) return config = configparser.ConfigParser() config.read(CONFIG_FILE) self.Keys = [config['Authentication']['APIKey'], config['Authentication']['InstToken']] case 12: self.listEntityA = [] self.reseauETS = False if self.response == '1' or self.response == '2' or self.response == '3': if self.response == '1': listAffil = load_ORN(self.console) self.fileNamePartA = 'Reseau_ORN' elif self.response == '2': listAffil = load_UQ(self.console) self.fileNamePartA = 'Reseau_UQ' elif self.response == '3': listAffil = load_ETS(self.console) self.fileNamePartA = 'Reseau_ETS' self.reseauETS = True self.listEntityA = add_affiliation_ids_to_list(listAffil, self.listEntityA, self.console) self.state += 3 self._affichageQuestions(15) elif self.response == '4': self.state += 2 self._affichageQuestions(14) else : self.console.append('<p style={}>! Veuillez choisir un ensemble dans la liste proposée</p>'.format(text_style_warning)) return case 13: self.listEntityA = [] if self.response == '1': self.fileNamePartA = 'Profs_ETS' listAffil = load_ETS_profs(self.console) self.listEntityA = add_affiliation_ids_to_list(listAffil, self.listEntityA, self.console) self.state += 2 self._affichageQuestions(15) elif self.response == '2': self.state += 1 self._affichageQuestions(14) else : self.console.append('<p style={}>! Veuillez choisir un ensemble dans la liste proposée</p>'.format(text_style_warning)) return case 14: self.listEntityA = [] # Verification de l'existence de l'entité RechercheParId=False if self.response == '' : self._affichageQuestions(14) elif ',' in self.response: self.response = self.response.replace(" ", "") self.response = self.response.replace("\n", "") entities = self.response.split(',') if self.entiteA == '1': for caractere in entities: if caractere.isdigit(): RechercheParId = True if RechercheParId is False: results = getEntityProfile(self.entiteA, self.response, self.Keys, RechercheParId) if results == 'NONE': self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) self._affichageQuestions(14) else: if len(results) == 1 : self.console.append(f"Resumé du profil de l'auteur : \n") self.console.append(results[0]) self.state += 1 self._affichageQuestions(15) for line in results[0].splitlines(): if line.startswith("ID :"): identifiant = line.split(":")[1].strip() identifiant = identifiant.split("-")[2].strip() self.listEntityA.append(identifiant) if line.startswith("Nom :"): self.fileNamePartA = line.split(":")[1].strip() self.fileNamePartA = self.fileNamePartA.replace(" ", "_") else: for index, result in enumerate(results): self.console.append(f"Resumé du profil de l'auteur : ") self.console.append(f"Index : {index}") self.console.append(result) self.console.append("\n") self.console.append('<p style={}>● Veuillez choisir l\'index de l\'auteur </p>'.format(text_style_question)) self.state = 19 self.saveresults = results else: for index, entity in enumerate(entities): results = getEntityProfile(self.entiteA, entity, self.Keys, RechercheParId) if results == 'NONE': self.console.append(f"Auteur {index + 1}: \n") self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) else: self.console.append(f"Resumé du profil de l'auteur {index + 1}: \n") self.console.append(results[0]) self.listEntityA.append(entity) self.fileNamePartA = 'Gr_Auteurs_A' self.state += 1 self._affichageQuestions(15) elif self.entiteA == '2': for index, entity in enumerate(entities): results = getEntityProfile(self.entiteA, entity, self.Keys, RechercheParId) if results == 'NONE': self.console.append(f"Institution {index + 1}: \n") self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) else: self.console.append(f"Resumé du profil de l'institution {index + 1}: \n") self.console.append(results) self.listEntityA.append(entity) self.fileNamePartA = 'Gr_Institutions_A' self.state += 1 self._affichageQuestions(15) else : self.response = self.response.replace(" ", "") self.response = self.response.replace("\n", "") results = getEntityProfile(self.entiteA, self.response, self.Keys, True) if results == 'NONE': self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) self._affichageQuestions(14) else: if self.entiteA == '1': self.console.append("Resumé du profil de l'auteur : \n") for line in results.splitlines(): if line.startswith("Nom :"): self.fileNamePartA = line.split(":")[1].strip() self.fileNamePartA = self.fileNamePartA.replace(" ", "_") elif self.entiteA == '2': self.console.append("Resumé du profil de l'institution : \n") self.fileNamePartA = 'Institution_A' self.console.append(results) self.listEntityA.append(self.response) self.state += 1 self._affichageQuestions(15) case 15: if self.response == '2' : self.entiteB = self.response self.state += 1 self._affichageQuestions(16) elif self.response == '1' or self.response == '3': self.entiteB = self.response self.state += 2 self._affichageQuestions(17) else : self.console.append('<p style={}>! Veuillez choisir un ensemble dans la liste proposée</p>'.format(text_style_warning)) return case 16: self.listEntityB = [] if self.response == '1' or self.response == '2' or self.response == '3': if self.response == '1': listAffil = load_ORN(self.console) self.fileNamePartB = 'Reseau_ORN' elif self.response == '2': listAffil = load_UQ(self.console) self.fileNamePartB = 'Reseau_UQ' elif self.response == '3': listAffil = load_ETS(self.console) self.fileNamePartB = 'Reseau_ETS' self.listEntityB = add_affiliation_ids_to_list(listAffil, self.listEntityB, self.console) self.state += 2 self._affichageQuestions(18) elif self.response == '4': self.state += 1 self._affichageQuestions(17) else : self.console.append('<p style={}>! Veuillez choisir un ensemble dans la liste proposée</p>'.format(text_style_warning)) return case 17: self.listEntityB = [] RechercheParId = False # Verification de l'existence de l'entité if self.response == '' : self._affichageQuestions(17) elif ',' in self.response: self.response = self.response.replace(" ", "") self.response = self.response.replace("\n", "") entities = self.response.split(',') if self.entiteB == '1': for caractere in entities: caractere = caractere.strip() if caractere.isdigit(): RechercheParId = True if RechercheParId is False: results = getEntityProfile(self.entiteB, self.response, self.Keys, RechercheParId) if results == 'NONE': self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) self._affichageQuestions(17) else: if len(results) == 1 : self.console.append(f"Resumé du profil de l'auteur : \n") self.console.append(results[0]) self.state += 1 self._affichageQuestions(18) for line in results[0].splitlines(): if line.startswith("ID :"): identifiant = line.split(":")[1].strip() identifiant = identifiant.split("-")[2].strip() self.listEntityB.append(identifiant) if line.startswith("Nom :"): self.fileNamePartB = line.split(":")[1].strip() self.fileNamePartB = self.fileNamePartB.replace(" ", "_") else: for index, result in enumerate(results): self.console.append(f"Resumé du profil de l'auteur : ") self.console.append(f"Index : {index}") self.console.append(result) self.console.append("\n") self.console.append('<p style={}>● Veuillez choisir l\'index de l\'auteur </p>'.format(text_style_question)) self.state = 20 self.saveresults = results else: for index, entity in enumerate(entities): results = getEntityProfile(self.entiteB, entity, self.Keys, RechercheParId) if results == 'NONE': self.console.append(f"Auteur {index + 1}: \n") self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) else: self.console.append(f"Resumé du profil de l'auteur {index + 1}: \n") self.console.append(results[0]) self.listEntityB.append(entity) self.fileNamePartB = 'Gr_Auteurs_B' self.state += 1 self._affichageQuestions(18) elif self.entiteB == '2': for index, entity in enumerate(entities): results = getEntityProfile(self.entiteB, entity, self.Keys, RechercheParId) if results == 'NONE': self.console.append(f"Institution {index + 1}: \n") self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) else: self.console.append(f"Resumé du profil de l'institution {index + 1}: \n") self.console.append(results) self.listEntityB.append(entity) self.fileNamePartB = 'Gr_Institutions_B' self.state += 1 self._affichageQuestions(18) else : self.response = self.response.replace(" ", "") self.response = self.response.replace("\n", "") if self.entiteB == '1' or self.entiteB == '2': results = getEntityProfile(self.entiteB, self.response, self.Keys, True) if results == 'NONE': self.console.append('<p style={}>! Aucun profil trouvé </p>'.format(text_style_warning)) self._affichageQuestions(17) else : self.listEntityB.append(self.response) if self.entiteB == '1': self.console.append("Resumé du profil de l'auteur : \n") for line in results.splitlines(): if line.startswith("Nom :"): self.fileNamePartB = line.split(":")[1].strip() self.fileNamePartB = self.fileNamePartB.replace(" ", "_") elif self.entiteB == '2': self.console.append("Resumé du profil de l'institution : \n") self.fileNamePartB = 'Institution_B' self.console.append(results) self.state += 1 self._affichageQuestions(18) elif self.entiteB == '3': self.country = self.response.strip() self.country_for_request = get_country_for_request(self.country) self.country_in_english = get_country_in_english(self.country) self.fileNamePartB = get_country_in_french(self.country).replace(" ", "_") if self.entiteB == '3' and self.country_in_english == 'NULL': self.console.append('<p style={}>!Aucun pays ne correspond à cette saisie </p>'.format(text_style_warning)) else : self.state += 1 self._affichageQuestions(18) case 18: self.start_year, self.end_year = getSelectedYears (self.response) # Vérifie la conformité de la commande de l'utilisateur if self.start_year == 'NULL' and self.end_year == 'NULL': self.console.append('<p style={}>! Veuillez saisir une plage correcte (Format : début, fin)</p>'.format(text_style_warning)) else: if self.entiteA == '2': if len(self.listEntityA) == 1 and self.listEntityA[0] == '60026786': self.fileNamePartA = 'ets' dateAjourdhui = str(datetime.now()).split(" ")[0] filename = f'{dateAjourdhui}_collabs_{self.fileNamePartA}_{self.fileNamePartB}_{self.start_year}_{self.end_year}.xlsm' #------------Recherche de données sur les collaborations entre l'entitéA et l'entitéB---------------------- if self.entiteA == '1' and self.entiteB == '1': dfAllResult = collaborationExtract(researchersA= self.listEntityA, researchersB= self.listEntityB,\ start_year=self.start_year, end_year=self.end_year, keys = self.Keys, console=self.console) if dfAllResult is None: self.state = 0 self._affichageQuestions(0) else: dfAllResult = count_document_types(dfAllResult) df_authors_collab = countAuthorsInCollab(dfAllResult, self.Keys) saveInter(fileName=filename, dfAllResults=dfAllResult , dfAuteurs=df_authors_collab) elif self.entiteA == '1' and self.entiteB == '2': dfAllResult = collaborationExtract(researchersA= self.listEntityA, institutionsB= self.listEntityB, \ start_year=self.start_year, end_year=self.end_year, keys = self.Keys, console=self.console) if dfAllResult is None: self.state = 0 self._affichageQuestions(0) else: dfAllResult = count_document_types(dfAllResult) df_authors_collab = countAuthorsInCollab(dfAllResult, self.Keys) df_authors_entityB = countEntityAuthorsInCollab(dfAllResult, self.listEntityB, self.Keys) saveInter(fileName=filename, dfAllResults=dfAllResult , dfAuteurs=df_authors_collab, dfAuteursB=df_authors_entityB) elif self.entiteA == '2' and self.entiteB == '1': dfAllResult = collaborationExtract(institutionsA= self.listEntityA, researchersB= self.listEntityB,\ start_year=self.start_year, end_year=self.end_year, keys = self.Keys, console=self.console) if dfAllResult is None: self.state = 0 self._affichageQuestions(0) else: dfAllResult = count_document_types(dfAllResult) df_authors_collab = countAuthorsInCollab(dfAllResult, self.Keys) df_authors_entityA = countEntityAuthorsInCollab(dfAllResult, self.listEntityA, self.Keys) saveInter(fileName=filename, dfAllResults=dfAllResult , dfAuteurs=df_authors_collab, dfAuteursA=df_authors_entityA) elif self.entiteA == '2' and self.entiteB == '2': dfAllResult = collaborationExtract(institutionsA= self.listEntityA, institutionsB= self.listEntityB,\ start_year=self.start_year, end_year=self.end_year, keys = self.Keys, console=self.console) if dfAllResult is None: self.state = 0 self._affichageQuestions(0) else: # if (len(self.listEntityA) == 1 and self.listEntityA[0] == '60026786' ) or self.reseauETS is True: # ETS ou reseau ETS # df_prof_ets = load_ETS_profs(self.console) # matches_df, non_matches_df, fuzzy_matches = findFuzzyMatches(df_authors_collab, df_prof_ets, self.console) # other_ets_authors_df = findOthersEtsAffiliations(non_matches_df, dfAllResult) # other_authors_df = countEntityAuthorsInCollab(dfAllResult, self.listEntityB, self.Keys) # Excel_collabs_ETS_pays(filename, matches_df, other_ets_authors_df, other_authors_df, df_institutions, dfAllResult, fuzzy_matches, self.fileNamePartB, self.start_year, self.end_year, dateAjourdhui) # else : dfAllResult = count_document_types(dfAllResult) df_authors_entityA = countEntityAuthorsInCollab(dfAllResult, self.listEntityA, self.Keys) df_authors_entityB = countEntityAuthorsInCollab(dfAllResult, self.listEntityB, self.Keys) saveInter(fileName=filename, dfAllResults=dfAllResult , dfAuteursA=df_authors_entityA, dfAuteursB=df_authors_entityB) elif self.entiteA == '1' and self.entiteB == '3': dfAllResult = collaborationExtract(researchersA= self.listEntityA, country=self.country_for_request,\ start_year=self.start_year, end_year=self.end_year, keys = self.Keys, console=self.console) if dfAllResult is None: self.state = 0 self._affichageQuestions(0) else: dfAllResult = count_document_types(dfAllResult) df_institutions = countInstitutionsInCollab(dfAllResult, collabCountry=self.country_in_english) df_authors_collab = countAuthorsInCollab(dfAllResult, self.Keys) df_authors_entityB = findCollabCountryAffiliations(df_authors_collab, dfAllResult, self.country_in_english, self.Keys) saveInter(fileName=filename, dfAllResults=dfAllResult , dfAuteurs=df_authors_collab, dfAuteursB=df_authors_entityB, dfInstitutions=df_institutions) elif self.entiteA == '2' and self.entiteB == '3': dfAllResult = collaborationExtract(institutionsA= self.listEntityA, country=self.country_for_request,\ start_year=self.start_year, end_year=self.end_year, keys = self.Keys, console=self.console) if dfAllResult is None: self.state = 0 self._affichageQuestions(0) else: dfAllResult = count_document_types(dfAllResult) df_authors_collab = countAuthorsInCollab(dfAllResult, self.Keys) df_institutions = countInstitutionsInCollab(dfAllResult, collabCountry=self.country_in_english) if (len(self.listEntityA) == 1 and self.listEntityA[0] == '60026786' ) or self.reseauETS is True: # ETS ou reseau ETS df_prof_ets = load_ETS_profs(self.console) matches_df, non_matches_df, fuzzy_matches = findFuzzyMatches(df_authors_collab, df_prof_ets, self.console) other_ets_authors_df = findOthersEtsAffiliations(non_matches_df, dfAllResult) other_authors_df = findCollabCountryAffiliations(non_matches_df, dfAllResult, self.country_in_english, self.Keys) # filename = f'collaborations_ets_{self.fileNamePartB}_{self.start_year}_{self.end_year}.xlsm' Excel_collabs_ETS_pays(filename, matches_df, other_ets_authors_df, other_authors_df, df_institutions, dfAllResult, fuzzy_matches, self.fileNamePartB, self.start_year, self.end_year, dateAjourdhui) else : df_authors_entityA = countEntityAuthorsInCollab(dfAllResult, self.listEntityA, self.Keys) df_authors_entityB = findCollabCountryAffiliations(df_authors_collab, dfAllResult, self.country_in_english, self.Keys) df_institutions = countInstitutionsInCollab(dfAllResult, collabCountry=self.country_in_english) saveInter(fileName=filename, dfAllResults=dfAllResult , dfAuteursA=df_authors_entityA, dfAuteursB=df_authors_entityB, dfInstitutions=df_institutions) # Fermer le message de chargement self.loading_dialog.close() self.timer.stop() if dfAllResult is not None: achieved_msg = AchievedMessageBox(time=self.timer.get_elapsed_time()) achieved_msg.exec() self.timer.reset() if dfAllResult is not None: # dfAllResult.to_excel(filename, index=False) self.console.append("\n") self.console.append("<b>Rapport de collaboration créé avec succès!</b>") self.console.append("\n\n") self.state = 0 self._affichageQuestions(0) # Cette partie a été rajoutée au dernier moment. D'ou son emplacement. # Ramener ces etapes apres l'etape 14 et mettre à jour le code en consequence case 19: index = int(self.response) if index >= len(self.saveresults) : self.console.append('<p style={}>! Veuillez choisir un index dans la liste proposée</p>'.format(text_style_warning)) else : line = self.saveresults[index].splitlines() identifiant = line[1].split(":")[1].strip() identifiant = identifiant.split("-")[2].strip() self.listEntityA.append(identifiant) self.fileNamePartA = line[0].split(":")[1].strip() self.fileNamePartA = self.fileNamePartA.replace(" ", "_") self.state = 15 self._affichageQuestions(15) case 20: index = int(self.response) if index >= len(self.saveresults) : self.console.append('<p style={}>! Veuillez choisir un index dans la liste proposée</p>'.format(text_style_warning)) else : line = self.saveresults[index].splitlines() identifiant = line[1].split(":")[1].strip() identifiant = identifiant.split("-")[2].strip() self.listEntityB.append(identifiant) self.fileNamePartB = line[0].split(":")[1].strip() self.fileNamePartB = self.fileNamePartB.replace(" ", "_") self.state = 18 self._affichageQuestions(18) #-------------------------------------------------------------------------------------------------------------------------------- # Une erreur est rencontrée lors de l'exécution d'un des états, alors une boîte de dialogue s'affiche avec les détails de l'erreur except Exception as e: error_message = "Une erreur s'est produite!\n\nSi l'erreur persiste, veuillez contacter le service technique de votre établissement.\n\nDétails :\n" + str(e) error_dialog = QMessageBox(QMessageBox.Critical, "Erreur", error_message, QMessageBox.Ok) error_dialog.exec() print(e) self.state = 0 self._affichageQuestions(self.state) # Déplacer le QTextEdit à sa toute fin self.console.verticalScrollBar().setValue(self.console.verticalScrollBar().maximum()) # Fermer le message de chargement self.loading_dialog.close()
def _affichageQuestions(self, affichage_type: int): """Affiche la question ou le message correspondant a l'etat du deroulement.""" if affichage_type == 3: # Supprimer le troisième élément en utilisant pop() self.tableauQuestions.pop(3) self.tableauQuestions.insert(3, "<span style={}>● Quelle est la plage d'années que vous choisissez? (Par défaut : {}, {} (c-à-d : 3ans et 5ans))(période carrière ajoutée par défaut, si aucune 3ème valeur n'est spécifiée)</span>".format(self.text_style_question, datetime.now().year-3, datetime.now().year-5)) elif affichage_type == 4: # Supprimer le quatrième élément en utilisant pop() self.tableauQuestions.pop(4) liste_types_selec = self.df_doc_type_selected['Type de documents'].to_list() default_first = self.index_list[0] default_second = self.index_list[1] if len(self.index_list) > 1 else 'N/A' label_first = liste_types_selec[0] label_second = liste_types_selec[1] if len(liste_types_selec) > 1 else 'N/A' prompt = ( "<span style={}>? Quels sont les 2 types de publications a mettre en avant ? (Par defaut : {}, {} soit {}, {}) [Entrez les numeros d'index]</span>" "<br><span style={}>--Option de combinaison [n1; n2] pour regrouper sous le premier type.</span>" ).format(self.text_style_question, default_first, default_second, label_first, label_second, '"color: #75163F"') self.tableauQuestions.insert(4, prompt) elif affichage_type == 18: self.tableauQuestions.pop(18) self.tableauQuestions.insert(18, "<span style={}>● Quelle est la plage d'années que vous choisissez? (Par défaut : {}, {})</span>".format(self.text_style_question, datetime.now().year-5, datetime.now().year)) elif affichage_type == 17: if self.entiteB == '1' or self.entiteB == '2': self.tableauQuestions.pop(17) self.tableauQuestions.insert(17, "<span style={}>● Veuillez entrer l\'identifiant (ou la liste des identifiants) Scopus de l\'entité B. <br> Si l\'entité B est un chercheur, vous pouvez aussi saisir son nom. <br> Utilisez la virgule comme séparateur si plusieurs identifiants à rentrer (ID1, ID2, ...) </span>".format(self.text_style_question)) elif self.entiteB == '3': self.tableauQuestions.pop(17) self.tableauQuestions.insert(17, "<span style={}>● Veuillez entrer le nom du pays </span>".format(self.text_style_question)) self.console.append('') self.console.append(self.tableauQuestions[affichage_type]) def _rechercheSurChercheur(self, choix: int = 0): """Interroge Scopus pour la personne selectionnee et actualise le cache local.""" from Include.Tools import retrieval, tous_les_docs_chercheur self.authorEID, self.au_retrieval = retrieval(choix, self.search, self.console) self.infos_API['AuthorRetrieval'].update({key: self.au_retrieval._header[key] for key in self.au_retrieval._header if key in self.infos_API['AuthorRetrieval']}) self.df_doc_type = tous_les_docs_chercheur(self.au_retrieval, self.console) self._affichageQuestions(2)
[docs] def closeEvent(self, event): """Propose une confirmation avant de fermer la fenetre de l'application.""" message_box = ExitBox(self) # Instanciation message_box.exec() if message_box.clickedButton() == message_box.buttonYes: event.accept() # Arrêt du chronomètre et remise à zéro self.timer.stop() self.timer.reset() # Fermer le gabarit sans l'enregistrer s'il est ouvert self.classeur.Close(SaveChanges=False) if self.classeur is not None else None else: event.ignore()
@Slot() def retour(self): """Revient a l'etape precedente lorsque le retour est autorise.""" if self.state != 0 and self.state != -1: if (self.state > 1 and self.state < 6) or (self.state > 11 and self.state < 21): if self.state == 13 or self.state == 16: self.state -= 2 else: if self.state == 19: self.state = 14 elif self.state == 20: self.state = 17 else: self.state -= 1 else: self.state = 0 self._affichageQuestions(self.state) @Slot() def raz(self): """Reinitialise l'interaction et renvoie vers l'accueil.""" if self.state != 0 and self.state != -1: self.state = 0 self._affichageQuestions(self.state) # Fermer le gabarit sans l'enregistrer s'il est ouvert if self.classeur is not None: self.classeur.Close(SaveChanges=False) self.classeur = None @Slot() def reconfig(self): """Ouvre l'assistant de reconfiguration pour mettre a jour identifiants et chemins.""" message_box = ReconfigMessageBox(self) # Instanciation message_box.exec() if self.state != -1 and message_box.clickedButton() == message_box.buttonYes: self.state = -1 self.console.setPlainText('') subprocess.run("del %userprofile%\\.config\\pybliometrics.cfg", shell=True) check_create_config(self.console, '', None, True) @Slot() def API(self): """Affiche les informations d'API memorisees pour la session en cours.""" message_box = InfoAPI(self.infos_API) # Instanciation message_box.exec() @Slot() def infos(self): """Affiche la boite d'information generale sur AutoBib+.""" message_box = Info() # Instanciation message_box.exec() @Slot() def whitesheet(self): """Vide la console sans modifier l'etat courant.""" if self.state != -1: self.console.setPlainText('') self.console.append("""Bienvenue sur <b>AutoBib+</b>, le logiciel qui vous permez de générer automatiquement les rapports d'analyses bibliométriques et de collaborations de l'ÉTS! <br><br>Les commandes suivantes pourraient vous aider : <br>&nbsp;&nbsp;- <b>Touche &lt;Entrée/Enter&gt;</b> :&nbsp;&nbsp;permet de sélectionner les paramètres <b>par défaut</b> <br>&nbsp;&nbsp;- <b>Séparateur de valeurs</b> :&nbsp;&nbsp;utilisez la virgule <br><br>La barre d'outils en rouge peut être déplacée à l'aide de sa ligne de points à son extrémité. <br><br><b>Tapez vos commandes dans la barre d'entrée de texte tout en bas de la page</b>, puis validez-les en appuyant sur la touche &lt;Entrée/Enter&gt; de votre clavier.<br>""") self.state = 1 self.raz()
# Main loop if __name__ == "__main__": ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID('ETS.Automatisation_Rapports_Bibliometriques') # Permet que l'OS voit l'exécution du script indépendante de Python et donc de changer le logo lors du script app = QApplication(sys.argv) # Instanciation d'une application QT avec les arguments système app.setWindowIcon(QIcon(os.path.dirname(os.path.abspath(__file__)) + "/Logos/ETS_Logo.png")) # Affiche l'icône window = ConsoleWindow() # Instanciation de la fenêtre que nous venons de créer window.setStyleSheet("background-color: white; border: NONE;") # Couleur d'arrière-plan de la fenêtre window.showMaximized() # Affiche en plein écran sys.exit(app.exec()) # Pour la fermeture de l'application