def handle_response(self, response, commit=True):
     '''
     handle the response from self.presenter.start() in self.start()
     '''
     not_ok_msg = 'Are you sure you want to lose your changes?'
     self._return = None
     self.clean_model()
     if response == gtk.RESPONSE_OK or response in self.ok_responses:
         try:
             self._return = self.model
             if self.presenter.dirty() and commit:
                 self.commit_changes()
         except DBAPIError, e:
             msg = _('Error committing changes.\n\n%s') % \
                   utils.xml_safe_utf8(unicode(e.orig))
             utils.message_details_dialog(msg, str(e), gtk.MESSAGE_ERROR)
             self.session.rollback()
             return False
         except Exception, e:
             msg = _('Unknown error when committing changes. See the '\
                     'details for more information.\n\n%s') \
                     % utils.xml_safe_utf8(e)
             debug(traceback.format_exc())
             utils.message_details_dialog(msg, traceback.format_exc(),
                                          gtk.MESSAGE_ERROR)
             self.session.rollback()
             return False
def remove_callback(genera):
    """
    The callback function to remove a genus from the genus context menu.
    """
    genus = genera[0]
    from bauble.plugins.plants.species_model import Species
    session = db.Session()
    nsp = session.query(Species).filter_by(genus_id=genus.id).count()
    safe_str = utils.xml_safe_utf8(str(genus))
    if nsp > 0:
        msg = _('The genus <i>%(genus)s</i> has %(num_species)s species.  '
                'Are you sure you want to remove it?') \
                % dict(genus=safe_str, num_species=nsp)
    else:
        msg = _("Are you sure you want to remove the genus <i>%s</i>?") \
            % safe_str
    if not utils.yes_no_dialog(msg):
        return
    try:
        obj = session.query(Genus).get(genus.id)
        session.delete(obj)
        session.commit()
    except Exception, e:
        msg = _('Could not delete.\n\n%s') % utils.xml_safe_utf8(e)
        utils.message_details_dialog(msg, traceback.format_exc(),
                                     type=gtk.MESSAGE_ERROR)
 def handle_response(self, response):
     """
     @return: return True if the editor is realdy to be closes, False if
     we want to keep editing, if any changes are committed they are stored
     in self._committed
     """
     # TODO: need to do a __cleanup_model before the commit to do things
     # like remove the insfraspecific information that's attached to the
     # model if the infraspecific rank is None
     not_ok_msg = 'Are you sure you want to lose your changes?'
     if response == gtk.RESPONSE_OK or response in self.ok_responses:
         try:
             if self.presenter.dirty():
                 self.commit_changes()
                 self._committed.append(self.model)
         except DBAPIError, e:
             exc = traceback.format_exc()
             msg = _('Error committing changes.\n\n%s') % \
                   utils.xml_safe_utf8(e.orig)
             utils.message_details_dialog(msg, str(e), gtk.MESSAGE_ERROR)
             return False
         except Exception, e:
             msg = _('Unknown error when committing changes. See the '\
                     'details for more information.\n\n%s') % \
                     utils.xml_safe_utf8(e)
             debug(traceback.format_exc())
             #warning(traceback.format_exc())
             utils.message_details_dialog(msg, traceback.format_exc(),
                                          gtk.MESSAGE_ERROR)
             return False
def remove_callback(families):
    """
    The callback function to remove a family from the family context menu.
    """
    family = families[0]
    from bauble.plugins.plants.genus import Genus
    session = db.Session()
    ngen = session.query(Genus).filter_by(family_id=family.id).count()
    safe_str = utils.xml_safe_utf8(str(family))
    if ngen > 0:
        msg = _('The family <i>%(family)s</i> has %(num_genera)s genera.  Are '
                'you sure you want to remove it?') % dict(family=safe_str,
                                                          num_genera=ngen)
    else:
        msg = _("Are you sure you want to remove the family <i>%s</i>?") \
            % safe_str
    if not utils.yes_no_dialog(msg):
        return
    try:
        obj = session.query(Family).get(family.id)
        session.delete(obj)
        session.commit()
    except Exception, e:
        msg = _('Could not delete.\n\n%s') % utils.xml_safe_utf8(e)
        utils.message_details_dialog(msg, traceback.format_exc(),
                                     type=gtk.MESSAGE_ERROR)
 def get_Notes(self):
     if not self.plant.notes:
         return None
     notes = []
     for note in self.plant.notes:
         notes.append(dict(date=utils.xml_safe_utf8(note.date.isoformat()),
                           user=xml_safe_utf8(note.user),
                           category=xml_safe_utf8(note.category),
                           note=xml_safe_utf8(note.note)))
     return xml_safe_utf8(str(notes))
