Пример #1
0
    def _tab_closed_cb(self, notebook, child):
        index = self.page_num(child)

        page = self.get_nth_page(index)

        text_buffer = page.get_children()[0].get_buffer()
        empty = text_buffer.get_char_count() == 0

        if empty:
            self.__tab_close(index)
            self.emit('tab-closed', index)
            return

        tablabel = self.get_tab_label(page)
        path = tablabel.get_path()
        example = self.activity.is_example(path)
        pristine = not text_buffer.get_modified()

        if example and pristine:
            self.__tab_close(index)
            self.emit('tab-closed', index)
            return

        alert = ConfirmationAlert()
        alert.props.title = _('Erase')
        alert.props.msg = _('Do you want to permanently erase \"%s\"?') \
                          % tablabel.get_text()
        alert.connect('response', self._tab_close_alert_response_cb, index)
        self.activity.add_alert(alert)
    def _new_game_alert(self, button):
        ''' We warn the user if the game is in progress before loading
        a new game. '''
        if self.collab.props.leader is not None and not self.collab.props.leader:
            # joiner cannot push buttons
            self._restoring = True
            self._no_action(button)
            self._restoring = False
            return

        if len(self._gnuchess.move_list) == 0:
            self._take_button_action(button)
            return

        self._restoring = True
        alert = ConfirmationAlert()
        alert.props.title = _('Game in progress.')
        alert.props.msg = _('Do you want to start a new game?')

        def _new_game_alert_response_cb(alert, response_id, self, button):
            if response_id is Gtk.ResponseType.OK:
                self._take_button_action(button)
            elif response_id is Gtk.ResponseType.CANCEL:
                self._no_action(button)
            self._restoring = False
            self.remove_alert(alert)

        alert.connect('response', _new_game_alert_response_cb, self, button)
        self.add_alert(alert)
        alert.show()
Пример #3
0
 def _save_colors(self, widget):
     alert = ConfirmationAlert()
     alert.props.title = _("Saving colors")
     alert.props.msg = _("Do you want to save these colors?")
     alert.connect('response', self._alert_response_cb)
     self.add_alert(alert)
     alert.show_all()
Пример #4
0
 def clear_trace_cb(self, button):
     clear_trace_alert = ConfirmationAlert()
     clear_trace_alert.props.title = _('Are You Sure?')
     clear_trace_alert.props.msg = \
         _('All trace points will be erased. This cannot be undone!')
     clear_trace_alert.connect('response', self.clear_trace_alert_cb)
     self.add_alert(clear_trace_alert)
Пример #5
0
 def __show_duplicate_alert(self, menu_item):
     alert = ConfirmationAlert()
     alert.props.title = _('Do you want to duplicate %s Activity?') % \
         self._activity_name
     alert.props.msg = _('This may take a few minutes')
     alert.connect('response', self.__duplicate_alert_response_cb)
     self.get_toplevel().add_alert(alert)
