<commit_before>"""Database handling class"""

import sys
import sqlite3 as sql
import logging
from functools import wraps

from backend.misc.check_host import is_RPI


def create_db_object():
    root = sys.path[-1]
    if is_RPI:
        db_path =  "/".join([root, 'backend', 'sys_database', "sys_database.db"])
    else:   
        db_path =  "\\".join([root, 'backend', 'sys_database', "sys_database.db"])

    return Database(db_path)

def save_create(func):
    """Handles connection commit eventual rollback and closing while saving or creating to database"""
    @wraps(func)
    def func_wrapper(*args, **kwargs):
        if args[0].connect():
            try:
                args[0].cur = args[0].con.cursor()            
                return func(*args, **kwargs)               
            except sql.Error as e:
                args[0].con.rollback()               
                args[0].logger.exception( "DATABASE SAVE/CREATE Error %s:",  e.args[0])
                pass
            finally:
                args[0].con.commit() 
                args[0].close()
        else:
            pass
    return func_wrapper

def read_remove(func):
    """Handles connection and closing of database while reading or removing from database"""
    @wraps(func)
    def func_wrapper(*args, **kwargs):
        if args[0].connect():
            try:
                args[0].cur = args[0].con.cursor()
                return func(*args, **kwargs)
            except sql.Error as e:               
                args[0].logger.exception( "DATABASE READ/REMOVE Error %s:", e.args[0])

            finally:
                args[0].close()
        else:
            args[0].logger.warning("Cannot open database")
            return 0
    return func_wrapper

class Database:
    """ Handles system database """
    def __init__(self, path):
        self.path = path
        self.logger = logging.getLogger('DB')
        self.logger.disabled = False
        self.logger.setLevel(logging.WARN)

        self.con = None
        self.cur = None
        self.__connected = False
        
        self.table_headers = {}

        self.sql_INSERT_INTO = "INSERT INTO"
        self.sql_VALUES = "VALUES"
        self.sql_CREATE_TABLE = "CREATE TABLE"
        self.sql_SELECT = "SELECT"
        self.sql_FROM = "FROM"
        self.sql_WHERE = "WHERE"
        self.sql_UPDATE = "UPDATE"
        self.sql_SET = "SET"
        self.sql_DELETE = "DELETE"

    def connect(self):
        """Connects to database. If database does not exist creates one"""
        try:           
            self.con = sql.connect(self.path)
            self.__connected = True
            self.logger.debug("Database opened")
        except sql.Error as e:
            self.__connected = False
            self.logger.warning(e.args[0])
        finally:
            return self.__connected
                     
    def close(self):
        """Closes connection to database"""
        if self.__connected:
            self.con.close()
            self.__connected = False
            self.logger.debug("Database closed")
        else:
            pass

    @save_create
    def create_tables(self, *Objects):
        """For every Object in Objects if Object's table does not exists creates one """

        for Object in Objects:
            #sprawdzenie czy tablica istnieje
            self.cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='" + Object.table_name +"'")
            tables = self.cur.fetchall()
            if not tables :
                #Przygotowanie komendy sql
                column_headers_and_types = [ ' '.join(col_header_and_type) for col_header_and_type in Object.column_headers_and_types]
                column_headers_and_types_str = self._brackets(','.join(column_headers_and_types))
                sql_command = self._put_spaces(self.sql_CREATE_TABLE, Object.table_name, column_headers_and_types_str)

                self.cur.execute(sql_command);
                self.logger.info("table: " + Object.table_name + " created")

    @read_remove
    def get_table(self, Object):
        sql_command = self._put_spaces(self.sql_SELECT, '*', self.sql_FROM, Object.table_name)
        self.cur.execute(sql_command)
        return self.cur.fetchall()

    @read_remove
    def clear_table(self, Object):
        sql_command = self._put_spaces(self.sql_DELETE, self.sql_FROM, Object.table_name)
        self.cur.execute(sql_command)

    def load_objects_from_table(self, Object):
        """Functions allows to retrieve objects from table based on object type"""
        table = self.get_table(Object)

        try:
            types = Object.types.copy()
            num_types = set()

            while types:
                num_types.add(types.pop().value) # convert enums to ints

            for data in table:
                if data[1] in num_types: # if type of object match with requierd type
                    data = list(data)
                    Object(*data)   # create Object
        except AttributeError: 
            for data in table: # simple load for objects without type
                data = list(data)
                Object(*data)   # create Object

    @read_remove
    def delete_tables(self, *Objects):
        """Removes tables from database"""
        for Object in Objects:
            self.cur.execute("DROP TABLE " + Object.table_name)
            self.logger.info("Table: " + Object.table_name + " droped")

    @save_create
    def save(self, Object, db_values):
        """Saves user to database"""
       
        #Prepare column headers
        column_headers = [ col_head_and_type[0] for col_head_and_type in Object.column_headers_and_types]
        column_headers_str = self._brackets (",".join(column_headers) )
        
        #Przygotowanie question marks for sql query
        question_marks = ""
        for col_head in Object.column_headers_and_types:
            question_marks += '?,'   
        question_marks = self._brackets(question_marks.rstrip(','))

        sql_command = self._put_spaces(self.sql_INSERT_INTO, Object.table_name, column_headers_str, self.sql_VALUES, question_marks)
        self.cur.execute(sql_command, db_values)

        self.logger.info(str(Object) + " added to DB")

    @read_remove
    def read(self, Object, key_name, key):
        """Reads object based on given key from database"""
        sql_command = self._put_spaces(self.sql_SELECT, "*", self.sql_FROM, Object.table_name, self.sql_WHERE, key_name, '=', '?')
        self.cur.execute(sql_command, (key,))
        object_data = self.cur.fetchone()
        if not object_data:
            return False
        return Object(*list(object_data))

    @read_remove
    def read_simple(self, table_name, key_name, key):
        """Reads object based on given key from database"""
        sql_command = self._put_spaces(self.sql_SELECT, "*", self.sql_FROM, table_name, self.sql_WHERE, key_name, '=', '?')
        self.cur.execute(sql_command, (key,))
        object_data = list(self.cur.fetchone())
        if not object_data:
            return False
        return object_data
    
    @save_create
    #def update_user_log_status(self, user):
    def update_field(self, object, field_name,  field_value):
        sql_command = self._put_spaces(self.sql_UPDATE, object.table_name, self.sql_SET, field_name, '=?', self.sql_WHERE, "id=?")
        self.cur.execute(sql_command, (field_value, object.id))
         
    def _brackets(self, str):
        return '(' + str + ')'

    def _put_spaces(self, *args):
        str = ""
        for arg in args:
            str += arg + " "

        return str.rstrip(" ")<commit_msg>1) Improved finding database. Should be recognized on every system. 2) Small names refactorization 3) Deleted catching exception when database is not connected. Program should break<commit_after>"""Database handling class"""