Example #6
0
def plant_markup_func(plant):
    """
    """
    sp_str = plant.accession.taxon_str(markup=True)
    # dead_color = "#777"
    dead_color = "#9900ff"
    if plant.quantity <= 0:
        dead_markup = '<span foreground="%s">%s</span>' % (dead_color, utils.xml_safe_utf8(plant))
        return dead_markup, sp_str
    else:
        return utils.xml_safe_utf8(plant), sp_str
def source_detail_remove_callback(details):
    detail = details[0]
    s = '%s: %s' % (detail.__class__.__name__, str(detail))
    msg = _("Are you sure you want to remove %s?") % utils.xml_safe_utf8(s)
    if not utils.yes_no_dialog(msg):
        return
    try:
        session = db.Session()
        obj = session.query(SourceDetail).get(detail.id)
        session.delete(obj)
        session.commit()
    except Exception, e:
        msg = _('Could not delete.\n\n%s') % utils.xml_safe_utf8(e)
        utils.message_details_dialog(msg, traceback.format_exc(),
                                     type=gtk.MESSAGE_ERROR)
 def on_activate(item, cb):
     result = False
     try:
         # have to get the selected values again here
         # because for some unknown reason using the
         # "selected" variable from the parent scope
         # will give us the objects but they won't be
         # in an session...maybe its a thread thing
         values = self.get_selected_values()
         result = cb(values)
     except Exception, e:
         msg = utils.xml_safe_utf8(str(e))
         tb = utils.xml_safe_utf8(traceback.format_exc())
         utils.message_details_dialog(msg, tb,gtk.MESSAGE_ERROR)
         warning(traceback.format_exc())
    def run(self, filenames, metadata, force=False):
        '''
        A generator method for importing filenames into the database.
        This method periodically yields control so that the GUI can
        update.

        :param filenames:
        :param metadata:
        :param force: default=False
        '''
        transaction = None
        connection = None
        self.__error_exc = BaubleError(_('Unknown Error.'))

        try:
            # user a contextual connect in case whoever called this
            # method called it inside a transaction then we can pick
            # up the parent connection and the transaction
            connection = metadata.bind.connect()
            transaction = connection.begin()
        except Exception, e:
            msg = _('Error connecting to database.\n\n%s') % \
                  utils.xml_safe_utf8(e)
            utils.message_dialog(msg, gtk.MESSAGE_ERROR)
            return
    def on_remove_button_clicked(self, button, data=None):
        """
        Removes the currently selected vernacular name from the view.
        """
        tree = self.view.widgets.vern_treeview
        path, col = tree.get_cursor()
        treemodel = tree.get_model()
        vn = treemodel[path][0]

        msg = _('Are you sure you want to remove the vernacular ' \
                    'name <b>%s</b>?') % utils.xml_safe_utf8(vn.name)
        if vn.name and not vn in self.session.new and not \
                utils.yes_no_dialog(msg, parent=self.view.get_window()):
            return

        treemodel.remove(treemodel.get_iter(path))
        self.model.vernacular_names.remove(vn)
        utils.delete_or_expunge(vn)
        if not self.model.default_vernacular_name:
            # if there is only one value in the tree then set it as the
            # default vernacular name
            first = treemodel.get_iter_first()
            if first:
#                 self.set_model_attr('default_vernacular_name',
#                                     tree_model[first][0])
                self.model.default_vernacular_name = treemodel[first][0]
        self.parent_ref().refresh_sensitivity()
        self.__dirty = True