Пример #6
0
    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _('Are You Sure?')
        confirmation_alert.props.msg = \
            _('All your work will be discarded. This cannot be undone!')

        def action(alert, response):
            self.remove_alert(alert)
            if response is not Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, 'r')
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
            except:
                title = _('Load project from journal')
                if not journal:
                    title = _('Load example')
                msg = _('Error: Cannot open Physics project from this file.')
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect('response',
                              lambda alert, response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect('response', action)
        self.add_alert(confirmation_alert)
Пример #7
0
def _downgrade_option_alert(bundle):
    alert = ConfirmationAlert()
    alert.props.title = _("Older Version Of %s Activity") % (bundle.get_name())
    alert.props.msg = _("Do you want to downgrade to version %s") % bundle.get_activity_version()
    alert.connect("response", _downgrade_alert_response_cb, bundle)
    journalwindow.get_journal_window().add_alert(alert)
    alert.show()
Пример #8
0
 def clear_trace_cb(self, button):
     clear_trace_alert = ConfirmationAlert()
     clear_trace_alert.props.title = _('Are You Sure?')
     clear_trace_alert.props.msg = \
         _('All trace points will be erased. This cannot be undone!')
     clear_trace_alert.connect('response', self.clear_trace_alert_cb)
     self.add_alert(clear_trace_alert)
Пример #9
0
 def __show_duplicate_alert(self, menu_item):
     alert = ConfirmationAlert()
     alert.props.title = _('Do you want to duplicate %s Activity?') % \
         self._activity_name
     alert.props.msg = _('This may take a few minutes')
     alert.connect('response', self.__duplicate_alert_response_cb)
     self.get_toplevel().add_alert(alert)
Пример #10
0
 def remove_extension(self, widget, event, path, cp_path, vbox, title):
     alert = ConfirmationAlert()
     alert.props.title = _('¿Remove extension?')
     alert.props.msg = _('Sure?')
     alert.connect('response', self.remove_confirmation, path,
         cp_path, vbox, title)
     self.activity.add_alert(alert)
Пример #11
0
    def __load_game(self, file_path, journal=False):
        confirmation_alert = ConfirmationAlert()
        confirmation_alert.props.title = _("Are You Sure?")
        confirmation_alert.props.msg = _("All your work will be discarded. This cannot be undone!")

        def action(alert, response):
            self.remove_alert(alert)
            if response is not Gtk.ResponseType.OK:
                return

            try:
                f = open(file_path, "r")
                # Test if the file is valid project.
                json.loads(f.read())
                f.close()

                self.read_file(file_path)
                self.game.run(True)
            except:
                title = _("Load project from journal")
                if not journal:
                    title = _("Load example")
                msg = _("Error: Cannot open Physics project from this file.")
                alert = NotifyAlert(5)
                alert.props.title = title
                alert.props.msg = msg
                alert.connect("response", lambda alert, response: self.remove_alert(alert))
                self.add_alert(alert)

        confirmation_alert.connect("response", action)
        self.add_alert(confirmation_alert)
Пример #12
0
 def _alert_confirmation(self):
     alert = ConfirmationAlert()
     alert.remove_button(Gtk.ResponseType.CANCEL)
     alert.props.title = (_('Download Error'))
     alert.props.msg = (_('There was a problem with the download'))
     alert.connect('response', self._alert_response)
     self.add_alert(alert)
Пример #13
0
def _downgrade_option_alert(bundle, metadata):
    alert = ConfirmationAlert()
    alert.props.title = _('Older Version Of %s Activity') % (bundle.get_name())
    alert.props.msg = _('Do you want to downgrade to version %s') % \
        bundle.get_activity_version()
    alert.connect('response', _downgrade_alert_response_cb, metadata)
    journalwindow.get_journal_window().add_alert(alert)
    alert.show()
Пример #14
0
def _downgrade_option_alert(bundle, metadata):
    alert = ConfirmationAlert()
    alert.props.title = _('Older Version Of %s Activity') % (bundle.get_name())
    alert.props.msg = _('Do you want to downgrade to version %s') % \
        bundle.get_activity_version()
    alert.connect('response', _downgrade_alert_response_cb, metadata)
    journalwindow.get_journal_window().add_alert(alert)
    alert.show()
Пример #15
0
 def show_msg(self, text, title=""):
     """show_msg(text) shows text in a drop-down alert message.
     """
     alert = ConfirmationAlert()
     alert.props.title = title
     alert.props.msg = text
     alert.connect('response', self.alert_cb)
     self.add_alert(alert)
     alert.show()
Пример #16
0
 def show_msg(self, text, title=""):
     """show_msg(text) shows text in a drop-down alert message.
     """
     alert = ConfirmationAlert()
     alert.props.title = title
     alert.props.msg = text
     alert.connect('response', self.alert_cb)
     self.add_alert(alert)
     alert.show()
Пример #17
0
    def confirmation_alert(self, msg, title=None, confirmation_cb=None):
        alert = ConfirmationAlert()
        alert.props.title = title
        alert.props.msg = msg
        alert.pydebug_cb = confirmation_cb
        alert.connect('response', self._alert_response_cb)
        self._activity.add_alert(alert)

        return alert
Пример #18
0
    def _webservice_alert(self, message):
        alert = ConfirmationAlert()
        alert.props.title = message
        alert.props.msg = _('We are unable to install some software on your '
                            'system.\nSugar must be upgraded before this '
                            'activity can be run.')

        alert.connect('response', self._close_alert_cb)
        self.add_alert(alert)
        self._load_intro_graphics(message=message)
Пример #19
0
    def _delete_poll_button_cb(self, button, event, sha, title):
        """
        A DELETE button was clicked.
        """

        alert = ConfirmationAlert()
        alert.props.title = _('Delete Poll?')
        alert.props.msg = _('Do you want delete the poll "%s"?') % title
        alert.connect('response', self._delete_alert_confirmation_cb, sha)
        self.add_alert(alert)
Пример #20
0
    def confirmation_alert(self, title, msg, cb, *cb_args):
        """Raise standard confirmation alert"""
        alert = ConfirmationAlert(title=title, msg=msg)

        def response(alert, response_id, self, cb, *cb_args):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                cb(*cb_args)

        alert.connect('response', response, self, cb, *cb_args)
        alert.show_all()
        self.add_alert(alert)
Пример #21
0
    def clear_all_cb(self, button):
        def clear_all_alert_cb(alert, response_id):
            self.remove_alert(alert)
            if response_id is Gtk.ResponseType.OK:
                pygame.event.post(pygame.event.Event(pygame.USEREVENT, action="clear_all"))

        if len(self.game.world.world.bodies) > 2:
            clear_all_alert = ConfirmationAlert()
            clear_all_alert.props.title = _("Are You Sure?")
            clear_all_alert.props.msg = _("All your work will be discarded. This cannot be undone!")
            clear_all_alert.connect("response", clear_all_alert_cb)
            self.add_alert(clear_all_alert)
Пример #22
0
 def __bookmarker_toggled_cb(self, button):
     page = self._view.get_current_page()
     if self._bookmarker.props.active:
         self._bookmark_view.add_bookmark(page)
     else:
         alert = ConfirmationAlert()
         alert.props.title = _('Delete bookmark')
         alert.props.msg = _('All the information related '
                             'with this bookmark will be lost')
         self.add_alert(alert)
         alert.connect('response', self.__alert_response_cb, page)
         alert.show()
Пример #23
0
 def clear_all_cb(self, button):
     def clear_all_alert_cb(alert, response_id):
         self.remove_alert(alert)
         if response_id is Gtk.ResponseType.OK:
             pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                  action='clear_all'))
     if len(self.game.world.world.bodies) > 2:
         clear_all_alert = ConfirmationAlert()
         clear_all_alert.props.title = _('Are You Sure?')
         clear_all_alert.props.msg = \
             _('All your work will be discarded. This cannot be undone!')
         clear_all_alert.connect('response', clear_all_alert_cb)
         self.add_alert(clear_all_alert)
