jarvis.Database

View Source
#
# Copyright (c) 2020 by Philipp Scheer. All Rights Reserved.
#

import types
import couchdb2
import requests
import traceback


class Database:
    """
    A Database class which handles DB connections and exceptions
    """

    Exception = requests.ConnectionError
    """
    An exception or a list of exception which might occur while making operations with the Database
    """

    def __init__(self, username: str = "jarvis", password: str = "jarvis", name: str = "jarvis", hostname: str = "127.0.0.1", port: int = 5984) -> None:
        """
        Creates a Database connection with the following arguments:
        * `username` specifies the database username
        * `password` specifies the database password
        * `name` specifies the database name
        * `hostname` specifies the hostname the database is running on
        * `port` specifies the port the database is running on
        """
        self.host = hostname
        self.port = port
        self.user = username
        self.name = name

        try:
            self.server = couchdb2.Server(
                f"http://{self.host}:{self.port}/", username=self.user, password=password)
        except Database.Exception:
            from jarvis import Logger
            Logger.e1("database", "refused",
                             "connection refused, database not running", traceback.format_exc(), database_entry=False)
            exit(1)

    def table(self, table_name: str, pure: bool = False):
        """
        Get a table in the database  
        Because CouchDB does not support tables, all tables are prefixed with the database name `self.name`  
        The final table name therefore is `self.name`-`table_name`  
        If `pure` is True, the final table name is `table_name`
        """
        return Table(self.server, table_name if pure else f"{self.name}-{table_name}")

    def delete(self):
        """
        Delete the current Database 
        """
        for db in self.server:
            if str(db).startswith(f"{self.name}-"):
                db.destroy()

    def drop(self):
        """
        Delete the current Database
        """
        return self.delete()

    @property
    def stats(self):
        """
        Contains Database stats
        """
        return self.server.get_node_stats(nodename='_local')

    @property
    def up(self):
        """
        Check if Database is up and running
        """
        return self.server.up()

    def __str__(self) -> str:
        """
        Return string representation of the Database
        """
        return f"jarvis.Database.Database({self.name})"


class Table:
    """
    Represents a Table in a Database  
    This class should never be called by the user, only by the `Database` class
    """
    def __init__(self, server: couchdb2.Server, table_name: str) -> None:
        """
        Initialize the table  
        * `server` is a CouchDB instance  
        * `table_name` specifies the table name
        """
        self.server = server
        self.name = table_name
        if self.name in self.server:
            self.table = self.server.get(self.name)
        else:
            self.table = self.server.create(self.name)

    def get(self, id: str) -> dict:
        """
        Get a document from the current table by `id`
        """
        return self.table.get(id)

    def all(self) -> list:
        """
        Return all documents from the current table
        """
        all_list = DocumentList(self)
        for doc in self.table:
            all_list.add(dict(doc))
        return all_list

    def insert(self, document: dict) -> any:
        """
        Insert a document in the current table
        """
        return self.table.put(document)

    def filter(self, filter: any = {}) -> list:
        """
        Filters a table  
        `filter` can be either a lamba or object  
        Returns a list of all documents that match
        """
        doc_list = DocumentList(self)
        if (isinstance(filter, types.LambdaType)):
            for document in self.all():
                if filter.__call__(document):
                    doc_list.add(document)
        if (isinstance(filter, dict)):
            if len(filter) == 0:
                return self.all()
            for document in self.all():
                for key in filter:
                    if key in document and document[key] == filter[key]:
                        doc_list.add(document)
        return doc_list

    def find(self, filter: dict = {}) -> list:
        """
        Find documents by a <a href="https://pouchdb.com/guides/mango-queries.html">Mango query</a>
        """
        doc_list = DocumentList(self)
        doc_list.document_list = self.table.find(filter)["docs"]
        return doc_list

    def delete(self, document):
        """
        Delete a document from the table
        """
        self.table.purge([document])

    def drop(self):
        """
        Drop the current table
        """
        return self.table.destroy()

    @property
    def size(self):
        """
        Return the size of the current table in bytes
        """
        return dict(self.table.get_info())["sizes"]["active"]

    def __str__(self) -> str:
        """
        Returns string representation of the current table
        """
        return f"jarvis.Database.Table({self.name})"


