root/protocols/protocol_launchpadstaging_web.py

Revision 80, 9.8 kB (checked in by ploum@…, 3 months ago)

gros commit tout pourri

Line 
1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3import gtk
4import gobject
5import urllib
6from bug import *
7from mechanize import Browser
8from BeautifulSoup import BeautifulSoup
9# You have to change this if you have another storage
10# from auth_file import user
11
12#baseurl = "https://staging.launchpad.net/"
13#baseurl = "https://launchpad.net/"
14
15class protocol:
16
17#constructor, common to all protocol file
18        def __init__(self,manager) :
19                #the user object contains login and password
20                self.user = manager
21               
22        def url(self):
23                zeurl = self.user.url
24                if zeurl.endswith("/") :
25                        return zeurl
26                else :
27                        return "%s/" %zeurl
28                                       
29        def login(self) :
30                return self.user.login
31               
32        def password(self) :
33                return self.user.password
34
35# LAUNCHPAD protocol using the web : suboptimal
36
37# Available informations for authentification are :
38# -  user.login()  -> string that contains the login
39# -  user.password() -> string that contains the password
40
41
42### Launchpad only functions ####
43
44        def htmlify(self, string) :
45                s1 = string.strip().replace(" "," ")
46                s2 = s1.strip().replace(">",">")
47                s3 = s2.strip().replace("&lt;","<")
48                return s3
49               
50       
51################INFORMATION ABOUT THE BTS ################################
52
53        # Send back the name of the protocol as a simple string
54       
55        @staticmethod
56        def Name():
57                return "launchpadstaging_web"
58
59        # send the name of the bts this protocol will talk too.
60        # this is intended for future multi-protocol support
61        # and will enable bookmarks sharing between differents protocols
62        # that speaks to the same bts
63        def btsName(self):
64                return "launchpad"
65
66################BUG RETRIEVING IN THE BTS #################################
67       
68        # Functions related to bugs in your BTS
69        # Functions return an object "bug" (see bug.py)
70        #
71        # You have to implement the following functions :
72        # - retrieveBug(int)
73        # - getBugUrl(int)
74
75        # Retrieve bug take an int, the bug number, and construct a bug object
76        # this bug object is then returned
77        def retrieveBug(self,nbr):
78                zebug = bug(nbr)
79                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
80                # this is what you want to modify to support your own protocol
81                zeurl="%sbugs/%s" %(self.url(),str(nbr))
82                f= urllib.urlopen(zeurl)
83                # ici on a le corps principal
84                # faudrait faire un extract pour soulager la mémoire
85                soup = BeautifulSoup(f).findAll(id="maincontent")[0]
86                title = soup.findAll('h1')[0].contents[0]
87                # no bug of this number here
88                if title == "Page not found" :
89                        # a negative number means that the bug doesn't exist
90                        zebug.setNbr(-1)
91                else :
92                        tbody = soup.findAll('tbody')[0].findAll('td')
93                        product = tbody[0].a.contents[0]
94                        status = tbody[1].contents[0]
95                        importance = tbody[2].contents[0]
96                        string_tmp = tbody[3].a
97                        # Here we look if the bug is assigned to someone or not
98                        if string_tmp != None :
99                                assigned = string_tmp.contents[2].strip()
100                        else :
101                                #Assigned to nobody
102                                assigned = "Nobody yet"
103                        #need to prettify content
104                        content=''
105                        p = soup.findAll('div')[3].findAll('div')[1].findAll('p', recursive=0)
106                        #p = soup.findAll('div', recursive=0)[1].div.findAll('p', recursive=0)
107                        for i in p:
108                                #print i
109                                #print "--------"
110                                for j in i.contents:
111                                        #essai pour avoir les urls
112                                        #print j
113                                        jj = j.string
114                                        #jj=j
115                                        #print jj
116                                        # we get rid of <br /> tags
117                                        if jj != None and jj != "<br />" :
118                                                # here we remove manually all &nbsp
119                                                newj = self.htmlify(jj)
120                                                #newj = jj
121                                                #newj = jj.strip().replace("&nbsp;"," ")
122                                                content = "%s %s"%(content,newj)
123                                        else :
124                                                content = "%s\n"%(content)
125                                content = "%s\n\n"%content
126
127                        zebug.setTitle(title)
128                        zebug.setStatus(status)
129                        zebug.setPackage(product)
130                        zebug.setImportance(importance)
131                        zebug.setDescription(content)
132                        zebug.setAssignee(assigned)
133                        zebug.setCommentable(self.__isCommentable(nbr))
134
135                        array_com = soup.findAll('div', recursive=0)[1].findAll('div','boardComment')
136                        j = 0
137                        com_counter = 0
138                        for i in array_com :
139                                details = i.contents[1].findAll('a')
140                                title = details[1].contents[0].strip()
141                                poster = details[1].contents[0]
142                                body = i.contents[3].findAll('p')
143                                com_counter += 1
144                                content=""
145                                for z in body :
146                                        phrase = ''
147                                        for ligne in z.contents :
148                                                if ligne.string != None :
149                                                        phrase += ligne.string.strip()
150                                        content += self.htmlify(phrase)
151                                        content += "\n\n"
152                                newcom = comment(com_counter,content,poster,title,"00/00/00")
153                                zebug.addComment(newcom)
154
155                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
156                return zebug   
157
158        # getBugUrl takes a int, the bug number, and return the URL of
159        # the bug in order to open it in a browser
160        def getBugUrl(self,nbr) :
161                bugnbr = str(nbr)
162                return "%sbugs/%s" %(self.url(),bugnbr)
163
164
165
166################SEARCH IN THE BTS #################################
167
168        # The functions Search will perform a search
169        # in the BTS
170        # All results matching the search are added to a
171        # gtk.ListStore with attributes in the following orders :
172        # gobject.TYPE_INT : number of the bug
173        # gobject.TYPE_STRING : package
174        # gobject.TYPE_STRING : title of the bug
175        # gobject.TYPE_STRING : importance
176        # gobject.TYPE_STRING : status
177        #
178        # You have to implement the following search methods for your BTS :
179        # - genericSearch(string)
180        # - packageSearch(string,string)
181        # - advancedSearch
182        # - packageExist(string) (return a boolean, not a listStore)
183
184
185        #private function to create a new result gtk.ListStore
186        def __newResult(self) :
187                return gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING)
188       
189        # content is an array of bug numbers
190        def __populateResult(self, results, content, typeofsearch, arg_package):
191                parse = content.body.findAll('tbody')
192                # if no results
193                if len(parse) == 0 :
194                        return results
195                else :
196                        table = parse[1].findAll('tr')
197                        for tr in table :
198                                row = tr.findAll('td')
199                                nbr = int(row[1].contents[0])
200                                title = row[2].a.contents[0]
201                                if typeofsearch == 2 :
202                                        package = row[3].contents[0]
203                                        importance = row[4].contents[0]
204                                        status = row[5].contents[0]
205                                elif typeofsearch == 3 :
206                                        package = arg_package
207                                        importance = row[3].contents[0]
208                                        status = row[4].contents[0]
209                                else :
210                                        package = ''
211                                        importance = ''
212                                        status = ''
213                                results.insert_before(None, [nbr, package, title, importance, status ])
214                        return results
215
216
217        # genericSearch on a given string search_str.
218        def genericSearch(self, search_str) :
219                #creating an empty result first
220                results= self.__newResult()
221                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
222                # this is what you want to modify to support your own protocol
223                #inserting dummies bugs
224                plus = search_str.replace(" ","+")
225                zeurl="%sdistros/ubuntu/+bugs?field.searchtext=%s" %(self.url(),plus)
226                f= urllib.urlopen(zeurl)
227                content=BeautifulSoup(f)
228                return self.__populateResult(results, content, 2, None)
229       
230               
231                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
232                #returning the results
233                #return results
234
235        # search on a given string search_str but only in bugs of
236        # a given package
237        def packageSearch(self, package, search_str):
238                #creating an empty result first
239                results= self.__newResult()
240                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241                # this is what you want to modify to support your own protocol
242                #inserting dummies bugs
243                # your package search must support a Null search_str
244                if search_str != None :
245                        plus = search_str.replace(" ","+")
246                        zeurl="%sdistros/ubuntu/+source/%s/+bugs?field.searchtext=%s" %(self.url(),package,plus)
247                else :
248                        zeurl="%sdistros/ubuntu/+source/%s/+bugs" %(self.url(),package)
249                f= urllib.urlopen(zeurl)
250                content=BeautifulSoup(f)
251                return self.__populateResult(results, content, 3, package)
252               
253                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
254                #returning the results
255                #return results
256
257       
258        # Return true is the package exist in the BTS, false if not             
259        def packageExist(self, package):
260                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261                # this is what you want to modify to support your own protocol
262                zeurl="%sproducts/%s" %(self.url(),package)
263                f= urllib.urlopen(zeurl)
264                #ugly launchpad hack !
265                title=BeautifulSoup(f).html.head.title.contents[0]
266                return title != "Error: Page not found"
267                #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 
268
269        #Return the different available status in the bts as a list
270        # this is used to build the advanced search interface
271        def availableStatus(self):
272                return ['Unconfirmed', 'Needs info', 'Rejected', 'Confirmed', 'In Progress', 'Fix Committed', 'Fix Released']
273
274        #Return the different available importante in the bts as a list
275        # this is used to build the advanced search interface
276        def availableImportance(self):
277                return ['Untriaged', 'Wishlist', 'Low', 'Medium', 'High', 'Critical']
278       
279################MODIFYING A BUG #################################
280
281        # The functions that will allow us to modify a bug
282        #
283        # You have to implement the following search modify for your BTS :
284        # - postComment(string,string,string)
285
286        #private function that log the user into Launchpad
287        # return a mechanize Browser object
288        def __login(self):
289                urllog="%s+login" %self.url()
290                br = Browser()
291                br.set_handle_robots(False)
292                br.open(urllog)
293                br.select_form(name="login")
294                br["loginpage_email"]=self.login()
295                br["loginpage_password"]=self.password()
296                response = br.submit()
297                return br
298               
299        def __isform_comment(self,htmlform) :
300                if htmlform.action.find("+addcomment") != -1 :
301                        return True
302                else :
303                        return False
304                       
305        def __isCommentable(self,bugnbr) :
306                urlcom="%sbugs/%s" %(self.url(),bugnbr)
307                br = self.__login()
308                br.open(urlcom)
309                try :
310                        br.select_form(predicate=self.__isform_comment)
311                        return True
312                except :
313                        return False
314
315        def postComment(self,bugnbr,title,comment) :
316                urlcom="%sbugs/%s" %(self.url(),bugnbr)
317                br = self.__login()
318                br.open(urlcom)
319                br.select_form(predicate=self.__isform_comment)
320                # TODO : set the title
321                br["field.comment"]= comment
322                br.submit()
323               
324
325
326       
327
328                       
Note: See TracBrowser for help on using the browser.