Outils pour utilisateurs

Outils du site


issue108:c_c

A few months ago, I was asked to create a JSON database for integration into an ubuntu phone app. Up until then, I’ve only ever used JSON files, but not created them from real-world data/files. I then set about using Python to list all files that should appear in the app, and creating the relevant JSON data. Since then, I’ve begun a data management and analysis course - where we work with similar JSON files. So for this month’s C&C, I’m going to walk you through how I created a new Python script to create a JSON database of my previous C&C articles. This way, I’ll be able to easily check when I wrote an article, and what the topic was. The Script I’ve posted my copy of the script on Pastebin: http://pastebin.com/eLASuY1T But… I Don’t Have Your Articles! This script isn’t intended to be used as-is. You can easily adjust it to generate lists/databases of files based off filenames. This is what I’ll be focusing on in the article.

Il y a quelques mois, on m'a demandé de créer une base données JSON pour l'intégrer dans une appli pour téléphone Ubuntu. Jusque-là, je n'avais qu'utiliser des fichiers JSON, mais je ne les avais pas créés à partir de données ou de fichiers existants. J'ai alors entrepris d'utiliser Python pour lister tous les fichiers qui devraient apparaître dans l'appli afin de créer les données JSON correspondantes. Depuis ce temps, j'ai commencé à suivre un cours de gestion et d'analyse de données - dans lequel nous travaillons avec des fichiers JSON similaires. Dans le C&C de ce mois-ci, je vais donc vous montrer comment j'ai écrit un nouveau script Python me permettant de créer une base de données de mes précédents articles C&C. De cette façon, il me sera facile de retrouver la date à laquelle j'ai écrit un article ainsi que son sujet.

Le script.

J'ai mis une copie de mon script sur Pastebin : http://pastebin.com/eLASuY1T

Mais… je n'ai pas vos articles !

Le script n'est pas fait pour être utilisé tel quel. Il peut être facilement modifié pour générer une liste/base de données de fichiers à partir de leur nom. C'est ce que je vais traiter dans cet article.

What Will I Need? The scripts are tested in Python 3.5. Older versions of 3 should work without issues - if you’re using Python2, you’ll need to adjust the code according to the error messages you’re given. I’ve linked my version of the script with Drive - the CLI tool for managing Google Drive. If you’re not interested in using it, you can safely ignore/delete the lines 3-6, 26-40, and 107. Otherwise you’ll need: Drive (see Further Reading), and to have imported subprocess.call, and contextlib.contextmanager. Otherwise the rest of the imports are necessary, and standard libraries.

De quoi aurai-je besoin ?

Les scripts ont été testés dans Python 3.5. Des versions 3, plus anciennes, devraient fonctionner sans problème. Si vous utilisez Python 2, vous devrez corriger le code en fonction des messages d'erreur que vous recevrez.

J'ai lié ma version du script avec Drive, l'outil CLI [Ndt : Command Line Interface (interface en ligne de commande)] qui gère Google Drive. Si son utilisation ne vous intéresse pas, vous pouvez tranquillement ignorer ou effacer les lignes 3-6, 26-40 et 107. Sinon, vous devrez disposer de Drive (voir les Lectures complémentaires) et avoir importé subprocess.call et contextlib.contextmanager.

Par ailleurs, les autres importations sont nécessaires ainsi que les bibliothèques standards.

What Do I Need To Change? These changes are mandatory - and need to be adjusted for your system. Optional changes can be found in the next section. You’ll want to edit line 9 - this is the target file (txtFile) for the intermediary list of all articles. You’ll also need to edit line 13 - and adjust the list to contain the paths you want to include in your database. It is expected to be a list, so even if you’re looking at a single path, make sure it’s wrapped in square brackets. If you don’t separate out the filename information with “ - “, then you’ll need to change line 70. What Can I Change? Line 10 indicates the file path to the JSON file. By default it’s fcm-database.json in the current folder. Line 16 contains a dictionary that stores the date of issue 100 (August, 2015). This is important for the calculations in dateFind. The reason it’s not integrated into the function, is so it can be more easily changed. Line 17 contains the empty dictionary which is used per entry. It could be integrated into a method, but I left it in to illustrate how it works (and to ensure that there’s a -1 element to delete).

