changeset 106:138d82f64b6f

plugin CS: friends parsing
author Goffi <goffi@goffi.org>
date Sat, 26 Jun 2010 15:33:16 +0800
parents d2630fba8dfd
children 5ae370c71803
files frontends/wix/xmlui.py plugins/plugin_misc_cs.py sat.tac tools/memory.py tools/xml_tools.py
diffstat 5 files changed, 135 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/wix/xmlui.py	Wed Jun 23 17:26:21 2010 +0800
+++ b/frontends/wix/xmlui.py	Sat Jun 26 15:33:16 2010 +0800
@@ -62,6 +62,7 @@
                 error(message)
                 raise Exception(message)
             _proportion = 0
+            id = elem.getAttribute("id")
             name = elem.getAttribute("name")
             type = elem.getAttribute("type")
             value = elem.getAttribute("value") if elem.hasAttribute('value') else u''
@@ -88,7 +89,10 @@
                 self.ctl_list.append({'name':name, 'type':type, 'control':ctrl})
                 _proportion = 1
             elif type=="button":
-                pass
+                callback_id = elem.getAttribute("callback_id")
+                ctrl = wx.Button(parent, -1, value)
+                ctrl.param_id = callback_id
+                parent.Bind(wx.EVT_BUTTON, self.onButtonClicked, ctrl)
             else:
                 error(_("FIXME FIXME FIXME: type [%s] is not implemented") % type)  #FIXME !
                 raise NotImplementedError
@@ -174,6 +178,16 @@
         self.sizer.Add(panel, 1, flag=wx.EXPAND)
         cat_dom.unlink()
 
+    ###events
+
+    def onButtonClicked(self, event):
+        """Called when a button is pushed"""
+        callback_id = event.GetEventObject().param_id
+        data = {"callback_id":callback_id}
+        id = self.host.bridge.launchAction("button", data)
+        self.host.current_action_ids.add(id)
+        event.Skip()
+
     def onFormSubmitted(self, event):
         """Called when submit button is clicked"""
         debug(_("Submitting form"))
--- a/plugins/plugin_misc_cs.py	Wed Jun 23 17:26:21 2010 +0800
+++ b/plugins/plugin_misc_cs.py	Sat Jun 26 15:33:16 2010 +0800
@@ -33,11 +33,11 @@
 from zope.interface import implements
 
 from wokkel import disco, iwokkel, data_form
-from tools.xml_tools import dataForm2xml
+from tools.xml_tools import XMLUI
 import urllib
 
 from BeautifulSoup import BeautifulSoup