Пример #24
0
 def clear_all_cb(self, button):
     def clear_all_alert_cb(alert, response_id):
         self.remove_alert(alert)
         if response_id is Gtk.ResponseType.OK:
             pygame.event.post(pygame.event.Event(pygame.USEREVENT,
                                                  action='clear_all'))
     if len(self.game.world.world.GetBodyList()) > 2:
         clear_all_alert = ConfirmationAlert()
         clear_all_alert.props.title = _('Are You Sure?')
         clear_all_alert.props.msg = \
             _('All your work will be discarded. This cannot be undone!')
         clear_all_alert.connect('response', clear_all_alert_cb)
         self.add_alert(clear_all_alert)
Пример #25
0
 def __remove_clicked_cb(self, file_name):
     if self._image_canvas.is_image_active():
         alert = ConfirmationAlert()
         alert.props.title = _('Do you want remove the selected image?')
         # alert.props.msg = _('')
         alert.connect('response', self.__confirm_remove_image_cb)
         self.add_alert(alert)
     else:
         if len(self._book_model.get_pages()) > 1:
             alert = ConfirmationAlert()
             alert.props.title = _('Do you want remove the page?')
             # alert.props.msg = _('')
             alert.connect('response', self.__confirm_remove_page_cb)
             self.add_alert(alert)
Пример #26
0
 def __remove_clicked_cb(self, file_name):
     if self._image_canvas.is_image_active():
         alert = ConfirmationAlert()
         alert.props.title = _('Do you want remove the selected image?')
         # alert.props.msg = _('')
         alert.connect('response', self.__confirm_remove_image_cb)
         self.add_alert(alert)
     else:
         if len(self._book_model.get_pages()) > 1:
             alert = ConfirmationAlert()
             alert.props.title = _('Do you want remove the page?')
             # alert.props.msg = _('')
             alert.connect('response', self.__confirm_remove_page_cb)
             self.add_alert(alert)