Que dois-je changer ?

Les changements suivants sont obligatoires et doivent être ajustés à votre système. Les changements optionnels se trouvent dans la section suivante.

Il faudra éditer la ligne 9 : il s'agit du fichier de destination (txtFile) pour la liste temporaire de tous les articles.

Vous devrez également éditer la ligne 13 et modifier la liste pour y indiquer les chemins que vous voulez inclure dans votre base de données. Il s'agira d'une liste ; donc, même si vous n'envisagez qu'un seul chemin, assurez-vous de bien le mettre entre crochets.

Si vous ne séparez pas les informations concernant vos fichiers avec des « - », il faudra modifier la ligne 70.

Que puis-je également changer ?

La ligne 10 donne le chemin du fichier JSON. Il s'agit par défaut de fcm-database.json dans le dossier courant.

La ligne 16 contient un dictionnaire où est stockée la date du numéro 100 (August, 2015 [Ndt : août 2015]). C'est important pour les calculs dans dateFind. On ne l'a pas intégrée dans la fonction pour pouvoir la changer plus facilement.

La ligne 17 contient un dictionnaire vide qui est utilisé pour chaque valeur. Il aurait pu être intégré dans une méthode, mais je l'ai laissé ici pour montrer comment il fonctionne (et m'assurer qu'il y a -1 élément à effacer).

How Does It Work? The first 25 lines are mainly variables that need to be set up for later functions. Most should be self-explanatory, and so I will skip them. Lines 26-40 are explained in the section “Drive Functions”. Function: dateFind This function was created to calculate the month and year that corresponds to the article. This is important for me, as some topics can be outdated - so if a topic was covered in 2012, it may be time for a refresh. It does so by knowing one date (#100 was for August, 2015), and the difference between the currently processed issue, and issue 100. So issue 98 would be a difference of 2, and 107 would be -7. The difference (times 365/12 - roughly one month) is then subtracted from the date of the startPoint (2015-08), and results in a new year and month. The days shift, but since FCM is a monthly release, it’s unimportant (and even deleted entirely with the strftime function). One could also just multiply 32 by the difference - though having that many extra days for some months may cause more problems. It’s by no means perfect - as it’s cumulative, there may be problems with February issues (for example).

Comment cela fonctionne-t-il ?

Les 25 premières lignes sont essentiellement des déclarations de variables qui ont besoin d'être initialisées pour des fonctions ultérieures. La plupart se comprennent d'elles-mêmes ; je ne vais donc pas m'y attarder. Les lignes 26 à 40 sont expliquées dans la partie « Drive Functions » [Ndt : les fonctions Drive].

Fonction : dateFind

Cette fonction a été créée pour calculer le mois et l'année qui correspond à l'article. C'est important pour moi, puisque certains sujets peuvent être démodés ; ainsi, si un sujet a été traité en 2012, il est peut-être temps de le réactualiser. Je le fais en connaissant une date (le numéro 100 en août 2015) et la différence entre le numéro en cours et le numéro 100. Ainsi le numéro 98 me donnera une différence de 2 alors que le numéro 107 sera de -7. La différence (le nombre de fois 365/12 - en gros un mois) est alors soustrait de la date de startPoint (2015-08), et le résultat donne une année et un mois nouveaux. Les jours se décalent, mais comme FCM est une publication mensuelle ça n'a pas d'importance (et c'est même complètement supprimé avec la fonction strftime).

On peut aussi simplement multiplier la différence par 32 - bien qu'avoir autant de jours en plus pour quelques mois peut présenter des problèmes supplémentaires. Ce n'est en aucune façon parfait ; du fait du cumul, il peut y avoir des problèmes avec les numéros de février (par exemple).

Function: createArticleList This function uses os.listdir, and some regexp/searches to ensure that only my articles are accounted for. Since my files are in the format FCM100 - C&C - Title, I simply make sure the filename starts with FCM[0-9]+ (one or more numbers after the FCM). I also make sure it doesn’t contain .desktop (this is important for drive - as every file has a .desktop companion). Once the lists have been added into output, the list isn’t flat (has multiple sublists). That’s what line 54 fixes. Line 55 removes any .odt extension listed (as Google Drive files have no extension, and .odt was what I originally used). Lastly, I use the same sort of replace function to shorten any Command & Conquer into C&C - not strictly necessary, but ensures things are uniform. Line 57 ensures there are no duplicates - as a Python set can contain only unique values. This essentially drops any duplicate filenames. The list is then printed into a temporary file (which is practical for debugging, or if you just want to see quickly how many files there are). The file is then closed, and the function returns True.

