class WebsiteSettings(node_models.Node): maintenance = node_models.Property(required=True) logo = node_models.Property(sftp=True) name = node_models.Property() def __str__(self): return f'<WebsiteSettings object>' def __repr__(self): return f'<WebsiteSettings object>' @classmethod def get(cls, uuid=None): response = gdbh.r_transaction('MATCH (w:WebsiteSettings) RETURN (w)') if response: fake_permission_instance = FakeClass() fake_permission_instance.__class__ = cls property_dict = response[0]["w"]._properties for property_name, property_value in property_dict.items(): setattr(fake_permission_instance, property_name, property_value) return fake_permission_instance else: return None def delete(self): gdbh.w_transaction(""" MATCH (w:WebsiteSettings) DETACH DELETE (w) """)
class User(node_models.Node): # Default groups is_super_user = node_models.Property(default=False) is_staff_user = node_models.Property(default=False) is_active_user = node_models.Property(default=True) first_name = node_models.Property(required=True) last_name = node_models.Property(required=True) email = node_models.Property(required=True, unique=True) password = node_models.Property(required=True) registration_datetime = node_models.Property(default=datetime.datetime.now) permissions = UserPermissionsRelationship() groups = UserGroupsRelationship() session = UserSessionRelationsip() email_confirmation_key = None email_is_confirmed = None if settings.BULB_REGISTRATION_USE_EMAIL_CONFIRMATION: email_confirmation_key = node_models.Property() email_is_confirmed = node_models.Property(default=False) def __str__(self): return f'<User object(first_name="{self.first_name}", last_name="{self.last_name}", uuid="{self.uuid}")>' def __repr__(self): return f'<User object(first_name="{self.first_name}", last_name="{self.last_name}", uuid="{self.uuid}")>' @classmethod def create(cls, **extrafields): extrafields.setdefault('is_super_user', False) extrafields.setdefault('is_staff_user', False) extrafields.setdefault('is_active_user', True) extrafields['password'] = _hash_password(extrafields['password']) new_user = cls(**extrafields) if settings.BULB_REGISTRATION_USE_EMAIL_CONFIRMATION: # Generate the email confirmation key. email_confirmation_key = make_uuid() # Apply it to the new User instance. new_user.update('email_confirmation_key', email_confirmation_key) target_email = extrafields[settings.BULB_USER_EMAIL_PROPERTY_NAME] confirmation_url = settings.BULB_CONFIRMATION_VIEW_PATH + email_confirmation_key send_mail( from_email=settings.BULB_EMAIL_CONFIRMATION_SENDER_NAME, subject=settings.BULB_EMAIL_CONFIRMATION_SUBJECT, recipient_list=[f"{target_email}"], message=settings.BULB_EMAIL_CONFIRMATION_DEFAULT_MESSAGE + confirmation_url, html_message=render_to_string( settings.BULB_EMAIL_CONFIRMATION_TEMPLATE_PATH, {"confirmation_url": confirmation_url})) return new_user @classmethod def create_super_user(cls, **extrafields): extrafields.setdefault('is_super_user', True) extrafields.setdefault('is_staff_user', True) extrafields.setdefault('is_active_user', True) extrafields['password'] = _hash_password(extrafields['password']) is_super_user = extrafields.get('is_super_user') is_staff_user = extrafields.get('is_staff_user') if not is_super_user: bulb_logger.error( 'ValueError(\'A SuperUser must have "is_super_user = True".\')' ) raise ValueError('A SuperUser must have "is_super_user = True".') elif not is_staff_user: bulb_logger.error( 'ValueError(\'A SuperUser must have "is_staff_user = True".\')' ) raise ValueError('A SuperUser must have "is_staff_user = True".') else: new_super_user = User(**extrafields) return new_super_user @classmethod def get(cls, uuid=None, email=None, email_confirmation_key=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, handmade=None, return_query=False): """ This method allow the retrieving of User (or of one of its children classes) instances. :param email (required if there is neither uuid nor email_confirmation_key) : The email of a user to get an unique user instance. :param uuid (required if there is neither email nor email_confirmation_key) : The Universal Unique Identifier of a node to get an unique instance. :param email_confirmation_key (required if there is neither email nor uuid) : The confirmation key for the email validation part. :param order_by (optional, default=None) : Must be the name of the property with which the returned datas will be sorted. Examples : "datetime", "first_name", etc... :param limit (optional, default=None) : Must be an integer. This parameter defines the number of returned elements. :param skip (optional, default=None) : Must be an integer. This parameter defines the number of skipped elements. For example if self.skip = 3, the 3 first returned elements will be skipped. :param desc (optional, default=False) : Must be a boolean. If it is False the elements will be returned in an increasing order, but it is True, they will be returned in a descending order. :param only (optional, default=None) : Must be a list of field_names. If this parameter is filled, the return will not be Node instances, but a dict with "only" the mentioned fields. :param filter (optional, default=None) : Must be Q statement. You must use the Q class stored in bulb.db Example: Q(name__contains="al") | Q(age__year__lte=8) :param distinct (optional, default=False) : Must be a boolean. If it is True, the returned list will be only composed with unique elements. :param handmade: If this param is filled, no other must be filled. With 'handmade', you can insert your own get query and the function will transform the objects returned by the database into Python instance of this class. WARNING : Please consider using the gdbh.r_transaction() method (imported from bulb.db) if your request no returns full Neo4j objects but a list of properties. Examples : ❌ : MATCH (u) RETURN u.first_name, u.last_name ✅ : MATCH (u) RETURN u In addition, note that the RETURN must absolutely be named 'u' like "user". :param return_query (optional, default=False) : Must be a boolean. If true, the method will return the cypher query. :return: If uuid is None, a list will be returned. Else it will be a unique instance. """ if handmade is None: where_statement = "" property_statement = "" order_by_statement = "" limit_statement = "" skip_statement = "" desc_statement = "" # Build the property_statement. if uuid is not None and email is not None: property_statement = "{uuid:'%s', email:'%s'}" % (uuid, email) elif uuid is not None: property_statement = "{uuid:'%s'}" % uuid elif email is not None: property_statement = "{email:'%s'}" % email elif email_confirmation_key is not None: property_statement = "{email_confirmation_key:'%s'}" % email_confirmation_key # Build the match_statement. cyher_labels = node_models.DatabaseNode.format_labels_to_cypher( cls._get_labels()) match_statement = f"MATCH (u:{cyher_labels} {property_statement})" # Build the where statement. if filter is not None: # where_statement = "WHERE " + filter #removed to fix if not filter[0] != "n": where_statement = "WHERE " + filter else: where_statement = filter where_statement = where_statement.replace("n.", "u.") # Build the with_statement. with_statement = "WITH u" # Build order_by statements. if order_by is not None: order_by_statement = f"ORDER BY u.{order_by}" # Build return_statement statements. if not only: if not distinct: return_statement = "RETURN (u)" else: return_statement = "RETURN DISTINCT (u)" else: only_statement_list = [] for element in only: only_statement_list.append(f"u.{element}") only_statement = ", ".join(only_statement_list) if not distinct: return_statement = f"RETURN {only_statement}" else: return_statement = f"RETURN DISTINCT {only_statement}" # Build limit_statement. if limit is not None: if not isinstance(limit, str) and not isinstance(limit, int): bulb_logger.error( f'BULBNodeError("The \'limit\' parameter of the get() method of {cls.__name__} must be a string or an integer.")' ) raise BULBNodeError( f"The 'limit' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: limit_statement = f"LIMIT {limit}" # Build skip_statement and add its required variable. if skip is not None: if not isinstance(skip, str) and not isinstance(skip, int): bulb_logger.error( f'BULBNodeError("The \'skip\' parameter of the get() method of {cls.__name__} must be a string or an integer.")' ) raise BULBNodeError( f"The 'skip' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: skip_statement = f"SKIP {skip}" # Build desc_statement. if not isinstance(desc, bool): bulb_logger.error( f'BULBNodeError("The \'desc\' parameter of the get() method of {cls.__name__} must be a boolean.")' ) raise BULBNodeError( f"The 'desc' parameter of the get() method of {cls.__name__} must be a boolean." ) else: if desc is True: desc_statement = "DESC" request_statement = """ %s %s %s %s %s %s %s %s """ % (match_statement, where_statement, with_statement, order_by_statement, desc_statement, skip_statement, limit_statement, return_statement) if return_query is False: response = gdbh.r_transaction(request_statement) if response: if only is None: fake_instances_list = [] for user_object in response: fake_instances_list.append( cls.build_fake_instance( user_object["u"], forced_fake_instance_class=cls)) if uuid is not None or email is not None or email_confirmation_key is not None: return fake_instances_list[0] else: return fake_instances_list else: return response else: if uuid is not None or email is not None or email_confirmation_key is not None: if settings.BULB_ANONYMOUSUSER_NODE_MODEL_FILE: # TODO : This line is maybe useless, check it. AnonymousUser_node_model = get_anonymoususer_node_model( ) return AnonymousUser_node_model() else: return None else: return request_statement else: response = gdbh.r_transaction(handmade) fake_instances_list = [] for node_object in response: fake_instances_list.append( cls.build_fake_instance(node_object["u"], forced_fake_instance_class=cls)) return fake_instances_list @classmethod def count(cls, uuid=None, email=None, email_confirmation_key=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, handmade=None, **extrafields): request_statement = cls.get( uuid=uuid, email=email, email_confirmation_key=email_confirmation_key, order_by=order_by, limit=limit, skip=skip, desc=desc, only=only, filter=filter, handmade=handmade, return_query=True, **extrafields) request_count_statement = None if not distinct: request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(u)" else: request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(DISTINCT u)" response = gdbh.r_transaction(request_count_statement) if not distinct: return response[0]["COUNT(u)"] else: return response[0]["COUNT(DISTINCT u)"] def set_password(self, new_password): self.update("password", _hash_password(new_password)) def has_perm(self, permission_code_name): if self.is_super_user: return True if not self.is_active_user: return False else: permission_to_test = Permission.get(codename=permission_code_name) all_user_permissions = self.permissions.get() if all_user_permissions is not None: if permission_to_test in all_user_permissions: return True return False
class Group(node_models.Node): name = node_models.Property(unique=True, required=True) permissions = GroupPermissionsRelationship() users = GroupUsersRelationship() def __str__(self): return f'<Group object(name="{self.name}", uuid="{self.uuid}")>' def __repr__(self): return f'<Group object(name="{self.name}", uuid="{self.uuid}")>' @classmethod def get(cls, uuid=None, name=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, handmade=None, return_query=False): """ This method allow the retrieving of Group (or of one of its children classes) instances. :param name (required if there is no uuid) : The name of a group to get an unique group instance. :param uuid (required if there is no name) : The Universal Unique Identifier of a node to get an unique instance. :param order_by (optional, default=None) : Must be the name of the property with which the returned datas will be sorted. Examples : "datetime", "first_name", etc... :param limit (optional, default=None) : Must be an integer. This parameter defines the number of returned elements. :param skip (optional, default=None) : Must be an integer. This parameter defines the number of skipped elements. For example if self.skip = 3, the 3 first returned elements will be skipped. :param desc (optional, default=False) : Must be a boolean. If it is False the elements will be returned in an increasing order, but it is True, they will be returned in a descending order. :param only (optional, default=None) : Must be a list of field_names. If this parameter is filled, the return will not be Node instances, but a dict with "only" the mentioned fields. :param filter (optional, default=None) : Must be Q statement. You must use the Q class stored in bulb.db Example: Q(name__contains="al") | Q(age__year__lte=8) :param distinct (optional, default=False) : Must be a boolean. If it is True, the returned list will be only composed with unique elements. :param handmade: If this param is filled, no other must be filled. With 'handmade', you can insert your own get query and the function will transform the objects returned by the database into Python instance of this class. WARNING : Please consider using the gdbh.r_transaction() method (imported from bulb.db) if your request no returns full Neo4j objects but a list of properties. Examples : ❌ : MATCH (g) RETURN g.first_name, g.last_name ✅ : MATCH (g) RETURN g In addition, note that the RETURN must absolutely be named 'g' like "group". :param return_query (optional, default=False) : Must be a boolean. If true, the method will return the cypher query. :return: If uuid is None, a list will be returned. Else it will be a unique instance. """ if handmade is None: where_statement = "" property_statement = "" order_by_statement = "" limit_statement = "" skip_statement = "" desc_statement = "" # Build the property_statement. if uuid is not None and name is not None: property_statement = "{uuid:'%s', name:'%s'}" % (uuid, name) elif uuid is not None: property_statement = "{uuid:'%s'}" % uuid elif name is not None: property_statement = "{name:'%s'}" % name # Build the match_statement. cyher_labels = node_models.DatabaseNode.format_labels_to_cypher( cls._get_labels()) match_statement = f"MATCH (g:{cyher_labels} {property_statement})" # Build the where statement. if filter is not None: if not filter[0] != "n": where_statement = "WHERE " + filter else: where_statement = filter where_statement = where_statement.replace("n.", "g.") # Build the with_statement. with_statement = "WITH g" # Build order_by statements. if order_by is not None: order_by_statement = f"ORDER BY g.{order_by}" # Build return_statement statements. if not only: if not distinct: return_statement = "RETURN (g)" else: return_statement = "RETURN DISTINCT (g)" else: only_statement_list = [] for element in only: only_statement_list.append(f"g.{element}") only_statement = ", ".join(only_statement_list) if not distinct: return_statement = f"RETURN {only_statement}" else: return_statement = f"RETURN DISTINCT {only_statement}" # Build limit_statement. if limit is not None: if not isinstance(limit, str) and not isinstance(limit, int): bulb_logger.error( f'BULBNodeError("The \'limit\' parameter of the get() method of {cls.__name__} must be a string or an integer.")' ) raise BULBNodeError( f"The 'limit' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: limit_statement = f"LIMIT {limit}" # Build skip_statement and add its required variable. if skip is not None: if not isinstance(skip, str) and not isinstance(skip, int): bulb_logger.error( f'BULBNodeError("The \'skip\' parameter of the get() method of {cls.__name__} must be a string or an integer.")' ) raise BULBNodeError( f"The 'skip' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: skip_statement = f"SKIP {skip}" # Build desc_statement. if not isinstance(desc, bool): bulb_logger.error( f'BULBNodeError("The \'desc\' parameter of the get() method of {cls.__name__} must be a boolean.")' ) raise BULBNodeError( f"The 'desc' parameter of the get() method of {cls.__name__} must be a boolean." ) else: if desc is True: desc_statement = "DESC" request_statement = """ %s %s %s %s %s %s %s %s """ % (match_statement, where_statement, with_statement, order_by_statement, desc_statement, skip_statement, limit_statement, return_statement) if return_query is False: response = gdbh.r_transaction(request_statement) if response: if only is None: fake_instances_list = [] for group_object in response: fake_instances_list.append( cls.build_fake_instance( group_object["g"], forced_fake_instance_class=cls)) if uuid is not None or name is not None: return fake_instances_list[0] else: return fake_instances_list else: return response else: return None else: return request_statement else: response = gdbh.r_transaction(handmade) fake_instances_list = [] for node_object in response: fake_instances_list.append( cls.build_fake_instance(node_object["g"], forced_fake_instance_class=cls)) return fake_instances_list @classmethod def count(cls, uuid=None, name=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, handmade=None, **extrafields): request_statement = cls.get(uuid=uuid, name=name, order_by=order_by, limit=limit, skip=skip, desc=desc, only=only, filter=filter, handmade=handmade, return_query=True, **extrafields) request_count_statement = None if not distinct: request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(g)" else: request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(DISTINCT g)" response = gdbh.r_transaction(request_count_statement) if not distinct: return response[0]["COUNT(g)"] else: return response[0]["COUNT(DISTINCT g)"]
class Session(node_models.Node, AbstractBaseSession): """ Django provides full support for anonymous sessions. The session framework lets you store and retrieve arbitrary data on a per-site-visitor basis. It stores data on the server side and abstracts the sending and receiving of cookies. Cookies contain a session ID -- not the data itself. The Django sessions framework is entirely cookie-based. It does not fall back to putting session IDs in URLs. This is an intentional design decision. Not only does that behavior make URLs ugly, it makes your site vulnerable to session-ID theft via the "Referer" header. For complete documentation on using Sessions in your code, consult the sessions documentation that is shipped with Django (also available on the Django Web site). """ session_key = node_models.Property( required=True, unique=True) # TODO : add max_length = 40 session_data = node_models.Property() expire_date = node_models.Property() # TODO : add db_index = True related_user = RelatedUserRelationship() def __str__(self): return f'<Session object(session_key="{self.session_key}", expire_date="{str(self.expire_date)}")>' def __repr__(self): return f'<Session object(session_key="{self.session_key}", expire_date="{str(self.expire_date)}")>' @classmethod def get_session_store_class(cls): from bulb.contrib.sessions.backends.db import SessionStore return SessionStore @classmethod def get(cls, uuid=None, session_key=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, return_query=False): """ This method allow the retrieving of Session (or of one of its children classes) instances. :param uuid: The Universal Unique Identifier of a session to get an unique session instance. :param session_key: The session_key of a session to get an unique session instance. :param order_by: Must be the name of the property with which the returned datas will be sorted. Examples : "datetime", "first_name", etc... :param limit: Must be an integer. This parameter defines the number of returned elements. :param skip: Must be an integer. This parameter defines the number of skipped elements. For example if self.skip = 3, the 3 first returned elements will be skipped. :param desc: Must be a boolean. If it is False the elements will be returned in an increasing order, but it is True, they will be returned in a descending order. :param only: Must be a list of field_names. If this parameter is filled, the return will not be Permission instances, but a dict with "only" the mentioned fields. :param filter: Must be Q statement. You must use the Q class stored in bulb.db Example: Q(name__contains="al") | Q(age__year__lte=8) :param return_query: Must be a boolean. If true, the method will return the cypher query. :return: If uuid is None, a list will be returned. Else it will be a unique instance. """ where_statement = "" property_statement = "" order_by_statement = "" limit_statement = "" skip_statement = "" desc_statement = "" # Build the property_statement. if uuid is not None and session_key is not None: property_statement = "{uuid:'%s', session_key:'%s'}" % ( uuid, session_key) elif uuid is not None: property_statement = "{uuid:'%s'}" % uuid elif session_key is not None: property_statement = "{session_key:'%s'}" % session_key # Build the match_statement. cyher_labels = node_models.DatabaseNode.format_labels_to_cypher( cls._get_labels()) match_statement = f"MATCH (s:{cyher_labels} {property_statement})" # Build the where statement. if filter is not None: where_statement = "WHERE " + filter where_statement = where_statement.replace("n.", "s.") # Build the with_statement. with_statement = "WITH s" # Build order_by statements. if order_by is not None: order_by_statement = f"ORDER BY s.{order_by}" # Build return_statement statements. if not only: return_statement = "RETURN (s)" else: only_statement_list = [] for element in only: only_statement_list.append(f"s.{element}") only_statement = ", ".join(only_statement_list) return_statement = f"RETURN {only_statement}" # Build limit_statement. if limit is not None: if not isinstance(limit, str) and not isinstance(limit, int): raise BULBSessionError( f"The 'limit' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: limit_statement = f"LIMIT {limit}" # Build skip_statement and add its required variable. if skip is not None: if not isinstance(skip, str) and not isinstance(skip, int): raise BULBSessionError( f"The 'skip' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: skip_statement = f"SKIP {skip}" # Build desc_statement. if not isinstance(desc, bool): raise BULBSessionError( f"The 'desc' parameter of the get() method of {cls.__name__} must be a boolean." ) else: if desc is True: desc_statement = "DESC" request_statement = """ %s %s %s %s %s %s %s %s """ % (match_statement, where_statement, with_statement, order_by_statement, desc_statement, skip_statement, limit_statement, return_statement) if return_query is False: response = gdbh.r_transaction(request_statement) if response: if only is None: fake_instances_list = [] for session_object in response: fake_instances_list.append( cls.build_fake_instance( session_object["s"], forced_fake_instance_class=cls)) if uuid is not None or session_key is not None: return fake_instances_list[0] else: return fake_instances_list else: return response else: return None else: return request_statement @classmethod def count(cls, uuid=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, **extrafields): request_statement = cls.get(uuid=uuid, order_by=order_by, limit=limit, skip=skip, desc=desc, only=only, filter=filter, return_query=True, **extrafields) request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(s)" response = gdbh.r_transaction(request_count_statement) return response[0]["COUNT(s)"] @classmethod def exists(cls, session_key): response = cls.get(session_key=session_key) if response: return True else: return False @classmethod def delete_session(cls, session_key): response = cls.get(session_key=session_key) if response: gdbh.w_transaction( "MATCH (s:Session {session_key:'%s'}) DETACH DELETE (s)" % session_key) else: raise BULBSessionDoesNotExist( f"No session with session_key = '{session_key}'. So it cannot be deleted." ) @classmethod def clear_expired_sessions(cls): gdbh.r_transaction(""" MATCH (s:Session) WHERE s.expire_date < datetime('%s') DETACH DELETE (s) """ % str(timezone.now()).replace(' ', 'T'))
class Permission(node_models.Node): codename = node_models.Property(unique=True, required=True) description = node_models.Property(unique=True, required=True) def __str__(self): return f'<Permission object(codename="{self.codename}", description="{self.description}")>' def __repr__(self): return f'<Permission object(codename="{self.codename}", description="{self.description}")>' def __hash__(self): return hash(self.codename) + hash(self.description) @classmethod def get(cls, uuid=None, codename=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, return_query=False): """ This method allow the retrieving of Permission (or of one of its children classes) instances. :param codename (required if there is no uuid) : The codename of a permission to get an unique permission instance. :param uuid (required if there is no codename) : The Universal Unique Identifier of a node to get an unique instance. :param order_by (optional, default=None) : Must be the name of the property with which the returned datas will be sorted. Examples : "datetime", "first_name", etc... :param limit (optional, default=None) : Must be an integer. This parameter defines the number of returned elements. :param skip (optional, default=None) : Must be an integer. This parameter defines the number of skipped elements. For example if self.skip = 3, the 3 first returned elements will be skipped. :param desc (optional, default=False) : Must be a boolean. If it is False the elements will be returned in an increasing order, but it is True, they will be returned in a descending order. :param only (optional, default=None) : Must be a list of field_names. If this parameter is filled, the return will not be Node instances, but a dict with "only" the mentioned fields. :param filter (optional, default=None) : Must be Q statement. You must use the Q class stored in bulb.db Example: Q(name__contains="al") | Q(age__year__lte=8) :param distinct (optional, default=False) : Must be a boolean. If it is True, the returned list will be only composed with unique elements. :param return_query (optional, default=False) : Must be a boolean. If true, the method will return the cypher query. :return: If uuid is None, a list will be returned. Else it will be a unique instance. """ where_statement = "" property_statement = "" order_by_statement = "" limit_statement = "" skip_statement = "" desc_statement = "" # Build the property_statement. if uuid is not None and codename is not None: property_statement = "{uuid:'%s', codename:'%s'}" % (uuid, codename) elif uuid is not None: property_statement = "{uuid:'%s'}" % uuid elif codename is not None: property_statement = "{codename:'%s'}" % codename # Build the match_statement. cyher_labels = node_models.DatabaseNode.format_labels_to_cypher( cls._get_labels()) match_statement = f"MATCH (p:{cyher_labels} {property_statement})" # Build the where statement. if filter is not None: if not filter[0] != "n": where_statement = "WHERE " + filter else: where_statement = filter where_statement = where_statement.replace("n.", "p.") # Build the with_statement. with_statement = "WITH p" # Build order_by statements. if order_by is not None: order_by_statement = f"ORDER BY p.{order_by}" # Build return_statement statements. if not only: if not distinct: return_statement = "RETURN (p)" else: return_statement = "RETURN DISTINCT (p)" else: only_statement_list = [] for element in only: only_statement_list.append(f"p.{element}") only_statement = ", ".join(only_statement_list) if not distinct: return_statement = f"RETURN {only_statement}" else: return_statement = f"RETURN DISTINCT {only_statement}" # Build limit_statement. if limit is not None: if not isinstance(limit, str) and not isinstance(limit, int): raise BULBGetPermissionError( f"The 'limit' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: limit_statement = f"LIMIT {limit}" # Build skip_statement and add its required variable. if skip is not None: if not isinstance(skip, str) and not isinstance(skip, int): raise BULBGetPermissionError( f"The 'skip' parameter of the get() method of {cls.__name__} must be a string or an integer." ) else: skip_statement = f"SKIP {skip}" # Build desc_statement. if not isinstance(desc, bool): raise BULBGetPermissionError( f"The 'desc' parameter of the get() method of {cls.__name__} must be a boolean." ) else: if desc is True: desc_statement = "DESC" request_statement = """ %s %s %s %s %s %s %s %s """ % (match_statement, where_statement, with_statement, order_by_statement, desc_statement, skip_statement, limit_statement, return_statement) if return_query is False: response = gdbh.r_transaction(request_statement) if response: if only is None: fake_instances_list = [] for permission_object in response: fake_instances_list.append( cls.build_fake_instance( permission_object["p"], forced_fake_instance_class=cls)) if uuid is not None or codename is not None: return fake_instances_list[0] else: return fake_instances_list else: return response else: return None else: return request_statement @classmethod def count(cls, uuid=None, codename=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, **extrafields): request_statement = cls.get(uuid=None, codename=None, order_by=None, limit=None, skip=None, desc=False, only=None, filter=None, distinct=False, return_query=True, **extrafields) request_count_statement = None if not distinct: request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(p)" else: request_count_statement = request_statement.split( "RETURN")[0] + "RETURN COUNT(DISTINCT p)" response = gdbh.r_transaction(request_count_statement) if not distinct: return response[0]["COUNT(p)"] else: return response[0]["COUNT(DISTINCT p)"]