Example #11
0
    def __export_task(self, path, one_file=True):
        ntables = len(db.metadata.tables)
        steps_so_far = 0
        if not one_file:
            tableset_el = etree.Element("tableset")

        for table_name, table in tables.iteritems():
            if one_file:
                tableset_el = etree.Element("tableset")
            info("exporting %s..." % table_name)
            table_el = ElementFactory(tableset_el, "table", attrib={"name": table_name})
            results = table.select().execute().fetchall()
            columns = table.c.keys()
            try:
                for row in results:
                    row_el = ElementFactory(table_el, "row")
                    for col in columns:
                        ElementFactory(row_el, "column", attrib={"name": col}, text=row[col])
            except ValueError, e:
                utils.message_details_dialog(utils.xml_safe_utf8(e), traceback.format_exc(), gtk.MESSAGE_ERROR)
                return
            else:
                if one_file:
                    tree = etree.ElementTree(tableset_el)
                    filename = os.path.join(path, "%s.xml" % table_name)
                    # TODO: can figure out why this keeps crashing
                    tree.write(filename, encoding="utf8", xml_declaration=True)
 def extra_elements(self, unit):
     bg_unit = ABCDElement(unit, 'BotanicalGardenUnit')
     ABCDElement(bg_unit, 'AccessionSpecimenNumbers',
                 text=xml_safe_utf8(self.plant.quantity))
     ABCDElement(bg_unit, 'LocationInGarden',
                 text=xml_safe_utf8(str(self.plant.location)))
     if self.for_labels:
         if self.species.label_distribution:
             etree.SubElement(unit, 'distribution').text=\
                 self.species.label_distribution
         elif self.species.distribution:
             etree.SubElement(unit, 'distribution').text=\
                 self.species.distribution_str()
     # TODO: AccessionStatus, AccessionMaterialtype,
     # ProvenanceCategory, AccessionLineage, DonorCategory,
     # PlantingDate, Propagation
     super(PlantABCDAdapter, self).extra_elements(unit)
def remove_callback(tags):
    """
    :param tags: a list of :class:`Tag` objects.
    """
    tag = tags[0]
    s = '%s: %s' % (tag.__class__.__name__, utils.xml_safe_utf8(tag))
    msg = _("Are you sure you want to remove %s?") % s
    if not utils.yes_no_dialog(msg):
        return
    session = db.Session()
    try:
        obj = session.query(Tag).get(tag.id)
        session.delete(obj)
        session.commit()
    except Exception, e:
        msg = _('Could not delete.\n\n%s') % utils.xml_safe_utf8(e)
        utils.message_details_dialog(msg, traceback.format_exc(),
                                     type=gtk.MESSAGE_ERROR)
def remove_callback(plants):
    s = ', '.join([str(p) for p in plants])
    msg = _("Are you sure you want to remove the following plants?\n\n%s") \
        % utils.xml_safe_utf8(s)
    if not utils.yes_no_dialog(msg):
        return

    session = db.Session()
    for plant in plants:
        obj = session.query(Plant).get(plant.id)
        session.delete(obj)
    try:
        session.commit()
    except Exception, e:
        msg = _('Could not delete.\n\n%s') % utils.xml_safe_utf8(e)

        utils.message_details_dialog(msg, traceback.format_exc(),
                                     type=gtk.MESSAGE_ERROR)
    def update(self, row):
        from textwrap import TextWrapper
        wrapper = TextWrapper(width=50, subsequent_indent='  ')
        self.set_widget_value('sd_name_data', '<big>%s</big>' %
                              utils.xml_safe_utf8(row.name), markup=True)
        source_type = ''
        if row.source_type:
            source_type = utils.xml_safe_utf8(row.source_type)
        self.set_widget_value('sd_type_data', source_type)

        description = ''
        if row.description:
            description = utils.xml_safe_utf8(row.description)
        self.set_widget_value('sd_desc_data', description)

        source = Source.__table__
        nacc = select([source.c.id], source.c.source_detail_id==row.id).\
            count().execute().fetchone()[0]
        self.set_widget_value('sd_nacc_data', nacc)