Fonction : createArticleList

Cette fonction utilise os.listdir et quelques recherches regexp pour ne tenir compte que de mes articles. Puisque le titre de mes articles a toujours le format FCM100 - C&C - Titre, je m'assure simplement que le nom du fichier commence bien par FCM[0-9]+ (un ou plusieurs nombres après FCM). Je vérifie également qu'il ne contient pas .desktop (c'est important pour Drive puisque chaque fichier est accompagné d'un .desktop). Une fois que les listes ont été ajoutées à output [Ndt : la variable de sortie], la liste n'est pas simple (elle comprend un grand nombre de sous-listes). C'est ce que résout la ligne 54.

La ligne 55 supprime toutes les extensions .odt de la liste (puisque les fichiers de Google Drive n'ont pas d'extension et que j'utilisais .odt au début). Enfin, j'utilise la même fonction de remplacement pour transformer les Command & Conquer en C&C - ce n'est pas absolument nécessaire mais offre une certaine uniformité.

La ligne 57 vérifie qu'il n'y a pas de doublon, puisqu'un ensemble Python ne peut contenir que des valeurs uniques. Cela élimine essentiellement tout nom de fichier dupliqué.

La liste est ensuite écrite dans un fichier temporaire (ce qui est pratique pour trouver les erreurs ou voir rapidement le nombre de fichiers). Le fichier est ensuite fermé et la fonction renvoie True (vrai).

Function: update_database This is the crux of the file. It essentially opens the article-list.txt file, and reads each line of it. For each non-empty line, it then does the following steps: • Split the line on the “ - “ separator (so you get a list like this: [‘FCM100’, ‘C&C’, ‘Title’]) • Removes the FCM part - to get just the issue number. • Creates an empty key/value pair in the entry dict. • Fills the information into the entry dict (if the title is empty - as some of my files were poorly named in the past, it just inserts a string “Unknown”). It also removes any newline characters. • dateFind is used to calculate the estimated date for that issue. • Database.update is used to insert (or update) the information for the current issue. Once the for loop is completed, the file is then closed, the -1 entry of the database (original entryTemplate) is deleted, and the database is returned. Function: write_database This is a quick function that uses json.dumps to simply write the dict to a JSON file. It also indents it nicely. Function: write_csv_database This function uses csv.writer to create a valid CSV file. Line 95 dumps the keys of an entry (specifically, entry 100) into a list. The list is then used to create the CSV header, and also to make sure the order of the values are the same as the headers - so it matches up.

Fonction : update_database

C'est le point d'orgue du fichier. Elle lit essentiellement chacune des lignes du fichier article-list.txt Pour chaque ligne non vide, elle exécute les étapes suivantes : • Découper la ligne selon le séparateur « - » (on obtient ainsi une ligne du genre ['FCM100', 'C&C', 'Title']). • Supprime le libellé FCM pour ne conserver que le numéro. • Crée une entrée, constituée d'une paire clé/valeur vide, dans le dictionnaire. • Complète les informations de cette entrée (si le titre est vide - certains de mes fichiers étaient mal nommés dans le passé -, elle rajoute la chaîne de caractères « Unknown » (inconnu). Elle supprime également d'éventuels caractères de saut de ligne. • dateFind est chargée de calculer une date estimée de ce numéro. • Database.update est utilisée pour ajouter (ou mettre à jour) les informations concernant le numéro courant.

Une fois que la boucle for est terminée, le fichier est refermé, la valeur -1 de la base de données (la valeur originale de entryTemplate) est effacée et la base de données est renvoyée.

Fonction : write_database

C'est une fonction rapide qui utilise json.dumps pour simplement écrire le dictionnaire dans un fichier JSON. Il l'indente également très bien.

Fonction : write_csv_database

Cette fonction utilise csv.writer pour créer un fichier CSV valide. La ligne 95 liste toutes les clés d'une entrée (ici, l'entrée 100). La liste est alors utilisée pour créer l'en-tête du CSV et pour s'assurer que l'ordre des valeurs est le même que celui des en-têtes et qu'ainsi tout correspond.

