def _on_add_tag_activated(*args): # get the selection from the search view # TODO: would be better if we could set the sensitivity of the menu # item depending on if something was selected in the search view, this # means we need to add more hooks to the search view or include the # tag plugin into the search view view = bauble.gui.get_view() if isinstance(view, SearchView): values = view.get_selected_values() if len(values) == 0: msg = _('Nothing selected') utils.message_dialog(msg) return # right now we can only tag a single item at a time, if we did # the f-spot style quick tagging then it would be easier to handle # multiple tags at a time, we could do it we would just have to find # the common tags for each of the selected items and then select them # but grey them out so you can see that some of the items have that # tag but not all of them tagitem = TagItemGUI(values) tagitem.start() view.update_bottom_notebook() else: msg = _('In order to tag an item you must first search for ' 'something and select one of the results.') bauble.gui.show_message_box(msg)
def add_meta(self, domain, cls, properties): """Add a domain to the search space an example of domain is a database table, where the properties would be the table columns to consider in the search. continuing this example, a record is be selected if any of the fields matches the searched value. :param domain: a string, list or tuple of domains that will resolve a search string to cls. domain act as a shorthand to the class name. :param cls: the class the domain will resolve to :param properties: a list of string names of the properties to search by default """ logger.debug('%s.add_meta(%s, %s, %s)' % (self, domain, cls, properties)) check(isinstance(properties, list), _('MapperSearch.add_meta(): ' 'default_columns argument must be list')) check(len(properties) > 0, _('MapperSearch.add_meta(): ' 'default_columns argument cannot be empty')) if isinstance(domain, (list, tuple)): self._domains[domain[0]] = cls, properties for d in domain[1:]: self._shorthand[d] = domain[0] else: self._domains[domain] = cls, properties self._properties[cls] = properties
def conservation(self): '''the IUCN conservation status of this taxon, or DD one of: EX, RE, CR, EN, VU, NT, LC, DD not enforced by the software in v1.0.x ''' { 'EX': _('Extinct (EX)'), 'EW': _('Extinct Wild (EW)'), 'RE': _('Regionally Extinct (RE)'), 'CR': _('Critically Endangered (CR)'), 'EN': _('Endangered (EN)'), 'VU': _('Vulnerable (VU)'), 'NT': _('Near Threatened (NT)'), 'LV': _('Least Concern (LC)'), 'DD': _('Data Deficient (DD)'), 'NE': _('Not Evaluated (NE)') } notes = [ i.note for i in self.notes if i.category and i.category.upper() == u'IUCN' ] return (notes + ['DD'])[0]
def start(self, filename=None, plants=None): if filename is None: # no filename, ask the user d = gtk.FileChooserDialog(_("Choose a file to export to..."), None, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) response = d.run() filename = d.get_filename() d.destroy() if response != gtk.RESPONSE_ACCEPT or filename is None: return if plants: nplants = len(plants) else: nplants = db.Session().query(Plant).count() if nplants > 3000: msg = _('You are exporting %(nplants)s plants to ABCD format. ' 'Exporting this many plants may take several minutes. ' '\n\n<i>Would you like to continue?</i>') \ % ({'nplants': nplants}) if not utils.yes_no_dialog(msg): return self.run(filename, plants)
def __init__(self, *args, **kwargs): super(SchemaBrowser, self).__init__(*args, **kwargs) self.props.spacing = 10 # WARNING: this is a hack from MapperSearch self.domain_map = {} self.domain_map = MapperSearch.get_domain_classes().copy() frame = gtk.Frame(_("Search Domain")) self.pack_start(frame, expand=False, fill=False) self.table_combo = gtk.combo_box_new_text() frame.add(self.table_combo) for key in sorted(self.domain_map.keys()): self.table_combo.append_text(key) self.table_combo.connect('changed', self.on_table_combo_changed) self.prop_tree = gtk.TreeView() self.prop_tree.set_headers_visible(False) cell = gtk.CellRendererText() column = gtk.TreeViewColumn(_("Property"), cell) self.prop_tree.append_column(column) column.add_attribute(cell, 'text', 0) self.prop_tree.connect('test_expand_row', self.on_row_expanded) frame = gtk.Frame(_('Domain Properties')) sw = gtk.ScrolledWindow() sw.add(self.prop_tree) frame.add(sw) self.pack_start(frame, expand=True, fill=True)
def remove_callback(values): """ The callback function to remove a species from the species context menu. """ from bauble.plugins.garden.accession import Accession session = db.Session() species = values[0] if isinstance(species, VernacularName): species = species.species nacc = session.query(Accession).filter_by(species_id=species.id).count() safe_str = utils.xml_safe(str(species)) if nacc > 0: msg = _( "The species <i>%(species)s</i> has %(num_accessions)s " "accessions. Are you sure you want remove it?" ) % dict(species=safe_str, num_accessions=nacc) else: msg = _("Are you sure you want to remove the species <i>%s</i>?") % safe_str if not utils.yes_no_dialog(msg): return try: obj = session.query(Species).get(species.id) session.delete(obj) session.commit() except Exception, e: msg = _("Could not delete.\n\n%s") % utils.xml_safe(e) utils.message_details_dialog(msg, traceback.format_exc(), type=gtk.MESSAGE_ERROR)
def _reset_tags_menu(): tags_menu = gtk.Menu() add_tag_menu_item = gtk.MenuItem(_('Tag Selection')) add_tag_menu_item.connect('activate', _on_add_tag_activated) accel_group = gtk.AccelGroup() bauble.gui.window.add_accel_group(accel_group) add_tag_menu_item.add_accelerator('activate', accel_group, ord('T'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) tags_menu.append(add_tag_menu_item) #manage_tag_item = gtk.MenuItem('Manage Tags') #tags_menu.append(manage_tag_item) tags_menu.append(gtk.SeparatorMenuItem()) session = db.Session() query = session.query(Tag) try: for tag in query: item = gtk.MenuItem(tag.tag, use_underline=False) item.connect("activate", _tag_menu_item_activated, tag.tag) tags_menu.append(item) except Exception: logger.debug(traceback.format_exc()) msg = _('Could not create the tags menus') utils.message_details_dialog(msg, traceback.format_exc(), gtk.MESSAGE_ERROR) global _tags_menu_item if _tags_menu_item is None: _tags_menu_item = bauble.gui.add_menu(_("Tags"), tags_menu) else: _tags_menu_item.remove_submenu() _tags_menu_item.set_submenu(tags_menu) _tags_menu_item.show_all() session.close()
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(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(e) utils.message_details_dialog(msg, traceback.format_exc(), type=gtk.MESSAGE_ERROR)
def format(objs, **kwargs): template_filename = kwargs["template"] use_private = kwargs.get("private", True) if not template_filename: msg = _("Please select a template.") utils.message_dialog(msg, gtk.MESSAGE_WARNING) return False template = Template(filename=template_filename, input_encoding="utf-8", output_encoding="utf-8") session = db.Session() values = map(session.merge, objs) report = template.render(values=values) session.close() # assume the template is the same file type as the output file head, ext = os.path.splitext(template_filename) fd, filename = tempfile.mkstemp(suffix=ext) os.write(fd, report) os.close(fd) try: desktop.open(filename) except OSError: utils.message_dialog( _("Could not open the report with the " "default program. You can open the " "file manually at %s") % filename ) return report
def handle_response(self, response): """ @return: return True if the editor is ready to be closed, 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.is_dirty(): self.commit_changes() self._committed.append(self.model) except DBAPIError, e: msg = _('Error committing changes.\n\n%s') % \ utils.xml_safe(e.orig) logger.debug(traceback.format_exc()) 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(e) logger.debug(traceback.format_exc()) utils.message_details_dialog(msg, traceback.format_exc(), gtk.MESSAGE_ERROR) return False
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(e) utils.message_dialog(msg, gtk.MESSAGE_ERROR) return
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.is_dirty() and commit: self.commit_changes() except DBAPIError, e: msg = _('Error committing changes.\n\n%s') % \ utils.xml_safe(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(e) logger.debug(traceback.format_exc()) utils.message_details_dialog(msg, traceback.format_exc(), gtk.MESSAGE_ERROR) self.session.rollback() return False
def __export_task(self, path): # if not os.path.exists(path): # raise ValueError("CSVExporter: path does not exist.\n" + path) filename_template = os.path.join(path, "%s.txt") # timeout = tasklet.WaitForTimeout(12) steps_so_far = 0 ntables = 0 for table in db.metadata.sorted_tables: ntables += 1 filename = filename_template % table.name if os.path.exists(filename): msg = _( "Export file <b>%(filename)s</b> for " "<b>%(table)s</b> table already exists.\n\n<i>Would " "you like to continue?</i>" ) % {"filename": filename, "table": table.name} if utils.yes_no_dialog(msg): return def replace(s): if isinstance(s, (str, unicode)): s.replace("\n", "\\n") return s def write_csv(filename, rows): f = open(filename, "wb") writer = UnicodeWriter(f, quotechar=QUOTE_CHAR, quoting=QUOTE_STYLE) writer.writerows(rows) f.close() update_every = 30 for table in db.metadata.sorted_tables: filename = filename_template % table.name steps_so_far += 1 fraction = float(steps_so_far) / float(ntables) pb_set_fraction(fraction) msg = _("exporting %(table)s table to %(filename)s") % {"table": table.name, "filename": filename} bauble.task.set_message(msg) logger.info("exporting %s" % table.name) # get the data results = table.select().execute().fetchall() # create empty files with only the column names if len(results) == 0: write_csv(filename, [table.c.keys()]) yield continue rows = [] rows.append(table.c.keys()) # append col names ctr = 0 for row in results: values = map(replace, row.values()) rows.append(values) if ctr == update_every: yield ctr = 0 ctr += 1 write_csv(filename, rows)
def start(self): ''' ''' # get the select results from the search view from bauble.view import SearchView view = bauble.gui.get_view() if not isinstance(view, SearchView): utils.message_dialog(_('Search for something first.')) return model = view.results_view.get_model() if model is None: utils.message_dialog(_('Search for something first.')) return bauble.gui.set_busy(True) ok = False try: while True: dialog = ReportToolDialog() formatter, settings = dialog.start() if formatter is None: break ok = formatter.format([row[0] for row in model], **settings) if ok: break except AssertionError, e: logger.debug(e) logger.debug(traceback.format_exc()) parent = None if hasattr(self, 'view') and hasattr(self.view, 'dialog'): parent = self.view.dialog utils.message_details_dialog(str(e), traceback.format_exc(), gtk.MESSAGE_ERROR, parent=parent)
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(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(e) logger.debug(traceback.format_exc()) utils.message_details_dialog(msg, traceback.format_exc(), gtk.MESSAGE_ERROR) self.session.rollback() return False
def remove_callback(values): """ The callback function to remove a species from the species context menu. """ from bauble.plugins.garden.accession import Accession session = db.Session() species = values[0] if isinstance(species, VernacularName): species = species.species nacc = session.query(Accession).filter_by(species_id=species.id).count() safe_str = utils.xml_safe(str(species)) if nacc > 0: msg = _('The species <i>%(species)s</i> has %(num_accessions)s ' 'accessions. Are you sure you want remove it?') \ % dict(species=safe_str, num_accessions=nacc) else: msg = _("Are you sure you want to remove the species <i>%s</i>?") \ % safe_str if not utils.yes_no_dialog(msg): return try: obj = session.query(Species).get(species.id) session.delete(obj) session.commit() except Exception, e: msg = _('Could not delete.\n\n%s') % utils.xml_safe(e) utils.message_details_dialog(msg, traceback.format_exc(), type=gtk.MESSAGE_ERROR)
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(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(e) utils.message_details_dialog(msg, traceback.format_exc(), type=gtk.MESSAGE_ERROR)
def add_meta(self, domain, cls, properties): """Add a domain to the search space an example of domain is a database table, where the properties would be the table columns to consider in the search. continuing this example, a record is be selected if any of the fields matches the searched value. :param domain: a string, list or tuple of domains that will resolve a search string to cls. domain act as a shorthand to the class name. :param cls: the class the domain will resolve to :param properties: a list of string names of the properties to search by default """ logger.debug('%s.add_meta(%s, %s, %s)' % (self, domain, cls, properties)) check( isinstance(properties, list), _('MapperSearch.add_meta(): ' 'default_columns argument must be list')) check( len(properties) > 0, _('MapperSearch.add_meta(): ' 'default_columns argument cannot be empty')) if isinstance(domain, (list, tuple)): self._domains[domain[0]] = cls, properties for d in domain[1:]: self._shorthand[d] = domain[0] else: self._domains[domain] = cls, properties self._properties[cls] = properties
def __init__(self, values, empty_to_none=False, strict=True, translations={}, **kwargs): """ : param values: A list of valid values for column. :param empty_to_none: Treat the empty string '' as None. None must be in the values list in order to set empty_to_none=True. :param strict: :param translations: A dictionary of values->translation """ # create the translations from the values and set those from # the translations argument, this way if some translations are # missing then the translation will be the same as value self.translations = dict((v, v) for v in values) for key, value in translations.iteritems(): self.translations[key] = value if values is None or len(values) is 0: raise EnumError(_('Enum requires a list of values')) if empty_to_none and None not in values: raise EnumError(_('You have configured empty_to_none=True but ' 'None is not in the values lists')) self.values = values[:] self.strict = strict self.empty_to_none = empty_to_none # the length of the string/unicode column should be the # longest string in values size = max([len(v) for v in values if v is not None]) super(Enum, self).__init__(size, **kwargs)
def format(objs, **kwargs): template_filename = kwargs['template'] use_private = kwargs.get('private', True) if not template_filename: msg = _('Please select a template.') utils.message_dialog(msg, gtk.MESSAGE_WARNING) return False template = Template(filename=template_filename, input_encoding='utf-8', output_encoding='utf-8') session = db.Session() values = map(session.merge, objs) report = template.render(values=values) session.close() # assume the template is the same file type as the output file head, ext = os.path.splitext(template_filename) fd, filename = tempfile.mkstemp(suffix=ext) os.write(fd, report) os.close(fd) try: desktop.open(filename) except OSError: utils.message_dialog( _('Could not open the report with the ' 'default program. You can open the ' 'file manually at %s') % filename) return report
class ABCDExportTool(pluginmgr.Tool): category = _("Export") label = _("ABCD") @classmethod def start(cls): ABCDExporter().start()
def start(self): """ Show the connection manager. """ self.create_gui() conn_list = prefs.prefs[bauble.conn_list_pref] if conn_list is None or len(conn_list.keys()) == 0: msg = _('You don\'t have any connections in your connection ' 'list.\nClose this message and click on "Add" to create ' 'a new connection.') utils.message_dialog(msg) else: self.set_active_connection_by_name(self.default_name) self._dirty = False self._error = True name = None uri = None while name is None or self._error: response = self.dialog.run() if response == gtk.RESPONSE_OK: name = self._get_connection_name() uri = self._get_connection_uri() if name is None: msg = _('You have to choose or create a new connection ' 'before you can connect to the database.') utils.message_dialog(msg) else: name = uri = None break ## now make sure the pictures dir contains a thumbs subdir path = os.path.sep.join( (prefs.prefs[prefs.picture_root_pref], 'thumbs')) try: logger.debug("checking presence of thumbs dir") os.makedirs(path) except OSError: if not os.path.isdir(path): logger.debug("something wrong in thumbs dir") raise # have to remove the cell_data_func to avoid a cyclical # reference which would cause the ConnectionManager to not get # garbage collected cell = self.type_combo.get_cells()[0] self.type_combo.set_cell_data_func(cell, None) self.type_combo.clear() self.name_combo.clear() # just to be sure let's destroy the dialog upfront and delete # the params box self.dialog.destroy() del self.params_box obj = utils.gc_objects_by_type(CMParamsBox) if obj: logger.info('ConnectionManager.start(): param box leaked: %s' % obj) return name, uri
def init(force=False): """ Initialize the plugin manager. 1. Check for and install any plugins in the plugins dict that aren't in the registry. 2. Call each init() for each plugin the registry in order of dependency 3. Register the command handlers in the plugin's commands[] NOTE: This should be called after after Bauble has established a connection to a database with db.open() """ logger.debug('bauble.pluginmgr.init()') # ****** # NOTE: Be careful not to keep any references to # PluginRegistry open here as it will cause a deadlock if you try # to create a new database. For example, don't query the # PluginRegistry with a session without closing the session. # ****** # search for plugins that are in the plugins dict but not in the registry registered = plugins.values() logger.debug('registered plugins: %s' % plugins) try: # try to access the plugin registry, if the table does not exist # then it might mean that we are opening a pre 0.9 database, in this # case we just assume all the plugins have been installed and # registered, this might be the right thing to do but at least it # allows you to connect to a pre bauble 0.9 database and use it to # upgrade to a >=0.9 database registered_names = PluginRegistry.names() not_installed = [ p for n, p in plugins.iteritems() if n not in registered_names ] if len(not_installed) > 0: msg = _('The following plugins were not found in the plugin ' 'registry:\n\n<b>%s</b>\n\n' '<i>Would you like to install them now?</i>' % ', '.join([p.__class__.__name__ for p in not_installed])) if force or utils.yes_no_dialog(msg): install([p for p in not_installed]) # sort plugins in the registry by their dependencies not_registered = [] for name in PluginRegistry.names(): try: registered.append(plugins[name]) except KeyError, e: logger.debug("could not find '%s' plugin. " "removing from database" % e) not_registered.append(utils.utf8(name)) PluginRegistry.remove(name=name) if not_registered: msg = _('The following plugins are in the registry but ' 'could not be loaded:\n\n%(plugins)s' % {'plugins': utils.utf8(', '.join(sorted(not_registered)))}) utils.message_dialog(utils.xml_safe(msg), type=gtk.MESSAGE_WARNING)
def __export_task(self, path): filename_template = os.path.join(path, "%s.txt") steps_so_far = 0 ntables = 0 for table in db.metadata.sorted_tables: ntables += 1 filename = filename_template % table.name if os.path.exists(filename): msg = _('Export file <b>%(filename)s</b> for ' '<b>%(table)s</b> table already exists.\n\n<i>Would ' 'you like to continue?</i>')\ % {'filename': filename, 'table': table.name} if utils.yes_no_dialog(msg): return def replace(s): if isinstance(s, (str, unicode)): s.replace('\n', '\\n') return s def write_csv(filename, rows): f = open(filename, 'wb') writer = UnicodeWriter(f, quotechar=QUOTE_CHAR, quoting=QUOTE_STYLE) writer.writerows(rows) f.close() update_every = 30 for table in db.metadata.sorted_tables: filename = filename_template % table.name steps_so_far += 1 fraction = float(steps_so_far) / float(ntables) pb_set_fraction(fraction) msg = _('exporting %(table)s table to %(filename)s')\ % {'table': table.name, 'filename': filename} bauble.task.set_message(msg) logger.info("exporting %s" % table.name) # get the data results = table.select().execute().fetchall() # create empty files with only the column names if len(results) == 0: write_csv(filename, [table.c.keys()]) yield continue rows = [] rows.append(table.c.keys()) # append col names ctr = 0 for row in results: values = map(replace, row.values()) rows.append(values) if ctr == update_every: yield ctr = 0 ctr += 1 write_csv(filename, rows)
def create_registry_view(self): #from bauble.pluginmgr import Registry from bauble.pluginmgr import PluginRegistry session = db.Session() plugins = session.query(PluginRegistry.name, PluginRegistry.version) tree = self.create_tree([_('Name'), _('Version')], plugins) session.close() return tree
class XMLExportTool(pluginmgr.Tool): category = _("Export") label = _("XML") @classmethod def start(cls): c = XMLExporter() c.start()
class CSVExportTool(pluginmgr.Tool): category = _('Export') label = _('Comma Separated Value') @classmethod def start(cls): c = CSVExporter() c.start()
def __export_task(self, path): filename_template = os.path.join(path, "%s.txt") steps_so_far = 0 ntables = 0 for table in db.metadata.sorted_tables: ntables += 1 filename = filename_template % table.name if os.path.exists(filename): msg = _('Export file <b>%(filename)s</b> for ' '<b>%(table)s</b> table already exists.\n\n<i>Would ' 'you like to continue?</i>')\ % {'filename': filename, 'table': table.name} if utils.yes_no_dialog(msg): return def replace(s): if isinstance(s, (str, unicode)): s.replace('\n', '\\n') return s def write_csv(filename, rows): f = open(filename, 'wb') writer = UnicodeWriter(f, quotechar=QUOTE_CHAR, quoting=QUOTE_STYLE) writer.writerows(rows) f.close() update_every = 30 for table in db.metadata.sorted_tables: filename = filename_template % table.name steps_so_far += 1 fraction = float(steps_so_far)/float(ntables) pb_set_fraction(fraction) msg = _('exporting %(table)s table to %(filename)s')\ % {'table': table.name, 'filename': filename} bauble.task.set_message(msg) logger.info("exporting %s" % table.name) # get the data results = table.select().execute().fetchall() # create empty files with only the column names if len(results) == 0: write_csv(filename, [table.c.keys()]) yield continue rows = [] rows.append(table.c.keys()) # append col names ctr = 0 for row in results: values = map(replace, row.values()) rows.append(values) if ctr == update_every: yield ctr = 0 ctr += 1 write_csv(filename, rows)
def init(force=False): """ Initialize the plugin manager. 1. Check for and install any plugins in the plugins dict that aren't in the registry. 2. Call each init() for each plugin the registry in order of dependency 3. Register the command handlers in the plugin's commands[] NOTE: This is called after after Ghini has created the GUI and established a connection to a database with db.open() """ logger.debug('bauble.pluginmgr.init()') # ****** # NOTE: Be careful not to keep any references to # PluginRegistry open here as it will cause a deadlock if you try # to create a new database. For example, don't query the # PluginRegistry with a session without closing the session. # ****** # search for plugins that are in the plugins dict but not in the registry registered = plugins.values() logger.debug('registered plugins: %s' % plugins) try: # try to access the plugin registry, if the table does not exist # then it might mean that we are opening a pre 0.9 database, in this # case we just assume all the plugins have been installed and # registered, this might be the right thing to do but at least it # allows you to connect to a pre bauble 0.9 database and use it to # upgrade to a >=0.9 database registered_names = PluginRegistry.names() not_installed = [p for n, p in plugins.iteritems() if n not in registered_names] if len(not_installed) > 0: msg = _('The following plugins were not found in the plugin ' 'registry:\n\n<b>%s</b>\n\n' '<i>Would you like to install them now?</i>') % \ ', '.join([p.__class__.__name__ for p in not_installed]) if force or utils.yes_no_dialog(msg): install([p for p in not_installed]) # sort plugins in the registry by their dependencies not_registered = [] for name in PluginRegistry.names(): try: registered.append(plugins[name]) except KeyError, e: logger.debug("could not find '%s' plugin. " "removing from database" % e) not_registered.append(utils.utf8(name)) PluginRegistry.remove(name=name) if not_registered: msg = _('The following plugins are in the registry but ' 'could not be loaded:\n\n%(plugins)s') % \ {'plugins': utils.utf8(', '.join(sorted(not_registered)))} utils.message_dialog(utils.xml_safe(msg), type=gtk.MESSAGE_WARNING)
def start_institution_editor(): glade_path = os.path.join(paths.lib_dir(), "plugins", "garden", "institution.glade") from bauble import prefs from bauble.editor import GenericEditorView, MockView if prefs.testing: view = MockView() else: view = GenericEditorView(glade_path, parent=None, root_widget_name="inst_dialog") view._tooltips = { "inst_name": _("The full name of the institution."), "inst_abbr": _("The standard abbreviation of the " "institution."), "inst_code": _("The intitution code should be unique among " "all institions."), "inst_contact": _("The name of the person to contact for " "information related to the institution."), "inst_tech": _( "The email address or phone number of the " "person to contact for technical " "information related to the institution." ), "inst_email": _("The email address of the institution."), "inst_tel": _("The telephone number of the institution."), "inst_fax": _("The fax number of the institution."), "inst_addr": _("The mailing address of the institition."), } o = Institution() inst_pres = InstitutionPresenter(o, view) response = inst_pres.start() if response == gtk.RESPONSE_OK: o.write() inst_pres.commit_changes() else: inst_pres.session.rollback() inst_pres.session.close()
class InstitutionEditorView(editor.GenericEditorView): _tooltips = {'inst_name': _('The full name of the institution.'), 'inst_abbr': _('The standard abbreviation of the ' 'institution.'), 'inst_code': _('The intitution code should be unique among ' 'all institions.'), 'inst_contact': _('The name of the person to contact for ' 'information related to the institution.'), 'inst_tech': _('The email address or phone number of the ' 'person to contact for technical ' 'information related to the institution.'), 'inst_email': _('The email address of the institution.'), 'inst_tel': _('The telephone number of the institution.'), 'inst_fax': _('The fax number of the institution.'), 'inst_addr': _('The mailing address of the institition.') } def __init__(self, parent=None): filename = os.path.join(paths.lib_dir(), 'plugins', 'garden', 'institution.glade') super(InstitutionEditorView, self).__init__(filename, parent=parent) def get_window(self): return self.widgets.inst_dialog def start(self): return self.get_window().run()
def condition(self): """the condition of this taxon, or None this is referred to what the garden conservator considers the area of interest. it is really an interpretation, not a fact. """ # one of, but not forcibly so: [_("endemic"), _("indigenous"), _("native"), _("introduced")] notes = [i.note for i in self.notes if i.category.lower() == u"condition"] return (notes + [None])[0]
class PicasaSettingsTool(pluginmgr.Tool): """ Tool for changing the Picasa settings and updated the auth token """ category = _('Picasa') label = _('Settings') @classmethod def start(cls): d = PicasaSettingsDialog() d.run()
class JSONExportTool(pluginmgr.Tool): category = _('Export') label = _('JSON') @classmethod def start(cls): # the presenter uses the view to interact with user then # performs the export, if this is the case. s = db.Session() presenter = JSONExporter(view=ExportToJson(s)) presenter.start() # interact && run s.close()
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 condition(self): '''the condition of this taxon, or None this is referred to what the garden conservator considers the area of interest. it is really an interpretation, not a fact. ''' # one of, but not forcibly so: [_('endemic'), _('indigenous'), _('native'), _('introduced')] notes = [i.note for i in self.notes if i.category.lower() == u'condition'] return (notes + [None])[0]
def verify_connection(engine, show_error_dialogs=False): """ Test whether a connection to an engine is a valid Bauble database. This method will raise an error for the first problem it finds with the database. :param engine: the engine to test :type engine: :class:`sqlalchemy.engine.Engine` :param show_error_dialogs: flag for whether or not to show message dialogs detailing the error, default=False :type show_error_dialogs: bool """ ## debug('entered verify_connection(%s)' % show_error_dialogs) import bauble if show_error_dialogs: try: return verify_connection(engine, False) except error.EmptyDatabaseError: msg = _("The database you have connected to is empty.") utils.message_dialog(msg, gtk.MESSAGE_ERROR) raise except error.MetaTableError: msg = _( "The database you have connected to does not have the " "bauble meta table. This usually means that the database " "is either corrupt or it was created with an old version " "of Bauble" ) utils.message_dialog(msg, gtk.MESSAGE_ERROR) raise except error.TimestampError: msg = _( "The database you have connected to does not have a " "timestamp for when it was created. This usually means " "that there was a problem when you created the " "database or the database you connected to wasn't " "created with Bauble." ) utils.message_dialog(msg, gtk.MESSAGE_ERROR) raise except error.VersionError, e: msg = _( "You are using Bauble version %(version)s while the " "database you have connected to was created with " "version %(db_version)s\n\nSome things might not work as " "or some of your data may become unexpectedly " "corrupted." ) % {"version": bauble.version, "db_version": "%s" % e.version} utils.message_dialog(msg, gtk.MESSAGE_ERROR) raise
def check_parameters_valid(self): """ check that all of the information in the current connection is valid and return true or false NOTE: this was meant to be used to implement an eclipse style information box at the top of the dialog but it's not really used right now """ if self.name_combo.get_active_text() == "": return False, _("Please choose a name for this connection") params = self.params_box if params["user"] == "": return False, _("Please choose a user name for this connection")
def on_dialog_response(self, dialog, response, data=None): """ The dialog's response signal handler. """ self._error = False if response == gtk.RESPONSE_OK: settings = self.params_box.get_prefs() dbtype = self.widgets.type_combo.get_active_text() if dbtype == 'SQLite': filename = settings['file'] if not os.path.exists(filename): path, f = os.path.split(filename) if not os.access(path, os.R_OK): self._error = True msg = _("Bauble does not have permission to " "read the directory:\n\n%s") % path utils.message_dialog(msg, gtk.MESSAGE_ERROR) elif not os.access(path, os.W_OK): self._error = True msg = _("Bauble does not have permission to " "write to the directory:\n\n%s") % path utils.message_dialog(msg, gtk.MESSAGE_ERROR) elif not os.access(filename, os.R_OK): self._error = True msg = _("Bauble does not have permission to read the " "database file:\n\n%s") % filename utils.message_dialog(msg, gtk.MESSAGE_ERROR) elif not os.access(filename, os.W_OK): self._error = True msg = _("Bauble does not have permission to " "write to the database file:\n\n%s") % filename utils.message_dialog(msg, gtk.MESSAGE_ERROR) if not self._error: self.save_current_to_prefs() prefs.prefs[prefs.picture_root_pref] = settings.get( 'pictures', '') elif response == gtk.RESPONSE_CANCEL or \ response == gtk.RESPONSE_DELETE_EVENT: if not self.compare_prefs_to_saved(self.current_name): msg = _("Do you want to save your changes?") if utils.yes_no_dialog(msg): self.save_current_to_prefs() # system-defined GtkDialog responses are always negative, in which # case we want to hide it if response < 0: dialog.hide() #dialog.emit_stop_by_name('response') return response
def init(cls): if 'GardenPlugin' in pluginmgr.plugins: species_context_menu.insert(1, add_accession_action) vernname_context_menu.insert(1, add_accession_action) mapper_search = search.get_strategy('MapperSearch') mapper_search.add_meta(('family', 'fam'), Family, ['family']) SearchView.row_meta[Family].set(children="genera", infobox=FamilyInfoBox, context_menu=family_context_menu) mapper_search.add_meta(('genus', 'gen'), Genus, ['genus']) SearchView.row_meta[Genus].set(children="species", infobox=GenusInfoBox, context_menu=genus_context_menu) from functools import partial search.add_strategy(SynonymSearch) mapper_search.add_meta( ('species', 'sp'), Species, ['sp', 'sp2', 'infrasp1', 'infrasp2', 'infrasp3', 'infrasp4']) SearchView.row_meta[Species].set(children=partial( db.natsort, 'accessions'), infobox=SpeciesInfoBox, context_menu=species_context_menu) mapper_search.add_meta(('vernacular', 'vern', 'common'), VernacularName, ['name']) SearchView.row_meta[VernacularName].set( children=partial(db.natsort, 'species.accessions'), infobox=VernacularNameInfoBox, context_menu=vernname_context_menu) mapper_search.add_meta(('geography', 'geo'), Geography, ['name']) SearchView.row_meta[Geography].set(children=get_species_in_geography) ## now it's the turn of the DefaultView logger.debug('PlantsPlugin::init, registering splash info box') DefaultView.infoboxclass = SplashInfoBox if bauble.gui is not None: bauble.gui.add_to_insert_menu(FamilyEditor, _('Family')) bauble.gui.add_to_insert_menu(GenusEditor, _('Genus')) bauble.gui.add_to_insert_menu(SpeciesEditorMenuItem, _('Species')) if sys.platform == 'win32': # TODO: for some reason using the cross as the hybrid # character doesn't work on windows Species.hybrid_char = 'x'
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(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(e) utils.message_details_dialog(msg, traceback.format_exc(), type=gtk.MESSAGE_ERROR)
def init(cls): if 'GardenPlugin' in pluginmgr.plugins: species_context_menu.insert(1, add_accession_action) vernname_context_menu.insert(1, add_accession_action) mapper_search = search.get_strategy('MapperSearch') mapper_search.add_meta(('family', 'fam'), Family, ['family']) SearchView.row_meta[Family].set(children="genera", infobox=FamilyInfoBox, context_menu=family_context_menu) mapper_search.add_meta(('genus', 'gen'), Genus, ['genus']) SearchView.row_meta[Genus].set(children="species", infobox=GenusInfoBox, context_menu=genus_context_menu) from functools import partial search.add_strategy(SynonymSearch) mapper_search.add_meta(('species', 'sp'), Species, ['sp', 'sp2', 'infrasp1', 'infrasp2', 'infrasp3', 'infrasp4']) SearchView.row_meta[Species].set( children=partial(db.natsort, 'accessions'), infobox=SpeciesInfoBox, context_menu=species_context_menu) mapper_search.add_meta(('vernacular', 'vern', 'common'), VernacularName, ['name']) SearchView.row_meta[VernacularName].set( children=partial(db.natsort, 'species.accessions'), infobox=VernacularNameInfoBox, context_menu=vernname_context_menu) mapper_search.add_meta(('geography', 'geo'), Geography, ['name']) SearchView.row_meta[Geography].set(children=get_species_in_geography) ## now it's the turn of the DefaultView logger.debug('PlantsPlugin::init, registering splash info box') DefaultView.infoboxclass = SplashInfoBox if bauble.gui is not None: bauble.gui.add_to_insert_menu(FamilyEditor, _('Family')) bauble.gui.add_to_insert_menu(GenusEditor, _('Genus')) bauble.gui.add_to_insert_menu(SpeciesEditorMenuItem, _('Species')) if sys.platform == 'win32': # TODO: for some reason using the cross as the hybrid # character doesn't work on windows Species.hybrid_char = 'x'
def init(cls): if 'GardenPlugin' in pluginmgr.plugins: species_context_menu.insert(1, add_accession_action) vernname_context_menu.insert(1, add_accession_action) mapper_search = search.get_strategy('MapperSearch') mapper_search.add_meta(('family', 'fam'), Family, ['family']) SearchView.view_meta[Family].set(children="genera", infobox=FamilyInfoBox, context_menu=family_context_menu, markup_func=family_markup_func) mapper_search.add_meta(('genus', 'gen'), Genus, ['genus']) SearchView.view_meta[Genus].set(children="species", infobox=GenusInfoBox, context_menu=genus_context_menu, markup_func=genus_markup_func) search.add_strategy(SynonymSearch) mapper_search.add_meta(('species', 'sp'), Species, ['sp', 'sp2', 'infrasp1', 'infrasp2', 'infrasp3', 'infrasp4']) SearchView.view_meta[Species].set(children=species_get_kids, infobox=SpeciesInfoBox, context_menu=species_context_menu, markup_func=species_markup_func) mapper_search.add_meta(('vernacular', 'vern', 'common'), VernacularName, ['name']) SearchView.view_meta[VernacularName].set( children=vernname_get_kids, infobox=VernacularNameInfoBox, context_menu=vernname_context_menu, markup_func=vernname_markup_func) mapper_search.add_meta(('geography', 'geo'), Geography, ['name']) SearchView.view_meta[Geography].set(children=get_species_in_geography) if bauble.gui is not None: bauble.gui.add_to_insert_menu(FamilyEditor, _('Family')) bauble.gui.add_to_insert_menu(GenusEditor, _('Genus')) bauble.gui.add_to_insert_menu(SpeciesEditorMenuItem, _('Species')) if sys.platform == 'win32': # TODO: for some reason using the cross as the hybrid # character doesn't work on windows Species.hybrid_char = 'x'
def __init__(self): """ the constructor """ super(SpeciesInfoPage, self).__init__() filename = os.path.join(paths.lib_dir(), "plugins", "plants", "infoboxes.glade") # load the widgets directly instead of using load_widgets() # because the caching that load_widgets() does can mess up # displaying the SpeciesInfoBox sometimes if you try to show # the infobox while having a vernacular names selected in # the search results and then a species name self.widgets = utils.BuilderWidgets(filename) self.general = GeneralSpeciesExpander(self.widgets) self.add_expander(self.general) self.vernacular = VernacularExpander(self.widgets) self.add_expander(self.vernacular) self.synonyms = SynonymsExpander(self.widgets) self.add_expander(self.synonyms) self.links = LinksExpander() self.add_expander(self.links) self.props = PropertiesExpander() self.add_expander(self.props) self.label = _("General") if "GardenPlugin" not in pluginmgr.plugins: self.widgets.remove_parent("sp_nacc_label") self.widgets.remove_parent("sp_nacc_data") self.widgets.remove_parent("sp_nplants_label") self.widgets.remove_parent("sp_nplants_data")