class TestNestedModel(Model):

    partition = columns.UUID(primary_key=True, default=uuid4)
    list_list = columns.List(columns.List(columns.Integer), required=False)
    map_list = columns.Map(columns.Text,
                           columns.List(columns.Text),
                           required=False)
    set_tuple = columns.Set(columns.Tuple(columns.Integer, columns.Integer),
                            required=False)
 def test_instantiation_with_column_class(self):
     """
     Tests that columns instantiated with a column class work properly
     and that the class is instantiated in the constructor
     """
     column = columns.List(columns.Text)
     self.assertIsInstance(column.value_col, columns.Text)
 def test_to_python(self):
     """ Tests that to_python of value column is called """
     column = columns.List(JsonTestColumn)
     val = [1, 2, 3]
     db_val = column.to_database(val)
     self.assertEqual(db_val, [json.dumps(v) for v in val])
     py_val = column.to_python(db_val)
     self.assertEqual(py_val, val)
class TestModelSave(Model):
    partition = columns.UUID(primary_key=True, default=uuid4)
    cluster = columns.Integer(primary_key=True)
    count = columns.Integer(required=False)
    text = columns.Text(required=False, index=True)
    text_set = columns.Set(columns.Text, required=False)
    text_list = columns.List(columns.Text, required=False)
    text_map = columns.Map(columns.Text, columns.Text, required=False)
class TestIndexSetModel(Model):
    partition = columns.UUID(primary_key=True)
    int_set = columns.Set(columns.Integer, index=True)
    int_list = columns.List(columns.Integer, index=True)
    text_map = columns.Map(columns.Text, columns.DateTime, index=True)
    mixed_tuple = columns.Tuple(columns.Text,
                                columns.Integer,
                                columns.Text,
                                index=True)
    def test_instantiation_with_column_class(self):
        """
        Tests that columns instantiated with a column class work properly
        and that the class is instantiated in the constructor

        @since 3.1
        @jira_ticket PYTHON-478
        @expected_result types are instantiated correctly

        @test_category object_mapper
        """
        list_list = columns.List(columns.List(columns.Integer), required=False)
        map_list = columns.Map(columns.Text, columns.List(columns.Text), required=False)
        set_tuple = columns.Set(columns.Tuple(columns.Integer, columns.Integer), required=False)

        self.assertIsInstance(list_list, columns.List)
        self.assertIsInstance(list_list.types[0], columns.List)
        self.assertIsInstance(map_list.types[0], columns.Text)
        self.assertIsInstance(map_list.types[1], columns.List)
        self.assertIsInstance(set_tuple.types[0], columns.Tuple)
        class OuterModel(Model):

            name = columns.Text(primary_key=True)
            first_name = columns.Text()
            nested = columns.List(columns.UserDefinedType(NestedUdt))
            simple = columns.UserDefinedType(NestedUdt)
 class Container(Model):
     id = columns.UUID(primary_key=True, default=uuid4)
     names = columns.List(columns.UserDefinedType(Name))
    situation = columns.Text(required=True)

    short_description = columns.Text()

    long_description = columns.Text()

    # The date when the company was founded
    foundation_date = columns.Date()

    # Country of the company
    # ISO 3166-1 alpha 3 code
    country_code = columns.Text(min_length=3, max_length=3)

    # A list of specialties of the company
    specialties = columns.List(value_type=columns.Text)

    # A field that represent a map of key-value
    # We use caravaggio KeyEncodedMap that appends the field name
    # to each of the keys in order to make them indexable by the
    # Search Indexer.
    websites = KeyEncodedMap(
        key_type=columns.Text, value_type=columns.Text)

    # A field that represents a raw JSON content
    extra_data = columns.Text()

    latitude = columns.Float()
    longitude = columns.Float()

    coordinates = columns.Text()