Function: main This is just a function where I call the rest of the helper functions (and debugged issues). You can just as easily paste this into the if name == “main” area, but the recommendation is to use a main function, to allow easier importing. Drive Functions Function: cd This just recreates the cd function from Bash, but also reverts to the original directory - so that the drive pull command can be executed in the correct directory, without messing up later writes to JSON or CSV files. Originally found on StackOverflow (see Further Reading). Function: update_drive This calls the cd command inside a with (to ensure the directory reverts), and calls drive pull. Why Do Some Functions Return True? This is mainly to indicate that the function completed correctly. It’s also a useful step if you want to support error handling.

Fonction : main

C'est tout simplement un endroit où j'appelle le reste des fonctions d'aide (et les problèmes corrigés). Vous pouvez également coller cela dans la zone ifname ==“main” mais ma recommandation est d'avoir une fonction main permettant des importations plus simples.

Les fonctions Drive

Fonction : cd

Cela recrée simplement une fonction cd à partir d'un Bash, mais renvoie également vers le répertoire (directory) original, de façon à ce que la commande d'extraction du disque puisse être exécutée dans le répertoire correct, sans mélanger les écritures suivantes dans les fichiers JSON ou CSV. On la trouvait initialement dans StackOverflow (voir les lectures complémentaires).

La fonction : update_drive

C'est un appel de la commande cd à l'intérieur d'une boucle with (pour être sûr de pouvoir retrouver le répertoire précédent) et appelle l'extraction du disque.

Pourquoi quelques fonctions renvoient True ?

C'est d'abord pour indiquer que la fonction s'est terminée correctement. C'est également très utile si vous voulez mettre en place un traitement des erreurs.

What Do I Do With The Database? There are a few things you can do. Using something like OpenRefine, you can clean up your database (such as find out which files are lacking titles). Or else you can export it to a CSV and import it into Google Sheets or similar. Lastly, you can open it in something like Python’s Pandas and analyse it however you’d like. Can I Search? You can either open the JSON file and search by hand, open it in some form of data analysis tool, or write a new function to search the nested dictionary structure in Python itself. I hope this article proves interesting to at least a few readers. Or, barring that, gives you some inspiration for new projects of your own. If you have any comments, suggestions or extensions, feel free to email me at lswest34+fcm@gmail.com. Further Reading https://github.com/odeke-em/drive Drive CLI tool. http://stackoverflow.com/questions/431684/how-do-i-cd-in-python/24176022#24176022 Stack Overflow cd command in Linux.

Que fais-je avec la base de données ?

Il y a un certain nombre de choses que vous pouvez faire. En utilisant quelque chose comme OpenRefine vous pouvez nettoyer votre base (rechercher les fichiers qui n'ont pas de titre, par exemple). Ou bien vous pouvez l'exporter au format CSV et la ré-importer dans Google Sheets ou quelque chose du même genre.

Enfin vous pouvez l'ouvrir avec un logiciel tel que Python's Pandas et l'analyser comme vous le souhaitez.

Puis-je faire des recherches ?

Vous pouvez soit ouvrir le fichier JSON et rechercher manuellement, ou l'ouvrir avec un outil quelconque d'analyse de données, ou écrire en langage Python une nouvelle fonction de recherche dans les sous-dictionnaires.

J'espère que cet article présentera quelque intérêt, au moins pour quelques lecteurs. Ou, sinon, vous inspirera de nouveaux projets. Si vous avez des commentaires, suggestions ou idées d'extension, n'hésitez pas à m'envoyer un courriel à mailto:lswest34+fcm@gmail.com.

Lectures complémentaires

https://github.com/odeke-em/drive Les outils du CLI de Drive.

http://stackoverflow.com/questions/431684/how-do-i-cd-in-python/24176022#24176022 La commande cd de Linux dans StackOverflow.

issue108/c_c.txt · Dernière modification : 2016/05/09 12:05 de auntiee