def update(self, row): ''' update the expander :param row: the row to get thevalues from ''' syn_box = self.widgets.sp_synonyms_box # remove old labels syn_box.foreach(syn_box.remove) logger.debug(row.synonyms) from sqlalchemy.orm.session import object_session self.session = object_session(row) syn = self.session.query(SpeciesSynonym).filter( SpeciesSynonym.synonym_id == row.id).first() accepted = syn and syn.species logger.debug("species %s is synonym of %s and has synonyms %s" % (row, accepted, row.synonyms)) self.set_label(_("Synonyms")) # reset default value on_label_clicked = lambda l, e, syn: select_in_search_results(syn) if accepted is not None: self.set_label(_("Accepted name")) # create clickable label that will select the synonym # in the search results box = gtk.EventBox() label = gtk.Label() label.set_alignment(0, .5) label.set_markup(Species.str(accepted, markup=True, authors=True)) box.add(label) utils.make_label_clickable(label, on_label_clicked, accepted) syn_box.pack_start(box, expand=False, fill=False) self.show_all() self.set_sensitive(True) self.set_expanded(True) elif len(row.synonyms) == 0: self.set_sensitive(False) self.set_expanded(False) else: # remove all the children syn_box.foreach(syn_box.remove) for syn in row.synonyms: # create clickable label that will select the synonym # in the search results box = gtk.EventBox() label = gtk.Label() label.set_alignment(0, .5) label.set_markup(Species.str(syn, markup=True, authors=True)) box.add(label) utils.make_label_clickable(label, on_label_clicked, syn) syn_box.pack_start(box, expand=False, fill=False) self.show_all() self.set_sensitive(True) # TODO: get expanded state from prefs self.set_expanded(True)
def update(self, row): ''' update the expander :param row: the row to get thevalues from ''' syn_box = self.widgets.sp_synonyms_box # remove old labels syn_box.foreach(syn_box.remove) logger.debug(row.synonyms) from sqlalchemy.orm.session import object_session self.session = object_session(row) syn = self.session.query(SpeciesSynonym).filter( SpeciesSynonym.synonym_id == row.id).first() accepted = syn and syn.species logger.debug("species %s is synonym of %s and has synonyms %s" % (row, accepted, row.synonyms)) self.set_label(_("Synonyms")) # reset default value on_label_clicked = lambda l, e, syn: select_in_search_results(syn) if accepted is not None: self.set_label(_("Accepted name")) # create clickable label that will select the synonym # in the search results box = gtk.EventBox() label = gtk.Label() label.set_alignment(0, .5) label.set_markup(Species.str(accepted, markup=True, authors=True)) box.add(label) utils.make_label_clickable(label, on_label_clicked, accepted) syn_box.pack_start(box, expand=False, fill=False) self.show_all() self.set_sensitive(True) self.set_expanded(True) elif len(row.synonyms) == 0: self.set_sensitive(False) self.set_expanded(False) else: # remove all the children syn_box.foreach(syn_box.remove) for syn in row.synonyms: # create clickable label that will select the synonym # in the search results box = gtk.EventBox() label = gtk.Label() label.set_alignment(0, .5) label.set_markup(Species.str(syn, markup=True, authors=True)) box.add(label) utils.make_label_clickable(label, on_label_clicked, syn) syn_box.pack_start(box, expand=False, fill=False) self.show_all() self.set_sensitive(True) # TODO: get expanded state from prefs self.set_expanded(True)
def test_between_evaluate(self): 'use BETWEEN value and value' Family = self.Family Genus = self.Genus from bauble.plugins.plants.species_model import Species from bauble.plugins.garden.accession import Accession #from bauble.plugins.garden.location import Location #from bauble.plugins.garden.plant import Plant family2 = Family(epithet=u'family2') g2 = Genus(family=family2, epithet=u'genus2') f3 = Family(epithet=u'fam3', aggregate=u'agg.') g3 = Genus(family=f3, epithet=u'Ixora') sp = Species(epithet=u"coccinea", genus=g3) ac = Accession(species=sp, code=u'1979.0001') self.session.add_all([family2, g2, f3, g3, sp, ac]) self.session.commit() mapper_search = search.get_strategy('MapperSearch') self.assertTrue(isinstance(mapper_search, search.MapperSearch)) s = 'accession where code between "1978" and "1980"' results = mapper_search.search(s, self.session) self.assertEqual(results, set([ac])) s = 'accession where code between "1980" and "1980"' results = mapper_search.search(s, self.session) self.assertEqual(results, set())
def test_search_by_query22_underscore(self): """can use fields starting with an underscore""" import datetime Family = self.Family Genus = self.Genus from bauble.plugins.plants.species_model import Species from bauble.plugins.garden.accession import Accession from bauble.plugins.garden.location import Location from bauble.plugins.garden.plant import Plant family2 = Family(epithet=u'family2') g2 = Genus(family=family2, epithet=u'genus2') f3 = Family(epithet=u'fam3', aggregate=u'agg.') g3 = Genus(family=f3, epithet=u'Ixora') sp = Species(epithet=u"coccinea", genus=g3) ac = Accession(species=sp, code=u'1979.0001') lc = Location(name=u'loc1', code=u'loc1') pp = Plant(accession=ac, code=u'01', location=lc, quantity=1) pp._last_updated = datetime.datetime(2009, 2, 13) self.session.add_all([family2, g2, f3, g3, sp, ac, lc, pp]) self.session.commit() mapper_search = search.get_strategy('MapperSearch') self.assertTrue(isinstance(mapper_search, search.MapperSearch)) s = 'plant where _last_updated < |datetime|2000,1,1|' results = mapper_search.search(s, self.session) self.assertEqual(results, set()) s = 'plant where _last_updated > |datetime|2000,1,1|' results = mapper_search.search(s, self.session) self.assertEqual(results, set([pp]))
def __init__(self, model=None, parent=None): ''' :param model: a species instance or None :param parent: the parent window or None ''' if model is None: model = Species() super(SpeciesEditorMenuItem, self).__init__(model, parent) if not parent and bauble.gui: parent = bauble.gui.window self.parent = parent self._committed = [] view = SpeciesEditorView(parent=self.parent) self.presenter = SpeciesEditorPresenter(self.model, view) ## I do not follow this: we have a MVP model, but also an extra ## 'Editor' thing and is it stealing functionality from either the ## view or the presenter? self.view = view # add quick response keys self.attach_response(view.get_window(), gtk.RESPONSE_OK, 'Return', gtk.gdk.CONTROL_MASK) self.attach_response(view.get_window(), self.RESPONSE_OK_AND_ADD, 'k', gtk.gdk.CONTROL_MASK) self.attach_response(view.get_window(), self.RESPONSE_NEXT, 'n', gtk.gdk.CONTROL_MASK) # set default focus if self.model.genus is None: view.widgets.sp_genus_entry.grab_focus() else: view.widgets.sp_species_entry.grab_focus()
def add_species_callback(genera): session = db.Session() genus = session.merge(genera[0]) from bauble.plugins.plants.species_editor import edit_species result = edit_species(model=Species(genus=genus)) is not None session.close() return result
def refresh_fullname_label(self, widget=None): ''' set the value of sp_fullname_label to either '--' if there is a problem or to the name of the string returned by Species.str ''' logger.debug("SpeciesEditorPresenter:refresh_fullname_label %s" % widget) if len(self.problems) > 0 or self.model.genus is None: self.view.set_label('sp_fullname_label', '--') return sp_str = Species.str(self.model, markup=True, authors=True) self.view.set_label('sp_fullname_label', sp_str) if self.model.genus is not None: genus = self.model.genus epithet = self.view.widget_get_value('sp_species_entry') omonym = self.session.query( Species).filter( Species.genus == genus, Species.sp == epithet ).first() logger.debug("looking for %s %s, found %s" % (genus, epithet, omonym)) if omonym in [None, self.model]: ## should not warn, so check warning and remove if self.omonym_box is not None: self.view.remove_box(self.omonym_box) self.omonym_box = None elif self.omonym_box is None: # should warn, but not twice msg = _("This binomial name is already in your collection" ", as %s.\n\n" "Are you sure you want to insert it again?") % \ Species.str(omonym, authors=True) def on_response(button, response): self.view.remove_box(self.omonym_box) self.omonym_box = None if response: logger.warning('yes') else: self.view.widget_set_value('sp_species_entry', '') box = self.omonym_box = ( self.view.add_message_box(utils.MESSAGE_BOX_YESNO)) box.message = msg box.on_response = on_response box.show() self.view.add_box(box)
def setUp(self): super(BinomialSearchTests, self).setUp() db.engine.execute('delete from genus') db.engine.execute('delete from family') from bauble.plugins.plants.family import Family from bauble.plugins.plants.genus import Genus from bauble.plugins.plants.species import Species f1 = Family(epithet=u'family1', aggregate=u'agg.') g1 = Genus(family=f1, epithet=u'genus1') f2 = Family(epithet=u'family2') g2 = Genus(family=f2, epithet=u'genus2') f3 = Family(epithet=u'fam3', aggregate=u'agg.') g3 = Genus(family=f3, epithet=u'Ixora') sp = Species(epithet=u"coccinea", genus=g3) sp2 = Species(epithet=u"peruviana", genus=g3) sp3 = Species(epithet=u"chinensis", genus=g3) g4 = Genus(family=f3, epithet=u'Pachystachys') sp4 = Species(epithet=u'coccinea', genus=g4) self.session.add_all([f1, f2, g1, g2, f3, g3, sp, sp2, sp3, g4, sp4]) self.session.commit() self.ixora, self.ic, self.pc = g3, sp, sp4
def setUp(self): super(AggregatingFunctions, self).setUp() db.engine.execute('delete from genus') db.engine.execute('delete from family') db.engine.execute('delete from species') db.engine.execute('delete from accession') from bauble.plugins.plants import Family, Genus, Species f1 = Family(epithet=u'Rutaceae', aggregate=u'') g1 = Genus(family=f1, epithet=u'Citrus') sp1 = Species(epithet=u"medica", genus=g1) sp2 = Species(epithet=u"maxima", genus=g1) sp3 = Species(epithet=u"aurantium", genus=g1) f2 = Family(epithet=u'Sapotaceae') g2 = Genus(family=f2, epithet=u'Manilkara') sp4 = Species(epithet=u'zapota', genus=g2) sp5 = Species(epithet=u'zapotilla', genus=g2) g3 = Genus(family=f2, epithet=u'Pouteria') sp6 = Species(epithet=u'stipitata', genus=g3) f3 = Family(epithet=u'Musaceae') g4 = Genus(family=f3, epithet=u'Musa') self.session.add_all( [f1, f2, f3, g1, g2, g3, g4, sp1, sp2, sp3, sp4, sp5, sp6]) self.session.commit()
def test_cultivar_also_matched(self): mapper_search = search.get_strategy('MapperSearch') self.assertTrue(isinstance(mapper_search, search.MapperSearch)) from bauble.plugins.plants.species import Species from bauble.plugins.plants.genus import Genus g3 = self.session.query(Genus).filter(Genus.epithet == u'Ixora').one() sp5 = Species(epithet=u"coccinea", genus=g3, infrasp1_rank=u'cv.', infrasp1=u'Nora Grant') self.session.add_all([sp5]) self.session.commit() s = 'Ixora coccinea' # matches I.coccinea and Nora Grant results = mapper_search.search(s, self.session) self.assertEqual(results, set([self.ic, sp5]))
def on_remove_button_clicked(self, button, data=None): """ removes the currently selected synonym from the list of synonyms for this species """ # TODO: maybe we should only ask 'are you sure' if the selected value # is an instance, this means it will be deleted from the database tree = self.view.widgets.sp_syn_treeview path, col = tree.get_cursor() tree_model = tree.get_model() value = tree_model[tree_model.get_iter(path)][0] s = Species.str(value.synonym, markup=True) msg = ( "Are you sure you want to remove %s as a synonym to the " "current species?\n\n<i>Note: This will not remove the species " "%s from the database.</i>" % (s, s) ) if not utils.yes_no_dialog(msg, parent=self.view.get_window()): return tree_model.remove(tree_model.get_iter(path)) self.model.synonyms.remove(value.synonym) utils.delete_or_expunge(value) # TODO: ** important ** this doesn't respect any unique # contraints on the species for synonyms and allow a # species to have another species as a synonym multiple # times...see below # TODO: using session.flush here with an argument is # deprecated in SA 0.5 and will probably removed in SA # 0.6...but how do we only flush the one value..unless we # create a new session, merge it, commit that session, # close it and then refresh the same object in # self.session # make the change in synonym immediately available so that if # we try to add the same species again we don't break the # SpeciesSynonym UniqueConstraint # tmp_session = db.Session() # tmp_value = tmp.session.merge(value) # tmp.session.commit() # tmp.session.close() # self.session.refresh(value) # self.session.flush([value]) self._dirty = True self.parent_ref().refresh_sensitivity()
def on_remove_button_clicked(self, button, data=None): ''' removes the currently selected synonym from the list of synonyms for this species ''' # TODO: maybe we should only ask 'are you sure' if the selected value # is an instance, this means it will be deleted from the database tree = self.view.widgets.sp_syn_treeview path, col = tree.get_cursor() tree_model = tree.get_model() value = tree_model[tree_model.get_iter(path)][0] s = Species.str(value.synonym, markup=True) msg = 'Are you sure you want to remove %s as a synonym to the ' \ 'current species?\n\n<i>Note: This will not remove the species '\ '%s from the database.</i>' % (s, s) if not utils.yes_no_dialog(msg, parent=self.view.get_window()): return tree_model.remove(tree_model.get_iter(path)) self.model.synonyms.remove(value.synonym) utils.delete_or_expunge(value) # TODO: ** important ** this doesn't respect any unique # contraints on the species for synonyms and allow a # species to have another species as a synonym multiple # times...see below # TODO: using session.flush here with an argument is # deprecated in SA 0.5 and will probably removed in SA # 0.6...but how do we only flush the one value..unless we # create a new session, merge it, commit that session, # close it and then refresh the same object in # self.session # make the change in synonym immediately available so that if # we try to add the same species again we don't break the # SpeciesSynonym UniqueConstraint # tmp_session = db.Session() # tmp_value = tmp.session.merge(value) # tmp.session.commit() # tmp.session.close() # self.session.refresh(value) #self.session.flush([value]) self._dirty = True self.parent_ref().refresh_sensitivity()
def test_search_by_query_binomial(self): """can use genus_species binomial identification""" raise SkipTest("related to issue 192") Family = self.Family Genus = self.Genus from bauble.plugins.plants.species_model import Species family2 = Family(family=u'family2') g2 = Genus(family=family2, genus=u'genus2') f3 = Family(family=u'fam3', qualifier=u's. lat.') g3 = Genus(family=f3, genus=u'Ixora') sp = Species(sp=u"coccinea", genus=g3) self.session.add_all([family2, g2, f3, g3, sp]) self.session.commit() mapper_search = search.get_strategy('MapperSearch') self.assertTrue(isinstance(mapper_search, search.MapperSearch)) s = '"Ixora coccinea"' results = mapper_search.search(s, self.session) self.assertEqual(results, set([sp]))
def test_search_by_query_vernacural(self): """can find species by vernacular name""" Family = self.Family Genus = self.Genus from bauble.plugins.plants.species_model import Species from bauble.plugins.plants.species_model import VernacularName family2 = Family(epithet=u'family2') g2 = Genus(family=family2, epithet=u'genus2') f3 = Family(epithet=u'fam3', aggregate=u'agg.') g3 = Genus(family=f3, epithet=u'Ixora') sp = Species(epithet=u"coccinea", genus=g3) vn = VernacularName(name=u"coral rojo", language=u"es", species=sp) self.session.add_all([family2, g2, f3, g3, sp, vn]) self.session.commit() mapper_search = search.get_strategy('MapperSearch') self.assertTrue(isinstance(mapper_search, search.MapperSearch)) s = "rojo" results = mapper_search.search(s, self.session) self.assertEqual(results, set([sp]))
utils.message_details_dialog(msg, traceback.format_exc(), gtk.MESSAGE_ERROR) return False elif self.presenter.is_dirty() and utils.yes_no_dialog(not_ok_msg) \ or not self.presenter.is_dirty(): self.session.rollback() self.view.close_boxes() return True else: return False more_committed = None if response == self.RESPONSE_NEXT: self.presenter.cleanup() e = SpeciesEditorMenuItem( Species(genus=self.model.genus), self.parent) more_committed = e.start() elif response == self.RESPONSE_OK_AND_ADD: from bauble.plugins.garden.accession import ( AccessionEditor, Accession) e = AccessionEditor(Accession(species=self.model), parent=self.parent) more_committed = e.start() if more_committed is not None: if isinstance(more_committed, list): self._committed.extend(more_committed) else: self._committed.append(more_committed) self.view.close_boxes()
logger.debug(traceback.format_exc()) utils.message_details_dialog(msg, traceback.format_exc(), gtk.MESSAGE_ERROR) return False elif self.presenter.is_dirty() and utils.yes_no_dialog(not_ok_msg) \ or not self.presenter.is_dirty(): self.session.rollback() self.view.close_boxes() return True else: return False more_committed = None if response == self.RESPONSE_NEXT: self.presenter.cleanup() e = SpeciesEditorMenuItem(Species(genus=self.model.genus), self.parent) more_committed = e.start() elif response == self.RESPONSE_OK_AND_ADD: from bauble.plugins.garden.accession import (AccessionEditor, Accession) e = AccessionEditor(Accession(species=self.model), parent=self.parent) more_committed = e.start() if more_committed is not None: if isinstance(more_committed, list): self._committed.extend(more_committed) else: self._committed.append(more_committed)
return True else: # we should never really even get here since we would have # to hit something besides "OK" and the above elif should # handle all the possible cases return False # respond to responses more_committed = None if response == self.RESPONSE_NEXT: self.presenter.cleanup() model = Genus(family=self.model.family) e = GenusEditor(model=model, parent=self.parent) more_committed = e.start() elif response == self.RESPONSE_OK_AND_ADD: sp = Species(genus=self.model) more_committed = edit_species(model=sp, parent=self.parent) if more_committed is not None: if isinstance(more_committed, list): self._committed.extend(more_committed) else: self._committed.append(more_committed) return True def start(self): if self.session.query(Family).count() == 0: msg = _('You must first add or import at least one Family into ' 'the database before you can add plants.') utils.message_dialog(msg)