def ready(self): """Auto load Trionyx""" enable_db_logger() models_config.auto_load_configs() from trionyx import widgets # noqa F401 self.auto_load_app_modules(['layouts', 'signals', 'forms', 'widgets']) app_menu.auto_load_model_menu() auto_register_search_models() from trionyx.views import tabs tabs.auto_generate_missing_tabs() from trionyx.trionyx.auditlog import init_auditlog init_auditlog() # Add admin menu items from trionyx.urls import model_url app_menu.add_item('dashboard', _('Dashboard'), url='/', icon='fa fa-dashboard', order=1) app_menu.add_item('admin', _('Admin'), icon='fa fa-cogs', order=9000, permission='is_superuser') app_menu.add_item('admin/users', _('Users'), url=model_url('trionyx.user', 'list'), order=9010, permission='is_superuser') app_menu.add_item('admin/groups', _('Permission groups'), url=model_url('auth.group', 'list'), order=9010, permission='is_superuser') app_menu.add_item('admin/logs', _('Logs'), url=model_url('trionyx.log', 'list'), order=9090, permission='is_superuser') # Add User renderer from trionyx.renderer import renderer renderer.register( get_user_model(), lambda value, **options: """<img src="{url}" class="avatar-sm" title="{user}">""".format( url=value.avatar.url if value.avatar else static('img/avatar.png'), user=str(value))) # Enable api route generation from trionyx.trionyx.urls import api_auto_router api_auto_router.app_ready = True
def get_url(self, view_name: str, model: Model = None, code: str = None) -> str: """Get url for model""" from trionyx.urls import model_url return model_url(model if model else self.model, view_name, code)
def get_layout(self, code, object, layout_id=None): """Get complete layout for given object""" if code not in self.layouts: raise LookupError('layout does not exist') layout_config = self.layouts.get(code) layout = layout_config['layout'](object) if isinstance(layout, Component): layout = Layout(layout) if isinstance(layout, list): layout = Layout(*layout) for update_layout in layout_config['updates']: try: update_layout(layout, object) except Exception as e: logger.error('Could not update layout for {}: {}'.format( code, str(e))) from trionyx.urls import model_url if layout_id: layout.id = layout_id layout.update_url = model_url(object, 'layout-update', code=code) layout.set_object(object) return layout
def get_add_address_url(obj, context): """Get add address url""" from trionyx.urls import model_url return model_url('trionyx_accounts.address', 'dialog-create', params={ 'account': obj.id, })
def item_sidebar(request, obj): layout = post_overview(obj) return { 'title': str(obj), 'fixed_content': 'Some fixed content 2', 'content': 'Real content <br>' * 100, 'theme': 'light', 'hover': False, 'actions': [{ 'label': 'Save', 'class': 'text-success text-bold', 'url': model_url(obj, 'dialog-edit'), 'dialog': True, 'dialog_options': {}, 'reload': True, }, { 'label': 'Warning', 'class': 'text-warning', 'url': model_url(obj, 'dialog-edit'), 'dialog': True, 'dialog_options': {}, 'reload': True, }, { 'label': 'Info', 'class': 'text-info', 'url': model_url(obj, 'dialog-edit'), 'dialog': True, 'dialog_options': {}, 'reload': True, }, { 'label': 'Delete', 'class': 'text-danger', 'url': model_url(obj, 'dialog-edit'), 'dialog': True, 'dialog_options': {}, 'reload': True, 'divider': True, }] }
def trionyx(request): """Add trionyx context data""" if not hasattr(request, 'trionyx_context'): locale, *_ = utils.get_current_locale().split('_') setattr( request, 'trionyx_context', { 'DEBUG': settings.DEBUG, 'TX_APP_NAME': tx_settings.APP_NAME, 'TX_LOGO_NAME_START': tx_settings.LOGO_NAME_START, 'TX_LOGO_NAME_END': tx_settings.LOGO_NAME_END, 'TX_LOGO_NAME_SMALL_START': tx_settings.LOGO_NAME_SMALL_START, 'TX_LOGO_NAME_SMALL_END': tx_settings.LOGO_NAME_SMALL_END, 'TX_THEME_COLOR': tx_settings.THEME_COLOR, 'tx_base64_icon': tx_base64_icon, 'tx_tasks_url': model_url(get_class('trionyx.Task'), 'list'), 'tx_version': __version__, 'tx_show_changelog': (tx_settings.SHOW_CHANGELOG_NEW_VERSION and request.user.is_authenticated and request.user.get_attribute('trionyx_last_shown_version') != utils.get_app_version()), 'trionyx_menu_items': app_menu.get_menu_items(request.user), 'trionyx_menu_collapse': request.COOKIES.get('menu.state') == 'collapsed', 'tx_custom_skin_css': 'css/skins/skin-{}.min.css'.format(tx_settings.THEME_COLOR) if settings.TX_THEME_COLOR != tx_settings.THEME_COLOR else '', 'app_version': utils.get_app_version(), 'datetime_input_format': utils.datetime_format_to_momentjs( utils.get_datetime_input_format()), 'date_input_format': utils.datetime_format_to_momentjs( utils.get_datetime_input_format(date_only=True)), 'current_locale': utils.get_current_locale(), 'summernote_language': '{}-{}'.format(locale, locale.upper()), 'summernote_language_js': 'plugins/summernote/lang/summernote-{}-{}.min.js'.format( locale, locale.upper()) if locale != 'en' and utils.get_current_language() != settings.LANGUAGE_CODE else '', **offline_context()[0] }) return getattr(request, 'trionyx_context')
def get_items(self, paginator, current_page): """Get list items for current page""" from trionyx.urls import model_url fields = self.get_model_config().get_list_fields() page = paginator.page(current_page) items = [] for item in page: items.append({ 'id': item.id, 'url': self.get_model_config().get_absolute_url(item), 'edit_url': model_url(item, 'edit'), 'delete_url': model_url(item, 'delete'), 'row_data': [ fields[field]['renderer'](item, field, no_link=True) for field in self.get_current_fields() ] }) return items
def updated(self): """Set onClick url based on object""" reload_functions = '' if self.dialog_reload_layout: reload_functions += "txUpdateLayout('{id}', '{component}');".format( id=self.layout_id, component=self.dialog_reload_layout if isinstance( self.dialog_reload_layout, str) else '') if self.dialog_reload_tab: dialog_reload_tab = self.dialog_reload_tab if isinstance( self.dialog_reload_tab, list) else [self.dialog_reload_tab] reload_functions += ''.join( [f"trionyx_reload_tab('{tab}');" for tab in dialog_reload_tab]) if self.dialog_reload_sidebar: reload_functions += "reloadSidebar();" if reload_functions: self.dialog_options['callback'] = f"""function(data, dialog){{ if (data.success) {{ dialog.close(); {reload_functions} }} }}""" if not self.on_click: from trionyx.urls import model_url url = model_url( model=self.object, view_name=self.model_url, code=self.model_code, params=self.model_params) if not self.url else self.url if not url and hasattr(self.object, 'get_absolute_url'): url = self.object.get_absolute_url() if self.dialog: self.attr[ 'onClick'] = "openDialog('{}', {}); return false;".format( url, self.format_dialog_options()) elif self.sidebar: self.attr[ 'onClick'] = "openSidebar('{}'); return false;".format(url) else: self.attr[ 'onClick'] = "window.location.href='{}'; return false;".format( url)
def model_url(model, view_name, code=None): """Short cut for generating model urls""" from trionyx.urls import model_url return model_url(model, view_name, code)
def post_overview(obj): return [ Column6( Panel( "Post info", TableDescription( 'status', 'title', 'publish_date', 'category', 'tags', 'price', { 'label': 'Update', 'value': Button( 'Update', url=model_url(obj, 'dialog-edit'), dialog=True, # dialog_options={ # 'callback': "function(){trionyx_reload_tab('general')}" # }, dialog_reload_layout='table-description', color=Colors.TEAL, ) }, { 'label': 'Sidebar', 'value': Button( 'Sidebar', model_url='sidebar', model_code='item', sidebar=True, ) }, )), Panel( 'Charts', LineChart( Post.objects.order_by('-sale_date'), 'sale_date', 'price', ), LineChart( Post.objects.annotate( day=TruncDay('publish_date')).values('day').annotate( sum_price=models.Sum('price')).values( 'day', 'sum_price').order_by('-day'), 'day', { 'label': 'Total price per day', 'field': 'sum_price', 'renderer': price_value_renderer, }, time_unit='day', ), LineChart( 'tags', 'name', 'id', ), BarChart( Post.objects.annotate( day=TruncDay('publish_date')).values('day').annotate( sum_price=models.Sum('price'), sum_id=models.Sum('id')).values( 'day', 'sum_price', 'sum_id').order_by('-day'), 'day', { 'label': 'Total price per day', 'field': 'sum_price', 'renderer': price_value_renderer, }, 'sum_id', time_unit='day', ), DoughnutChart( [['Python3', 60], ['Javascript', 30], ['Sql', 10]], 'name', 'value', ), PieChart( [['Python3', 60], ['Javascript', 30], ['Sql', 10]], 'name', 'value', ), )), Column6( Panel( 'Components', TableDescription( { 'label': 'img', 'value': Img( src= 'https://www.python.org/static/img/python-logo.png', width='200px', ), }, { 'label': 'Input', 'value': Input(name='test') }, { 'label': 'Badges', 'value': Component( Badge('status', color=Colors.THEME), Badge('status', color=Colors.LIGHT_BLUE), Badge('status', color=Colors.AQUA), Badge('status', color=Colors.GREEN), Badge('status', color=Colors.YELLOW), Badge('status', color=Colors.RED), Badge('status', color=Colors.GRAY), Badge('status', color=Colors.NAVY), Badge('status', color=Colors.TEAL), Badge('status', color=Colors.PURPLE), Badge('status', color=Colors.ORANGE), Badge('status', color=Colors.MAROON), Badge('status', color=Colors.BLACK), ) }, { 'label': 'Alerts', 'value': Component( Alert('Warning with no_margin', alert=Alert.INFO, no_margin=True), Alert('success', alert=Alert.SUCCESS), Alert('warning', alert=Alert.WARNING), Alert('danger', alert=Alert.DANGER), ) }, { 'label': 'Thumbnail', 'value': Thumbnail( 'https://www.python.org/static/img/python-logo.png', url=model_url(obj, 'dialog-edit'), dialog=True, dialog_options={ 'callback': "function(){trionyx_reload_tab('general')}" }, ) }, { 'label': 'ProgressBar', 'value': Component( ProgressBar(value=543, max_value=2400), ProgressBar(value=20, size='xs'), ProgressBar( value=78, active=True, color=Colors.GREEN), ), }, { 'label': 'Lists', 'value': UnorderedList( 'title', 'content', {'value': 'Fixed value'}, { 'label': 'Unordered sublist', 'value': UnorderedList( 'status', { 'label': 'Another unordered sublist', 'value': UnorderedList('price'), }) }, { 'label': 'Ordered sublist', 'value': OrderedList( 'status', { 'label': 'Another ordered sublist', 'value': OrderedList('price'), }) }, { 'label': 'tags', 'value': UnorderedList('name', objects='tags'), }) }, id="table-description"), )), Column12( Panel( 'Tags', Table( [ ['list object'], { 'name': 'dict object', }, Tag(name='Tag object'), ], 'name=width:120px', 'Value=value:fixed value', { 'label': 'Value', 'value': 'fixed value', 'renderer': lambda value, data_object, **options: '{}'.format( data_object), }, footer=[[ ['Subtotaal', '12,50'], ['Totaal', '12,50'], ], { 'colspan': 2, 'class': 'text-right' }, 'Price'], hover=True, ), collapse=True, )) ]
def item_sidebar(request, obj): content = Component( Panel( 'Description', Html(obj.description), ), Panel( 'Info', TableDescription( { 'field': 'item_type', 'renderer': lambda value, data_object, **options: "{} {}".format( data_object.get_type_icon(data_object.item_type), data_object.get_item_type_display(), ) }, { 'field': 'priority', 'renderer': lambda value, data_object, **options: "{} {}".format( data_object.get_priority_icon(data_object.priority), data_object.get_priority_display(), ) }, 'created_at', 'updated_at', ), ), Panel( 'Comments', Button('Add comment', model_url='dialog-create', model_params={'item': obj.id}, dialog=True, dialog_reload_sidebar=True, css_class='btn btn-flat bg-theme btn-block', object=Comment()), *[ Component( HtmlTemplate('trionyx_projects/project_comment.html', object=comment, lock_object=True), ) for comment in obj.comments.order_by('-created_at') ]), Panel( 'Worklogs', Button('Add worklog', model_url='dialog-create', model_params={'item': obj.id}, dialog=True, dialog_reload_sidebar=True, css_class='btn btn-flat bg-theme btn-block', object=WorkLog()), Table(obj.worklogs.order_by('-date', 'id'), 'date', { 'field': 'created_by', 'label': 'User', }, 'description', { 'field': 'worked', 'label': 'W', }, { 'field': 'billed', 'label': 'B', }, { 'label': 'Options', 'width': '60px', 'value': ButtonGroup( Button( '<i class="fa fa-edit"></i>', css_class='btn bg-theme btn-xs', dialog=True, model_url='dialog-edit', dialog_reload_sidebar=True, should_render=lambda comp: comp.object.created_by == get_current_request().user or get_current_request( ).user.is_superuser, ), Button( '<i class="fa fa-times"></i>', css_class='btn bg-red btn-xs', dialog=True, model_url='dialog-delete', dialog_reload_sidebar=True, should_render=lambda comp: comp.object.created_by == get_current_request().user or get_current_request( ).user.is_superuser, ), ) }), ), ) content.set_object(obj) return { 'title': f"{obj.code} - {obj.name}", 'fixed_content': TableDescription( { 'field': 'estimate', 'renderer': lambda value, **options: f"{value}h" if value else '0h', 'class': 'text-right' }, { 'field': 'total_worked', 'label': 'Logged', 'renderer': lambda value, **options: f"{value}h" if value else '0h', 'class': 'text-right' }, { 'field': 'total_billed', 'label': 'Billed', 'renderer': lambda value, **options: f"{value}h" if value else '0h', 'class': 'text-right' }, css_class='no-margin', object=obj, ).render({}, request), 'content': content.render({}, request), 'actions': [{ 'label': 'Edit', 'url': model_url(obj, 'dialog-edit'), 'dialog': True, 'dialog_options': { 'callback': """ if (data.success) { trionyx_reload_tab('general'); reloadSidebar(); dialog.close(); }; """ }, }, { 'label': 'Delete', 'class': 'text-danger', 'url': model_url(obj, 'dialog-delete'), 'dialog': True, 'dialog_options': { 'callback': """ if (data.success) { trionyx_reload_tab('general'); closeSidebar(); dialog.close(); }; """ }, 'divider': True, }] }