Пример #27
0
    def _title_changed_event_cb(self, widget):
        old_title = self._metadata.get('title', None)
        new_title = self._title.get_text()
        if old_title != new_title:
            if new_title == '' or new_title.isspace():
                alert = ConfirmationAlert()
                alert.props.title = _('Empty title')
                alert.props.msg = _('The title is usually not left empty')
                alert.connect('response', self._title_alert_response_cb,
                              old_title,
                              self._metadata.get('title_set_by_user', 0))
                journalwindow.get_journal_window().add_alert(alert)
                alert.show()

            self._update_entry()
    def __permission_request_cb(self, webview, request):
        description = self._get_permission_name(request)
        site = webview.get_uri()
        match = _HOSTNAME_REGEX.match(site)
        if match:
            site = match.group(1)

        alert = ConfirmationAlert()
        alert.props.title = _('Allow %s to %s?') % \
            (site, description)
        alert.props.msg = _(
            'You can change your choice later by reloading the page')
        alert.connect('response', self.__permission_request_alert_cb, request)
        self._activity.add_alert(alert)

        # Allow async handeling
        return True
Пример #29
0
    def _save_colors_cb(self, button=None):
        ''' Save the new XO colors. '''
        ''' We warn the user if they are going to save their selection '''
        alert = ConfirmationAlert()
        alert.props.title = _('Saving colors')
        alert.props.msg = _('Do you want to save these colors?')
	def _change_colors_alert_response_cb(alert, response_id, self):
		if response_id is Gtk.ResponseType.OK:
			_logger.debug('saving colors')
			self.remove_alert(alert)
			self._confirm_save()
		elif response_id is Gtk.ResponseType.CANCEL:
			_logger.debug('cancel save')
			self.remove_alert(alert)
	alert.connect('response', _change_colors_alert_response_cb, self)
	self.add_alert(alert)
	alert.show()
Пример #30
0
    def __cell_title_edited_cb(self, cell, path, new_text):
        iterator = self._model.get_iter(path)
        old_text = self._model[iterator][ListModel.COLUMN_TITLE]

        if old_text != new_text and (new_text == '' or new_text.isspace()):
            alert = ConfirmationAlert()
            alert.props.title = _('Empty title')
            alert.props.msg = _('The title is usually not left empty')
            alert.connect('response', self._cell_title_alert_response_cb, path,
                          new_text)
            journalwindow.get_journal_window().add_alert(alert)
            alert.show()
            return

        if old_text != new_text:
            self._model[iterator][ListModel.COLUMN_TITLE] = new_text
            self.emit('title-edit-finished', new_text, path)
Пример #31
0
    def __permission_request_cb(self, webview, request):
        description = self._get_permission_name(request)
        site = webview.get_uri()
        match = _HOSTNAME_REGEX.match(site)
        if match:
            site = match.group(1)

        alert = ConfirmationAlert()
        alert.props.title = _('Allow %s to %s?') % \
            (site, description)
        alert.props.msg = _(
            'You can change your choice later by reloading the page')
        alert.connect('response', self.__permission_request_alert_cb, request)
        self._activity.add_alert(alert)

        # Allow async handeling
        return True
    def _save_colors_cb(self, button=None):
        ''' Save the new XO colors. '''
        ''' We warn the user if they are going to save their selection '''
        alert = ConfirmationAlert()
        alert.props.title = _('Saving colors')
        alert.props.msg = _('Do you want to save these colors?')

        def _change_colors_alert_response_cb(alert, response_id, self):
            if response_id is Gtk.ResponseType.OK:
                _logger.debug('saving colors')
                self.remove_alert(alert)
                self._confirm_save()
            elif response_id is Gtk.ResponseType.CANCEL:
                _logger.debug('cancel save')
                self.remove_alert(alert)

        alert.connect('response', _change_colors_alert_response_cb, self)
        self.add_alert(alert)
        alert.show()