def remove_callback(locations):
    loc = locations[0]
    s = '%s: %s' % (loc.__class__.__name__, str(loc))
    if len(loc.plants) > 0:
        msg = _('Please remove the plants from <b>%(location)s</b> '\
                    'before deleting it.') % {'location': loc}
        utils.message_dialog(msg, gtk.MESSAGE_WARNING)
        return
    msg = _("Are you sure you want to remove %s?") % \
          utils.xml_safe_utf8(s)
    if not utils.yes_no_dialog(msg):
        return
    try:
        session = db.Session()
        obj = session.query(Location).get(loc.id)
        session.delete(obj)
        session.commit()
    except Exception, e:
        msg = _('Could not delete.\n\n%s') % utils.xml_safe_utf8(e)
        utils.message_details_dialog(msg, traceback.format_exc(),
                                     type=gtk.MESSAGE_ERROR)
        def update_label(self):
            label = []
            date_str = None
            if self.model.date and isinstance(self.model.date, datetime.date):
                format = prefs.prefs[prefs.date_format_pref]
                date_str =utils.xml_safe_utf8(self.model.date.strftime(format))
            elif self.model.date:
                date_str = utils.xml_safe_utf8(self.model.date)
            else:
                date_str = self.widgets.date_entry.props.text

            if self.model.user and date_str:# and self.model.date:
                label.append(_('%(user)s on %(date)s') % \
                             dict(user=utils.xml_safe_utf8(self.model.user),
                                  date=date_str))
            elif date_str:
                label.append('%s' % date_str)
            elif self.model.user:
                label.append('%s' % utils.xml_safe_utf8(self.model.user))

            if self.model.category:
                label.append('(%s)' % utils.xml_safe_utf8(self.model.category))

            if self.model.note:
                note_str = ' : %s' % utils.xml_safe_utf8(self.model.note).\
                    replace('\n', '  ')
                max_length = 25
                # label.props.ellipsize doesn't work properly on a
                # label in an expander we just do it ourselves here
                if len(self.model.note) > max_length:
                    label.append('%s ...' % note_str[0:max_length-1])
                else:
                    label.append(note_str)

            self.widgets.notes_expander.set_label(' '.join(label))
Example #18
0
    def on_file_menu_new(self, widget, data=None):
        msg = "If a database already exists at this connection then creating "\
              "a new database could destroy your data.\n\n<i>Are you sure "\
              "this is what you want to do?</i>"

        if not utils.yes_no_dialog(msg, yes_delay=2):
            return

        #if gui is not None and hasattr(gui, 'insert_menu'):
        submenu = self.insert_menu.get_submenu()
        for c in submenu.get_children():
            submenu.remove(c)
        self.insert_menu.show()
        try:
            db.create()
            pluginmgr.init()
        except Exception, e:
            msg = _('Could not create a new database.\n\n%s' % \
                        utils.xml_safe_utf8(e))
            tb = utils.xml_safe_utf8(traceback.format_exc())
            utils.message_details_dialog(msg, tb, gtk.MESSAGE_ERROR)
            return
 def handle_response(self, response):
     '''
     handle the response from self.presenter.start() in self.start()
     '''
     not_ok_msg = _('Are you sure you want to lose your changes?')
     if response == gtk.RESPONSE_OK or response in self.ok_responses:
         try:
             if self.presenter.dirty():
                 self.commit_changes()
                 self._committed.append(self.model)
         except DBAPIError, e:
             msg = _('Error committing changes.\n\n%s' \
                     % utils.xml_safe_utf8(e.orig))
             utils.message_details_dialog(msg, str(e), gtk.MESSAGE_ERROR)
             return False
         except Exception, e:
             msg = _('Unknown error when committing changes. See the '\
                    'details for more information.\n\n%s' \
                    % utils.xml_safe_utf8(e))
             utils.message_details_dialog(msg, traceback.format_exc(),
                                          gtk.MESSAGE_ERROR)
             return False
 def handle_response(self, response):
     '''
     @return: return a list if we want to tell start() to close the editor,
     the list should either be empty or the list of committed values, return
     None if we want to keep editing
     '''
     not_ok_msg = 'Are you sure you want to lose your changes?'
     if response == gtk.RESPONSE_OK or response in self.ok_responses:
         try:
             if self.presenter.dirty():
                 self.commit_changes()
                 self._committed.append(self.model)
         except DBAPIError, e:
             msg = _('Error committing changes.\n\n%s') % \
                   utils.xml_safe_utf8(e.orig)
             utils.message_details_dialog(msg, str(e), gtk.MESSAGE_ERROR)
             return False
         except Exception, e:
             msg = _('Unknown error when committing changes. See the ' \
                   'details for more information.\n\n%s') % \
                   utils.xml_safe_utf8(e)
             utils.message_details_dialog(msg, traceback.format_exc(),
                                          gtk.MESSAGE_ERROR)
             return False
