Source code for kangaroo.table

from kangaroo.filters import get_operator
from kangaroo.unique import generate_aleatory_string

[docs]class Row(dict): def __init__(self, table=None, **kwargs): super(Row, self).__init__(**kwargs) self.__id = generate_aleatory_string() self.table = table def __getattribute__(self, name): try: return super(Row, self).__getattribute__(name) except AttributeError: if name in self.keys(): return self[name] raise def __setitem__(self, key, value): super(Row, self).__setitem__(key, value) # We use getattr here otherwise it will generate a problem with # pickle in python3.2+ if getattr(self, "table", None) is not None: self.table.row_updated(self, key) def __setattr__(self, name, value): if name in self: self[name] = value else: super(Row, self).__setattr__(name, value) @property def idd(self): """Returns an unique id of the row """ return self.__id
[docs]class Table(object): def __init__(self, tbl_name, tbl_index=[]): """Creates a new instance of kangaroo.Table :param tbl_name: The name of the table :param tbl_index: a list of index names that we want to use in this table. """ self.__tbl_name = tbl_name self.__rows = [] self.__index = {} self.__index_map = {} for i in tbl_index: self.__index[i] = {} def __unicode__(self): return "Kangaroo.Table<{0}>".format(self.tbl_name) @property def tbl_name(self): """Returns the name of the table :returns: an string that represents the name of the table """ return self.__tbl_name @property def tbl_index(self): """Returns the list of indexs of the table :returns: A list of index names """ return list(self.__index.keys())
[docs] def add_index(self, index_name): """Add a new index in the table :param index_name: The name of the index (a column in the table). """ if index_name not in self.__index: self.__index[index_name] = {} self.__build_index(index_name)
[docs] def delete_index(self, index_name): """Deletes an existing index in the table :param index_name: The name of the index """ if index_name in self.__index: del self.__index[index_name]
def __delete_row_from_index(self, row): if row.idd in self.__index_map: for index_name, v in self.__index_map[row.idd]: self.__index[index_name][v].remove(row) def __build_index(self, index_name, rows=None): if rows is None: rows = self.__rows for row in rows: if index_name in row: v = row[index_name] if v not in self.__index[index_name]: self.__index[index_name][v] = [] self.__index[index_name][v].append(row) if row.idd not in self.__index_map: self.__index_map[row.idd] = [] self.__index_map[row.idd].append((index_name, v))
[docs] def row_updated(self, row, key_changed): """Updates the index tree when a row it's modified :param row: An instance of kangaroo.Row :param key_changed: The name of the column that was modified """ if key_changed not in self.tbl_index: return # verify if we need to update the index_map update = True if row.idd in self.__index_map: self.__index_map[row.idd] = [] for i, v in self.__index_map[row.idd]: if i == key_changed: if v != row[i]: self.__index[i][v].remove(row) self.__index_map[row.idd].remove((i, v)) else: # if the value didn't change update = False if update: value = row[key_changed] if value not in self.__index[key_changed]: self.__index[key_changed][value] = [] self.__index[key_changed][value].append(row) im = (key_changed, value) self.__index_map[row.idd].append(im)
[docs] def delete_row(self, row): """Deletes a row from the table :param row: An instance of kangaroo.Row """ self.__delete_row_from_index(row) self.__rows.remove(row)
[docs] def insert(self, data): """Inserts a new row in the table :param data: A dictionary that it's going to define the columns of the new Row. :returns: An instance of Row """ row = Row(table=self, **data) self.__rows.append(row) for k in self.tbl_index: self.__build_index(k, [row]) return row
[docs] def find(self, **kwargs): """Finds a row in the table Example: >> table.database.find(my_field=1, other_field__gt=50) :param kwargs: a list of params that we are going to use to filter the existing rows. :returns: None if there is no row that matchs or an instance of Row otherwise. """ result_set = self.find_all(**kwargs) if len(result_set) > 0: return result_set[0] return None
def __reduce_row_by_index(self, filters): row_groups = [] active_indexs = 0 for filter_name, filter_value in filters.items(): if filter_name in self.tbl_index: active_indexs += 1 row_groups.append( self.__index[filter_name].get(filter_value, [])) if active_indexs == 0: return self.__rows rows = [] row_groups.sort(key=lambda x: len(x)) for row in row_groups[0]: add = True for ll in row_groups[1:]: add = a in ll if not add: break if add: rows.append(row) return rows def __parse_filters(self, params): filters = [] fields = {} for k in params.keys(): args = k.split("__") key = args[0] value = params[k] operator_name = args[1] if len(args) == 2 else "eq" fields[key] = value op_class = get_operator(operator_name) operator = op_class(key, value) filters.append(operator) return fields, filters
[docs] def find_all(self, **kwargs): """Finds a list of rows in the table Example: >> table.database.find_all(my_field=1, other_field__gt=50) :param kwargs: a list of params that we are going to use to filter the existing rows. :returns: None if there is no row that matchs or a list of Row instances otherwise. """ fields, filters = self.__parse_filters(kwargs) result_set = self.__reduce_row_by_index(fields) for f in filters: result_set = list(filter(f.compare, result_set)) return result_set