class Company(CustomDjangoCassandraModel):
    """
    A public traded company
    """
    __table_name__ = "company"

    # A unique identifier of the entity
    _id = columns.UUID(partition_key=True, default=uuid.uuid4)

    # The owner of the data. Who own's the company data persisted
    user = columns.Text(primary_key=True)

    # When was created the entity and the last modification date
    created_at = columns.DateTime(default=datetime.utcnow)
    updated_at = columns.DateTime(default=datetime.utcnow)

    # Controls if the entity is active or has been deleted
    is_deleted = columns.Boolean(default=False)
    deleted_reason = columns.Text()

    # The name of the company
    name = columns.Text(required=True)

    # A short description about the company
    short_description = columns.Text()

    # The company domain (e.g. preseries.com)
    domain = columns.Text(max_length=50)

    # The date when the company was founded
    foundation_date = columns.Date()

    # The date of the latest funding round
    last_round = columns.Date()

    # The total number of funding rounds
    round_notes = columns.Text()

    # Country of the company
    # ISO 3166-1 alpha 3 code
    country_code = columns.Text(min_length=3, max_length=3)

    # The stock trading symbol
    stock_symbol = columns.Text()

    # Contact email of the company
    contact_email = columns.Text()

    # The IDs of the founders of the company
    founders = columns.List(value_type=columns.UUID)

    # Address of the headquarters of the company
    address = UserDefinedType(Address)

    # A list of specialties of the company
    specialties = columns.List(value_type=columns.Text)

    # The counters of the latest followers in twitter
    #  (example of list of integers)
    latest_twitter_followers = columns.List(value_type=columns.Integer)

    # A field that represent a map of key-value
    # We use caravaggio KeyEncodedMap that appends the field name
    # to each of the keys in order to make them indexable by the
    # Search Indexer.
    websites = KeyEncodedMap(key_type=columns.Text, value_type=columns.Text)

    # A field that represents a raw JSON with the crawler configurations, each
    # key is a reference to a crawler
    crawler_config = columns.Text()

    # A field that represents a raw JSON content
    extra_data = columns.Text()

    latitude = columns.Float()
    longitude = columns.Float()

    coordinates = columns.Text()

    class Meta:
        get_pk_field = '_id'

    def validate(self):
        super(Company, self).validate()
        if self.name == "test":
            raise ValidationError('The company name cannot be test')
class Task(CustomDjangoCassandraModel):
    """
    Represents a task that could be an on demand task or a batch task.

    Args:
        task_id: the task id that is the unique partition key.
        user: The user that asked for the task, if it is an ondemand task.
        created_at: the date of the creation of the task.
        updated_at: the date that we last updated the task.
        is_deleted: controls if the data is deleted.
        status: representes the actual status of the task, could be:
            - 0 (Created)
            - 1 (Queued)
            - 2 (In Progress)
            - 3 (Finished)
            - 4 (Faulty)
            - 5 (Unknown)
        kind: the name of the crawler that will execute the task.
        params: the set of params used to execute the crawler command, this
        will be saved as Text.
        params_map: the exactly same content as `params` but saved on a way
        that we can search using solr (KeyEncodedMap).
        options: the set of options that is used to guide the crawler during
        the execution, this will be saved as text.
        options_map: the exactly same content as `options` but saved on a way
        that we can search using solr (KeyEncodedMap).
        times_performed: keep track on how many times the task was run.
        type: the type of the task, could be OnDemand(1) or Batch(2)
    """

    __table_name__ = "davinci_task"
    _cassandra_consistency_level_read = ConsistencyLevel.ONE
    _cassandra_consistency_level_write = ConsistencyLevel.ALL

    # Force that all the values will reside in the seam node of the cluster
    task_id = columns.UUID(partition_key=True, default=uuid.uuid4)

    # The owner of the data. Who own's the company data persisted
    user = columns.Text()

    # When was created the entity and the last modification date
    created_at = columns.DateTime(default=timezone.now,
                                  primary_key=True,
                                  clustering_order="DESC")
    updated_at = columns.DateTime(default=timezone.now)

    # Controls if the entity is active or has been deleted
    is_deleted = columns.Boolean(default=False)

    status = columns.SmallInt(default=STATUS_CREATED)

    kind = columns.Text(required=True)

    params_map = KeyEncodedMap(key_type=columns.Text, value_type=columns.Text)

    params = columns.Text(required=True)

    options_map = KeyEncodedMap(key_type=columns.Text, value_type=columns.Text)

    options = columns.Text(required=False)

    times_performed = columns.SmallInt(default=0)

    type = columns.SmallInt(default=ON_DEMAND_TASK)

    more_info = columns.List(value_type=UserDefinedType(TaskMoreInfo))

    differences_from_last_version = columns.Text()

    inserted_fields = columns.List(value_type=columns.Text)

    updated_fields = columns.List(value_type=columns.Text)

    deleted_fields = columns.List(value_type=columns.Text)

    changed_fields = columns.List(value_type=columns.Text)

    logging_task = columns.Boolean(default=False)

    class Meta:
        get_pk_field = "task_id"

    def validate(self):
        super().validate()

        if self.type not in ALL_TASK_TYPES:
            raise ValidationError("Invalid task type [{0}]. Valid types are: "
                                  "{1}.".format(self.type, ALL_TASK_TYPES))

        if self.status not in ALL_STATUS:
            raise ValidationError(
                "Invalid task status [{0}]. Valid status are: "
                "{1}.".format(self.status, ALL_STATUS))
 def test_instantiation_with_column_instance(self):
     """
     Tests that columns instantiated with a column instance work properly
     """
     column = columns.List(columns.Text(min_length=100))
     self.assertIsInstance(column.value_col, columns.Text)
