Coverage for src/botadi/mokadi/mokadi_action_emotion.py: 34%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Defines an action for Mokadi.
5"""
6import datetime
7import os
8from PIL import Image, ImageDraw
9from .mokadi_action import MokadiAction
10from .mokadi_info import MokadiInfo
11from .mokadi_exceptions import MokadiException
12from .cognitive_services_helper import call_api_emotions
13from .mokadi_picture import take_picture
16class MokadiActionEmotion(MokadiAction):
17 """
18 Action. Emotion. Takes a picture and analyses it.
19 """
21 def __init__(self, subkey, folder, fLOG=None):
22 """
23 Constructor.
25 @param subkey subscription key
26 @param folder where to save the picture (must exist)
27 @param fLOG logging function
28 """
29 MokadiAction.__init__(self, fLOG=fLOG)
30 self._subkey = subkey
31 self._folder = folder
33 if not os.path.exists(folder):
34 raise FileNotFoundError(folder)
36 def can_do(self, interpreted, message):
37 """
38 Tells if the class can process the message.
40 @param interpreted interpreted message
41 @param message message
42 @return true if the class can process the message
43 """
44 if len(interpreted) < 2:
45 return False
46 for word in interpreted:
47 if word[1] == ":emotion:":
48 return True
49 return False
51 def get_picture_name(self):
52 """
53 return a picture name
54 """
55 dt = datetime.datetime.now()
56 name = "camera-%04d-%02d-%02dT%02d-%02d-%02d" % (dt.year, dt.month, dt.day,
57 dt.hour, dt.minute, dt.second)
58 final = os.path.join(self._folder, name + ".png")
59 i = 0
60 while os.path.exists(final):
61 i += 1
62 final = os.path.join(self._folder, "%s.%d.png" % (name, i))
63 return final
65 def process_interpreted_message(self, interpretation, message):
66 """
67 Processes the interpreted message.
69 @param interpretation interpretation
70 @param message original message
71 @return iterator on Info
72 """
73 filename = self.get_picture_name()
74 self.fLOG(
75 "[MokadiActionEmotion.process_interpreted_message] create ", filename)
76 take_picture(filename)
77 if not os.path.exists(filename):
78 yield MokadiInfo("error", "", "Aucune photo n'a pas pu être prise. Je ne peux pas en dire plus.")
79 done = False
80 res = call_api_emotions(self._subkey, filename)
81 self.fLOG("[MokadiActionEmotion.process_interpreted_message] ", res)
82 if len(res) == 0 or res.get('statusCode', 200) == 404:
83 yield MokadiInfo("error", "", "Aucun résultat. Veuillez recommencer.")
84 done = True
85 elif "error" in res:
86 yield MokadiInfo("error", "", res.get("message", "no message"))
87 done = True
88 else:
89 img = Image.open(filename)
90 draw = ImageDraw.Draw(img)
91 new_filename = os.path.splitext(filename)[0] + ".emotion.png"
92 # [{'faceRectangle': {'height': 198, 'left': 268, 'top': 191, 'width': 198},
93 done = True
94 if len(res) > 1:
95 yield MokadiInfo("ok", "Vous êtes plusieurs...")
96 done = False
98 for i, el in enumerate(res):
99 score, emotion = self.analyse_emotion(el["scores"])
100 rect = el["faceRectangle"]
101 self.fLOG(
102 "[MokadiActionEmotion.process_interpreted_message] ", el)
103 draw.rectangle([rect["left"], rect["top"], rect["left"] + rect["width"], rect["top"] + rect["height"]],
104 outline=(255, 255, 0))
105 if i == 0:
106 numero = "Le premier"
107 else:
108 numero = "Le suivant"
109 if score == 0:
110 yield MokadiInfo("ok", numero + " " + "Je ne sais pas.")
111 elif score < 0.5:
112 yield MokadiInfo("ok", "Dur dur. Dans le doute, " + numero + " " + emotion)
113 elif score < 0.8:
114 yield MokadiInfo("ok", "J'hésite. " + numero + " " + emotion)
115 else:
116 yield MokadiInfo("ok", numero + " " + emotion)
118 else:
119 for el in res:
120 score, emotion = self.analyse_emotion(el["scores"])
121 rect = el["faceRectangle"]
122 self.fLOG(
123 "[MokadiActionEmotion.process_interpreted_message] ", el)
124 draw.rectangle([rect["left"], rect["top"], rect["left"] + rect["width"], rect["top"] + rect["height"]],
125 outline=(255, 255, 0))
126 if score == 0:
127 yield MokadiInfo("ok", "Je ne sais pas.")
128 elif score < 0.5:
129 yield MokadiInfo("ok", "Dur dur. Dans le doute, tu " + emotion)
130 elif score < 0.8:
131 yield MokadiInfo("ok", "J'hésite. Tu " + emotion)
132 else:
133 yield MokadiInfo("ok", "Tu " + emotion)
135 img.save(new_filename)
136 yield MokadiInfo("ok", image=new_filename)
138 if not done:
139 raise MokadiException(
140 "Unable to interpret '{0}'\n{1}".format(interpretation, message))
142 def analyse_emotion(self, scores):
143 """
144 Returns the emotion as text.
146 @param scores dictionary
147 @return string
149 Example for *scores*:
151 ::
153 'scores': {'anger': 0.000243422386, 'contempt': 0.00207486819,
154 'disgust': 2.390567e-05, 'fear': 2.512976e-07,
155 'happiness': 3.5321933e-05, 'neutral': 0.9925101,
156 'sadness': 0.00510106329, 'surprise': 1.10992869e-05}}]
158 """
159 traduction = {'anger': 'est en colère',
160 'contempt': 'est content',
161 'disgust': "est dégoûté",
162 'fear': "a peur",
163 'happiness': 'est heureux',
164 'neutral': 'est difficile à lire',
165 'sadness': 'est triste',
166 'surprise': 'est surpris'}
167 strong = [traduction[k] for k, v in scores.items() if v > 0.8]
168 weak = [traduction[k] for k, v in scores.items() if v > 0.5]
169 if len(strong) > 0:
170 return 0.8, " et ".join(strong)
171 elif len(weak) > 0:
172 return 0.5, " et ".join(weak)
173 else:
174 tri = [(v, k) for k, v in scores.items()]
175 ans = max(tri)
176 return ans[0], traduction[ans[1]]