├── explores ├── .gitkeep └── parsed_transcripts.explore.lkml ├── views ├── .gitkeep ├── parameters.view.lkml ├── session_facts.view.lkml └── parsed_transcripts.view.lkml ├── dashboards ├── .gitkeep └── dialogflow.dashboard.lookml ├── dialogflow_block.model.lkml ├── manifest.lkml ├── marketplace.json ├── LICENSE └── README.md /explores/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboards/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dialogflow_block.model.lkml: -------------------------------------------------------------------------------- 1 | #need to update 2 | connection: "@{CONNECTION_NAME}" 3 | 4 | # include all the views 5 | include: "/explores/*" 6 | include: "/dashboards/*.dashboard" 7 | 8 | persist_with: default_datagroup 9 | 10 | datagroup: default_datagroup { 11 | # sql_trigger: SELECT MAX(id) FROM etl_log;; 12 | max_cache_age: "1 hour" 13 | } 14 | -------------------------------------------------------------------------------- /manifest.lkml: -------------------------------------------------------------------------------- 1 | project_name: "block-dialogflow" 2 | 3 | 4 | ################ Constants ################ 5 | 6 | constant: CONNECTION_NAME { 7 | value: "your_connection" 8 | export: override_optional 9 | } 10 | 11 | constant: DATASET_NAME { 12 | value: "enter_your_dataset_here" 13 | export: override_optional 14 | } 15 | 16 | constant: TABLE_PARTITION { 17 | value: "" 18 | export: override_optional 19 | } 20 | -------------------------------------------------------------------------------- /views/parameters.view.lkml: -------------------------------------------------------------------------------- 1 | view: parameters { 2 | dimension: key { 3 | type: string 4 | sql: json_extract_scalar(parameters, '$.key') ;; 5 | } 6 | 7 | dimension: value { 8 | type: string 9 | sql: json_extract_scalar(parameters, '$.value.string_value') ;; 10 | } 11 | 12 | parameter: parameter_selector { 13 | type: string 14 | suggest_dimension: parameters.key 15 | } 16 | 17 | dimension: dynamic_value { 18 | sql: (select ${value} from parameters where ${key} = 'covid-19') ;; 19 | } 20 | 21 | dimension: country { 22 | type: string 23 | sql: (SELECT json_extract_scalar(parameters, '$.value.string_value') from UNNEST([${TABLE}]) WHERE ${key} = 'geo-country');; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /marketplace.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Dialogflow Parser", 3 | "category_label": "Models", 4 | "branding": { 5 | "image_uri": "https://marketplace-api.looker.com/block-icons/dialogflow_icon.png", 6 | "tagline": "A turnkey solution for gaining visibility into user interaction with Dialogflow applications." 7 | }, 8 | 9 | "constants": { 10 | "CONNECTION_NAME": { 11 | "label": "Connection Name", 12 | "description": "Your Looker connection to BigQuery.", 13 | "value_constraint": "connection" 14 | }, 15 | "DATASET_NAME": { 16 | "label": "Dialogflow Dataset", 17 | "description": "The dataset that receives the Dialogflow log files from Stackdriver." 18 | }, 19 | "TABLE_PARTITION": { 20 | "label": "Table Partition", 21 | "description": "If using partitioned tables, you may enter a partition here with a leading underscore: \"_\"" 22 | } 23 | }, 24 | "models": [ 25 | { 26 | "name": "dialogflow_block", 27 | "connection_constant": "CONNECTION_NAME" 28 | } 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Google 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /views/session_facts.view.lkml: -------------------------------------------------------------------------------- 1 | #include: "/models/dialogflow.model.lkml" 2 | 3 | view: session_facts { 4 | derived_table: { 5 | explore_source: parsed_transcripts { 6 | column: max_timestamp {} 7 | column: min_timestamp {} 8 | column: session_id {} 9 | } 10 | } 11 | dimension_group: max_timestamp { 12 | type: time 13 | label: "Session End" 14 | group_label: "Session End" 15 | description: "Time when session ended" 16 | } 17 | dimension_group: min_timestamp { 18 | type: time 19 | label: "Session Start" 20 | group_label: "Session Start" 21 | description: "Time when session started" 22 | } 23 | dimension: session_duration { 24 | label: "Session Duration (Seconds)" 25 | description: "Number of seconds from beginning to end of session" 26 | type: duration_second 27 | sql_start: ${min_timestamp_raw} ;; 28 | sql_end: ${max_timestamp_raw} ;; 29 | group_label: "Duration" 30 | } 31 | 32 | dimension: session_duration_tiers { 33 | label: "Session Duration Tier (Seconds)" 34 | description: "Tiers sessions based on number of seconds from beginning to end of session" 35 | type: tier 36 | tiers: [0,10,30,120,560] 37 | sql: ${session_duration} ;; 38 | group_label: "Duration" 39 | } 40 | 41 | dimension: session_duration_minutes { 42 | label: "Session Duration (Minutes)" 43 | description: "Number of Minutes from beginning to end of session" 44 | type: duration_minute 45 | sql_start: ${min_timestamp_raw} ;; 46 | sql_end: ${max_timestamp_raw} ;; 47 | group_label: "Duration" 48 | } 49 | 50 | dimension: session_duration_tiers_minutes { 51 | label: "Session Duration Tier (Minutes)" 52 | description: "Tiers sessions based on number of minutes from beginning to end of session" 53 | type: tier 54 | tiers: [1,2,5,10] 55 | sql: ${session_duration} ;; 56 | group_label: "Duration" 57 | } 58 | 59 | measure: average_session_duration { 60 | type: average 61 | label: "Average Session Duration (Seconds)" 62 | sql: ${session_duration};; 63 | value_format_name: decimal_1 64 | description: "Average length of session in number of seconds" 65 | } 66 | 67 | measure: total_session_duration { 68 | type: sum 69 | label: "Total Session Duration (Seconds)" 70 | sql: ${session_duration};; 71 | value_format_name: decimal_1 72 | description: "Total length of sessions in number of seconds" 73 | } 74 | 75 | 76 | dimension: session_id { 77 | primary_key: yes 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /explores/parsed_transcripts.explore.lkml: -------------------------------------------------------------------------------- 1 | include: "/views/*" 2 | 3 | explore: parsed_transcripts { 4 | sql_preamble: CREATE TEMP FUNCTION proto2json(prototext STRING, arrayKeys STRING) 5 | RETURNS STRING 6 | LANGUAGE js AS """ 7 | 8 | /*TODO: maybe escape existing # in case it shows up in an unquoted key */ 9 | 10 | /* Replace all strings with opaque reference to avoid matching inside them */ 11 | var strings = [] 12 | prototext = prototext.replace( 13 | /"([^"\\\\]*(\\\\.[^"\\\\]*)*)"/g, 14 | function(match){ 15 | strings.push(match); 16 | return '#'+(strings.length-1)+' ' 17 | } 18 | ) 19 | 20 | /*Strip the leading type declaration*/ 21 | prototext = prototext.replace(/^[A-za-z0-0 _]+\\s*:/,''); 22 | /* Add a colon between object key and abject */ 23 | prototext = prototext.replace(/([a-zA-Z0-9_]+)\\s*\\{/g, function(match,m1){return m1+': {';}); 24 | /* Add quotes around keys */ 25 | prototext = prototext.replace(/([a-zA-Z0-9_]+):/g, function(match,m1){return '"'+m1+'" :';}); 26 | /* Add commas between values */ 27 | prototext = prototext.replace(/([0-9"}]|true|false)\\s*\\n\\s*"/g, function(match, m1){return m1+' ,\\n "';}); 28 | 29 | /* If array keys, take matching keys and prep them to not collapse */ 30 | if(arrayKeys){ 31 | if(arrayKeys && !arrayKeys.match(/^[A-Za-z0-9_]+(,[A-Za-z0-9_]+)*$/)){ 32 | throw "Only [A-Za-z0-9_] array keys are currently supported, delimited by commas" 33 | } 34 | arrayKeys = arrayKeys.split(',') 35 | var arrayKeyRegex = new RegExp('"('+arrayKeys.join('|')+')"','g') 36 | var counter=0 37 | prototext = prototext.replace(arrayKeyRegex,function(match,key){ 38 | counter++ 39 | return '"'+key+'#'+counter+'"' 40 | }) 41 | } 42 | 43 | /* Replace string references with their original values*/ 44 | prototext = prototext.replace( 45 | /#(\\d+) /g, 46 | function(match,m1){ 47 | return strings[parseInt(m1)] 48 | } 49 | ) 50 | var jsonish = '{'+prototext+'}' 51 | 52 | if(!arrayKeys){return jsonish} 53 | var obj 54 | try{ 55 | /* Parse jsonish, but replace all key#n entries with arrays*/ 56 | obj = JSON.parse(jsonish, function(key,objValue){ 57 | if(typeof objValue !== "object"){return objValue} 58 | var returnValue = {} 59 | var entries = Object.entries(objValue) 60 | /* Entries should already come out in lexicographical order, but if not we could sort here */ 61 | for(let [entryKey,entryVal] of entries){ 62 | let [groupKey,n] = entryKey.split('#') 63 | if(n===undefined){ 64 | returnValue[entryKey] = entryVal 65 | } 66 | else{ 67 | returnValue[groupKey] = (returnValue[groupKey]||[]).concat(entryVal) 68 | } 69 | } 70 | return returnValue 71 | }) 72 | } 73 | catch(e){return "JSON Error! "+e+"\\n"+jsonish} 74 | return JSON.stringify(obj,undefined,1) 75 | """; ;; 76 | sql_always_where: ${payload_type} = 'Dialogflow Response ' ;; 77 | join: parameters { 78 | view_label: "Custom Parameters" 79 | sql: LEFT JOIN UNNEST(${parsed_transcripts.parameters}) as parameters ;; 80 | relationship: one_to_one 81 | } 82 | join: session_facts { 83 | relationship: many_to_one 84 | sql_on: ${session_facts.session_id} = ${parsed_transcripts.session_id} ;; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **NOTE: This block requires exporting Dialogflow Log Files to BigQuery via Stackdriver. Please [review the step-by-step guide](https://github.com/GoogleCloudPlatform/dialogflow-integrations/tree/master/stacklogs-looker) and ensure the following is complete before attempting to use this block:** 2 | 1. Created a Dialogflow Agent 3 | 2. Created a BigQuery Dataset to hold Dialogflow Stackdriver Logs and Looker Persistent Derived Table 4 | 3. Created a StackDriver Sink Export to BigQuery 5 | 6 | ___ 7 | # Readme 8 | 9 | ### What does this Block do for me? 10 | 11 | **(1) Analyze Dialogflow Efficiency** - Provides visibility into Dialogflow application performance so that you can identify the frequency and type of user queries that are being resolved inefficiently and remediate those interactions accordingly. 12 | 13 | **(2) Understand User Behavior** - Provides insight into the topics that users most frequently ask questions about, the nature of how their questions are phrased, and their satisfaction with answers so that you can fine-tune your interactions to maximize customer satisfaction. 14 | 15 | ### Block Info 16 | 17 | This block is modeled on the Dialogflow schema. Each record is parsed to extract the content as well as metadata of a human interaction with the bot. For telephonic interactions, additional metadata, such as the Area Code and Trace are extracted. 18 | 19 | ### Dialogflow Raw Data Structure 20 | 21 | Human interactions with the bot are initially extracted as a payload, which can be converted into a JSON format. Each payload contains all the information about that chat. 22 | 23 | ### Dialogflow Block Structure 24 | 25 | The Dialogflow block consists of an Explore with three underlying views. 26 | 27 | **(1) Parsed Transcripts View** 28 | 29 | This view creates a Persistent Derived Table which extracts all the data about an interaction from the Dialogflow payload into a JSON object. That JSON is then parsed to extract the content, as well as characteristics, of that interaction, which form the dimensions and measures of the view. 30 | 31 | **(2) Session Facts View** 32 | 33 | The session_id associated with an interaction is part of the Dialogflow payload. In order to understand the context of the session in which an interaction occurred, a Persistent Derived Table is created that captures the characteristic of that session, such as its start and end time as well as the overall session duration. 34 | 35 | **(3) Parameters View** 36 | 37 | This view is used to define any custom variables as well as their values that are logged as part of a specific Dialogflow deployment. 38 | 39 | ### Implementation Instructions 40 | This block is installed via the Looker Marketplace. For more information about the Looker Marketplace, please visit this [link](https://docs.looker.com/data-modeling/marketplace). 41 | 42 | #### Constants #### 43 | During installation you will provide two values to populate the following constants: 44 | * Connection Name - the Looker connection with access to and permission to retrieve data from your exported dialogflow tables. 45 | * Schema - the schema name for your exported data. 46 | * Table Partition - Table Partition Name (If Applicable). 47 | 48 | #### Customization #### 49 | - This block uses Refinements to allow for modification or extension of the LookML content. For more information on using refinements to customize marketplace blocks, please see [this documentation](https://docs.looker.com/data-modeling/marketplace/customize-blocks). 50 | 51 | **Custom Variables** 52 | 53 | Using the refinements file, you'll be able to add any custom dimensions that you'd like to track with a dimension declaration. An example that extracts the custom dimension country from a parameter called 'geo-country' is shown below: 54 | 55 | ``` 56 | view +parsed_transcripts { 57 | dimension: country { 58 | type: string 59 | sql: (SELECT json_extract_scalar(parameters, '$.value.string_value') 60 | FROM UNNEST([${TABLE}]) WHERE ${key} = 'geo-country');; 61 | }} 62 | ``` 63 | -------------------------------------------------------------------------------- /views/parsed_transcripts.view.lkml: -------------------------------------------------------------------------------- 1 | view: parsed_transcripts { 2 | derived_table: { 3 | sql: 4 | SELECT 5 | textPayload as textPayload 6 | , proto2json(textPayload,"messages,fields") as payload_as_json 7 | FROM `@{DATASET_NAME}.dialogflow_agent@{TABLE_PARTITION}` 8 | ;; 9 | } 10 | 11 | dimension: id { 12 | type: string 13 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.id') ;; 14 | label: "Conversation ID" 15 | group_label: "IDs" 16 | } 17 | dimension: lang { 18 | type: string 19 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.lang') ;; 20 | label: "Language" 21 | description: "Language in which conversation took place" 22 | view_label: "Conversation Characteristics" 23 | } 24 | dimension_group: timestamp { 25 | type: time 26 | sql: cast(JSON_EXTRACT_SCALAR(${payload_as_json}, '$.timestamp') as timestamp);; 27 | group_label: "Conversation Time" 28 | label: "Conversation Time" 29 | description: "Time when conversation occurred" 30 | } 31 | dimension: session_id { 32 | type: string 33 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.session_id') ;; 34 | group_label: "IDs" 35 | } 36 | 37 | #### Result Payload #### 38 | 39 | dimension: source { 40 | type: string 41 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.source') ;; 42 | view_label: "Conversation Characteristics" 43 | description: "Source of Conversation" 44 | } 45 | dimension: score_tier { 46 | type: tier 47 | sql: ${score} ;; 48 | style: interval 49 | tiers: [0.5,0.8,1] 50 | } 51 | dimension: resolved_query { 52 | description: "User Question / Message to bot" 53 | type: string 54 | sql:JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.resolved_query') ;; 55 | label: "User Query" 56 | } 57 | dimension: score { 58 | type: number 59 | sql:CAST(JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.score') AS NUMERIC) ;; 60 | view_label: "Conversation Characteristics" 61 | description: "Score given to Conversation" 62 | } 63 | 64 | #### Metadata Payload 65 | 66 | dimension: webhook_for_slot_filling_used { 67 | type: yesno 68 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.webhook_for_slot_filling_used') = 'true' ;; 69 | view_label: "Conversation Characteristics" 70 | } 71 | dimension: is_fallback_intent { 72 | type: yesno 73 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.is_fallback_intent') = 'true' ;; 74 | description: "Whether the intent of the call was a fallback" 75 | view_label: "Conversation Characteristics" 76 | } 77 | dimension: intent_id { 78 | type: string 79 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.intent_id') ;; 80 | group_label: "Intent" 81 | hidden: yes 82 | } 83 | dimension: web_hook_response_time { 84 | type: number 85 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.webhook_response_time') ;; 86 | view_label: "Conversation Characteristics" 87 | } 88 | dimension: response_time_tiers { 89 | 90 | } 91 | dimension: intent_name { 92 | type: string 93 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.intent_name') ;; 94 | group_label: "Intent" 95 | description: "A description of the caller's intent" 96 | view_label: "Conversation Characteristics" 97 | } 98 | dimension: intent_category { 99 | type: string 100 | sql: split(${intent_name}, '.')[OFFSET(0)];; 101 | group_label: "Intent" 102 | drill_fields: [intent_name] 103 | description: "The category associated with the caller's intent" 104 | view_label: "Conversation Characteristics" 105 | } 106 | dimension: original_webhook_payload { 107 | type: string 108 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.original_webhook_payload') ;; 109 | group_label: "Original Webhook" 110 | } 111 | dimension: webhook_used { 112 | type: yesno 113 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.webhook_used') = 'true' ;; 114 | view_label: "Conversation Characteristics" 115 | } 116 | dimension: original_webhook_body { 117 | type: string 118 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.metadata.original_webhook_body') ;; 119 | group_label: "Original Webhook" 120 | } 121 | 122 | ### Fulfillment #### 123 | 124 | dimension: speech { 125 | description: "Bot Response" 126 | type: string 127 | sql: JSON_EXTRACT_SCALAR(${payload_as_json}, '$.result.fulfillment.speech') ;; 128 | label: "Bot Answer" 129 | } 130 | 131 | ### Raw Data ### 132 | 133 | dimension: text_payload { 134 | view_label: "Raw Data" 135 | type: string 136 | sql: ${TABLE}.textPayload ;; 137 | } 138 | dimension: payload_type { 139 | view_label: "Raw Data" 140 | ### SQL Always Where in Model File is filtering data down to only Dialogflow Requests ### 141 | type: string 142 | sql: split(${text_payload}, ':')[OFFSET(0)];; 143 | } 144 | dimension: parameters { 145 | #Only used for unnesting join 146 | type: string 147 | hidden: yes 148 | sql:JSON_EXTRACT_ARRAY(${payload_as_json}, '$.result.parameters.fields') ;; 149 | } 150 | dimension: parameters_as_string { 151 | view_label: "Raw Data" 152 | type: string 153 | sql:JSON_EXTRACT(${payload_as_json}, '$.result.parameters.fields') ;; 154 | } 155 | dimension: payload_as_json { 156 | 157 | view_label: "Raw Data" 158 | html:
{{value}}
;; 159 | } 160 | 161 | ##Below are Calculations From "Metrics to Measure" Google Doc 162 | 163 | dimension: is_user_query { 164 | #Should be exclude any intents related to welcome messages 165 | type: yesno 166 | sql: ${intent_category} <> 'support' ;; 167 | view_label: "Conversation Characteristics" 168 | description: "Did the user submit a question?" 169 | } 170 | measure: count { 171 | description: "Raw Count of Total User Inputs - Includes Welcome Intent" 172 | type: count 173 | drill_fields: [detail*] 174 | } 175 | measure: total_sessions { 176 | type: count_distinct 177 | sql: ${session_id} ;; 178 | drill_fields: [session_id] 179 | } 180 | measure: queries_per_session { 181 | type: number 182 | sql: 1.0 * ${total_user_queries} / nullif(${total_sessions},0) ;; 183 | value_format_name: decimal_1 184 | } 185 | measure: distinct_intent_values { 186 | type: count_distinct 187 | sql: ${intent_name} ;; 188 | } 189 | measure: total_fallbacks { 190 | type: count 191 | filters: { 192 | field: is_fallback_intent 193 | value: "yes" 194 | } 195 | } 196 | measure: total_successful_intents { 197 | type: count 198 | filters: { 199 | field: is_fallback_intent 200 | value: "no" 201 | } 202 | } 203 | measure: total_user_queries { 204 | description: "Total number of user questions excluding introduction text." 205 | type: count 206 | ### Customize this filter to only include messages related to a customer question. #### 207 | filters: [intent_category: "-support"] 208 | } 209 | measure: successful_intent_rate { 210 | type: number 211 | value_format_name: percent_2 212 | sql: ${total_successful_intents}/NULLIF(${total_user_queries},0) ;; 213 | } 214 | measure: fallback_rate { 215 | type: number 216 | value_format_name: percent_2 217 | sql: ${total_fallbacks}/NULLIF(${count},0) ;; 218 | } 219 | measure: max_timestamp { 220 | hidden: yes 221 | type: date_time 222 | sql: MAX(${timestamp_raw}) ;; 223 | } 224 | measure: min_timestamp { 225 | hidden: yes 226 | type: date_time 227 | sql: MIN(${timestamp_raw}) ;; 228 | } 229 | 230 | #### Additional Metrics for Telephony Bots 231 | 232 | dimension: trace { 233 | view_label: "Telephony Metrics" 234 | type: string 235 | } 236 | dimension: caller_id { 237 | view_label: "Telephony Metrics" 238 | } 239 | measure: count_distinct_trace { 240 | view_label: "Telephony Metrics" 241 | type: count_distinct 242 | sql: ${trace} ;; 243 | } 244 | measure: total_telephone_users { 245 | view_label: "Telephony Metrics" 246 | type: count_distinct 247 | sql: ${caller_id} ;; 248 | } 249 | dimension: area_code { 250 | view_label: "Telephony Metrics" 251 | } 252 | set: detail { 253 | fields: [ 254 | webhook_used, 255 | webhook_for_slot_filling_used, 256 | speech, 257 | source, 258 | session_id, 259 | score, 260 | resolved_query, 261 | intent_name, 262 | intent_id, 263 | is_fallback_intent, 264 | lang ] 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /dashboards/dialogflow.dashboard.lookml: -------------------------------------------------------------------------------- 1 | - dashboard: dialogflow 2 | preferred_viewer: dashboards-next 3 | title: Dialogflow Overview 4 | layout: newspaper 5 | filters: 6 | - name: Timestamp Date 7 | title: 'Date Range' 8 | type: date_filter 9 | default_value: 30 days 10 | 11 | elements: 12 | - title: Fallback Rate 13 | name: Fallback Rate 14 | model: dialogflow_block 15 | explore: parsed_transcripts 16 | type: single_value 17 | fields: [parsed_transcripts.fallback_rate] 18 | limit: 500 19 | query_timezone: America/Los_Angeles 20 | custom_color_enabled: true 21 | show_single_value_title: true 22 | show_comparison: false 23 | comparison_type: value 24 | comparison_reverse_colors: false 25 | show_comparison_label: true 26 | enable_conditional_formatting: false 27 | conditional_formatting_include_totals: false 28 | conditional_formatting_include_nulls: false 29 | defaults_version: 1 30 | listen: 31 | Timestamp Date: parsed_transcripts.timestamp_date 32 | row: 17 33 | col: 10 34 | width: 6 35 | height: 4 36 | # - title: Total Chat Sessions 37 | # name: Total Chat Sessions 38 | # model: dialogflow_block 39 | # explore: parsed_transcripts 40 | # type: single_value 41 | # fields: [parsed_transcripts.total_chat_sessions] 42 | # limit: 500 43 | # query_timezone: America/Los_Angeles 44 | # custom_color_enabled: true 45 | # show_single_value_title: true 46 | # show_comparison: false 47 | # comparison_type: value 48 | # comparison_reverse_colors: false 49 | # show_comparison_label: true 50 | # enable_conditional_formatting: false 51 | # conditional_formatting_include_totals: false 52 | # conditional_formatting_include_nulls: false 53 | # defaults_version: 1 54 | # listen: 55 | # Timestamp Date: parsed_transcripts.timestamp_date 56 | # row: 0 57 | # col: 4 58 | # width: 5 59 | # height: 5 60 | - title: Total Phone Users 61 | name: Total Phone Users 62 | model: dialogflow_block 63 | explore: parsed_transcripts 64 | type: single_value 65 | fields: [parsed_transcripts.total_telephone_users] 66 | limit: 500 67 | query_timezone: America/Los_Angeles 68 | custom_color_enabled: true 69 | show_single_value_title: true 70 | show_comparison: false 71 | comparison_type: value 72 | comparison_reverse_colors: false 73 | show_comparison_label: true 74 | enable_conditional_formatting: false 75 | conditional_formatting_include_totals: false 76 | conditional_formatting_include_nulls: false 77 | defaults_version: 1 78 | listen: 79 | Timestamp Date: parsed_transcripts.timestamp_date 80 | row: 0 81 | col: 9 82 | width: 4 83 | height: 5 84 | - title: Top Area Codes 85 | name: Top Area Codes 86 | model: dialogflow_block 87 | explore: parsed_transcripts 88 | type: looker_pie 89 | fields: [parsed_transcripts.area_code, parsed_transcripts.count] 90 | filters: 91 | parsed_transcripts.area_code: "-NULL" 92 | sorts: [parsed_transcripts.area_code] 93 | limit: 500 94 | column_limit: 50 95 | value_labels: legend 96 | label_type: labPer 97 | x_axis_gridlines: false 98 | y_axis_gridlines: true 99 | show_view_names: false 100 | show_y_axis_labels: true 101 | show_y_axis_ticks: true 102 | y_axis_tick_density: default 103 | y_axis_tick_density_custom: 5 104 | show_x_axis_label: true 105 | show_x_axis_ticks: true 106 | y_axis_scale_mode: linear 107 | x_axis_reversed: false 108 | y_axis_reversed: false 109 | plot_size_by_field: false 110 | trellis: '' 111 | stacking: '' 112 | limit_displayed_rows: false 113 | legend_position: center 114 | point_style: none 115 | show_value_labels: false 116 | label_density: 25 117 | x_axis_scale: auto 118 | y_axis_combined: true 119 | ordering: none 120 | show_null_labels: false 121 | show_totals_labels: false 122 | show_silhouette: false 123 | totals_color: "#808080" 124 | defaults_version: 1 125 | series_types: {} 126 | show_null_points: true 127 | interpolation: linear 128 | show_row_numbers: true 129 | transpose: false 130 | truncate_text: true 131 | hide_totals: false 132 | hide_row_totals: false 133 | size_to_fit: true 134 | table_theme: white 135 | enable_conditional_formatting: false 136 | header_text_alignment: left 137 | header_font_size: 12 138 | rows_font_size: 12 139 | conditional_formatting_include_totals: false 140 | conditional_formatting_include_nulls: false 141 | map_plot_mode: points 142 | heatmap_gridlines: false 143 | heatmap_gridlines_empty: false 144 | heatmap_opacity: 0.5 145 | show_region_field: true 146 | draw_map_labels_above_data: true 147 | map_tile_provider: light 148 | map_position: fit_data 149 | map_scale_indicator: 'off' 150 | map_pannable: true 151 | map_zoomable: true 152 | map_marker_type: circle 153 | map_marker_icon_name: default 154 | map_marker_radius_mode: proportional_value 155 | map_marker_units: meters 156 | map_marker_proportional_scale_type: linear 157 | map_marker_color_mode: fixed 158 | show_legend: true 159 | quantize_map_value_colors: false 160 | reverse_map_value_colors: false 161 | listen: 162 | Timestamp Date: parsed_transcripts.timestamp_date 163 | row: 17 164 | col: 0 165 | width: 10 166 | height: 9 167 | - title: Total User Sessions 168 | name: Total User Sessions 169 | model: dialogflow_block 170 | explore: parsed_transcripts 171 | type: single_value 172 | fields: [parsed_transcripts.count_distinct_trace] 173 | limit: 500 174 | total: true 175 | query_timezone: America/Los_Angeles 176 | custom_color_enabled: true 177 | show_single_value_title: true 178 | show_comparison: false 179 | comparison_type: value 180 | comparison_reverse_colors: false 181 | show_comparison_label: true 182 | enable_conditional_formatting: false 183 | conditional_formatting_include_totals: false 184 | conditional_formatting_include_nulls: false 185 | defaults_version: 1 186 | listen: 187 | Timestamp Date: parsed_transcripts.timestamp_date 188 | row: 0 189 | col: 0 190 | width: 4 191 | height: 5 192 | - title: Total Queries 193 | name: Total Queries 194 | model: dialogflow_block 195 | explore: parsed_transcripts 196 | type: single_value 197 | fields: [parsed_transcripts.count] 198 | filters: 199 | parsed_transcripts.resolved_query: "-NULL" 200 | limit: 500 201 | column_limit: 50 202 | custom_color_enabled: true 203 | show_single_value_title: true 204 | show_comparison: false 205 | comparison_type: value 206 | comparison_reverse_colors: false 207 | show_comparison_label: true 208 | enable_conditional_formatting: false 209 | conditional_formatting_include_totals: false 210 | conditional_formatting_include_nulls: false 211 | x_axis_gridlines: false 212 | y_axis_gridlines: true 213 | show_view_names: false 214 | show_y_axis_labels: true 215 | show_y_axis_ticks: true 216 | y_axis_tick_density: default 217 | y_axis_tick_density_custom: 5 218 | show_x_axis_label: true 219 | show_x_axis_ticks: true 220 | y_axis_scale_mode: linear 221 | x_axis_reversed: false 222 | y_axis_reversed: false 223 | plot_size_by_field: false 224 | trellis: '' 225 | stacking: '' 226 | limit_displayed_rows: false 227 | legend_position: center 228 | point_style: none 229 | show_value_labels: false 230 | label_density: 25 231 | x_axis_scale: auto 232 | y_axis_combined: true 233 | ordering: none 234 | show_null_labels: false 235 | show_totals_labels: false 236 | show_silhouette: false 237 | totals_color: "#808080" 238 | defaults_version: 1 239 | series_types: {} 240 | listen: 241 | Timestamp Date: parsed_transcripts.timestamp_date 242 | row: 0 243 | col: 13 244 | width: 5 245 | height: 5 246 | - title: Average Queries per Session 247 | name: Average Queries per Session 248 | model: dialogflow_block 249 | explore: parsed_transcripts 250 | type: single_value 251 | fields: [parsed_transcripts.count, parsed_transcripts.session_id] 252 | filters: 253 | parsed_transcripts.resolved_query: "-NULL" 254 | sorts: [parsed_transcripts.count desc] 255 | limit: 500 256 | column_limit: 50 257 | dynamic_fields: [{table_calculation: calculation_1, label: Calculation 1, expression: 'mean(${parsed_transcripts.count})', 258 | value_format: !!null '', value_format_name: decimal_2, _kind_hint: measure, 259 | _type_hint: number}] 260 | custom_color_enabled: true 261 | show_single_value_title: true 262 | show_comparison: false 263 | comparison_type: value 264 | comparison_reverse_colors: false 265 | show_comparison_label: true 266 | enable_conditional_formatting: false 267 | conditional_formatting_include_totals: false 268 | conditional_formatting_include_nulls: false 269 | x_axis_gridlines: false 270 | y_axis_gridlines: true 271 | show_view_names: false 272 | show_y_axis_labels: true 273 | show_y_axis_ticks: true 274 | y_axis_tick_density: default 275 | y_axis_tick_density_custom: 5 276 | show_x_axis_label: true 277 | show_x_axis_ticks: true 278 | y_axis_scale_mode: linear 279 | x_axis_reversed: false 280 | y_axis_reversed: false 281 | plot_size_by_field: false 282 | trellis: '' 283 | stacking: '' 284 | limit_displayed_rows: false 285 | legend_position: center 286 | point_style: none 287 | show_value_labels: false 288 | label_density: 25 289 | x_axis_scale: auto 290 | y_axis_combined: true 291 | ordering: none 292 | show_null_labels: false 293 | show_totals_labels: false 294 | show_silhouette: false 295 | totals_color: "#808080" 296 | defaults_version: 1 297 | series_types: {} 298 | hidden_fields: [parsed_transcripts.count] 299 | listen: {} 300 | row: 0 301 | col: 18 302 | width: 6 303 | height: 5 304 | - title: Top User Phrases 305 | name: Top User Phrases 306 | model: dialogflow_block 307 | explore: parsed_transcripts 308 | type: looker_wordcloud 309 | fields: [parsed_transcripts.resolved_query, parsed_transcripts.count] 310 | filters: 311 | parsed_transcripts.resolved_query: -NULL,-"TELEPHONY_WARMUP",-"TELEPHONY_WELCOME",-WELCOME,-?,-"GOOGLE_ASSISTANT_WELCOME",-"HANGOUTS_WELCOME" 312 | sorts: [parsed_transcripts.resolved_query] 313 | limit: 500 314 | column_limit: 50 315 | color_application: undefined 316 | x_axis_gridlines: false 317 | y_axis_gridlines: true 318 | show_view_names: false 319 | show_y_axis_labels: true 320 | show_y_axis_ticks: true 321 | y_axis_tick_density: default 322 | y_axis_tick_density_custom: 5 323 | show_x_axis_label: true 324 | show_x_axis_ticks: true 325 | y_axis_scale_mode: linear 326 | x_axis_reversed: false 327 | y_axis_reversed: false 328 | plot_size_by_field: false 329 | trellis: '' 330 | stacking: '' 331 | limit_displayed_rows: false 332 | legend_position: center 333 | point_style: none 334 | show_value_labels: false 335 | label_density: 25 336 | x_axis_scale: auto 337 | y_axis_combined: true 338 | ordering: none 339 | show_null_labels: false 340 | show_totals_labels: false 341 | show_silhouette: false 342 | totals_color: "#808080" 343 | defaults_version: 1 344 | series_types: {} 345 | hidden_fields: [] 346 | hidden_points_if_no: [] 347 | series_labels: {} 348 | up_color: "#3EB0D5" 349 | down_color: "#B1399E" 350 | total_color: "#C2DD67" 351 | groupBars: true 352 | labelSize: 10pt 353 | showLegend: true 354 | leftAxisLabelVisible: false 355 | leftAxisLabel: '' 356 | rightAxisLabelVisible: false 357 | rightAxisLabel: '' 358 | smoothedBars: false 359 | orientation: automatic 360 | labelPosition: left 361 | percentType: total 362 | percentPosition: inline 363 | valuePosition: right 364 | labelColorEnabled: false 365 | labelColor: "#FFF" 366 | font_size: 12 367 | listen: {} 368 | row: 5 369 | col: 13 370 | width: 11 371 | height: 12 372 | - title: Top Intents 373 | name: Top Intents 374 | model: dialogflow_block 375 | explore: parsed_transcripts 376 | type: looker_bar 377 | fields: [parsed_transcripts.intent_name, parsed_transcripts.count] 378 | filters: 379 | parsed_transcripts.intent_name: "-NULL,-Default Welcome Intent,-Default Fallback Intent" 380 | sorts: [parsed_transcripts.count desc] 381 | limit: 500 382 | column_limit: 50 383 | x_axis_gridlines: false 384 | y_axis_gridlines: true 385 | show_view_names: false 386 | show_y_axis_labels: true 387 | show_y_axis_ticks: true 388 | y_axis_tick_density: default 389 | y_axis_tick_density_custom: 5 390 | show_x_axis_label: true 391 | show_x_axis_ticks: true 392 | y_axis_scale_mode: linear 393 | x_axis_reversed: false 394 | y_axis_reversed: false 395 | plot_size_by_field: false 396 | trellis: '' 397 | stacking: '' 398 | limit_displayed_rows: false 399 | legend_position: center 400 | point_style: none 401 | show_value_labels: false 402 | label_density: 25 403 | x_axis_scale: auto 404 | y_axis_combined: true 405 | ordering: none 406 | show_null_labels: false 407 | show_totals_labels: false 408 | show_silhouette: false 409 | totals_color: "#808080" 410 | defaults_version: 1 411 | series_types: {} 412 | show_row_numbers: true 413 | transpose: false 414 | truncate_text: true 415 | hide_totals: false 416 | hide_row_totals: false 417 | size_to_fit: true 418 | table_theme: white 419 | enable_conditional_formatting: false 420 | header_text_alignment: left 421 | header_font_size: 12 422 | rows_font_size: 12 423 | conditional_formatting_include_totals: false 424 | conditional_formatting_include_nulls: false 425 | show_null_points: true 426 | interpolation: linear 427 | value_labels: legend 428 | label_type: labPer 429 | custom_color_enabled: true 430 | show_single_value_title: true 431 | show_comparison: false 432 | comparison_type: value 433 | comparison_reverse_colors: false 434 | show_comparison_label: true 435 | color_application: undefined 436 | hidden_fields: [] 437 | hidden_points_if_no: [] 438 | series_labels: {} 439 | up_color: "#3EB0D5" 440 | down_color: "#B1399E" 441 | total_color: "#C2DD67" 442 | truncate_column_names: false 443 | map: usa 444 | map_projection: '' 445 | quantize_colors: false 446 | leftAxisLabelVisible: false 447 | leftAxisLabel: '' 448 | rightAxisLabelVisible: false 449 | rightAxisLabel: '' 450 | smoothedBars: false 451 | orientation: automatic 452 | labelPosition: left 453 | percentType: total 454 | percentPosition: inline 455 | valuePosition: right 456 | labelColorEnabled: false 457 | labelColor: "#FFF" 458 | row: 5 459 | col: 0 460 | width: 13 461 | height: 12 462 | --------------------------------------------------------------------------------