Пример #33
0
    def _sync_data_from_USB(self, usb_data_path=None):
        # We need to sync up file on USB with file on disk,
        # but only if the email addresses match. Otherwise,
        # raise an error.
        if usb_data_path is not None:
            usb_data = {}
            if os.path.exists(usb_data_path):
                fd = open(usb_data_path, 'r')
                json_data = fd.read()
                fd.close()
                if len(json_data) > 0:
                    try:
                        usb_data = json.loads(json_data)
                    except ValueError as e:
                        _logger.error('Cannot load USB data: %s' % e)
            else:
                _logger.error('Cannot find USB data: %s' % usb_data_path)

            sugar_data_path = os.path.join(
                self.volume_data[0]['sugar_path'],
                self.volume_data[0]['uid'])
            sugar_data = {}
            if os.path.exists(sugar_data_path):
                fd = open(sugar_data_path, 'r')
                json_data = fd.read()
                fd.close()
                if len(json_data) > 0:
                    try:
                        sugar_data = json.loads(json_data)
                    except ValueError as e:
                        _logger.error('Cannot load Sugar data: %s' % e)
            else:
                _logger.error('Cannot find Sugar data: %s' % sugar_data_path)

            # First, check to make sure email_address matches
            if EMAIL_UID in usb_data:
                usb_email = usb_data[EMAIL_UID]
            else:
                usb_email = None
            if EMAIL_UID in sugar_data:
                sugar_email = sugar_data[EMAIL_UID]
            else:
                sugar_email = None
            if usb_email != sugar_email:
                if usb_email is None and sugar_email is not None:
                    _logger.warning('Using email address from Sugar: %s' %
                                    sugar_email)
                    usb_data[EMAIL_UID] = sugar_email
                elif usb_email is not None and sugar_email is None:
                    _logger.warning('Using email address from USB: %s' %
                                    usb_email)
                    sugar_data[EMAIL_UID] = usb_email
                elif usb_email is None and sugar_email is None:
                    _logger.warning('No email address found')
                else:
                    # FIX ME: We need to resolve this, but for right now, punt.
                    alert = ConfirmationAlert()
                    alert.props.title = _('Data mismatch')
                    alert.props.msg = _('Are you %(usb)s or %(sugar)s?' %
                                        {'usb': usb_email,
                                         'sugar': sugar_email})
                    alert.connect('response', self._close_alert_cb)
                    self.add_alert(alert)
                    self._load_intro_graphics(message=alert.props.msg)
                    return False

            def count_completed(data):
                count = 0
                for key in data:
                    if isinstance(data[key], dict) and \
                       'completed' in data[key] and \
                       data[key]['completed']:
                        count += 1
                return count

            # The database with the most completed tasks takes precedence.
            if count_completed(usb_data) >= count_completed(sugar_data):
                _logger.debug('data sync: USB data takes precedence')
                data_one = usb_data
                data_two = sugar_data
            else:
                _logger.debug('data sync: Sugar data takes precedence')
                data_one = sugar_data
                data_two = usb_data

            # Copy completed tasks from one to two
            for key in data_one:
                if isinstance(data_one[key], dict) and \
                   'completed' in data_one[key] and \
                   data_one[key]['completed']:
                    data_two[key] = data_one[key]

            # Copy completed tasks from two to one
            for key in data_two:
                if isinstance(data_two[key], dict) and \
                   'completed' in data_two[key] and \
                   data_two[key]['completed']:
                    data_one[key] = data_two[key]

            # Copy incompleted tasks from one to two
            for key in data_one:
                if isinstance(data_one[key], dict) and \
                   (not 'completed' in data_one[key] or
                    not data_one[key]['completed']):
                        data_two[key] = data_one[key]

            # Copy incompleted tasks from two to one
            for key in data_two:
                if isinstance(data_two[key], dict) and \
                   (not 'completed' in data_two[key] or
                    not data_two[key]['completed']):
                        data_one[key] = data_two[key]

            # Copy name, email_address, current_task...
            for key in data_one:
                if not isinstance(data_one[key], dict):
                    data_two[key] = data_one[key]
            for key in data_two:
                if not isinstance(data_two[key], dict):
                    data_one[key] = data_two[key]

            # Finally, write to the USB and ...
            json_data = json.dumps(data_one)
            fd = open(usb_data_path, 'w')
            fd.write(json_data)
            fd.close()

            # ...save a shadow copy in Sugar
            fd = open(sugar_data_path, 'w')
            fd.write(json_data)
            fd.close()
            return True
        else:
            _logger.error('No data to sync on USB')
            return False
