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.candidates.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 constituent 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(
                f"Candidate subclass {class_name} "
                f"already exists in memory with incompatible "
                f"specification: {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__": [_.__tablename__ for _ in args],
            "mentions": args,
        }
        class_attribs["document_id"] = Column(
            Integer, ForeignKey("document.id", ondelete="CASCADE")
        )
        class_attribs["document"] = relationship(
            "Document",
            backref=backref(table_name + "s", cascade="all, delete-orphan"),
            foreign_keys=class_attribs["document_id"],
        )

        # Create named arguments, i.e. the entity mentions comprising the
        # relation mention.
        unique_args = []
        for arg in args:
            # Primary arguments are constituent Contexts, and their ids
            class_attribs[arg.__tablename__ + "_id"] = Column(
                Integer, ForeignKey(arg.__tablename__ + ".id", ondelete="CASCADE")
            )
            class_attribs[arg.__tablename__] = relationship(
                arg.__name__,
                backref=backref(
                    table_name + "_" + arg.__tablename__ + "s",
                    cascade_backrefs=False,
                    cascade="all, delete-orphan",
                ),
                cascade_backrefs=False,
                foreign_keys=class_attribs[arg.__tablename__ + "_id"],
            )
            unique_args.append(class_attribs[arg.__tablename__ + "_id"])

        # 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
def candidate_subclass ( class_name , args , table_name = None , cardinality = None , values = None ) : if table_name is None : table_name = camel_to_under ( class_name ) if cardinality is None and values is None : values = [ True , False ] cardinality = 2 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." ) 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 ) 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 ( f"Candidate subclass {class_name} " f"already exists in memory with incompatible " f"specification: {candidate_subclasses[class_name][1]}" ) else : class_attribs = { "__tablename__" : table_name , "id" : Column ( Integer , ForeignKey ( "candidate.id" , ondelete = "CASCADE" ) , primary_key = True , ) , "values" : values , "cardinality" : cardinality , "__mapper_args__" : { "polymorphic_identity" : table_name } , "__argnames__" : [ _ . __tablename__ for _ in args ] , "mentions" : args , } class_attribs [ "document_id" ] = Column ( Integer , ForeignKey ( "document.id" , ondelete = "CASCADE" ) ) class_attribs [ "document" ] = relationship ( "Document" , backref = backref ( table_name + "s" , cascade = "all, delete-orphan" ) , foreign_keys = class_attribs [ "document_id" ] , ) unique_args = [ ] for arg in args : class_attribs [ arg . __tablename__ + "_id" ] = Column ( Integer , ForeignKey ( arg . __tablename__ + ".id" , ondelete = "CASCADE" ) ) class_attribs [ arg . __tablename__ ] = relationship ( arg . __name__ , backref = backref ( table_name + "_" + arg . __tablename__ + "s" , cascade_backrefs = False , cascade = "all, delete-orphan" , ) , cascade_backrefs = False , foreign_keys = class_attribs [ arg . __tablename__ + "_id" ] , ) unique_args . append ( class_attribs [ arg . __tablename__ + "_id" ] ) class_attribs [ "__table_args__" ] = ( UniqueConstraint ( * unique_args ) , ) C = type ( class_name , ( Candidate , ) , class_attribs ) 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
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.candidates.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 constituent 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.