def test_validation_order(self): # This test reproduces a previously existing bug in computer input # validation dependencies. fields = ( pp.Field('a', type=pd.String(not_null=True)), pp.Field('b', type=pd.String(not_null=True, maxlen=1), computer=pp.computer(lambda r, a: a[0].upper(), validate=True)), pp.Field('c', type=pd.String(enumerator=pd.FixedEnumerator(range(10)), not_null=True), computer=pp.computer(lambda r, b: str(ord(b) % 10), validate=True)), ) row = self._row(fields, new=True) row.validate('a', 'foo') assert row['b'].value() == 'F' assert row['c'].value() == '0' # Set 'b' to an invalid value (violates maxlen=1). row.validate('b', 'xxx') def cb(): # This used to fail when the computer for 'c' was not called # due to invalid value of 'b' (when 'b' validity was not refreshed # correctly). assert row['c'].value() == '6' row.register_callback(row.CALL_CHANGE, 'c', cb) row.validate('a', 'bar')
def _customize_fields(self, fields): fields.set_property('width', id=8, data=130, filename=30, title=30, descr=60) fields.modify('id', default=pytis.util.nextval('images_id_seq')) fields.modify('data', height=130, type=pytis.data.Image(maxlen=(2 * MB), formats=('JPEG', 'PNG'), not_null=True, minsize=(130, 130), maxsize=(3000, 3000)), filename='filename', filename_extensions=('jpg', 'jpeg', 'gif', 'png')) fields.modify('filename', editable=Editable.ALWAYS, computer=computer(self._filename)) fields.modify('size', editable=Editable.NEVER, computer=computer(self._size))
def _customize_fields(self, fields): fields.modify_many(( 'value', 'fee', ), fixed=True) fields.modify('fee', editable=computer(lambda row, value: value is not None), runtime_arguments=computer( lambda row, value: dict(value=pd.ival(value))), codebook='cb.InsuranceFee')
def fields(self): return ( Field('item_id'), Field('kind'), Field('spec_name', not_null=True, codebook='help.Help', value_column='spec_name'), Field('identifier', _("Identifier"), width=30, editable=Editable.NEVER), Field('content', _("Description"), width=80, height=15, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('label', _("Title"), width=30, virtual=True, editable=pp.Editable.NEVER, computer=computer(self._label)), Field('removed', _("Removed"), editable=Editable.NEVER), Field('changed', _("Changed"), editable=Editable.NEVER, computer=computer(lambda r, content: True)), )
def test_completer(self): completer = self._enumerator( ('x', ), data=(('Apple', ), ('Bananas', ), ('Basil', ), ('Bacardi', ), ('Cinamon', )), ) fields = ( pp.Field('a', type=pd.Integer(not_null=True)), pp.Field('x0', type=pd.String(enumerator=pd.FixedEnumerator(('a', 'b', 'c')))), pp.Field('x1', type=pd.String(), completer=('yes', 'no', 'maybe')), pp.Field('x2', type=pd.String(), completer=completer, runtime_filter=pp.computer(lambda r, a: pd.NE( 'x', pd.sval('Bacardi')) if a == 1 else None)), ) row = self._row(fields, new=True) assert not row.has_completer('a') assert row.has_completer('x0') assert row.has_completer('x1', static=True) assert row.has_completer('x2') assert not row.has_completer('x2', static=True) assert row.completions('a') == [] assert row.completions('x0') == ['a', 'b', 'c'] assert row.completions('x1') == ['maybe', 'no', 'yes'] assert row.completions('x1', prefix='y') == ['yes'] assert row.completions('x2') == ('Apple', 'Bananas', 'Basil', 'Bacardi', 'Cinamon') assert row.completions('x2', prefix='ba') == ('Bananas', 'Basil', 'Bacardi') row['a'] = 1 assert row.completions('x2', prefix='ba') == ('Bananas', 'Basil')
def fields(self): return ( Field('mod_id', default=nextval('cms_modules_mod_id_seq')), Field('modname', _("Title"), width=32), Field('descr', _("Description"), width=64, virtual=True, editable=NEVER, computer=computer(self._descr)), )
def fields(self): return ( Field('key_id', _("Id"), editable=Editable.NEVER), Field('name', _("Name"), not_null=True, codebook='CryptoNames', editable=Editable.NEVER), Field('uid', _("User"), not_null=True, codebook='Users', editable=Editable.ONCE), Field('new_uid', _("New user"), not_null=True, codebook='Users', type=pd.Integer, virtual=True, runtime_filter=computer(self._new_uid_filter)), Field('key', _("Key")), Field('remove', _("Action"), virtual=True, computer=computer(lambda r: _("Remove"))), Field('old_password', _("Current password"), type=pd.Password, verify=False, virtual=True), Field('new_password', _("New password"), type=pd.Password, virtual=True), Field('delete', virtual=True, computer=computer(lambda row: _("Remove"))), )
def test_permissions(self): data = pd.RestrictedMemData( [pd.ColumnSpec(c, pd.String()) for c in ('x', 'y', 'z')], access_rights=pd.AccessRights(('x', (None, pd.Permission.ALL)), ('y', (None, pd.Permission.VIEW)), ('z', ((), pd.Permission.ALL))), ) row = pp.PresentedRow(( pp.Field('x'), pp.Field('y'), pp.Field('z'), pp.Field('zz', virtual=True, computer=pp.computer(lambda r, z: '-' + z + '-')), ), data, None, new=True) assert row.permitted('x', pd.Permission.VIEW) assert row.permitted('x', True) assert row.permitted('y', pd.Permission.VIEW) assert not row.permitted('y', pd.Permission.UPDATE) assert not row.permitted('z', pd.Permission.VIEW) protected_row = row.protected() with pytest.raises(protected_row.ProtectionError): protected_row['z'].value() with pytest.raises(protected_row.ProtectionError): protected_row['zz'].value() # Cover special cases for a non-permitted field in various methods. assert row.get('z', secure=True) is None assert row.display('z') == '' assert row.enumerate('z') == [] assert not row.editable('z')
def fields(self): return ( Field('lang_id', default=nextval('cms_languages_lang_id_seq')), Field('lang', _("Kód"), width=2, column_width=6, fixed=True, filter=pp.TextFilter.ALPHANUMERIC, post_process=pp.PostProcess.LOWER), Field('name', _("Title"), virtual=True, editable=NEVER, computer=computer(lambda record, lang: self._language_name(lang))), )
def fields(self): return ( Field('ip_address', _("IP adresa"), width=12, editable=NEVER), Field('hostname', _("Hostname"), virtual=True, editable=NEVER, computer=computer(self._hostname), column_width=15, width=40), Field('user_agent', _("User agent"), column_width=25, width=80), Field('referer', _("Referer"), column_width=25, width=80) )
def fields(self): return (Field('ip_address', _("IP adresa"), width=12, editable=NEVER), Field('hostname', _("Hostname"), virtual=True, editable=NEVER, computer=computer(self._hostname), column_width=15, width=40), Field('user_agent', _("User agent"), column_width=25, width=80), Field('referer', _("Referer"), column_width=25, width=80))
def fields(self): return ( Field('key_id', _("Id"), editable=Editable.NEVER), Field('name', _("Name"), not_null=True, codebook='CryptoNames', editable=Editable.NEVER), Field('uid', _("User"), not_null=True, codebook='Users', selection_type=SelectionType.CHOICE, editable=Editable.ONCE), Field('new_uid', _("New user"), not_null=True, codebook='Users', selection_type=SelectionType.CHOICE, type=pd.Integer, virtual=True, runtime_filter=computer(self._new_uid_filter)), Field('key', _("Key")), Field('remove', _("Action"), virtual=True, computer=computer(lambda r: _("Remove"))), Field('old_password', _("Current password"), type=pd.Password, verify=False, virtual=True), Field('new_password', _("New password"), type=pd.Password, virtual=True), Field('delete', virtual=True, computer=computer(lambda row: _("Remove"))), )
def fields(self): return ( Field('filter', _("Country name filter"), default='*', descr=_("Enter a wildcard expression to filter only matching countries in the " "codebook field below.")), Field('switch', _("Show only european countries"), editable=Editable.ALWAYS, type=pd.Boolean(), default=True, descr=_("Check/uncheck this checkbox to enable/disable non-european " "countries in the codebook field below.")), Field('country', _("Country"), width=2, type=pd.String, not_null=True, maxlen=2, codebook='cb.Countries', runtime_filter=computer(self._country_filter), descr=_("Lists countries filtered by the above two fields.")), Field('group', _("Group"), enumerator=self._SystemGroups(), not_null=True, descr=_("Lists system user groups.")), Field('user', _("User"), enumerator=self._SystemGroupMembers(), not_null=True, runtime_arguments=computer(lambda record, group: dict(group=group)), descr=_("Lists users of the above selected group.")), )
def fields(self): return ( Field('rights_assignment_id', default=nextval('cms_rights_assignment_rights_assignment_id_seq')), Field('menu_item_id'), Field('role_id', _("Role"), not_null=True, codebook=self._spec_name('AllRoles', False)), Field('role_name', _("Role")), Field('system_role'), Field('mod_id'), Field('action_id', _("Action"), codebook=self._spec_name('Actions', False), not_null=True, runtime_filter=computer(self._action_filter)), Field('action_name', _("Action Name")), Field('action_description', _("Description"), width=30, editable=NEVER), )
def test_recursive_computer_validation(self): fields = ( pp.Field('a', type=pd.Integer(not_null=True), computer=pp.computer(lambda r, b: 2 * b, validate=True)), pp.Field( 'b', type=pd.Integer(not_null=True, enumerator=pd.FixedEnumerator(range(101))), runtime_filter=pp.computer(lambda r, a: lambda x: x % a == 0, validate=True)), ) # The computer for 'a' is called to compute the initial value and will lead to recursion # because it requires validation of 'b' which needs the value of 'a'... with pytest.raises(RuntimeError): self._row(fields, new=True) class Specification(pp.Specification): pass Specification.fields = fields # ViewSpec initialization should detect the cyclic dependency. with pytest.raises(AssertionError): Specification().view_spec() class Specification2(Specification): def _customize_fields(self, fields): fields.modify('b', runtime_filter=pp.computer( lambda r, a: lambda x: x % a == 0, validate=True, novalidate=('a', ))) row = self._row(Specification2().view_spec().fields(), new=True) # 'a' is None so runtime_filter will try to compute x % None (because 'a' is not validated). with pytest.raises(TypeError): row.enumerate('b')
def test_binary_computer(self): a = '\x04\x00\x00\x05\x00\x19\x00' b = '\x00\x05\x04\xa4\xbb\x10\x00' row = self._row(( pp.Field('x', type=pd.String()), pp.Field( 'data', type=pd.Binary(not_null=True), computer=pp.computer( lambda r, x: x == 'a' and a or x == 'b' and b or None)), )) assert row['data'].value() is None row['x'] = pd.sval('a') assert row['data'].value() == a assert isinstance(row['data'].value(), pd.Binary.Data) row['x'] = pd.sval('b') assert row['data'].value() == b
def fields(self): return ( Field('lang_id', default=nextval('cms_languages_lang_id_seq')), Field('lang', _("Kód"), width=2, column_width=6, fixed=True, filter=pp.TextFilter.ALPHANUMERIC, post_process=pp.PostProcess.LOWER), Field('name', _("Title"), virtual=True, editable=NEVER, computer=computer( lambda record, lang: self._language_name(lang))), )
def _customize_fields(self, fields): fields.append(Field('price2', _("Price with VAT"), virtual=True, type=pd.Float(precision=2), computer=computer(lambda r, price: price and price * 1.21))) fields.set_property('width', product_id=3, product=30, count=12, price=12, price2=12, notes=50) fields.modify('product_id', column_width=6, fixed=True) fields.modify('product', style=lambda r: (pp.Style(overstrike=True) if r['count'].value() == 0 else None)) fields.modify('count', style=lambda r: (pp.Style(foreground='#f00') if r['count'].value() <= 5 else None)) fields.modify('price', type=pd.Monetary(not_null=True)) fields.modify('since', descr=_("Date when the product was first included.")) fields.modify('notes', descr=_("Arbitrary internal notes about given product."), height=3, text_format=pp.TextFormat.LCG)
def fields(self): return ( Field('menu_id'), Field('menu_item_id'), Field('tree_order'), Field('tree_order_nsub'), Field('identifier', _("Identifier"), width=20, fixed=True, editable=ONCE, type=pd.RegexString(maxlen=32, not_null=True, regex='^[a-zA-Z][0-9a-zA-Z_-]*$'), descr=_("Identifikátor bude vystupovat jako vnější adresa stránky. " "Může být použit " "v odkazech na tuto stránku v rámci textu jiných stránek. Platný " "identifikátor může obsahovat pouze písmena bez diakritiky, číslice, " "pomlčky a podtržítka a musí začínat písmenem.")), Field('lang', _("Jazyk"), editable=ONCE, codebook=self._spec_name('Languages'), value_column='lang', not_null=True, selection_type=pp.SelectionType.CHOICE), Field('title_or_identifier', _("Title"), width=30, type=_TreeOrder()), Field('title', _("Title"), width=20, type=pd.String(maxlen=32, not_null=True), descr=_("Název položky menu - krátký a výstižný.")), Field('heading', _("Nadpis"), width=32, type=pd.String(maxlen=40), descr=_("Hlavní nadpis při zobrazení stránky. Pokud ponecháte nevyplněné, " "použije se název položky. Ten je ale někdy kvůli použití v menu příliš " "krátký, takže zde je možné určit jeho delší variantu.")), Field('description', _("Description"), width=72, descr=_("Stručný popis stránky (zobrazen v menu jako tooltip).")), Field('content', _("Content"), compact=True, height=20, width=80, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage, descr=_("Text stránky formátovaný jako LCG strukturovaný text (wiki)")), Field('mod_id', _("Modul"), type=pd.Integer(), not_null=False, codebook=self._spec_name('Modules', False), allow_codebook_insert=True, descr=_("Vyberte rozšiřující modul zobrazený uvnitř stránky. " "Ponechte prázdné pro prostou textovou stránku.")), Field('modname', _("Modul")), Field('parent', _("Parent item"), type=pd.Integer(), not_null=False, codebook=self._spec_name('MenuParents', False), value_column='menu_item_id', runtime_filter=computer(self._parent_filter), descr=_("Vyberte bezprostředně nadřízenou položku v hierarchii menu. Ponechte " "prázdné pro stránky na nejvyšší úrovni menu.")), Field('published', _("Zveřejněno"), width=6, default=True, fixed=True, descr=_("Umožňuje řídit dostupnost dané položky nezávisle pro kažou jazykovou " "verzi.")), Field('ord', _("Ordering"), width=8, editable=ALWAYS, fixed=True, descr=_("Enter a number denoting the order of the item in menu between " "pages of the same hierarchy level. Leave empty to put the item " "automatically to bottom.")), )
def test_editable_always_bool(self): row = self._row(( pp.Field('a', type=pd.String()), pp.Field('b', type=pd.Integer()), pp.Field('c', editable=pp.computer(lambda r, a, b: a or b)), )) for a, b, editable in ( (None, None, False), ('', None, False), (None, 0, False), ('', 0, False), (None, 1, True), ('', 5, True), ('x', None, True), ('y', 8, True), ): row['a'] = a row['b'] = b assert row.editable('c') is editable
def fields(self): return ( Field('rights_assignment_id', default=nextval( 'cms_rights_assignment_rights_assignment_id_seq')), Field('menu_item_id'), Field('role_id', _("Role"), not_null=True, codebook=self._spec_name('AllRoles', False)), Field('role_name', _("Role")), Field('system_role'), Field('mod_id'), Field('action_id', _("Action"), codebook=self._spec_name('Actions', False), not_null=True, runtime_filter=computer(self._action_filter)), Field('action_name', _("Action Name")), Field('action_description', _("Description"), width=30, editable=NEVER), )
def fields(self): return ( Field('help_id', # The computer is only used for help pages (with page_id) so # we don't need to care about other kinds of help_id. New # record is always a new page. computer=computer(lambda r, page_id: 'page/%d' % page_id)), Field('fullname', _("Fullname"), width=50, editable=Editable.NEVER), Field('spec_name', _("Specification Name"), width=50, editable=Editable.NEVER), Field('page_id', default=nextval('e_pytis_help_pages_page_id_seq')), Field('position'), Field('position_nsub'), Field('title', _("Title"), width=20, editable=computer(self._is_page), type=_TreeOrderLTree(tree_column_id='position', subcount_column_id='position_nsub'), ), Field('description', _("Description"), width=70, editable=computer(self._is_page),), Field('content', _("Content"), width=80, height=20, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('menu_help', _(u"Menu item description"), width=80, height=20, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('spec_description', _("Brief form description"), width=80, height=3, compact=True), Field('spec_help', _("Detailed form help"), width=80, height=20, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('parent', _("Parent item"), not_null=False, codebook='help.HelpParents', value_column='page_id', editable=computer(self._is_page), runtime_filter=computer(self._parent_filter), descr=_("Choose the directly superordinate item in menu hierarchy. Leave " "empty for pages at the top level menu.")), Field('ord', _("Ordering"), width=8, fixed=True, type=pd.Integer, maximum=999998, editable=computer(self._is_page), descr=_("Enter a number denoting the order of the item in menu between " "pages of the same hierarchy level. Leave empty to put the item " "automatically to bottom.")), Field('removed', _("Removed"), editable=Editable.NEVER), Field('changed', _("Changed"), editable=Editable.NEVER), )
def fields(self): return ( Field('help_id', # The computer is only used for help pages (, editable=pp.Editable.NEVER, editable=pp.Editable.NEVER, editable=pp.Editable.NEVER, editable=pp.Editable.NEVER, editable=Editable.NEVERwith page_id) so # we don't need to care about other kinds of help_id. New # record is always a new page. computer=computer(lambda r, page_id: 'page/%d' % page_id)), Field('fullname', _("Fullname"), width=50, editable=Editable.NEVER), Field('spec_name', _("Specification Name"), width=50, editable=Editable.NEVER), Field('page_id', default=nextval('e_pytis_help_pages_page_id_seq')), Field('position'), Field('position_nsub'), Field('title', _("Title"), width=20, editable=computer(self._is_page), type=_TreeOrderLTree(tree_column_id='position', subcount_column_id='position_nsub'), ), Field('description', _("Description"), width=70, editable=computer(self._is_page),), Field('content', _("Content"), width=80, height=20, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('menu_help', _(u"Menu item description"), width=80, height=20, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('spec_description', _("Brief form description"), width=80, height=3, compact=True), Field('spec_help', _("Detailed form help"), width=80, height=20, compact=True, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage), Field('parent', _("Parent item"), not_null=False, codebook='help.HelpParents', value_column='page_id', editable=computer(self._is_page), runtime_filter=computer(self._parent_filter), descr=_("Choose the directly superordinate item in menu hierarchy. Leave " "empty for pages at the top level menu.")), Field('ord', _("Ordering"), width=8, fixed=True, type=pd.Integer, maximum=999998, editable=computer(self._is_page), descr=_("Enter a number denoting the order of the item in menu between " "pages of the same hierarchy level. Leave empty to put the item " "automatically to bottom.")), Field('removed', _("Removed"), editable=Editable.NEVER), Field('changed', _("Changed"), editable=Editable.NEVER), )
def fields(self): return ( Field('id'), Field( 'continent', _("Continent"), width=3, codebook='Continents', selection_type=SelectionType.RADIO, not_null=True, descr=_( "Select continent to limit country selection below to " "given continent.")), Field( 'country', _("Country"), codebook='Countries', selection_type=SelectionType.CHOICE, runtime_filter=computer(self._country_filter), not_null=True, editable=computer( lambda r, continent: continent is not None), descr=_( "Select the country. Use the radio buttons above to limit the " "available options to countries of given continent.")), Field( 'filter', _("Product filter"), default='*', descr=_( "Enter a wildcard expression to filter only matching products in " "the codebook field below."), virtual=True), Field( 'marked', _("Marked products only"), type=pd.Boolean(), virtual=True, descr=_( "Check this checkbox if you want to filter the products in the list " "below to contain only marked products.")), Field('product_id', _("Product"), codebook='Products', display='product', not_null=True, runtime_filter=computer(self._product_filter), descr=_("Select the product to set the price for.")), Field('price', _("Price"), editable=Editable.NEVER, computer=CbComputer('product_id', 'price'), virtual=True), Field( 'expense', _("Expense"), editable=Editable.NEVER, computer=computer(self._expense), virtual=True, enumerator=pd.FixedEnumerator(('low', 'medium', 'high')), not_null=True, # Uncomment to test runtime editability for radio buttons: # editable=computer(lambda r, marked: not marked), display=lambda x: x.capitalize(), selection_type=SelectionType.RADIO), )
def _customize_fields(self, fields): fields.set_property('width', id=8, descr=60) fields.modify('id', default=pytis.util.nextval('binary_data_id_seq')) fields.modify('filename', editable=Editable.NEVER, computer=computer(self._filename)) fields.modify('data', filename='filename', type=BigBinary())
class FormProfiles(Specification): public = True table = 'ev_pytis_form_profiles' title = _("Profily formulářů") fields = ( Field('id', _("Identifier"), width=25, editable=Editable.NEVER), Field('title', _("Title"), width=25, editable=Editable.NEVER), Field('username', _("User"), not_null=True, codebook='statistics.FormUserList', value_column='login', editable=Editable.NEVER), Field('fullname', _("Fullname"), not_null=True, codebook='menu.ApplicationMenuM', width=80, column_width=30, editable=Editable.NEVER), Field('spec_name', _("Specification Name"), width=50, column_width=30, editable=Editable.NEVER), Field('form_name', _("Form Class"), width=50, column_width=30, editable=Editable.NEVER), Field('profile_id', _("Id profilu"), width=25, editable=Editable.NEVER), Field('pickled_filter', editable=Editable.NEVER), Field('pickled_params', editable=Editable.NEVER), Field('dump', _("Content"), width=80, height=8, editable=Editable.NEVER), Field('errors', _("Chyby"), width=80, height=8, editable=Editable.NEVER), Field('invalid', _("Neplatný"), type=pd.Boolean, virtual=True, width=1, computer=computer(lambda r, errors: errors is not None), editable=Editable.NEVER), ) cb = CodebookSpec(display='title') columns = ('title', 'profile_id', 'username', 'spec_name', 'form_name', 'invalid') layout = HGroup(('title', 'profile_id', 'username'), ('spec_name', 'form_name', 'dump', 'errors')) profiles = ( Profile('invalid-profiles', _("Neplatné profily"), filter=pd.NE('errors', pd.sval(None))), Profile('user-profiles', _("Uživatelské profily"), filter=pd.WM('profile_id', pd.WMValue(pd.String(), '_user_profile_*'))), Profile('system-profiles', _("Systémové profily"), filter=pd.NW('profile_id', pd.WMValue(pd.String(), '_user_profile_*'))), )
def _mega_row(self, new=False, row=None, singleline=False, **prefill): # TODO: This all-in-one row was historically used for all tests # but it proved to be problematic. You can not see the definition # of the fields from paticular tests so it is hard to guess the test # logic. Morover the specification is getting too complicated # and hard to maintain usable in all situations. Thus new tests should # Define their own fields directly just for their purpose. class BigString(pd.String, pd.Big): pass class SpecialEnumerator(pd.FixedEnumerator): # This class is overriden just to allow definition of runtime_filter # and runtime_arguments for the same field (which is only important # to improve test coverage) def values(self, a=None): # Accepts argument a as returned by runtime_arguments. return super(SpecialEnumerator, self).values() fields = ( pp.Field('a', type=pd.Integer(not_null=True)), pp.Field('b', type=pd.Integer(not_null=True, enumerator=SpecialEnumerator(range(101))), runtime_filter=pp.computer( lambda r, a: lambda x: x % a == 0, validate=True), runtime_arguments=pp.computer(lambda r, a: dict(a=a))), pp.Field('c', type=pd.Integer(not_null=True), default=lambda: 5), pp.Field('d', type=pd.Integer(), editable=pp.computer(lambda r, total: total > 5 if total else False), computer=pp.computer(lambda r, c: c * 2, validate=True)), pp.Field('fruit', type=pd.String(), codebook='Fruits', display='title', null_display='none'), pp.Field('fruit_code', virtual=True, computer=pp.CbComputer('fruit', 'code')), pp.Field('range', type=pd.IntegerRange(), visible=pp.computer(lambda r, a: a != 0)), pp.Field('total', type=pd.Integer(), virtual=True, editable=pp.Editable.NEVER, computer=pp.computer(lambda r, b, c: b + c, validate=True, fallback=0)), pp.Field('half_total', type=pd.Integer(), virtual=True, editable=pp.Editable.NEVER, computer=pp.computer(lambda r, total: total // 2 if total is not None else None)), pp.Field('x', type=pd.Integer(), virtual=True, default=88), pp.Field('password', type=pd.Password(), virtual=True), pp.Field('big', type=BigString(), virtual=True), pp.Field('array', type=pd.Array, inner_type=pd.String, codebook='Fruits', display='title', virtual=True), ) class Fruits(pytis.presentation.Specification): fields = ( pp.Field('id'), pp.Field('title'), pp.Field('code', type=pd.Integer()), pp.Field('tropical', type=pd.Boolean()), ) data_cls = pd.MemData data = (('apl', 'Apple', 123, False), ('ban', 'Banana', 234, True), ('str', 'Strawberry', 234, False), ('org', 'Orange', 456, True)) class TestResolver(pytis.util.Resolver): _specifications = {'Fruits': Fruits} def _get_object_by_name(self, name): try: return self._specifications[name] except KeyError: return super(TestResolver, self)._get_object_by_name(name) pytis.config.resolver = TestResolver() return self._row(fields, row=row, new=new, singleline=singleline, prefill=prefill)
def fields(self): return ( # Basic fields Field('text', _("Text"), type=pd.String(maxlen=50), width=30, editable=computer(self._editable)), Field('completion', _("Completion"), type=pd.String(maxlen=20), editable=computer(self._editable), completer=('John', 'Jane', 'Joe', 'Judith', 'Jeremy', 'Peter', 'Paul', 'Paula', 'Richard', 'Samantha', 'Samuel', 'Steve', 'Sue'), descr=_("Text entry with autocompletion. The 'completer' provides " "available completions. As opposed to enumerator (which " "also provides autocompletion values), completer has no " "effect on validation. You can define a static (like here), " "dynamic or database connected list of completions.")), Field('multiline', _("Multiline"), type=pd.String(), width=30, height=4, editable=computer(self._editable)), Field('password', _("Password"), type=pd.Password(maxlen=20, verify=True), editable=computer(self._editable), width=14, descr=_("Password field doesn't display the current value. If 'verify' " "is set to True in field's type, the field requires the value to " "be entered twice and validation makes sure both values match.")), Field('number', _("Number"), type=pd.Integer(), default=0, editable=computer(self._editable), descr=_("A simple numeric field.")), Field('slider', _("Slider"), type=pd.Integer(minimum=20, maximum=50), default=30, slider=True, width=2, editable=computer(self._editable), descr=_("A simple numeric field.")), Field('float', _("Decimal"), type=pd.Float(precision=2), default=0, editable=computer(self._editable)), Field('date', _("Date"), default=pd.Date.datetime, type=pd.Date(), editable=computer(self._editable), descr=_("Date entry field with a calendar control and a default value.")), Field('time', _("Time"), type=pytis.data.Time(), editable=computer(self._editable)), Field('datetime', _("Date and time"), type=pytis.data.DateTime(), editable=computer(self._editable)), Field('file', _("File"), type=pytis.data.Binary(), editable=computer(self._editable)), Field('image', _("Image"), type=pytis.data.Image(), editable=computer(self._editable)), Field('color', _("Color"), type=pytis.data.Color(), editable=computer(self._editable)), # Enumeration fields Field('editable', _("Check box"), type=pd.Boolean(), default=True, descr=_("Check/uncheck this checkbox to enable/disable all other " "form fields. This field demonstrates a simple boolean " "checkbox field. It also demonstrates how to make fields " "editable based on the values of some other fields (any " "more complicated logic is possible in the 'computer' of " "the 'editable' property in field specification). " "Finally it demonstrates how all field types look in the " "editable and non-editable state.")), Field('choice', _("Choice"), type=pd.String(not_null=True, maxlen=2), codebook='cb.Continents', selection_type=SelectionType.CHOICE, editable=computer(self._editable), descr=_("Simple pull-down menu selection.")), Field('radio', _("Radio box"), not_null=True, enumerator=self._Fruits, editable=computer(self._editable), selection_type=SelectionType.RADIO, descr=_("Radio box selection. The enumeration is defined statically by " "an Enumeration class.")), Field('listbox', _("List box"), not_null=True, enumerator=self._Fruits, selection_type=SelectionType.LISTBOX, editable=computer(self._editable), descr=_("List selection which becomes scrollable when the number of " "items exceeds its height.")), Field('codebook', _("Codebook"), type=pd.String(not_null=True, maxlen=2), codebook='cb.Countries', width=2, # SelectionType.CODEBOOK is actually the default when 'codebook' is set. selection_type=SelectionType.CODEBOOK, post_process=PostProcess.UPPER, editable=computer(self._editable), descr=_( "This field provides a text entry for writing down the code directly. " "It most convenient when the codebook contains a relatively large number of " "items while their codes are short and simple. Then the user most often " "knows the codes without looking them up. Right from the text entry field, " "there is a codebook invocation button, which allows the user to select the " "item from a codebook form (when he doesn't remember the code or wants to " "see the full listing of all available items for some other reason). " "Further on the right, there is a gray field called `display'. It shows " "the description of the currently selected item (eg. the full name for the " "selected code). Thus it provides immediate feedback when the user " "modifies the value. It is empty when the entered code is invalid (not " "contained in the codebook). It is updated with any change of the field " "value.")), Field('listfield', _("List Field"), type=pd.String(not_null=True, maxlen=2), codebook='cb.Countries', compact=True, height=6, selection_type=SelectionType.LIST, editable=computer(self._editable), descr=_("This field also allows direct codebook manipulations through the row " "context menu.")), # Range fields Field('intrange', _("Numeric range"), type=pytis.data.IntegerRange(), editable=computer(self._editable)), Field('daterange', _("Date range"), type=pytis.data.DateRange(), editable=computer(self._editable)), )
def _customize_fields(self, fields): fields.modify('b', runtime_filter=pp.computer( lambda r, a: lambda x: x % a == 0, validate=True, novalidate=('a', )))
def fields(self): return ( Field('menu_id'), Field('menu_item_id'), Field('tree_order'), Field('tree_order_nsub'), Field( 'identifier', _("Identifier"), width=20, fixed=True, editable=ONCE, type=pd.RegexString(maxlen=32, not_null=True, regex='^[a-zA-Z][0-9a-zA-Z_-]*$'), descr= _("Identifikátor bude vystupovat jako vnější adresa stránky. " "Může být použit " "v odkazech na tuto stránku v rámci textu jiných stránek. Platný " "identifikátor může obsahovat pouze písmena bez diakritiky, číslice, " "pomlčky a podtržítka a musí začínat písmenem.")), Field('lang', _("Jazyk"), editable=ONCE, codebook=self._spec_name('Languages'), value_column='lang', not_null=True, selection_type=pp.SelectionType.CHOICE), Field('title_or_identifier', _("Title"), width=30, type=_TreeOrder()), Field('title', _("Title"), width=20, type=pd.String(maxlen=32, not_null=True), descr=_("Název položky menu - krátký a výstižný.")), Field( 'heading', _("Nadpis"), width=32, type=pd.String(maxlen=40), descr= _("Hlavní nadpis při zobrazení stránky. Pokud ponecháte nevyplněné, " "použije se název položky. Ten je ale někdy kvůli použití v menu příliš " "krátký, takže zde je možné určit jeho delší variantu.")), Field( 'description', _("Description"), width=72, descr=_( "Stručný popis stránky (zobrazen v menu jako tooltip).")), Field( 'content', _("Content"), compact=True, height=20, width=80, text_format=pp.TextFormat.LCG, attachment_storage=self._attachment_storage, descr= _("Text stránky formátovaný jako LCG strukturovaný text (wiki)" )), Field('mod_id', _("Modul"), type=pd.Integer(), not_null=False, codebook=self._spec_name('Modules', False), allow_codebook_insert=True, descr=_( "Vyberte rozšiřující modul zobrazený uvnitř stránky. " "Ponechte prázdné pro prostou textovou stránku.")), Field('modname', _("Modul")), Field( 'parent', _("Parent item"), type=pd.Integer(), not_null=False, codebook=self._spec_name('MenuParents', False), value_column='menu_item_id', runtime_filter=computer(self._parent_filter), descr=_( "Vyberte bezprostředně nadřízenou položku v hierarchii menu. Ponechte " "prázdné pro stránky na nejvyšší úrovni menu.")), Field( 'published', _("Zveřejněno"), width=6, default=True, fixed=True, descr=_( "Umožňuje řídit dostupnost dané položky nezávisle pro kažou jazykovou " "verzi.")), Field( 'ord', _("Ordering"), width=8, editable=ALWAYS, fixed=True, descr= _("Enter a number denoting the order of the item in menu between " "pages of the same hierarchy level. Leave empty to put the item " "automatically to bottom.")), )