def create(import_defaults=True): """ Create new Bauble database at the current connection :param import_defaults: A flag that is passed to each plugins install() method to indicate where it should import its default data. This is mainly used for testing. The default value is True :type import_defaults: bool """ # TODO: when creating a database there shouldn't be any errors # on import since we are importing from the default values, we should # just force the import and send everything in the database at once # instead of using slices, this would make it alot faster but it may # make it more difficult to make the interface more responsive, # maybe we can use a dialog without the progress bar to show the status, # should probably work on the status bar to display this # TODO: we create a transaction here and the csv import creates another # nested transaction, we need to verify that if we rollback here then all # of the changes made by csv import are rolled back as well ## debug('entered db.create()') if not engine: raise ValueError('engine is None, not connected to a database') import bauble import bauble.meta as meta import bauble.pluginmgr as pluginmgr import datetime connection = engine.connect() transaction = connection.begin() try: # TODO: here we are dropping/creating all the tables in the # metadata whether they are in the registry or not, we should # really only be creating those tables from registered # plugins, maybe with an uninstall() method on Plugin metadata.drop_all(bind=connection, checkfirst=True) metadata.create_all(bind=connection) # fill in the bauble meta table and install all the plugins meta_table = meta.BaubleMeta.__table__ meta_table.insert(bind=connection).\ execute(name=meta.VERSION_KEY, value=unicode(bauble.version)).close() meta_table.insert(bind=connection).\ execute(name=meta.CREATED_KEY, value=unicode(datetime.datetime.now())).close() except GeneratorExit, e: # this is here in case the main windows is closed in the middle # of a task # UPDATE 2009.06.18: i'm not sure if this is still relevant since we # switched the task system to use fibra...but it doesn't hurt # having it here until we can make sure warning('bauble.db.create(): %s' % utils.utf8(e)) transaction.rollback() raise
def commit_changes(self): ''' Commit the changes to self.session() ''' objs = list(self.session) try: self.session.commit() except Exception, e: warning(e) self.session.rollback() self.session.add_all(objs) raise
def reset_sequence(column): """ If column.sequence is not None or the column is an Integer and column.autoincrement is true then reset the sequence for the next available value for the column...if the column doesn't have a sequence then do nothing and return The SQL statements are executed directly from db.engine This function only works for PostgreSQL database. It does nothing for other database engines. """ import bauble import bauble.db as db from sqlalchemy.types import Integer from sqlalchemy import schema if not db.engine.name == 'postgresql': return sequence_name = None if hasattr(column,'default') and isinstance(column.default,schema.Sequence): sequence_name = column.default.name elif (isinstance(column.type, Integer) and column.autoincrement) and \ (column.default is None or \ (isinstance(column.default, schema.Sequence) and \ column.default.optional)) and \ len(column.foreign_keys)==0: sequence_name = '%s_%s_seq' %(column.table.name, column.name) else: return conn = db.engine.connect() trans = conn.begin() try: # the FOR UPDATE locks the table for the transaction stmt = "SELECT %s from %s FOR UPDATE;" %(column.name, column.table.name) result = conn.execute(stmt) maxid = None vals = list(result) if vals: maxid = max(vals, key=lambda x: x[0])[0] result.close() if maxid == None: # set the sequence to nextval() stmt = "SELECT nextval('%s');" % (sequence_name) else: stmt = "SELECT setval('%s', max(%s)+1) from %s;" \ % (sequence_name, column.name, column.table.name) conn.execute(stmt) except Exception, e: warning('bauble.utils.reset_sequence(): %s' % utf8(e)) trans.rollback()
def start(self): """ Show the connection manager. """ self.create_gui() self.dialog.connect('response', self.on_dialog_response) self.dialog.connect('close', self.on_dialog_close_or_delete) self.dialog.connect('delete-event', self.on_dialog_close_or_delete) conn_list = 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 # 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: warning('ConnectionManager.start(): param box leaked: %s' % obj) return name, uri
def cell_data_func(self, col, cell, model, treeiter): path = model.get_path(treeiter) tree_rect = self.results_view.get_visible_rect() cell_rect = self.results_view.get_cell_area(path, col) if cell_rect.y > tree_rect.height: # only update the cells if they're visible...this # drastically speeds up populating the view with large # datasets return value = model[treeiter][0] if isinstance(value, basestring): cell.set_property('markup', value) else: # if the value isn't part of a session then add it to the # view's session so that we can access its child # properties...this usually happens when one of the # ViewMeta's get_children() functions return a list of # object who's session was closed...we add it here for # performance reasons so we only add it onces its visible if not object_session(value): if value in self.session: # expire the object in the session with the same key self.session.expire(value) else: self.session.add(value) try: func = self.view_meta[type(value)].markup_func if func is not None: r = func(value) if isinstance(r, (list,tuple)): main, substr = r else: main = r substr = '(%s)' % type(value).__name__ else: main = utils.xml_safe(str(value)) substr = '(%s)' % type(value).__name__ cell.set_property('markup', '%s\n%s' % \ (_mainstr_tmpl % utils.utf8(main), _substr_tmpl % utils.utf8(substr))) except (saexc.InvalidRequestError, TypeError), e: warning('bauble.view.SearchView.cell_data_func(): \n%s' % e) def remove(): model = self.results_view.get_model() self.results_view.set_model(None) # detach model for found in utils.search_tree_model(model, value): model.remove(found) self.results_view.set_model(model) gobject.idle_add(remove)
def _find_plugins(path): """ Return the plugins at path. """ plugins = [] import bauble.plugins plugin_module = bauble.plugins errors = {} if path.find('library.zip') != -1: plugin_names = [m for m in _find_module_names(path) \ if m.startswith('bauble.plugins')] else: plugin_names =['bauble.plugins.%s'%m for m in _find_module_names(path)] for name in plugin_names: mod = None # Fast path: see if the module has already been imported. if name in sys.modules: mod = sys.modules[name] else: try: mod = __import__(name, globals(), locals(), [name], -1) except Exception, e: msg = _('Could not import the %(module)s module.\n\n'\ '%(error)s' % {'module': name, 'error': e}) debug(msg) errors[name] = sys.exc_info() if not hasattr(mod, "plugin"): continue # if mod.plugin is a function it should return a plugin or list of # plugins try: mod_plugin = mod.plugin() except: mod_plugin = mod.plugin is_plugin = lambda p: isinstance(p, (type, types.ClassType)) and issubclass(p, Plugin) if isinstance(mod_plugin, (list, tuple)): for p in mod_plugin: if is_plugin(p) or True: plugins.append(p) elif is_plugin(mod_plugin) or True: plugins.append(mod_plugin) else: warning(_('%s.plugin is not an instance of pluginmgr.Plugin'\ % mod.__name__))
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 on_cursor_changed(self, view): ''' Update the infobox and switch the accelerators depending on the type of the row that the cursor points to. ''' self.update_infobox() self.update_notes() for accel, cb in self.installed_accels: # disconnect previously installed accelerators by the key # and modifier, accel_group.disconnect_by_func won't work # here since we install a closure as the actual callback # in instead of the original action.callback r = self.accel_group.disconnect_key(accel[0], accel[1]) if not r: warning('callback not removed: %s' % cb) self.installed_accels = [] selected = self.get_selected_values() if not selected: return selected_type = type(selected[0]) for action in self.view_meta[selected_type].actions: enabled = (len(selected) > 1 and action.multiselect) or \ (len(selected)<=1 and action.singleselect) if not enabled: continue # if enabled then connect the accelerator keyval, mod = gtk.accelerator_parse(action.accelerator) if (keyval, mod) != (0, 0): def cb(func): def _impl(*args): # getting the selected here allows the # callback to be called on all the selected # values and not just the value where the # cursor is sel = self.get_selected_values() if func(sel): self.reset_view() return _impl self.accel_group.connect_group(keyval, mod, gtk.ACCEL_VISIBLE, cb(action.callback)) self.installed_accels.append(((keyval, mod), action.callback)) else: warning('Could not parse accelerator: %s' %(action.accelerator))
def create_gui(self): if self.working_dbtypes is None or len(self.working_dbtypes) == 0: msg = _("No Python database connectors installed.\n"\ "Please consult the documentation for the "\ "prerequesites for installing Bauble.") utils.message_dialog(msg, gtk.MESSAGE_ERROR) raise Exception(msg) glade_path = os.path.join(paths.lib_dir(), "connmgr.glade") self.widgets = utils.BuilderWidgets(glade_path) self.dialog = self.widgets.main_dialog try: pixbuf = gtk.gdk.pixbuf_new_from_file(bauble.default_icon) self.dialog.set_icon(pixbuf) except Exception, e: warning(_('Could not load icon from %s' % bauble.default_icon)) warning(traceback.format_exc())
def _get_working_dbtypes(self, retry=False): """ get for self.working_dbtypes property this sets self._working_dbtypes to a dictionary where the keys are the database names and the values are the index in the connectiona manager's database types """ if self._working_dbtypes != [] and not retry: return self._working_dbtypes self._working_dbtypes = [] try: try: import pysqlite2 except Exception: import sqlite3 self._working_dbtypes.append('SQLite') except ImportError, e: warning('ConnectionManager: %s' % e)
def on_file_menu_open(self, widget, data=None): """ Open the connection manager. """ from connmgr import ConnectionManager default_conn = prefs[bauble.conn_default_pref] cm = ConnectionManager(default_conn) name, uri = cm.start() if name is None: return engine = None try: engine = db.open(uri, True, True) except Exception, e: # we don't do anything to handle the exception since # db.open() should have shown an error dialog if there was # a problem opening the database as long as the # show_error_dialogs parameter is True warning(e)
def _get_tagged_object_pairs(tag): """ :param tag: a Tag instance """ from bauble.view import SearchView kids = [] for obj in tag._objects: try: # __import__ "from_list" parameters has to be a list of strings module_name, part, cls_name = str(obj.obj_class).rpartition('.') module = __import__(module_name, globals(), locals(), module_name.split('.')[1:]) cls = getattr(module, cls_name) kids.append((cls, obj.obj_id)) except KeyError, e: warning('KeyError -- tag.get_tagged_objects(%s): %s' % (tag, e)) continue except DBAPIError, e: warning('DBAPIError -- tag.get_tagged_objects(%s): %s' % (tag, e)) continue
def init(self): ''' initialize the preferences, should only be called from app.main ''' # create directory tree of filename if it doesn't yet exist head, tail = os.path.split(self._filename) if not os.path.exists(head): os.makedirs(head) self.config = RawConfigParser() # set the version if the file doesn't exist if not os.path.exists(self._filename): self[config_version_pref] = config_version else: self.config.read(self._filename) version = self[config_version_pref] if version is None: from bauble.utils.log import warning warning('%s has no config version pref' % self._filename) warning('setting the config version to %s.%s' % (config_version)) self[config_version_pref] = config_version # set some defaults if they don't exist if date_format_pref not in self: self[date_format_pref] = '%d-%m-%Y' if parse_dayfirst_pref not in self: format = self[date_format_pref] if format.find('%d') < format.find('%m'): self[parse_dayfirst_pref] = True else: self[parse_dayfirst_pref] = False if parse_yearfirst_pref not in self: format = self[date_format_pref] if format.find('%Y') == 0 or format.find('%y') == 0: self[parse_yearfirst_pref] = True else: self[parse_yearfirst_pref] = False if units_pref not in self: self[units_pref] = 'metric'
from bauble.connmgr import ConnectionManager default_conn = prefs[conn_default_pref] while True: if not uri or not conn_name: cm = ConnectionManager(default_conn) conn_name, uri = cm.start() if conn_name is None: quit() try: if db.open(uri, True, True): prefs[conn_default_pref] = conn_name break else: uri = conn_name = None except err.VersionError, e: warning(e) db.open(uri, False) break except (err.EmptyDatabaseError, err.MetaTableError, err.VersionError, err.TimestampError, err.RegistryError), e: warning(e) open_exc = e # reopen without verification so that db.Session and # db.engine, db.metadata will be bound to an engine db.open(uri, False) break except err.DatabaseError, e: debug(e) #traceback.format_exc() open_exc = e
def install(plugins_to_install, import_defaults=True, force=False): """ :param plugins_to_install: A list of plugins to install. If the string "all" is passed then install all plugins listed in the bauble.pluginmgr.plugins dict that aren't already listed in the plugin registry. :param import_defaults: Flag passed to the plugin's install() method to indicate whether it should import its default data. :type import_defaults: bool :param force: Force, don't ask questions. :type force: book """ #debug('pluginmgr.install(%s)' % plugins_to_install) if plugins_to_install is 'all': to_install = plugins.values() else: to_install = plugins_to_install if len(to_install) == 0: # no plugins to install return # sort the plugins by their dependency depends, unmet = _create_dependency_pairs(to_install) if unmet != {}: debug(unmet) raise BaubleError('unmet dependencies') to_install = utils.topological_sort(to_install, depends) if not to_install: raise BaubleError(_('The plugins contain a dependency loop. This '\ 'can happend if two plugins directly or '\ 'indirectly rely on each other')) # msg = _('The %(plugin)s plugin depends on the %(other_plugin)s '\ # 'plugin but the %(other_plugin)s plugin wasn\'t found.') \ # % {'plugin': e.plugin.__name__, 'other_plugin': e.not_found} # utils.message_dialog(msg, gtk.MESSAGE_WARNING) # to_install = topological_sort(to_install, depends) # except DependencyError, e: # msg = _('The %(plugin)s plugin depends on the %(other_plugin)s '\ # 'plugin but the %(other_plugin)s plugin wasn\'t found.') \ # % {'plugin': e.plugin.__name__, 'other_plugin': e.not_found} # utils.message_dialog(msg, gtk.MESSAGE_WARNING) # raise # except DependencyError, e: # error(utils.utf8(e)) try: for p in to_install: #debug('install: %s' % p.__name__) p.install(import_defaults=import_defaults) # TODO: here we make sure we don't add the plugin to the # registry twice but we should really update the version # number in the future when we accept versioned plugins # (if ever) if not PluginRegistry.exists(p): PluginRegistry.add(p) #session.commit() except Exception, e: warning('bauble.pluginmgr.install(): %s' % utils.utf8(e)) raise
'indirectly rely on each other')) # call init() for each ofthe plugins for plugin in ordered: #debug('init: %s' % plugin) try: plugin.init() except KeyError, e: # don't remove the plugin from the registry because if we # find it again the user might decide to reinstall it # which could overwrite data ordered.remove(plugin) msg = _("The %(plugin_name)s plugin is listed in the registry "\ "but isn't wasn't found in the plugin directory") \ % dict(plugin_name=plugin.__class__.__name__) warning(msg) except Exception, e: #error(e) ordered.remove(plugin) error(traceback.print_exc()) safe = utils.xml_safe_utf8 values = dict(entry_name=plugin.__class__.__name__, exception=safe(e)) utils.message_details_dialog(_("Error: Couldn't initialize "\ "%(entry_name)s\n\n" \ "%(exception)s." % values), traceback.format_exc(), gtk.MESSAGE_ERROR) # register the plugin commands seperately from the plugin initialization for plugin in ordered:
def __init__(self): filename = os.path.join(paths.lib_dir(), 'bauble.glade') self.widgets = utils.load_widgets(filename) self.window = self.widgets.main_window self.window.hide() # restore the window size geometry = prefs[self.window_geometry_pref] if geometry is not None: self.window.set_default_size(*geometry) self.window.connect('delete-event', self.on_delete_event) self.window.connect("destroy", self.on_quit) self.window.set_title(self.title) try: pixbuf = gtk.gdk.pixbuf_new_from_file(bauble.default_icon) self.window.set_icon(pixbuf) except Exception: warning(_('Could not load icon from %s' % bauble.default_icon)) warning(traceback.format_exc()) # utils.message_details_dialog(_('Could not load icon from %s' % \ # bauble.default_icon), # traceback.format_exc(), # gtk.MESSAGE_ERROR) menubar = self.create_main_menu() self.widgets.menu_box.pack_start(menubar) combo = self.widgets.main_comboentry model = gtk.ListStore(str) combo.set_model(model) self.populate_main_entry() # disable: causes the entry contents to be selected on backspace #combo.connect('changed', lambda c: c.grab_focus()) main_entry = combo.child # main_entry.connect('key_press_event', self.on_main_entry_key_press) main_entry.connect('activate', self.on_main_entry_activate) accel_group = gtk.AccelGroup() main_entry.add_accelerator("grab-focus", accel_group, ord('L'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE) self.window.add_accel_group(accel_group) go_button = self.widgets.go_button go_button.connect('clicked', self.on_go_button_clicked) query_button = self.widgets.query_button query_button.connect('clicked', self.on_query_button_clicked) self.set_default_view() # add a progressbar to the status bar # Warning: this relies on gtk.Statusbar internals and could break in # future versions of gtk statusbar = self.widgets.statusbar statusbar.set_spacing(10) statusbar.set_has_resize_grip(True) self._cids = [] def on_statusbar_push(sb, cid, txt): if cid not in self._cids: self._cids.append(cid) statusbar.connect('text-pushed', on_statusbar_push) # remove label from frame frame = statusbar.get_children()[0] #frame.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse('#FF0000')) label = frame.get_children()[0] frame.remove(label) # replace label with hbox and put label and progress bar in hbox hbox = gtk.HBox(False, 5) frame.add(hbox) hbox.pack_start(label, True, True, 0) vbox = gtk.VBox(True, 0) hbox.pack_end(vbox, False, True, 15) self.progressbar = gtk.ProgressBar() vbox.pack_start(self.progressbar, False, False, 0) self.progressbar.set_size_request(-1, 10) vbox.show() hbox.show() from pyparsing import StringStart, Word, alphanums, restOfLine, \ StringEnd cmd = StringStart() +':'+ Word(alphanums + '-_').setResultsName('cmd') arg = restOfLine.setResultsName('arg') self.cmd_parser = (cmd + StringEnd()) | (cmd + '=' + arg) | arg combo.grab_focus()
error('bauble.gui.on_insert_menu_item_activate():\n %s' \ % traceback.format_exc()) return presenter_cls = view_cls = None if hasattr(editor, 'presenter'): presenter_cls = type(editor.presenter) view_cls = type(editor.presenter.view) # delete the editor del editor # check for leaks obj = utils.gc_objects_by_type(editor_cls) if obj != []: warning('%s leaked: %s' % (editor_cls.__name__, obj)) if presenter_cls: obj = utils.gc_objects_by_type(presenter_cls) if obj != []: warning('%s leaked: %s' % (presenter_cls.__name__, obj)) obj = utils.gc_objects_by_type(view_cls) if obj != []: warning('%s leaked: %s' % (view_cls.__name__, obj)) ## def on_edit_menu_prefs(self, widget, data=None): ## p = PreferencesMgr() ## p.run() ## p.destroy()
for obj in tag._objects: try: # __import__ "from_list" parameters has to be a list of strings module_name, part, cls_name = str(obj.obj_class).rpartition('.') module = __import__(module_name, globals(), locals(), module_name.split('.')[1:]) cls = getattr(module, cls_name) kids.append((cls, obj.obj_id)) except KeyError, e: warning('KeyError -- tag.get_tagged_objects(%s): %s' % (tag, e)) continue except DBAPIError, e: warning('DBAPIError -- tag.get_tagged_objects(%s): %s' % (tag, e)) continue except AttributeError, e: warning('AttributeError -- tag.get_tagged_objects(%s): %s' \ % (tag, e)) warning('Could not get the object for %s.%s(%s)' % \ (module_name, cls_name, obj.obj_id)) continue return kids def get_tagged_objects(tag, session=None): """ Return all object tagged with tag. :param tag: A string or :class:`Tag` :param session: """ close_session = False
execute(name=meta.VERSION_KEY, value=unicode(bauble.version)).close() meta_table.insert(bind=connection).\ execute(name=meta.CREATED_KEY, value=unicode(datetime.datetime.now())).close() except GeneratorExit, e: # this is here in case the main windows is closed in the middle # of a task # UPDATE 2009.06.18: i'm not sure if this is still relevant since we # switched the task system to use fibra...but it doesn't hurt # having it here until we can make sure warning('bauble.db.create(): %s' % utils.utf8(e)) transaction.rollback() raise except Exception, e: warning('bauble.db.create(): %s' % utils.utf8(e)) transaction.rollback() raise else: transaction.commit() finally: connection.close() connection = engine.connect() transaction = connection.begin() try: pluginmgr.install('all', import_defaults, force=True) except GeneratorExit, e: # this is here in case the main windows is closed in the middle # of a task # UPDATE 2009.06.18: i'm not sure if this is still relevant since we