class DocumentList:
    """
    A list of object with various additional features  
    This class should never be called by the user, only by the `Table` class
    """
    def __init__(self, table: Table) -> None:
        """
        Initialize an empty DocumentList  
        * `table` is a `Table` object which got called by `Database`
        """
        self.table = table
        self.document_list = []

    def add(self, item: dict) -> None:
        """
        Add a document to the DocumentList
        """
        self.document_list.append(item)

    def set(self, new_document: dict) -> None:
        """
        Update a document, the document needs to contain an _id and _rev (CouchDB internals)
        """
        for document in self.document_list:
            if "_id" not in new_document:
                new_document["_id"] = document["_id"]
                new_document["_rev"] = document["_rev"]
            self.table.insert(new_document)

    def update(self, modify_function_or_new_object: any) -> None:
        """
        Update all documents using a function or a dictionary with updated keys  
        The if a function is passed, the object the function returns is used,  
        else the merged dictionaries
        """
        if isinstance(modify_function_or_new_object, dict):
            for document in self.document_list:
                def merge_dicts(x, y):
                    z = x.copy()
                    z.update(y)
                    return z
                self.table.insert(merge_dicts(document, modify_function_or_new_object))
        else:
            for old_document in self.document_list:
                new_document = modify_function_or_new_object(dict(old_document))
                self.table.insert(new_document)

    def delete(self) -> None:
        """
        Delete all documents in the current DocumentList
        """
        for document in self.document_list:
            self.table.delete(document)

    def sort(self, keyname: str) -> None:
        """
        Sort all documents in the current DocumentList by a given `keyname`
        """
        self.document_list = sorted(
            self.document_list, key=lambda k: k[keyname] if keyname in k else 0)
        return self

    def reverse(self):
        """
        Reverse the current DocumentList
        """
        self.document_list.reverse()
        return self

    @property
    def found(self):
        """
        Check if the Table query returned any results (length of the current DocumentList is not 0)
        """
        return len(self.document_list) != 0

    def __getitem__(self, key: int):
        """
        Get element from DocumentList
        """
        return self.document_list[key]

    def __list__(self):
        """
        Return the full DocumentList as a list object
        """
        return self.document_list

    def __str__(self) -> str:
        """
        String representation of the current DocumentList
        """
        return f"jarvis.Database.DocumentList(table={str(self.table)}, list={str(self.document_list)})"
#   class Database:
View Source
class Database:
    """
    A Database class which handles DB connections and exceptions
    """

    Exception = requests.ConnectionError
    """
    An exception or a list of exception which might occur while making operations with the Database
    """

    def __init__(self, username: str = "jarvis", password: str = "jarvis", name: str = "jarvis", hostname: str = "127.0.0.1", port: int = 5984) -> None:
        """
        Creates a Database connection with the following arguments:
        * `username` specifies the database username
        * `password` specifies the database password
        * `name` specifies the database name
        * `hostname` specifies the hostname the database is running on
        * `port` specifies the port the database is running on
        """
        self.host = hostname
        self.port = port
        self.user = username
        self.name = name

        try:
            self.server = couchdb2.Server(
                f"http://{self.host}:{self.port}/", username=self.user, password=password)
        except Database.Exception:
            from jarvis import Logger
            Logger.e1("database", "refused",
                             "connection refused, database not running", traceback.format_exc(), database_entry=False)
            exit(1)

    def table(self, table_name: str, pure: bool = False):
        """
        Get a table in the database  
        Because CouchDB does not support tables, all tables are prefixed with the database name `self.name`  
        The final table name therefore is `self.name`-`table_name`  
        If `pure` is True, the final table name is `table_name`
        """
        return Table(self.server, table_name if pure else f"{self.name}-{table_name}")

    def delete(self):
        """
        Delete the current Database 
        """
        for db in self.server:
            if str(db).startswith(f"{self.name}-"):
                db.destroy()

    def drop(self):
        """
        Delete the current Database
        """
        return self.delete()

    @property
    def stats(self):
        """
        Contains Database stats
        """
        return self.server.get_node_stats(nodename='_local')

    @property
    def up(self):
        """
        Check if Database is up and running
        """
        return self.server.up()

    def __str__(self) -> str:
        """
        Return string representation of the Database
        """
        return f"jarvis.Database.Database({self.name})"