def load(path=None):
    """
    Search the plugin path for modules that provide a plugin. If path
    is a directory then search the directory for plugins. If path is
    None then use the default plugins path, bauble.plugins.

    This method populates the pluginmgr.plugins dict and imports the
    plugins but doesn't do any plugin initialization.

    :param path: the path where to look for the plugins
    :type path: str
    """
    if path is None:
        if bauble.main_is_frozen():
            #path = os.path.join(paths.lib_dir(), 'library.zip')
            path = os.path.join(paths.main_dir(), 'library.zip')
        else:
            path = os.path.join(paths.lib_dir(), 'plugins')
    found, errors = _find_plugins(path)

    # show error dialog for plugins that couldn't be loaded...we only
    # give details for the first error and assume the others are the
    # same...and if not then it doesn't really help anyways
    if errors:
        name = ', '.join(sorted(errors.keys()))
        exc_info = errors.values()[0]
        exc_str = utils.xml_safe_utf8(exc_info[1])
        tb_str = ''.join(traceback.format_tb(exc_info[2]))
        utils.message_details_dialog('Could not load plugin: '
                                     '\n\n<i>%s</i>\n\n%s' \
                                         % (name, exc_str),
                                     tb_str, type=gtk.MESSAGE_ERROR)

    if len(found) == 0:
        debug('No plugins found at path: %s' % path)

    for plugin in found:
        # TODO: should we include the module name of the plugin to allow
        # for plugin namespaces or just assume that the plugin class
        # name is unique
        plugins[plugin.__class__.__name__] = plugin
 def handle_response(self, response):
     not_ok_msg = _('Are you sure you want to lose your changes?')
     if response == gtk.RESPONSE_OK or response in self.ok_responses:
         try:
             if self.presenter.dirty():
                 # commit_changes() will append the commited plants
                 # to self._committed
                 self.commit_changes()
         except DBAPIError, e:
             exc = traceback.format_exc()
             msg = _('Error committing changes.\n\n%s') % e.orig
             utils.message_details_dialog(msg, str(e), gtk.MESSAGE_ERROR)
             self.session.rollback()
             return False
         except Exception, e:
             msg = _('Unknown error when committing changes. See the '\
                   'details for more information.\n\n%s') \
                   % utils.xml_safe_utf8(e)
             debug(traceback.format_exc())
             utils.message_details_dialog(msg, traceback.format_exc(),
                                          gtk.MESSAGE_ERROR)
             self.session.rollback()
             return False
 def _post_loop():
     gtk.gdk.threads_enter()
     try:
         if isinstance(open_exc, err.DatabaseError):
             msg = _('Would you like to create a new Bauble database at ' \
                     'the current connection?\n\n<i>Warning: If there is '\
                     'already a database at this connection any existing '\
                     'data will be destroyed!</i>')
             if utils.yes_no_dialog(msg, yes_delay=2):
                 try:
                     db.create()
                     # db.create() creates all tables registered with
                     # the default metadata so the pluginmgr should be
                     # loaded after the database is created so we don't
                     # inadvertantly create tables from the plugins
                     pluginmgr.init()
                     # set the default connection
                     prefs[conn_default_pref] = conn_name
                 except Exception, e:
                     utils.message_details_dialog(utils.xml_safe_utf8(e),
                                                  traceback.format_exc(),
                                                  gtk.MESSAGE_ERROR)
                     error(e)
         else:
