Thursday, May 18, 2017

Obtaining Wave Dashboard Info

For those times when you would like info about your dashboards within your Salesforce Wave application you may use the following utility. Please note that this process will run as your currently logged in user, and it will utilize your active session ID to acquire the data. It will be unable to get dashboard info for dashboards that may exist in the private folders of other users or applications you are unable to view. This will require the installation of a bookmarklet and works best using the chrome browser. It will optionally create a dataset if you can create datasets or you may download a CSV file of the data obtained.

1) Within the Chrome browser application - from the "View" menu, make sure the "Always Show Bookmarks" option is activated. 

2) Drag the following link onto the bookmarks bar

Wave dashboard info

3) Login into your instance and go to the Wave Analytics home page.

4) Click the bookmarklet and it will launch a background task. It should run in under a few seconds. If it does not complete in that time, try refreshing your window and restart from step 3.

5) Once complete you will see this popup, you may create the dataset (it will require 1 row per dashboard) - or you can create a file to download. "Ok" will create a dataset, "Cancel" will generate a link to CSV data you may download. Your server name will be different.


6) If you selected a dataset, you will have a new dataset named "Dataset_Info" typically within 15 seconds. If you selected the file you will see a link that looks like the following and clicking it will automatically trigger the file download.



7) Now you may either explore the dataset name 'Dashboard_Info' or the CSV file you downloaded.

Friday, April 28, 2017

Add Chatter into Einstein Analytics

Another request that's appeared a few times is to have chatter more accessible within Einstein Analytics. With the action framework you can include global actions based upon the Id reference of a single record. In this example we will add the Opportunity chatter feed via a custom apex page making it accessible on the Name field of an Einstein Analytics visualization. 

Here's a screenshot of what it will look like within Einstein Analytics when we are complete.




Before we begin, this assumes that you have an Opportunity dataset that includes the "Name" and "Id" fields available from the Opportunity object. And that these are based on real opportunities that exist within your instance.


Let's get started.



  1.   Activate the Developer Console

  1.   Create a new Visualforce Page -> File -> New -> Visualforce Page


  1.   Name the file "ChatterGlobalAction", or replace your name in the subsequent actions.
  2.   Save the following text for the Visualforce page
<apex:page standardController="Opportunity">
    <apex:outputPanel layout="block" style="overflow:auto;height:500px" >
      <chatter:feed entityId="{!Id}"/>
    </apex:outputPanel>
</apex:page>
  1. Go to Setup - Build -> Customize -> Opportunities -> Buttons, Links, and Actions  (Note - for other global actions you might want to considering - Build -> Create -> Global Actions -> Global Actions)
  2. Create a "New Action" 
    Action Type: Custom Visualforce
    Visualforce Page: ChatterGlobalAction
    Height: 500px
    Standard Label Type: <none>
    Label: View and Post to Chatter
    Name: View_and_Post_to_Chatter
    Description: <blank>
    Icon: <left as is>
  1. View an opportunity
  2. Activate the Edit Layout

  1. Click on Quick Actions

  1. Drag the "View and Post to Chatter" button to the quick action Publisher section


  1. Save the update
  2. NOTE (I need to update this with screenshots of editing the action framework UI for this and the next step) Create the following xmd file on your local computer and save it with a name like opp.xmd.json. This is the step that will activate the action framework within Einstein Analytics. If you look at the code below, when you invoke "visit the record", the linkTemplate specifies how to build the link to visit the record. You can activate this selection with either the "Name" or "Id" fields based on this configuration. The recordIdField indicates what to pass to the global action.
{
    "dataset": {},
    "dates": [],
    "derivedDimensions": [
    ],
    "derivedMeasures": [],
    "dimensions": [
        {
            "customActions": [],
            "field": "Name",
            "linkTemplate": "/{{row.Id}}",
            "linkTooltip": "Opportunity",
            "members": [],
            "recordDisplayFields": [],
            "recordIdField": "Id",
            "salesforceActions": []
        },
        {
            "customActions": [],
            "field": "Id",
            "linkTemplate": "/{{row.Id}}",
            "linkTooltip": "Opportunity",
            "members": [],
            "recordDisplayFields": [],
            "recordIdField": "Id",
            "salesforceActions": []
        }        
    ],
    "measures": [],
    "organizations": [],
    "showDetailsDefaultFields": []
}


  1. Upload the XMD to your opportunity dataset 


  1. Group Opportunities by Name
  2. Then you can pull up the action menu to select the new Chatter Global Action




Monday, April 10, 2017

Wave example of dynamic groups, measures and charts

I have seen the request often enough to be able to change groups, measures and chart types based on your selections. This is beyond the scope of the normal filter/faceting that is immediately available. I wanted to post an example of how to use binding to build this solution. Because SAQL was not required exploration with the faceted results is still available. 


Links to download the full set of assets are available for download, however, please keep in mind that the dataset references, etc. will prevent you from being able to drop it in directly to work within your org. And all of this needs to be built using the flex dashboard designer.