A Database class which handles DB connections and exceptions

#   Database( self, username: str = 'jarvis', password: str = 'jarvis', name: str = 'jarvis', hostname: str = '127.0.0.1', port: int = 5984 )
View Source
    def __init__(self, username: str = "jarvis", password: str = "jarvis", name: str = "jarvis", hostname: str = "127.0.0.1", port: int = 5984) -> None:
        """
        Creates a Database connection with the following arguments:
        * `username` specifies the database username
        * `password` specifies the database password
        * `name` specifies the database name
        * `hostname` specifies the hostname the database is running on
        * `port` specifies the port the database is running on
        """
        self.host = hostname
        self.port = port
        self.user = username
        self.name = name

        try:
            self.server = couchdb2.Server(
                f"http://{self.host}:{self.port}/", username=self.user, password=password)
        except Database.Exception:
            from jarvis import Logger
            Logger.e1("database", "refused",
                             "connection refused, database not running", traceback.format_exc(), database_entry=False)
            exit(1)

Creates a Database connection with the following arguments:

  • username specifies the database username
  • password specifies the database password
  • name specifies the database name
  • hostname specifies the hostname the database is running on
  • port specifies the port the database is running on
#   def table(self, table_name: str, pure: bool = False):
View Source
    def table(self, table_name: str, pure: bool = False):
        """
        Get a table in the database  
        Because CouchDB does not support tables, all tables are prefixed with the database name `self.name`  
        The final table name therefore is `self.name`-`table_name`  
        If `pure` is True, the final table name is `table_name`
        """
        return Table(self.server, table_name if pure else f"{self.name}-{table_name}")

Get a table in the database
Because CouchDB does not support tables, all tables are prefixed with the database name self.name
The final table name therefore is self.name-table_name
If pure is True, the final table name is table_name

#   def delete(self):
View Source
    def delete(self):
        """
        Delete the current Database 
        """
        for db in self.server:
            if str(db).startswith(f"{self.name}-"):
                db.destroy()

Delete the current Database

#   def drop(self):
View Source
    def drop(self):
        """
        Delete the current Database
        """
        return self.delete()

Delete the current Database

#   stats

Contains Database stats

#   up

Check if Database is up and running

#   class Database.Exception(requests.exceptions.RequestException):
View Source
class ConnectionError(RequestException):
    """A Connection error occurred."""

A Connection error occurred.

Inherited Members
requests.exceptions.RequestException
RequestException
builtins.OSError
errno
strerror
filename
filename2
winerror
characters_written
builtins.BaseException
with_traceback
args
#   class Table:
View Source
class Table:
    """
    Represents a Table in a Database  
    This class should never be called by the user, only by the `Database` class
    """
    def __init__(self, server: couchdb2.Server, table_name: str) -> None:
        """
        Initialize the table  
        * `server` is a CouchDB instance  
        * `table_name` specifies the table name
        """
        self.server = server
        self.name = table_name
        if self.name in self.server:
            self.table = self.server.get(self.name)
        else:
            self.table = self.server.create(self.name)

    def get(self, id: str) -> dict:
        """
        Get a document from the current table by `id`
        """
        return self.table.get(id)

    def all(self) -> list:
        """
        Return all documents from the current table
        """
        all_list = DocumentList(self)
        for doc in self.table:
            all_list.add(dict(doc))
        return all_list

    def insert(self, document: dict) -> any:
        """
        Insert a document in the current table
        """
        return self.table.put(document)

    def filter(self, filter: any = {}) -> list:
        """
        Filters a table  
        `filter` can be either a lamba or object  
        Returns a list of all documents that match
        """
        doc_list = DocumentList(self)
        if (isinstance(filter, types.LambdaType)):
            for document in self.all():
                if filter.__call__(document):
                    doc_list.add(document)
        if (isinstance(filter, dict)):
            if len(filter) == 0:
                return self.all()
            for document in self.all():
                for key in filter:
                    if key in document and document[key] == filter[key]:
                        doc_list.add(document)
        return doc_list

    def find(self, filter: dict = {}) -> list:
        """
        Find documents by a <a href="https://pouchdb.com/guides/mango-queries.html">Mango query</a>
        """
        doc_list = DocumentList(self)
        doc_list.document_list = self.table.find(filter)["docs"]
        return doc_list

    def delete(self, document):
        """
        Delete a document from the table
        """
        self.table.purge([document])

    def drop(self):
        """
        Drop the current table
        """
        return self.table.destroy()

    @property
    def size(self):
        """
        Return the size of the current table in bytes
        """
        return dict(self.table.get_info())["sizes"]["active"]

    def __str__(self) -> str:
        """
        Returns string representation of the current table
        """
        return f"jarvis.Database.Table({self.name})"