-
+import re
 
 
 PLUGIN_INFO = {
@@ -73,14 +73,13 @@
         host.memory.importParams(CS_Plugin.params)
         #menu
         host.importMenu(_("Plugin"), "CouchSurfing", self.menuSelected, help_string = _("Launch CoushSurfing mangement interface"))
-        self.data={} #TODO: delete cookies/data after a while
+        self.data=self.host.memory.getPrivate('plugin_cs_data') or {} #TODO: delete cookies/data after a while
 
     def erroCB(self, e, id):
         """Called when something is going wrong when contacting CS website"""
         message_data={"reason": "connection error", "message":_(u"Impossible to contact CS website, please check your login/password, connection or try again later")}
         self.host.bridge.actionResult("ERROR", id, message_data)
 
-
     def menuSelected(self, id, profile):
         """Called when the couchsurfing menu item is selected"""
         login = self.host.memory.getParamA("Login", "CouchSurfing", profile_key=profile)
@@ -92,36 +91,113 @@
 
         post_data = urllib.urlencode({'auth_login[un]':login,'auth_login[pw]':password,'auth_login[action]':'Login...'}) 
         
-        self.data[profile] = {'cookies':{}}
+        if not self.data.has_key(profile):
+            self.data[profile] = {'cookies':{}}
+        else:
+            self.data[profile]['cookies'] = {}
 
-        def connectionCB(html):
-            print 'Response received'
-            soup = self.data[profile]['soup'] = BeautifulSoup(html)
-            user_nick = soup.find('a','item_link',href='/home.html').contents[0]
-            user_name = soup.html.head.title.string.split(' - ')[1]
-            unread_messages = int(soup.find('div','item_bubble').a.string)
-            form_xml = """
-            <form>
-                <elem type='text' value='Welcome %(name)s, you have %(nb_message)i unread messages'/>
-            </form>
-            """ % {'name':user_name, 'nb_message':unread_messages}
-            self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":form_xml})
 
 
 
-            #d = getPage('http://www.couchsurfing.org/messages.html?message_status=inbox', agent=AGENT, cookies=self.session_cookies[profile])
-       
+   
         #tmp
         f = open('/home/goffi/tmp/CS_principale.html','r')
         html = f.read()
-        connectionCB(html)
+        self.__connectionCB(html, id, profile)
 
         """d = getPage('http://www.couchsurfing.org/login.html', method='POST', postdata=post_data, headers={'Content-Type':'application/x-www-form-urlencoded'} , agent=AGENT, cookies=self.data[profile]['cookies'])
-        d.addCallback(connectionCB)
+        d.addCallback(self.__connectionCB, id, profile)
         d.addErrback(self.erroCB, id)"""
 
 
-        #self.host.bridge.actionResult("SUPPRESS", id, {})
+    #self.host.bridge.actionResult("SUPPRESS", id, {})
+
+
+#pages parsing callbacks
+    def savePage(self, name, html):
+        f = open ('/home/goffi/tmp/CS_'+name+'.html','w')
+        f.write(html)
+        f.close()
+        print "page [%s] sauvee" % name
+        #pdb.set_trace()
+
+    def __connectionCB(self, html, id, profile):
+        print 'Response received'
+        self.savePage('principale',html)
+        soup = BeautifulSoup(html)
+        self.data[profile]['user_nick'] = soup.find('a','item_link',href='/home.html').contents[0]
+        self.data[profile]['user_name'] = soup.html.head.title.string.split(' - ')[1]
+        #unread messages
+        try:
+            self.data[profile]['unread_messages'] = int(soup.find(lambda tag: tag.name=='div' and ('class','item_bubble') in tag.attrs and tag.find('a', href="/messages.html?message_status=inbox")).find(text=True))
+        except:
+            self.data[profile]['unread_messages'] = 0
+        #unread couchrequest messages
+        try:
+            self.data[profile]['unread_CR_messages'] = int(soup.find(lambda tag: tag.name=='div' and ('class','item_bubble') in tag.attrs and tag.find('a', href="/couchmanager")).find(text=True))
+        except:
+            self.data[profile]['unread_CR_messages'] = 0
+
+        #if we have already the list of friend, no need to make new requests
+        if not self.data[profile].has_key('friends'):
+            self.data[profile]['friends'] = {}
+            """f = open('/home/goffi/tmp/CS_friends.html','r')
+            html = f.read()
+            self.__friendsPageCB(html, id, profile)"""
+            d = getPage('http://www.couchsurfing.org/connections.html?type=myfriends&show=10000', agent=AGENT, cookies=self.data[profile]['cookies'])
+            d.addCallback(self.__friendsPageCB, id=id, profile=profile)
+            d.addErrback(self.erroCB, id)
+        else:
+            self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":self.__buildUI(self.data[profile])})
+
+    def __buildUI(self, data):
+        """Build the XML UI of the plugin
+        @param data: data store for the profile"""
+        user_nick = data['user_nick']
+        user_name = data['user_name']
+        unread_mess = data['unread_messages']
+        unread_CR_mess = data['unread_CR_messages']
+        friends_list = data['friends'].keys()
+        friends_list.sort()
+        interface = XMLUI('window','tabs')
+        interface.addCategory(_("Messages"), "vertical")
+        interface.addText(_("G'day %(name)s, you have %(nb_message)i unread message%(plural_mess)s and %(unread_CR_mess)s unread couch request message%(plural_CR)s") % {'name':user_name, 'nb_message':unread_mess, 'plural_mess':'s' if unread_mess>1 else '', 'unread_CR_mess': unread_CR_mess, 'plural_CR':'s' if unread_CR_mess>1 else ''})
+        interface.addList(friends_list, 'friends')
+        interface.addCategory(_("Events"), "vertical")
+        interface.addCategory(_("Couch search"), "vertical")
+        return interface.toXml()
+
+    def __meetingPageCB(self, html):
+        """Called when the meeting page has been received"""
+
+    def __friendsPageCB(self, html, id, profile):
+        """Called when the friends list page has been received"""
+        self.savePage('friends',html)
+        soup = BeautifulSoup(html.replace('"formtable width="400','"formtable" width="400"'))
+        friends = self.data[profile]['friends']
+        for _tr in soup.findAll('tr', {'class':re.compile("^msgRow*")}): #we parse the row with friends infos
+            _nobr = _tr.find('nobr')  #contain the friend name
+            friend_name = unicode(_nobr.string)
+            friend_link = u'http://www.couchsurfing.org'+_nobr.parent['href']
+            regex_href = re.compile(r'/connections\.html\?id=([^&]+)')
+            a_tag = _tr.find('a',href=regex_href)
+            friend_id = regex_href.search(unicode(a_tag)).groups()[0]
+
+            debug(_("CS friend found: %(friend_name)s (id: %(friend_id)s, link: %(friend_link)s)") % {'friend_name':friend_name, 'friend_id':friend_id, 'friend_link':friend_link})
+            friends[friend_name] = {'link':friend_link,'id':friend_id} 
+        a = soup.find('td','barmiddle next').a  #is there several pages ?
+        if a:
+            #yes, we parse the next page
+            d = getPage('http://www.couchsurfing.org/'+str(a['href']), agent=AGENT, cookies=self.data[profile]['cookies'])
+            d.addCallback(self.__friendsPageCB, id=id, profile=profile)
+            d.addErrback(self.erroCB, id)
+        else:
+            #no, we show the result
+            self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":self.__buildUI(self.data[profile])})
+            #and save the data
+            self.host.memory.setPrivate('plugin_cs_data', self.data)
+            
 
 
 
+
--- a/sat.tac	Wed Jun 23 17:26:21 2010 +0800
+++ b/sat.tac	Sat Jun 26 15:33:16 2010 +0800
@@ -25,6 +25,8 @@
     'local_dir' : '~/.sat'
 }
 