import sys
import sqlite3 as sql
import logging
from functools import wraps

from backend.misc.check_host import is_RPI


def create_db_object():

    root = None
    for path in sys.path:
        if path.endswith("system"):
            root = path

    if is_RPI:
        db_path = "/".join([root, 'backend', 'sys_database', "sys_database.db"])
    else:   
        db_path = "\\".join([root, 'backend', 'sys_database', "sys_database.db"])

    return Database(db_path)


def save_create(func):
    """Handles connection commit eventual rollback and closing while saving or creating to database"""
    @wraps(func)
    def func_wrapper(self, *args):
        if self.connect():
            try:
                self.cur = self.con.cursor()
                return func(self, *args)
            except sql.Error as e:
                self.con.rollback()
                self.logger.exception("DATABASE SAVE/CREATE Error %s:",  e.args[0])
                pass
            finally:
                self.con.commit()
                self.close()
        else:
            pass
    return func_wrapper


def read_remove(func):
    """Handles connection and closing of database while reading or removing from database"""
    @wraps(func)
    def func_wrapper(self, *args):
        if self.connect():
            try:
                self.cur = self.con.cursor()
                return func(self, *args)
            except sql.Error as e:               
                self.logger.exception("DATABASE READ/REMOVE Error %s:", e.args[0])

            finally:
                self.close()
        else:
            self.logger.warning("Cannot open database")
            return 0
    return func_wrapper