Represents a Table in a Database
This class should never be called by the user, only by the Database class

#   Table(server: couchdb2.Server, table_name: str)
View Source
    def __init__(self, server: couchdb2.Server, table_name: str) -> None:
        """
        Initialize the table  
        * `server` is a CouchDB instance  
        * `table_name` specifies the table name
        """
        self.server = server
        self.name = table_name
        if self.name in self.server:
            self.table = self.server.get(self.name)
        else:
            self.table = self.server.create(self.name)

Initialize the table

  • server is a CouchDB instance
  • table_name specifies the table name
#   def get(self, id: str) -> dict:
View Source
    def get(self, id: str) -> dict:
        """
        Get a document from the current table by `id`
        """
        return self.table.get(id)

Get a document from the current table by id

#   def all(self) -> list:
View Source
    def all(self) -> list:
        """
        Return all documents from the current table
        """
        all_list = DocumentList(self)
        for doc in self.table:
            all_list.add(dict(doc))
        return all_list

Return all documents from the current table

#   def insert(self, document: dict) -> <built-in function any>:
View Source
    def insert(self, document: dict) -> any:
        """
        Insert a document in the current table
        """
        return self.table.put(document)

Insert a document in the current table

#   def filter(self, filter: <built-in function any> = {}) -> list:
View Source
    def filter(self, filter: any = {}) -> list:
        """
        Filters a table  
        `filter` can be either a lamba or object  
        Returns a list of all documents that match
        """
        doc_list = DocumentList(self)
        if (isinstance(filter, types.LambdaType)):
            for document in self.all():
                if filter.__call__(document):
                    doc_list.add(document)
        if (isinstance(filter, dict)):
            if len(filter) == 0:
                return self.all()
            for document in self.all():
                for key in filter:
                    if key in document and document[key] == filter[key]:
                        doc_list.add(document)
        return doc_list

Filters a table
filter can be either a lamba or object
Returns a list of all documents that match

#   def find(self, filter: dict = {}) -> list:
View Source
    def find(self, filter: dict = {}) -> list:
        """
        Find documents by a <a href="https://pouchdb.com/guides/mango-queries.html">Mango query</a>
        """
        doc_list = DocumentList(self)
        doc_list.document_list = self.table.find(filter)["docs"]
        return doc_list

Find documents by a Mango query

#   def delete(self, document):
View Source
    def delete(self, document):
        """
        Delete a document from the table
        """
        self.table.purge([document])

Delete a document from the table

#   def drop(self):
View Source
    def drop(self):
        """
        Drop the current table
        """
        return self.table.destroy()

Drop the current table

#   size

Return the size of the current table in bytes