Пример #34
0
    def _load_extension(self):
        if not WEBSERVICES_AVAILABLE:
            _logger.error('Webservices not available on this version of Sugar')
            self._webservice_alert(_('Sugar upgrade required.'))
            return False

        extensions_path = os.path.join(os.path.expanduser('~'), '.sugar',
                                       'default', 'extensions')
        webservice_path = os.path.join(extensions_path, 'webservice')
        sugarservices_path = os.path.join(self.bundle_path, 'sugarservices')
        init_path = os.path.join(self.bundle_path, 'sugarservices',
                                 '__init__.py')

        if not os.path.exists(extensions_path):
            try:
                subprocess.call(['mkdir', extensions_path])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (extensions_path, e))
                self._webservice_alert(_('System error.'))
                return False

        if not os.path.exists(webservice_path):
            try:
                subprocess.call(['mkdir', webservice_path])
            except OSError as e:
                _logger.error('Could not mkdir %s, %s' % (webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False
            try:
                subprocess.call(['cp', init_path, webservice_path])
            except OSError as e:
                _logger.error('Could not cp %s to %s, %s' %
                              (init_path, webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False

        install = False
        if not os.path.exists(os.path.join(webservice_path, 'sugarservices')):
            _logger.error('SugarServices webservice not found. Installing...')
            install = True
        elif utils.get_sugarservices_version() < \
             _REQUIRED_SUGARSERVICES_VERSION:
            _logger.error('Found old SugarServices version. Installing...')
            install = True

        if install:
            try:
                subprocess.call(['cp', '-r', sugarservices_path,
                                 webservice_path])
            except OSError as e:
                _logger.error('Could not copy %s to %s, %s' %
                              (sugarservices_path, webservice_path, e))
                self._webservice_alert(_('System error.'))
                return False

            alert = ConfirmationAlert()
            alert.props.title = _('Restart required')
            alert.props.msg = _('We needed to install some software on your '
                                'system.\nSugar must be restarted before '
                                'sugarservices can commence.')

            alert.connect('response', self._reboot_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(file_name='restart.html')

        return not install
Пример #35
0
    def check_volume_data(self):
        # Before we begin (and before each task),
        # we need to find any and all USB keys
        # and any and all training-data files on them.

        _logger.debug(utils.get_volume_paths())
        self.volume_data = []
        for path in utils.get_volume_paths():
            os.path.basename(path)
            self.volume_data.append(
                {'basename': os.path.basename(path),
                 'files': utils.look_for_training_data(path),
                 'sugar_path': os.path.join(self.get_activity_root(), 'data'),
                 'usb_path': path})
            _logger.debug(self.volume_data[-1])

        # (1) We require a USB key
        if len(self.volume_data) == 0:
            _logger.error('NO USB KEY INSERTED')
            alert = ConfirmationAlert()
            alert.props.title = _('USB key required')
            alert.props.msg = _('You must insert a USB key before launching '
                                'this activity.')
            alert.connect('response', self._remove_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(file_name='insert-usb.html')
            return False

        # (2) Only one USB key
        if len(self.volume_data) > 1:
            _logger.error('MULTIPLE USB KEYS INSERTED')
            alert = ConfirmationAlert()
            alert.props.title = _('Multiple USB keys found')
            alert.props.msg = _('Only one USB key must be inserted while '
                                'running this program.\nPlease remove any '
                                'additional USB keys before launching '
                                'this activity.')
            alert.connect('response', self._remove_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        volume = self.volume_data[0]

        # (3) At least 10MB of free space
        if utils.is_full(volume['usb_path'],
                         required=_MINIMUM_SPACE):
            _logger.error('USB IS FULL')
            alert = ConfirmationAlert()
            alert.props.title = _('USB key is full')
            alert.props.msg = _('No room on USB')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        # (4) File is read/write
        if not utils.is_writeable(volume['usb_path']):
            _logger.error('CANNOT WRITE TO USB')
            alert = ConfirmationAlert()
            alert.props.title = _('Cannot write to USB')
            alert.props.msg = _('USB key seems to be read-only.')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False

        # (5) Only one set of training data per USB key
        # We expect UIDs to formated as XXXX-XXXX
        # We need to make sure we have proper UIDs associated with
        # the USBs and the files on them match the UID.
        # (a) If there are no files, we will assign the UID based on the
        #     volume path;
        # (b) If there is one file with a valid UID, we use that UID;
        if len(volume['files']) == 0:
            volume['uid'] = 'training-data-%s' % \
                            utils.format_volume_name(volume['basename'])
            _logger.debug('No training data found. Using UID %s' %
                          volume['uid'])
            return True
        elif len(volume['files']) == 1:
            volume['uid'] = 'training-data-%s' % volume['files'][0][-9:]
            _logger.debug('Training data found. Using UID %s' %
                          volume['uid'])
            return True
        else:
            _logger.error('MULTIPLE TRAINING-DATA FILES FOUND')
            alert = ConfirmationAlert()
            alert.props.title = _('Multiple training-data files found.')
            alert.props.msg = _('There can only be one set of training '
                                'data per USB key.')
            alert.connect('response', self._close_alert_cb)
            self.add_alert(alert)
            self._load_intro_graphics(message=alert.props.msg)
            return False