Notes et transcriptions du cours “Concevez un site avec Flask” disponible sur la plateforme Openclassrooms
Vous avez vu, dans les chapitres précédents, comment créer une application Flask, la configurer et intégrer des fonctionnalités intéressantes telles que l'authentification par Facebook.
Dans ce chapitre vous ajouterez la dernière fonctionnalité manquante : la génération d'une image à partager sur Facebook.
Cette image contiendra le prénom de la personne suivi de la description trouvée préalablement.
Dans sa documentation, Facebook conseille que les images à partager aient les dimensions suivantes : 1 200 x 630 pixels.
L'image basique sera donc un rectangle de 1200 x 630 pixels coloré en bleu turquoise (une des couleurs du template).
Nous allons utiliser la librairie Pillow (Python Imaging Library) pour générer l'image.
Commencez par l'installer en utilisant PIP :
pip3 install Pillow
Créez une nouvelle classe dans utils.py.
from random import choice as random_choice from fbapp.models import Content, Gender from PIL import Image ... class OpenGraphImage: def __init__(self, first_name, description): background = self.base() def base(self): img = Image.new('RGB', (1200, 630), '#18BC9C') return img
Pour tester le code de génération de l'image, on va utiliser la méthode show()
dans la méthode d'initialisation, commenter la partie du script qui concerne les modèles puis créer une instance de la classe OpenGraphImage():
from PIL import Image #def find_content(aGender): # # get all contents for a specific gender aGender giving by parameter # contents_for_a_gender = Content.query.filter(Content.gender == Gender[aGender]).all() # # return random_choice(contents_for_a_gender) class OpenGraphImage: def __init__(self, first_name, description): background = self.base() background.show() def base(self): img = Image.new('RGB', (1200, 630), '#18BC9C') return img aDesc = """ Toi, tu sais comment utiliser la console ! Jamais à court d'idées pour réaliser ton objectif, tu es déterminé-e et persévérant-e. Tes amis disent d'ailleurs volontiers que tu as du caractère et que tu ne te laisses pas marcher sur les pieds. Un peu hacker sur les bords, tu aimes trouver des solutions à tout problème. N'aurais-tu pas un petit problème d'autorité ? ;-) """ OpenGraphImage('Toby', aDesc)
Exécuter le script :
$ python3 ./fbapp/utils.py
La seconde étape consiste à écrire deux éléments sur l'image de base : le prénom et la description.
Créez une nouvelle méthode : print_on_image()
qui prend plusieurs arguments : l'image de base sur laquelle écrire, le texte, la taille de police et la position dans l'image de base.
Créez une instance, pour le prénom, dont la taille de police est égale à 70px.
La hauteur représente la coordonnée, en ordonnée, à partir de laquelle le logiciel commence à tracer des lettres. Indiquez “50” afin de commencer à écrire à 50px du bord supérieur.
#from random import choice as random_choice #from fbapp.models import Content, Gender import os from PIL import Image, ImageFont, ImageDraw #def find_content(aGender): # # get all contents for a specific gender aGender giving by parameter # contents_for_a_gender = Content.query.filter(Content.gender == Gender[aGender]).all() # # return random_choice(contents_for_a_gender) class OpenGraphImage: def __init__(self, first_name, description): background = self.base() self.print_on_img(background, first_name.capitalize(), 70, 50) background.show() def base(self): img = Image.new('RGB', (1200, 630), '#18BC9C') return img def print_on_img(self, img, text, size, height): # On commence par charger la police utilisée. font = ImageFont.truetype(os.path.join('fbapp', 'static', 'fonts', 'Arcon-Regular.otf'), size) # Création d'une nouvelle instance draw = ImageDraw.Draw(img) # textsize renvoie la largeur et la hauteur en pixel # d'une chaine de caractères donnée. w = font.size h = draw.textlength(text, font) # Calcul de la position pour que le texte soit centré # et non pas aligné à gauche. position = ((img.width - w) / 2, height) # Ajout du texte à l'image. draw.text(position, text, (255, 255, 255), font=font) aDesc = """ Toi, tu sais comment utiliser la console ! Jamais à court d'idées pour réaliser ton objectif, tu es déterminé-e et persévérant-e. Tes amis disent d'ailleurs volontiers que tu as du caractère et que tu ne te laisses pas marcher sur les pieds. Un peu hacker sur les bords, tu aimes trouver des solutions à tout problème. N'aurais-tu pas un petit problème d'autorité ? ;-) """ OpenGraphImage('Toby', aDesc)
Dans le code ci-dessus nous utilisons deux nouveaux modules de la librairie :
ImageFont
qui permet d'utiliser une police de caractère personnalisée ;ImageDraw
de dessiner sur une image.Écrire la description demande un peu plus de réflexion. Avez-vous trouvé pourquoi ?
Si vous exécutez le script en passant en paramètres une phrase assez longue, Python renverra une erreur. En effet, vous essayez d'écrire hors de l'image !
Il faut donc :
Voici notre solution :
from random import choice as random_choice #from fbapp.models import Content, Gender import os from textwrap import wrap as twrap from PIL import Image, ImageFont, ImageDraw #def find_content(aGender): # # get all contents for a specific gender aGender giving by parameter # contents_for_a_gender = Content.query.filter(Content.gender == Gender[aGender]).all() # # return random_choice(contents_for_a_gender) class OpenGraphImage: def __init__(self, first_name, description): background = self.base() self.print_on_img(background, first_name.capitalize(), 70, 50) # retournes plusieurs chaines sans couper au milieu des mots sentences = twrap(description, width=40) # current_h : hauteur à laquelle commencer à écrire # pad : hauteur interligne en pixels current_h, pad = 180, 10 for sentence in sentences: w, h = self.print_on_img(background, sentence, 40, current_h) # incrementer la hauteur avant d'écrire une nouvelle ligne current_h += 50 background.show() def base(self): img = Image.new('RGB', (1200, 630), '#18BC9C') return img def print_on_img(self, img, text, size, height): # On commence par charger la police utilisée. font = ImageFont.truetype(os.path.join('fbapp', 'static', 'fonts', 'Arcon-Regular.otf'), size) # Création d'une nouvelle instance draw = ImageDraw.Draw(img) # textsize renvoie la largeur et la hauteur en pixel # d'une chaine de caractères donnée. w = font.size h = draw.textlength(text, font) # Calcul de la position pour que le texte soit centré # et non pas aligné à gauche. position = ((img.width - w) / 2, height) # Ajout du texte à l'image. draw.text(position, text, (255, 255, 255), font=font) # On retourne la largeur et la hauteur return(w, h) aDesc = """ Toi, tu sais comment utiliser la console ! Jamais à court d'idées pour réaliser ton objectif, tu es déterminé-e et persévérant-e. Tes amis disent d'ailleurs volontiers que tu as du caractère et que tu ne te laisses pas marcher sur les pieds. Un peu hacker sur les bords, tu aimes trouver des solutions à tout problème. N'aurais-tu pas un petit problème d'autorité ? ;-) """ OpenGraphImage('Toby', aDesc)
L'image est créée mais comment l'enregistrer ? Utilisez la méthode save()
qui prend en paramètres le chemin vers le dossier où enregistrer l'image.
Créez une nouvelle méthode protégée, _path
, qui renverra ce chemin et ajoutez une nouvelle méthode qui renverra le nom de l'image uniquement _location
from random import choice as random_choice from fbapp.models import Content, Gender import os from textwrap import wrap as twrap from PIL import Image, ImageFont, ImageDraw def find_content(aGender): # get all contents for a specific gender aGender giving by parameter contents_for_a_gender = Content.query.filter(Content.gender == Gender[aGender]).all() return random_choice(contents_for_a_gender) class OpenGraphImage: def __init__(self, uid, first_name, description): self.location = self._location(uid) background = self.base() self.print_on_img(background, first_name.capitalize(), 70, 50) # retournes plusieurs chaines sans couper au milieu des mots sentences = twrap(description, width=40) # current_h : hauteur à laquelle commencer à écrire # pad : hauteur interligne en pixels current_h, pad = 180, 10 for sentence in sentences: w, h = self.print_on_img(background, sentence, 40, current_h) # incrementer la hauteur avant d'écrire une nouvelle ligne current_h += 50 background.save(self._path(uid)) def base(self): img = Image.new('RGB', (1200, 630), '#18BC9C') return img def print_on_img(self, img, text, size, height): # On commence par charger la police utilisée. font = ImageFont.truetype(os.path.join('fbapp', 'static', 'fonts', 'Arcon-Regular.otf'), size) # Création d'une nouvelle instance draw = ImageDraw.Draw(img) # textsize renvoie la largeur et la hauteur en pixel # d'une chaine de caractères donnée. w = font.size h = draw.textlength(text, font) # Calcul de la position pour que le texte soit centré # et non pas aligné à gauche. position = ((img.width - w) / 2, height) # Ajout du texte à l'image. draw.text(position, text, (255, 255, 255), font=font) # On retourne la largeur et la hauteur return(w, h) def _path(self, uid): # Le nom de l'image est l'identifiant Facebook de la personne. # Cela nous garantit de ne pas avoir de doublon. return os.path.join('fbapp', 'static', 'tmp', '{}.jpg'.format(uid)) def _location(self, uid): return 'tmp/{}.jpg'.format(uid)
from flask import Flask, render_template, url_for, request app = Flask(__name__) # Config options - Make sure you created a 'config.py' file. # To get one variable, tape app.config['MY_VARIABLE'] app.config.from_object('config') from .utils import find_content, OpenGraphImage ... @app.route('/result/') def result(): req_gender = request.args.get('gender') req_username = request.args.get('first_name') uid = request.args.get('id') profile_pic = 'https://graph.facebook.com/' + uid + '/picture?type=large' aDescription = find_content(req_gender).description img = OpenGraphImage(uid, req_username, aDescription).location og_url = url_for('index', img=img, _external=True) return render_template('result.html', username=req_username, photo=profile_pic, description=aDescription, og_url=og_url) ...
Lancez le serveur et allez sur l'URL suivante :
http://localhost:5000/result/?first_name=C%C3%A9line&id=1933433063608371&gender=female
Elle devrait se charger normalement.
Ouvrez l'inspecteur et regardez le contenu du head : vous devriez voir que la balise méta og:url contient désormais votre nouvelle image !
Copiez l'URL de la balise et collez-la dans la barre de navigation. Elle devrait ressembler à celle-ci :
http://localhost:5000/index/?img=tmp%2F1933433063608371.jpg
Là encore, regardez le contenu du head : la balise méta og:image devrait maintenant contenir votre toute nouvelle photo de couverture ! L'URL devrait ressembler à la suivante :
http://localhost:5000/static/tmp/1933433063608371.jpg
D'ailleurs, vous ne trouvez pas que tester tout ceci devient un peu contraignant ? Moi, si. J'ai bien envie de tester le parcours utilisateur afin de ne plus avoir à le faire à la main.
En résumé
Dans ce chapitre, vous avez vu comment :
os
de Python pour manipuler des cheminsrender_template()
de la vue pour la passer au template