Attachments you submit will be routed for moderation. If you have an account, please log in first.

Ticket #1439: 0001-Option-1-not-working-Upload-logs-button-displays.patch

File 0001-Option-1-not-working-Upload-logs-button-displays.patch, 22.9 KB (added by wadeb, 4 years ago)

Option 1 for Log activity (doesn't work): Bring up control panel when the Upload logs button is clicked.

  • (a) a/logcollect.py vs. (b) /dev/null

    From ba906b7e3746bf0734b78d081868bd0f81828e30 Mon Sep 17 00:00:00 2001
    From: Wade Brainerd <wadetb@gmail.com>
    Date: Sun, 27 Sep 2009 21:14:18 -0400
    Subject: [PATCH] Option 1 (not working): Upload logs button displays Report a problem control panel.
    
    ---
     logcollect.py |  554 ---------------------------------------------------------
     logviewer.py  |   70 ++------
     2 files changed, 11 insertions(+), 613 deletions(-)
     delete mode 100644 logcollect.py
    
    diff --git a/logcollect.py b/logcollect.py
    deleted file mode 100644
    index 8713048..0000000
    a b  
    1 # Copyright (C) 2007, Pascal Scheffers <pascal@scheffers.net>  
    2 # 
    3 # Permission is hereby granted, free of charge, to any person 
    4 # obtaining a copy of this software and associated documentation 
    5 # files (the "Software"), to deal in the Software without 
    6 # restriction, including without limitation the rights to use, 
    7 # copy, modify, merge, publish, distribute, sublicense, and/or sell 
    8 # copies of the Software, and to permit persons to whom the 
    9 # Software is furnished to do so, subject to the following 
    10 # conditions: 
    11 # 
    12 # The above copyright notice and this permission notice shall be 
    13 # included in all copies or substantial portions of the Software. 
    14 # 
    15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
    16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
    17 # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
    18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 
    19 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
    20 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
    21 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
    22 # OTHER DEALINGS IN THE SOFTWARE. 
    23 # 
    24 # log-collect for OLPC 
    25 # 
    26 # Compile a report containing: 
    27 #  * Basic system information: 
    28 #  ** Serial number 
    29 #  ** Battery type 
    30 #  ** Build number 
    31 #  ** Uptime 
    32 #  ** disk free space 
    33 #  ** ... 
    34 #  * Installed packages list 
    35 #  * All relevant log files (all of them, at first) 
    36 #  
    37 # The report is output as a tarfile 
    38 # 
    39 # This file has two modes: 
    40 # 1. It is a stand-alone python script, when invoked as 'log-collect' 
    41 # 2. It is a python module. 
    42  
    43 import os 
    44 import zipfile 
    45 import glob 
    46 import sys 
    47 import time 
    48  
    49 # The next couple are used by LogSend 
    50 import httplib 
    51 import mimetypes 
    52 import urlparse 
    53  
    54 class MachineProperties: 
    55     """Various machine properties in easy to access chunks. 
    56     """ 
    57  
    58     def __read_file(self, filename): 
    59         """Read the entire contents of a file and return it as a string""" 
    60  
    61         data = '' 
    62  
    63         f = open(filename) 
    64         try: 
    65             data = f.read() 
    66         finally: 
    67             f.close() 
    68  
    69         return data 
    70  
    71     def olpc_build(self): 
    72         """Buildnumber, from /etc/issue""" 
    73         # Is there a better place to get the build number? 
    74         if not os.path.exists('/etc/issue'): 
    75             return '#/etc/issue not found' 
    76  
    77         # Needed, because we want to default to the first non blank line: 
    78         first_line = ''  
    79  
    80         for line in self.__read_file('/etc/issue').splitlines(): 
    81             if line.lower().find('olpc build') > -1: 
    82                 return line                 
    83             if first_line == '': 
    84                 first_line=line 
    85  
    86         return first_line 
    87  
    88     def uptime(self): 
    89         for line in self.__read_file('/proc/uptime').splitlines(): 
    90             if line != '': 
    91                 return line             
    92         return '' 
    93  
    94     def loadavg(self): 
    95         for line in self.__read_file('/proc/loadavg').splitlines(): 
    96             if line != '': 
    97                 return line             
    98         return '' 
    99  
    100     def kernel_version(self): 
    101         for line in self.__read_file('/proc/version').splitlines(): 
    102             if line != '': 
    103                 return line             
    104         return '' 
    105  
    106     def memfree(self): 
    107         line = '' 
    108  
    109         for line in self.__read_file('/proc/meminfo').splitlines(): 
    110             if line.find('MemFree:') > -1: 
    111                 return line[8:].strip() 
    112  
    113     def _mfg_data(self, item): 
    114         """Return mfg data item from /ofw/mfg-data/""" 
    115          
    116         if not os.path.exists('/ofw/mfg-data/'+item): 
    117             return '' 
    118          
    119         v = self.__read_file('/ofw/mfg-data/'+item) 
    120         # Remove trailing 0 character, if any: 
    121         if v != '' and ord(v[len(v)-1]) == 0: 
    122             v = v[:len(v)-1] 
    123          
    124         return v 
    125              
    126     def laptop_serial_number(self): 
    127         return self._mfg_data('SN') 
    128  
    129     def laptop_motherboard_number(self): 
    130         return self._mfg_data('B#') 
    131  
    132     def laptop_board_revision(self): 
    133         s = self._mfg_data('SG')[0:1] 
    134         if s == '': 
    135             return '' 
    136              
    137         return '%02X' % ord(self._mfg_data('SG')[0:1]) 
    138          
    139  
    140     def laptop_uuid(self): 
    141         return self._mfg_data('U#') 
    142  
    143     def laptop_keyboard(self): 
    144         kb = self._mfg_data('KM') + '-' 
    145         kb += self._mfg_data('KL') + '-' 
    146         kb += self._mfg_data('KV') 
    147         return kb 
    148  
    149     def laptop_wireless_mac(self): 
    150         return self._mfg_data('WM') 
    151  
    152     def laptop_bios_version(self): 
    153         return self._mfg_data('BV') 
    154  
    155     def laptop_country(self): 
    156         return self._mfg_data('LA') 
    157  
    158     def laptop_localization(self): 
    159         return self._mfg_data('LO') 
    160      
    161     def _battery_info(self, item): 
    162         """ from  /sys/class/power-supply/olpc-battery/ """ 
    163         root = '/sys/class/power_supply/olpc-battery/' 
    164         if not os.path.exists(root+item): 
    165             return '' 
    166          
    167         return self.__read_file(root+item).strip() 
    168  
    169     def battery_serial_number(self): 
    170         return self._battery_info('serial_number') 
    171      
    172     def battery_capacity(self): 
    173         return self._battery_info('capacity') + ' ' + \ 
    174                     self._battery_info('capacity_level') 
    175  
    176     def battery_info(self): 
    177         #Should be just: 
    178         #return self._battery_info('uevent') 
    179          
    180         #But because of a bug in the kernel, that has trash, lets filter: 
    181         bi = ''         
    182         for line in self._battery_info('uevent').splitlines(): 
    183             if line.startswith('POWER_'): 
    184                 bi += line + '\n' 
    185          
    186         return bi 
    187         
    188     def disksize(self, path): 
    189         return os.statvfs(path).f_bsize * os.statvfs(path).f_blocks 
    190      
    191     def diskfree(self, path): 
    192         return os.statvfs(path).f_bsize * os.statvfs(path).f_bavail 
    193          
    194     def _read_popen(self, cmd): 
    195         p = os.popen(cmd) 
    196         s = '' 
    197         try: 
    198             for line in p: 
    199                 s += line  
    200         finally: 
    201             p.close()         
    202          
    203         return s 
    204      
    205     def ifconfig(self):         
    206         return self._read_popen('/sbin/ifconfig') 
    207                 
    208     def route_n(self):         
    209         return self._read_popen('/sbin/route -n') 
    210      
    211     def df_a(self): 
    212         return self._read_popen('/bin/df -a') 
    213    
    214     def ps_auxfwww(self): 
    215         return self._read_popen('/bin/ps auxfwww') 
    216      
    217     def usr_bin_free(self): 
    218         return self._read_popen('/usr/bin/free') 
    219  
    220     def top(self): 
    221         return self._read_popen('/usr/bin/top -bn2') 
    222          
    223     def installed_activities(self):         
    224         s = ''         
    225         for path in glob.glob('/usr/share/activities/*.activity'): 
    226             s += os.path.basename(path) + '\n' 
    227  
    228         for path in glob.glob('/home/olpc/Activities/*'): 
    229             s += '~' + os.path.basename(path) + '\n' 
    230              
    231         return s 
    232          
    233          
    234  
    235 class LogCollect: 
    236     """Collect XO logfiles and machine metadata for reporting to OLPC 
    237  
    238     """ 
    239     def __init__(self): 
    240         self._mp = MachineProperties() 
    241  
    242     def write_logs(self, archive='', logbytes=15360): 
    243         """Write a zipfile containing the tails of the logfiles and machine info of the XO 
    244          
    245         Arguments: 
    246             archive -   Specifies the location where to store the data 
    247                         defaults to /dev/shm/logs-<xo-serial>.zip 
    248                          
    249             logbytes -  Maximum number of bytes to read from each log file. 
    250                         0 means complete logfiles, not just the tail 
    251                         -1 means only save machine info, no logs 
    252         """ 
    253         #This function is crammed with try...except to make sure we get as much 
    254         #data as possible, if anything fails. 
    255          
    256         if archive=='': 
    257             archive = '/dev/shm/logs.zip' 
    258             try: 
    259                 #With serial number is more convenient, but might fail for some 
    260                 #Unknown reason... 
    261                 archive = '/dev/shm/logs-%s.zip' % self._mp.laptop_serial_number() 
    262             except Exception: 
    263                 pass 
    264              
    265         z = zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED) 
    266          
    267         try:             
    268             try:  
    269                 z.writestr('info.txt', self.laptop_info()) 
    270             except Exception, e: 
    271                 z.writestr('info.txt', 
    272                            "logcollect: could not add info.txt: %s" % e) 
    273              
    274             if logbytes > -1:             
    275                 # Include some log files from /var/log. 
    276                 for fn in ['dmesg', 'messages', 'cron', 'maillog','rpmpkgs', 
    277                            'Xorg.0.log', 'spooler']: 
    278                     try: 
    279                         if os.access('/var/log/'+fn, os.F_OK): 
    280                             if logbytes == 0: 
    281                                 z.write('/var/log/'+fn, 'var-log/'+fn) 
    282                             else: 
    283                                 z.writestr('var-log/'+fn, 
    284                                            self.file_tail('/var/log/'+fn, logbytes)) 
    285                     except Exception, e: 
    286                         z.writestr('var-log/'+fn, 
    287                                    "logcollect: could not add %s: %s" % (fn, e)) 
    288                          
    289                 # Include all current ones from sugar/logs 
    290                 for path in glob.glob('/home/olpc/.sugar/default/logs/*.log'): 
    291                     try: 
    292                         if os.access(path, os.F_OK): 
    293                             if logbytes == 0: 
    294                                 z.write(path, 'sugar-logs/'+os.path.basename(path)) 
    295                             else: 
    296                                 z.writestr('sugar-logs/'+os.path.basename(path), 
    297                                            self.file_tail(path, logbytes)) 
    298                     except Exception, e: 
    299                         z.writestr('sugar-logs/'+fn, 
    300                                    "logcollect: could not add %s: %s" % (fn, e)) 
    301                 try:                  
    302                     z.write('/etc/resolv.conf') 
    303                 except Exception, e: 
    304                     z.writestr('/etc/resolv.conf', 
    305                                "logcollect: could not add resolv.conf: %s" % e) 
    306                      
    307         except Exception, e: 
    308             print 'While creating zip archive: %s' % e             
    309          
    310         z.close()         
    311          
    312         return archive 
    313  
    314     def file_tail(self, filename, tailbytes): 
    315         """Read the tail (end) of the file 
    316          
    317         Arguments: 
    318             filename    The name of the file to read 
    319             tailbytes   Number of bytes to include or 0 for entire file 
    320         """ 
    321  
    322         data = '' 
    323  
    324         f = open(filename) 
    325         try: 
    326             fsize = os.stat(filename).st_size 
    327              
    328             if tailbytes > 0 and fsize > tailbytes: 
    329                 f.seek(-tailbytes, 2) 
    330                  
    331             data = f.read() 
    332         finally: 
    333             f.close() 
    334  
    335         return data         
    336                
    337  
    338     def make_report(self, target='stdout'): 
    339         """Create the report 
    340  
    341         Arguments: 
    342             target - where to save the logs, a path or stdout             
    343  
    344         """ 
    345  
    346         li = self.laptop_info() 
    347         for k, v in li.iteritems(): 
    348             print k + ': ' +v 
    349              
    350         print self._mp.battery_info() 
    351  
    352     def laptop_info(self): 
    353         """Return a string with laptop serial, battery type, build, memory info, etc.""" 
    354  
    355         s = ''         
    356         try: 
    357             # Do not include UUID! 
    358             s += 'laptop-info-version: 1.0\n' 
    359             s += 'clock: %f\n' % time.clock() 
    360             s += 'date: %s' % time.strftime("%a, %d %b %Y %H:%M:%S +0000", 
    361                                             time.gmtime()) 
    362             s += 'memfree: %s\n' % self._mp.memfree() 
    363             s += 'disksize: %s MB\n' % ( self._mp.disksize('/') / (1024*1024) )  
    364             s += 'diskfree: %s MB\n' % ( self._mp.diskfree('/') / (1024*1024) )  
    365             s += 'olpc_build: %s\n' % self._mp.olpc_build() 
    366             s += 'kernel_version: %s\n' % self._mp.kernel_version() 
    367             s += 'uptime: %s\n' % self._mp.uptime() 
    368             s += 'loadavg: %s\n' % self._mp.loadavg()         
    369             s += 'serial-number: %s\n' % self._mp.laptop_serial_number() 
    370             s += 'motherboard-number: %s\n' % self._mp.laptop_motherboard_number() 
    371             s += 'board-revision: %s\n' %  self._mp.laptop_board_revision() 
    372             s += 'keyboard: %s\n' %  self._mp.laptop_keyboard() 
    373             s += 'wireless_mac: %s\n' %  self._mp.laptop_wireless_mac() 
    374             s += 'firmware: %s\n' %  self._mp.laptop_bios_version() 
    375             s += 'country: %s\n' % self._mp.laptop_country() 
    376             s += 'localization: %s\n' % self._mp.laptop_localization() 
    377                  
    378             s += self._mp.battery_info() 
    379              
    380             s += "\n[/sbin/ifconfig]\n%s\n" % self._mp.ifconfig() 
    381             s += "\n[/sbin/route -n]\n%s\n" % self._mp.route_n() 
    382              
    383             s += '\n[Installed Activities]\n%s\n' % self._mp.installed_activities() 
    384      
    385             s += '\n[df -a]\n%s\n' % self._mp.df_a() 
    386             s += '\n[ps auxwww]\n%s\n' % self._mp.ps_auxfwww() 
    387             s += '\n[free]\n%s\n' % self._mp.usr_bin_free() 
    388             s += '\n[top -bn2]\n%s\n' % self._mp.top() 
    389         except Exception, e: 
    390             s += '\nException while building info:\n%s\n' % e 
    391          
    392         return s 
    393  
    394 class LogSend: 
    395      
    396     # post_multipart and encode_multipart_formdata have been taken from 
    397     #  http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 
    398     def post_multipart(self, host, selector, fields, files): 
    399         """ 
    400         Post fields and files to an http host as multipart/form-data. 
    401         fields is a sequence of (name, value) elements for regular form fields. 
    402         files is a sequence of (name, filename, value) elements for data to be uploaded as files 
    403         Return the server's response page.         
    404         """ 
    405         content_type, body = self.encode_multipart_formdata(fields, files) 
    406         h = httplib.HTTP(host) 
    407         h.putrequest('POST', selector) 
    408         h.putheader('content-type', content_type) 
    409         h.putheader('content-length', str(len(body))) 
    410         h.putheader('Host', host) 
    411         h.endheaders() 
    412         h.send(body) 
    413         errcode, errmsg, headers = h.getreply() 
    414         return h.file.read() 
    415      
    416     def encode_multipart_formdata(self, fields, files): 
    417         """ 
    418         fields is a sequence of (name, value) elements for regular form fields. 
    419         files is a sequence of (name, filename, value) elements for data to be uploaded as files 
    420         Return (content_type, body) ready for httplib.HTTP instance 
    421         """ 
    422         BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' 
    423         CRLF = '\r\n' 
    424         L = [] 
    425         for (key, value) in fields: 
    426             L.append('--' + BOUNDARY) 
    427             L.append('Content-Disposition: form-data; name="%s"' % key) 
    428             L.append('') 
    429             L.append(value) 
    430         for (key, filename, value) in files: 
    431             L.append('--' + BOUNDARY) 
    432             L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) 
    433             L.append('Content-Type: %s' % self.get_content_type(filename)) 
    434             L.append('') 
    435             L.append(value) 
    436         L.append('--' + BOUNDARY + '--') 
    437         L.append('') 
    438         body = CRLF.join(L) 
    439         content_type = 'multipart/form-data; boundary=%s' % BOUNDARY 
    440         return content_type, body 
    441      
    442     def read_file(self, filename): 
    443         """Read the entire contents of a file and return it as a string""" 
    444  
    445         data = '' 
    446  
    447         f = open(filename) 
    448         try: 
    449             data = f.read() 
    450         finally: 
    451             f.close() 
    452  
    453         return data 
    454  
    455     def get_content_type(self, filename): 
    456         return mimetypes.guess_type(filename)[0] or 'application/octet-stream' 
    457          
    458     def http_post_logs(self, url, archive): 
    459         #host, selector, fields, files 
    460         files = ('logs', os.path.basename(archive), self.read_file(archive)), 
    461          
    462         # Client= olpc will make the server return just "OK" or "FAIL" 
    463         fields = ('client', 'xo'), 
    464         urlparts = urlparse.urlsplit(url) 
    465         print "Sending logs to %s" % url 
    466         r = self.post_multipart(urlparts[1], urlparts[2], fields, files) 
    467         print r 
    468         return (r == 'OK') 
    469  
    470  
    471 # This script is dual-mode, it can be used as a command line tool and as 
    472 # a library.  
    473 if sys.argv[0].endswith('logcollect.py') or \ 
    474         sys.argv[0].endswith('logcollect'): 
    475     print 'log-collect utility 1.0' 
    476          
    477     lc = LogCollect() 
    478     ls = LogSend() 
    479  
    480     logs = '' 
    481     mode = 'http' 
    482      
    483     if len(sys.argv)==1: 
    484         print """logcollect.py - send your XO logs to OLPC 
    485          
    486 Usage: 
    487     logcollect.py http  - send logs to default server 
    488      
    489     logcollect.py http://server.name/submit.php 
    490                          - submit logs to alternative server 
    491                           
    492     logcollect.py file:/media/xxxx-yyyy/mylog.zip 
    493                          - save the zip file on a USB device or SD card 
    494                           
    495     logcollect.py all file:/media/xxxx-yyyy/mylog.zip 
    496                         - Save to zip file and include ALL logs 
    497  
    498     logcollect.py none http 
    499                         - Just send info.txt, but no logs via http. 
    500  
    501     logcollect.py none file 
    502                         - Just save info.txt in /dev/shm/logs-SN123.zip 
    503  
    504     If you specify 'all' or 'none' you must specify http or file as well. 
    505         """ 
    506         sys.exit() 
    507          
    508      
    509     logbytes = 15360    
    510     if len(sys.argv)>1: 
    511         mode = sys.argv[len(sys.argv)-1] 
    512         if sys.argv[1] == 'all': 
    513             logbytes = 0 
    514         if sys.argv[1] == 'none': 
    515             logbytes = -1 
    516     
    517  
    518     if mode.startswith('file'): 
    519         # file:// 
    520         logs = mode[5:] 
    521   
    522     #if mode.lower().startswith('http'): 
    523     #    pass 
    524     #else if mode.lower().startswith('usb'): 
    525     #    pass 
    526     #else if mode.lower().startswith('sd'): 
    527     #    pass 
    528      
    529     logs = lc.write_logs(logs, logbytes) 
    530     print 'Logs saved in %s' % logs 
    531      
    532     sent_ok = False 
    533     if len(sys.argv)>1: 
    534        mode = sys.argv[len(sys.argv)-1] 
    535      
    536     if mode.startswith('http'): 
    537         print "Trying to send the logs using HTTP (web)" 
    538         if len(mode) == 4: 
    539             url = 'http://olpc.scheffers.net/olpc/submit.tcl' 
    540         else: 
    541             url = mode 
    542              
    543         if ls.http_post_logs(url, logs): 
    544             print "Logs were sent." 
    545             sent_ok = True             
    546         else: 
    547             print "FAILED to send logs." 
    548   
    549  
    550     if sent_ok: 
    551         os.remove(logs) 
    552         print "Logs were sent, tempfile deleted." 
    553  
    554  
  • logviewer.py

    diff --git a/logviewer.py b/logviewer.py
    index cdd77ed..2c08a03 100644
    a b  
    1616# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
    1717 
    1818import os 
     19import sys 
    1920import logging 
    2021from gettext import gettext as _ 
    2122 
     
    3334from sugar.graphics.toggletoolbutton import ToggleToolButton 
    3435from sugar.graphics.palette import Palette 
    3536from sugar.graphics.alert import NotifyAlert 
    36 from logcollect import LogCollect, LogSend 
    3737from sugar.graphics.toolbarbox import ToolbarButton, ToolbarBox 
    3838from sugar.activity.widgets import * 
     39from jarabe import config 
     40from jarabe.controlpanel.gui import ControlPanel 
    3941 
    4042# Should be builtin to sugar.graphics.alert.NotifyAlert... 
    4143def _notify_response_cb(notify, response, activity): 
     
    399401        delete_btn.connect('clicked', self._delete_log_cb) 
    400402        edit_toolbar.insert(delete_btn, -1) 
    401403 
    402         self.collector_palette = CollectorPalette(self) 
    403404        collector_btn = ToolButton('zoom-best-fit') 
    404         collector_btn.set_palette(self.collector_palette) 
    405         collector_btn.connect('clicked', self._logviewer_cb) 
     405        collector_btn.set_tooltip(_('Report a problem')) 
     406        collector_btn.connect('clicked', self._collector_cb) 
    406407        edit_toolbar.insert(collector_btn, -1) 
    407408 
    408409        edit_toolbar.show_all() 
     
    472473                notify.connect('response', _notify_response_cb, self) 
    473474                self.add_alert(notify) 
    474475 
    475     def _logviewer_cb(self, widget): 
    476         self.collector_palette.popup(True) 
    477  
    478 class CollectorPalette(Palette): 
    479     _DEFAULT_SERVER = 'http://olpc.scheffers.net/olpc/submit.tcl' 
    480  
    481     def __init__(self, handler): 
    482         Palette.__init__(self, _('Log Collector: Send XO information')) 
    483  
    484         self._handler = handler 
    485          
    486         self._collector = LogCollect() 
     476    def _collector_cb(self, widget): 
     477        # Allows control panel extensions to load. 
     478        sys.path.append(config.ext_path) 
    487479         
    488         label = gtk.Label( 
    489             _('Log collector sends information about the system\n'\ 
    490               'and running processes to a central server.  Use\n'\ 
    491               'this option if you want to report a problem.')) 
    492          
    493         send_button = gtk.Button(_('Send information')) 
    494         send_button.connect('clicked', self._on_send_button_clicked_cb) 
    495  
    496         vbox = gtk.VBox(False, 5) 
    497         vbox.pack_start(label) 
    498         vbox.pack_start(send_button) 
    499         vbox.show_all() 
    500  
    501         self.set_content(vbox) 
    502  
    503     def _on_send_button_clicked_cb(self, button): 
    504         success = True 
    505         try: 
    506             data = self._collector.write_logs() 
    507             sender = LogSend() 
    508             success = sender.http_post_logs(self._DEFAULT_SERVER, data) 
    509         except: 
    510             success = False 
    511  
    512         os.remove(data) 
    513         self.popdown(True) 
    514  
    515         title = '' 
    516         msg = '' 
    517         if success: 
    518             title = _('Logs sent') 
    519             msg = _('The logs were uploaded to the server.') 
    520         else: 
    521             title = _('Logs not sent') 
    522             msg = _('The logs could not be uploaded to the server. '\ 
    523                     'Please check your network connection.') 
    524  
    525         notify = NotifyAlert() 
    526         notify.props.title = title 
    527         notify.props.msg = msg 
    528         notify.connect('response', _notify_response_cb, self._handler) 
    529         self._handler.add_alert(notify) 
    530  
     480        panel = ControlPanel() 
     481        panel.set_transient_for(self.get_toplevel()) 
     482        panel.show_section_view('report')