To dynamically tailor the groups you need a selector and its binding.

            "Grouping_Selector": {
                "datasets": [],
                "dimensions": [],
                "isFacet": true,
                "selectMode": "singlerequired",
                "start": [
                    [
                        "CloseDate_Year"
                    ]
                ],
                "type": "static",
                "values": [
                    {
                        "value": [
                            "CloseDate_Year"
                        ],
                        "display": "Year"
                    },
                    {
                        "value": [
                            "CloseDate_Year",
                            "CloseDate_Quarter"
                        ],
                        "display": "Year and Quarter"
                    },
                    {
                        "value": [
                            "Region"
                        ],
                        "display": "Region"
                    },
                    {
                        "value": [
                            "Region",
                            "Account_State"
                        ],
                        "display": "Region and State"
                    },
                    {
                        "value": [
                            "StageName"
                        ],
                        "display": "Stage"
                    },
                    {
                        "value": [
                            [
                                "CloseDate_Year",
                                "CloseDate_Quarter"
                            ]
                        ],
                        "display": "Year - Quarter (NOT AVAIL, USE MEASURE SOLUTION)"
                    }
                ]

            },

Then include the binding in the groups section of your dashboard json

                    "groups": "{{ flatten(column(Grouping_Selector.selection, [\"value\"])).asObject() }}",


As another option we are loading the measure selections via CSV with the column headers:

axisMode,display,measure_comp_1,measure_comp_2,visualizationType

This dataset should be added to a list selector picking the "display" value.


            "listselector_3": {

                "parameters": {

                    "compact": false,

                    "exploreLink": false,

                    "instant": true,
                    "measureField": "count",
                    "step": "Measure_Selector",
                    "title": "Measure"
                },
                "type": "listselector"
            },


Then we create a selector based on the results of what was selected for the CSV:

            "Measure_Selections": {
                "datasets": [
                    {
                        "id": "0Fbf1000000QSeXCAW",
                        "label": "measure_info",
                        "name": "measure_info1",
                        "url": "/services/data/v39.0/wave/datasets/0Fbf1000000QSeXCAW"
                    }
                ],
                "isFacet": true,
                "isGlobal": false,
                "query": {
                    "measures": [
                        [
                            "count",
                            "*"
                        ]
                    ],
                    "groups": [
                        "measure_comp_1",
                        "measure_comp_2",
                        "axisMode",
                        "visualizationType"
                    ],
                    "filters": [
                        [
                            "display",
                            [
                                "{{ cell(Measure_Selector.selection, 0 , \"display\").asObject() }}"
                            ],
                            "in"
                        ]
                    ]
                },
                "selectMode": "single",
                "type": "aggregateflex",
                "useGlobal": true,
                "visualizationParameters": {
                    "visualizationType": "hbar",
                    "options": {}
                }
            },

Then the results of this step can update the measures dynamically as well as the chart type.

                    "measures": "{{ row(Measure_Selections.result, [], [\"measure_comp_1\", \"measure_comp_2\"]).asObject() }}",


And to be able to update the visualization


                    "axisMode": "{{ valueAt(row(Measure_Selections.result, [0], [\"axisMode\"]), 0).asString() }}",
                    "visualizationType": "{{ valueAt(row(Measure_Selections.result, [0], [\"visualizationType\"]), 0).asString() }}",



Wednesday, April 5, 2017

Counting Rows in Datasets

Would you like to count your dataset rows? There is now an app for that and the article below is now rendered obsolete. I'm leaving this available because it does show how easily a dataset can be created with some calculated information.

Where do you obtain this magical app? It's here and includes adoption metrics as well: https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3A00000EJxtBUAT&modal=ask_your_admin_for_help

Now on to the legacy article:

For those times when you would like to know the row count for your existing datasets within your Salesforce Wave application you may use the following utility. Please note that this process will run as your currently logged in user. If the security predicate restricts the view of data for any dataset, then the row count will only reflect the count of rows that the currently logged in user can see. It will also be unable to count rows for datasets that may exist in the private folders of other users or applications for which the current user does not have access. This will require the installation of a bookmarklet and works best using the chrome browser.

1) Within the Chrome browser application - from the "View" menu, make sure the "Always Show Bookmarks" option is activated. 

2) Drag the following link onto the bookmarks bar


3) Login into your instance and go to the Wave Analytics home page.

4) Click the bookmarklet and it will launch a background task. Once it has the list of datasets you can access it will display a status bar in the upper right.



5) If you have datasets that have been moved, then you may see the screen turn gray with brief popups while it's progressing, you can safely do work on another tab while the task continues to run (but please be sure to leave this active tab untouched).

6) Once complete you will see this popup, you may create the dataset (it will require 1 row per dataset queried) - or you can create a file to download. Ok will create a dataset, cancel will generate a link to CSV data you may download. Your server name will be different.


7) If you selected a dataset, you will have a new dataset named "Dataset_Info" typically within 15 seconds. If selected the file you will see a link that looks like the following and clicking it will automatically trigger the file download.


8) Now you may either explore the dataset using the information provided, including a measure that specifies the count of rows observed in each dataset, along with some other relevant information (such as user created, folder, etc.)




Sunday, March 26, 2017