#   class DocumentList:
View Source
class DocumentList:
    """
    A list of object with various additional features  
    This class should never be called by the user, only by the `Table` class
    """
    def __init__(self, table: Table) -> None:
        """
        Initialize an empty DocumentList  
        * `table` is a `Table` object which got called by `Database`
        """
        self.table = table
        self.document_list = []

    def add(self, item: dict) -> None:
        """
        Add a document to the DocumentList
        """
        self.document_list.append(item)

    def set(self, new_document: dict) -> None:
        """
        Update a document, the document needs to contain an _id and _rev (CouchDB internals)
        """
        for document in self.document_list:
            if "_id" not in new_document:
                new_document["_id"] = document["_id"]
                new_document["_rev"] = document["_rev"]
            self.table.insert(new_document)

    def update(self, modify_function_or_new_object: any) -> None:
        """
        Update all documents using a function or a dictionary with updated keys  
        The if a function is passed, the object the function returns is used,  
        else the merged dictionaries
        """
        if isinstance(modify_function_or_new_object, dict):
            for document in self.document_list:
                def merge_dicts(x, y):
                    z = x.copy()
                    z.update(y)
                    return z
                self.table.insert(merge_dicts(document, modify_function_or_new_object))
        else:
            for old_document in self.document_list:
                new_document = modify_function_or_new_object(dict(old_document))
                self.table.insert(new_document)

    def delete(self) -> None:
        """
        Delete all documents in the current DocumentList
        """
        for document in self.document_list:
            self.table.delete(document)

    def sort(self, keyname: str) -> None:
        """
        Sort all documents in the current DocumentList by a given `keyname`
        """
        self.document_list = sorted(
            self.document_list, key=lambda k: k[keyname] if keyname in k else 0)
        return self

    def reverse(self):
        """
        Reverse the current DocumentList
        """
        self.document_list.reverse()
        return self

    @property
    def found(self):
        """
        Check if the Table query returned any results (length of the current DocumentList is not 0)
        """
        return len(self.document_list) != 0

    def __getitem__(self, key: int):
        """
        Get element from DocumentList
        """
        return self.document_list[key]

    def __list__(self):
        """
        Return the full DocumentList as a list object
        """
        return self.document_list

    def __str__(self) -> str:
        """
        String representation of the current DocumentList
        """
        return f"jarvis.Database.DocumentList(table={str(self.table)}, list={str(self.document_list)})"

A list of object with various additional features
This class should never be called by the user, only by the Table class

#   DocumentList(table: jarvis.Database.Table)
View Source
    def __init__(self, table: Table) -> None:
        """
        Initialize an empty DocumentList  
        * `table` is a `Table` object which got called by `Database`
        """
        self.table = table
        self.document_list = []

Initialize an empty DocumentList

#   def add(self, item: dict) -> None:
View Source
    def add(self, item: dict) -> None:
        """
        Add a document to the DocumentList
        """
        self.document_list.append(item)

Add a document to the DocumentList

#   def set(self, new_document: dict) -> None:
View Source
    def set(self, new_document: dict) -> None:
        """
        Update a document, the document needs to contain an _id and _rev (CouchDB internals)
        """
        for document in self.document_list:
            if "_id" not in new_document:
                new_document["_id"] = document["_id"]
                new_document["_rev"] = document["_rev"]
            self.table.insert(new_document)

Update a document, the document needs to contain an _id and _rev (CouchDB internals)

#   def update(self, modify_function_or_new_object: <built-in function any>) -> None:
View Source
    def update(self, modify_function_or_new_object: any) -> None:
        """
        Update all documents using a function or a dictionary with updated keys  
        The if a function is passed, the object the function returns is used,  
        else the merged dictionaries
        """
        if isinstance(modify_function_or_new_object, dict):
            for document in self.document_list:
                def merge_dicts(x, y):
                    z = x.copy()
                    z.update(y)
                    return z
                self.table.insert(merge_dicts(document, modify_function_or_new_object))
        else:
            for old_document in self.document_list:
                new_document = modify_function_or_new_object(dict(old_document))
                self.table.insert(new_document)

Update all documents using a function or a dictionary with updated keys
The if a function is passed, the object the function returns is used,
else the merged dictionaries

#   def delete(self) -> None:
View Source
    def delete(self) -> None:
        """
        Delete all documents in the current DocumentList
        """
        for document in self.document_list:
            self.table.delete(document)

Delete all documents in the current DocumentList

#   def sort(self, keyname: str) -> None:
View Source
    def sort(self, keyname: str) -> None:
        """
        Sort all documents in the current DocumentList by a given `keyname`
        """
        self.document_list = sorted(
            self.document_list, key=lambda k: k[keyname] if keyname in k else 0)
        return self

Sort all documents in the current DocumentList by a given keyname

#   def reverse(self):
View Source
    def reverse(self):
        """
        Reverse the current DocumentList
        """
        self.document_list.reverse()
        return self

Reverse the current DocumentList

#   found

Check if the Table query returned any results (length of the current DocumentList is not 0)