Mes premiers pas sur Python — acte III

Afin de pratiquer Python, j’ai trouvé quelques pages d’exercices sur w3resource. Je n’ai pas réalisé tous les exercices présentés mais plutôt une sélection d’entre eux. Dans cet article, je vais présenter mon code pour chacun d’entre eux, la solution officielle n’étant pas forcément la seule valable. Nombre de ces scripts utilisent des arguments pour être fonctionnels ; vous pouvez appeler l’aide intégrée au script en passant -h en argument. J’ai codé ces snippets avec Python 3.9.1 sur macOS.

Continue reading

Mes premiers pas sur Python – acte II

Il y a quelques semaines de cela, je réalisais mes premiers scripts en Python, langage que j’ai toujours voulu apprendre mais pour lequel je manquais de use case concrets, ayant plus l’habitude de Powershell, travaillant sur des environnements Microsoft. Cependant, par curiosité et envie d’apprendre, je planche volontiers à l’occasion sur Python et c’est aujourd’hui pour un besoin purement personnel que j’ai écrit ces deux scripts.

Pour replacer le contexte, je participe à des compétitions de speedrunning de jeux rétro. Un score est calculé en fonction du temps réalisé pour compléter le jeu en question. Récemment, je me suis demandé si il n’était pas intéressant de valoriser les temps d’exécution très rapides en doublant chaque point attribué sous les 2 heures plutôt que d’avoir un traitement linéaire comme actuellement : on part d’une base de 36000 points (1 par seconde, jusqu’à 10 heures) et l’on soustrait un point pour chaque seconde.

Par exemple, pour une partie complétée en 1 heure, 50 minutes et 28 secondes, on a donc un score de 36000 – 6628 (1 x 3600 + 50 x 60 + 28) soit 29372. Avec ce calcul, on a donc 29372 ; mais avec la nouvelle formule, 36000 – 6628 + 572 soit 29944. Les 572 points proviennent des 572 secondes qui séparent le chronomètre des 2 heures.

J’ai donc développé deux scripts Python permettant de faire le calcul. Le premier script demande à l’utilisateur de saisir à la suite les valeurs. Les boucles try et if permettent de faire les contrôles de robustesse nécessaires :

hrCount = input("Hours: ")
mnCount = input("Minutes: ")
seCount = input("Seconds: ")
try:
	hrCount = int(hrCount)
	mnCount = int(mnCount)
	seCount = int(seCount)
except:
	print("Please input integer values as hours, minutes and seconds.")
	quit()
if seCount >= 60:
	print("Seconds value can't be higher than 60.")
	quit()
if mnCount >= 60:
	print("Minutes value can't be higher than 60.")
	quit()
totalSec = hrCount*3600+mnCount*60+seCount
if totalSec > 36000:
	print("Timer can't be higher than 10 hours.")
	quit()
base = 36000 - totalSec
if totalSec < 7200:
	bonus = 7200 - totalSec
else:
	bonus = 0
sc = base + bonus
print(base)
print(bonus)
print(sc)

Le script affiche à la fin le score de base calculé linéairement, le bonus puis le total.

Le deuxième script effectue le même traitement mais avec une approche différente. J’ai voulu alléger un peu le code et les contrôles de robustesse en utilisant le module argparse. Ce module permet d’ajouter des paramètres en ligne de commande au script : ainsi c’est Python lui-même qui va vérifier le type d’entrée de mes arguments (ici, des integer) et permettre d’afficher une aide.

import argparse
parser = argparse.ArgumentParser(description='Counts all seconds under a 2-hour timer twice.')
parser.add_argument('hrCount', metavar='h', type=int, help='Number of hours')
parser.add_argument('mnCount', metavar='m', type=int, help='Number of minutes')
parser.add_argument('seCount', metavar='s', type=int, help='Number of seconds')
args = parser.parse_args()

if args.seCount >= 60:
	print("Seconds value can't be higher than 60.")
	quit()
if args.mnCount >= 60:
	print("Minutes value can't be higher than 60.")
	quit()
totalSec = args.hrCount*3600+args.mnCount*60+args.seCount
if totalSec > 36000:
	print("Timer can't be higher than 10 hours.")
	quit()