Derived Fields in Wave Dataflows using case statements- sample syntax to use

The following dataflow loads an existing dataset, adds derived fields thru a computeExpression node and merges with the source node, then registers the result in a new dataset. notice the combination of condition statements used and syntax.

{

 "z-1": {
    "action": "edgemart",
    "parameters": {
      "alias": "Leads5"
    }
  },
  "z-2": {
    "action": "computeExpression",
    "parameters": {
      "source": "z-1",
      "mergeWithSource": true,
      "computedFields": [
        {
          "name": "CaseStatus",
          "type": "Text",
          "label": "Case Status",
          "saqlExpression": " case  when 'Status' == \"Open\" && 'Include_Flag' == 1 then \"Open\" when 'Status' == \"Closed\"  then \"Closed\"   else \"Other\"  end"
        },
        {
          "name": "CustomOrderedTimeBucket",
          "type": "Text",
          "saqlExpression": " case  when 'Elapsed_Days' == \"NOACTVTY\" then \"0. NOACTVTY\"  when 'Elapsed_Days' == \"GT12MO\" then \"1. >12Mon\" when 'Elapsed_Days' == \"6to12MO\" then \"2. 6to12Mon\" when 'Elapsed_Days' == \"2to6MO\" then \"3. 2to6Mon\"   when 'Elapsed_Days' == \"1MO\" then \"4. 1Mon\" else \"5. Other\" end"
        },
        {
          "name": "CaseAge",
          "label": "CaseAge",
          "type": "Text",
          "saqlExpression": " case  when date('CreatedDate_Year', 'CreatedDate_Month', 'CreatedDate_Day') in [dateRange([2016,1,1], [2016,12,31])]  then \"2016 Bucket\"  else \"Older\"  end "
        }
      ]
    }
  },
  "z-3": {
    "action": "sfdcRegister",
    "parameters": {
      "alias": "finalZdataset",
      "name": "finalZdataset",
      "source": "z-2"
    }
  }
}






Thursday, March 23, 2017

How to limit rows or filter data being pulled from Salesforce Objects to Datasets (specifically on sfdcdigest node)

There are cases where you might want to limit the rows being read from a salesforce object (for example Leads) in a sfdcdigest node in the dataflow. For that, you can use the following:

1- for simple filtering on text or numeric values, use this syntax:

{
    "z-1": {
        "action": "sfdcDigest",
        "parameters": {
            "object": "Lead",
            "fields": [
             
                {
                    "name": "Id"
                },
                {
                    "name": "Division"
                },

                {
                    "name": "BillingDate"
                }
            ],
            "filterConditions" : [
            {
               "field": "Division",
               "operator": "LIKE",
               "value": "Ship01%"
            },
            {
                "field": "FiscalQuarter",
                "operator": "!=",
                "value": "2"
            }
            ]
   },
"z-2": {
      "action": "sfdcRegister",
      "parameters": {
         "alias": "MyFilteredDataset",
         "name": "MyFilteredDataset",
         "source": "z-1"
}
}


note that the operator can by =, <, >, INCLUDES, EXCLUDES, etc... for full list and details check this link:
https://resources.docs.salesforce.com/206/latest/en-us/sfdc/pdf/bi_admin_guide_data_integration_guide.pdf


2- for complex filtering (leveraging a SOQL 'Where' clause to filter the extracted sfdc object), here is an an example (filtering by CreateDate):

{
    "z-1": {
        "action": "sfdcDigest",
        "parameters": {
            "object": "Lead",
            "fields": [
             
                {
                    "name": "Id"
                },
                {
                    "name": "Division"
                },

                {
                    "name": "BillingDate"
                }
            ],
              "complexFilterConditions": "(BillingDate >= 2016-01-01T00:00:00.000Z) AND (BillingDate < 2017-01-01T00:00:00.000Z)"
        }
   },
"z-2": {
      "action": "sfdcRegister",
      "parameters": {
         "alias": "MyFilteredDataset",
         "name": "MyFilteredDataset",
         "source": "z-1"
}
}



Monday, March 13, 2017

Is there a way to show up and down arrows with colors on a widget in a wave dashboard?

Here is a cool example how @terence.wilson came up with up and down arrows for widget display:
(PS: at this point in time, it does require some saql editing)




1-first here are the buttons :  ▲  , ▼  ,  and ◄ ►   (you can copy and paste in the case statement directly - no need to lookup the shortcut keyboard keys)
2-you need to create a SAQL step to produce the desired outcome (the arrows) using a case statement: alternatively you can apply this in the dataflow using a saqlexpression:
(case when 'perc_Roll' > 0 then \"▲\" when  'perc_Roll' < 0 then \"▼\"  else \"◄ ►\" end) as 'diff_arrow')
3-utilize the outcome of the saql step in a binding example in a text widget:
(
            "text_1": {
                "parameters": {
                    "fontSize": 24,
                    "text": "{{coalesce(cell(housepurchase_Roll_7on7.result, 0, \"diff_arrow\"),\"◄ ►\" ).asString()}}",
                    "textAlignment": "left",
                    "textColor": "#9271E8"
                },
                "type": "text"
            },
)