Quelques optimisations pour réduire les requêtes chez Google.
[progfou.git] / pygtranslator / pygtranslator.py
1 #!/usr/bin/env python2.5
2 # -*- coding: utf-8 -*-
3 """
4 PygTranslator - pas de doc cette fois-ci... :-P
5
6 Copyright : Agence universitaire de la Francophonie
7 Licence : GNU General Public Licence, version 2
8 Auteur : Jean Christophe André
9 Date de création : 28 septembre 2009
10 """
11
12 from threading import Thread
13 from Queue import Queue
14 import xmpp
15
16 import pygtk
17 pygtk.require('2.0')
18 import gtk, gobject
19
20 def DEBUG(message):
21 print "\x1b[31;1mDEBUG\x1b[m:", message
22 return
23
24 class XmppTranslator(Thread):
25
26 def __init__(self, jid, password):
27 Thread.__init__(self)
28 self.setDaemon(True)
29 self._quit = False
30
31 self._input_queue = Queue(0)
32 self._output_queue = Queue(0)
33 self._last_text_to_translate = {}
34
35 self._jid = xmpp.JID(jid)
36 self._password = password
37 if self._jid.getResource() == '':
38 self._jid.setResource('translator')
39
40 self._client = xmpp.Client(self._jid.getDomain(), debug=[])
41
42 def __del__(self):
43 if self._client and not self._client.isConnected():
44 return
45 self._client.sendPresence(typ='unavailable')
46 self._client.disconnect()
47
48 def _connect(self):
49 try:
50 if not self._client.connect():
51 raise IOError
52 except IOError:
53 raise IOError, "can't connect to '%s'" % self._jid.getDomain()
54 self._client.RegisterHandler('message', self._messageHandler)
55
56 auth = self._client.auth(self._jid.getNode(), self._password,
57 self._jid.getResource())
58 if not auth:
59 raise IOError, "unable to authenticate '%s'" % self._jid.getNode()
60 self._client.sendInitPresence(requestRoster=0)
61
62 def _messageHandler(self, client, message):
63 if message.getType() == 'error':
64 return
65 translation_path = message.getFrom().getNode()
66 text = message.getBody()
67 self._output_queue.put( (translation_path, text) )
68 raise xmpp.NodeProcessed
69
70 def _process_input_queue(self):
71 if self._input_queue.empty():
72 return
73 # process pending translation job
74 translation_path, text = self._input_queue.get()
75 jid = '%s@bot.talk.google.com' % translation_path
76 message = xmpp.Message(to=jid, typ='chat', body=text)
77 self._client.send(message)
78 DEBUG("sent [%s] to '%s'." % (text, jid))
79 self._input_queue.task_done()
80
81 def run(self):
82 self._connect()
83 self._client.Process(1)
84 while not self._quit:
85 self._process_input_queue()
86 self._client.Process(1)
87
88 def request_translation(self, translation_path, text):
89 text = text.strip().replace('\r\n','\n')
90 # skip consecutive identical requests
91 if text == self._last_text_to_translate.get(translation_path):
92 return
93 self._last_text_to_translate[translation_path] = text
94 # remove any pending translation job
95 while not self._input_queue.empty():
96 self._input_queue.get()
97 DEBUG("removed a pending translation job")
98 self._input_queue.task_done()
99 # put a new translation job
100 self._input_queue.put( (translation_path, text) )
101
102 def translation_result(self):
103 if self._output_queue.empty():
104 return None
105 translation_path, text = self._output_queue.get()
106 return translation_path, text
107
108 def translation_done(self):
109 self._output_queue.task_done()
110
111 class GtkTranslator(object):
112
113 def __init__(self, translator):
114 self._translator = translator
115
116 self._textbuf = {}
117
118 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
119 self.window.set_title(u"PygTranslator")
120 self.window.set_border_width(0)
121 self.window.connect("destroy", lambda w: gtk.main_quit())
122 self.window.connect("delete_event", self._delete_event)
123
124 vbox = gtk.VBox(True, 0)
125 vbox.show()
126
127 textview = gtk.TextView()
128 textview.set_size_request(400, 100)
129 textview.set_wrap_mode(gtk.WRAP_WORD)
130 textview.show()
131 scroll = gtk.ScrolledWindow()
132 scroll.add(textview)
133 scroll.show()
134 frame = gtk.Frame(u"Texte en français")
135 frame.connect("key_release_event", self._text_change, 'fr')
136 frame.connect("button_release_event", self._text_change, 'fr')
137 frame.add(scroll)
138 frame.show()
139 vbox.pack_start(frame, True, True, 0)
140 self._textbuf['fr'] = textview.get_buffer()
141
142 textview = gtk.TextView()
143 textview.set_size_request(400, 100)
144 textview.set_wrap_mode(gtk.WRAP_WORD)
145 textview.show()
146 scroll = gtk.ScrolledWindow()
147 scroll.add(textview)
148 scroll.show()
149 frame = gtk.Frame(u"Text in English")
150 frame.connect("key_release_event", self._text_change, 'en')
151 frame.connect("button_release_event", self._text_change, 'en')
152 frame.add(scroll)
153 frame.show()
154 vbox.pack_start(frame, True, True, 0)
155 self._textbuf['en'] = textview.get_buffer()
156
157 self.window.add(vbox)
158 self.window.show()
159
160 def _delete_event(self, widget, event, data=None):
161 DEBUG("caught delete event")
162 gtk.main_quit()
163 return False
164
165 def _text_change(self, widget, event, lang):
166 start, end = self._textbuf[lang].get_bounds()
167 text = self._textbuf[lang].get_text(start, end)
168 other_lang = (set(self._textbuf.keys()) - set((lang,))).pop()
169 translation_path = lang + '2' + other_lang
170 self._translator.request_translation(translation_path, text)
171
172 def _process_translation_result(self):
173 result = {}
174 # FIXME: to transform into an iterator...
175 while True:
176 r = self._translator.translation_result()
177 if r is None:
178 break
179 translation_path, text = r
180 lang = translation_path[3:]
181 if lang in self._textbuf:
182 result[lang] = text
183 else:
184 DEBUG("unknown destination language '%s'" % lang)
185 self._translator.translation_done()
186 for lang, text in result.items():
187 self._textbuf[lang].set_text(text)
188 gobject.timeout_add(300, self._process_translation_result)
189
190 def run(self):
191 DEBUG("running graphic interface")
192 gobject.timeout_add(300, self._process_translation_result)
193 gtk.gdk.threads_init()
194 gtk.main()
195
196 if __name__ == "__main__":
197 import sys
198 if len(sys.argv) != 3:
199 print "Usage: %s <jid> <password>" % sys.argv[0]
200 sys.exit(1)
201
202 translator = XmppTranslator(sys.argv[1], sys.argv[2])
203 translator.start()
204
205 pygtranslator = GtkTranslator(translator)
206 try:
207 pygtranslator.run()
208 except KeyboardInterrupt:
209 pass
210
211 translator._quit = True
212 translator.join()
213
214 sys.exit(0)
215