class Database:
    """ Handles system database """
    def __init__(self, path):
        self.path = path
        self.logger = logging.getLogger('DB')
        self.logger.disabled = False
        self.logger.setLevel(logging.ERROR)

        self.con = None
        self.cur = None
        self.__connected = False
        
        self.table_headers = {}

        self.sql_INSERT_INTO = "INSERT INTO"
        self.sql_VALUES = "VALUES"
        self.sql_CREATE_TABLE = "CREATE TABLE"
        self.sql_SELECT = "SELECT"
        self.sql_FROM = "FROM"
        self.sql_WHERE = "WHERE"
        self.sql_UPDATE = "UPDATE"
        self.sql_SET = "SET"
        self.sql_DELETE = "DELETE"

    def connect(self):
        """Connects to database. If database does not exist creates one"""
        self.con = sql.connect(self.path)
        self.__connected = True
        self.logger.debug("Database opened")
        return True
                     
    def close(self):
        """Closes connection to database"""
        if self.__connected:
            self.con.close()
            self.__connected = False
            self.logger.debug("Database closed")
        else:
            pass

    @save_create
    def create_tables(self, *Objects):
        """For every Object in Objects if Object's table does not exists creates one """

        for Object in Objects:
            # Check if the table exists
            self.cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='" + Object.table_name +"'")
            tables = self.cur.fetchall()
            if not tables:
                # Prepare sql command
                column_headers_and_types = [' '.join(col_header_and_type) for col_header_and_type in Object.column_headers_and_types]
                column_headers_and_types_str = self._brackets(','.join(column_headers_and_types))
                sql_command = self._put_spaces(self.sql_CREATE_TABLE, Object.table_name, column_headers_and_types_str)

                self.cur.execute(sql_command)
                self.logger.info("table: " + Object.table_name + " created")

    @read_remove
    def get_table(self, Object):
        sql_command = self._put_spaces(self.sql_SELECT, '*', self.sql_FROM, Object.table_name)
        self.cur.execute(sql_command)
        return self.cur.fetchall()

    @read_remove
    def clear_table(self, Object):
        sql_command = self._put_spaces(self.sql_DELETE, self.sql_FROM, Object.table_name)
        self.cur.execute(sql_command)

    def load_objects_from_table(self, Object):
        """Functions allows to retrieve objects from table based on object type"""
        table = self.get_table(Object)

        try:
            types = Object.types.copy()
            num_types = set()

            while types:
                num_types.add(types.pop().value)  # convert enums to ints

            for data in table:
                if data[1] in num_types:  # if type of object match with requierd type
                    data = list(data)
                    Object(*data)   # create Object
        except AttributeError: 
            for data in table: # simple load for objects without type
                data = list(data)
                Object(*data)   # create Object

    @read_remove
    def delete_tables(self, *Objects):
        """Removes tables from database"""
        for Object in Objects:
            self.cur.execute("DROP TABLE " + Object.table_name)
            self.logger.info("Table: " + Object.table_name + " droped")

    @save_create
    def save(self, Object, db_values):
        """Saves user to database"""
       
        # Prepare column headers
        column_headers = [ col_head_and_type[0] for col_head_and_type in Object.column_headers_and_types]
        column_headers_str = self._brackets (",".join(column_headers) )
        
        # Przygotowanie question marks for sql query
        question_marks = ""
        for _ in Object.column_headers_and_types:
            question_marks += '?,'   
        question_marks = self._brackets(question_marks.rstrip(','))

        sql_command = self._put_spaces(self.sql_INSERT_INTO, Object.table_name, column_headers_str, self.sql_VALUES, question_marks)
        self.cur.execute(sql_command, db_values)

        self.logger.info(str(Object) + " added to DB")

    @read_remove
    def read(self, Object, key_name, key):
        """Reads object based on given key from database"""
        sql_command = self._put_spaces(self.sql_SELECT, "*", self.sql_FROM, Object.table_name, self.sql_WHERE, key_name, '=', '?')
        self.cur.execute(sql_command, (key,))
        object_data = self.cur.fetchone()
        if not object_data:
            return False
        return Object(*list(object_data))

    @read_remove
    def read_simple(self, table_name, key_name, key):
        """Reads object based on given key from database"""
        sql_command = self._put_spaces(self.sql_SELECT, "*", self.sql_FROM, table_name, self.sql_WHERE, key_name, '=', '?')
        self.cur.execute(sql_command, (key,))
        object_data = list(self.cur.fetchone())
        if not object_data:
            return False
        return object_data
    
    @save_create
    # def update_user_log_status(self, user):
    def update_field(self, object, field_name,  field_value):
        sql_command = self._put_spaces(self.sql_UPDATE, object.table_name, self.sql_SET, field_name, '=?', self.sql_WHERE, "id=?")
        self.cur.execute(sql_command, (field_value, object.id))
         
    def _brackets(self, str):
        return '(' + str + ')'

    def _put_spaces(self, *args):
        str = ""
        for arg in args:
            str += arg + " "

        return str.rstrip(" ")
{'fraction_non_alphanumeric': 0.10488505747126436, 'fraction_numerical': 0.001959247648902821, 'mean_word_length': 2.1566687280972996, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4129462', 'n_tokens_mistral': 4490, 'n_tokens_neox': 4331, 'n_words': 1068}