home *** CD-ROM | disk | FTP | other *** search
- #!/usr/bin/python
-
- '''Command line Apport user interface.
-
- Copyright (C) 2007 Canonical Ltd.
- Author: Michael Hofmann <mh21@piware.de>
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the
- Free Software Foundation; either version 2 of the License, or (at your
- option) any later version. See http://www.gnu.org/copyleft/gpl.html for
- the full text of the license.
- '''
-
- # Web browser support:
- # w3m, lynx: do not work
- # elinks: works
-
- import os.path, os, sys, subprocess, re, errno
- import tty, termios
- from datetime import datetime
-
- try:
- from gettext import gettext as _
- import apport.ui
- except ImportError, e:
- # this can happen while upgrading python packages
- print >> sys.stderr, 'Could not import module, is a package upgrade in progress? Error:', e
- sys.exit(1)
-
- class CLIDialog:
- '''Command line dialog wrapper.'''
-
- def __init__(self, heading, text):
- self.heading = '\n*** ' + heading + '\n'
- self.text = text
- self.keys = []
- self.buttons = []
- self.visible = False
-
- def raw_input_char(self, text):
- """ raw_input, but read only one character """
-
- sys.stdout.write(text + ' ')
-
- file = sys.stdin.fileno()
- saved_attributes = termios.tcgetattr(file)
- attributes = termios.tcgetattr(file)
- attributes[3] = attributes[3] & ~(termios.ICANON)
- attributes[6][termios.VMIN] = 1
- attributes[6][termios.VTIME] = 0
- termios.tcsetattr(file, termios.TCSANOW, attributes)
-
- try:
- ch = str(sys.stdin.read(1))
- finally:
- termios.tcsetattr(file, termios.TCSANOW, saved_attributes)
-
- print
- return ch
-
- def show(self):
- self.visible = True
- print self.heading
- print self.text
-
- def run(self):
- if not self.visible:
- self.show()
-
- print
- try:
- # Only one button
- if len (self.keys) <= 1:
- self.raw_input_char(_('Press any key to continue...'))
- return 0
- # Multiple choices
- while True:
- print _('What would you like to do? Your options are:')
- for index, button in enumerate(self.buttons):
- print ' %s: %s' % (self.keys[index], button)
-
- response = self.raw_input_char(_('Please choose (%s):') % ('/'.join(self.keys)))
- try:
- return self.keys.index(response[0].upper()) + 1
- except ValueError:
- pass
- except KeyboardInterrupt:
- print
- sys.exit(1)
-
- def addbutton(self, button):
- self.keys.append(re.search('&(.)', button).group(1).upper())
- self.buttons.append(re.sub('&', '', button))
- return len(self.keys)
-
-
- class CLIProgressDialog(CLIDialog):
- '''Command line progress dialog wrapper.'''
-
- def __init__(self, heading, text):
- CLIDialog.__init__(self, heading, text)
- self.progresscount = 0
-
- def set(self, progress = None):
- self.progresscount = (self.progresscount + 1) % 5
- if self.progresscount:
- return
-
- if progress != None:
- sys.stdout.write('\r%u%%' % (progress * 100))
- else:
- sys.stdout.write('.')
- sys.stdout.flush()
-
- class CLIUserInterface(apport.ui.UserInterface):
- '''Command line Apport user interface'''
-
- def __init__(self):
- apport.ui.UserInterface.__init__(self)
-
- #
- # ui_* implementation of abstract UserInterface classes
- #
-
- def ui_present_crash(self, desktop_entry):
- date = datetime.strptime(self.report['Date'], '%a %b %d %H:%M:%S %Y')
- # adapt dialog heading and label appropriately
- if desktop_entry:
- name = desktop_entry.getName()
- elif self.report.has_key('ExecutablePath'):
- name = os.path.basename(self.report['ExecutablePath'])
- else:
- name = self.cur_package
- # translators: first %s: application name, second %s: date, third %s: time
- heading = _('%s closed unexpectedly on %s at %s.') % (name, date.date(), date.time())
-
- dialog = CLIDialog(
- heading,
- _('If you were not doing anything confidential (entering passwords or other\n'
- 'private information), you can help to improve the application by reporting\n'
- 'the problem.'))
- report = dialog.addbutton(_('&Report Problem...'))
- ignore = dialog.addbutton(_('Cancel and &ignore future crashes of this program version'))
- dialog.addbutton(_('&Cancel'))
-
- # show crash notification dialog
- response = dialog.run()
-
- if response == report:
- return {'action': 'report', 'blacklist': False}
- if response == ignore:
- return {'action': 'cancel', 'blacklist': True}
- # Fallback
- return {'action': 'cancel', 'blacklist': False}
-
- def ui_present_package_error(self):
- name = self.report['Package']
- dialog = CLIDialog(
- _('The package "%s" failed to install or upgrade.') % name,
- _('You can help the developers to fix the package by reporting the problem.'))
- report = dialog.addbutton(_('&Report Problem...'))
- dialog.addbutton(_('&Cancel'))
-
- response = dialog.run()
-
- if response == report:
- return 'report'
- # Fallback
- return 'cancel'
-
- def ui_present_kernel_error(self):
- dialog = CLIDialog (
- _('The kernel encountered a serious problem'),
- _('Your system might become unstable now and might need to be restarted.\n'
- 'You can help the developers to fix the problem by reporting it.'))
- report = dialog.addbutton(_('&Report Problem...'))
- dialog.addbutton(_('&Cancel'))
-
- response = dialog.run()
-
- if response == report:
- return 'report'
- # Fallback
- return 'cancel'
-
- def ui_present_report_details(self):
- dialog = CLIDialog (
- _('Send problem report to the developers?'),
- _('After the problem report has been sent, please fill out the form in the\n'
- 'automatically opened web browser.'))
-
- # report contents
- details = ''
- for key in self.report:
- details += key + ':'
- # string value
- if not hasattr(self.report[key], 'gzipvalue') and \
- hasattr(self.report[key], 'isspace') and \
- not self.report._is_binary(self.report[key]):
- lines = self.report[key].splitlines()
- if len(lines) <= 1:
- details += ' ' + self.report[key] + '\n'
- else:
- details += '\n'
- for line in lines:
- details += ' ' + line + '\n'
- else:
- details += ' ' + _('(binary data)') + '\n'
-
- # complete/reduced reports
- if self.report.has_key('CoreDump') and self.report.has_useful_stacktrace():
- complete = dialog.addbutton(_('&Send complete report (recommended; %s)') %
- self.format_filesize(self.get_complete_size()))
- reduced = dialog.addbutton(_('Send &reduced report (slow Internet connection; %s)') %
- self.format_filesize(self.get_reduced_size()))
- else:
- complete = dialog.addbutton(_('&Send report (%s)') %
- self.format_filesize(self.get_complete_size()))
- reduced = None
-
- view = dialog.addbutton(_('&View report'))
- save = dialog.addbutton(_('&Keep report file for sending later or copying to somewhere else'))
-
- dialog.addbutton(_('&Cancel'))
-
- while True:
- response = dialog.run()
-
- if response == complete:
- return 'full'
- if response == reduced:
- return 'reduced'
- if response == view:
- try:
- subprocess.Popen(["/usr/bin/sensible-pager"],
- stdin=subprocess.PIPE,
- close_fds=True).communicate(details)
- except IOError, e:
- # ignore broken pipe (premature quit)
- if e.errno == errno.EPIPE:
- pass
- else:
- raise
- continue
- if response == save:
- print _('Problem report file:'), self.report_file
- return 'cancel'
-
- # Fallback
- return 'cancel'
-
- def ui_info_message(self, title, text):
- dialog = CLIDialog(title, text)
- dialog.addbutton(_('&Confirm'))
- dialog.run()
-
- def ui_error_message(self, title, text):
- dialog = CLIDialog(_('Error: %s') % title, text)
- dialog.addbutton(_('&Confirm'))
- dialog.run()
-
- def ui_start_info_collection_progress(self):
- self.progress = CLIProgressDialog (
- _('Collecting problem information'),
- _('The collected information can be sent to the developers to improve the\n'
- 'application. This might take a few minutes.'))
- self.progress.show()
-
- def ui_pulse_info_collection_progress(self):
- self.progress.set()
-
- def ui_stop_info_collection_progress(self):
- print
-
- def ui_start_upload_progress(self):
- self.progress = CLIProgressDialog (
- _('Uploading problem information'),
- _('The collected information is being sent to the bug tracking system.\n'
- 'This might take a few minutes.'))
- self.progress.show()
-
- def ui_set_upload_progress(self, progress):
- self.progress.set(progress)
-
- def ui_stop_upload_progress(self):
- print
-
- if __name__ == '__main__':
- app = CLIUserInterface()
- if not app.run_argv():
- print >> sys.stderr, _('No pending crash reports. Try --help for more information.')
-