def vocabulary_column(name, title, vocabulary): """Get getter for the enum-value of an enumerated column. """ if isinstance(vocabulary, basestring): # !+tmp vocabulary = get_vocabulary(vocabulary) #from bungeni.ui.vocabulary import VDEXVocabularyMixin def getter(context, formatter, vocabulary=vocabulary): # if this is a vocabulary factory, we need to instantiate with context # !+ but, when context is irrelevant, call-it-as-factory to get a # context-bound instance seems unnecessarily inefficient! # !+ VDEXVocabularyMixin-based FlatVDEXVocabularyFactory and # TreeVDEXVocabulary (but this is as yet never included in a listing) # already implement getTerm() -- that is all that is needed here, and # the term returned is already localized, so we really do not need to # call-it-as-factory to bind it to context on each lookup... #if not isinstance(vocabulary, VDEXVocabularyMixin): if IVocabularyFactory.providedBy(vocabulary): vocabulary = vocabulary(context) try: return vocabulary.getTerm(getattr(context, name)).title except LookupError: # !+NONE_LOOKUPERROR(mr, jul-2012) probably a vdex, # and getattr(context, name)... value = getattr(context, name) m = "******** LookupError: vocabulary [%s, length:%s] term value [%s] " \ "for context [%s.%s] -- vocabulary terms: %s" % ( vocabulary, len(vocabulary), value, context, name, [ (t.value, t.token, t.title) for t in vocabulary._terms ]) log.error(m) # we should only have a LookupError on a None value (and it is not # defined in the vocabulary), in which case we return None if value is not None: raise return column.GetterColumn(title, getter)
def batch_serialize(type_key="*", start_date=None, end_date=None): """Serialize all objects of `type_key` or all types if with a wildcard(*) as the type key. Item set may be filtered by status date (start_date and/or end date) range. """ # keep count of serialized objects for feedback serialized_count = 0 # list of domain classes to be serialized domain_models = [] if type_key == "*": types_vocab = get_vocabulary("serializable_type") # we add the legislature and the chamber first for term in types_vocab(None): if term.value in ("legislature", "chamber"): info = capi.get_type_info(term.value) domain_models.append(info.domain_model) # we add the rest now for term in types_vocab(None): if term.value == "*": continue if term.value not in ("legislature", "chamber"): info = capi.get_type_info(term.value) domain_models.append(info.domain_model) else: info = capi.get_type_info(type_key) if info.workflow: domain_models.append(info.domain_model) session = Session() for domain_model in domain_models: query = session.query(domain_model) if IWorkflowed.implementedBy(domain_model) and (start_date or end_date): column = domain_model.status_date if start_date and end_date: expression = sql.between(column, start_date, end_date) elif start_date: expression = (column>=start_date) elif end_date: expression = (column<=end_date) query = query.filter(expression) objects = query.all() # !+FILTER(ah, 2014-09-19) adding a filter here - sometimes there is a mismatch # between the count shown on the screen i.e. X items sent for serialization and # and only X-n items appear in the queue - there seem to be empty objects returned # sometimes, so eliminating those objects = filter(None, objects) map(queue_object_serialization, objects) log.error(" COUNTING_TYPES_SERIALIZED -- %s COUNT -- %s", domain_model, len(objects)) serialized_count += len(objects) return serialized_count
def validate_derived_table_schema(self, action, data): """Look-ahead validate against database contraints. """ dm = self.domain_model ti = capi.get_type_info(dm) derived_table_schema = ti.derived_table_schema errors = [] for name in derived_table_schema: ff = self.form_fields.get(name) # skip if no form does not define a corresponding form field if not ff: continue # !+ skip if field not included in this form "mode"? #if name not in getattr("%s_columns" % (self.mode), ti.descriptor_model): # continue field = derived_table_schema.get(name) assert field is ff.field, name # !+TMP sanity check value = data.get(name, None) ''' !+VTF disabling for now, as attempting to preset a contextualized vocab fails for subsequent lodaing of a *view*, as this is executed when validating a *submit* -- may still work if executed right moment. # !+VTF bungeni.ui.fields.VocabularyTextField -- here a vocabulary # field is not necessarily the type defined by bungeni # e.g. zope.schema._field.Choice that during validation attempts to # retrieve the vocabulary and initialize it with a None context... # To preempt the error that this causes, we check for and stuff # an appropiately initialized vocabulary instance onto this field... if hasattr(field, "vocabulary"): # preset a vocabulary on self.context, or reset with one off current # context if the one preset previously was off a different context if (field.vocabulary is None or ( # a vocab was preset previously hasattr(field, "_vtf_last_context") and # and it was prepared with a different context getattr(field, "_vtf_last_context") is not self.context) ): from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)(self.context) # remember last context used to preset vocabulary field._vtf_last_context = self.context log.debug("Validation of vocabulary field (%r, %r) with value=%r -- " "stuffing vocabulary instance %s [context: %r] onto field %s ...", name, field.vocabularyName, value, field.vocabulary, self.context, field) ''' # !+VTF a "temporary" and simpler version of above, to avoid # AttributeError in try block below (and incorrectly failed validation). TMP_SET_VOCAB = False if hasattr(field, "vocabulary") and field.vocabulary is None: TMP_SET_VOCAB = True from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)(self.context) # standard field validation !+ necessary, already called elsewhere? try: widget_error = field.validate(value) except (RequiredMissing,) as exc: widget_error = exc # !+ other possible exceptions, should never pass here? except (formlib.form.NoInputData, interface.Invalid, Exception,) as exc: widget_error = exc probing.log_exc(sys.exc_info(), log_handler=log.error) log.debug(probing.class_inheritance(exc)) log.error("\n" " %r.validate(%r) FAILED (field %r)\n" " [context: %r]", field, value, name, self.context) #raise # !+? # !+VTF clear any vocabulary that we preset above if TMP_SET_VOCAB: field.vocabulary = None if widget_error: errors.append(self.set_widget_error(name, widget_error)) widget_error = None continue # get db column definition domain_attr = getattr(dm, name) if not isinstance(domain_attr, sa.orm.attributes.InstrumentedAttribute): continue domain_attr_property = domain_attr.property assert isinstance(domain_attr_property, sa.orm.properties.ColumnProperty), name # !+TMP sanity check # !+MULTIPLE_COLUMNS only single columns supported (so far) if len(domain_attr_property.columns) == 1: col = domain_attr_property.columns[0] #!+AttributeError: '_Label' object has no attribute 'nullable' # not: sa.sql.expression._Label, sa.sql.expression.ColumnElement assert isinstance(col, sa.schema.Column), col else: log.warn("SQLAlchemy property %r NOT defined as a single column, " "skipping derived_table_schema validation...", name) continue # validate against db column definition # nullable if value is None: if not col.nullable: errors.append(self.set_widget_error(name, _(u"May not be null"))) continue # sa.String if isinstance(col.type, sa.types.String): # length length = col.type.length if length is not None: if length < len(value): errors.append(self.set_widget_error(name, _(u"May not be longer than ${length}", mapping={"length": length}))) return errors
def validate_derived_table_schema(self, action, data): """Look-ahead validate against database contraints. """ dm = self.domain_model ti = capi.get_type_info(dm) derived_table_schema = ti.derived_table_schema errors = [] for name in derived_table_schema: ff = self.form_fields.get(name) # skip if no form does not define a corresponding form field if not ff: continue # !+ skip if field not included in this form "mode"? #if name not in getattr("%s_columns" % (self.mode), ti.descriptor_model): # continue field = derived_table_schema.get(name) assert field is ff.field, name # !+TMP sanity check value = data.get(name, None) ''' !+VTF disabling for now, as attempting to preset a contextualized vocab fails for subsequent lodaing of a *view*, as this is executed when validating a *submit* -- may still work if executed right moment. # !+VTF bungeni.ui.fields.VocabularyTextField -- here a vocabulary # field is not necessarily the type defined by bungeni # e.g. zope.schema._field.Choice that during validation attempts to # retrieve the vocabulary and initialize it with a None context... # To preempt the error that this causes, we check for and stuff # an appropiately initialized vocabulary instance onto this field... if hasattr(field, "vocabulary"): # preset a vocabulary on self.context, or reset with one off current # context if the one preset previously was off a different context if (field.vocabulary is None or ( # a vocab was preset previously hasattr(field, "_vtf_last_context") and # and it was prepared with a different context getattr(field, "_vtf_last_context") is not self.context) ): from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)(self.context) # remember last context used to preset vocabulary field._vtf_last_context = self.context log.debug("Validation of vocabulary field (%r, %r) with value=%r -- " "stuffing vocabulary instance %s [context: %r] onto field %s ...", name, field.vocabularyName, value, field.vocabulary, self.context, field) ''' # !+VTF a "temporary" and simpler version of above, to avoid # AttributeError in try block below (and incorrectly failed validation). TMP_SET_VOCAB = False if hasattr(field, "vocabulary") and field.vocabulary is None: TMP_SET_VOCAB = True from bungeni.alchemist.utils import get_vocabulary field.vocabulary = get_vocabulary(field.vocabularyName)( self.context) # standard field validation !+ necessary, already called elsewhere? try: widget_error = field.validate(value) except (RequiredMissing, ) as exc: widget_error = exc # !+ other possible exceptions, should never pass here? except ( formlib.form.NoInputData, interface.Invalid, Exception, ) as exc: widget_error = exc probing.log_exc(sys.exc_info(), log_handler=log.error) log.debug(probing.class_inheritance(exc)) log.error( "\n" " %r.validate(%r) FAILED (field %r)\n" " [context: %r]", field, value, name, self.context) #raise # !+? # !+VTF clear any vocabulary that we preset above if TMP_SET_VOCAB: field.vocabulary = None if widget_error: errors.append(self.set_widget_error(name, widget_error)) widget_error = None continue # get db column definition domain_attr = getattr(dm, name) if not isinstance(domain_attr, sa.orm.attributes.InstrumentedAttribute): continue domain_attr_property = domain_attr.property assert isinstance( domain_attr_property, sa.orm.properties.ColumnProperty), name # !+TMP sanity check # !+MULTIPLE_COLUMNS only single columns supported (so far) if len(domain_attr_property.columns) == 1: col = domain_attr_property.columns[0] #!+AttributeError: '_Label' object has no attribute 'nullable' # not: sa.sql.expression._Label, sa.sql.expression.ColumnElement assert isinstance(col, sa.schema.Column), col else: log.warn( "SQLAlchemy property %r NOT defined as a single column, " "skipping derived_table_schema validation...", name) continue # validate against db column definition # nullable if value is None: if not col.nullable: errors.append( self.set_widget_error(name, _(u"May not be null"))) continue # sa.String if isinstance(col.type, sa.types.String): # length length = col.type.length if length is not None: if length < len(value): errors.append( self.set_widget_error( name, _(u"May not be longer than ${length}", mapping={"length": length}))) return errors
def vocabulary(self): return get_vocabulary(self.vocabulary_name)
def obj2dict(obj, depth, parent=None, include=[], exclude=[], lang=None, root_key=None): """ Returns dictionary representation of a domain object. """ if lang is None: lang = getattr(obj, "language", capi.default_language) result = {} obj = zope.security.proxy.removeSecurityProxy(obj) descriptor = None if IAlchemistContent.providedBy(obj): try: descriptor = utils.get_descriptor(obj) except KeyError: log.error("Could not get descriptor for IAlchemistContent %r", obj) if parent is not None and IWorkflowed.providedBy(obj): permissions = get_object_state_rpm(obj).permissions result["permissions"] = get_permissions_dict(permissions) result["tags"] = IStateController(obj).get_state().tags # Get additional attributes for name in include: value = getattr(obj, name, None) if value is None: continue if not name.endswith("s"): name += "s" if isinstance(value, collections.Iterable): res = [] # !+ allowance for non-container-api-conformant alchemist containers if IAlchemistContainer.providedBy(value): value = value.values() for item in value: i = obj2dict(item, 0, lang=lang, root_key=root_key) res.append(i) result[name] = res else: result[name] = value # Get mapped attributes seen_keys = [] mapper = class_mapper(obj.__class__) for mproperty in mapper.iterate_properties: if mproperty.key.startswith("_vp"): #skip vertical props continue if mproperty.key in exclude: continue seen_keys.append(mproperty.key) value = getattr(obj, mproperty.key) if value == parent: continue if value is None: continue if isinstance(mproperty, RelationshipProperty) and depth > 0: if isinstance(value, collections.Iterable): result[mproperty.key] = [] for item in value: # !+DEPTH(ah, 2014-09-19) depth was set to 1 here, this causes # a very deep branching for upper level groups like legislature and chamber # and legislature times out occasionally. Doesnt seem neccessary to go depth=1 # for child objects, because they get serialized independently anyway, changing # depth to depth-1 so all dependent objects are iterated 1 level lower than the # parent. # UPDATE(ah, 2014-11-03) Item Schedule is an exceptional case of an object # whose context is within a parent container but is not visible outside of the sitting # it is not a type defined in types.xml and does not have its own # wokflow so we need to handle that in a unique way # we don't decrement the depth and instead process it as is active_depth = depth if item.__class__.__name__ == "ItemSchedule": active_depth = depth else: active_depth = depth-1 result[mproperty.key].append( obj2dict( item, active_depth, parent=obj, include=["owner", "item_schedule", "item_schedule_discussion"], exclude=exclude + INNER_EXCLUDES, lang=lang, root_key=root_key ) ) else: result[mproperty.key] = obj2dict(value, depth-1, parent=obj, include=["owner"], exclude=exclude + INNER_EXCLUDES, lang=lang, root_key=root_key ) else: if isinstance(mproperty, RelationshipProperty): continue elif isinstance(mproperty, ColumnProperty): columns = mproperty.columns if len(columns) == 1: if is_column_binary(columns[0]): fname = PersistFiles.store_file(obj, parent, columns[0].key, root_key) if fname: result[columns[0].key] = dict(saved_file=fname) continue if descriptor: columns = mproperty.columns is_foreign = False if len(columns) == 1: if len(columns[0].foreign_keys): is_foreign = True if (not is_foreign) and (mproperty.key in descriptor.keys()): field = descriptor.get(mproperty.key) if (field and field.property and (schema.interfaces.IChoice.providedBy(field.property) or IVocabularyTextField.providedBy(field.property)) ): factory = field.property.vocabulary or field.property.source if factory is None: vocab_name = getattr(field.property, "vocabularyName", None) factory = get_vocabulary(vocab_name) # !+VOCABULARIES(mb, aug-2012)some vocabularies # expect an interaction to generate values # todo - update these vocabularies to work # with no request e.g. in notification threads display_name = None try: vocabulary = factory(obj) # handle vdex hierarchical terms if ITreeVocabulary.providedBy(factory): values = value.splitlines() term_values = [] for val in values: term_values.append(dict( name=mproperty.key, value=val, displayAs=factory.getTermCaption( factory.getTermById(val), lang=lang))) result[mproperty.key] = term_values continue term = vocabulary.getTerm(value) if lang: if hasattr(factory, "getTermCaption"): display_name = factory.getTermCaption( factory.getTermById(value), lang=lang) else: display_name = translate( term.title or term.value, target_language=lang, domain="bungeni") else: display_name = term.title or term.value except zope.security.interfaces.NoInteraction: log.error("This vocabulary %s expects an interaction " "to generate terms.", factory) # try to use dc adapter lookup try: _prop = mapper.get_property_by_column( mproperty.columns[0]) _prop_value = getattr(obj, _prop.key) dc = IDCDescriptiveProperties(_prop_value, None) if dc: display_name = IDCDescriptiveProperties( _prop_value).title except KeyError: log.warn("No display text found for %s on " "object %s. Unmapped in orm.", property.key, obj) except Exception, e: log.error("Could not instantiate vocabulary %s. " "Exception: %s", factory, e) finally: # fallback we cannot look up vocabularies/dc if display_name is None: if not isinstance(value, unicode): display_name = unicode(value) if display_name is not None: result[mproperty.key] = dict( name=mproperty.key, value=value, displayAs=display_name) continue