Controllers
The interface of Automail consists of a left menu (up to 3 levels of submenus), a top menu with a search bar + a dropdown on the right side, and the content.
All controls are set in urls.py , in the dictionary CONTROLLERS, which is used in the Automail initiation process when the server boots:
Copy CONTROLLERS = {
'platform': {
'show_searchbar': True,
'show_notifications': False,
},
'left_menu': [],
'top_right_menu': [],
'search': [],
}
Permissions
Users and groups are created from the admin page.
left_menu has its menu items as a list of dictionaries.
The following code is an example that displays the left menu above with the corresponding grid content (grid layout, selectpickers, navs and panels):
Copy 'left_menu': [
{
'label': 'General',
'children': [
{
'label': 'Development',
'icon': 'codepen',
'allowed_user_groups': [
'Development Group',
],
'allowed_users': [],
'children': [
{
'allowed_user_groups': [
'Development Group',
],
'allowed_users': [],
'label': 'Data',
'url_name': 'grid_development_report_data',
'view': controller_views.GridView,
'view_params': {
'show_selectpickers': False,
'selectpickers_columns': 3,
'selectpickers': [
selectpicker_brand_seasons_active,
selectpicker_productlines,
selectpicker_suppliers,
],
'panels': [
{
'title': 'Development Data',
'subtitle': 'Limited to 100k rows',
'tooltip': 'Please be <strong>very careful when editing the data in this table</strong>.',
'width': 12,
'full_row': True,
'url_name': 'frame_table',
'url_action_name': 'table_development',
'content': {
'view': custom_views.TableDevelopment,
'view_params': {
'model': custom_models.Development,
'order_by': 'id',
'table_settings': table_settings_development,
'field_definitions': field_definitions_development,
'filter_key_mapping': {
'brand__name': 'season__brand__name',
},
},
},
}
]
},
},
{
'label': 'Tracking',
'url_name': 'grid_development_tracking',
'view': controller_views.GridView,
'view_params': {
'show_selectpickers': False,
'selectpickers_columns': 2,
'selectpickers': [
selectpicker_brand_seasons_active,
selectpicker_suppliers,
],
'navs': [
{
'title': 'Development Metrics',
'panel_names': [
'metrics_monitoring_development',
],
},
{
'title': 'Development Tracking',
'active': True,
'panel_names': [
'tracking_monitoring_development',
],
},
],
'panels': [
{
'title': 'Development Metrics',
'width': 12,
'height': 280,
'full_row': True,
'type': 'bar',
'url_name': 'frame_chartjs',
'url_action_name': 'metrics_monitoring_development',
'content': {
'view': api_views.ChartJSAPIBar,
'view_params': {
'model': custom_models.Monitor,
'decimal_rounding': 0,
'group_by': ['season__season_short', 'request_status'],
'order_by': 'request_status',
'value_style_mapping': {
'Not Sent': {'backgroundColor': '#f1f1f1'},
'Failed Sending': {'backgroundColor': '#f3e4ec'},
'Link Sent': {'backgroundColor': '#f3efdf'},
'Link Opened': {'backgroundColor': '#dee3ee'},
'Development Report Updated': {'backgroundColor': '#deefe4'},
},
'aggregations': [
{'function': 'Count', 'value': 'id'}, # annotation
],
'include_filters_extra': {
'request_category': 'Material Development Report',
},
'filter_key_mapping': {
'brand__name': 'season__brand__name',
'supplier__name': 'monitor__supplier__name',
},
},
},
},
{
'title': 'Development Tracking',
'width': 12,
'full_row': True,
'reload_div': True,
'url_name': 'frame_regular_table',
'url_action_name': 'tracking_monitoring_development',
'content': {
'view': module_views.RegularTable,
'view_params': {
'js_imports': ['control_checkbox'],
'model': custom_models.Monitor,
'order_by': 'id',
'limit': 1000,
'is_empty': False,
'row_style_function': custom_functions.row_style_function_monitoring_status,
'field_definitions': {
'checkbox': {'header_input_type': 'checkbox', 'format': 'checkbox'},
'costing_stage': {'label': 'Costing Stage'},
'request_status': {'label': 'Status'},
'season__brand__name': {'label': 'Brand'},
'season__season_short': {'label': 'Season'},
'supplier__link': {'label': 'Supplier', 'format': 'text_link'},
'request_sent_timestamp': {'label': 'Initial Email', 'format': 'date'},
'request_reminder_sent_timestamp': {'label': 'Reminder', 'format': 'date'},
'request_deadline': {'label': 'Deadline', 'format': 'date'},
'request_last_reply_timestamp': {'label': 'Last Reply', 'format': 'datetime'},
'link_monitor_development': {'label': 'Review', 'format': 'text_link'},
'autoform_link': {'label': 'Autoform', 'format': 'external_link'},
},
'include_filters_extra': {
'request_category': 'Material Development Report',
},
'filter_key_mapping': {
'brand__name': 'season__brand__name',
},
},
},
'footer': {
'buttons': [
{
'type': 'url_action',
'label': 'Send Email',
'icon': 'envelope',
'url': reverse_lazy_panel('action_email_monitor'),
},
{
'allowed_user_groups': [],
'allowed_users': [],
'type': 'url_action',
'label': 'Next Costing Stage',
'icon': 'step-forward',
'dependency_list': 'tracking_monitoring_development',
'url': reverse_lazy_panel('action_monitor_next_costing_stage'),
},
{
'allowed_user_groups': [],
'allowed_users': [],
'type': 'url_action',
'label': 'Last Costing Stage',
'icon': 'fast-forward',
'dependency_list': 'tracking_monitoring_development',
'url': reverse_lazy_panel('action_monitor_next_costing_stage', kwargs={
'category': 'last',
}),
},
],
},
},
]
},
},
],
},
],
},
{
'label': 'Advanced',
'children': [
{
'label': 'Settings',
'icon': 'table',
'url_name': 'grid_table_settings',
'allowed_user_groups': [
'Master Group',
'Superuser',
],
'allowed_users': [],
'view': controller_views.GridView,
'view_params': {
'title': 'Settings',
'panels': [
{
'width': 12,
'full_row': True,
'url_name': 'panel_listgroup_tables',
'content': {
'view': module_views.ListGroup,
'view_params': {
'url_open_new_tab': False,
'context_items': context_items,
},
}
}
]
},
},
{
'label': 'Downloads',
'icon': 'cloud-download',
'url_name': 'grid_downloader',
'allowed_user_groups': [],
'allowed_users': [],
}
]
},
]
Grid Controllers
A grid is a container for selectpickers and panels (which can be linked to navs):
The example above is configured with the following code:
Copy {
'label': 'Tracking',
'url_name': 'grid_development_tracking',
'allowed_user_groups': [],
'allowed_users': [],
'view': controller_views.GridView,
'view_params': {
'show_selectpickers': False,
'selectpickers_columns': 2,
'selectpickers': [
selectpicker_brand_seasons_active,
selectpicker_suppliers,
],
'navs': [
{
'title': 'Development Metrics',
'panel_names': [
'metrics_monitoring_development',
],
},
{
'title': 'Development Tracking',
'active': True,
'panel_names': [
'tracking_monitoring_development',
],
},
],
'panels': [
{
'title': 'Development Metrics',
'width': 12,
'height': 280,
'full_row': True,
'type': 'bar',
'url_name': 'frame_chartjs',
'url_action_name': 'metrics_monitoring_development',
'content': {
'view': api_views.ChartJSAPIBar,
'view_params': {
'model': custom_models.Monitor,
'decimal_rounding': 0,
'group_by': ['season__season_short', 'request_status'],
'order_by': 'request_status',
'value_style_mapping': {
'Not Sent': {'backgroundColor': '#f1f1f1'},
'Failed Sending': {'backgroundColor': '#f3e4ec'},
'Link Sent': {'backgroundColor': '#f3efdf'},
'Link Opened': {'backgroundColor': '#dee3ee'},
'Development Report Updated': {'backgroundColor': '#deefe4'},
},
'aggregations': [
{'function': 'Count', 'value': 'id'}, # annotation
],
'include_filters_extra': {
'request_category': 'Material Development Report',
},
'filter_key_mapping': {
'brand__name': 'season__brand__name',
'supplier__name': 'monitor__supplier__name',
},
},
},
},
{
'title': 'Development Tracking',
'width': 12,
'full_row': True,
'reload_div': True,
'url_name': 'frame_regular_table',
'url_action_name': 'tracking_monitoring_development',
'content': {
'view': module_views.RegularTable,
'view_params': {
'js_imports': ['control_checkbox'],
'model': custom_models.Monitor,
'order_by': 'id',
'limit': 1000,
'is_empty': False,
'row_style_function': custom_functions.row_style_function_monitoring_status,
'field_definitions': {
'checkbox': {'header_input_type': 'checkbox', 'format': 'checkbox'},
'costing_stage': {'label': 'Costing Stage'},
'request_status': {'label': 'Status'},
'season__brand__name': {'label': 'Brand'},
'season__season_short': {'label': 'Season'},
'supplier__link': {'label': 'Supplier', 'format': 'text_link'},
'request_sent_timestamp': {'label': 'Initial Email', 'format': 'date'},
'request_reminder_sent_timestamp': {'label': 'Reminder', 'format': 'date'},
'request_deadline': {'label': 'Deadline', 'format': 'date'},
'request_last_reply_timestamp': {'label': 'Last Reply', 'format': 'datetime'},
'link_monitor_development': {'label': 'Review', 'format': 'text_link'},
'autoform_link': {'label': 'Autoform', 'format': 'external_link'},
},
'include_filters_extra': {
'request_category': 'Material Development Report',
},
'filter_key_mapping': {
'brand__name': 'season__brand__name',
},
},
},
'footer': {
'buttons': [
{
'type': 'url_action',
'label': 'Send Email',
'icon': 'envelope',
'url': reverse_lazy_panel('action_email_monitor'),
},
{
'allowed_user_groups': [],
'allowed_users': [],
'type': 'url_action',
'label': 'Next Costing Stage',
'icon': 'step-forward',
'dependency_list': 'tracking_monitoring_development',
'url': reverse_lazy_panel('action_monitor_next_costing_stage'),
},
{
'allowed_user_groups': [],
'allowed_users': [],
'type': 'url_action',
'label': 'Last Costing Stage',
'icon': 'fast-forward',
'dependency_list': 'tracking_monitoring_development',
'url': reverse_lazy_panel('action_monitor_next_costing_stage', kwargs={
'category': 'last',
}),
},
],
},
},
]
},
}
The grid attributes are:
url_name the URL reference of the grid
allowed_user_groups the user group permissions
allowed_users the user permissions
view the generic view controller_views.GridView , which includes all session and security-related configurations
view_params
show_selectpickers control the display of selectpickers on grid load (False means the selectpickers are collapsed)
selectpickers the filters that apply to all panels in the grid
category - filter pre-loaded dropdown list of values with the attributes:
'name ': 'supplier__name' (example value) value used for interacting with the models through queryset include filters
label ': 'Factory/Facility Name' (example value)
'live_search ': True to display a search box
'action_box ': True to display select all/deselect all buttons
'multiple ': True to allow selecting multiple values
Data source options:
'queryset ': {'model': custom_models.Supplier, 'value_field': 'id', 'label_field': 'label'} queryset config to fetch data from a model
'fixed_values ': [{'value': True, 'label': 'Yes', 'selected': False}, {'value': False, 'label': 'No', 'selected': False}] hardcoded dropdown values
category - token list of values that are loaded at the time of the search
category - datepicker mini-calendar
navs the tabs right under the selectpickers container, with the attributes:
'title ': 'Detected Issues in Questions'
'active ': True the active tab when the page is first loaded
'panel_names ': [ 'panel_suppliersurveymanagementdetectedissue_supplieranswer'] list that holds all panels to be part of the nav
panels list of panels included in the grid
Panel Controllers
Panels are loaded into a grid asynchronously through AJAX calls. Each panel is loaded independently from other panels and can be refreshed by using the refresh icon.
Each panel is configured as a dictionary. As an example, the Performance panel above has the following config:
Copy {
'title': 'Performance',
'width': 12,
'height': 450,
'full_row': True,
'type': 'combo_double_y_axis',
'url_name': 'frame_chartjs',
'url_action_name': 'panel_delivery_report_monthly_otp_autoform',
'content': {
'view': custom_views.ChartJSAPIComboOTP,
'url_params_list': [
None,
{'group_by': 'vendor__name'},
{'group_by': 'ready_d_month__year_month_name_short'},
],
'view_params': {
'label_quantity': 'Shipped Quantity (units)',
'month_field': 'ready_d_month',
'model': custom_models.Delivery,
'decimal_rounding': 4,
'limit': 15,
'order_by': '-annotation',
'aggregations': [
{'function': 'Sum', 'value': 'ready_quantity'},
{'function': 'Custom', 'value': Cast(Sum('ontime_quantity'), FloatField())/Cast(Sum('ready_quantity'), FloatField())},
],
'include_filters_extra': {
'monitor__isnull': False,
'ready_quantity__gt': 0,
}
},
},
'footer': {
'select_name': 'group_by',
'select_values': {
'self': 'Material Supplier',
'vendor__name': 'T1 Vendor',
'ready_d_month__year_month_name_short': 'Shipment Month'
}
},
}
The core panel attributes are (non-core attributes are described separately):
title visible title of the panel frame (if None, then the panel frame is not visible, only its content)
height in px defined as an integer value
width based on a divider of 12 as used by Bootstrap (full width is 12 and allowed values are 2, 3, 4, 5, 6, 7, 8, 9, 10, and 12)
row_start to make the panel start at a new row (only if full_row is False)
row_end to make the panel close an existing row (only if full_row is False and the panel is following another panel that has the row_start set to True)
type the type of panel used for JavaScript rendering (e.g. 'combo_double_y_axis', 'PieChart', 'regular_table', 'activityflow', etc. with full list below)
url_name URL of either the panel content or the panel frame used for rendering (e.g. 'frame_table', 'frame_chartjs', etc. with full list below)
url_action_name URL of the panel content (only applicable if the url_name is not referring to a panel frame)
footer the footer content of the panel
Copy {
'select_name': 'group_by',
'select_values': {
'self': 'Material Supplier',
'vendor__name': 'T1 Vendor',
'ready_d_month__year_month_name_short': 'Shipment Month'
}
}
Buttons (list of dictionaries):
Copy 'buttons': [
{
'allowed_user_groups': [
'Editor Group',
'Viewer Group',
],
'type': 'url_action',
'label': 'Queue',
'icon': 'plus',
'dependency_list': 'panel_follow_up_review_tracking',
'url': reverse_lazy_panel('action_monitor', kwargs={
'category': 'is_in_queue_True',
}),
},
{
'allowed_user_groups': [
'Editor Group',
'Viewer Group',
],
'type': 'url_action',
'label': 'Queue',
'icon': 'minus',
'dependency_list': 'panel_follow_up_review_tracking',
'url': reverse_lazy_panel('action_monitor', kwargs={
'category': 'is_in_queue_False',
}),
},
{
'allowed_user_groups': [
'Editor Group',
'Viewer Group',
],
'type': 'url_action',
'label': 'Remark',
'icon': 'floppy-disk',
'url': reverse_lazy_panel('action_monitor', kwargs={
'category': 'survey_remark',
}),
},
]
content the content of the panel
Filter content
Panel Content
The config of a panel's panel content depends on the type of content.