Beispiel #1
0
 def candidate(cls):
     return relationship(
         'Candidate',
         backref=backref(
             camel_to_under(cls.__name__) + 's',
             cascade='all, delete-orphan',
             cascade_backrefs=False),
         cascade_backrefs=False)
Beispiel #2
0
 def candidate(cls):
     return relationship(
         "Candidate",
         backref=backref(
             camel_to_under(cls.__name__) + "s",
             cascade="all, delete-orphan",
             cascade_backrefs=False,
         ),
         cascade_backrefs=False,
     )
Beispiel #3
0
 def key(cls):
     return relationship(
         '%sKey' % cls.__name__,
         backref=backref(
             camel_to_under(cls.__name__) + 's',
             cascade='all, delete-orphan'))
Beispiel #4
0
 def __tablename__(cls):
     return camel_to_under(cls.__name__)
Beispiel #5
0
def candidate_subclass(class_name,
                       args,
                       table_name=None,
                       cardinality=None,
                       values=None):
    """
    Creates and returns a Candidate subclass with provided argument names,
    which are Context type. Creates the table in DB if does not exist yet.

    Import using:

    .. code-block:: python

        from fonduer.models import candidate_subclass

    :param class_name: The name of the class, should be "camel case" e.g.
        NewCandidate
    :param args: A list of names of consituent arguments, which refer to the
        Contexts--representing mentions--that comprise the candidate
    :param table_name: The name of the corresponding table in DB; if not
        provided, is converted from camel case by default, e.g. new_candidate
    :param cardinality: The cardinality of the variable corresponding to the
        Candidate. By default is 2 i.e. is a binary value, e.g. is or is not
        a true mention.
    """
    if table_name is None:
        table_name = camel_to_under(class_name)

    # If cardinality and values are None, default to binary classification
    if cardinality is None and values is None:
        values = [True, False]
        cardinality = 2

    # Else use values if present, and validate proper input
    elif values is not None:
        if cardinality is not None and len(values) != cardinality:
            raise ValueError("Number of values must match cardinality.")
        if None in values:
            raise ValueError("`None` is a protected value.")
        # Note that bools are instances of ints in Python...
        if any([isinstance(v, int) and not isinstance(v, bool) for v in values]):
            raise ValueError(
                ("Default usage of values is consecutive integers."
                 "Leave values unset if trying to define values as integers."))
        cardinality = len(values)

    # If cardinality is specified but not values, fill in with ints
    elif cardinality is not None:
        values = list(range(cardinality))

    class_spec = (args, table_name, cardinality, values)
    if class_name in candidate_subclasses:
        if class_spec == candidate_subclasses[class_name][1]:
            return candidate_subclasses[class_name][0]
        else:
            raise ValueError(
                'Candidate subclass ' + class_name +
                ' already exists in memory with incompatible ' +
                'specification: ' + str(candidate_subclasses[class_name][1]))
    else:
        # Set the class attributes == the columns in the database
        class_attribs = {

            # Declares name for storage table
            '__tablename__':
            table_name,

            # Connects candidate_subclass records to generic Candidate records
            'id':
            Column(
                Integer,
                ForeignKey('candidate.id', ondelete='CASCADE'),
                primary_key=True),

            # Store values & cardinality information in the class only
            'values':
            values,
            'cardinality':
            cardinality,

            # Polymorphism information for SQLAlchemy
            '__mapper_args__': {
                'polymorphic_identity': table_name
            },

            # Helper method to get argument names
            '__argnames__':
            args,
        }

        # Create named arguments, i.e. the entity mentions comprising the relation
        # mention
        # For each entity mention: id, cid ("canonical id"), and pointer to Context
        unique_args = []
        for arg in args:

            # Primary arguments are constituent Contexts, and their ids
            class_attribs[arg + '_id'] = Column(Integer,
                                                ForeignKey(
                                                    'context.id',
                                                    ondelete='CASCADE'))
            class_attribs[arg] = relationship(
                'Context',
                backref=backref(
                    table_name + '_' + arg + 's',
                    cascade_backrefs=False,
                    cascade='all, delete-orphan'),
                cascade_backrefs=False,
                foreign_keys=class_attribs[arg + '_id'])
            unique_args.append(class_attribs[arg + '_id'])

            # Canonical ids, to be set post-entity normalization stage
            class_attribs[arg + '_cid'] = Column(String)

        # Add unique constraints to the arguments
        class_attribs['__table_args__'] = (UniqueConstraint(*unique_args), )

        # Create class
        C = type(class_name, (Candidate, ), class_attribs)

        # Create table in DB
        if not _meta.engine.dialect.has_table(_meta.engine, table_name):
            C.__table__.create(bind=_meta.engine)

        candidate_subclasses[class_name] = C, class_spec

        return C
Beispiel #6
0
 def key(cls):
     return relationship(
         "%sKey" % cls.__name__,
         backref=backref(camel_to_under(cls.__name__) + "s",
                         cascade="all, delete-orphan"),
     )