#        debug('SearchView.search(%s)' % text)
        error_msg = None
        error_details_msg = None
        self.session.close()
        # create a new session for each search...maybe we shouldn't
        # even have session as a class attribute
        self.session = db.Session()
        bold = '<b>%s</b>'
        results = []
        try:
            results = search.search(text, self.session)
        except ParseException, err:
            error_msg = _('Error in search string at column %s') % err.column
        except (BaubleError, AttributeError, Exception, SyntaxError), e:
            #debug(traceback.format_exc())
            error_msg = _('** Error: %s') % utils.xml_safe_utf8(e)
            error_details_msg = utils.xml_safe_utf8(traceback.format_exc())

        if error_msg:
            bauble.gui.show_error_box(error_msg, error_details_msg)
            return

        # not error
        utils.clear_model(self.results_view)
        self.set_infobox_from_row(None)
        statusbar = bauble.gui.widgets.statusbar
        sbcontext_id = statusbar.get_context_id('searchview.nresults')
        statusbar.pop(sbcontext_id)
        if len(results) == 0:
            model = gtk.ListStore(str)
            msg = bold % _('Couldn\'t find anything for search: "%s"') \
        # has a sequence doesn't update the sequence, we shortcut this
        # by setting the sequence manually to the max(column)+1
        col = None
        try:
            for table, filename in sorted_tables:
                for col in table.c:
                    utils.reset_sequence(col)
        except Exception, e:
            col_name = None
            try:
                col_name = col.name
            except Exception:
                pass
            msg = _('Error: Could not set the sequence for column: %s') \
                  % col_name
            utils.message_details_dialog(_(utils.xml_safe_utf8(msg)),
                                         traceback.format_exc(),
                                         type=gtk.MESSAGE_ERROR)


# TODO: we don't use the progress dialog any more but we'll leave this
# around to remind us when we support cancelling via the progress statusbar
#
#     def _cancel_import(self, *args):
#         '''
#         called by the progress dialog to cancel the current import
#         '''
#         msg = _('Are you sure you want to cancel importing?\n\n<i>All '
#                 'changes so far will be rolled back.</i>')
#         self.__pause = True
#         if utils.yes_no_dialog(msg, parent=self.__progress_dialog):
 def get_UnitID(self):
     return xml_safe_utf8(str(self.plant))
        last_handler = handler_cls()
    handler_view = last_handler.get_view()
    old_view = gui.get_view()
    if type(old_view) != type(handler_view) and handler_view:
        # remove the accel_group from the window if the previous view
        # had one
        if hasattr(old_view, 'accel_group'):
            gui.window.remove_accel_group(old_view.accel_group)
        # add the new view and its accel_group if it has one
        gui.set_view(handler_view)
        if hasattr(handler_view, 'accel_group'):
            gui.window.add_accel_group(handler_view.accel_group)
    try:
        last_handler(cmd, arg)
    except Exception, e:
        msg = utils.xml_safe_utf8(e)
        error('bauble.command_handler(): %s' % msg)
        utils.message_details_dialog(msg, traceback.format_exc(),
                                     gtk.MESSAGE_ERROR)


conn_default_pref = "conn.default"
conn_list_pref = "conn.list"

