def get_available_skins(selected=None): """selected is a name of preferred skin if it's None, then information about all skins will be returned otherwise, only data about selected and default skins will be returned selected skin is guaranteed to be the first item in the dictionary """ skins = SortedDict() if hasattr(django_settings, 'ASKBOT_EXTRA_SKINS_DIR'): skins.update(get_skins_from_dir(django_settings.ASKBOT_EXTRA_SKINS_DIR)) stock_dir = os.path.normpath(os.path.dirname(__file__)) stock_skins = get_skins_from_dir(stock_dir) default_dir = stock_skins.pop('default') skins.update(stock_skins) if selected: if selected in skins: selected_dir = skins[selected] skins.clear() skins[selected] = selected_dir else: assert(selected == 'default') skins = SortedDict() #re-insert default as a last item skins['default'] = default_dir return skins
def get_available_skins(selected=None): """Returns a dictionary of skin name --> directory where "templates" and "media" subdirectories can be found. selected is a name of preferred skin if it's None, then information about all skins will be returned otherwise, only data about selected and default skins will be returned selected skin is guaranteed to be the first item in the dictionary """ skins = SortedDict() if hasattr(django_settings, 'ASKBOT_EXTRA_SKINS_DIR'): skins.update(get_skins_from_dir(django_settings.ASKBOT_EXTRA_SKINS_DIR)) if 'default' in skins: raise ValueError('"default" is not an acceptable name for a custom skin') if selected in skins: selected_dir = skins[selected] skins.clear() skins[selected] = selected_dir elif selected == 'default': skins = SortedDict() elif selected: raise ValueError( 'skin ' + str(selected) + \ ' not found, please check ASKBOT_EXTRA_SKINS_DIR setting ' + \ 'or in the corresponding directory' ) #insert default as a last item skins['default'] = askbot.get_install_directory() return skins
def get_available_skins(selected=None): """selected is a name of preferred skin if it's None, then information about all skins will be returned otherwise, only data about selected and default skins will be returned selected skin is guaranteed to be the first item in the dictionary """ skins = SortedDict() if hasattr(django_settings, 'ASKBOT_EXTRA_SKINS_DIR'): skins.update(get_skins_from_dir( django_settings.ASKBOT_EXTRA_SKINS_DIR)) stock_dir = os.path.normpath(os.path.dirname(__file__)) stock_skins = get_skins_from_dir(stock_dir) default_dir = stock_skins.pop('default') skins.update(stock_skins) if selected: if selected in skins: selected_dir = skins[selected] skins.clear() skins[selected] = selected_dir else: assert (selected == 'default') skins = SortedDict() #re-insert default as a last item skins['default'] = default_dir return skins
class Cart(models.Model): user = models.OneToOneField(User, null=True, blank=True) created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) objects = CartManager() def __init__(self, *args, **kwargs): super(Cart, self).__init__(*args, **kwargs) self.modifiers = SortedDict() def reset_cached_items(self): key = self.cachekey() l = list(self.items.all()) cache.set(key, l, 60 * 60 * 24 * 7 * 4) return l def cachekey(self, append=''): return 'fastcart_items:%s%s' % (self.pk, append) def get_items(self): key = self.cachekey() l = cache.get(key, '!') if l is '!': return self.reset_cached_items() elif l: return l else: return [] def get_price(self): price = Decimal('0.00') for item in self.get_items(): price += item.get_total_price() return price def get_count(self): c = 0 for item in self.get_items(): c = c + item.quantity return c def get_total_price(self): total_price = self.get_price() for modifier in get_cart_modifiers(): total_price = modifier(self, total_price) return total_price def add(self, product, quantity=1): item, created = self.items.get_or_create(product=product) if not created: item.quantity += quantity else: item.quantity = quantity item.save() return item def clear(self): self.items.all().delete() self.modifiers.clear()
class SortedDictTests(SimpleTestCase): def setUp(self): self.d1 = SortedDict() self.d1[7] = 'seven' self.d1[1] = 'one' self.d1[9] = 'nine' self.d2 = SortedDict() self.d2[1] = 'one' self.d2[9] = 'nine' self.d2[0] = 'nil' self.d2[7] = 'seven' def test_basic_methods(self): self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9]) self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'one', 'nine']) self.assertEqual(list(six.iteritems(self.d1)), [(7, 'seven'), (1, 'one'), (9, 'nine')]) def test_overwrite_ordering(self): """ Overwriting an item keeps its place. """ self.d1[1] = 'ONE' self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'ONE', 'nine']) def test_append_items(self): """ New items go to the end. """ self.d1[0] = 'nil' self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9, 0]) def test_delete_and_insert(self): """ Deleting an item, then inserting the same key again will place it at the end. """ del self.d2[7] self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0]) self.d2[7] = 'lucky number 7' self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7]) if six.PY2: def test_change_keys(self): """ Changing the keys won't do anything, it's only a copy of the keys dict. This test doesn't make sense under Python 3 because keys is an iterator. """ k = self.d2.keys() k.remove(9) self.assertEqual(self.d2.keys(), [1, 9, 0, 7]) def test_init_keys(self): """ Initialising a SortedDict with two keys will just take the first one. A real dict will actually take the second value so we will too, but we'll keep the ordering from the first key found. """ tuples = ((2, 'two'), (1, 'one'), (2, 'second-two')) d = SortedDict(tuples) self.assertEqual(list(six.iterkeys(d)), [2, 1]) real_dict = dict(tuples) self.assertEqual(sorted(six.itervalues(real_dict)), ['one', 'second-two']) # Here the order of SortedDict values *is* what we are testing self.assertEqual(list(six.itervalues(d)), ['second-two', 'one']) def test_overwrite(self): self.d1[1] = 'not one' self.assertEqual(self.d1[1], 'not one') self.assertEqual(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy()))) def test_append(self): self.d1[13] = 'thirteen' self.assertEqual( repr(self.d1), "{7: 'seven', 1: 'one', 9: 'nine', 13: 'thirteen'}" ) def test_pop(self): self.assertEqual(self.d1.pop(1, 'missing'), 'one') self.assertEqual(self.d1.pop(1, 'missing'), 'missing') # We don't know which item will be popped in popitem(), so we'll # just check that the number of keys has decreased. l = len(self.d1) self.d1.popitem() self.assertEqual(l - len(self.d1), 1) def test_dict_equality(self): d = SortedDict((i, i) for i in range(3)) self.assertEqual(d, {0: 0, 1: 1, 2: 2}) def test_tuple_init(self): d = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) self.assertEqual(repr(d), "{1: 'one', 0: 'zero', 2: 'two'}") def test_pickle(self): self.assertEqual( pickle.loads(pickle.dumps(self.d1, 2)), {7: 'seven', 1: 'one', 9: 'nine'} ) def test_copy(self): orig = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) copied = copy.copy(orig) self.assertEqual(list(six.iterkeys(orig)), [1, 0, 2]) self.assertEqual(list(six.iterkeys(copied)), [1, 0, 2]) def test_clear(self): self.d1.clear() self.assertEqual(self.d1, {}) self.assertEqual(self.d1.keyOrder, []) def test_reversed(self): self.assertEqual(list(self.d1), [7, 1, 9]) self.assertEqual(list(self.d2), [1, 9, 0, 7]) self.assertEqual(list(reversed(self.d1)), [9, 1, 7]) self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1]) def test_insert(self): d = SortedDict() with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") d.insert(0, "hello", "world") assert w[0].category is DeprecationWarning def test_value_for_index(self): d = SortedDict({"a": 3}) with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") self.assertEqual(d.value_for_index(0), 3) assert w[0].category is DeprecationWarning
class SortedDictTests(IgnoreDeprecationWarningsMixin, SimpleTestCase): def setUp(self): super(SortedDictTests, self).setUp() self.d1 = SortedDict() self.d1[7] = 'seven' self.d1[1] = 'one' self.d1[9] = 'nine' self.d2 = SortedDict() self.d2[1] = 'one' self.d2[9] = 'nine' self.d2[0] = 'nil' self.d2[7] = 'seven' def test_basic_methods(self): self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9]) self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'one', 'nine']) self.assertEqual(list(six.iteritems(self.d1)), [(7, 'seven'), (1, 'one'), (9, 'nine')]) def test_overwrite_ordering(self): """ Overwriting an item keeps its place. """ self.d1[1] = 'ONE' self.assertEqual(list(six.itervalues(self.d1)), ['seven', 'ONE', 'nine']) def test_append_items(self): """ New items go to the end. """ self.d1[0] = 'nil' self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9, 0]) def test_delete_and_insert(self): """ Deleting an item, then inserting the same key again will place it at the end. """ del self.d2[7] self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0]) self.d2[7] = 'lucky number 7' self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7]) if six.PY2: def test_change_keys(self): """ Changing the keys won't do anything, it's only a copy of the keys dict. This test doesn't make sense under Python 3 because keys is an iterator. """ k = self.d2.keys() k.remove(9) self.assertEqual(self.d2.keys(), [1, 9, 0, 7]) def test_init_keys(self): """ Initialising a SortedDict with two keys will just take the first one. A real dict will actually take the second value so we will too, but we'll keep the ordering from the first key found. """ tuples = ((2, 'two'), (1, 'one'), (2, 'second-two')) d = SortedDict(tuples) self.assertEqual(list(six.iterkeys(d)), [2, 1]) real_dict = dict(tuples) self.assertEqual(sorted(six.itervalues(real_dict)), ['one', 'second-two']) # Here the order of SortedDict values *is* what we are testing self.assertEqual(list(six.itervalues(d)), ['second-two', 'one']) def test_overwrite(self): self.d1[1] = 'not one' self.assertEqual(self.d1[1], 'not one') self.assertEqual(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy()))) def test_append(self): self.d1[13] = 'thirteen' self.assertEqual(repr(self.d1), "{7: 'seven', 1: 'one', 9: 'nine', 13: 'thirteen'}") def test_pop(self): self.assertEqual(self.d1.pop(1, 'missing'), 'one') self.assertEqual(self.d1.pop(1, 'missing'), 'missing') # We don't know which item will be popped in popitem(), so we'll # just check that the number of keys has decreased. l = len(self.d1) self.d1.popitem() self.assertEqual(l - len(self.d1), 1) def test_dict_equality(self): d = SortedDict((i, i) for i in range(3)) self.assertEqual(d, {0: 0, 1: 1, 2: 2}) def test_tuple_init(self): d = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) self.assertEqual(repr(d), "{1: 'one', 0: 'zero', 2: 'two'}") def test_pickle(self): self.assertEqual(pickle.loads(pickle.dumps(self.d1, 2)), { 7: 'seven', 1: 'one', 9: 'nine' }) def test_copy(self): orig = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) copied = copy.copy(orig) self.assertEqual(list(six.iterkeys(orig)), [1, 0, 2]) self.assertEqual(list(six.iterkeys(copied)), [1, 0, 2]) def test_clear(self): self.d1.clear() self.assertEqual(self.d1, {}) self.assertEqual(self.d1.keyOrder, []) def test_reversed(self): self.assertEqual(list(self.d1), [7, 1, 9]) self.assertEqual(list(self.d2), [1, 9, 0, 7]) self.assertEqual(list(reversed(self.d1)), [9, 1, 7]) self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1])
class SortedDictTests(SimpleTestCase): def setUp(self): self.d1 = SortedDict() self.d1[7] = 'seven' self.d1[1] = 'one' self.d1[9] = 'nine' self.d2 = SortedDict() self.d2[1] = 'one' self.d2[9] = 'nine' self.d2[0] = 'nil' self.d2[7] = 'seven' def test_basic_methods(self): self.assertEqual(self.d1.keys(), [7, 1, 9]) self.assertEqual(self.d1.values(), ['seven', 'one', 'nine']) self.assertEqual(self.d1.items(), [(7, 'seven'), (1, 'one'), (9, 'nine')]) def test_overwrite_ordering(self): """ Overwriting an item keeps it's place. """ self.d1[1] = 'ONE' self.assertEqual(self.d1.values(), ['seven', 'ONE', 'nine']) def test_append_items(self): """ New items go to the end. """ self.d1[0] = 'nil' self.assertEqual(self.d1.keys(), [7, 1, 9, 0]) def test_delete_and_insert(self): """ Deleting an item, then inserting the same key again will place it at the end. """ del self.d2[7] self.assertEqual(self.d2.keys(), [1, 9, 0]) self.d2[7] = 'lucky number 7' self.assertEqual(self.d2.keys(), [1, 9, 0, 7]) def test_change_keys(self): """ Changing the keys won't do anything, it's only a copy of the keys dict. """ k = self.d2.keys() k.remove(9) self.assertEqual(self.d2.keys(), [1, 9, 0, 7]) def test_init_keys(self): """ Initialising a SortedDict with two keys will just take the first one. A real dict will actually take the second value so we will too, but we'll keep the ordering from the first key found. """ tuples = ((2, 'two'), (1, 'one'), (2, 'second-two')) d = SortedDict(tuples) self.assertEqual(d.keys(), [2, 1]) real_dict = dict(tuples) self.assertEqual(sorted(real_dict.values()), ['one', 'second-two']) # Here the order of SortedDict values *is* what we are testing self.assertEqual(d.values(), ['second-two', 'one']) def test_overwrite(self): self.d1[1] = 'not one' self.assertEqual(self.d1[1], 'not one') self.assertEqual(self.d1.keys(), self.d1.copy().keys()) def test_append(self): self.d1[13] = 'thirteen' self.assertEqual( repr(self.d1), "{7: 'seven', 1: 'one', 9: 'nine', 13: 'thirteen'}" ) def test_pop(self): self.assertEqual(self.d1.pop(1, 'missing'), 'one') self.assertEqual(self.d1.pop(1, 'missing'), 'missing') # We don't know which item will be popped in popitem(), so we'll # just check that the number of keys has decreased. l = len(self.d1) self.d1.popitem() self.assertEqual(l - len(self.d1), 1) def test_dict_equality(self): d = SortedDict((i, i) for i in xrange(3)) self.assertEqual(d, {0: 0, 1: 1, 2: 2}) def test_tuple_init(self): d = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) self.assertEqual(repr(d), "{1: 'one', 0: 'zero', 2: 'two'}") def test_pickle(self): self.assertEqual( pickle.loads(pickle.dumps(self.d1, 2)), {7: 'seven', 1: 'one', 9: 'nine'} ) def test_clear(self): self.d1.clear() self.assertEqual(self.d1, {}) self.assertEqual(self.d1.keyOrder, [])
class Cart(models.Model): user = models.OneToOneField(User, null=True, blank=True) created_on = models.DateTimeField(auto_now_add=True) updated_on = models.DateTimeField(auto_now=True) objects = CartManager() def __init__(self, *args, **kwargs): super(Cart, self).__init__(*args, **kwargs) self.modifiers = SortedDict() def reset_cached_items(self): key = self.cachekey() l = list(self.items.all()) cache.set(key, l, 60 * 60 * 24 * 7 * 4) return l def cachekey(self, append=''): return 'fastcart_items:%s%s' % (self.pk, append) def get_items(self): key = self.cachekey() l = cache.get(key, '!') if l is '!': return self.reset_cached_items() elif l: return l else: return [] def get_price(self): price = Decimal('0.00') for item in self.get_items(): price += item.get_total_price() return price def get_count(self): c = 0 for item in self.get_items(): c = c + item.quantity return c def get_total_price(self): total_price = self.get_price() for modifier in get_cart_modifiers(): total_price = modifier(self, total_price) return total_price def add(self, product, quantity=1, price=None): item_qry = self.items.filter(product=product) item = None # import pdb;pdb.set_trace() if item_qry.exists(): item = item_qry[0] item.quantity += quantity else: item = CartItem() item.product = product item.cart = self if price: item.price = price else: price_qry = Price.objects.filter(product=product) if price_qry.exists(): item.price = price_qry[0] item.quantity = quantity item.save() return item def clear(self): self.items.all().delete() self.modifiers.clear()
class SortedDictTests(SimpleTestCase): def setUp(self): super(SortedDictTests, self).setUp() self.d1 = SortedDict() self.d1[7] = "seven" self.d1[1] = "one" self.d1[9] = "nine" self.d2 = SortedDict() self.d2[1] = "one" self.d2[9] = "nine" self.d2[0] = "nil" self.d2[7] = "seven" def test_basic_methods(self): self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9]) self.assertEqual(list(six.itervalues(self.d1)), ["seven", "one", "nine"]) self.assertEqual(list(six.iteritems(self.d1)), [(7, "seven"), (1, "one"), (9, "nine")]) def test_overwrite_ordering(self): """ Overwriting an item keeps its place. """ self.d1[1] = "ONE" self.assertEqual(list(six.itervalues(self.d1)), ["seven", "ONE", "nine"]) def test_append_items(self): """ New items go to the end. """ self.d1[0] = "nil" self.assertEqual(list(six.iterkeys(self.d1)), [7, 1, 9, 0]) def test_delete_and_insert(self): """ Deleting an item, then inserting the same key again will place it at the end. """ del self.d2[7] self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0]) self.d2[7] = "lucky number 7" self.assertEqual(list(six.iterkeys(self.d2)), [1, 9, 0, 7]) if six.PY2: def test_change_keys(self): """ Changing the keys won't do anything, it's only a copy of the keys dict. This test doesn't make sense under Python 3 because keys is an iterator. """ k = self.d2.keys() k.remove(9) self.assertEqual(self.d2.keys(), [1, 9, 0, 7]) def test_init_keys(self): """ Initialising a SortedDict with two keys will just take the first one. A real dict will actually take the second value so we will too, but we'll keep the ordering from the first key found. """ tuples = ((2, "two"), (1, "one"), (2, "second-two")) d = SortedDict(tuples) self.assertEqual(list(six.iterkeys(d)), [2, 1]) real_dict = dict(tuples) self.assertEqual(sorted(six.itervalues(real_dict)), ["one", "second-two"]) # Here the order of SortedDict values *is* what we are testing self.assertEqual(list(six.itervalues(d)), ["second-two", "one"]) def test_overwrite(self): self.d1[1] = "not one" self.assertEqual(self.d1[1], "not one") self.assertEqual(list(six.iterkeys(self.d1)), list(six.iterkeys(self.d1.copy()))) def test_append(self): self.d1[13] = "thirteen" self.assertEqual(repr(self.d1), "{7: 'seven', 1: 'one', 9: 'nine', 13: 'thirteen'}") def test_pop(self): self.assertEqual(self.d1.pop(1, "missing"), "one") self.assertEqual(self.d1.pop(1, "missing"), "missing") # We don't know which item will be popped in popitem(), so we'll # just check that the number of keys has decreased. l = len(self.d1) self.d1.popitem() self.assertEqual(l - len(self.d1), 1) def test_dict_equality(self): d = SortedDict((i, i) for i in range(3)) self.assertEqual(d, {0: 0, 1: 1, 2: 2}) def test_tuple_init(self): d = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) self.assertEqual(repr(d), "{1: 'one', 0: 'zero', 2: 'two'}") def test_pickle(self): self.assertEqual(pickle.loads(pickle.dumps(self.d1, 2)), {7: "seven", 1: "one", 9: "nine"}) def test_copy(self): orig = SortedDict(((1, "one"), (0, "zero"), (2, "two"))) copied = copy.copy(orig) self.assertEqual(list(six.iterkeys(orig)), [1, 0, 2]) self.assertEqual(list(six.iterkeys(copied)), [1, 0, 2]) def test_clear(self): self.d1.clear() self.assertEqual(self.d1, {}) self.assertEqual(self.d1.keyOrder, []) def test_reversed(self): self.assertEqual(list(self.d1), [7, 1, 9]) self.assertEqual(list(self.d2), [1, 9, 0, 7]) self.assertEqual(list(reversed(self.d1)), [9, 1, 7]) self.assertEqual(list(reversed(self.d2)), [7, 0, 9, 1])
class ExtGrid(BaseExtPanel): """ Таблица (Grid) Внимание! Грид реализует двуличное поведение в зависимости от атрибута editor. Порождающая его функция createGridPanel может вернуть экземпляр Ext.m3.GridPanel (False) или Ext.m3.EditorGridPanel (True), поэтому некоторые атрибуты могут действовать в одном, но не действовать в другом гриде. """ manageable_listeners = ("dblclick",) # TODO: Реализовать человеческий MVC грид def __init__(self, *args, **kwargs): super(ExtGrid, self).__init__(*args, **kwargs) self._items = [] self.__store = None # Будет ли редактироваться self.editor = False # Объект маскирования, который будет отображаться при загрузке self.load_mask = False # Сколько раз нужно щелкнуть для редактирования ячейки. # Только для EditorGridPanel self.clicks_to_edit = 2 self.drag_drop = False self.drag_drop_group = None # Разворачивать колонки грида по всей ширине (True) self.force_fit = True # selection model self.__sm = None self.__view = None # Колонка для авторасширения self.auto_expand_column = None # устанавливается True, если sm=CheckBoxSelectionModel. Этот флаг нужен # чтобы знать когда нужен дополнительный column self.__checkbox = False # перечень плагинов self.plugins = [] # модель колонок self.__cm = None self.col_model = ExtGridDefaultColumnModel() # Конфигурация для уровня view self._view_config = {} self.show_preview = False self.enable_row_body = False self.get_row_class = None # признак отображения вертикальных линий в гриде self.column_lines = True # Если True не рендерим drag and drop, выключаем editor self.read_only = False # Метка. Использовать только если задан layout=form self.label = None self.init_component(*args, **kwargs) # protected self.show_banded_columns = False self.banded_columns = SortedDict() def t_render_plugins(self): """ Рендеринг плагинов """ res = [] for plugin in self.plugins: res.append(plugin.render() if hasattr(plugin, "render") else plugin) return "[%s]" % ",".join(res) def t_render_banded_columns(self): """ Возвращает JS массив состоящий из массивов с описанием объединенных колонок. Каждый вложенный массив соответствует уровню шапки грида от верхней к нижней. """ result = [] for level_list in self.banded_columns.values(): result.append("[%s]" % ",".join([column.render() for column in level_list])) return "[%s]" % ",".join(result) def t_render_columns(self): return self.t_render_items() def t_render_store(self): assert self.__store, "Store is not define" return self.__store.render(self.columns) def t_render_col_model(self): return self.__cm.render() def add_column(self, **kwargs): """ Добавляет стандартную колонку """ self.columns.append(ExtGridColumn(**kwargs)) def add_bool_column(self, **kwargs): """ Добавляет булевую колонку """ self.columns.append(ExtGridBooleanColumn(**kwargs)) def add_check_column(self, **kwargs): """ Добавляет колонку для выбора значения """ self.columns.append(ExtGridCheckColumn(**kwargs)) def add_number_column(self, **kwargs): """ Добавляет числовую колонку """ self.columns.append(ExtGridNumberColumn(**kwargs)) def add_date_column(self, **kwargs): """ Добавляет колонку с датой """ self.columns.append(ExtGridDateColumn(**kwargs)) def add_banded_column(self, column, level, colspan): """ Добавляет в грид объединенную ячейку. :param column: Колонка грида :type column: ExtGridColumn или None :param colspan: Количество колонок, кот-е находятся под данной колонкой :type colspan: int :param level: Уровень ячейки (0 - самый верхний, 1-ниже, и т.д) :type level: int .. note :: upd:26.10.2010 kirov колонка может быть не указана, т.е. None, в этом случае на указанном уровне будет "дырка" """ class BlankBandColumn: colspan = 0 def render(self): return "{%s}" % (("colspan:%s" % self.colspan) if self.colspan else "") assert isinstance(level, int) assert isinstance(colspan, int) assert isinstance(column, ExtGridColumn) or not column if not column: column = BlankBandColumn() # Колонки хранятся в списках внутки сортированного словаря, # чтобы их можно было # извечь по возрастанию уровней column.colspan = colspan level_list = self.banded_columns.get(level, []) level_list.append(column) self.banded_columns[level] = level_list self.show_banded_columns = True def clear_banded_columns(self): """ Удаляет все объединенные колонки из грида """ self.banded_columns.clear() self.show_banded_columns = False def set_store(self, store): self.__store = store def get_store(self): return self.__store store = property(get_store, set_store) def _make_read_only(self, access_off=True, exclude_list=(), *args, **kwargs): super(ExtGrid, self)._make_read_only(access_off, exclude_list, *args, **kwargs) self.read_only = access_off if self.columns: for column in self.columns: column.make_read_only(self.read_only, exclude_list, *args, **kwargs) # контекстное меню. context_menu_items = [self.handler_contextmenu, self.handler_rowcontextmenu] for context_menu in context_menu_items: if ( context_menu and hasattr(context_menu, "items") and context_menu.items and hasattr(context_menu.items, "__iter__") ): for item in context_menu.items: if isinstance(item, ExtUIComponent): item.make_read_only(self.read_only, exclude_list, *args, **kwargs) @property def columns(self): return self._items @property def sm(self): return self.__sm @sm.setter def sm(self, value): self.__sm = value self.checkbox_model = isinstance(self.__sm, ExtGridCheckBoxSelModel) @property def view(self): return self.__view @view.setter def view(self, value): self.__view = value def t_render_view(self): return self.view.render() def pre_render(self): super(ExtGrid, self).pre_render() if self.store: self.store.action_context = self.action_context @property def col_model(self): return self.__cm @col_model.setter def col_model(self, value): self.__cm = value self.__cm.grid = self @property def handler_click(self): return self._listeners.get("click") @handler_click.setter def handler_click(self, function): self._listeners["click"] = function @property def handler_dblclick(self): if self.read_only: # Если грид работает в режиме "чтения" # выключаем двойной клик, заменяя пустой функцией return "Ext.emptyFn" return self._listeners.get("dblclick") @handler_dblclick.setter def handler_dblclick(self, function): self._listeners["dblclick"] = function @property def handler_contextmenu(self): return self._listeners.get("contextmenu") @handler_contextmenu.setter def handler_contextmenu(self, menu): menu.container = self self._listeners["contextmenu"] = menu @property def handler_rowcontextmenu(self): return self._listeners.get("rowcontextmenu") @handler_rowcontextmenu.setter def handler_rowcontextmenu(self, menu): menu.container = self self._listeners["rowcontextmenu"] = menu def render_base_config(self): super(ExtGrid, self).render_base_config() if self.force_fit: self._view_config["forceFit"] = self.force_fit if self.show_preview: self._view_config["showPreview"] = self.show_preview if self.enable_row_body: self._view_config["enableRowBody"] = self.enable_row_body if self.get_row_class: self._view_config["getRowClass"] = self.get_row_class for args in ( ("stripeRows", True), ("stateful", True), ("loadMask", self.load_mask), ("autoExpandColumn", self.auto_expand_column), ("editor", self.editor), ("view", self.t_render_view, self.view), ("store", self.t_render_store, self.get_store()), ("viewConfig", self._view_config), ("columnLines", self.column_lines, self.column_lines), ("enableDragDrop", self.drag_drop) if self.read_only else (), ("ddGroup", self.drag_drop_group) if self.read_only else (), ("fieldLabel", self.label) if self.label else (), ("clicksToEdit", self.clicks_to_edit, self.clicks_to_edit != 2) if self.editor else (), ): if args: self._put_config_value(*args) def render_params(self): super(ExtGrid, self).render_params() handler_cont_menu = self.handler_contextmenu.render if self.handler_contextmenu else "" handler_rowcontextmenu = self.handler_rowcontextmenu.render if self.handler_rowcontextmenu else "" self._put_params_value("menus", {"contextMenu": handler_cont_menu, "rowContextMenu": handler_rowcontextmenu}) if self.sm: self._put_params_value("selModel", self.sm.render) self._put_params_value("colModel", self.col_model.render) # проверим набор колонок на наличие фильтров, # если есть, то добавим плагин с фильтрами for col in self.columns: if col.filter: self.plugins.append(u"new Ext.ux.grid.GridFilters({menuFilterText:'Фильтр'})") break self._put_params_value("plugins", self.t_render_plugins) if self.show_banded_columns: self._put_params_value("bundedColumns", self.t_render_banded_columns) def render(self): try: self.pre_render() self.render_base_config() self.render_params() except UnicodeDecodeError as msg: raise Exception(msg) config = self._get_config_str() params = self._get_params_str() return "createGridPanel({%s}, {%s})" % (config, params)
class ExtGrid(BaseExtPanel): """ Таблица (Grid) Внимание! Грид реализует двуличное поведение в зависимости от атрибута editor. Порождающая его функция createGridPanel может вернуть экземпляр Ext.m3.GridPanel (False) или Ext.m3.EditorGridPanel (True), поэтому некоторые атрибуты могут действовать в одном, но не действовать в другом гриде. """ manageable_listeners = ('dblclick', ) # TODO: Реализовать человеческий MVC грид def __init__(self, *args, **kwargs): super(ExtGrid, self).__init__(*args, **kwargs) self._items = [] self.__store = None # Будет ли редактироваться self.editor = False # Объект маскирования, который будет отображаться при загрузке self.load_mask = False # Сколько раз нужно щелкнуть для редактирования ячейки. # Только для EditorGridPanel self.clicks_to_edit = 2 self.drag_drop = False self.drag_drop_group = None # Разворачивать колонки грида по всей ширине (True) self.force_fit = True # selection model self.__sm = None self.__view = None # Колонка для авторасширения self.auto_expand_column = None # устанавливается True, если sm=CheckBoxSelectionModel. Этот флаг нужен # чтобы знать когда нужен дополнительный column self.__checkbox = False # перечень плагинов self.plugins = [] # модель колонок self.__cm = None self.col_model = ExtGridDefaultColumnModel() # Конфигурация для уровня view self._view_config = {} self.show_preview = False self.enable_row_body = False self.get_row_class = None # признак отображения вертикальных линий в гриде self.column_lines = True #Если True не рендерим drag and drop, выключаем editor self.read_only = False # Метка. Использовать только если задан layout=form self.label = None self.init_component(*args, **kwargs) # protected self.show_banded_columns = False self.banded_columns = SortedDict() def t_render_plugins(self): """ Рендеринг плагинов """ res = [] for plugin in self.plugins: res.append( plugin.render() if hasattr(plugin, 'render') else plugin) return '[%s]' % ','.join(res) def t_render_banded_columns(self): """ Возвращает JS массив состоящий из массивов с описанием объединенных колонок. Каждый вложенный массив соответствует уровню шапки грида от верхней к нижней. """ result = [] for level_list in self.banded_columns.values(): result.append('[%s]' % ','.join([column.render() for column in level_list])) return '[%s]' % ','.join(result) def t_render_columns(self): return self.t_render_items() def t_render_store(self): assert self.__store, 'Store is not define' return self.__store.render(self.columns) def t_render_col_model(self): return self.__cm.render() def add_column(self, **kwargs): """ Добавляет стандартную колонку """ self.columns.append(ExtGridColumn(**kwargs)) def add_bool_column(self, **kwargs): """ Добавляет булевую колонку """ self.columns.append(ExtGridBooleanColumn(**kwargs)) def add_check_column(self, **kwargs): """ Добавляет колонку для выбора значения """ self.columns.append(ExtGridCheckColumn(**kwargs)) def add_number_column(self, **kwargs): """ Добавляет числовую колонку """ self.columns.append(ExtGridNumberColumn(**kwargs)) def add_date_column(self, **kwargs): """ Добавляет колонку с датой """ self.columns.append(ExtGridDateColumn(**kwargs)) def add_banded_column(self, column, level, colspan): """ Добавляет в грид объединенную ячейку. :param column: Колонка грида :type column: ExtGridColumn или None :param colspan: Количество колонок, кот-е находятся под данной колонкой :type colspan: int :param level: Уровень ячейки (0 - самый верхний, 1-ниже, и т.д) :type level: int .. note :: upd:26.10.2010 kirov колонка может быть не указана, т.е. None, в этом случае на указанном уровне будет "дырка" """ class BlankBandColumn(): colspan = 0 def render(self): return '{%s}' % ( ('colspan:%s' % self.colspan) if self.colspan else '') assert isinstance(level, int) assert isinstance(colspan, int) assert isinstance(column, ExtGridColumn) or not column if not column: column = BlankBandColumn() # Колонки хранятся в списках внутки сортированного словаря, #чтобы их можно было # извечь по возрастанию уровней column.colspan = colspan level_list = self.banded_columns.get(level, []) level_list.append(column) self.banded_columns[level] = level_list self.show_banded_columns = True def clear_banded_columns(self): """ Удаляет все объединенные колонки из грида """ self.banded_columns.clear() self.show_banded_columns = False def set_store(self, store): self.__store = store def get_store(self): return self.__store store = property(get_store, set_store) def _make_read_only(self, access_off=True, exclude_list=(), *args, **kwargs): super(ExtGrid, self)._make_read_only(access_off, exclude_list, *args, **kwargs) self.read_only = access_off if self.columns: for column in self.columns: column.make_read_only(self.read_only, exclude_list, *args, **kwargs) # контекстное меню. context_menu_items = [ self.handler_contextmenu, self.handler_rowcontextmenu ] for context_menu in context_menu_items: if (context_menu and hasattr(context_menu, 'items') and context_menu.items and hasattr(context_menu.items, '__iter__')): for item in context_menu.items: if isinstance(item, ExtUIComponent): item.make_read_only(self.read_only, exclude_list, *args, **kwargs) @property def columns(self): return self._items @property def sm(self): return self.__sm @sm.setter def sm(self, value): self.__sm = value self.checkbox_model = isinstance(self.__sm, ExtGridCheckBoxSelModel) @property def view(self): return self.__view @view.setter def view(self, value): self.__view = value def t_render_view(self): return self.view.render() def pre_render(self): super(ExtGrid, self).pre_render() if self.store: self.store.action_context = self.action_context @property def col_model(self): return self.__cm @col_model.setter def col_model(self, value): self.__cm = value self.__cm.grid = self @property def handler_click(self): return self._listeners.get('click') @handler_click.setter def handler_click(self, function): self._listeners['click'] = function @property def handler_dblclick(self): if self.read_only: # Если грид работает в режиме "чтения" # выключаем двойной клик, заменяя пустой функцией return 'Ext.emptyFn' return self._listeners.get('dblclick') @handler_dblclick.setter def handler_dblclick(self, function): self._listeners['dblclick'] = function @property def handler_contextmenu(self): return self._listeners.get('contextmenu') @handler_contextmenu.setter def handler_contextmenu(self, menu): menu.container = self self._listeners['contextmenu'] = menu @property def handler_rowcontextmenu(self): return self._listeners.get('rowcontextmenu') @handler_rowcontextmenu.setter def handler_rowcontextmenu(self, menu): menu.container = self self._listeners['rowcontextmenu'] = menu def render_base_config(self): super(ExtGrid, self).render_base_config() if self.force_fit: self._view_config['forceFit'] = self.force_fit if self.show_preview: self._view_config['showPreview'] = self.show_preview if self.enable_row_body: self._view_config['enableRowBody'] = self.enable_row_body if self.get_row_class: self._view_config['getRowClass'] = self.get_row_class for args in ( ('stripeRows', True), ('stateful', True), ('loadMask', self.load_mask), ('autoExpandColumn', self.auto_expand_column), ('editor', self.editor), ('view', self.t_render_view, self.view), ('store', self.t_render_store, self.get_store()), ('viewConfig', self._view_config), ('columnLines', self.column_lines, self.column_lines), ('enableDragDrop', self.drag_drop) if self.read_only else (), ('ddGroup', self.drag_drop_group) if self.read_only else (), ('fieldLabel', self.label) if self.label else (), ('clicksToEdit', self.clicks_to_edit, self.clicks_to_edit != 2) if self.editor else (), ): if args: self._put_config_value(*args) def render_params(self): super(ExtGrid, self).render_params() handler_cont_menu = (self.handler_contextmenu.render if self.handler_contextmenu else '') handler_rowcontextmenu = (self.handler_rowcontextmenu.render if self.handler_rowcontextmenu else '') self._put_params_value( 'menus', { 'contextMenu': handler_cont_menu, 'rowContextMenu': handler_rowcontextmenu }) if self.sm: self._put_params_value('selModel', self.sm.render) self._put_params_value('colModel', self.col_model.render) # проверим набор колонок на наличие фильтров, # если есть, то добавим плагин с фильтрами for col in self.columns: if col.filter: self.plugins.append( u"new Ext.ux.grid.GridFilters({menuFilterText:'Фильтр'})") break self._put_params_value('plugins', self.t_render_plugins) if self.show_banded_columns: self._put_params_value('bundedColumns', self.t_render_banded_columns) def render(self): try: self.pre_render() self.render_base_config() self.render_params() except UnicodeDecodeError as msg: raise Exception(msg) config = self._get_config_str() params = self._get_params_str() return 'createGridPanel({%s}, {%s})' % (config, params)