Sisense Community logo
     
    • Community Feedback
    • Chapters
    • Events
    • Forums
      • Help and How To
      • Product Feedback Forum
      • Strategy & Use Cases
    • Blogs
    • KB Docs
      • KB Docs
      • Add-Ons & Plug-Ins
      • APIs
      • Best Practices
      • Blox
      • CDT
      • Cloud Managed Service
      • Data Models
      • Data Sources
      • Embedding Analytics
      • How-Tos & FAQs
      • Onboarding
      • PySisense
      • Security
      • Sisense Administration
      • Sisense Intelligence & AI
      • Troubleshooting
      • Widget & Dashboard Scripts
    • Support
    • Learning
      • Sisense Academy: Free Courses and Certifications
      • Official Developer Documentation
      • Official Product Documentation
      • Official Sisense Youtube Channel
      • Sisense Compose SDK Playground
      • Official Sisense Discord
    • Use Case Gallery
    •      
    Discussions
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
    •                    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   
    Discussions
    • HomeChevronRightIcon
    • All postsChevronRightIcon
    Use Case Gallery

    Created Mar 17, 2026

    8 members

    22 discussions

    Find inspiration for your visualizations, or show off your best work and tell us how you did it!

    Use Case Gallery
                       
                                       
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Disabling navigation hover UI for viewer users in Sisense

                               

      What the Solution Does The  RemoveNavigationHoverAndMenu  plugin simplifies the Sisense navigation for viewer users by: Hiding the three-dots “more” menu in the left navigation. Hiding the dashboard metadata tooltip that appears on hover. Preventing hover-triggered UI behavior, so menus and tooltips do not activate. Leaving the default navigation fully intact for admins and authors. The plugin automatically detects the user’s base role (prism.user.baseRoleName) and applies these changes only for viewers. It uses scoped JavaScript and CSS to remove the unwanted hover interactions without modifying Sisense core files or affecting navigation performance. How it works: Viewer-only condition:  Runs only for viewer users (where prism.user.baseRoleName === "consumer"). Hover interception: Capture-phase event listeners block hover tooltip appearance  Scoped CSS:  Injects a short style block to hide hover UI elements and remove tooltip styling. Installation: Download  RemoveNavigationHoverAndMenu.zip . Extract the folder  RemoveNavigationHoverAndMenu  into your Sisense plugins directory:/opt/sisense/storage/plugins/Alternatively, upload it through  Admin > System Management > File Management  to the plugins folder. Refresh dashboards or restart Sisense to activate the plugin. Verification: Log in as a viewer user. Hover over dashboards or folders in the left navigation. Confirm the three-dots menu and metadata tooltip no longer appear. Log in as an admin and confirm the navigation behaves normally. Files included: RemoveNavigationHoverAndMenu/plugin.json RemoveNavigationHoverAndMenu/main.6.js RemoveNavigationHoverAndMenu/README.md Why It’s Useful Simplifying the Sisense interface for viewer users creates a cleaner, more focused environment that emphasizes content rather than controls. By removing hover-based menus and tooltips for viewers while preserving them for admins, this plugin improves usability without compromising functionality. This approach also supports governance and user-experience goals: Governance:  Viewers no longer see or interact with features they do not need. Consistency:  Admins and authors retain their full toolset for management tasks. Stability:  The plugin modifies only the UI layer and requires no changes to data models or access permissions. With this small enhancement, organizations can deliver a more streamlined viewing experience while maintaining full control for those managing dashboards and content. Outcome After installation, viewer users experience a simplified left navigation that shows only essential content. The three-dots menu and dashboard metadata tooltip are removed, and hover-based interactions no longer trigger any UI overlays. Admins and authors retain the complete navigation behavior, ensuring full functionality for management and editing tasks. The result is a cleaner, more predictable interface for viewers and a consistent, role appropriate experience across the Sisense environment. Hover Before Change (for viewers): Hover After Plugin (for viewers): Three Dot Menu Before Change (for viewers): Three Dot After Plugin (Is not visible, for viewers):   Side-by-Side Comparison Before and After Comparison:

      Jeremy Friedel
      Jeremy FriedelPosted 6 months ago • Last reply 1 week ago
      4
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Extending pivot widget panel limits in Sisense using user groups

                                                       

      Overview Pivot widgets in Sisense are often used to explore and visualize complex datasets with multiple dimensions and measures. In some scenarios, users need to build very large pivot tables with many rows, columns, values, or filters. However, pivot widgets enforce internal panel item limits that can restrict how many fields and dimensions can be added to each panel. While these limits are useful for protecting performance, load time, and usability in general, they can become a constraint for advanced users working with large datasets or detailed analytical models. At the same time, organizations may want to apply different limits for different groups of users rather than increasing limits globally for everyone. This use case describes a Sisense plugin that automatically increases pivot panel limits as widgets load, with support for both a global default and optional overrides based on user group membership. The challenge By default, pivot widgets in Sisense can reach panel item limits that prevent users from adding additional dimensions or measures. This can affect: Analysts building large exploratory pivot tables Power users working with wide schemas or detailed hierarchies Dashboards that rely on complex pivots with many fields Manually adjusting widget configuration is not scalable, especially when dashboards contain many pivot widgets or when widgets are opened independently outside a dashboard. In addition, organizations often want different limits for different user groups, rather than applying a single global setting. It is important to note that if a very large number of dimensions are used in an individual pivot widget, that widget may have an extended load time. What the solution does The  PivotMaxPanelItems  plugin automatically sets the panel.metadata.maxitems value for every panel in pivot-type widgets as they load. At a high level, the plugin: Applies only to pivot widgets Updates all pivot panels (rows, columns, values, filters) Works for widgets inside dashboards and for widgets opened directly Supports a configurable default limit for all users Supports optional overrides based on Sisense user group membership The plugin runs on dashboard and widget load events, ensuring that pivot panel limits are applied consistently without requiring manual changes to individual widgets. Role and group-based configuration The plugin can apply different panel limits depending on the user’s Sisense group membership. This allows organizations to: Grant higher limits to advanced users or analysts Keep more conservative limits for general users Control behavior centrally through configuration If a user belongs to multiple configured groups, the plugin applies the first matching group based on the order defined in the configuration file. If no group matches, the default limit is used. This approach provides flexibility while keeping behavior predictable and easy to manage. How it is used Configuration is handled through a simple configuration file included with the plugin. Administrators can define: A default maximum number of items per pivot panel Optional overrides for specific Sisense user groups Once configured and installed, the plugin will likely require minimal ongoing maintenance in most circumstances. It applies automatically whenever pivot widgets are initialized. The full plugin is attached as a Zip file to this article and is available to download. The code is not compressed or obfuscated, and can be modified as needed, or used as example code for similar plugins. The plugin can be installed as a standard plugin by placing the decompressed folder into the plugin folder. The plugin includes a Readme file with further information. Why it’s useful This approach allows organizations to remove artificial constraints on pivot widget design while still maintaining control over performance and usability. Key benefits include: Enabling larger and more flexible pivot tables Reducing manual widget configuration and rework Applying consistent behavior across dashboards and standalone widgets Supporting different usage patterns across user groups Centralized control through a single configuration file The solution is particularly valuable in environments where advanced users need more flexibility without changing defaults for all users. Outcome With the  PivotMaxPanelItems  plugin in place, pivot widgets can support more dimensions without manually adding widget scripts. Advanced users gain the flexibility they need, while administrators retain control over limits at the group level through simple configuration. By applying limits automatically and consistently at load time, the plugin ensures predictable behavior across dashboards and widgets, supporting scalable group and role-aware analytics and visualization in Sisense. Screenshots Without a plugin, if a panel type includes too many items, the Add Panel Button is hidden With the plugin, the Add Button does not disappear when below the new limit, as many dimensions and fields as needed can be added to the widget.

      Jeremy Friedel
      Jeremy FriedelPosted 4 months ago • Last reply 2 months ago
      3
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Views bookmarking: Use case of a financial technology company

                                                                       

      Introduction FlexTrade is a global provider of multi-asset execution and order management systems, supporting trading workflows across asset classes, venues, and strategies. Their platforms generate large volumes of highly detailed data that users rely on for day-to-day analysis and decision-making. Companies like FlexTrade operate in an environment where users need deep, flexible analysis across a wide range of dimensions: asset class, venue, strategy, region, client, trader, time, and more. Pivot tables are a natural fit for this kind of detailed, highly dimensional analysis. However, as the number of dimensions grows, teams quickly hit a trade-off: Putting all dimensions into a single widget becomes expensive to query and difficult to interpret. Creating separate widgets for every dimension (or combination of dimensions) leads to bloated dashboards, slower load times, and a poor user experience. This use case focuses on how BloX was used to solve this problem by introducing view bookmarking, a flexible way for users to switch between different slicing configurations (in this example, a set of four dimensions) within a single widget. It also highlights how BloX can be used not just for custom visualizations, but also for building small, purpose-driven mini apps directly inside a dashboard . What the solution does This solution uses  BloX to manage view bookmarks for a pivot table. Instead of permanently adding all dimensions to the widget, BloX acts as a control layer that lets users select dimensions to include in the pivot at a time. Each selected combination can be saved as a view bookmark , representing a specific slicing configuration of the same underlying pivot. With this solution, users can: Select up to four dimensions to apply to the pivot table Save the selected combination as a personal bookmark Load and reuse previously saved bookmarks Delete bookmarks that are no longer needed Up to 20 bookmarks are supported out of the box, and all bookmarks are user-specific, allowing each user to maintain their own set of preferred analytical views. The solution also includes basic validation and error handling, such as preventing empty and duplicate bookmark names. From a technical perspective, BloX dynamically updates the pivot’s metadata. From a user perspective, it feels like switching views within a single widget. This keeps the analysis flexible while the dashboard structure remains simple and performant. Why it’s useful Scales to 10+ dimensions without UI overload Multi-asset trading analysis often requires exploring many dimensions, but not all at the same time. This solution allows FlexTrade users to work with 10+ dimensions while only surfacing the few that matter for the current question, resulting in less visual noise , lower cognitive load , and faster insights . Maintains dashboard performance and keeps dashboards clean and maintainable By avoiding massive pivots with every dimension enabled or dozens of near-duplicate widgets, the solution keeps queries efficient and dashboards responsive, even as analytical depth increases. One widget with dynamic views replaces an entire grid of narrowly focused widgets, resulting in dashboards that are easier to navigate , faster to load , and easier to maintain . Attachments BloX-ViewDimensionBookmarks.dash.txt (example dashboard using the Sample ECommerce cube) BloXActionsForBookmarks.zip (BloX actions' scripts) ViewsBookmarkV2-2025-12-29.json (BloX template for the view bookmark widget, also included in the .dash file above). Note: Remove the .txt extension before importing the dashboard (.dash) file. The BloX widget also includes a script that automatically populates the dropdown menus with the available dimension names and existing bookmarks based on the widget’s metadata. Here is the script: // Dropdown classes used in the BloX code const dropdownClasses = [ "dimensionDropdown", //dropdowns for selecting the four dimensions "bookmarkDropdown" // dropdown for selecting existing bookmarks ]; const valueToDisable = "Select"; // placeholder value to disable widget.on('ready', function() { dimensions = widget.metadata.panels[0].items; dimensionTitles = dimensions .map(i => i.jaql.title); // Add each dimension title to the dimension dropdowns dimensionTitles.forEach(function(title, index) { $('.dimensionDropdown', element).append( '<option value="' + (index + 1) + '">' + title + '</option>' ); }); bookmarks = widget.metadata.panels[1].items; bookmarkTitles = bookmarks .filter(i => !i.disabled) // keep only not disabled .map(i => i.jaql.title); // extract title // Add each existing bookmark title to the bookmark dropdown bookmarkTitles.forEach(function(title) { $('#bookmarkDropdown', element).append( '<option value="' + title + '">' + title + '</option>' ); }); // Disable placeholder values from selection dropdownClasses.forEach(cls => { $(`.${cls}`).each(function () { let $select = $(this); if (!$select.is("select")) { $select = $select.find("select"); } if ($select.length === 0) return; $select.find("option").first().prop("disabled", true); }); }); });

      Tri Anthony
      Tri AnthonyPosted 4 months ago • Last reply 4 months ago
      1
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      FAQ-style chatbot with BloX: use case of AI Assistant

                                                       

      Introduction While Sisense AI features (Simply Ask and the newer Dashboard Assistant) support free-text questions, outcomes can vary depending on factors such as data model quality, business terminology, and user familiarity. In practice, this can result in inconsistent questions, ambiguous phrasing, or less predictable results, especially for less technical users or in environments with less than ideal data models. This use case focuses on how Sisense BloX was used to create a guided FAQ-style interface that triggers the AI chatbot automatically, providing a more controlled, consistent, and user-friendly experience. This solution was implemented for a financial technology company to support users with a wide range of recurring business questions related to multi-asset trading and order management. What the solution does This solution uses  BloX to create a guided AI chatbot experience. Instead of typing questions manually, users select a question from a dropdown of predefined FAQs and submit it with a button click. BloX then automatically opens the AI chat window (Simply Ask or Dashboard Assistant), populates the question, and submits it to the chatbot. Questions can be defined directly in the BloX code or sourced dynamically from a data model, which allows the team to manage and update the list of supported questions over time. From the user’s perspective, the experience feels like interacting with an FAQ. Under the hood, the AI chatbot handles the analysis and response. Why it’s useful Lower barrier to entry for AI features By guiding users through predefined, curated questions, the solution reduces ambiguity and removes the need to worry about phrasing, terminology, or syntax. This results in more consistent, predictable, and accurate answers, making AI insights accessible to a broader audience, including users with varying technical backgrounds and less mature or optimized data models. Fewer widgets and dashboards to maintain Not every user needs answers to every possible question. By centralizing common questions into a single guided AI experience, the team avoids creating and maintaining excessive widgets and dashboards for individual analysis, improving performance and reducing long-term maintenance effort. Attachments FAQswithSimplyAskOrAIAssistant.dash.txt (example dashboard using the Sample ECommerce cube) BloXActionsForAI-FAQs.zip (BloX actions' scripts) BloXTemplatesForAI-FAQs.zip (BloX templates for the FAQ widgets, also included in the .dash file above). Note: Remove the  .txt  extension before importing the dashboard (.dash) file.

      Tri Anthony
      Tri AnthonyPosted 4 months ago
      0
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Using Git to transfer datasources between Sisense servers when connections differ

                               

        Overview Many companies and organizations use Sisense Git Integration to transfer data models between Sisense servers or to promote data models from development or staging environments into production. This approach helps teams manage changes, track history, and deploy data model updates in a controlled, repeatable way. A common challenge arises when different servers or environments, such as development, staging, and production, must connect to different data sources or data warehouse connections. While the underlying schema, tables, and joins may be identical, connection credentials, hosts, or database names can be environment-specific. By default, promoting a data model through Git includes connection configuration, which can unintentionally overwrite production connections during deployment. This use case describes a practical Sisense Git Integration workflow that allows teams to promote data model changes between Sisense environments while preserving environment-specific connection configurations. Terminology To keep the workflow clear, this article uses the following terms: Development environment : The Sisense server used for building and testing data model changes. This can also be any Sisense server where data model schema changes are applied first. Staging environment : An optional intermediate Sisense server used for validation before production. Production environment : The Sisense server is used in production and by most users. This can be any server where data model schema changes are deployed only after being tested in other environments Schema changes : Updates to tables, joins, columns, and relationships within a data model. Connection configuration : Data source connection details stored in connection.json. The challenge A typical setup includes separate Sisense environments for development or staging and production, each potentially connected to a slightly different data source connection, such as differing parameters. Organizations and businesses want to: Promote schema changes such as tables, joins, and column updates from development to production. Use Sisense Git Integration to track, manage, and deploy these changes across servers. Avoid overwriting environment-specific connection details or parameters with development settings. Without a defined Git promotion process, teams often resort to manually fixing connections in production after each import. This manual step takes time, introduces the risk of errors, and becomes increasingly difficult if the number of data models or environments grows. Key insight Sisense stores connection configuration in a single file named connection.json, which is separate from other data model files tracked in Git. As long as this file remains unchanged during a Git pull or merge, each Sisense environment retains its existing data source connection configuration. Sisense Git Integration treats all data model assets as plain text JSON files, so only files that change are applied during a pull or merge operation. This behavior makes it possible to promote schema changes independently of connection settings by carefully controlling how connection.json is handled in Git. What the solution does The solution uses standard Git workflows to promote data model schema changes while excluding connection changes from deployment. At a high level, the approach: Preserves environment-specific connections on each Sisense server. Uses Git branches to separate development data model changes from production-ready data models. Ensures that only schema-related files are promoted between environments. This allows teams to safely deploy updates without risking accidental connection overwrites. Recommended Git strategy A simple and effective approach is to maintain two primary branches in the external Git repository: Development branch : Used for ongoing development and testing, typically connected to the development or staging environment. Production branch : Represents production-ready data models and contains the production connection configuration. Initial setup When a data model is first committed to Git, connection.json is included by design and cannot be excluded. For this reason, the initial commit should be made carefully so that each environment captures the correct baseline connection. In practice, this means ensuring the production branch contains the production connection configuration. Ongoing development workflow Developers make schema changes in the development environment. Before committing, any changes to connection.json are discarded or confined to the development branch. The production branch retains its own connection.json, which may differ from the development connection. During merges, the production connection configuration is never overwritten Only schema-related files are committed to the development branch. When changes are ready for promotion, they are merged or cherry-picked into the production branch, again excluding connection.json. The production environment pulls only from the production branch, ensuring that schema changes are applied while the production connection remains unchanged. On the production server, always perform Git pulls from the production branch only. The production branch contains the production connection.json, ensuring that production connections are never replaced by development settings Why this works Sisense Git Integration applies updates based on file differences. If connection.json is unchanged during a Git pull, Sisense does not modify the existing connection in the target environment. By using Git to control which files are modified in each branch (development and production), teams can cleanly separate schema evolution in a data model from connection configuration. This makes deployments predictable and avoids unintended side effects. Best practices Always verify that connection.json remains unchanged in the production branch before committing schema updates. Use external Git tools for merges or cherry-picks when finer control over files is required. Avoid editing connections in development after the initial baseline unless intentionally changing the connection configuration. Document the workflow clearly so all team members follow the same process. Limitations and notes connection.json cannot be excluded from the initial commit of a data model. Each environment must already have the correct connection configured. In some cases, each environment may maintain its own branch containing its specific connection.json. This approach applies only to data models and does not affect dashboards or other Sisense assets. Outcome By adopting this workflow, teams can safely promote data model schema changes between Sisense environments without overwriting environment-specific data source connections. Production connections remain stable, manual post-import fixes are eliminated, and Git-based promotion becomes a reliable and repeatable process across environments. This approach strengthens governance, reduces deployment risk, and allows teams to scale their Sisense development workflows with confidence. Screenshots: Connection.json in Sisense Git UI Diagram of Example Git Flow Development Environment (Sisense Server) | | 1. Schema changes | - tables | - joins | - columns | (connection.json may differ) | v Development Branch (Git Repository) | | 2. Commit schema files only | - Exclude or discard connection.json changes | v Production Branch (Git Repository) | | 3. Merge or cherry-pick | - Schema changes only | - connection.json unchanged | v Production Environment (Sisense Server) | | 4. Git pull | - Schema updated | - Production connection preserved Discard all unintended changes to connection.json When in the Production Environment, only pull from the Production Git Branch, with the Production connection.json

      Jeremy Friedel
      Jeremy FriedelPosted 4 months ago
      0
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Tracking ElastiCube size over time

                                               

      What the solution does ✏️ This solution leverages Custom Code Notebooks and the Sisense REST API to capture ElastiCube size on a desired interval, making the data available for historical analysis.  This analysis can be performed via Dashboards, Pulse, or any other method that benefits from a basic flat file structure. Why it’s useful ✅ As mentioned in the Introduction, ElastiCube size is important because it directly impacts performance, hardware resource consumption, build times, and scalability. Efficiently managing cube size is key to maintaining a fast and stable analytics environment. There may also be licensing considerations, requiring the deployment to remain below a sizing threshold.  However, it can be challenging to monitor this data on a historical basis for purposes of trending, forecasting, or capturing anomalies.  This solution aims to remove that challenge and provide your team with this data in an easy-to-use format. 🔨How it's achieved Create a new ElastiCube and add a Custom Code table. Import the attached Notebook file, getElasticubeSize.ipynb (inside .zip) -- the raw code can also be found below Infer the schema from the Notebook Ensure LastBuildDate and SnapshotDate_UTC are set to DateTime data type “Apply” the schema changes Save the Custom Code table and rename it as desired # Test Cell # When the notebook is executed by the Build process, this cell is ignored. # See the `Test Cell` section below for further details. additional_parameters = '''{}''' from init_sisense import sisense_conn import os import json import requests import pandas as pd from datetime import datetime # Construct the Sisense server base URL server = ( "http://" + os.environ["API_GATEWAY_EXTERNAL_SERVICE_HOST"] + ":" + os.environ["API_GATEWAY_EXTERNAL_SERVICE_PORT"] ) required_columns = [ "ElasticubeName", "TenantId", "SizeInMb", "SizeInGb", "LastBuildDate", "SnapshotDate_UTC", ] def get_elasticube_size(server): """Retrieve Elasticube size and metadata from Sisense API with error handling.""" endpoint = "/api/v1/elasticubes/servers/next" # API call try: response = sisense_conn.call_api_custom("GET", server, endpoint, payload=None) response.raise_for_status() response_json = response.json() except Exception as e: print(f"API error: {e}") return pd.DataFrame(columns=required_columns) # Validate JSON list structure if not isinstance(response_json, list): print(f"Unexpected response format: {type(response_json)}") return pd.DataFrame(columns=required_columns) # Compute snapshot timestamp once for all rows snapshot_timestamp = datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ") ec_size_data = [] for item in response_json: size_mb = item.get("sizeInMb", 0) size_gb = (size_mb or 0) / 1024 ec_size_data.append( { "ElasticubeName": item.get("title") or pd.NA, "TenantId": item.get("tenantId"), "SizeInMb": size_mb, "SizeInGb": size_gb, "LastBuildDate": item.get("lastBuildUtc"), "SnapshotDate_UTC": snapshot_timestamp, } ) df = pd.DataFrame(ec_size_data) # Convert LastBuildDate if not df.empty: df["LastBuildDate"] = pd.to_datetime( df["LastBuildDate"], errors="coerce", utc=True ) # Format to ISO 8601 df["LastBuildDate"] = df["LastBuildDate"].dt.strftime("%Y-%m-%dT%H:%M:%SZ") # Ensure correct column order return df[required_columns] # === Main Execution === df_result = get_elasticube_size(server) print(df_result.head()) # === Write DataFrame to CSV === output_folder = "/opt/sisense/storage/notebooks/ec_size" os.makedirs(output_folder, exist_ok=True) ts = datetime.utcnow().strftime("%Y%m%d_%H%M%S") file_name = f"elasticube_sizes_{ts}.csv" output_path = os.path.join(output_folder, file_name) df_result.to_csv(output_path, index=False) print(f"CSV written: {output_path}") print("Program completed successfully.") This Custom Code hits the Sisense REST API to capture ElastiCube size, along with the capture date (for historical trending purposes).  It only serves as a starting point and can be freely edited.  Every time the cube is built, it will generate a csv of the data and place it under /opt/sisense/storage/notebooks/ec_size .  This location can be accessed in the Sisense UI via File Management – the ec_size folder may need to be created manually. To leverage the data in the csv files, I recommend creating a separate ElastiCube with a csv connection.  In the connection: Select “Server Location” Define the Input Folder Path: /opt/sisense/storage/notebooks/ec_size/ Ensure “Union Selected” is enabled.  This will combine all of the csv files into a singular data set. The reason I recommend creating a separate data model is so you don’t have to worry about table build order.  For example, if the Custom Code and CSV tables exist in the same model, it’s possible for the CSV table to be built before the Custom Code builds/executes, so the latest CSV file’s data would be missed until the next build (and so on).  By keeping the Custom Code and csv data models separate, you have more control over the build order by scheduling the builds sequentially. This dataset can be used as-is to build basic historical analyses, or you can enhance it by building separate custom tables that sit on top of it.  Further, you can modify the Custom Code table itself to pull whatever data is needed from the Sisense REST API , such as ElastiCube row counts and more.   NOTE : Similar data can be sourced from the Usage Analytics data model, using the SummarizeBuild table.  But the Custom Code solution provides more flexibility in what is pulled, when, and how long it is retained, without affecting anything else.  Additionally, each csv is available for independent review/modification as needed.

      akaplan
      akaplanPosted 4 months ago
      0
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Creating widgets via the Sisense API

                                       

      Overview Many companies and organizations use Sisense’s REST API to automate dashboard and widget creation. This can help integrate Sisense with existing automations, dynamically generate dashboards for different users, or manage large scale dashboard and widget deployments programmatically. Sisense provides an API endpoint for creating widgets directly on a dashboard. This allows developers to define widget configurations in JSON and publish them without using the Sisense UI for each new widget. Each widget’s behavior, layout, and data structure are defined in its metadata, mirroring the structure found inside Sisense dashboard export files (.dash files). This use case describes how to create widgets programmatically using the Sisense API and explains how to work with widget metadata and JAQL structures when doing so. What the solution does The Sisense Widget Creation API endpoint allows you to programmatically create widgets on an existing dashboard using a POST request: POST /api/v1/dashboards/{dashboardID}/widgets The request body is a widget metadata object defining the widget’s structure, data query, style, and layout. This metadata format is identical to what Sisense stores within a .dash file. The type property specifies the visualization type (for example, chart/pie, chart/bar, or pivot2), and the metadata section uses the JAQL query format to describe dimensions, measures, and filters. Developers can build widgets dynamically by: Defining visualization type and subtype (e.g., pie chart, bar chart, pivot table) Referencing the correct data source Configuring dimensions and measures within the metadata.panels structure Applying filters or plugin (such as JTD) behaviors Posting the configuration to the API endpoint API Reference: Full documentation for this endpoint and payload structure is available in the Sisense REST API documentation . Understanding widget Metadata Each Sisense widget contains a metadata object that defines the data query (via JAQL), chart style, and layout. A practical way to understand this format is to export a dashboard and inspect the widgets inside the resulting .dash file. Each widget entry includes: type – The widget type (for example, chart/pie, chart/bar, pivot2) datasource – Data model used metadata – A JAQL definition of dimensions, measures, and filters and any other dimensions, equivalent to the left hand panel in widget editor, plus filters style – Visual configuration options such as labels, legends, and axis settings options – Additional behaviors such as filter synchronization or drill options The JAQL format underpins the widget’s data model. It describes how dimensions, measures, and filters are applied when the widget queries the Elasticube or Live data source. References: Sisense JAQL documentation Metadata Item Widget Class Widget Metadata   Example payloads Pie Chart Example (Basic Configuration) { "title": "", "type": "chart/pie", "tags": [], "datasource": { "fullname": "localhost/CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "address": "LocalHost", "database": "aCopyOfECommerce", "live": false, "title": "CopyOfECommerce" }, "subtype": "pie/classic", "style": { "legend": { "enabled": false, "position": "left" }, "labels": { "enabled": true, "categories": true, "value": false, "percent": true, "decimals": false, "fontFamily": "Open Sans", "color": "red" }, "convolution": { "enabled": true, "selectedConvolutionType": "byPercentage", "minimalIndependentSlicePercentage": 3, "independentSlicesCount": 7 }, "dataLimits": { "seriesCapacity": 100000 } }, "instanceid": "5466B-AEAE-4D", "drillToDashboardConfig": { "drilledDashboardPrefix": "_drill", "drilledDashboardsFolderPrefix": "", "displayFilterPane": true, "displayDashboardsPane": true, "displayToolbarRow": true, "displayHeaderRow": true, "volatile": false, "hideDrilledDashboards": true, "hideSharedDashboardsForNonOwner": true, "drillToDashboardMenuCaption": "Jump to dashboard", "drillToDashboardRightMenuCaption": "Jump to ", "drillToDashboardNavigateType": 1, "drillToDashboardNavigateTypePivot": 2, "drillToDashboardNavigateTypeCharts": 1, "drillToDashboardNavigateTypeOthers": 3, "excludeFilterDims": [], "includeFilterDims": [], "drilledDashboardDisplayType": 2, "dashboardIds": [], "modalWindowResize": false, "showFolderNameOnMenuSelection": false, "resetDashFiltersAfterJTD": false, "sameCubeRestriction": true, "showJTDIcon": true, "sendPieChartMeasureFiltersOnClick": true, "forceZeroInsteadNull": false, "mergeTargetDashboardFilters": false, "drillToDashboardByName": false }, "realTimeRefreshing": false, "metadata": { "ignore": { "dimensions": [], "ids": [], "all": false }, "panels": [ { "name": "categories", "items": [ { "jaql": { "table": "Brand", "column": "Brand", "dim": "[Brand.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand", "merged": true, "title": "Brand" }, "instanceid": "749C8-B7DE-7A", "field": { "id": "[Brand.Brand]", "index": 0 }, "format": { "members": {} } } ] }, { "name": "values", "items": [] }, { "name": "filters", "items": [] } ] }, "options": { "dashboardFiltersMode": "select", "selector": true, "triggersDomready": true, "autoUpdateOnEveryChange": true, "drillToAnywhere": true } } This payload demonstrates a simple pie chart with one category (dimension) and a single measure, along with styling options for labels and legends. Bar Chart Example (With Filters and Multiple Dimensions) { "title": "", "type": "chart/bar", "tags": [], "datasource": { "fullname": "localhost/CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "address": "LocalHost", "database": "aCopyOfECommerce", "live": false, "title": "CopyOfECommerce" }, "subtype": "bar/classic", "style": { "legend": { "enabled": true, "position": "bottom" }, "seriesLabels": { "enabled": false, "rotation": 0, "labels": { "enabled": false, "types": { "count": false, "percentage": false, "relative": false, "totals": false }, "stacked": false, "stackedPercentage": false } }, "xAxis": { "enabled": true, "ticks": true, "labels": { "enabled": true, "rotation": 0 }, "title": { "enabled": false }, "x2Title": { "enabled": false }, "gridLines": true, "isIntervalEnabled": false }, "yAxis": { "inactive": false, "enabled": true, "ticks": true, "labels": { "enabled": true, "rotation": 0 }, "title": { "enabled": false }, "gridLines": true, "logarithmic": false, "isIntervalEnabled": true, "hideMinMax": false }, "y2Axis": { "inactive": true, "enabled": true, "ticks": true, "labels": { "enabled": true, "rotation": 0 }, "title": { "enabled": false }, "gridLines": false, "logarithmic": false, "isIntervalEnabled": true, "hideMinMax": false }, "dataLimits": { "seriesCapacity": 50, "categoriesCapacity": 100000 }, "navigator": { "enabled": true }, "narration": { "display": "above", "verbosity": "low", "labels": [ { "id": "category", "title": "Category", "singular": "Category", "plural": "Category" }, { "id": "brand", "title": "Brand", "singular": "Brand", "plural": "Brand" } ] } }, "instanceid": "FF03B-0D74-36", "drillToDashboardConfig": { "drilledDashboardPrefix": "_drill", "drilledDashboardsFolderPrefix": "", "displayFilterPane": true, "displayDashboardsPane": true, "displayToolbarRow": true, "displayHeaderRow": true, "volatile": false, "hideDrilledDashboards": true, "hideSharedDashboardsForNonOwner": true, "drillToDashboardMenuCaption": "Jump to dashboard", "drillToDashboardRightMenuCaption": "Jump to ", "drillToDashboardNavigateType": 1, "drillToDashboardNavigateTypePivot": 2, "drillToDashboardNavigateTypeCharts": 1, "drillToDashboardNavigateTypeOthers": 3, "excludeFilterDims": [], "includeFilterDims": [], "drilledDashboardDisplayType": 2, "dashboardIds": [], "modalWindowResize": false, "showFolderNameOnMenuSelection": false, "resetDashFiltersAfterJTD": false, "sameCubeRestriction": true, "showJTDIcon": true, "sendPieChartMeasureFiltersOnClick": true, "forceZeroInsteadNull": false, "mergeTargetDashboardFilters": false, "drillToDashboardByName": false }, "realTimeRefreshing": false, "metadata": { "ignore": { "dimensions": [], "ids": [], "all": false }, "panels": [ { "name": "categories", "items": [ { "jaql": { "table": "Brand_Category_with_NULLS", "column": "Category", "dim": "[Brand_Category_with_NULLS.Category]", "datatype": "text", "columnTitle": "Category", "tableTitle": "Brand_Category_with_NULLS", "merged": true, "title": "Category" }, "instanceid": "635B0-67BA-AF", "field": { "id": "[Brand_Category_with_NULLS.Category]", "index": 0 }, "format": {}, "panel": "rows" } ] }, { "name": "values", "items": [ { "jaql": { "table": "Brand_Category_with_NULLS", "column": "Brand", "dim": "[Brand_Category_with_NULLS.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand_Category_with_NULLS", "merged": true, "title": "# of unique Brand", "agg": "count" }, "instanceid": "DDE5F-0407-39", "panel": "measures", "field": { "id": "[Brand_Category_with_NULLS.Brand]", "index": 2 }, "format": { "mask": { "type": "number", "abbreviations": { "t": true, "b": true, "m": true, "k": true }, "separated": true, "decimals": "auto", "abbreviateAll": false, "isdefault": true } } } ] }, { "name": "break by", "items": [ { "jaql": { "table": "Brand_Category_with_NULLS", "column": "Category", "dim": "[Brand_Category_with_NULLS.Category]", "datatype": "text", "columnTitle": "Category", "tableTitle": "Brand_Category_with_NULLS", "merged": true, "title": "Category" }, "instanceid": "C1963-AE15-BF", "panel": "columns", "field": { "id": "[Brand_Category_with_NULLS.Category]", "index": 1 }, "format": { "members": {} } } ] }, { "name": "filters", "items": [ { "jaql": { "table": "Brand", "column": "Brand", "dim": "[Brand.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand", "merged": true, "isPrimary": false, "isDashboardFilter": false, "datasource": { "fullname": "localhost/CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "address": "LocalHost", "database": "aCopyOfECommerce", "live": false, "title": "CopyOfECommerce" }, "locale": "en-us", "title": "Brand", "collapsed": true, "filter": { "explicit": true, "multiSelection": true, "members": [ "Adbananor WorldWide " ] } }, "instanceid": "7B315-30EC-C0", "panel": "scope" } ] } ] }, "options": { "dashboardFiltersMode": "filter", "selector": true, "triggersDomready": true, "autoUpdateOnEveryChange": true, "drillToAnywhere": true, "previousScrollerLocation": { "min": null, "max": null } } }   This example includes multiple JAQL panels (categories, measures, and breakby dimensions), along with explicit filters and advanced style options. It also shows how to define navigation and plugin (such as JTD) configurations. Working with dashboard files Existing dashboard files (.dash) can serve as valuable templates for widget creation. Each widget definition inside a dashboard export can be used as a direct payload for the API. To do this: Export a dashboard from Sisense. Open the .dash file in a text editor. Locate the widget definition under the "widgets" array. Use that widget object as the body of your POST request to the API. This approach allows developers to automate dashboard replication or dynamically add widgets that follow consistent design patterns. Full Dashboard File Example { "title": "TestExport", "oid": "68f924f4b7958b87a83905c3", "desc": "", "source": null, "type": "dashboard", "style": { "palette": { "name": "Vivid", "colors": [ "#00cee6", "#9b9bd7", "#6EDA55", "#fc7570", "#fbb755", "#218A8C" ] } }, "layout": { "instanceid": "87457-1EB6-EA", "type": "columnar", "columns": [ { "width": 100, "cells": [ { "subcells": [ { "elements": [ { "minHeight": 128, "maxHeight": 2048, "minWidth": 128, "maxWidth": 2048, "height": "756px", "defaultWidth": 512, "widgetid": "68f92503b7958b87a83905c5", "autoHeight": "756px" } ], "width": 100, "stretchable": false, "pxlWidth": 647, "index": 0 } ] }, { "subcells": [ { "elements": [ { "minHeight": 96, "maxHeight": 2048, "minWidth": 128, "maxWidth": 2048, "height": 384, "defaultWidth": 512, "widgetid": "68f925dbb7958b87a83905c9" } ], "width": 100, "stretchable": false, "pxlWidth": 647, "index": 0 } ] }, { "subcells": [ { "elements": [ { "minHeight": 96, "maxHeight": 2048, "minWidth": 128, "maxWidth": 2048, "height": 384, "defaultWidth": 512, "widgetid": "68f9269db7958b87a83905cc" } ] } ] } ], "pxlWidth": 647, "index": 0 } ] }, "original": null, "dataExploration": false, "lastOpened": null, "previewLayout": [], "datasource": { "address": "LocalHost", "title": "CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "database": "aCopyOfECommerce", "fullname": "localhost/CopyOfECommerce", "live": false }, "filters": [], "editing": true, "settings": { "autoUpdateOnFiltersChange": true }, "widgets": [ { "title": "", "type": "pivot2", "subtype": "pivot2", "oid": "68f92503b7958b87a83905c5", "desc": null, "source": null, "datasource": { "address": "LocalHost", "title": "CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "database": "aCopyOfECommerce", "fullname": "localhost/CopyOfECommerce", "live": false }, "selection": null, "metadata": { "ignore": { "dimensions": [], "ids": [], "all": false }, "panels": [ { "name": "rows", "items": [ { "jaql": { "table": "Brand", "column": "Brand", "dim": "[Brand.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand", "merged": true, "title": "Brand" }, "instanceid": "D2FFC-C3EB-81", "panel": "rows", "field": { "id": "[Brand.Brand]", "index": 0 } } ] }, { "name": "values", "items": [] }, { "name": "columns", "items": [] }, { "name": "filters", "items": [] } ], "usedFormulasMapping": {} }, "style": { "scroll": false, "pageSize": 25, "automaticHeight": true, "colors": { "rows": true, "columns": false, "headers": false, "members": false, "totals": false } }, "instanceid": "012C6-D97F-BF", "realTimeRefreshing": false, "options": { "dashboardFiltersMode": "filter", "selector": false, "triggersDomready": true, "drillToAnywhere": true }, "dashboardid": "68f924f4b7958b87a83905c3", "query": { "datasource": { "fullname": "localhost/CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "address": "LocalHost", "database": "aCopyOfECommerce", "live": false, "title": "CopyOfECommerce" }, "format": "pivot", "grandTotals": { "title": "Grand Total" }, "metadata": [ { "jaql": { "table": "Brand", "column": "Brand", "dim": "[Brand.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand", "merged": true, "title": "Brand" }, "instanceid": "D2FFC-C3EB-81", "panel": "rows", "field": { "id": "[Brand.Brand]", "index": 0 }, "handlers": [] } ], "m2mThresholdFlag": 0 } }, { "title": "", "type": "chart/pie", "subtype": "pie/classic", "oid": "68f925dbb7958b87a83905c9", "desc": null, "source": null, "datasource": { "address": "LocalHost", "title": "CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "database": "aCopyOfECommerce", "fullname": "localhost/CopyOfECommerce", "live": false }, "selection": null, "metadata": { "ignore": { "dimensions": [], "ids": [], "all": false }, "panels": [ { "name": "categories", "items": [ { "jaql": { "table": "Brand", "column": "Brand", "dim": "[Brand.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand", "merged": true, "title": "Brand" }, "instanceid": "749C8-B7DE-7A", "field": { "id": "[Brand.Brand]", "index": 0 }, "format": { "members": {} } } ] }, { "name": "values", "items": [] }, { "name": "filters", "items": [] } ], "usedFormulasMapping": {} }, "style": { "legend": { "enabled": false, "position": "left" }, "labels": { "enabled": true, "categories": true, "value": false, "percent": true, "decimals": false, "fontFamily": "Open Sans", "color": "red" }, "convolution": { "enabled": true, "selectedConvolutionType": "byPercentage", "minimalIndependentSlicePercentage": 3, "independentSlicesCount": 7 }, "dataLimits": { "seriesCapacity": 100000 } }, "instanceid": "5466B-AEAE-4D", "drillToDashboardConfig": { "drilledDashboardPrefix": "_drill", "drilledDashboardsFolderPrefix": "", "displayFilterPane": true, "displayDashboardsPane": true, "displayToolbarRow": true, "displayHeaderRow": true, "volatile": false, "hideDrilledDashboards": true, "hideSharedDashboardsForNonOwner": true, "drillToDashboardMenuCaption": "Jump to dashboard", "drillToDashboardRightMenuCaption": "Jump to ", "drillToDashboardNavigateType": 1, "drillToDashboardNavigateTypePivot": 2, "drillToDashboardNavigateTypeCharts": 1, "drillToDashboardNavigateTypeOthers": 3, "excludeFilterDims": [], "includeFilterDims": [], "drilledDashboardDisplayType": 2, "dashboardIds": [], "modalWindowResize": false, "showFolderNameOnMenuSelection": false, "resetDashFiltersAfterJTD": false, "sameCubeRestriction": true, "showJTDIcon": true, "sendPieChartMeasureFiltersOnClick": true, "forceZeroInsteadNull": false, "mergeTargetDashboardFilters": false, "drillToDashboardByName": false }, "realTimeRefreshing": false, "options": { "dashboardFiltersMode": "select", "selector": true, "triggersDomready": true, "autoUpdateOnEveryChange": true, "drillToAnywhere": true }, "dashboardid": "68f924f4b7958b87a83905c3" }, { "title": "", "type": "chart/bar", "subtype": "bar/classic", "oid": "68f9269db7958b87a83905cc", "desc": null, "source": null, "datasource": { "address": "LocalHost", "title": "CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "database": "aCopyOfECommerce", "fullname": "localhost/CopyOfECommerce", "live": false }, "selection": null, "metadata": { "ignore": { "dimensions": [], "ids": [], "all": false }, "panels": [ { "name": "categories", "items": [ { "jaql": { "table": "Brand_Category_with_NULLS", "column": "Category", "dim": "[Brand_Category_with_NULLS.Category]", "datatype": "text", "columnTitle": "Category", "tableTitle": "Brand_Category_with_NULLS", "merged": true, "title": "Category" }, "instanceid": "635B0-67BA-AF", "field": { "id": "[Brand_Category_with_NULLS.Category]", "index": 0 }, "format": {}, "panel": "rows" } ] }, { "name": "values", "items": [ { "jaql": { "table": "Brand_Category_with_NULLS", "column": "Brand", "dim": "[Brand_Category_with_NULLS.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand_Category_with_NULLS", "merged": true, "title": "# of unique Brand", "agg": "count" }, "instanceid": "DDE5F-0407-39", "panel": "measures", "field": { "id": "[Brand_Category_with_NULLS.Brand]", "index": 2 }, "format": { "mask": { "type": "number", "abbreviations": { "t": true, "b": true, "m": true, "k": true }, "separated": true, "decimals": "auto", "abbreviateAll": false, "isdefault": true } } } ] }, { "name": "break by", "items": [ { "jaql": { "table": "Brand_Category_with_NULLS", "column": "Category", "dim": "[Brand_Category_with_NULLS.Category]", "datatype": "text", "columnTitle": "Category", "tableTitle": "Brand_Category_with_NULLS", "merged": true, "title": "Category" }, "instanceid": "C1963-AE15-BF", "panel": "columns", "field": { "id": "[Brand_Category_with_NULLS.Category]", "index": 1 }, "format": { "members": {} } } ] }, { "name": "filters", "items": [ { "jaql": { "table": "Brand", "column": "Brand", "dim": "[Brand.Brand]", "datatype": "text", "columnTitle": "Brand", "tableTitle": "Brand", "merged": true, "isPrimary": false, "isDashboardFilter": false, "datasource": { "fullname": "localhost/CopyOfECommerce", "id": "localhost_aCopyOfECommerce", "address": "LocalHost", "database": "aCopyOfECommerce", "live": false, "title": "CopyOfECommerce" }, "locale": "en-us", "title": "Brand", "collapsed": true, "filter": { "explicit": true, "multiSelection": true, "members": [ "Adbananor WorldWide " ] } }, "instanceid": "7B315-30EC-C0", "panel": "scope" } ] } ], "usedFormulasMapping": {} }, "style": { "legend": { "enabled": true, "position": "bottom" }, "seriesLabels": { "enabled": false, "rotation": 0, "labels": { "enabled": false, "types": { "count": false, "percentage": false, "relative": false, "totals": false }, "stacked": false, "stackedPercentage": false } }, "xAxis": { "enabled": true, "ticks": true, "labels": { "enabled": true, "rotation": 0 }, "title": { "enabled": false }, "x2Title": { "enabled": false }, "gridLines": true, "isIntervalEnabled": false }, "yAxis": { "inactive": false, "enabled": true, "ticks": true, "labels": { "enabled": true, "rotation": 0 }, "title": { "enabled": false }, "gridLines": true, "logarithmic": false, "isIntervalEnabled": true, "hideMinMax": false }, "y2Axis": { "inactive": true, "enabled": true, "ticks": true, "labels": { "enabled": true, "rotation": 0 }, "title": { "enabled": false }, "gridLines": false, "logarithmic": false, "isIntervalEnabled": true, "hideMinMax": false }, "dataLimits": { "seriesCapacity": 50, "categoriesCapacity": 100000 }, "navigator": { "enabled": true }, "narration": { "display": "above", "verbosity": "low", "labels": [ { "id": "category", "title": "Category", "singular": "Category", "plural": "Category" }, { "id": "brand", "title": "Brand", "singular": "Brand", "plural": "Brand" } ] } }, "instanceid": "FF03B-0D74-36", "drillToDashboardConfig": { "drilledDashboardPrefix": "_drill", "drilledDashboardsFolderPrefix": "", "displayFilterPane": true, "displayDashboardsPane": true, "displayToolbarRow": true, "displayHeaderRow": true, "volatile": false, "hideDrilledDashboards": true, "hideSharedDashboardsForNonOwner": true, "drillToDashboardMenuCaption": "Jump to dashboard", "drillToDashboardRightMenuCaption": "Jump to ", "drillToDashboardNavigateType": 1, "drillToDashboardNavigateTypePivot": 2, "drillToDashboardNavigateTypeCharts": 1, "drillToDashboardNavigateTypeOthers": 3, "excludeFilterDims": [], "includeFilterDims": [], "drilledDashboardDisplayType": 2, "dashboardIds": [], "modalWindowResize": false, "showFolderNameOnMenuSelection": false, "resetDashFiltersAfterJTD": false, "sameCubeRestriction": true, "showJTDIcon": true, "sendPieChartMeasureFiltersOnClick": true, "forceZeroInsteadNull": false, "mergeTargetDashboardFilters": false, "drillToDashboardByName": false }, "realTimeRefreshing": false, "options": { "dashboardFiltersMode": "filter", "selector": true, "triggersDomready": true, "autoUpdateOnEveryChange": true, "drillToAnywhere": true, "previousScrollerLocation": { "min": null, "max": null } }, "dashboardid": "68f924f4b7958b87a83905c3" } ], "hierarchies": [] } Why it’s useful Creating widgets through the Sisense API offers significant benefits for developers and system administrators: Automation: Supports large scale or dynamic creation of dashboards and widgets without manual effort. Consistency: Enables teams to apply standardized widget templates across multiple dashboards or environments. Integration: Makes it easy to connect Sisense with external systems or automated workflows. Scalability: Allows for the creation of thousands of widgets or dashboards using consistent metadata. Flexibility: By editing the JAQL and style properties, teams can tailor each widget to specific data or design requirements. Programmatic widget creation is particularly valuable for embedding Sisense into larger analytics workflows, for multi-tenant deployments, or for any scenario where dashboards must be created and maintained dynamically. Outcome Using the Widget Creation API endpoint, organizations can generate Sisense dashboards and visualizations in a consistent, repeatable way. Developers can define complex widgets, including data dimensions, filtering, and styling such as color schemes, entirely through JSON payloads, enabling uniform design and reusable frameworks across a large number of programmatically created widgets. By combining automation with Sisense’s flexible JAQL and metadata structure, teams can scale dashboard and widget deployment while maintaining full control over layout, behavior, and visual standards.  

      Jeremy Friedel
      Jeremy FriedelPosted 5 months ago
      0
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      User Landing Page for Effortless Dashboard Navigation

      📘Use Case We use Sisense to deliver data dashboards to a wide range of users across different client groups. As our dashboard library grew, we noticed that users struggled to find the information they needed.  When users logged into the platform, they were met with a wall of folders and dashboard names, without any guidance on where to start or how to find the KPIs they cared about. This led to confusion and wasted time. 💡What We Built To improve the experience, we built a User Landing Page — visually guided homepage that users see when they first log in. This page is customized by client group and includes: 1.  Folder categories clearly labeled 2. Dashboard preview/snippets 3. High-level dashboard descriptions 4. Clickable embedded URLs This guided landing page, helping users quickly recognize and access the dashboards most relevant to them. It streamlines navigation and significantly improved the user experience. 📌How We Built We built this using Sisense Blox .  Create a Blox widget. Use the ColumnSet layout to divide the page into multiple columns, each representing a dashboard. Add dashboard preview images using the Image component (e.g., /branding/Landing Page Photos/Dashboard1.png) to display the image  Wrap each image with a clickable Action.OpenUrl link that redirects users to the target dashboard. "columns": [ { "type": "Column", "width": "25%", "items": [ { "type": "Image", "url": "/branding/Landing Page Photos/Dashboard1.png", "size": "stretch", "height": "98%", "width": "100%", "style": { "border": "1px solid #ddd" }, "selectAction": { "type": "Action.OpenUrl", "url": "Dashboard url" -- Replace with your actual dashboard link } } ] }       5. Below each image, use Text Block elements to add: Dashboard titles (bold and accent-colored) Brief descriptions of each dashboard "columns": [ { "type": "Column", "width": "25%", "items": [ { "type": "TextBlock", "text": "This is the dashboard description.This is the dashboard description.", "wrap": true, "size": "small" } ] }  

      DXC
      DXCPosted 8 months ago • Last reply 6 months ago
      2
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Comprehensive framework for menu item removal in Sisense

               

      Overview Companies and organizations often wish to control which Sisense menu options are visible to different role types of users. Sisense administrators or software developers may require menu items such as  Edit Script  or  Embed Code , while business users should potentially see a more streamlined interface with only the essential options visible in the menu. Customizing these menus can be an important part of both governance and usability, ensuring that users are not viewing nonrelevent menu items. Code modifying menu's in Sisense can be present in both widget and dashboard scripts , and plugins . Custom menu items, added by plugins and scripts, can also be removed for the relevant user types. Challenge Removing menu items in Sisense requires intercepting the  beforemenu  event and filtering items out before the menu is displayed. This can be straightforward for most options, but there are several complexities to account for: Menu items can be identified by different properties (caption, command.title, description, or tooltip). Simply reassigning menuArgs.settings.items to a new array does not always work, since Sisense components and other listeners may hold references to the original array. In some cases, items may be injected into the menu after it is already open. The dashboard co-authoring  Hide  option is an example of this edge case, requiring a second filtering pass to remove consistently. The requirement was to create a robust framework that could handle these variations, while also supporting conditional filtering based on user type. Sisense exposes the property  prism.user .baseRoleName, which identifies the base role of the logged-in user. This allows developers to apply conditional logic when customizing menus. For example, users with the role "super" are administrators who may need access to advanced actions such as  Export  or  Embed Code , while users with the role "consumer" are viewers who should only see a simplified set of options. By checking this property, menu filtering can be adapted to match governance rules and usability needs for each audience. For example, administrators (prism.user.baseRoleName = "super") may retain advanced options, while viewers (prism.user.baseRoleName = "consumer") see a simplified set of actions. Solution A comprehensive framework was developed with the following key techniques: Configuration-driven filtering: Administrators can specify which object properties to inspect for matching strings, and which menu items to hide, making the script flexible and maintainable. In-place mutation of the menu array: Instead of replacing the array, the script updates it in place with splice. This ensures all references across the Sisense application reflect the updated menu. Two-phase filtering: The filter runs immediately during the beforemenu event to catch standard items, and again with setTimeout to handle menu items injected later, such as the  Hide option, which is an unusual case. Conditional visibility by user role:  By checking prism.user.baseRoleName, the script can apply different filtering rules for administrators and consumers. This ensures the right balance of power and simplicity depending on the user type. Full Implementation   /* * Hide specified Sisense menu items at both the dashboard and widget level. * All configuration is defined inside the beforemenu function for clarity */ prism.on("beforemenu", function (event, menuArgs) { // no menu items to process if (!menuArgs?.settings?.items) { return; } /** * Configuration * * fieldsToCheck: choose which object parameters to inspect on each menu item. * Set any field to false to skip it. * * itemsToHide: case-insensitive list of labels to remove from the menu. * Provide readable strings, such as "embed code" or "get url for embedding". */ const configuration = { fieldsToCheck: { caption: true, commandTitle: true, description: true, tooltip: true }, itemsToHide: [ "delete", "duplicate", "export", "edit script", "embed code", "get url for embedding", "simply ask (nlq)", "exploration paths", "widget filters indication", "widget affects dashboard filters", "dashboard settings", "hide widget header", "hide dashboard for non-owners", "featured on mobile app", "show dashboard in the featured dashboards list in the mobile app.", "navver.dashboard_menu_options.restore_dashboard", "switch to administrator view", "share the assistant", "change data source", "hide widget header", "hide" ] }; /** * Prepare a fast lookup set from the configured hide list. * Comparison is case-insensitive and ignores extra whitespace. */ const hiddenLabelSet = new Set( (configuration.itemsToHide || []) .map(formatLabelForComparison) .filter(Boolean) ); /** * Standardize a label for comparison: * - Trim whitespace * - Convert to lowercase * Returns undefined for non-strings or empty values after trimming. */ function formatLabelForComparison(rawValue) { if (typeof rawValue !== "string") { return undefined; } const trimmedLowercase = rawValue.trim().toLowerCase(); return trimmedLowercase.length ? trimmedLowercase : undefined; } /** * Gather all label values from the menu item according to the configuration. * Returns an array of standardized, non-empty strings. */ function getLabelsForMenuItem(menuItem) { const labels = []; if (configuration.fieldsToCheck.caption) { const captionLabel = formatLabelForComparison(menuItem.caption); if (captionLabel) { labels.push(captionLabel); } } if (configuration.fieldsToCheck.commandTitle) { const commandTitleLabel = formatLabelForComparison(menuItem.command?.title); if (commandTitleLabel) { labels.push(commandTitleLabel); } } if (configuration.fieldsToCheck.description) { const descriptionLabel = formatLabelForComparison(menuItem.desc); if (descriptionLabel) { labels.push(descriptionLabel); } } if (configuration.fieldsToCheck.tooltip) { const tooltipLabel = formatLabelForComparison(menuItem.tooltip); if (tooltipLabel) { labels.push(tooltipLabel); } } return labels; } /** * Decide whether a menu item should be hidden. * If any menu item parameter matches an entry in the hidden label set, the item is removed. */ function shouldHideMenuItem(menuItem) { const labels = getLabelsForMenuItem(menuItem); return labels.some(function (label) { return hiddenLabelSet.has(label); }); } /** * Recursively filter menu structures: * - Remove items that match the hide list. * - If an item has children (sub-menu), filter those sub-menus as well. */ function filterMenuItemsRecursively(menuItems) { return (menuItems || []) .filter(function (menuItem) { return !shouldHideMenuItem(menuItem); }) .map(function (menuItem) { if (Array.isArray(menuItem.items)) { menuItem.items = filterMenuItemsRecursively(menuItem.items); } return menuItem; }); } // Apply the filtering to the base array of menu items (function runFilter() { const filteredItems = filterMenuItemsRecursively(menuArgs.settings.items); // replace the contents of the original array so any existing references see the filtered result menuArgs.settings.items.splice(0, menuArgs.settings.items.length, ...filteredItems); })(); // Run the filter again after other beforemenu listeners finish setTimeout(function () { const filteredItems = filterMenuItemsRecursively(menuArgs.settings.items); menuArgs.settings.items.splice(0, menuArgs.settings.items.length, ...filteredItems); }, 0); });   Outcome With this framework, companies and organizations can reliably hide sensitive or unnecessary menu items while keeping the interface clear and consistent for end users. Because it mutates the menu array in place and applies filtering twice, it covers both standard and rare late injected menu items. By using prism.user.baseRoleName as a condition, administrator users can still view all menu items where needed, while viewer or designer level users  see only the options relevant to them. This makes the solution comprehensive, flexible, and aligned with the exact organization requirements for menu items.      

      Jeremy Friedel
      Jeremy FriedelPosted 7 months ago • Last reply 7 months ago
      1
               
    • Blog banner
      • Use Case GalleryChevronRightIcon

      Week over week analysis with custom fiscal year: Use case of a fuel and convenience retail operator

                                                       

      Introduction Week-over-week (WoW) analysis is a key part of performance tracking for fast-moving, high-traffic businesses such as convenience stores, gas stations, and car washes.  For these organizations, aligning the fiscal calendar with operational cycles rather than the standard calendar year makes reporting more meaningful. In this use case, the fiscal year begins on the closest Sunday to January 1st, ensuring each year starts with a full week. This structure simplifies weekly reporting and keeps week-to-week comparisons consistent across years, which is important for tracking trends like fuel sales, store traffic, and service volumes. While nonstandard, this setup is commonly used in practice. What the Solution Does For standard, fixed calendar or fiscal years, week-over-week analysis can be achieved using the “First Month of Fiscal Calendar” and “First Day of Week” settings, along with the PASTYEAR function. However, for dynamic fiscal years that begin on a weekday closest to January 1st, these features don’t provide a usable solution, since the start date can fall in the previous or following calendar year. The solution uses the Filtered Measure certified add-on and a custom dashboard script to handle the custom fiscal year. Two year filters are added to the dashboard: one represents the selected fiscal year (user-selectable), and the other represents the prior year for comparison (locked and optionally hidden), which is automatically set with a dashboard script. The Filtered Measure plugin applies the selected-year filter to the measure for the chosen period, while the prior-year filter applies to the measure for the corresponding period in the previous year. This approach ensures that week-over-week calculations respect the custom fiscal calendar, providing accurate comparisons across equivalent weeks. Note: In this particular implementation, the fiscal years and week numbers are pre-calculated in the database and stored as numeric columns. To create a Date dimension table in your Elasticube with fiscal years starting on the first Sunday closest to January 1st, refer to the SQL example below. Why It’s Useful This solution addresses the native functional limitation by respecting the custom fiscal calendar, ensuring weekly trends are comparable across years. As a result, teams can reliably track key metrics, such as fuel sales, store traffic, and service volumes, on a true week-by-week basis, supporting better operational planning and more informed decision-making. Attachments WeekoverWeekAnalysiswithCustomFiscalYear.dash.txt (dashboard) Sample ECommerce - Custom Fiscal Year.smodel.txt (elasticube) JS Script - Automatic Update for Second Year Filter.txt (dashboard script) SQL Query - Dim Date with Custom Fiscal Year.txt (custom table SQL query) For the script to hide the second filter, refer to this BINextLevel article: Hide dashboard filters . Note: remove the .txt extension before importing the dashboard (.dash) and the Elasticube (.smodel) files.

      Tri Anthony
      Tri AnthonyPosted 7 months ago
      0