def main(uri=None):
    """
    Run the main Bauble application.

    :param uri:  the URI of the database to connect to.  For more information about database URIs see `<http://www.sqlalchemy.org/docs/05/dbengine.html#create-engine-url-arguments>`_
    :type uri: str
    """
    def update(self, row):
        '''
        '''
        self.table.foreach(self.table.remove)
        if not row.changes:
            return
        nrows = len(row.changes)
        self.table.resize(nrows, 2)
        date_format = prefs.prefs[prefs.date_format_pref]
        current_row = 0

        def _cmp(x, y):
            """
            Sort by change.date and then change._created.  If they are
            equal then removals sort before transfers.
            """
            if x.date < y.date:
                return -1
            elif x.date > y.date:
                return 1
            elif x.date == y.date and x._created < y._created:
                return -1
            elif x.date == y.date and x._created > y._created:
                return 1
            elif x.quantity < 0:
                return -1
            else:
                return 1

        for change in sorted(row.changes, cmp=_cmp, reverse=True):
            date = change.date.strftime(date_format)
            label = gtk.Label('%s:' % date)
            label.set_alignment(0, 0)
            self.table.attach(label, 0, 1, current_row, current_row+1,
                              xoptions=gtk.FILL)
            if change.to_location and change.from_location:
                s = '%(quantity)s Transferred from %(from_loc)s to %(to)s' % \
                    dict(quantity=change.quantity,
                         from_loc=change.from_location, to=change.to_location)
            elif change.quantity < 0:
                s = '%(quantity)s Removed from %(location)s' % \
                    dict(quantity=-change.quantity,
                         location=change.from_location)
            elif change.quantity > 0:
                s = '%(quantity)s Added to %(location)s' % \
                    dict(quantity=change.quantity, location=change.to_location)
            else:
                s = '%s: %s -> %s' % (change.quantity, change.from_location,
                                      change.to_location)
            if change.reason is not None:
                s += '\n%s' % change_reasons[change.reason]
            label = gtk.Label(s)
            label.set_alignment(0, .5)
            self.table.attach(label, 1, 2, current_row, current_row+1,
                              xoptions=gtk.FILL)
            current_row += 1
            if change.parent_plant:
                s = _('<i>Branched from %(plant)s</i>') % \
                    dict(plant=utils.xml_safe_utf8(change.parent_plant))
                label = gtk.Label()
                label.set_alignment(0, .5)
                label.set_markup(s)
                eb = gtk.EventBox()
                eb.add(label)
                self.table.attach(eb, 1, 2, current_row, current_row+1,
                                  xoptions=gtk.FILL)
                def on_clicked(widget, event, parent):
                    select_in_search_results(parent)
                utils.make_label_clickable(label, on_clicked,
                                           change.parent_plant)
                current_row += 1

        self.vbox.show_all()
    def extra_elements(self, unit):
        super(AccessionABCDAdapter, self).extra_elements(unit)
        if self.for_labels:
            if self.species.label_distribution:
                etree.SubElement(unit, 'distribution').text=\
                    self.species.label_distribution
            elif self.species.distribution:
                etree.SubElement(unit, 'distribution').text=\
                    self.species.distribution_str()

        if self.accession.source and self.accession.source.collection:
            collection = self.accession.source.collection
            utf8 = xml_safe_utf8
            gathering = ABCDElement(unit, 'Gathering')

            if collection.collectors_code:
                ABCDElement(gathering, 'Code',
                            text=utf8(collection.collectors_code))

            # TODO: get date pref for DayNumberBegin
            if collection.date:
                date_time = ABCDElement(gathering, 'DateTime')
                ABCDElement(date_time, 'DateText',
                            xml_safe_utf8(collection.date.isoformat()))

            if collection.collector:
                agents = ABCDElement(gathering, 'Agents')
                agent = ABCDElement(agents, 'GatheringAgent')
                ABCDElement(agent, 'AgentText', text=utf8(collection.collector))

            if collection.locale:
                ABCDElement(gathering, 'LocalityText',
                            text=utf8(collection.locale))

            if collection.region:
                named_areas = ABCDElement(gathering, 'NamedAreas')
                named_area = ABCDElement(named_areas, 'NamedArea')
                ABCDElement(named_area, 'AreaName',
                            text=utf8(collection.region))

            if collection.habitat:
                ABCDElement(gathering, 'AreaDetail',
                            text=utf8(collection.habitat))

            if collection.longitude or collection.latitude:
                site_coords = ABCDElement(gathering, 'SiteCoordinateSets')
                coord = ABCDElement(site_coords, 'SiteCoordinates')
                lat_long = ABCDElement(coord, 'CoordinatesLatLong')
                ABCDElement(lat_long, 'LongitudeDecimal',
                            text=utf8(collection.longitude))
                ABCDElement(lat_long, 'LatitudeDecimal',
                            text=utf8(collection.latitude))
                if collection.gps_datum:
                    ABCDElement(lat_long, 'SpatialDatum',
                                text=utf8(collection.gps_datum))
                if collection.geo_accy:
                    ABCDElement(coord, 'CoordinateErrorDistanceInMeters',
                                text=utf8(collection.geo_accy))

            if collection.elevation:
                altitude = ABCDElement(gathering, 'Altitude')
                if collection.elevation_accy:
                    text = '%sm (+/- %sm)' % (collection.elevation,
                                              collection.elevation_accy)
                else:
                    text = '%sm' % collection.elevation
                ABCDElement(altitude, 'MeasurementOrFactText', text=text)

            if collection.notes:
                ABCDElement(gathering, 'Notes', utf8(collection.notes))
 def get_DateLastEdited(self):
     return utils.xml_safe_utf8(self.plant._last_updated.isoformat())