def assign_simple_handler(self, widget_name, model_attr, validator=None): ''' Assign handlers to widgets to change fields in the model. :param widget_name: :param model_attr: :param validator: Note: Where widget is a gtk.ComboBox or gtk.ComboBoxEntry then the value is assumed to be stored in model[row][0] ''' widget = self.view.widgets[widget_name] check(widget is not None, _('no widget with name %s') % widget_name) class ProblemValidator(Validator): def __init__(self, presenter, wrapped): self.presenter = presenter self.wrapped = wrapped def to_python(self, value): try: value = self.wrapped.to_python(value) self.presenter.remove_problem('BAD_VALUE_%s' \ % model_attr,widget) except Exception, e: self.presenter.add_problem('BAD_VALUE_%s' \ % model_attr, widget) raise return value
def invoke(self, search_strategy): """ update search_strategy object with statement results Queries can use more database specific features. This also means that the same query might not work the same on different database types. For example, on a PostgreSQL database you can use ilike but this would raise an error on SQLite. """ domain = self.domain check( domain in search_strategy._domains or domain in search_strategy._shorthand, 'Unknown search domain: %s' % domain) self.domain = search_strategy._shorthand.get(domain, domain) self.domain = search_strategy._domains[domain][0] self.search_strategy = search_strategy result = set() if search_strategy._session is not None: self.domains = self.filter.needs_join(self) self.session = search_strategy._session records = self.filter.evaluate(self).all() result.update(records) return result
def range_builder(text): """Return a list of numbers from a string range of the form 1-3,4,5 """ from pyparsing import Word, Group, Suppress, delimitedList, nums, \ ParseException, ParseResults rng = Group(Word(nums) + Suppress('-') + Word(nums)) range_list = delimitedList(rng | Word(nums)) token = None try: tokens = range_list.parseString(text) except (AttributeError, ParseException) as e: return [] values = set() for rng in tokens: if isinstance(rng, ParseResults): # get here if the token is a range start = int(rng[0]) end = int(rng[1]) + 1 check(start<end, 'start must be less than end') values.update(range(start, end)) else: # get here if the token is an integer values.add(int(rng)) return list(values)
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 invoke(self, search_strategy): """ update search_strategy object with statement results Queries can use more database specific features. This also means that the same query might not work the same on different database types. For example, on a PostgreSQL database you can use ilike but this would raise an error on SQLite. """ domain = self.domain check(domain in search_strategy._domains or domain in search_strategy._shorthand, 'Unknown search domain: %s' % domain) self.domain = search_strategy._shorthand.get(domain, domain) self.domain = search_strategy._domains[domain][0] self.search_strategy = search_strategy result = set() if search_strategy._session is not None: self.domains = self.filter.needs_join(self) self.session = search_strategy._session records = self.filter.evaluate(self).all() result.update(records) return result
def add_meta(self, domain, cls, properties): """ Adds search meta to the domain :param domain: a string, list or tuple of domains that will resolve to cls a search string, 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 """ 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)): # domain[0] is the result key self._result_keys[cls] = domain[0] self._domains[domain[0]] = cls, properties for d in domain[1:]: self._shorthand[d] = domain[0] else: self._result_keys[cls] = domain self._domains[d] = cls, properties self._properties[cls] = properties
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 make_label_clickable(label, on_clicked, *args): """ :param label: a gtk.Label that has a gtk.EventBox as its parent :param on_clicked: callback to be called when the label is clicked on_clicked(label, event, data) """ eventbox = label.parent check(eventbox is not None, 'label must have a parent') check(isinstance(eventbox, gtk.EventBox), 'label must have an gtk.EventBox as its parent') label.__pressed = False def on_enter_notify(*args): label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) def on_leave_notify(*args): label.modify_fg(gtk.STATE_NORMAL, None) label.__pressed = False def on_press(*args): label.__pressed = True def on_release(widget, event, *args): if label.__pressed: label.__pressed = False label.modify_fg(gtk.STATE_NORMAL, None) on_clicked(label, event, *args) eventbox.connect('enter_notify_event', on_enter_notify) eventbox.connect('leave_notify_event', on_leave_notify) eventbox.connect('button_press_event', on_press) eventbox.connect('button_release_event', on_release, *args)
def append_children(self, model, parent, kids): """ append object to a parent iter in the model :param model: the model the append to :param parent: the parent gtk.TreeIter :param kids: a list of kids to append @return: the model with the kids appended """ check(parent is not None, "append_children(): need a parent") for k in kids: i = model.append(parent, [k]) if self.view_meta[type(k)].children is not None: model.append(i, ["_dummy"]) return model
def set_active_connection_by_name(self, name): """ sets the name of the connection in the name combo, this causes on_changed_name_combo to be fired which changes the param box type and set the connection parameters """ check(hasattr(self, "name_combo")) i = 0 active = 0 conn_list = prefs.prefs[bauble.conn_list_pref] if conn_list is None: return for conn in conn_list: self.name_combo.insert_text(i, conn) if conn == name: active = i i += 1 self.name_combo.set_active(active)
def make_label_clickable(label, on_clicked, *args): """ :param label: a gtk.Label that has a gtk.EventBox as its parent :param on_clicked: callback to be called when the label is clicked on_clicked(label, event, data) """ eventbox = label.parent check(eventbox is not None, 'label must have a parent') check(isinstance(eventbox, gtk.EventBox), 'label must have an gtk.EventBox as its parent') label.__pressed = False def on_enter_notify(widget, *args): widget.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#faf8f7")) label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.color_parse("blue")) def on_leave_notify(widget, *args): widget.modify_bg(gtk.STATE_NORMAL, None) label.modify_fg(gtk.STATE_NORMAL, None) label.__pressed = False def on_press(*args): label.__pressed = True def on_release(widget, event, *args): if label.__pressed: label.__pressed = False label.modify_fg(gtk.STATE_NORMAL, None) on_clicked(label, event, *args) try: eventbox.disconnect(label.__on_event) logger.debug('disconnected previous release-event handler') label.__on_event = eventbox.connect( 'button_release_event', on_release, *args) except AttributeError: logger.debug('defining handlers') label.__on_event = eventbox.connect( 'button_release_event', on_release, *args) eventbox.connect('enter_notify_event', on_enter_notify) eventbox.connect('leave_notify_event', on_leave_notify) eventbox.connect('button_press_event', on_press)
def add_meta(self, domain, cls, properties): """ Adds search meta to the domain :param domain: a string, list or tuple of domains that will resolve to cls a search string, 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 """ 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[d] = cls, properties self._properties[cls] = properties
def select_in_search_results(obj): """ :param obj: the object the select @returns: a gtk.TreeIter to the selected row Search the tree model for obj if it exists then select it if not then add it and select it. The the obj is not in the model then we add it. """ check(obj is not None, 'select_in_search_results: arg is None') view = bauble.gui.get_view() if not isinstance(view, SearchView): return None model = view.results_view.get_model() found = utils.search_tree_model(model, obj) row_iter = None if len(found) > 0: row_iter = found[0] else: row_iter = model.append(None, [obj]) model.append(row_iter, ['-']) view.results_view.set_cursor(model.get_path(row_iter)) return row_iter
rng = Group(Word(nums) + Suppress("-") + Word(nums)) range_list = delimitedList(rng | Word(nums)) token = None try: tokens = range_list.parseString(text) except (AttributeError, ParseException), e: logger.debug(e) return [] values = set() for rng in tokens: if isinstance(rng, ParseResults): # get here if the token is a range start = int(rng[0]) end = int(rng[1]) + 1 check(start < end, "start must be less than end") values.update(range(start, end)) else: # get here if the token is an integer values.add(int(rng)) return list(values) def gc_objects_by_type(tipe): """ Return a list of objects from the garbage collector by type. """ import inspect import gc if isinstance(tipe, basestring):
def set_privilege(role, privilege): """Set the role's privileges. Arguments: - `role`: - `privilege`: """ check(privilege in ('read', 'write', 'admin', None), 'invalid privilege: %s' % privilege) conn = db.engine.connect() trans = conn.begin() if privilege: privs = _privileges[privilege] try: # revoke everything first for table in db.metadata.sorted_tables: stmt = 'revoke all on table %s from %s;' % (table.name, role) conn.execute(stmt) for col in table.c: if hasattr(col, 'sequence'): stmt = 'revoke all on sequence %s from %s' % \ (col.sequence.name, role) conn.execute(stmt) stmt = 'revoke all on database %s from %s' \ % (bauble.db.engine.url.database, role) conn.execute(stmt) stmt = 'alter role %s with nocreaterole' % role conn.execute(stmt) # privilege is None so all permissions are revoked if not privilege: trans.commit() conn.close() return # change privileges on the database if privilege == 'admin': stmt = 'grant all on database %s to %s' % \ (bauble.db.engine.url.database, role) if privilege == 'admin': stmt += ' with grant option' conn.execute(stmt) stmt = 'alter role %s with createuser' % role conn.execute(stmt) # grant privileges on the tables and sequences for table in bauble.db.metadata.sorted_tables: tbl_privs = filter(lambda x: x.lower() in _table_privs, privs) for priv in tbl_privs: stmt = 'grant %s on %s to %s' % (priv, table.name, role) if privilege == 'admin': stmt += ' with grant option' #debug(stmt) conn.execute(stmt) for col in table.c: seq_privs = filter(lambda x: x.lower() in __sequence_privs, privs) for priv in seq_privs: if hasattr(col, 'sequence'): stmt = 'grant %s on sequence %s to %s' % \ (priv, col.sequence.name, role) #debug(stmt) if privilege == 'admin': stmt += ' with grant option' conn.execute(stmt) except Exception, e: error('users.set_privilege(): %s' % utils.utf8(e)) trans.rollback() raise
rng = Group(Word(nums) + Suppress('-') + Word(nums)) range_list = delimitedList(rng | Word(nums)) token = None try: tokens = range_list.parseString(text) except (AttributeError, ParseException), e: logger.debug(e) return [] values = set() for rng in tokens: if isinstance(rng, ParseResults): # get here if the token is a range start = int(rng[0]) end = int(rng[1]) + 1 check(start < end, 'start must be less than end') values.update(range(start, end)) else: # get here if the token is an integer values.add(int(rng)) return list(values) def gc_objects_by_type(tipe): """ Return a list of objects from the garbage collector by type. """ import inspect import gc if isinstance(tipe, basestring): return [o for o in gc.get_objects() if type(o).__name__ == tipe]
def on_query(self, s, loc, tokens): """ Called when the parser hits a query token. Queries can use more database specific features. This also means that the same query might not work the same on different database types. For example, on a PostgreSQL database you can use ilike but this would raise an error on SQLite. """ # The method requires that the underlying database support # union and intersect. At the time of writing this MySQL # didn't. # TODO: support 'not' a boolean op as well, e.g sp where # genus.genus=Maxillaria and not genus.family=Orchidaceae domain, expr = tokens check(domain in self._domains or domain in self._shorthand, 'Unknown search domain: %s' % domain) if domain in self._shorthand: domain = self._shorthand[domain] cls = self._domains[domain][0] main_query = self._session.query(cls) mapper = class_mapper(cls) expr_iter = iter(expr) boolop = None for e in expr_iter: idents, cond, val = e # debug('cls: %s, idents: %s, cond: %s, val: %s' # % (cls.__name__, idents, cond, val)) if val == 'None': val = None if cond == 'is': cond = '=' elif cond == 'is not': cond = '!=' elif cond in ('ilike', 'icontains', 'ihas'): cond = lambda col: \ lambda val: utils.ilike(col, '%s' % val) if len(idents) == 1: # we get here when the idents only refer to a property # on the mapper table..i.e. a column col = idents[0] msg = _('The %(tablename)s table does not have a '\ 'column named "%(columname)s"') % \ dict(tablename=mapper.local_table.name, columname=col) check(col in mapper.c, msg) if isinstance(cond, str): clause = getattr(cls, col).op(cond)(utils.utf8(val)) else: clause = cond(getattr(cls, col))(utils.utf8(val)) query = self._session.query(cls).filter(clause).order_by(None) else: # we get here when the idents refer to a relation on a # mapper/table relations = idents[:-1] col = idents[-1] query = self._session.query(cls) query = query.join(*relations) # NOTE: SA07 - this depends on Query._joinpoint not changing, # it changed in SA05 which broke this local_table = query._joinpoint['prev'][0][1].local_table if isinstance(cond, str): clause = local_table.c[col].op(cond)(utils.utf8(val)) else: clause = cond(local_table.c[col])(utils.utf8(val)) query = query.filter(clause).order_by(None) if boolop == 'or': main_query = main_query.union(query) elif boolop == 'and': main_query = main_query.intersect(query) else: main_query = query try: boolop = expr_iter.next() except StopIteration: pass self._results.update(main_query.order_by(None).all())
def create_abcd(decorated_objects, authors=True, validate=True): """ :param objects: a list/tuple of objects that implement the ABCDDecorator interface :param authors: flag to control whether to include the authors in the species name :param validate: whether we should validate the data before returning :returns: a valid ABCD ElementTree """ import bauble.plugins.garden.institution as institution inst = institution.Institution() if not verify_institution(inst): msg = _('Some or all of the information about your institution or ' \ 'business is not complete. Please make sure that the ' \ 'Name, Technical Contact, Email, Contact and Institution ' 'Code fields are filled in.') utils.message_dialog(msg) institution.InstitutionEditor().start() return create_abcd(decorated_objects, authors, validate) datasets = DataSets() ds = ABCDElement(datasets, 'DataSet') tech_contacts = ABCDElement(ds, 'TechnicalContacts') tech_contact = ABCDElement(tech_contacts, 'TechnicalContact') # TODO: need to include contact information in bauble meta when # creating a new database ABCDElement(tech_contact, 'Name', text=inst.inst_technical_contact) ABCDElement(tech_contact, 'Email', text=inst.inst_email) cont_contacts = ABCDElement(ds, 'ContentContacts') cont_contact = ABCDElement(cont_contacts, 'ContentContact') ABCDElement(cont_contact, 'Name', text=inst.inst_contact) ABCDElement(cont_contact, 'Email', text=inst.inst_email) metadata = ABCDElement( ds, 'Metadata', ) description = ABCDElement(metadata, 'Description') # TODO: need to get the localized language representation = ABCDElement(description, 'Representation', attrib={'language': 'en'}) revision = ABCDElement(metadata, 'RevisionData') ABCDElement(revision, 'DateModified', text='2001-03-01T00:00:00') title = ABCDElement(representation, 'Title', text='TheTitle') units = ABCDElement(ds, 'Units') # build the ABCD unit for obj in decorated_objects: unit = ABCDElement(units, 'Unit') ABCDElement(unit, 'SourceInstitutionID', text=inst.inst_code) # TODO: don't really understand the SourceID element ABCDElement(unit, 'SourceID', text='Bauble') unit_id = ABCDElement(unit, 'UnitID', text=obj.get_UnitID()) ABCDElement(unit, 'DateLastEdited', text=obj.get_DateLastEdited()) # TODO: add list of verifications to Identifications # scientific name identification identifications = ABCDElement(unit, 'Identifications') identification = ABCDElement(identifications, 'Identification') result = ABCDElement(identification, 'Result') taxon_identified = ABCDElement(result, 'TaxonIdentified') higher_taxa = ABCDElement(taxon_identified, 'HigherTaxa') higher_taxon = ABCDElement(higher_taxa, 'HigherTaxon') # TODO: ABCDDecorator should provide an iterator so that we can # have multiple HigherTaxonName's higher_taxon_name = ABCDElement(higher_taxon, 'HigherTaxonName', text=obj.get_family()) higher_taxon_rank = ABCDElement(higher_taxon, 'HigherTaxonRank', text='familia') scientific_name = ABCDElement(taxon_identified, 'ScientificName') ABCDElement(scientific_name, 'FullScientificNameString', text=obj.get_FullScientificNameString(authors)) name_atomised = ABCDElement(scientific_name, 'NameAtomised') botanical = ABCDElement(name_atomised, 'Botanical') ABCDElement(botanical, 'GenusOrMonomial', text=obj.get_GenusOrMonomial()) ABCDElement(botanical, 'FirstEpithet', text=obj.get_FirstEpithet()) author_team = obj.get_AuthorTeam() if author_team is not None: ABCDElement(botanical, 'AuthorTeam', text=author_team) ABCDElement(identification, 'PreferredFlag', text='true') # vernacular name identification # TODO: should we include all the vernacular names or only the default # one vernacular_name = obj.get_InformalNameString() if vernacular_name is not None: identification = ABCDElement(identifications, 'Identification') result = ABCDElement(identification, 'Result') taxon_identified = ABCDElement(result, 'TaxonIdentified') ABCDElement(taxon_identified, 'InformalNameString', text=vernacular_name) # add all the extra non standard elements obj.extra_elements(unit) # TODO: handle verifiers/identifiers # TODO: RecordBasis # notes are last in the schema and extra_elements() shouldn't # add anything that comes past Notes, e.g. RecordURI, # EAnnotations, UnitExtension notes = obj.get_Notes() if notes: ABCDElement(unit, 'Notes', text=notes) if validate: check(validate_xml(datasets), 'ABCD data not valid') return ElementTree(datasets)
def create_abcd(decorated_objects, authors=True, validate=True): """ :param objects: a list/tuple of objects that implement the ABCDDecorator interface :param authors: flag to control whether to include the authors in the species name :param validate: whether we should validate the data before returning :returns: a valid ABCD ElementTree """ import bauble.plugins.garden.institution as institution inst = institution.Institution() if not verify_institution(inst): msg = _('Some or all of the information about your institution or ' \ 'business is not complete. Please make sure that the ' \ 'Name, Technical Contact, Email, Contact and Institution ' 'Code fields are filled in.') utils.message_dialog(msg) institution.InstitutionEditor().start() return create_abcd(decorated_objects, authors, validate) datasets = DataSets() ds = ABCDElement(datasets, 'DataSet') tech_contacts = ABCDElement(ds, 'TechnicalContacts') tech_contact = ABCDElement(tech_contacts, 'TechnicalContact') # TODO: need to include contact information in bauble meta when # creating a new database ABCDElement(tech_contact, 'Name', text=inst.inst_technical_contact) ABCDElement(tech_contact, 'Email', text=inst.inst_email) cont_contacts = ABCDElement(ds, 'ContentContacts') cont_contact = ABCDElement(cont_contacts, 'ContentContact') ABCDElement(cont_contact, 'Name', text=inst.inst_contact) ABCDElement(cont_contact, 'Email', text=inst.inst_email) metadata = ABCDElement(ds, 'Metadata', ) description = ABCDElement(metadata, 'Description') # TODO: need to get the localized language representation = ABCDElement(description, 'Representation', attrib={'language': 'en'}) revision = ABCDElement(metadata, 'RevisionData') ABCDElement(revision, 'DateModified', text='2001-03-01T00:00:00') title = ABCDElement(representation, 'Title', text='TheTitle') units = ABCDElement(ds, 'Units') # build the ABCD unit for obj in decorated_objects: unit = ABCDElement(units, 'Unit') ABCDElement(unit, 'SourceInstitutionID', text=inst.inst_code) # TODO: don't really understand the SourceID element ABCDElement(unit, 'SourceID', text='Bauble') unit_id = ABCDElement(unit, 'UnitID', text=obj.get_UnitID()) ABCDElement(unit, 'DateLastEdited', text=obj.get_DateLastEdited()) # TODO: add list of verifications to Identifications # scientific name identification identifications = ABCDElement(unit, 'Identifications') identification = ABCDElement(identifications, 'Identification') result = ABCDElement(identification, 'Result') taxon_identified = ABCDElement(result, 'TaxonIdentified') higher_taxa = ABCDElement(taxon_identified, 'HigherTaxa') higher_taxon = ABCDElement(higher_taxa, 'HigherTaxon') # TODO: ABCDDecorator should provide an iterator so that we can # have multiple HigherTaxonName's higher_taxon_name = ABCDElement(higher_taxon, 'HigherTaxonName', text=obj.get_family()) higher_taxon_rank = ABCDElement(higher_taxon, 'HigherTaxonRank', text='familia') scientific_name = ABCDElement(taxon_identified, 'ScientificName') ABCDElement(scientific_name, 'FullScientificNameString', text=obj.get_FullScientificNameString(authors)) name_atomised = ABCDElement(scientific_name, 'NameAtomised') botanical = ABCDElement(name_atomised, 'Botanical') ABCDElement(botanical, 'GenusOrMonomial', text=obj.get_GenusOrMonomial()) ABCDElement(botanical, 'FirstEpithet', text=obj.get_FirstEpithet()) author_team = obj.get_AuthorTeam() if author_team is not None: ABCDElement(botanical, 'AuthorTeam', text=author_team) ABCDElement(identification, 'PreferredFlag', text='true') # vernacular name identification # TODO: should we include all the vernacular names or only the default # one vernacular_name = obj.get_InformalNameString() if vernacular_name is not None: identification = ABCDElement(identifications, 'Identification') result = ABCDElement(identification, 'Result') taxon_identified = ABCDElement(result, 'TaxonIdentified') ABCDElement(taxon_identified, 'InformalNameString', text=vernacular_name) # add all the extra non standard elements obj.extra_elements(unit) # TODO: handle verifiers/identifiers # TODO: RecordBasis # notes are last in the schema and extra_elements() shouldn't # add anything that comes past Notes, e.g. RecordURI, # EAnnotations, UnitExtension notes = obj.get_Notes() if notes: ABCDElement(unit, 'Notes', text=notes) if validate: check(validate_xml(datasets), 'ABCD data not valid') return ElementTree(datasets)
def set_privilege(role, privilege): """Set the role's privileges. Arguments: - `role`: - `privilege`: """ check(privilege in ("read", "write", "admin", None), "invalid privilege: %s" % privilege) conn = db.engine.connect() trans = conn.begin() if privilege: privs = _privileges[privilege] try: # revoke everything first for table in db.metadata.sorted_tables: stmt = "revoke all on table %s from %s;" % (table.name, role) conn.execute(stmt) for col in table.c: if hasattr(col, "sequence"): stmt = "revoke all on sequence %s from %s" % (col.sequence.name, role) conn.execute(stmt) stmt = "revoke all on database %s from %s" % (bauble.db.engine.url.database, role) conn.execute(stmt) stmt = "alter role %s with nocreaterole" % role conn.execute(stmt) # privilege is None so all permissions are revoked if not privilege: trans.commit() conn.close() return # change privileges on the database if privilege == "admin": stmt = "grant all on database %s to %s" % (bauble.db.engine.url.database, role) if privilege == "admin": stmt += " with grant option" conn.execute(stmt) stmt = "alter role %s with createuser" % role conn.execute(stmt) # grant privileges on the tables and sequences for table in bauble.db.metadata.sorted_tables: tbl_privs = filter(lambda x: x.lower() in _table_privs, privs) for priv in tbl_privs: stmt = "grant %s on %s to %s" % (priv, table.name, role) if privilege == "admin": stmt += " with grant option" # debug(stmt) conn.execute(stmt) for col in table.c: seq_privs = filter(lambda x: x.lower() in __sequence_privs, privs) for priv in seq_privs: if hasattr(col, "sequence"): stmt = "grant %s on sequence %s to %s" % (priv, col.sequence.name, role) # debug(stmt) if privilege == "admin": stmt += " with grant option" conn.execute(stmt) except Exception, e: error("users.set_privilege(): %s" % utils.utf8(e)) trans.rollback() raise
def on_query(self, s, loc, tokens): """ Called when the parser hits a query token. Queries can use more database specific features. This also means that the same query might not work the same on different database types. For example, on a PostgreSQL database you can use ilike but this would raise an error on SQLite. """ # The method requires that the underlying database support # union and intersect. At the time of writing this MySQL # didn't. # TODO: support 'not' a boolean op as well, e.g sp where # genus.genus=Maxillaria and not genus.family=Orchidaceae domain, expr = tokens check(domain in self._domains or domain in self._shorthand, 'Unknown search domain: %s' % domain) if domain in self._shorthand: domain = self._shorthand[domain] cls = self._domains[domain][0] main_query = self._session.query(cls) mapper = class_mapper(cls) expr_iter = iter(expr) boolop = None for e in expr_iter: idents, cond, val = e # debug('cls: %s, idents: %s, cond: %s, val: %s' # % (cls.__name__, idents, cond, val)) if val == 'None': val = None if cond == 'is': cond = '=' elif cond == 'is not': cond = '!=' elif cond in ('ilike', 'icontains', 'ihas'): return col.op.ilike(val) # cond = lambda col: \ # lambda val: utils.ilike(col, '%s' % val) if len(idents) == 1: # we get here when the idents only refer to a property # on the mapper table..i.e. a column col = idents[0] msg = _('The %(tablename)s table does not have a '\ 'column named "%(columname)s"') % \ dict(tablename=mapper.local_table.name, columname=col) check(col in mapper.c, msg) if isinstance(cond, str): #clause = getattr(cls, col).op(cond)(utils.utf8(val)) clause = getattr(cls, col).op(cond)(val) else: #clause = cond(getattr(cls, col))(utils.utf8(val)) clause = cond(getattr(cls, col))(val) query = self._session.query(cls).filter(clause).order_by(None) else: # we get here when the idents refer to a relation on a # mapper/table relations = idents[:-1] col = idents[-1] query = self._session.query(cls) query = query.join(*relations) # NOTE: SA07 - this depends on Query._joinpoint not changing, # it changed in SA05 which broke this local_table = query._joinpoint['prev'][0][1].local_table if isinstance(cond, str): #clause = local_table.c[col].op(cond)(utils.utf8(val)) clause = local_table.c[col].op(cond)(val) else: #clause = cond(local_table.c[col])(utils.utf8(val)) clause = cond(local_table.c[col])(val) query = query.filter(clause).order_by(None) if boolop == 'or': main_query = main_query.union(query) elif boolop == 'and': main_query = main_query.intersect(query) else: main_query = query try: boolop = next(expr_iter) except StopIteration: pass self._results[self._result_keys[cls]] = main_query.order_by(None).all()
def get_objects(self): '''return the list of objects to be exported if "based_on" is "selection", return the top level selection only. if "based_on" is something else, return all that is needed to create a complete export. ''' if self._choices['based_on'] == 'selection': class EmptySelectionException(Exception): pass from bauble.view import SearchView view = bauble.gui.get_view() try: check(isinstance(view, SearchView)) tree_view = view.results_view.get_model() check(tree_view is not None) except CheckConditionError: utils.message_dialog(_('Search for something first.')) return return [row[0] for row in tree_view] ## export disregarding selection result = [] if self._choices['based_on'] == 'plants': plants = self.session.query(Plant).order_by(Plant.code).join( Accession).order_by(Accession.code).all() plantnotes = self.session.query(PlantNote).all() ## only used locations and accessions locations = self.session.query(Location).filter( Location.id.in_([j.location_id for j in plants])).all() accessions = self.session.query(Accession).filter( Accession.id.in_([j.accession_id for j in plants])).order_by( Accession.code).all() ## notes are linked in opposite direction accessionnotes = self.session.query(AccessionNote).filter( AccessionNote.accession_id.in_( [j.id for j in accessions])).all() # extend results with things not further used result.extend(locations) result.extend(plants) result.extend(plantnotes) elif self._choices['based_on'] == 'accessions': accessions = self.session.query(Accession).order_by( Accession.code).all() accessionnotes = self.session.query(AccessionNote).all() ## now the taxonomy, based either on all species or on the ones used if self._choices['based_on'] == 'taxa': species = self.session.query(Species).order_by( Species.sp).all() else: # prepend results with accession data result = accessions + accessionnotes + result species = self.session.query(Species).filter( Species.id.in_([j.species_id for j in accessions])).order_by( Species.sp).all() ## and all used genera and families genera = self.session.query(Genus).filter( Genus.id.in_([j.genus_id for j in species])).order_by( Genus.genus).all() families = self.session.query(Familia).filter( Familia.id.in_([j.family_id for j in genera])).order_by( Familia.family).all() ## prepend the result with the taxonomic information result = families + genera + species + result ## done, return the result return result
def assign_completions_handler(self, widget, get_completions, on_select=lambda v: v): """ Dynamically handle completions on a gtk.Entry. :param widget: a gtk.Entry instance or widget name :param get_completions: the method to call when a list of completions is requested, returns a list of completions :param on_select: callback for when a value is selected from the list of completions """ if not isinstance(widget, gtk.Entry): widget = self.view.widgets[widget] PROBLEM = hash(widget.get_name()) def add_completions(text): if get_completions is None: # get_completions is None usually means that the # completions model already has a static list of # completions return # get the completions using [0:key_length] as the start of # the string def idle_callback(values): completion = widget.get_completion() utils.clear_model(completion) completion_model = gtk.ListStore(object) for v in values: completion_model.append([v]) completion.set_model(completion_model) key_length = widget.get_completion().props.minimum_key_length values = get_completions(text[:key_length]) gobject.idle_add(idle_callback, values) def on_changed(entry, *args): text = entry.get_text() comp = entry.get_completion() comp_model = comp.get_model() found = [] if comp_model: # search the tree model to see if the text in the # entry matches one of the completions, if so then # emit the match-selected signal, this allows us to # type a match in the entry without having to select # it from the popup def _cmp(row, data): return utils.utf8(row[0]) == text found = utils.search_tree_model(comp_model, text, _cmp) if len(found) == 1: v = comp.get_model()[found[0]][0] # only auto select if the full string has been entered if text.lower() == utils.utf8(v).lower(): comp.emit('match-selected', comp.get_model(), found[0]) else: found = None if text != '' and not found and PROBLEM not in self.problems: self.add_problem(PROBLEM, widget) on_select(None) key_length = widget.get_completion().props.minimum_key_length if (not comp_model and len(text)>key_length) or \ len(text) == key_length: add_completions(text) # if entry is empty select nothing and remove all problem if text == '': on_select(None) self.remove_problem(PROBLEM, widget) return True def on_match_select(completion, compl_model, treeiter): value = compl_model[treeiter][0] # temporarily block the changed ID so that this function # doesn't get called twice widget.handler_block(_changed_sid) widget.props.text = utils.utf8(value) widget.handler_unblock(_changed_sid) self.remove_problem(PROBLEM, widget) on_select(value) return True # return True or on_changed() will be called with '' completion = widget.get_completion() check(completion is not None, 'the gtk.Entry %s doesn\'t have a '\ 'completion attached to it' % widget.get_name()) _changed_sid = self.view.connect(widget, 'changed', on_changed) self.view.connect(completion, 'match-selected', on_match_select)