base = 36000 - totalSec
if totalSec < 7200:
	bonus = 7200 - totalSec
else:
	bonus = 0
sc = base + bonus
print(base)
print(bonus)
print(sc)

Voici ce que cela donne en exécution. Le paramètre -h est directement intégré et n’a pas à être codé dans le script.

Les deux scripts sont disponibles dans des versions commentées dans l’archive en lien ci-dessous. J’ai utilisé la version 3.9 de Python sous macOS pour les écrire et les utiliser.

Mes premiers pas sur Python – acte I

Cela faisait quelques temps que je m’intéressais à Python par curiosité et envie d’apprendre quelque chose de nouveau, et cela me permettra d’ajouter une corde à mon arc car à la différence de Powershell, Python fonctionne sur un grand nombre de systèmes d’exploitation. Ce qu’il me manquait pour commencer Python, c’était réellement un besoin, une idée, quelque chose que je pouvais faire avec un autre langage mais pour laquelle il n’y avait aucun enjeu à ce que je prenne plus de temps où qu’elle soit faite dans un autre langage. J’ai finalement trouvé l’inspiration pour un premier bout de code en Python grâce à la table périodique des éléments.

J’ai donc développé mes 2 premiers scripts Python sur ce thème. Le premier demande à l’utilisateur de saisir un symbole de cette table pour lui retourner l’élément correspondant ainsi que son numéro atomique :

import csv
eleminput = input("Enter a symbol:")
with open('PubChemElements_all.csv', newline='') as tablecsv:
	pertbl = csv.reader(tablecsv,delimiter=',')
	for row in pertbl:
		elemread = row[0]
		if elemread == eleminput:
			print(elemread + " is " + row[1] + " and is atomic number " + row[2])

La manière d’ouvrir le fichier CSV est un peu déroutante au départ. En Powershell, la commande Import-CSV permet de le faire en une ligne. Ici, il est nécessaire d’appeler le fichier dans une variable puis d’instancier un objet depuis la classe csv. Le reste est plutôt logique et l’on adresse chaque élément de la ligne parcourue ici nommée row comme un tableau dont l’index commence par 0, comme en .NET.

Le deuxième est un peu plus complexe car il s’agit d’un « jeu » : Python choisit au hasard un élément parmi la liste et expose le nom de cet élément à l’utilisateur qui doit en deviner le symbole :

import csv
import random
randelem = random.randint(1, 118)
with open('PubChemElements_all.csv', newline='') as tablecsv:
	pertbl = csv.reader(tablecsv,delimiter=',')
	for row in pertbl:
		if pertbl.line_num == randelem:
			elemsym = row[0]
			elemname = row[1]
eleminput = input("Please input the symbol for element " + elemname + " : ")
if eleminput == elemsym:
	print("Well done! " + elemsym + " is the symbol for " + elemname)
else:
	print("Sorry! " + elemname + " has " + elemsym + " as symbol")

Voici ce que cela donne :

J’ai donc importé le module random pour générer de manière aléatoire un nombre. Etant donné que mon fichier CSV comporte 118 éléments, je vais chercher un nombre allant de 1 à 118 – il est important de garder à l’esprit que les index commencent à zéro en Python mais ici cela n’a pas d’importance car je cherche à faire concorder cette valeur avec un nombre de lignes parcourues, et ce nombre ne peut pas être égal à zéro – et je parcours ensuite mon fichier CSV en comptant le nombre de lignes jusqu’à arriver au nombre choisi aléatoirement en début de code. Une fois que je tombe sur celui-ci, je stocke dans deux variables le symbole et le nom de l’élément, qui me permettront de faire la vérification de la réponse de l’utilisateur ainsi que l’affichage de l’élément à trouver.

J’ai joué ces scripts sur un environnement macOS Catalina avec Python 3.8. J’ai regroupé les deux scripts ainsi que le fichier CSV d’entrée dans un fichier au format zip téléchargeable directement depuis le miroir du blog.

Mise en place de PlexPy, un outil de supervision de Plex Media Server

