Attachment 'TwitterStock_Version7.py'

Download

   1 import logging, dbus, threading, gobject, time, gtk
   2 import urllib
   3 import hippo
   4 import twyt.twitter as twyt
   5 import twyt.data as twyt_data
   6 from bigboard.stock import Stock
   7 from bigboard.slideout import Slideout
   8 import bigboard.accounts_dialog as accounts_dialog
   9 import bigboard.libbig.gutil as gutil
  10 from bigboard.libbig.logutil import log_except
  11 import bigboard.libbig as libbig
  12 from bigboard.libbig.http import AsyncHTTPFetcherWithAuth
  13 from bigboard.big_widgets import PrelightingCanvasBox, CanvasVBox, CanvasHBox, Header, CanvasURLImage
  14 
  15 _logger = logging.getLogger('bigboard.stocks.TwitterStock')
  16 
  17 LOGIN_TO_TWITTER_STRING = "Login to Twitter"
  18 FAILED_TO_LOGIN_STRING = "Failed to login."
  19 
  20 TWITTER_KIND = "twitter"
  21 
  22 TWITTER_STATUS_MAX_LENGTH = 140
  23 
  24 FRIENDS_UPDATES_LIMIT = 3
  25 
  26 def make_update_string(result, details):
  27     string = ""
  28     try:
  29         if details:
  30             utc = time.mktime(time.strptime(
  31                               "%s UTC" % result.created_at, "%a %b %d %H:%M:%S +0000 %Y %Z"))
  32             stamp = time.strftime("%a %b %d %H:%M:%S %Y", time.localtime(utc))
  33 
  34 	    string = "%s (%s via %s)" % (result.text, stamp, result.source)
  35         else:
  36             string = "%s" % (result.text)                      
  37     except AttributeError, e:
  38 	_logger.debug("AttributeError when parsing result %s" % e)
  39 
  40     return string
  41 
  42 class UpdateSlideout(Slideout):
  43     def __init__(self, result):
  44         super(UpdateSlideout, self).__init__()
  45         vbox = CanvasVBox(border_color=0x0000000ff, spacing=4, box_width=320)
  46         self.get_root().append(vbox)
  47         self.__header = Header(topborder=False)
  48         # TODO: for some reason, specifying an additional class with  the color did not set the color
  49         text = hippo.CanvasText(classes='header', color=0x00AABBFF, text=result.user.screen_name, xalign=hippo.ALIGNMENT_START, padding=2)
  50         self.__header.append(text, hippo.PACK_EXPAND)        
  51         vbox.append(self.__header)
  52         
  53         self.id = result.id        
  54 
  55         hbox = CanvasHBox(spacing=4)
  56       
  57         image = CanvasURLImage(result.user.profile_image_url, padding=2) 
  58         status = hippo.CanvasText(text=make_update_string(result, True), xalign=hippo.ALIGNMENT_START,
  59                                   size_mode=hippo.CANVAS_SIZE_WRAP_WORD, padding=2)
  60         hbox.append(image)
  61         hbox.append(status, hippo.PACK_EXPAND) 
  62         vbox.append(hbox)
  63 
  64 class CheckTwitterTask(libbig.polling.Task):
  65     def __init__(self, twitter_stock):
  66         libbig.polling.Task.__init__(self, 1000 * 120, initial_interval=0)
  67         self.__twitter_stock = twitter_stock  
  68         
  69     def do_periodic_task(self):
  70         self.__twitter_stock.do_periodic_updates()
  71 
  72 class TwitterStock(Stock):
  73 
  74     def __init__(self, *args, **kwargs):
  75         Stock.__init__(self, *args, **kwargs)
  76         _logger.debug("Hello Twitter")
  77         self.__box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL)
  78 
  79         self.__login_button = hippo.CanvasButton(text=LOGIN_TO_TWITTER_STRING)
  80         self.__login_button.connect('activated', lambda button: self.__open_login_dialog())
  81         self.__box.append(self.__login_button)
  82   
  83         self.__twitter_status_counter_text = hippo.CanvasText(text=str(TWITTER_STATUS_MAX_LENGTH))
  84    
  85         self.__twitter_status_input = hippo.CanvasEntry()
  86         self.__twitter_status_input.get_property("widget").set_max_length(TWITTER_STATUS_MAX_LENGTH)
  87         self.__twitter_status_input.connect("notify::text", self.__on_status_edited)
  88         self.__twitter_status_input.connect("key-press-event", self.__on_status_submitted)
  89 
  90         self._add_more_button(self.__on_more_button)
  91 
  92         self.__friends_updates_box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL)
  93 
  94         self.__slideout = None
  95         self.__last_slideout_event_time = None  
  96 
  97         self.__twitter = twyt.Twitter()
  98         self.__twitter.set_user_agent("gnome")
  99 
 100         self.__fetcher = AsyncHTTPFetcherWithAuth()
 101 
 102         self.__check_twitter_task = CheckTwitterTask(self)
 103 
 104         # even though the account system can theoretically return multiple Twitter accounts, this stock
 105         # only supports one -- the last one that was added
 106         self.__twitter_account = None
 107         self.__twitter_account_changed_signal_match = None
 108 
 109         try:
 110             onlineaccounts_proxy = dbus.SessionBus().get_object('org.gnome.OnlineAccounts', '/onlineaccounts')
 111         except dbus.DBusException, e:
 112             _logger.error("onlineaccounts DBus service not available, can't get twitter accounts")
 113             return
 114 
 115         all_twitter_account_paths = onlineaccounts_proxy.GetEnabledAccountsWithKinds([TWITTER_KIND])
 116         for a_path in all_twitter_account_paths:
 117             self.__on_account_added(a_path)
 118 
 119         self.__connections = gutil.DisconnectSet()
 120 
 121         id = onlineaccounts_proxy.connect_to_signal('AccountEnabled', self.__on_account_added)
 122         self.__connections.add(onlineaccounts_proxy, id)
 123         id = onlineaccounts_proxy.connect_to_signal('AccountDisabled', self.__on_account_removed)
 124         self.__connections.add(onlineaccounts_proxy, id)
 125 
 126     def _on_delisted(self):
 127         if self.__twitter_account:
 128             self.__on_account_removed(self.__twitter_account.GetObjectPath())
 129         self.__connections.disconnect_all()
 130 
 131     def get_content(self, size):
 132         return self.__box
 133 
 134     @log_except(_logger)
 135     def __on_account_added(self, acct_path):
 136         try:
 137             _logger.debug("acct_path is %s" % acct_path)
 138             acct = dbus.SessionBus().get_object('org.gnome.OnlineAccounts', acct_path)
 139         except dbus.DBusException, e:
 140             _logger.error("onlineaccount for path %s was not found" % acct_path)
 141             return
 142 
 143         if acct.GetKind() != TWITTER_KIND:
 144             return
 145        
 146         _logger.debug("in __on_account_added for %s %s" % (str(acct), acct.GetUsername()))    
 147   
 148         if self.__twitter_account:
 149             self.__on_account_removed(self.__twitter_account.GetObjectPath())
 150        
 151         self.__twitter_account = acct
 152         self.__twitter_account_changed_signal_match = \
 153             self.__twitter_account.connect_to_signal('Changed', self.__on_twitter_account_changed)
 154         self.__on_twitter_account_changed()  
 155     
 156     @log_except(_logger)
 157     def __on_account_removed(self, acct_path):
 158         _logger.debug("in __on_account_removed")
 159         if self.__twitter_account and self.__twitter_account.GetObjectPath() == acct_path: 
 160             _logger.debug("in __on_account_removed for %s", acct_path)   
 161             self.__twitter_account_changed_signal_match.remove()
 162             self.__twitter_account_changed_signal_match = None 
 163             self.__twitter_account = None
 164             self.__check_twitter_task.stop() 
 165             self.__box.remove_all() 
 166             self.__box.append(self.__login_button)
 167 
 168     @log_except(_logger)
 169     def __on_twitter_account_changed(self):
 170         _logger.debug("will change stuff")
 171         username = self.__twitter_account.GetUsername()
 172         password = self.__twitter_account.GetPassword() 
 173         if password != "":
 174             self.__box.remove_all()
 175             checking = hippo.CanvasText(text="Checking credentials for " + username + "...",
 176                                           size_mode=hippo.CANVAS_SIZE_WRAP_WORD, classes="info")
 177             self.__box.append(checking)
 178             self.__fetcher.fetch("http://twitter.com/account/verify_credentials.json", 
 179                                  username, password,
 180                                  lambda url, data: self.__on_twitter_response(url, data, username, password), 
 181                                  lambda url, resp: self.__on_twitter_error(url, resp, username, password),
 182                                  lambda url: self.__on_auth_failed(username, password),
 183                                  data = urllib.urlencode({}))
 184         else:
 185             self.__add_login_button()  
 186 
 187     def __on_status_edited(self, input, param_spec):
 188         self.__twitter_status_counter_text.set_property("text", str(TWITTER_STATUS_MAX_LENGTH - len(self.__twitter_status_input.get_property("text"))))
 189 
 190     def __on_status_submitted(self, entry, event):
 191         if event.key == hippo.KEY_RETURN:
 192             status = self.__twitter_status_input.get_property("text").strip()
 193             if status != "":
 194                 _logger.debug("will send status: %s" % status)
 195                 username = self.__twitter_account.GetUsername()
 196                 password =  self.__twitter_account.GetPassword()
 197                 self.__fetcher.fetch("http://twitter.com/statuses/update.json", 
 198                                  username, password,
 199                                  lambda url, data: self.__on_twitter_status_response(url, data, username, password), 
 200                                  lambda url, resp: self.__on_twitter_error(url, resp, username, password),
 201                                  lambda url: self.__on_auth_failed(username, password),
 202                                  data = urllib.urlencode({"status":status}))
 203 
 204             self.__twitter_status_input.set_property("text", "")
 205 
 206     def __on_twitter_response(self, url, data, username, password):
 207         _logger.debug("Authentication must be good")
 208         if self.__same_credentials(username, password):
 209             self.__box.remove_all()
 210             hello_message = hippo.CanvasText(text="Update status for " + self.__twitter_account.GetUsername() + ":",
 211                                              size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
 212             self.__box.append(hello_message)
 213             self.__box.append(self.__twitter_status_counter_text)
 214             self.__box.append(self.__twitter_status_input)  
 215        
 216             self.__box.append(self.__friends_updates_box)
 217 
 218             loading = hippo.CanvasText(text="Loading friends' updates...",
 219                                           size_mode=hippo.CANVAS_SIZE_WRAP_WORD, classes="info")
 220             self.__friends_updates_box.append(loading)
 221 
 222             self.__check_twitter_task.start()
 223 
 224     def do_periodic_updates(self):
 225         t = threading.Thread(target=self.__get_friends_updates, 
 226                              kwargs={"username": self.__twitter_account.GetUsername(), "password": self.__twitter_account.GetPassword()},
 227                              name="Twitter Updates Request")
 228         t.setDaemon(True)
 229         t.start()
 230 
 231     def __get_friends_updates(self, username, password):
 232         self.__twitter.set_auth(username, password)
 233         answer = self.__twitter.status_friends_timeline()
 234         results = twyt_data.StatusList(answer)
 235         gobject.idle_add(self.__process_friends_updates, results, username, password)
 236 
 237     def __process_friends_updates(self, results, username, password):
 238         if self.__same_credentials(username, password):
 239             self.__friends_updates_box.remove_all()
 240             i = 0
 241             for result in results:
 242                 if i >= FRIENDS_UPDATES_LIMIT: break
 243 
 244                 box = PrelightingCanvasBox(orientation=hippo.ORIENTATION_VERTICAL)
 245 
 246                 box.connect("button-press-event", self.create_update_slideout, result)
 247                 self.__friends_updates_box.append(box)
 248                 name = hippo.CanvasText(text=result.user.screen_name, xalign=hippo.ALIGNMENT_START, classes="friend-name")
 249                 status = hippo.CanvasText(text=make_update_string(result, False), xalign=hippo.ALIGNMENT_START,
 250                                           size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
 251                 box.append(name)
 252                 box.append(status)
 253                 i += 1
 254 
 255                 _logger.debug("%s" % result)
 256         else:
 257             _logger.debug("the credentials changed while we were getting friends updates from Twitter")
 258 
 259     def __on_twitter_status_response(self, url, data, username, password):
 260         _logger.debug("Twitter status update went fine")
 261 
 262     def __on_twitter_error(self, url, resp, username, password):
 263         status = resp and str(resp.status) or "No response"
 264         _logger.debug("There was a Twitter error. Response: %s" % status)
 265         if self.__same_credentials(username, password):
 266             self.__add_login_button(True)
 267 
 268     def __on_auth_failed(self, username, password):
 269         _logger.debug("There was an authentication failure")
 270         if self.__same_credentials(username, password):
 271             self.__add_login_button(True)
 272 
 273     def __same_credentials(self, username, password):
 274         return self.__twitter_account and self.__twitter_account.GetUsername() == username and self.__twitter_account.GetPassword() == password
 275 
 276     def __add_login_button(self, failed_to_login = False):
 277         self.__check_twitter_task.stop() 
 278         self.__box.remove_all()
 279         if failed_to_login:
 280             error = hippo.CanvasText(text=FAILED_TO_LOGIN_STRING, size_mode=hippo.CANVAS_SIZE_WRAP_WORD, classes="error")
 281             self.__box.append(error) 
 282         hello_message = hippo.CanvasText(text="Hello Twitter User " + self.__twitter_account.GetUsername(),
 283                                          size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
 284         self.__box.append(hello_message)                           
 285         self.__box.append(self.__login_button) 
 286  
 287     def __open_login_dialog(self):
 288         accounts_dialog.open_dialog(["twitter"])
 289 
 290     def __on_more_button(self):
 291         if self.__twitter_account:
 292             url = "http://twitter.com/home"
 293         else:
 294             url = "http://twitter.com"
 295         libbig.show_url(url)
 296 
 297     def create_update_slideout(self, widget, hippo_event, result):
 298         if self.__slideout and self.__slideout.id == result.id and self.__last_slideout_event_time == gtk.get_current_event_time():
 299             self.__slideout = None
 300             return 
 301         self.__slideout = UpdateSlideout(result)
 302         def on_slideout_close(s, action_taken):
 303             self.__last_slideout_event_time = gtk.get_current_event_time() 
 304             if action_taken:
 305                 self._panel.action_taken()
 306             s.destroy()
 307             self._set_active(False)
 308         self._set_active(True)
 309         self.__slideout.connect('close', on_slideout_close)
 310         y = widget.get_context().translate_to_screen(widget)[1]
 311         if not self.__slideout.slideout_from(204, y):
 312             self.__slideout.destroy()
 313             self.__slideout = None
 314             self._set_active(False)
 315             return

Attached Files

To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.
  • [get | view] (2021-02-25 09:46:11, 450.6 KB) [[attachment:GUADEC-Presentation.odp]]
  • [get | view] (2021-02-25 09:46:11, 0.6 KB) [[attachment:HippoCanvasExample.txt]]
  • [get | view] (2021-02-25 09:46:11, 0.5 KB) [[attachment:TwitterStock_Version1.py]]
  • [get | view] (2021-02-25 09:46:11, 3.9 KB) [[attachment:TwitterStock_Version2.py]]
  • [get | view] (2021-02-25 09:46:11, 0.4 KB) [[attachment:TwitterStock_Version3.py]]
  • [get | view] (2021-02-25 09:46:11, 0.9 KB) [[attachment:TwitterStock_Version4.py]]
  • [get | view] (2021-02-25 09:46:11, 3.1 KB) [[attachment:TwitterStock_Version5.py]]
  • [get | view] (2021-02-25 09:46:11, 2.4 KB) [[attachment:TwitterStock_Version6.py]]
  • [get | view] (2021-02-25 09:46:11, 14.1 KB) [[attachment:TwitterStock_Version7.py]]
 All files | Selected Files: delete move to page copy to page

You are not allowed to attach a file to this page.