+import gettext
+gettext.install('sat', "i18n", unicode=True)
 
 from twisted.application import internet, service
 from twisted.internet import glib2reactor, protocol, task
@@ -50,9 +52,6 @@
 from tools.xml_tools import tupleList2dataForm
 from glob import glob
 
-import gettext
-gettext.install('sat', "i18n", unicode=True)
-
 try:
     from twisted.words.protocols.xmlstream import XMPPHandler
 except ImportError:
@@ -568,7 +567,7 @@
         """
         if type=="button":
             try:
-                cb_name = self.memory.getParamA(data["name"], data["category"], "callback")
+                cb_name = data['callback_id']
             except KeyError:
                 error (_("Incomplete data"))
                 return ""
--- a/tools/memory.py	Wed Jun 23 17:26:21 2010 +0800
+++ b/tools/memory.py	Sat Jun 26 15:33:16 2010 +0800
@@ -23,7 +23,7 @@
 
 import os.path
 import time
-import pickle
+import cPickle as pickle
 from xml.dom import minidom
 from logging import debug, info, error
 import pdb
@@ -46,13 +46,13 @@
     <general>
     </general>
     <individual>
-        <category name="Connection">
+        <category name='"""+_("Connection")+"""'>
             <param name="JabberID" value="goffi@necton2.int/TestScript" type="string" />
             <param name="Password" value="toto" type="password" />
             <param name="Server" value="necton2.int" type="string" />
-            <param name="NewAccount" value="Register new account" type="button" callback="registerNewAccount"/>
+            <param name="NewAccount" value='"""+_("Register new account")+"""' type="button" callback_id="registerNewAccount"/>
         </category>
-        <category name="Misc">
+        <category name='"""+_("Misc")+"""'>
             <param name="Watched" value="test@Jabber.goffi.int" type="string" />
         </category>
     </individual>
--- a/tools/xml_tools.py	Wed Jun 23 17:26:21 2010 +0800
+++ b/tools/xml_tools.py	Sat Jun 26 15:33:16 2010 +0800
@@ -88,9 +88,12 @@
                 assert(False)
             type = param.getAttribute('type')
             value = param.getAttribute('value') or None
-            callback = param.getAttribute('callback') or None
-            param_ui.addLabel(name)
-            param_ui.addElement(name=name, type=type, value=value, callback=callback)
+            callback_id = param.getAttribute('callback_id') or None
+            if type == "button":
+                param_ui.addEmpty()
+            else:
+                param_ui.addLabel(name)
+            param_ui.addElement(name=name, type=type, value=value, callback_id=callback_id)
 
     return param_ui.toXml()
 
@@ -200,15 +203,18 @@
         if value:
             elem.setAttribute('value', value)
 
-    def addButton(self, callback, name):
+    def addButton(self, callback_id, name, value):
         """Add a button
         @param callback: callback which will be called if button is pressed
-        @param name: name shown on the button"""
+        @param name: name
+        @param value: label of the button"""
         elem = self.__createElem('button', name, self.currentLayout)
-        elem.setAttribute('callback', callback)
+        elem.setAttribute('callback_id', callback_id)
+        if value:
+            elem.setAttribute('value', value)
 
     
-    def addElement(self, type, name = None, content = None, value = None, options = None, callback = None):
+    def addElement(self, type, name = None, content = None, value = None, options = None, callback_id = None):
         """Convenience method to add element, the params correspond to the ones in addSomething methods"""
         if type == 'empty':
             self.addEmpty(name)
@@ -224,8 +230,8 @@
         elif type == 'list':
             self.addList(options, name, value)
         elif type == 'button':
-            assert(callback and name)
-            self.addButton(callback, name)
+            assert(callback_id and value)
+            self.addButton(callback_id, name, value)
 
     def addOptions(self, options, parent):
         """Add options to a multi-values element (e.g. list)