PlexPy est un outil de supervision et de monitoring pour un serveur Plex. Suite à un crash de mon serveur Plex aujourd’hui – qui deviennent de plus en plus fréquents (un par semaine…) sans que je comprenne trop pourquoi – je voulais trouver un moyen d’être averti par courriel lorsque le serveur ne répond plus correctement ou lorsque le streaming est impossible. PlexPy fonctionne comme son nom l’indique avec Python et offre une interface Web. Bien qu’il soit plutôt facile à installer, je vais en détailler le processus d’installation et de configuration sur une machine Debian. A noter qu’il n’est pas nécessaire d’installer PlexPy sur le serveur Plex en question.

Tout d’abord, on créé un utilisateur qui exécutera l’application :

adduser plexpy

Je pars du principe que Python et Git sont installés sur la machine.

cd /opt/
git clone https://github.com/JonnyWong16/plexpy.git
cd plexpy
python PlexPy.py

L’idée étant que PlexPy démarre tel un service au lancement du système afin d’éviter d’avoir à laisser la console ouverte ou à passer par screen, il va falloir quelques manipulations supplémentaires ; copier-coller le contenu du fichier .service présent sur le GitHub (ou bien récupérer la version déjà renommée sur mon Google Drive) puis l’enregistrer dans /lib/systemd/system (en le renommant plexpy.service si vous avez copié/collé). Ensuite, faire en sorte que ce service démarre avec le système :

systemctl daemon-reload
systemctl enable plexpy.service

Il démarre ensuite grâce à l’instruction systemctl start plexpy.service. Il ne faudra pas oublier de s’assurer que l’utilisateur plexpy qui a été créé plus tôt soit propriétaire du répertoire de l’application :

chown -R /opt/plexpy

Le fichier .service peut se modifier si jamais l’application n’a pas été installée dans le répertoire par défaut. Pour ma part, étant le seul administrateur de la machine Plex et le seul à y avoir un accès distant, j’ai laissé le fichier de configuration dans son emplacement par défaut.

Une fois l’application exécutée, un navigateur va s’ouvrir sur http://localhost:8181. Etant connecté en SSH, c’est w3m qui s’est ouvert.

Pour plus de facilité, j’ai contrôlé la pré-configuration de l’application via un navigateur installé sur une autre machine du réseau.

L’assistant divisé en plusieurs écrans est plutôt clair et permet de faire une configuration basique de l’outil. Par la suite, j’irai dans la configuration avancée pour modifier les notifications.

Une fois que tout est défini, on arrive sur la page d’accueil, qui présente diverses statistiques et informations par rapport au serveur Plex qui est écouté. Si l’on souhaite monitorer plusieurs serveurs Plex, alors il faut installer PlexPy autant de fois que nécessaire, sachant qu’il est possible de changer le port de l’interface web pour avoir plusieurs instances sur la même machine sans que cela ne pose problème.

Pour configurer ce qui m’intéresse, c’est-à-dire une alerte par courriel qui m’avertit d’un éventuel dysfonctionnement de Plex, il faut aller fouiner dans la configuration avancée, qui mérite de toutes façons le détour car c’est ici qu’on configure le degré d’alerte, le moyen de contact (il est possible de recevoir des notifications par une multitude de biais autre que le courriel, notamment Twitter ou autre outils de communication comme Telegraph…) et qu’on gère toute la partie technique (base de données, taille des logs, etc.).

Le design étant proche de celui de Plex, y retrouver ses petits est aisé. Tout d’abord, je vérifie que ma configuration est bien la bonne.

La section « Notifications » permet de paramétrer ce qui sera envoyé : titre, contenu, etc. Ce qui m’intéresse le plus cependant et là où je vais pouvoir mettre en place ce que je souhaite est dans la section « Notifications Agents ». Je choisis la ligne « E-mail » afin de la paramétrer. Il m’est donc naturellement demandé un serveur SMTP entre autres adresses émettrices et réceptrices.

Puis, une fois la configuration effectuée, un clic sur la cloche pour l’activer ; tout un panel de cases à cocher s’affiche, permettant de choisir exactement le moment où l’on souhaite être averti. J’ai donc choisi d’être averti par courriel lorsque le serveur est inaccessible et lorsqu’il revient en ligne.