class TestListModel(Model):

    partition = columns.UUID(primary_key=True, default=uuid4)
    int_list = columns.List(columns.Integer, required=False)
    text_list = columns.List(columns.Text, required=False)
Beispiel #14
0
class User(Model):
    """User Model
    
    This is used to store a Radon user
    
    :param uuid: A uuid associated to the user
    :type uuid: :class:`columns.Text`
    :param name: The user name, used as the primary key
    :type name: :class:`columns.Text`
    :param email: The user email
    :type email: :class:`columns.Text`
    :param password: The user password, stored hashed
    :type password: :class:`columns.Text`
    :param administrator: A boolean if the user has admin access
    :type administrator: :class:`columns.Boolean`
    :param active: A boolean if the user is active or not
    :type active: :class:`columns.Boolean`
    :param ldap: A boolean if the user password has to be checked on a 
      LDAP server
    :type ldap: :class:`columns.Boolean`
    :param groups: A list of group names
    :type groups: :class:`columns.List`
    """

    uuid = columns.Text(default=default_uuid)
    name = columns.Text(primary_key=True, required=True)
    email = columns.Text(required=True)
    password = columns.Text(required=True)
    administrator = columns.Boolean(required=True, default=False)
    active = columns.Boolean(required=True, default=True)
    ldap = columns.Boolean(required=True, default=False)
    groups = columns.List(columns.Text, index=True)

    def add_group(self, groupname, username=None):
        """
        Add the user to a group
        
        :param groupname: The group to be added to
        :type groupname: str
        :param username: the name of the user who made the action
        :type username: str, optional
        """
        self.add_groups([groupname], username)

    def add_groups(self, ls_group, username=None):
        """
        Add the user to a list of groups
        
        :param ls_group: The groups to be added to
        :type groupname: List[str]
        :param username: the name of the user who made the action
        :type username: str, optional
        """
        new_groups = self.get_groups() + ls_group
        # remove duplicate
        new_groups = list(set(new_groups))
        self.update(groups=new_groups, username=username)

    def authenticate(self, password):
        """
        Check user password against an existing hash (hash)
    
        :param password: the password we want to test (plain)
        :type password: str
        
        :return: a boolean which indicate if the password is correct
        :rtype: bool
        """
        if self.active:
            if self.ldap:
                return verify_ldap_password(self.name, password)
            else:
                return verify_password(password, self.password)
        return False

    @classmethod
    def create(cls, **kwargs):
        """Create a user

        We intercept the create call so that we can correctly
        hash the password into an unreadable form
        
        :param name: the name of the user
        :type name: str
        :param password: The plain password to encrypt
        :type password: str
        :param username: the name of the user who made the action
        :type username: str, optional
        
        :return: The new created user
        :rtype: :class:`radon.model.User`
        """
        # username is the name of the user who initiated the call, it has to
        # be removed for the Cassandra call
        if "username" in kwargs:
            username = kwargs["username"]
            del kwargs["username"]
        else:
            username = radon.cfg.sys_lib_user
        kwargs["password"] = encrypt_password(kwargs["password"])

        if cls.objects.filter(name=kwargs["name"]).count():
            raise UserConflictError(kwargs["name"])

        user = super(User, cls).create(**kwargs)

        state = user.mqtt_get_state()
        payload = user.mqtt_payload({}, state)
        Notification.create_user(username, user.name, payload)
        return user

    def delete(self, username=None):
        """
        Delete the user in the database.
        
        :param username: the name of the user who made the action
        :type username: str, optional
        """
        state = self.mqtt_get_state()
        super(User, self).delete()
        payload = self.mqtt_payload(state, {})
        # username is the id of the user who did the operation
        # user.uuid is the id of the new user
        Notification.delete_user(username, self.name, payload)

    @classmethod
    def find(cls, name):
        """
        Find a user from his name.
        
        :param name: the name of the user
        :type name: str
        
        :return: The user which has been found
        :rtype: :class:`radon.model.User`
        """
        return cls.objects.filter(name=name).first()

    def get_groups(self):
        """
        Return the list of group names for the user
        
        :return: The list of groups
        :rtype: List[str]
        """
        return self.groups

    def is_active(self):
        """
        Check if the user is active
        
        :return: The user status
        :rtype: bool
        """
        return self.active

    def is_authenticated(self):
        """
        Check if the user is authenticated
        
        :return: The user status
        :rtype: bool
        """
        return True

    def mqtt_get_state(self):
        """
        Get the user state that will be used in the payload
        
        :return: The user state as a dictionary
        :rtype: dict
        """
        payload = dict()
        payload["uuid"] = self.uuid
        payload["name"] = self.name
        payload["email"] = self.email
        payload["active"] = self.active
        payload["groups"] = [g.name for g in Group.find_all(self.groups)]
        return payload

    def mqtt_payload(self, pre_state, post_state):
        """
        Get a string version of the payload of the message, with the pre and
        post states. The pre and post states are stored in a dictionary and
        dumped in a JSON string.
        
        :param pre_state: The dictionary which describes the state of the user
          before a modification
        :type pre_state: dict
        :param post_state: The dictionary which describes the state of the user
          after a modification
        :type post_state: dict
        
        :return: The payload as a JSON string
        :rtype: str
        """
        payload = dict()
        payload["pre"] = pre_state
        payload["post"] = post_state
        return json.dumps(payload, default=datetime_serializer)

    def rm_group(self, groupname, username=None):
        """
        Remove the user from a group.
        
        :param groupname: The group to be removed from
        :type groupname: str
        :param username: the name of the user who made the action
        :type username: str, optional
        """
        self.rm_groups([groupname])

    def rm_groups(self, ls_group, username=None):
        """
        Remove the user from a list of groups.
        
        :param groupname: The groups to be removed from
        :type groupname: List[str]
        :param username: the name of the user who made the action
        :type username: str, optional
        """
        new_groups = set(self.get_groups()) - set(ls_group)
        # remove duplicate
        new_groups = list(set(new_groups))
        self.update(groups=new_groups, username=username)

    def to_dict(self):
        """
        Return a dictionary which describes a resource for the web ui
        
        :return: The dictionary with the information needed for the UI
        :rtype: dict
        """
        return {
            "uuid": self.uuid,
            "name": self.name,
            "email": self.email,
            "administrator": self.administrator,
            "active": self.active,
            "ldap": self.ldap,
            "groups": [g.to_dict() for g in Group.find_all(self.groups)],
        }

    def update(self, **kwargs):
        """
        Update a user. We intercept the call to encrypt the password if we
        modify it.
        
        :param username: the name of the user who made the action
        :type username: str, optional
        :param password: The plain password to encrypt
        :type password: str
        
        :return: The modified user
        :rtype: :class:`radon.model.User`
        """
        pre_state = self.mqtt_get_state()
        # If we want to update the password we need to encrypt it first

        if "password" in kwargs:
            kwargs["password"] = encrypt_password(kwargs["password"])

        if "username" in kwargs:
            username = kwargs["username"]
            del kwargs["username"]
        else:
            username = None

        super(User, self).update(**kwargs)
        user = User.find(self.name)
        post_state = user.mqtt_get_state()
        payload = user.mqtt_payload(pre_state, post_state)
        Notification.update_user(username, user.name, payload)
        return self