diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/address_deepdive.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/address_deepdive.dashboard.lookml new file mode 100644 index 0000000000000..0d613afa8aa34 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/address_deepdive.dashboard.lookml @@ -0,0 +1,266 @@ +- dashboard: address_deepdive + title: Address Deep-dive + layout: newspaper + elements: + - title: Location + name: Location + model: retail_block_model + explore: transactions + type: looker_map + fields: [customers.location] + filters: + transactions.transaction_date: 720 days + sorts: [customers.location] + limit: 500 + column_limit: 50 + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: traffic_day + map_position: fit_data + map_zoom: 20 + map_scale_indicator: metric_imperial + map_pannable: true + map_zoomable: true + map_marker_type: icon + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: meters + map_marker_proportional_scale_type: linear + map_marker_color_mode: fixed + show_view_names: false + show_legend: true + quantize_map_value_colors: false + reverse_map_value_colors: false + series_types: {} + listen: + Address: customers.address + row: 4 + col: 0 + width: 8 + height: 6 + - title: Address Street View + name: Address Street View + model: retail_block_model + explore: transactions + type: single_value + fields: [customers.address_street_view] + filters: + transactions.transaction_date: 720 days + sorts: [customers.address_street_view] + limit: 500 + column_limit: 50 + series_types: {} + listen: + Address: customers.address + row: 4 + col: 8 + width: 16 + height: 10 + - title: Registered customers at this address + name: Registered customers at this address + model: retail_block_model + explore: transactions + type: single_value + fields: [customers.address_comparison, transactions__line_items.number_of_customers_per_address] + filters: + transactions.transaction_date: 720 days + sorts: [customers.address_comparison] + limit: 500 + dynamic_fields: [{table_calculation: vs_average, label: vs Average, expression: "${transactions__line_items.number_of_customers_per_address}-if(is_null(offset(${transactions__line_items.number_of_customers_per_address},1)),0,offset(${transactions__line_items.number_of_customers_per_address},1))", + value_format: !!null '', value_format_name: decimal_0, _kind_hint: measure, + _type_hint: number}] + query_timezone: America/Los_Angeles + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: true + show_comparison_label: true + comparison_label: vs Average + enable_conditional_formatting: false + conditional_formatting: [{type: equal to, value: !!null '', background_color: "#5A30C2", + font_color: !!null '', color_application: {collection_id: retailer-scheme, + palette_id: retailer-scheme-sequential-0}, bold: false, italic: false, strikethrough: false, + fields: !!null ''}] + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [customers.address_comparison] + listen: + Address: customers.address_comparison_filter + row: 10 + col: 0 + width: 8 + height: 4 + - title: Order Pattern + name: Order Pattern + model: retail_block_model + explore: transactions + type: looker_line + fields: [transactions.transaction_date, transactions__line_items.total_quantity, + transactions__line_items.total_sales] + fill_fields: [transactions.transaction_date] + filters: {} + sorts: [transactions.transaction_date desc] + limit: 500 + query_timezone: America/Los_Angeles + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: fb62237b-4172-a396-b394-be13521b401d + label: Custom + type: discrete + colors: + - "#5A30C2" + - "#9d81e6" + options: + steps: 5 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + y_axes: [{label: '', orientation: left, series: [{axisId: transactions__line_items.total_quantity, + id: transactions__line_items.total_quantity, name: Total Quantity}], showLabels: true, + showValues: true, unpinAxis: false, tickDensity: default, type: linear}, { + label: !!null '', orientation: right, series: [{axisId: transactions__line_items.total_sales, + id: transactions__line_items.total_sales, name: Total Sales}], showLabels: true, + showValues: true, unpinAxis: false, tickDensity: default, type: linear}] + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + series_types: + transactions__line_items.total_quantity: scatter + transactions__line_items.total_sales: column + point_style: circle_outline + series_colors: {} + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: false + interpolation: linear + listen: + Address: customers.address + Date Range: transactions.transaction_date + row: 14 + col: 0 + width: 12 + height: 8 + - title: Most Ordered Items + name: Most Ordered Items + model: retail_block_model + explore: transactions + type: looker_grid + fields: [transactions__line_items.total_quantity, transactions__line_items.average_item_price, + products.name, products.product_image, products.category] + filters: {} + sorts: [transactions__line_items.total_quantity desc] + limit: 500 + query_timezone: America/Los_Angeles + show_totals: true + show_row_totals: true + show_view_names: false + show_row_numbers: true + transpose: false + truncate_text: false + size_to_fit: true + series_cell_visualizations: + transactions__line_items.total_quantity: + is_active: true + palette: + palette_id: b8916b58-92d8-1cc3-f7a2-03bbcbc3635d + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom_colors: + - "#ffffff" + - "#9d81e6" + - "#5A30C2" + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + truncate_column_names: false + hide_totals: false + hide_row_totals: false + x_axis_gridlines: false + y_axis_gridlines: true + y_axes: [{label: '', orientation: left, series: [{axisId: transactions__line_items.total_quantity, + id: transactions__line_items.total_quantity, name: Total Quantity}], showLabels: true, + showValues: true, unpinAxis: false, tickDensity: default, type: linear}, { + label: !!null '', orientation: right, series: [{axisId: transactions__line_items.total_sales, + id: transactions__line_items.total_sales, name: Total Sales}], showLabels: true, + showValues: true, unpinAxis: false, tickDensity: default, type: linear}] + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + legend_position: center + series_types: {} + point_style: circle_outline + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: false + interpolation: linear + listen: + Address: customers.address + Date Range: transactions.transaction_date + row: 14 + col: 12 + width: 12 + height: 8 + - name: " Address Overview" + type: text + title_text: " Address Overview" + subtitle_text: Are there any strange patterns happening + at this address? + body_text: |- +
Recommended Action ? + Check if the address does not look residential, or has an abnormally high number of registered customers or volume. If so, set up a regular alert to monitor it, and alert the fraud team if the pattern continues ?.
+ row: 0 + col: 0 + width: 24 + height: 4 + filters: + - name: Address + title: Address + type: field_filter + default_value: '"1909 W 95th St, Chicago, IL 60643, United States"' + allow_multiple_values: true + required: false + model: retail_block_model + explore: transactions + listens_to_filters: [] + field: customers.address + - name: Date Range + title: Date Range + type: date_filter + default_value: 90 days + allow_multiple_values: true + required: false diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/customer_segment_deepdive.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/customer_segment_deepdive.dashboard.lookml new file mode 100644 index 0000000000000..4a3d432e92094 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/customer_segment_deepdive.dashboard.lookml @@ -0,0 +1,384 @@ +- dashboard: customer_segment_deepdive + title: Customer Segment Deep-dive + layout: newspaper + elements: + - title: Customer Coverage + name: Customer Coverage + model: retail_block_model + explore: transactions + type: looker_map + fields: [customers.location, transactions__line_items.total_sales] + filters: + transactions.transaction_date: 12 months + customers.country: USA + customers.location_bin_level: '9' + customers.location: inside box from 55.7765730186677, -157.50000000000003 to + 21.94304553343818, -45 + sorts: [transactions__line_items.total_sales desc] + limit: 5000 + column_limit: 50 + map_plot_mode: automagic_heatmap + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: dark + map_position: custom + map_latitude: 37.96152331396614 + map_longitude: -96.7236328125 + map_zoom: 4 + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: meters + map_marker_proportional_scale_type: linear + map_marker_color_mode: fixed + show_view_names: false + show_legend: true + quantize_map_value_colors: false + reverse_map_value_colors: false + listen: + Customer Segment: customer_clustering_prediction.customer_segment + row: 2 + col: 0 + width: 12 + height: 8 + - title: Sales + name: Sales + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions__line_items.total_quantity] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.average_basket_size, transactions__line_items.total_quantity, + transactions.number_of_transactions] + listen: + Customer Segment: customer_clustering_prediction.customer_segment + Date Range: transactions.date_comparison_filter + row: 2 + col: 12 + width: 6 + height: 4 + - title: Transactions + name: Transactions + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions__line_items.total_quantity] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions.number_of_transactions}/offset(${transactions.number_of_transactions},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.average_basket_size, transactions__line_items.total_sales, + transactions__line_items.total_quantity] + listen: + Customer Segment: customer_clustering_prediction.customer_segment + Date Range: transactions.date_comparison_filter + row: 2 + col: 18 + width: 6 + height: 4 + - title: Basket Size + name: Basket Size + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions__line_items.total_quantity] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.average_basket_size}/offset(${transactions__line_items.average_basket_size},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.total_sales, transactions__line_items.total_quantity, + transactions.number_of_transactions] + listen: + Customer Segment: customer_clustering_prediction.customer_segment + Date Range: transactions.date_comparison_filter + row: 6 + col: 12 + width: 6 + height: 4 + - title: Quantity + name: Quantity + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions__line_items.total_quantity] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_quantity}/offset(${transactions__line_items.total_quantity},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.total_sales, transactions.number_of_transactions, + transactions__line_items.average_basket_size] + listen: + Customer Segment: customer_clustering_prediction.customer_segment + Date Range: transactions.date_comparison_filter + row: 6 + col: 18 + width: 6 + height: 4 + - title: Customer Segment + name: Customer Segment + model: retail_block_model + explore: transactions + type: single_value + fields: [customer_clustering_prediction.customer_segment] + filters: + transactions.transaction_date: 30 days + sorts: [customer_clustering_prediction.customer_segment] + limit: 500 + column_limit: 50 + series_types: {} + listen: + Customer Segment: customer_clustering_prediction.customer_segment + row: 0 + col: 0 + width: 24 + height: 2 + - name: '' + type: text + title_text: '' + subtitle_text: '' + body_text: |- +
+
+
+
+ row: 12 + col: 18 + width: 6 + height: 6 + - title: Potential Value from 10% Reactivation + name: Potential Value from 10% Reactivation + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions__line_items.total_sales] + filters: + transactions.transaction_date: 12 months + customer_facts.customer_spend_trend_past_year: "<-0.1" + limit: 5000 + column_limit: 50 + dynamic_fields: [{table_calculation: reactivation_threshold, label: Reactivation + Threshold, expression: '0.1', value_format: !!null '', value_format_name: !!null '', + _kind_hint: dimension, _type_hint: number}, {table_calculation: potential_value, + label: Potential Value, expression: "${transactions__line_items.total_sales}*${reactivation_threshold}", + value_format: !!null '', value_format_name: usd_0, _kind_hint: measure, _type_hint: number}] + custom_color_enabled: true + custom_color: "#49c244" + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_view_names: false + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + customer_facts.customer_spend_trend_past_year: + is_active: true + palette: + palette_id: retailer-scheme-diverging-0 + collection_id: retailer-scheme + table_theme: white + limit_displayed_rows: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + series_value_format: + customer_facts.customer_id: + name: id + format_string: '0' + label: ID + map_plot_mode: automagic_heatmap + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: dark + map_position: custom + map_latitude: 37.96152331396614 + map_longitude: -96.7236328125 + map_zoom: 4 + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: meters + map_marker_proportional_scale_type: linear + map_marker_color_mode: fixed + show_legend: true + quantize_map_value_colors: false + reverse_map_value_colors: false + series_types: {} + hidden_fields: [reactivation_threshold, transactions__line_items.total_sales] + listen: + Customer Segment: customer_clustering_prediction.customer_segment + row: 10 + col: 18 + width: 6 + height: 2 + - name: " (2)" + type: text + title_text: '' + subtitle_text: '' + body_text: |- + **Recommended Action ?** + These are our previously loyal customers in our segment, who have dropped off over the past year. Click the button on the right to create a campaign segment in Salesforce to prepare an email campaign for the CRM manager. + row: 10 + col: 0 + width: 3 + height: 8 + - title: Customers for Reactivation Campaign + name: Customers for Reactivation Campaign + model: retail_block_model + explore: transactions + type: looker_grid + fields: [customer_facts.customer_id, customer_facts.customer_spend_trend_past_year, + customers.email, customers.gender, customers.age, customers.postcode] + filters: + transactions.transaction_date: 12 months + customer_facts.customer_spend_trend_past_year: "<-0.1" + sorts: [customer_facts.customer_spend_trend_past_year] + limit: 500 + query_timezone: America/Los_Angeles + show_totals: true + show_row_totals: true + show_view_names: false + show_row_numbers: true + transpose: false + truncate_text: true + size_to_fit: true + series_cell_visualizations: + customer_facts.customer_spend_trend_past_year: + is_active: true + palette: + palette_id: 50d44102-be13-bd23-eed2-8000589f3610 + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom_colors: + - "#ab2824" + - "#ffffff" + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + listen: + Customer Segment: customer_clustering_prediction.customer_segment + row: 10 + col: 3 + width: 15 + height: 8 + filters: + - name: Customer Segment + title: Customer Segment + type: field_filter + default_value: Emerging Millennials 🥑 + allow_multiple_values: false + required: true + model: retail_block_model + explore: transactions + listens_to_filters: [] + field: customer_clustering_prediction.customer_segment + - name: Date Range + title: Date Range + type: date_filter + default_value: 30 days + allow_multiple_values: true + required: false diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/group_overview.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/group_overview.dashboard.lookml new file mode 100644 index 0000000000000..bb438754877fc --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/group_overview.dashboard.lookml @@ -0,0 +1,715 @@ +- dashboard: group_overview + title: Group Overview + layout: newspaper + elements: + - title: Sales + name: Sales + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}, {table_calculation: target, label: Target, expression: 'round(${transactions__line_items.total_sales}*1.1/1000,0)*1000', + value_format: !!null '', value_format_name: usd_0, _kind_hint: measure, _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: progress_percentage + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions, vs_ly] + listen: + Date: transactions.date_comparison_filter + row: 2 + col: 0 + width: 6 + height: 2 + - title: Transactions + name: Transactions + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions.number_of_transactions}/offset(${transactions.number_of_transactions},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.average_basket_size, transactions.percent_customer_transactions, + transactions__line_items.total_sales] + listen: + Date: transactions.date_comparison_filter + row: 2 + col: 6 + width: 6 + height: 2 + - title: Basket Size + name: Basket Size + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.average_basket_size}/offset(${transactions__line_items.average_basket_size},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.percent_customer_transactions, transactions__line_items.total_sales, + transactions.number_of_transactions] + listen: + Date: transactions.date_comparison_filter + row: 2 + col: 12 + width: 6 + height: 2 + - title: "% Trx from Loyalty" + name: "% Trx from Loyalty" + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions.percent_customer_transactions}-offset(${transactions.percent_customer_transactions},1)", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.total_sales, transactions.number_of_transactions, + transactions__line_items.average_basket_size] + listen: + Date: transactions.date_comparison_filter + row: 2 + col: 18 + width: 6 + height: 2 + - title: Store Overview + name: Store Overview + model: retail_block_model + explore: transactions + type: looker_map + fields: [stores.location, transactions__line_items.total_sales, transactions.number_of_transactions, + stores.name] + filters: {} + sorts: [transactions.number_of_transactions desc] + limit: 500 + column_limit: 50 + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_view_names: false + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + show_null_points: true + series_types: {} + hidden_fields: + listen: + Date: transactions.transaction_date + row: 4 + col: 10 + width: 14 + height: 9 + - title: Change by Store + name: Change by Store + model: retail_block_model + explore: transactions + type: looker_bar + fields: [stores.name, transactions__line_items.sales_change] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + sorts: [transactions__line_items.sales_change desc] + limit: 500 + column_limit: 50 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: 2cf23ac7-6136-e038-cb59-0b0d03864953 + label: Custom + type: discrete + colors: + - "#5A30C2" + - "#4fd3f0" + - "#04b5cc" + - "#009688" + - "#4CAF50" + - "#8BC34A" + - "#CDDC39" + - "#FFEB3B" + - "#9E9E9E" + - "#607D8B" + - "#607D8B" + options: + steps: 5 + y_axes: [{label: '', orientation: bottom, series: [{axisId: transactions__line_items.sales_change, + id: transactions__line_items.sales_change, name: Sales Change (%)}], showLabels: false, + showValues: true, unpinAxis: false, tickDensity: default, tickDensityCustom: 5, + type: linear}] + series_types: {} + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + show_null_points: true + hidden_fields: + defaults_version: 1 + listen: + Date: transactions.date_comparison_filter + row: 17 + col: 0 + width: 12 + height: 13 + - title: Change by Category + name: Change by Category + model: retail_block_model + explore: transactions + type: looker_bar + fields: [transactions__line_items.sales_change, products.category] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + products.category: "-NULL" + sorts: [transactions__line_items.sales_change desc] + limit: 500 + column_limit: 50 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: 91999ca4-f13f-8b66-db6b-db77995d1766 + label: Custom + type: discrete + colors: + - "#5A30C2" + - "#4fd3f0" + - "#04b5cc" + - "#009688" + - "#4CAF50" + - "#8BC34A" + - "#CDDC39" + - "#FFEB3B" + - "#9E9E9E" + - "#607D8B" + - "#607D8B" + options: + steps: 5 + series_types: {} + series_colors: {} + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + show_null_points: true + hidden_fields: + defaults_version: 1 + listen: + Date: transactions.date_comparison_filter + row: 17 + col: 12 + width: 12 + height: 13 + - name: " Top movers" + type: text + title_text: " Top movers" + subtitle_text: Where do I see the biggest movement vs the + same time last year? + body_text: |- +
Recommended Action ? + Text/email store managers of underperforming stores to look into their store, or dive into the store performance. Dive into underperforming categories to better understand their stock and item dynamics, or alert the category manager ?.
+ row: 13 + col: 0 + width: 24 + height: 4 + - name: " Customer Behaviour" + type: text + title_text: " Customer Behaviour" + subtitle_text: How am I performing with my target customer + segments? + body_text: |- +
Recommended Action ? + We've clustered our customer segments according to a ML algorithm. Look for segments with low YoY performance, or with no spikes in retention, and drill into them to see possible actions to drive them back to our brand.
+ row: 30 + col: 0 + width: 24 + height: 4 + - title: Emerging Millennials 🥑 + name: Emerging Millennials 🥑 + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + customer_clustering_prediction.customer_segment] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: Emerging Millennials% + sorts: [customer_clustering_prediction.customer_segment, transactions.selected_comparison + desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.selected_comparison] + listen: + Date: transactions.date_comparison_filter + row: 34 + col: 0 + width: 6 + height: 4 + - title: Regular Gen Xers 🛒 + name: Regular Gen Xers 🛒 + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: Regular Gen Xers% + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + custom_color: "#5A30C2" + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + defaults_version: 1 + listen: + Date: transactions.date_comparison_filter + row: 34 + col: 6 + width: 6 + height: 4 + - title: One-off locals 🏪 + name: One-off locals 🏪 + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: One-off locals% + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + custom_color: "#5A30C2" + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + defaults_version: 1 + listen: + Date: transactions.date_comparison_filter + row: 38 + col: 6 + width: 6 + height: 5 + - title: Affluent Retirees 👴 + name: Affluent Retirees 👴 + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: Affluent Retirees% + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + custom_color: "#5A30C2" + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + defaults_version: 1 + listen: + Date: transactions.date_comparison_filter + row: 38 + col: 0 + width: 6 + height: 5 + - title: How well are we retaining these customer segments? + name: How well are we retaining these customer segments? + model: retail_block_model + explore: transactions + type: looker_line + fields: [customer_clustering_prediction.customer_segment, transactions.number_of_customers, + transactions.months_since_first_customer_transaction] + pivots: [customer_clustering_prediction.customer_segment] + filters: + transactions.transaction_date: 12 months + transactions.months_since_first_customer_transaction: "<=12" + customer_clustering_prediction.customer_segment: "-NULL" + sorts: [transactions.months_since_first_customer_transaction, customer_clustering_prediction.customer_segment] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: percent_of_customers, label: Percent of Customers, + expression: "${transactions.number_of_customers}/index(${transactions.number_of_customers},1)", + value_format: !!null '', value_format_name: percent_0, _kind_hint: measure, + _type_hint: number}] + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: 7697335c-a9b7-5dd1-9525-d094e796d1b6 + label: Custom + type: discrete + colors: + - "#5A30C2" + - "#b885f7" + - "#0d071c" + - "#d852db" + - "#4CAF50" + - "#8BC34A" + - "#CDDC39" + - "#FFEB3B" + - "#9E9E9E" + - "#607D8B" + - "#607D8B" + options: + steps: 5 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + series_colors: {} + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: true + interpolation: monotone + hidden_fields: [transactions.number_of_customers] + listen: + Date: transactions.date_comparison_filter + row: 34 + col: 12 + width: 12 + height: 9 + - name: " Company Overview" + type: text + title_text: " Company Overview" + subtitle_text: '' + body_text: '' + row: 0 + col: 4 + width: 14 + height: 2 + - name: + type: text + title_text: + subtitle_text: '' + body_text: '' + row: 0 + col: 18 + width: 6 + height: 2 + - title: Sales YoY Trends + name: Sales YoY Trends + model: retail_block_model + explore: transactions + type: looker_line + fields: [transactions__line_items.total_sales, transactions.transaction_month_num, + transactions.transaction_year] + pivots: [transactions.transaction_year] + fill_fields: [transactions.transaction_month_num, transactions.transaction_year] + filters: + transactions.transaction_date: 4 years + transactions.transaction_month: before 0 months ago + sorts: [transactions__line_items.total_sales desc 0, transactions.transaction_year] + limit: 500 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: false + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: false + interpolation: monotone + color_application: + collection_id: 5b121cce-cf79-457c-a52a-9162dc174766 + palette_id: 55dee055-18cf-4472-9669-469322a6f264 + options: + steps: 5 + defaults_version: 1 + listen: {} + row: 4 + col: 0 + width: 10 + height: 9 + filters: + - name: Date + title: Date + type: date_filter + default_value: 7 days + allow_multiple_values: true + required: false + ui_config: + type: relative_timeframes + display: inline diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/item_affinity_analysis.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/item_affinity_analysis.dashboard.lookml new file mode 100644 index 0000000000000..aa67b2522a35c --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/item_affinity_analysis.dashboard.lookml @@ -0,0 +1,618 @@ +- dashboard: item_affinity_analysis + title: Item Affinity Analysis + layout: newspaper + elements: + - title: Product Segmentation + name: Product Segmentation + model: retail_block_model + explore: order_purchase_affinity + type: looker_scatter + fields: [order_purchase_affinity.product_a_average_rest_of_basket_margin, order_purchase_affinity.product_a_order_frequency, + order_purchase_affinity.product_a_order_count, order_purchase_affinity.product_a] + filters: {} + sorts: [order_purchase_affinity.product_a_order_count desc] + limit: 500 + query_timezone: America/Los_Angeles + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: b6cd43f7-2a37-3289-6c70-2de144292004 + label: Custom + type: discrete + colors: + - "#5a30c2" + options: + steps: 5 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + series_types: {} + point_style: circle + series_colors: {} + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + reference_lines: [] + trend_lines: [] + show_null_points: true + interpolation: step + hidden_fields: [order_purchase_affinity.product_a, order_purchase_affinity.product_a_order_frequency] + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Minimum Purchase Frequency: order_purchase_affinity.product_a_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 2 + col: 15 + width: 9 + height: 8 + - title: What are my most popular and margin-generating items? + name: What are my most popular and margin-generating items? + model: retail_block_model + explore: order_purchase_affinity + type: table + fields: [order_purchase_affinity.product_a, order_purchase_affinity.product_a_average_rest_of_basket_margin, + order_purchase_affinity.product_a_order_frequency, order_purchase_affinity.product_a_order_count, + order_purchase_affinity.product_a_percent_purchased_by_loyalty_customer] + filters: {} + sorts: [order_purchase_affinity.product_a_average_rest_of_basket_margin desc] + limit: 500 + query_timezone: America/Los_Angeles + show_view_names: false + show_row_numbers: true + truncate_column_names: false + hide_totals: false + hide_row_totals: false + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: true + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: !!null '', + font_color: !!null '', color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, + custom: {id: 0beedf53-60ed-7e3d-6b1e-9800cfe27788, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: [order_purchase_affinity.product_a_average_rest_of_basket_margin]}] + subtotals_at_bottom: false + stacking: '' + show_value_labels: false + label_density: 25 + legend_position: center + x_axis_gridlines: false + y_axis_gridlines: true + point_style: circle + series_types: {} + y_axis_combined: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + x_axis_scale: auto + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + reference_lines: [] + trend_lines: [] + show_null_points: true + interpolation: step + hidden_fields: + defaults_version: 1 + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Minimum Purchase Frequency: order_purchase_affinity.product_a_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 2 + col: 3 + width: 12 + height: 8 + - title: Proposed Attachments + name: Proposed Attachments + model: retail_block_model + explore: order_purchase_affinity + type: looker_scatter + fields: [order_purchase_affinity.product_b, order_purchase_affinity.product_b_average_rest_of_basket_margin, + order_purchase_affinity.lift, order_purchase_affinity.product_b_order_frequency] + filters: {} + sorts: [order_purchase_affinity.lift desc] + limit: 50 + query_timezone: America/Los_Angeles + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: 99ec795f-cfc7-8237-d8b8-b11d96625502 + label: Custom + type: discrete + colors: + - "#5a30c2" + options: + steps: 5 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + series_types: {} + point_style: circle + series_colors: {} + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + reference_lines: [] + trend_lines: [] + show_null_points: true + show_row_numbers: true + truncate_column_names: false + subtotals_at_bottom: false + hide_totals: false + hide_row_totals: false + table_theme: editable + enable_conditional_formatting: true + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: !!null '', + font_color: !!null '', color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, + custom: {id: 0beedf53-60ed-7e3d-6b1e-9800cfe27788, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: []}] + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + interpolation: step + hidden_fields: [order_purchase_affinity.product_b] + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Focus Product: order_purchase_affinity.product_a + Minimum Purchase Frequency: order_purchase_affinity.product_b_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 14 + col: 13 + width: 11 + height: 10 + - title: What items go well with the focus product and drive the biggest combined + check? + name: What items go well with the focus product and drive the biggest combined + check? + model: retail_block_model + explore: order_purchase_affinity + type: table + fields: [order_purchase_affinity.product_b, order_purchase_affinity.product_b_average_rest_of_basket_margin, + order_purchase_affinity.lift, order_purchase_affinity.product_b_order_frequency] + filters: {} + sorts: [order_purchase_affinity.lift desc] + limit: 50 + query_timezone: America/Los_Angeles + show_view_names: false + show_row_numbers: true + truncate_column_names: false + hide_totals: false + hide_row_totals: false + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: true + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: !!null '', + font_color: !!null '', color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, + custom: {id: 0beedf53-60ed-7e3d-6b1e-9800cfe27788, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: [order_purchase_affinity.lift]}, + {type: along a scale..., value: !!null '', background_color: !!null '', font_color: !!null '', + color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, custom: { + id: 42c42989-a24b-cae4-b890-84b06c7d20ed, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: [order_purchase_affinity.product_b_average_rest_of_basket_margin]}] + subtotals_at_bottom: false + stacking: '' + show_value_labels: false + label_density: 25 + legend_position: center + x_axis_gridlines: false + y_axis_gridlines: true + point_style: circle + series_types: {} + y_axis_combined: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + x_axis_scale: auto + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + reference_lines: [] + trend_lines: [] + show_null_points: true + interpolation: step + hidden_fields: + defaults_version: 1 + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Focus Product: order_purchase_affinity.product_a + Minimum Purchase Frequency: order_purchase_affinity.product_b_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 14 + col: 3 + width: 10 + height: 10 + - title: Focus Product + name: Focus Product + model: retail_block_model + explore: order_purchase_affinity + type: single_value + fields: [order_purchase_affinity.product_a] + sorts: [order_purchase_affinity.product_a] + limit: 50 + dynamic_fields: [{table_calculation: focus_product, label: Focus Product, expression: 'if(count(${order_purchase_affinity.product_a})=1,${order_purchase_affinity.product_a},null)', + value_format: !!null '', value_format_name: !!null '', _kind_hint: dimension, + _type_hint: string}] + query_timezone: America/Los_Angeles + show_view_names: false + show_row_numbers: true + truncate_column_names: false + subtotals_at_bottom: false + hide_totals: false + hide_row_totals: false + table_theme: editable + limit_displayed_rows: false + enable_conditional_formatting: true + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: !!null '', + font_color: !!null '', color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, + custom: {id: 0beedf53-60ed-7e3d-6b1e-9800cfe27788, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: []}, {type: along a scale..., + value: !!null '', background_color: !!null '', font_color: !!null '', color_application: { + collection_id: d588263f-f0cb-4f93-b431-700d57025b85, custom: {id: 42c42989-a24b-cae4-b890-84b06c7d20ed, + label: Custom, type: continuous, stops: [{color: "#ffffff", offset: 0}, + {color: "#83ff80", offset: 50}, {color: "#06bf23", offset: 100}]}, options: { + steps: 5}}, bold: false, italic: false, strikethrough: false, fields: []}] + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + stacking: '' + show_value_labels: false + label_density: 25 + legend_position: center + x_axis_gridlines: false + y_axis_gridlines: true + point_style: circle + series_types: {} + y_axis_combined: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + x_axis_scale: auto + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + reference_lines: [] + trend_lines: [] + show_null_points: true + interpolation: step + hidden_fields: [order_purchase_affinity.product_a] + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Focus Product: order_purchase_affinity.product_a + Minimum Purchase Frequency: order_purchase_affinity.product_a_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 12 + col: 3 + width: 10 + height: 2 + - name: Product Segmentation (2) + type: text + title_text: Product Segmentation + row: 0 + col: 0 + width: 24 + height: 2 + - name: '' + type: text + title_text: '' + subtitle_text: What are the best products to bundle with the focus product? + row: 10 + col: 0 + width: 24 + height: 2 + - name: " (2)" + type: text + subtitle_text: Finally, what are my least popular items in that timeframe? + body_text: '' + row: 24 + col: 0 + width: 24 + height: 2 + - title: Product Rationalisation + name: Product Rationalisation + model: retail_block_model + explore: order_purchase_affinity + type: table + fields: [order_purchase_affinity.product_a, order_purchase_affinity.product_a_order_frequency, + order_purchase_affinity.product_a_average_basket_margin, order_purchase_affinity.product_a_percent_purchased_by_loyalty_customer, + order_purchase_affinity.product_a_percent_customer_exclusivity, order_purchase_affinity.product_a_image] + filters: {} + sorts: [order_purchase_affinity.product_a_order_frequency] + limit: 50 + query_timezone: America/Los_Angeles + show_view_names: false + show_row_numbers: true + truncate_column_names: false + hide_totals: false + hide_row_totals: false + table_theme: unstyled + limit_displayed_rows: false + enable_conditional_formatting: true + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + subtotals_at_bottom: false + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: !!null '', + font_color: !!null '', color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, + custom: {id: e957ced6-3159-da29-15b3-b19b2036f209, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#bf402c", offset: 100}]}, + options: {steps: 5, reverse: true}}, bold: false, italic: false, strikethrough: false, + fields: [order_purchase_affinity.product_a_order_frequency]}, {type: along + a scale..., value: !!null '', background_color: !!null '', font_color: !!null '', + color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, custom: { + id: 62da82c5-db03-598f-d9d2-24c05cea68bc, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#bf402c", offset: 100}]}, + options: {steps: 5, reverse: true}}, bold: false, italic: false, strikethrough: false, + fields: [order_purchase_affinity.product_a_average_basket_margin]}, {type: along + a scale..., value: !!null '', background_color: !!null '', font_color: !!null '', + color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, custom: { + id: 33756f45-fbc4-92e6-96c6-32bc11615cf8, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#bf402c", offset: 100}]}, + options: {steps: 5, reverse: true}}, bold: false, italic: false, strikethrough: false, + fields: [order_purchase_affinity.product_a_percent_purchased_by_loyalty_customer]}, + {type: along a scale..., value: !!null '', background_color: !!null '', font_color: !!null '', + color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, custom: { + id: 721b90d4-be82-368f-b843-70e922627449, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#bf402c", offset: 100}]}, + options: {steps: 5, reverse: true}}, bold: false, italic: false, strikethrough: false, + fields: [order_purchase_affinity.product_a_percent_customer_exclusivity]}] + stacking: '' + show_value_labels: false + label_density: 25 + legend_position: center + x_axis_gridlines: false + y_axis_gridlines: true + point_style: circle + series_types: {} + y_axis_combined: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + x_axis_scale: auto + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + reference_lines: [] + trend_lines: [] + show_null_points: true + interpolation: step + hidden_fields: + defaults_version: 1 + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Minimum Purchase Frequency: order_purchase_affinity.product_a_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 26 + col: 3 + width: 21 + height: 11 + - title: Margin Uplift from Top 5 Promotions in Category + name: Margin Uplift from Top 5 Promotions in Category + model: retail_block_model + explore: order_purchase_affinity + type: single_value + fields: [order_purchase_affinity.product_b, order_purchase_affinity.product_a_total_margin, + order_purchase_affinity.product_b_total_basket_margin, order_purchase_affinity.lift, + assumed_promotion_uptake, margin_uplift_from_promotion] + filters: {} + sorts: [margin_uplift_from_promotion desc] + limit: 50 + column_limit: 50 + dynamic_fields: [{dimension: margin_uplift_from_promotion, label: Margin Uplift + from Promotion, expression: "(${order_purchase_affinity.product_a_total_margin}+${order_purchase_affinity.product_b_total_basket_margin})*${assumed_promotion_uptake}*(${order_purchase_affinity.lift})", + value_format: !!null '', value_format_name: usd_0, _kind_hint: dimension, + _type_hint: number}, {dimension: assumed_promotion_uptake, label: Assumed + Promotion Uptake, expression: '0.1', value_format: !!null '', value_format_name: percent_1, + _kind_hint: dimension, _type_hint: number}, {table_calculation: margin_uplift_from_top_5_promotions, + label: Margin Uplift from Top 5 Promotions, expression: 'sum(if(row()<=5,${margin_uplift_from_promotion},null))', + value_format: !!null '', value_format_name: usd_0, _kind_hint: dimension, + _type_hint: number}] + query_timezone: America/Los_Angeles + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + custom_color: "#5A30C2" + conditional_formatting: [{type: equal to, value: !!null '', background_color: '', + font_color: !!null '', color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, + custom: {id: 0beedf53-60ed-7e3d-6b1e-9800cfe27788, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: [order_purchase_affinity.lift]}, + {type: equal to, value: !!null '', background_color: !!null '', font_color: !!null '', + color_application: {collection_id: d588263f-f0cb-4f93-b431-700d57025b85, custom: { + id: 42c42989-a24b-cae4-b890-84b06c7d20ed, label: Custom, type: continuous, + stops: [{color: "#ffffff", offset: 0}, {color: "#83ff80", offset: 50}, + {color: "#06bf23", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: []}] + show_view_names: false + show_row_numbers: true + truncate_column_names: false + hide_totals: false + hide_row_totals: false + table_theme: white + limit_displayed_rows: false + subtotals_at_bottom: false + stacking: '' + show_value_labels: false + label_density: 25 + legend_position: center + x_axis_gridlines: false + y_axis_gridlines: true + point_style: circle + series_types: {} + y_axis_combined: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + x_axis_scale: auto + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + reference_lines: [] + trend_lines: [] + show_null_points: true + interpolation: step + hidden_fields: [order_purchase_affinity.product_b, order_purchase_affinity.product_a_total_margin, + order_purchase_affinity.product_b_total_basket_margin, order_purchase_affinity.lift, + assumed_promotion_uptake, margin_uplift_from_promotion] + defaults_version: 1 + listen: + Product Level: order_items_base.product_level + Analysis Timeframe: order_purchase_affinity.affinity_timeframe + Focus Product: order_purchase_affinity.product_a + Minimum Purchase Frequency: order_purchase_affinity.product_b_order_frequency + Focus Category: order_purchase_affinity.product_a_category + row: 12 + col: 13 + width: 11 + height: 2 + - name: " (3)" + type: text + body_text: |- + **Recommended Action ?** + Items with a box in a strong green colour are items that are not only sold frequently, but drive large baskets. Click on one to focus on it and see what products we can bundle with it to sell it more + row: 2 + col: 0 + width: 3 + height: 8 + - name: " (4)" + type: text + body_text: |- + **Recommended Action ?** + Look for items with the strongest green colour in all columns; those are items that attach well to the focus product, **and** drive large baskets. + + The value in **purple** shows the potential value of a **10%** uptake of those top 5 promotions over the analysis timeframe. + row: 12 + col: 0 + width: 3 + height: 12 + - name: " (5)" + type: text + body_text: |- + **Recommended Action ?** + Items with a strong red colour are prime candidates for removal from our inventory. They are items that: + + - are not sold frequently + + - do not drive large baskets + + - are not preferred by our key customer segments + + - will not cause us to lose many item-loyal customers if removed + row: 26 + col: 0 + width: 3 + height: 11 + filters: + - name: Product Level + title: Product Level + type: field_filter + default_value: product + allow_multiple_values: true + required: false + model: retail_block_model + explore: order_purchase_affinity + listens_to_filters: [] + field: order_items_base.product_level + - name: Analysis Timeframe + title: Analysis Timeframe + type: date_filter + default_value: 90 day + allow_multiple_values: true + required: false + - name: Focus Product + title: Focus Product + type: field_filter + default_value: '' + allow_multiple_values: true + required: false + model: retail_block_model + explore: order_purchase_affinity + listens_to_filters: [] + field: order_purchase_affinity.product_a + - name: Minimum Purchase Frequency + title: Minimum Purchase Frequency + type: number_filter + default_value: ">=0.005" + allow_multiple_values: true + required: false + - name: Focus Category + title: Focus Category + type: field_filter + default_value: '' + allow_multiple_values: true + required: false + model: retail_block_model + explore: order_purchase_affinity + listens_to_filters: [] + field: order_purchase_affinity.product_a_category diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/store_deepdive.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/store_deepdive.dashboard.lookml new file mode 100644 index 0000000000000..1c3985b2b3f0f --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/dashboards/store_deepdive.dashboard.lookml @@ -0,0 +1,1246 @@ +- dashboard: store_deepdive + title: Store Deep-dive + layout: newspaper + elements: + - title: Sales + name: Sales + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}, {table_calculation: target, label: Target, expression: 'round(${transactions__line_items.total_sales}*1.1/10000,0)*10000', + value_format: !!null '', value_format_name: usd_0, _kind_hint: measure, _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: progress_percentage + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions, vs_ly] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 0 + col: 0 + width: 6 + height: 4 + - title: Transactions + name: Transactions + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions.number_of_transactions}/offset(${transactions.number_of_transactions},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.average_basket_size, transactions.percent_customer_transactions, + transactions__line_items.total_sales] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 0 + col: 6 + width: 6 + height: 4 + - title: Basket Size + name: Basket Size + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.average_basket_size}/offset(${transactions__line_items.average_basket_size},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.percent_customer_transactions, transactions__line_items.total_sales, + transactions.number_of_transactions] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 0 + col: 12 + width: 6 + height: 4 + - title: "% Trx from Loyalty" + name: "% Trx from Loyalty" + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size, + transactions.percent_customer_transactions] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions.percent_customer_transactions}-offset(${transactions.percent_customer_transactions},1)", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions__line_items.total_sales, transactions.number_of_transactions, + transactions__line_items.average_basket_size] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 0 + col: 18 + width: 6 + height: 4 + - name: " Peer Store Comparison" + type: text + title_text: " Peer Store Comparison" + subtitle_text: How well am I doing vs my peer stores? + body_text: |- +
Recommended Action ? + Text/call high-performing store managers to see if they have any advice. Check if different weather trends are affecting your performance as much as in your peer stores.
+ row: 4 + col: 0 + width: 24 + height: 4 + - name: " Customer Behaviour" + type: text + title_text: " Customer Behaviour" + subtitle_text: How am I performing with my target customer + segments? + body_text: |- +
Recommended Action ? + Look for segments with low performance (YoY or vs peer stores), and drill into them to see possible actions to drive them back to our store.
+ row: 45 + col: 0 + width: 24 + height: 4 + - title: Emerging Millennials ? + name: Emerging Millennials ? + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: Emerging Millennials ? + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 49 + col: 0 + width: 6 + height: 4 + - title: Regular Gen Xers ? + name: Regular Gen Xers ? + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: Regular Gen Xers ? + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 49 + col: 6 + width: 6 + height: 4 + - title: One-off locals ? + name: One-off locals ? + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: One-off locals ? + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + note_state: expanded + note_display: below + note_text: '' + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 53 + col: 6 + width: 6 + height: 5 + - title: Affluent Retirees ? + name: Affluent Retirees ? + model: retail_block_model + explore: transactions + type: single_value + fields: [transactions.selected_comparison, transactions__line_items.total_sales, + transactions.number_of_transactions, transactions__line_items.average_basket_size] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + transactions.selected_comparison: "-NULL" + customer_clustering_prediction.customer_segment: Affluent Retirees ? + sorts: [transactions.selected_comparison desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: vs_ly, label: vs LY, expression: "${transactions__line_items.total_sales}/offset(${transactions__line_items.total_sales},1)-1", + value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + custom_color: "#5A30C2" + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hidden_fields: [transactions.number_of_transactions, transactions__line_items.average_basket_size] + listen: + Date: transactions.date_comparison_filter + Store: stores.name + row: 53 + col: 0 + width: 6 + height: 5 + - title: YoY Sales + name: YoY Sales + model: retail_block_model + explore: transactions + type: looker_bar + fields: [transactions__line_items.sales_change, transactions.number_of_transactions_change, + stores.store_comparison_vs_stores_in_tier_with_weather] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + sorts: [transactions__line_items.sales_change desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: store, label: Store, expression: 'concat(${stores.store_comparison_vs_stores_in_tier}," + ",${weather})', value_format: !!null '', value_format_name: !!null '', is_disabled: true, + _kind_hint: dimension, _type_hint: string}, {table_calculation: focus_store_sales_change, + label: Focus Store Sales Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),${transactions__line_items.sales_change},null)', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number}, { + table_calculation: other_stores_sales_change, label: Other Stores Sales Change + (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),null,${transactions__line_items.sales_change})', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number}, { + table_calculation: weather, label: Weather, expression: 'if(${store_weather.average_daily_precipitation}<2.0,"☀️",if(${store_weather.average_daily_precipitation}<4.0,"☁️","?"))', + value_format: !!null '', value_format_name: !!null '', is_disabled: true, + _kind_hint: dimension, _type_hint: string}] + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + palette_id: 89f8fd99-5003-4efd-ae1a-ae0aa28825ca + options: + steps: 5 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: normal + limit_displayed_rows: false + legend_position: center + series_types: {} + point_style: circle + series_colors: + focus_store_sales_change: "#5A30C2" + other_stores_sales_change: "#9d81e6" + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + focus_store_sales_change: + is_active: false + other_stores_sales_change: + is_active: true + table_theme: white + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + show_null_points: true + hidden_fields: [transactions__line_items.sales_change, transactions.number_of_transactions_change] + note_state: collapsed + note_display: hover + note_text: This shows your year over year sales + listen: + Date: transactions.date_comparison_filter + Store: stores.store_for_comparison + row: 8 + col: 0 + width: 12 + height: 8 + - title: YoY Transaction Count + name: YoY Transaction Count + model: retail_block_model + explore: transactions + type: looker_bar + fields: [stores.store_comparison_vs_stores_in_tier_with_weather, transactions__line_items.sales_change, + transactions.number_of_transactions_change] + filters: + transactions.transaction_date: 2 years + transactions.comparison_type: year + sorts: [transactions.number_of_transactions_change desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: focus_store_transaction_count_change, label: Focus + Store Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),${transactions.number_of_transactions_change},null)', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number}, { + table_calculation: other_stores_transaction_count_change, label: Other Stores + Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),null,${transactions.number_of_transactions_change})', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number}] + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: normal + limit_displayed_rows: false + legend_position: center + series_types: {} + point_style: circle + series_colors: + other_stores_transaction_count_change: "#9d81e6" + focus_store_transaction_count_change: "#5A30C2" + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + focus_store_sales_change: + is_active: false + other_stores_sales_change: + is_active: true + table_theme: white + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + show_null_points: true + hidden_fields: [transactions__line_items.sales_change, transactions.number_of_transactions_change] + listen: + Date: transactions.date_comparison_filter + Store: stores.store_for_comparison + row: 8 + col: 12 + width: 12 + height: 8 + - title: Weather Trend vs Sales Trend + name: Weather Trend vs Sales Trend + model: retail_block_model + explore: transactions + type: looker_line + fields: [store_weather.average_max_temparature, transactions.transaction_date, + transactions__line_items.total_sales_per_store] + fill_fields: [transactions.transaction_date] + filters: + transactions.transaction_date: 14 days + stores.store_comparison_vs_tier: 1-% + sorts: [transactions.transaction_date desc] + limit: 500 + column_limit: 1 + dynamic_fields: [{table_calculation: focus_store_transaction_count_change, label: Focus + Store Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),${transactions.number_of_transactions_change},null)', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number, is_disabled: true}, + {table_calculation: other_stores_transaction_count_change, label: Other Stores + Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),null,${transactions.number_of_transactions_change})', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number, is_disabled: true}] + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + y_axes: [{label: '', orientation: left, series: [{axisId: store_weather.average_max_temparature, + id: store_weather.average_max_temparature, name: Average Max Temparature}], + showLabels: true, showValues: true, unpinAxis: false, tickDensity: default, + type: linear}, {label: '', orientation: right, series: [{axisId: transactions__line_items.total_sales_per_store, + id: transactions__line_items.total_sales_per_store, name: Total Sales + per Store}], showLabels: true, showValues: true, unpinAxis: false, tickDensity: default, + type: linear}] + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + hidden_series: [] + legend_position: center + series_types: {} + point_style: circle + series_colors: + 1- Los Angeles - transactions__line_items.total_sales_per_store: "#1F1142" + 2- Rest of Stores in Tier - store_weather.average_max_temparature: "#5A30C2" + 2- Rest of Stores in Tier - transactions__line_items.total_sales_per_store: "#1F1142" + transactions__line_items.total_sales_per_store: "#1F1142" + store_weather.average_max_temparature: "#9d81e6" + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + reference_lines: [{reference_type: range, margin_top: deviation, margin_value: mean, + margin_bottom: deviation, label_position: right, color: "#588eff", line_value: '10', + range_start: "-10", range_end: '10', label: Cold}, {reference_type: range, + line_value: mean, margin_top: deviation, margin_value: mean, margin_bottom: deviation, + label_position: right, color: "#f0c157", range_start: '10', range_end: '20', + label: Warm}, {reference_type: range, line_value: mean, margin_top: deviation, + margin_value: mean, margin_bottom: deviation, label_position: right, color: "#ed5432", + range_start: '20', range_end: '60', label: Hot}] + show_null_points: false + interpolation: linear + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + focus_store_sales_change: + is_active: false + other_stores_sales_change: + is_active: true + table_theme: white + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + hidden_fields: [] + listen: + Store: stores.store_for_comparison + row: 16 + col: 0 + width: 12 + height: 7 + - title: How well am I selling products that are doing well in my peer stores? + name: How well am I selling products that are doing well in my peer stores? + model: retail_block_model + explore: transactions + type: table + fields: [stores.store_comparison_vs_tier, transactions__line_items.total_quantity_per_store, + transactions.number_of_transactions_per_store, products.name] + pivots: [stores.store_comparison_vs_tier] + filters: + transactions.transaction_date: 7 days + sorts: [stores.store_comparison_vs_tier 0, transactions__line_items.total_quantity_per_store + desc 1] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: focus_store_transaction_count_change, label: Focus + Store Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),${transactions.number_of_transactions_change},null)', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number, is_disabled: true}, + {table_calculation: other_stores_transaction_count_change, label: Other Stores + Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),null,${transactions.number_of_transactions_change})', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number, is_disabled: true}] + show_view_names: false + show_row_numbers: true + truncate_column_names: false + hide_totals: false + hide_row_totals: false + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: true + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: "#5A30C2", + font_color: !!null '', color_application: {collection_id: f14810d2-98d7-42df-82d0-bc185a074e42, + custom: {id: c9dd1a4e-a553-d252-0a2b-98cd3e570d22, label: Custom, type: continuous, + stops: [{color: "#fff", offset: 0}, {color: "#9d81e6", offset: 50}, { + color: "#5930c2", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: [transactions__line_items.total_quantity_per_store]}, + {type: along a scale..., value: !!null '', background_color: "#5A30C2", font_color: !!null '', + color_application: {collection_id: f14810d2-98d7-42df-82d0-bc185a074e42, custom: { + id: 4235bfed-58e1-1626-48da-a6a40492da29, label: Custom, type: continuous, + stops: [{color: "#fff", offset: 0}, {color: "#9d81e6", offset: 50}, { + color: "#5930c2", offset: 100}]}, options: {steps: 5}}, bold: false, + italic: false, strikethrough: false, fields: [transactions.number_of_transactions_per_store]}] + x_axis_gridlines: false + y_axis_gridlines: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: normal + legend_position: center + series_types: {} + point_style: circle + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + reference_lines: [{reference_type: range, margin_top: deviation, margin_value: mean, + margin_bottom: deviation, label_position: right, color: "#588eff", line_value: '10', + range_start: "-10", range_end: '10', label: Cold}, {reference_type: range, + line_value: mean, margin_top: deviation, margin_value: mean, margin_bottom: deviation, + label_position: right, color: "#f0c157", range_start: '10', range_end: '20', + label: Warm}, {reference_type: range, line_value: mean, margin_top: deviation, + margin_value: mean, margin_bottom: deviation, label_position: right, color: "#ed5432", + range_start: '20', range_end: '60', label: Hot}] + show_null_points: true + interpolation: linear + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + transpose: false + truncate_text: true + size_to_fit: true + series_cell_visualizations: + focus_store_sales_change: + is_active: false + other_stores_sales_change: + is_active: true + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + hidden_fields: [] + defaults_version: 1 + listen: + Date: transactions.transaction_week + Store: stores.store_for_comparison + row: 35 + col: 0 + width: 24 + height: 10 + - title: How well am I gaining key customer segments vs peer stores? + name: How well am I gaining key customer segments vs peer stores? + model: retail_block_model + explore: transactions + type: looker_bar + fields: [stores.store_comparison_vs_tier, transactions.number_of_transactions_change, + customer_clustering_prediction.customer_segment, transactions.number_of_customers_change] + pivots: [stores.store_comparison_vs_tier] + filters: + transactions.transaction_date: 2 years + customer_clustering_prediction.customer_segment: "-NULL" + transactions.comparison_type: year + sorts: [stores.store_comparison_vs_tier 0, customer_clustering_prediction.customer_segment] + limit: 500 + column_limit: 50 + color_application: + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom: + id: 31e42998-49be-9a52-785f-aa0045688ee9 + label: Custom + type: discrete + colors: + - "#5A30C2" + - "#9d81e6" + options: + steps: 5 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + series_types: {} + point_style: circle + series_colors: {} + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + reference_lines: [] + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + show_row_numbers: true + truncate_column_names: false + hide_totals: false + hide_row_totals: false + table_theme: white + enable_conditional_formatting: true + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: "#5A30C2", + font_color: !!null '', color_application: {collection_id: retailer-scheme, + palette_id: retailer-scheme-sequential-0}, bold: false, italic: false, strikethrough: false, + fields: []}, {type: along a scale..., value: !!null '', background_color: "#5A30C2", + font_color: !!null '', color_application: {collection_id: retailer-scheme, + palette_id: retailer-scheme-sequential-0}, bold: false, italic: false, strikethrough: false, + fields: []}] + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_null_points: true + interpolation: linear + transpose: false + truncate_text: true + size_to_fit: true + series_cell_visualizations: + focus_store_sales_change: + is_active: false + other_stores_sales_change: + is_active: true + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + hidden_fields: [transactions.number_of_transactions_change] + note_state: expanded + note_display: below + note_text: '' + listen: + Date: transactions.date_comparison_filter + Store: stores.store_for_comparison + row: 49 + col: 12 + width: 12 + height: 9 + - name: " Inventory Tracking" + type: text + title_text: " Inventory Tracking" + subtitle_text: Where are the opportunities to optimise my + inventory? + body_text: |- +
Recommended Action ? + Check which products are over/under stocked and make/save money accordingly. Compare to products that are selling better in peer stores, and order them in or drill into their item dynamics to see how to bundle them.
+ row: 23 + col: 0 + width: 24 + height: 4 + - title: Total Value of Missing Stock + name: Total Value of Missing Stock + model: retail_block_model + explore: stock_forecasting_explore_base + type: single_value + fields: [stock_forecasting_explore_base.total_quantity, stock_forecasting_prediction.forecasted_quantity, + stock_forecasting_explore_base.stock_difference, stock_forecasting_explore_base.stock_difference_value, + stock_forecasting_explore_base.product_name] + filters: + stock_forecasting_explore_base.stock_difference_value: ">0" + sorts: [stock_forecasting_explore_base.stock_difference_value desc] + limit: 500 + dynamic_fields: [{table_calculation: missing_stock_value, label: Missing Stock + Value, expression: 'sum(${stock_forecasting_explore_base.stock_difference_value})', + value_format: !!null '', value_format_name: usd_0, _kind_hint: measure, _type_hint: number}] + query_timezone: America/Los_Angeles + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: true + conditional_formatting: [{type: greater than, value: 0, background_color: '', + font_color: "#86c780", color_application: {collection_id: retailer-scheme, + palette_id: retailer-scheme-sequential-0}, bold: false, italic: false, strikethrough: false, + fields: !!null ''}] + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_view_names: false + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + stock_forecasting_explore_base.total_quantity: + is_active: false + stock_forecasting_explore_base.stock_difference_value: + is_active: true + palette: + palette_id: retailer-scheme-diverging-0 + collection_id: retailer-scheme + table_theme: white + limit_displayed_rows: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + series_types: {} + hidden_fields: [stock_forecasting_explore_base.product_name, stock_forecasting_explore_base.stock_difference_value, + stock_forecasting_explore_base.total_quantity, stock_forecasting_prediction.forecasted_quantity, + stock_forecasting_explore_base.stock_difference] + listen: + Date: stock_forecasting_explore_base.transaction_week_filter + Store: stock_forecasting_explore_base.store_name + row: 27 + col: 16 + width: 8 + height: 4 + - title: Total Value of Overstock + name: Total Value of Overstock + model: retail_block_model + explore: stock_forecasting_explore_base + type: single_value + fields: [stock_forecasting_explore_base.total_quantity, stock_forecasting_prediction.forecasted_quantity, + stock_forecasting_explore_base.stock_difference, stock_forecasting_explore_base.stock_difference_value, + stock_forecasting_explore_base.product_name] + filters: + stock_forecasting_explore_base.stock_difference_value: "<0" + sorts: [stock_forecasting_explore_base.stock_difference_value desc] + limit: 500 + dynamic_fields: [{table_calculation: missing_stock_value, label: Missing Stock + Value, expression: 'sum(${stock_forecasting_explore_base.stock_difference_value})', + value_format: !!null '', value_format_name: usd_0, _kind_hint: measure, _type_hint: number}] + query_timezone: America/Los_Angeles + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: true + conditional_formatting: [{type: less than, value: 0, background_color: '', font_color: "#a61610", + color_application: {collection_id: retailer-scheme, palette_id: retailer-scheme-sequential-0}, + bold: false, italic: false, strikethrough: false, fields: !!null ''}] + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_view_names: false + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + stock_forecasting_explore_base.total_quantity: + is_active: false + stock_forecasting_explore_base.stock_difference_value: + is_active: true + palette: + palette_id: retailer-scheme-diverging-0 + collection_id: retailer-scheme + table_theme: white + limit_displayed_rows: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + series_types: {} + hidden_fields: [stock_forecasting_explore_base.product_name, stock_forecasting_explore_base.stock_difference_value, + stock_forecasting_explore_base.total_quantity, stock_forecasting_prediction.forecasted_quantity, + stock_forecasting_explore_base.stock_difference] + listen: + Date: stock_forecasting_explore_base.transaction_week_filter + Store: stock_forecasting_explore_base.store_name + row: 31 + col: 16 + width: 8 + height: 4 + - title: Weather Trend vs Sales Trend - Peer Stores + name: Weather Trend vs Sales Trend - Peer Stores + model: retail_block_model + explore: transactions + type: looker_line + fields: [store_weather.average_max_temparature, transactions.transaction_date, + transactions__line_items.total_sales_per_store] + fill_fields: [transactions.transaction_date] + filters: + transactions.transaction_date: 14 days + stores.store_comparison_vs_tier: 2-% + sorts: [transactions.transaction_date desc] + limit: 500 + column_limit: 1 + dynamic_fields: [{table_calculation: focus_store_transaction_count_change, label: Focus + Store Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),${transactions.number_of_transactions_change},null)', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number, is_disabled: true}, + {table_calculation: other_stores_transaction_count_change, label: Other Stores + Transaction Count Change (%), expression: 'if(contains(${stores.store_comparison_vs_stores_in_tier_with_weather},"1- + "),null,${transactions.number_of_transactions_change})', value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number, is_disabled: true}] + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + y_axes: [{label: '', orientation: left, series: [{axisId: store_weather.average_max_temparature, + id: store_weather.average_max_temparature, name: Average Max Temparature}], + showLabels: true, showValues: true, unpinAxis: false, tickDensity: default, + type: linear}, {label: '', orientation: right, series: [{axisId: transactions__line_items.total_sales_per_store, + id: transactions__line_items.total_sales_per_store, name: Total Sales + per Store}], showLabels: true, showValues: true, unpinAxis: false, tickDensity: default, + type: linear}] + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + hidden_series: [] + legend_position: center + series_types: {} + point_style: circle + series_colors: + 1- Los Angeles - transactions__line_items.total_sales_per_store: "#1F1142" + 2- Rest of Stores in Tier - store_weather.average_max_temparature: "#5A30C2" + 2- Rest of Stores in Tier - transactions__line_items.total_sales_per_store: "#1F1142" + store_weather.average_max_temparature: "#9d81e6" + transactions__line_items.total_sales_per_store: "#1F1142" + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + reference_lines: [{reference_type: range, margin_top: deviation, margin_value: mean, + margin_bottom: deviation, label_position: right, color: "#588eff", line_value: '10', + range_start: "-10", range_end: '10', label: Cold}, {reference_type: range, + line_value: mean, margin_top: deviation, margin_value: mean, margin_bottom: deviation, + label_position: right, color: "#f0c157", range_start: '10', range_end: '20', + label: Warm}, {reference_type: range, line_value: mean, margin_top: deviation, + margin_value: mean, margin_bottom: deviation, label_position: right, color: "#ed5432", + range_start: '20', range_end: '60', label: Hot}] + show_null_points: false + interpolation: linear + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + series_cell_visualizations: + focus_store_sales_change: + is_active: false + other_stores_sales_change: + is_active: true + table_theme: white + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: circle + map_marker_icon_name: default + map_marker_radius_mode: proportional_value + map_marker_units: pixels + map_marker_radius_min: 3 + map_marker_radius_max: 20 + map_marker_proportional_scale_type: linear + map_marker_color_mode: value + show_legend: true + map_value_colors: ["#aaa", "#5930c2"] + quantize_map_value_colors: false + reverse_map_value_colors: false + color_range: ["#5A30C2", "#9d81e6", "#2D2442", "#42248F", "#1F1142"] + color_by: root + hidden_fields: [] + listen: + Store: stores.store_for_comparison + row: 16 + col: 12 + width: 12 + height: 7 + - title: Main Over-/Under- Stocked Products + name: Main Over-/Under- Stocked Products + model: retail_block_model + explore: stock_forecasting_explore_base + type: looker_grid + fields: [stock_forecasting_explore_base.product_name, stock_forecasting_explore_base.total_quantity, + stock_forecasting_prediction.forecasted_quantity, stock_forecasting_explore_base.stock_difference, + stock_forecasting_explore_base.stock_difference_value] + filters: + stock_forecasting_explore_base.stock_difference: not 0 + stock_forecasting_explore_base.stock_difference_value: ">0,<-200" + sorts: [stock_forecasting_explore_base.stock_difference_value desc] + limit: 500 + column_limit: 50 + show_view_names: false + show_row_numbers: true + transpose: false + truncate_text: true + hide_totals: false + hide_row_totals: false + size_to_fit: true + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_sql_query_menu_options: false + show_totals: true + show_row_totals: true + series_cell_visualizations: + stock_forecasting_explore_base.total_quantity: + is_active: false + stock_forecasting_explore_base.stock_difference_value: + is_active: true + palette: + palette_id: 0ab20095-1901-c8ba-05e9-122c506bc899 + collection_id: f14810d2-98d7-42df-82d0-bc185a074e42 + custom_colors: + - "#a8282e" + - "#ffffff" + - "#5930c2" + series_types: {} + defaults_version: 1 + listen: + Date: stock_forecasting_explore_base.transaction_week_filter + Store: stock_forecasting_explore_base.store_name + row: 27 + col: 0 + width: 16 + height: 8 + filters: + - name: Date + title: Date + type: date_filter + default_value: 7 days + allow_multiple_values: true + required: false + - name: Store + title: Store + type: field_filter + default_value: Los Angeles + allow_multiple_values: true + required: false + model: retail_block_model + explore: transactions + listens_to_filters: [] + field: stores.name diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/explores/omni_channel.explores.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/explores/omni_channel.explores.lkml new file mode 100644 index 0000000000000..58de0f54b7491 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/explores/omni_channel.explores.lkml @@ -0,0 +1,4 @@ +include: "/omni_channel/omni_channel_events.view" +explore: omni_channel_events_base { + view_name: omni_channel_events +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/manifest.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/manifest.lkml new file mode 100644 index 0000000000000..a6ad791139ebd --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/manifest.lkml @@ -0,0 +1 @@ +project_name: "retail" diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/models/omni_channel.model.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/models/omni_channel.model.lkml new file mode 100644 index 0000000000000..795ad2455bd74 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/models/omni_channel.model.lkml @@ -0,0 +1,83 @@ +connection: "looker-private-demo" + +include: "/omni_channel/*.view.lkml" +include: "/omni_channel/*.dashboard.lookml" + + +datagroup: new_day { + sql_trigger: SELECT CURRENT_DATE() ;; + max_cache_age: "24 hours" +} + +explore: omni_channel_transactions { + description: "#audience-builder" # for audience builder extension + persist_with: new_day + join: omni_channel_transactions__transaction_details { + type: left_outer + relationship: one_to_many + sql: LEFT JOIN UNNEST(${omni_channel_transactions.transaction_details}) as omni_channel_transactions__transaction_details ;; + } + join: c360 { + type: inner + relationship: many_to_one + sql_on: ${omni_channel_transactions.customer_id} = ${c360.customer_id} ;; + } + join: customers { + type: inner + relationship: one_to_one + sql_on: ${c360.customer_id} = ${customers.id} ;; + } + join: retail_clv_predict { + view_label: "C360" + fields: [predicted_clv, average_clv] + relationship: one_to_one + sql_on: ${c360.customer_id} = ${retail_clv_predict.customer_id} ;; + } +} + +explore: omni_channel_events { + persist_with: new_day + join: c360 { + type: inner + relationship: many_to_one + sql_on: ${c360.customer_id} = ${omni_channel_events.customer_id} ;; + } + join: omni_channel_transactions { + fields: [] + relationship: one_to_many + sql_on: ${omni_channel_transactions.customer_id} = ${c360.customer_id} ;; + } + join: omni_channel_transactions__transaction_details { + fields: [omni_channel_transactions__transaction_details.total_sales] + type: left_outer + relationship: one_to_many + sql: LEFT JOIN UNNEST(${omni_channel_transactions.transaction_details}) as omni_channel_transactions__transaction_details ;; + } + join: retail_clv_predict { + view_label: "C360" + fields: [predicted_clv, average_clv] + relationship: one_to_one + sql_on: ${c360.customer_id} = ${retail_clv_predict.customer_id} ;; + } +} + +explore: omni_channel_support_calls { + persist_with: new_day +} + +explore: customer_event_fact {} + + +explore: customer_transaction_fact { + persist_with: new_day + join: customer_event_fact { + type: left_outer + relationship: one_to_one + sql_on: ${customer_event_fact.customer_id} = ${customer_transaction_fact.customer_id} ;; + } + join: customer_support_fact { + type: left_outer + relationship: one_to_one + sql_on: ${customer_support_fact.client_id} = ${customer_transaction_fact.customer_id} ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/models/retail_block_model.model.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/models/retail_block_model.model.lkml new file mode 100644 index 0000000000000..173be35b67a12 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/models/retail_block_model.model.lkml @@ -0,0 +1,156 @@ +connection: "looker-private-demo" +label: "Retail" + +include: "/views/**/*.view" # include all the views +include: "/dashboards/*.dashboard.lookml" # include all the dashboards + + +explore: transactions { + label: "(1) Transaction Detail 🏷" + always_filter: { + filters: { + field: transaction_date + value: "last 30 days" + } + } + + access_filter: { + field: stores.name + user_attribute: store + } + + join: transactions__line_items { + relationship: one_to_many + sql: LEFT JOIN UNNEST(${transactions.line_items}) transactions__line_items ;; + } + + join: customers { + relationship: many_to_one + sql_on: ${transactions.customer_id} = ${customers.id} ;; + } + + join: customer_facts { + relationship: many_to_one + view_label: "Customers" + sql_on: ${transactions.customer_id} = ${customer_facts.customer_id} ;; + } + + join: products { + relationship: many_to_one + sql_on: ${products.id} = ${transactions__line_items.product_id} ;; + } + + join: stores { + type: left_outer + sql_on: ${stores.id} = ${transactions.store_id} ;; + relationship: many_to_one + } + + join: channels { + type: left_outer + view_label: "Transactions" + sql_on: ${channels.id} = ${transactions.channel_id} ;; + relationship: many_to_one + } + + join: customer_transaction_sequence { + relationship: many_to_one + sql_on: ${transactions.customer_id} = ${customer_transaction_sequence.customer_id} + AND ${transactions.transaction_raw} = ${customer_transaction_sequence.transaction_raw} ;; + } + + join: store_weather { + relationship: many_to_one + sql_on: ${transactions.transaction_date} = ${store_weather.weather_date} + AND ${transactions.store_id} = ${store_weather.store_id};; + } + + join: customer_clustering_prediction { + fields: [customer_clustering_prediction.centroid_id,customer_clustering_prediction.customer_segment] + view_label: "Customers" + relationship: many_to_one + sql_on: ${transactions.customer_id} = ${customer_clustering_prediction.customer_id} ;; + } + + sql_always_where: {% if transactions.comparison_type._is_filtered %} + {% if transactions.comparison_type._parameter_value == 'year' %} + {% condition transactions.date_comparison_filter %} ${transaction_raw} {% endcondition %} OR (${transaction_raw} >= TIMESTAMP(DATE_ADD(CAST({% date_start transactions.date_comparison_filter %} AS DATE),INTERVAL -1 YEAR)) AND ${transaction_raw} <= TIMESTAMP(DATE_ADD(CAST({% date_end transactions.date_comparison_filter %} AS DATE),INTERVAL -365 DAY))) + {% elsif transactions.comparison_type._parameter_value == 'week' %} + {% condition transactions.date_comparison_filter %} ${transaction_raw} {% endcondition %} OR (${transaction_raw} >= TIMESTAMP(DATE_ADD(CAST({% date_start transactions.date_comparison_filter %} AS DATE),INTERVAL -1 WEEK)) AND ${transaction_raw} <= TIMESTAMP(DATE_ADD(CAST({% date_end transactions.date_comparison_filter %} AS DATE),INTERVAL -6 DAY))) + {% elsif transactions.comparison_type._parameter_value == 'YTD' %} + EXTRACT(YEAR FROM ${transaction_date}) = EXTRACT(YEAR FROM CURRENT_DATE()) OR EXTRACT(YEAR FROM ${transaction_date}) = EXTRACT(YEAR FROM DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)) AND ${transaction_date} <= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY) + {% elsif transactions.comparison_type._parameter_value == 'WTD' %} + DATE_TRUNC(${transaction_date}, WEEK(MONDAY)) = DATE_TRUNC(CURRENT_DATE() , WEEK(MONDAY)) OR (EXTRACT(WEEK FROM ${transaction_date}) = EXTRACT(WEEK FROM DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)) AND ${transaction_date} <= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)) + + {% else %} + 1=1 + {% endif %} + {% else %} + 1=1 + {% endif %};; +} + +explore: stock_forecasting_explore_base { + label: "(2) Stock Forecasting 🏭" + + always_filter: { + filters: { + field: transaction_week_filter + value: "last 12 weeks" + } + } + + join: stock_forecasting_prediction { + relationship: one_to_one + type: full_outer + sql_on: ${stock_forecasting_explore_base.transaction_week_of_year_for_join} = ${stock_forecasting_prediction.transaction_week_of_year} + AND ${stock_forecasting_explore_base.store_id_for_join} = ${stock_forecasting_prediction.store_id} + AND ${stock_forecasting_explore_base.product_name_for_join} = ${stock_forecasting_prediction.product_name};; + } +} + +explore: order_purchase_affinity { + label: "(3) Item Affinity 🔗" + view_label: "Item Affinity" + + always_filter: { + filters: { + field: affinity_timeframe + value: "last 90 days" + } + filters: { + field: order_items_base.product_level + value: "product" + } + } + + join: order_items_base {} + + join: total_orders { + type: cross + relationship: many_to_one + } +} + +explore: customer_clustering_prediction { + label: "(4) Customer Segments 👤" + join: transactions { + # to avoid warning on dashboard URL link in customer_segment dimension + fields: [transactions.date_comparison_filter] + } +} + +### caching + +datagroup: daily { + sql_trigger: SELECT CURRENT_DATE() ;; + max_cache_age: "24 hours" +} + +datagroup: weekly { + sql_trigger: SELECT EXTRACT(WEEK FROM CURRENT_DATE()) ;; +} + +datagroup: monthly { + sql_trigger: SELECT EXTRACT(MONTH FROM CURRENT_DATE()) ;; +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/c360.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/c360.dashboard.lookml new file mode 100644 index 0000000000000..2700dfb1738cb --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/c360.dashboard.lookml @@ -0,0 +1,936 @@ +- dashboard: customer_360 + title: Customer 360 + layout: newspaper + preferred_viewer: dashboards-next + elements: + - title: Transaction Count by Channel + name: Transaction Count by Channel + model: omni_channel + explore: omni_channel_transactions + type: looker_area + fields: [omni_channel_transactions.transaction_month, omni_channel_transactions.transaction_count, + omni_channel_transactions.fulfillment_channel] + pivots: [omni_channel_transactions.fulfillment_channel] + filters: + omni_channel_transactions.transaction_date: 12 months ago for 12 months + sorts: [omni_channel_transactions.transaction_month desc, omni_channel_transactions.fulfillment_channel] + limit: 500 + column_limit: 50 + row_total: right + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: normal + limit_displayed_rows: false + legend_position: center + point_style: circle + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: false + interpolation: monotone + show_totals_labels: true + show_silhouette: false + totals_color: "#808080" + y_axes: [{label: '', orientation: left, series: [{axisId: Assisted Checkout - + omni_channel_transactions.transaction_count, id: Assisted Checkout - + omni_channel_transactions.transaction_count, name: Assisted Checkout}, + {axisId: Delivery - omni_channel_transactions.transaction_count, id: Delivery + - omni_channel_transactions.transaction_count, name: Delivery}, {axisId: In-store + Pickup - omni_channel_transactions.transaction_count, id: In-store Pickup + - omni_channel_transactions.transaction_count, name: In-store Pickup}, + {axisId: Self Checkout - omni_channel_transactions.transaction_count, id: Self + Checkout - omni_channel_transactions.transaction_count, name: Self Checkout}], + showLabels: false, showValues: true, unpinAxis: false, tickDensity: default, + tickDensityCustom: 5, type: linear}] + series_types: {} + defaults_version: 1 + listen: {} + row: 5 + col: 0 + width: 12 + height: 9 + - title: In-Store and Online + name: In-Store and Online + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.customer_count] + filters: + c360.customer_type: Both Online and Instore + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + defaults_version: 1 + listen: {} + row: 2 + col: 8 + width: 4 + height: 3 + - name: Customer Health + type: text + title_text: Customer Health + subtitle_text: '' + body_text: '' + row: 43 + col: 0 + width: 24 + height: 2 + - name: Customer Acquisition + type: text + title_text: Customer Acquisition + subtitle_text: '' + body_text: '' + row: 25 + col: 0 + width: 24 + height: 2 + - name: Omni-Channel Overview + type: text + title_text: Omni-Channel Overview + subtitle_text: '' + body_text: '' + row: 0 + col: 0 + width: 24 + height: 2 + - name: Channel Analysis + type: text + title_text: Channel Analysis + subtitle_text: '' + body_text: '' + row: 14 + col: 0 + width: 24 + height: 2 + - title: RFV + name: RFV + model: omni_channel + explore: omni_channel_transactions + type: treemap + fields: [c360.customer_count, c360.rfm_rating] + sorts: [c360.customer_count desc] + limit: 500 + column_limit: 50 + hidden_fields: [] + hidden_points_if_no: [] + series_labels: {} + show_view_names: false + color_range: ["#4285F4", "#EA4335", "#FBBC04", "#34A853", "#5F6368", "#185ABC", + "#9AA0A6", "#B31412", "#BDC1C6", "#EA8600", "#E8EAED", "#137333"] + x_axis_gridlines: false + y_axis_gridlines: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 0 + series_types: {} + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + row: 45 + col: 0 + width: 12 + height: 9 + - title: Recency + name: Recency + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.high_recency_customers, c360.customer_count, c360.low_recency_customers, + c360.high_frequency_customers, c360.low_frequency_customers, c360.high_monetary_value_customers, + c360.low_monetary_value_customers] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: progress_percentage + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + single_value_title: High Recency Customers + comparison_label: Customers + hidden_fields: [] + hidden_points_if_no: [] + series_labels: {} + show_view_names: false + x_axis_gridlines: false + y_axis_gridlines: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + show_null_points: true + interpolation: linear + listen: {} + row: 45 + col: 12 + width: 12 + height: 3 + - title: Frequency + name: Frequency + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.high_recency_customers, c360.low_recency_customers, c360.high_frequency_customers, + c360.customer_count, c360.low_frequency_customers, c360.high_monetary_value_customers, + c360.low_monetary_value_customers] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: progress_percentage + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + single_value_title: High Frequency Customers + comparison_label: Customers + hidden_fields: [c360.high_recency_customers, c360.low_recency_customers] + hidden_points_if_no: [] + series_labels: {} + show_view_names: false + x_axis_gridlines: false + y_axis_gridlines: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + show_null_points: true + interpolation: linear + listen: {} + row: 48 + col: 12 + width: 12 + height: 3 + - title: Value + name: Value + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.high_recency_customers, c360.low_recency_customers, c360.high_frequency_customers, + c360.low_frequency_customers, c360.high_monetary_value_customers, c360.customer_count, + c360.low_monetary_value_customers] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: progress_percentage + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + single_value_title: High Value Customers + comparison_label: Customers + hidden_fields: [c360.high_recency_customers, c360.low_recency_customers, c360.high_frequency_customers, + c360.low_frequency_customers] + hidden_points_if_no: [] + series_labels: {} + show_view_names: false + x_axis_gridlines: false + y_axis_gridlines: true + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + show_null_points: true + interpolation: linear + listen: {} + row: 51 + col: 12 + width: 12 + height: 3 + - title: Customer Behavior by Acquisition Source + name: Customer Behavior by Acquisition Source + model: omni_channel + explore: omni_channel_events + type: looker_column + fields: [delivery_only, in_store_only, in_store_and_online, pickup_only, omni_channel_events.traffic_source] + sorts: [delivery_only desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{measure: delivery_only, based_on: c360.customer_count, type: count_distinct, + label: Delivery Only, value_format: !!null '', value_format_name: !!null '', + _kind_hint: measure, _type_hint: number, filter_expression: "${c360.online_transaction_count}\ + \ > 0 AND ${c360.instore_transaction_count} = 0 AND ${c360.curbside_transaction_count}\ + \ = 0"}, {measure: in_store_only, based_on: c360.customer_count, type: count_distinct, + label: In Store Only, value_format: !!null '', value_format_name: !!null '', + _kind_hint: measure, _type_hint: number, filter_expression: "${c360.curbside_transaction_count}\ + \ = 0 AND ${c360.instore_transaction_count} > 0 AND ${c360.online_transaction_count}\ + \ = 0"}, {measure: in_store_and_online, based_on: c360.customer_count, type: count_distinct, + label: In Store and Online, value_format: !!null '', value_format_name: !!null '', + _kind_hint: measure, _type_hint: number, filter_expression: "${c360.instore_transaction_count}\ + \ > 0 AND ${c360.online_transaction_count} > 0"}, {measure: pickup_only, + based_on: c360.customer_count, type: count_distinct, label: Pickup Only, value_format: !!null '', + value_format_name: !!null '', _kind_hint: measure, _type_hint: number, filter_expression: "${c360.online_transaction_count}\ + \ >0 AND ${c360.curbside_transaction_count} >0 AND ${c360.instore_transaction_count}\ + \ =0"}] + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: normal + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + font_size: '12' + series_types: {} + defaults_version: 1 + show_null_points: true + interpolation: linear + listen: {} + row: 35 + col: 0 + width: 12 + height: 8 + - title: Sales YoY + name: Sales YoY + model: omni_channel + explore: omni_channel_transactions + type: looker_line + fields: [omni_channel_transactions__transaction_details.total_sales, omni_channel_transactions.transaction_month_name, + omni_channel_transactions.transaction_year] + pivots: [omni_channel_transactions.transaction_year] + fill_fields: [omni_channel_transactions.transaction_month_name, omni_channel_transactions.transaction_year] + sorts: [omni_channel_transactions.transaction_year 0, omni_channel_transactions.transaction_month_name] + limit: 500 + column_limit: 50 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: circle + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: false + interpolation: monotone + y_axes: [{label: '', orientation: left, series: [{axisId: Assisted Checkout - + omni_channel_transactions.transaction_count, id: Assisted Checkout - + omni_channel_transactions.transaction_count, name: Assisted Checkout}, + {axisId: Delivery - omni_channel_transactions.transaction_count, id: Delivery + - omni_channel_transactions.transaction_count, name: Delivery}, {axisId: In-store + Pickup - omni_channel_transactions.transaction_count, id: In-store Pickup + - omni_channel_transactions.transaction_count, name: In-store Pickup}, + {axisId: Self Checkout - omni_channel_transactions.transaction_count, id: Self + Checkout - omni_channel_transactions.transaction_count, name: Self Checkout}], + showLabels: false, showValues: true, unpinAxis: false, tickDensity: default, + tickDensityCustom: 5, type: linear}] + series_types: {} + show_totals_labels: true + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + listen: {} + row: 5 + col: 12 + width: 12 + height: 9 + - title: New Customers + name: New Customers + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.customer_count] + filters: + c360.first_purchase_date: 90 days + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: goal, label: Goal, expression: '1000', value_format: !!null '', + value_format_name: decimal_0, _kind_hint: dimension, _type_hint: number}] + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: progress_percentage + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + defaults_version: 1 + listen: {} + row: 2 + col: 12 + width: 6 + height: 3 + - title: Transactions + name: Transactions + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [omni_channel_transactions.transaction_count, omni_channel_transactions.reporting_period] + filters: + omni_channel_transactions.transaction_date: 2 years + sorts: [omni_channel_transactions.transaction_count desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: from_last_year, label: from last year, expression: "${omni_channel_transactions.transaction_count}/offset(${omni_channel_transactions.transaction_count},1)\ + \ - 1", value_format: !!null '', value_format_name: percent_1, _kind_hint: measure, + _type_hint: number}] + custom_color_enabled: true + show_single_value_title: true + show_comparison: true + comparison_type: change + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + defaults_version: 1 + hidden_fields: [omni_channel_transactions.reporting_period] + listen: {} + row: 2 + col: 18 + width: 6 + height: 3 + - title: Sales by Channel + name: Sales by Channel + model: omni_channel + explore: omni_channel_transactions + type: looker_pie + fields: [c360.customer_type, omni_channel_transactions__transaction_details.total_sales] + fill_fields: [c360.customer_type] + limit: 500 + column_limit: 50 + row_total: right + value_labels: legend + label_type: labPer + inner_radius: 45 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: normal + limit_displayed_rows: false + legend_position: center + point_style: circle + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: false + interpolation: monotone + show_totals_labels: true + show_silhouette: false + totals_color: "#808080" + y_axes: [{label: '', orientation: left, series: [{axisId: Assisted Checkout - + omni_channel_transactions.transaction_count, id: Assisted Checkout - + omni_channel_transactions.transaction_count, name: Assisted Checkout}, + {axisId: Delivery - omni_channel_transactions.transaction_count, id: Delivery + - omni_channel_transactions.transaction_count, name: Delivery}, {axisId: In-store + Pickup - omni_channel_transactions.transaction_count, id: In-store Pickup + - omni_channel_transactions.transaction_count, name: In-store Pickup}, + {axisId: Self Checkout - omni_channel_transactions.transaction_count, id: Self + Checkout - omni_channel_transactions.transaction_count, name: Self Checkout}], + showLabels: false, showValues: true, unpinAxis: false, tickDensity: default, + tickDensityCustom: 5, type: linear}] + series_types: {} + defaults_version: 1 + listen: {} + row: 16 + col: 0 + width: 8 + height: 9 + - title: Profitability and CLV by Channel + name: Profitability and CLV by Channel + model: omni_channel + explore: omni_channel_transactions + type: looker_column + fields: [c360.customer_type, c360.customer_count, omni_channel_transactions__transaction_details.profit_margin, + retail_clv_predict.average_clv] + fill_fields: [c360.customer_type] + sorts: [c360.customer_count desc] + limit: 500 + column_limit: 50 + row_total: right + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: circle + show_value_labels: true + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: true + show_silhouette: false + totals_color: "#808080" + y_axes: [{label: '', orientation: left, series: [{axisId: retail_clv_predict.average_clv, id: retail_clv_predict.average_clv, + name: Average Clv}], showLabels: false, showValues: false, unpinAxis: false, + tickDensity: default, tickDensityCustom: 5, type: linear}, {label: '', orientation: left, + series: [{axisId: c360.customer_count, id: c360.customer_count, name: Customer + Count}], showLabels: false, showValues: false, unpinAxis: false, tickDensity: default, + type: linear}, {label: !!null '', orientation: right, series: [{axisId: omni_channel_transactions__transaction_details.profit_margin, + id: omni_channel_transactions__transaction_details.profit_margin, name: Profit + Margin}], showLabels: false, showValues: false, unpinAxis: false, tickDensity: default, + tickDensityCustom: 5, type: linear}] + series_types: {} + show_null_points: false + interpolation: monotone + value_labels: legend + label_type: labPer + inner_radius: 45 + defaults_version: 1 + listen: {} + row: 16 + col: 8 + width: 8 + height: 9 + - title: In Store Customers + name: In Store Customers + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.customer_count] + filters: + c360.customer_type: Instore Only + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + defaults_version: 1 + listen: {} + row: 2 + col: 0 + width: 4 + height: 3 + - title: Online Customers + name: Online Customers + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.customer_count] + filters: + c360.customer_type: Online Only + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + defaults_version: 1 + listen: {} + row: 2 + col: 4 + width: 4 + height: 3 + - title: LTV by Channel + name: LTV by Channel + model: omni_channel + explore: omni_channel_transactions + type: looker_line + fields: [c360.months_since_first_purchase, c360.customer_count, omni_channel_transactions__transaction_details.total_sales, + c360.customer_type] + pivots: [c360.customer_type] + fill_fields: [c360.customer_type] + filters: + c360.months_since_first_purchase: ">0" + sorts: [c360.customer_type 0, c360.months_since_first_purchase] + limit: 500 + dynamic_fields: [{table_calculation: average_per_customer_sales, label: Average + Per Customer Sales, expression: 'running_total(${omni_channel_transactions__transaction_details.total_sales}/index(${c360.customer_count},1))', + value_format: !!null '', value_format_name: usd, _kind_hint: measure, _type_hint: number}] + query_timezone: America/Los_Angeles + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: true + interpolation: linear + y_axes: [{label: Average Lifetime Sales, orientation: left, series: [{axisId: average_per_customer_sales, + id: Online Only - 0 - average_per_customer_sales, name: Online Only}, + {axisId: average_per_customer_sales, id: Instore Only - 1 - average_per_customer_sales, + name: Instore Only}, {axisId: average_per_customer_sales, id: Both Online + and Instore - 2 - average_per_customer_sales, name: Both Online and + Instore}], showLabels: true, showValues: false, unpinAxis: false, tickDensity: default, + tickDensityCustom: 5, type: linear}] + series_types: {} + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + hidden_fields: [c360.customer_count, omni_channel_transactions__transaction_details.total_sales] + listen: {} + row: 16 + col: 16 + width: 8 + height: 9 + - title: LTV by Acquisition Source + name: LTV by Acquisition Source + model: omni_channel + explore: omni_channel_transactions + type: looker_line + fields: [c360.months_since_first_purchase, c360.customer_count, omni_channel_transactions__transaction_details.total_sales, + c360.acquisition_source] + pivots: [c360.acquisition_source] + filters: + c360.months_since_first_purchase: ">0" + c360.acquisition_source: "-EMPTY" + sorts: [c360.months_since_first_purchase, c360.acquisition_source] + limit: 500 + dynamic_fields: [{table_calculation: average_per_customer_sales, label: Average + Per Customer Sales, expression: 'running_total(${omni_channel_transactions__transaction_details.total_sales}/index(${c360.customer_count},1))', + value_format: !!null '', value_format_name: usd, _kind_hint: measure, _type_hint: number}] + query_timezone: America/Los_Angeles + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + show_null_points: true + interpolation: linear + series_types: {} + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + hidden_fields: [c360.customer_count, omni_channel_transactions__transaction_details.total_sales] + listen: {} + row: 27 + col: 12 + width: 12 + height: 8 + - title: Acquisition Source + name: Acquisition Source + model: omni_channel + explore: omni_channel_transactions + type: looker_donut_multiples + fields: [c360.acquisition_source, c360.customer_count, c360.customer_type] + pivots: [c360.acquisition_source] + fill_fields: [c360.customer_type] + sorts: [c360.customer_count desc 0, c360.customer_type, c360.acquisition_source] + limit: 500 + column_limit: 50 + show_value_labels: false + font_size: 12 + value_labels: legend + label_type: labPer + inner_radius: 45 + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + row: 27 + col: 0 + width: 12 + height: 8 + - title: Website Activity by Traffic Source + name: Website Activity by Traffic Source + model: omni_channel + explore: omni_channel_transactions + type: looker_column + fields: [c360.acquisition_source, session_count, cart_adds, purchases] + filters: + c360.acquisition_source: "-Unknown" + sorts: [session_count desc] + limit: 500 + column_limit: 50 + dynamic_fields: [{measure: purchases, based_on: c360.purchases, type: sum, label: Purchases, + expression: !!null '', value_format: !!null '', value_format_name: !!null '', + _kind_hint: measure, _type_hint: number}, {measure: cart_adds, based_on: c360.cart_adds, + type: sum, label: Cart Adds, expression: !!null '', value_format: !!null '', + value_format_name: !!null '', _kind_hint: measure, _type_hint: number}, { + measure: session_count, based_on: c360.session_count, type: sum, label: Session + Count, expression: !!null '', value_format: !!null '', value_format_name: !!null '', + _kind_hint: measure, _type_hint: number}, {table_calculation: conversion_rate, + label: Conversion Rate, expression: "${purchases} / ${session_count}", value_format: !!null '', + value_format_name: percent_1, _kind_hint: measure, _type_hint: number}] + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + y_axes: [{label: '', orientation: left, series: [{axisId: session_count, id: session_count, + name: Session Count}, {axisId: cart_adds, id: cart_adds, name: Cart Adds}, + {axisId: purchases, id: purchases, name: Purchases}], showLabels: true, + showValues: true, unpinAxis: false, tickDensity: default, tickDensityCustom: 5, + type: linear}, {label: !!null '', orientation: right, series: [{axisId: conversion_rate, + id: conversion_rate, name: Conversion Rate}], showLabels: true, showValues: true, + unpinAxis: true, tickDensity: default, tickDensityCustom: 5, type: linear}] + font_size: '12' + series_types: + conversion_rate: line + value_labels: legend + label_type: labPer + inner_radius: 45 + defaults_version: 1 + listen: {} + row: 35 + col: 12 + width: 12 + height: 8 diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/c360.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/c360.view.lkml new file mode 100644 index 0000000000000..8164af1e7d433 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/c360.view.lkml @@ -0,0 +1,295 @@ +# If necessary, uncomment the line below to include explore_source. +include: "/models/omni_channel.model.lkml" + +view: c360 { + derived_table: { + datagroup_trigger: new_day + explore_source: customer_transaction_fact { + column: cart_adds { field: customer_event_fact.cart_adds } + column: event_count { field: customer_event_fact.event_count } + column: purchases { field: customer_event_fact.purchases } + column: session_count { field: customer_event_fact.session_count } + column: count { field: customer_support_fact.count } + column: acquisition_source {field: customer_event_fact.acquisition_source} + column: curbside_transaction_count {} + column: customer_id {} + column: discounted_transaction_count {} + column: first_purchase {} + column: instore_transaction_count {} + column: item_count {} + column: l180_transaction_count {} + column: l30_transaction_count {} + column: l360_transaction_count {} + column: l90_transaction_count {} + column: last_purchase {} + column: online_transaction_count {} + column: return_count {} + column: total_sales {} + column: transaction_count {} + } + } + dimension: acquisition_source { + drill_fields: [omni_channel_transactions.offer_type] + type: string + sql: COALESCE(${TABLE}.acquisition_source,'Unknown') ;; + } + dimension: customer_type { + drill_fields: [acquisition_source,has_visited_website] + case: { + when: { + sql: ${online_transaction_count} > 0 and ${instore_transaction_count} > 0 ;; + label: "Online Only" + } + when: { + sql: ${online_transaction_count} > 0 and ${instore_transaction_count} = 0 ;; + label: "Instore Only" + } + when: { + sql: ${online_transaction_count} = 0 and ${instore_transaction_count} > 0 ;; + label: "Both Online and Instore" + } + + } + } + + dimension: cart_adds { + type: number + } + dimension: event_count { + type: number + } + dimension: purchases { + type: number + } + dimension: session_count { + type: number + } + dimension: has_visited_website { + type: yesno + sql: ${event_count} > 0 ;; + } + dimension: count { + label: "Support Calls" + type: number + } + dimension: curbside_transaction_count { + type: number + } + dimension: customer_id { + value_format_name: id + primary_key: yes + type: number + } + dimension: discounted_transaction_count { + type: number + } + dimension_group: first_purchase { + type: time + timeframes: [raw,date] + sql: CAST(${TABLE}.first_purchase as timestamp) ;; + } + dimension: instore_transaction_count { + type: number + } + dimension: item_count { + type: number + } + dimension: l180_transaction_count { + type: number + } + dimension: l30_transaction_count { + type: number + } + dimension: l360_transaction_count { + type: number + } + dimension: l90_transaction_count { + type: number + } + dimension_group: last_purchase { + type: time + timeframes: [raw,date] + sql: CAST(${TABLE}.last_purchase as timestamp) ;; + } + dimension: days_a_customer { + type: number + sql: IF(DATE_DIFF(CURRENT_DATE(), ${first_purchase_date}, DAY) > 30,DATE_DIFF(CURRENT_DATE(), ${first_purchase_date}, DAY),30) ;; + } + dimension: days_since_last_purchase { + type: number + sql: DATE_DIFF(CURRENT_DATE(), ${last_purchase_date}, DAY) ;; + } + dimension: average_days_between_transaction { + type: number + sql: ${days_a_customer} / nullif(${purchases},0) ;; + } + dimension: risk_of_churn { + type: number + value_format_name: percent_1 + sql: IF(1 - (${average_days_between_transaction} / nullif(${days_since_last_purchase},0))<0,0,1 - (${average_days_between_transaction} / nullif(${days_since_last_purchase},0))) ;; + } + dimension: risk_of_churn_100 { + type: number + sql: ${risk_of_churn} * 100 ;; + } + dimension: online_transaction_count { + type: number + } + dimension: return_count { + type: number + } + dimension: total_sales { + value_format_name: usd + type: number + } + dimension: transaction_count { + type: number + } + +#use customer transaction date + + dimension: months_since_first_purchase { + type: number + sql: DATE_DIFF(${omni_channel_transactions.transaction_date}, ${first_purchase_date}, MONTH) ;; + } + + dimension: transactions_per_month { + type: number + sql: ${transaction_count}/nullif(${months_since_first_purchase},0) ;; + } + + dimension: recency_rating { + hidden: yes + type: number + sql: CASE WHEN ${l30_transaction_count} >= 1 THEN 5 + WHEN ${l30_transaction_count} < 1 AND ${l90_transaction_count} >=1 THEN 4 + WHEN ${l30_transaction_count} < 1 AND ${l90_transaction_count} < 1 AND ${l180_transaction_count} >= 1 THEN 3 + WHEN ${l30_transaction_count} < 1 AND ${l90_transaction_count} < 1 AND ${l180_transaction_count} < 1 AND ${l360_transaction_count} >= 2 THEN 2 + WHEN ${l360_transaction_count} < 2 THEN 1 + ELSE null + END ;; + } + +# number of transactions/orders per month + dimension: frequency_rating { + hidden: yes + type: number + sql: CASE WHEN ${transactions_per_month} >= 2 THEN 5 + WHEN ${transactions_per_month} >= 1 THEN 4 + WHEN ${transactions_per_month} >= 0.5 THEN 3 + WHEN ${transactions_per_month} >= 0.25 THEN 2 + else 1 + END ;; + } + + dimension: value_rating { + hidden: yes + type: number + sql: CASE WHEN ${total_sales} >= 1000 THEN 5 + WHEN ${total_sales} < 1000 AND ${total_sales} >= 800 THEN 4 + WHEN ${total_sales} < 800 AND ${total_sales} >= 600 THEN 3 + WHEN ${total_sales} < 600 AND ${total_sales} >= 400 THEN 2 + WHEN ${total_sales} < 400 THEN 1 + ELSE null + END ;; + } + + dimension: rfm_rating { + type: string + sql: CASE WHEN ${recency_rating} = 5 AND (${frequency_rating} = 5 OR ${frequency_rating} = 4) THEN 'Champion' + WHEN (${recency_rating} = 5 OR ${recency_rating} = 4) AND (${frequency_rating} = 3 OR ${frequency_rating} = 2) THEN 'Potential Loyalist' + WHEN ${recency_rating} = 5 AND ${frequency_rating} = 1 THEN 'New Customer' + WHEN ${recency_rating} = 4 AND ${frequency_rating} = 1 THEN 'Promising' + WHEN (${recency_rating} = 4 OR ${recency_rating} = 3) AND (${frequency_rating} = 5 OR ${frequency_rating} = 4) THEN 'Loyal Customer' + WHEN (${recency_rating} = 2 OR ${recency_rating} = 1) AND ${frequency_rating} = 5 THEN 'Cant lose them' + WHEN (${recency_rating} = 2 OR ${recency_rating} = 1) AND (${frequency_rating} = 4 OR ${frequency_rating} = 3) THEN 'At Risk' + WHEN ${recency_rating} = 3 AND ${frequency_rating} = 3 THEN 'Needs Attention' + WHEN (${recency_rating} = 2 OR ${recency_rating} = 1) AND (${frequency_rating} = 2 OR ${frequency_rating} = 1) THEN 'Hibernating' + WHEN ${recency_rating} = 3 AND (${frequency_rating} = 2 OR ${frequency_rating} = 1) THEN 'About to sleep' + ELSE null + END ;; + } + + measure: customer_count { + value_format:"[>=1000000]0.0,,\"M\";[>=1000]0.0,\"K\";0" + drill_fields: [customer_id,customers.name,customers.email,customers.address,retail_clv_predict.predicted_clv,risk_of_churn,omni_channel_transactions__transaction_details.recommended_products] + link: { + label: "Top 100 Predicted CLV Customers" + url: "{{ link }}&sorts=c360.predicted_clv+desc&limit=100" + } + type: count + } + + measure: high_recency_customers { + group_label: "RFV Analysis" + type: count + filters: [recency_rating: "5"] + } + + measure: low_recency_customers { + group_label: "RFV Analysis" + type: count + filters: [recency_rating: "1"] + } + + measure: high_frequency_customers { + group_label: "RFV Analysis" + type: count + filters: [frequency_rating: "5"] + } + + measure: low_frequency_customers { + group_label: "RFV Analysis" + type: count + filters: [frequency_rating: "1"] + } + + measure: average_sales_amount { + value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + type: average + sql: ${total_sales} ;; + } + + measure: average_transaction_count { + type: average + sql: ${transaction_count} ;; + } + + measure: high_monetary_value_customers { + group_label: "RFV Analysis" + type: count + filters: [value_rating: "5"] + } + + measure: low_monetary_value_customers { + group_label: "RFV Analysis" + type: count + filters: [value_rating: "1"] + } + + # using retail_clv_predict.predicted_clv for CLV metrics below + # see colab: https://colab.research.google.com/drive/1eLUqHgAv8gJig61t8NgrVzT7uv_pqsFi?resourcekey=0-E5aEzPTb44U80SLiE4NMvg&usp=sharing + # view with predictions: https://googledemo.looker.com/projects/retail/files/omni_channel/lpd_retail_clv_predict_tbl.view.lkml + + # dimension: predicted_clv { + # value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + # type: number + # sql: (${total_sales} / NULLIF(${days_a_customer},0) * 365) / IF(${customer_type} = 'Both Online and Instore',0.05,0.1) ;; + # } + + # measure: average_clv { + # value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + # type: average + # sql: ${predicted_clv} ;; + # } + + # measure: customer_count_first_purchase { + # type: count + # filters: [months_since_first_purchase: "0"] + # } + + # measure: sales_per_user { + # type: number + # sql: ${omni_channel_transactions__transaction_details.total_sales}/${customer_count_first_purchase} ;; + # } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/campaign_activation.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/campaign_activation.dashboard.lookml new file mode 100644 index 0000000000000..3e7c4da8d0113 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/campaign_activation.dashboard.lookml @@ -0,0 +1,221 @@ +- dashboard: campaign_activation + title: Campaign Activation + layout: newspaper + preferred_viewer: dashboards-next + elements: + - title: Predicted High CLV Customers + name: Predicted High CLV Customers + model: omni_channel + explore: omni_channel_transactions + type: looker_grid + fields: [c360.customer_id, customers.name, customers.email, customers.address, + c360.predicted_clv, recommended_products, c360.risk_of_churn] + sorts: [c360.predicted_clv desc] + limit: 100 + dynamic_fields: [{measure: recommended_products, based_on: omni_channel_transactions__transaction_details.product_name, + type: list, label: Recommended Products, expression: !!null '', value_format: !!null '', + value_format_name: !!null '', _kind_hint: measure, _type_hint: list}] + query_timezone: America/Los_Angeles + show_view_names: false + show_row_numbers: false + transpose: false + truncate_text: false + hide_totals: false + hide_row_totals: false + size_to_fit: true + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: true + header_text_alignment: left + header_font_size: '15' + rows_font_size: '13' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_sql_query_menu_options: false + show_totals: true + show_row_totals: true + series_column_widths: + c360.customer_id: 130 + customers.name: 175 + c360.risk_of_churn: 133 + c360.predicted_clv: 300 + series_cell_visualizations: + recommended_products: + is_active: true + c360.predicted_clv: + is_active: true + value_display: true + series_text_format: + c360.risk_of_churn: + align: left + conditional_formatting: [{type: along a scale..., value: !!null '', background_color: "#4285F4", + font_color: !!null '', color_application: {collection_id: google, palette_id: google-diverging-0, + options: {steps: 5, constraints: {min: {type: minimum}, mid: {type: number, + value: 0}, max: {type: maximum}}, mirror: false, reverse: true, stepped: false}}, + bold: false, italic: false, strikethrough: false, fields: [c360.risk_of_churn]}] + defaults_version: 1 + listen: + Customer Type: c360.customer_type + Has Visited Website?: c360.has_visited_website + Likely to Interact With: c360.acquisition_source + Likely to Purchase: omni_channel_transactions__transaction_details.product_category + Likely to Respond To: omni_channel_transactions.offer_type + Churn Risk: c360.risk_of_churn_100 + Lifetime Purchases: c360.transaction_count + row: 0 + col: 0 + width: 20 + height: 14 + - name: '' + type: text + title_text: '' + subtitle_text: '' + body_text: "\n\ + \ Send to Google Analytics\n" + row: 4 + col: 20 + width: 4 + height: 2 + - name: " (2)" + type: text + title_text: '' + subtitle_text: '' + body_text: "\n\ + \ Send to Marketo\n" + row: 2 + col: 20 + width: 4 + height: 2 + - name: " (3)" + type: text + title_text: '' + subtitle_text: '' + body_text: "\n Send to SendGrid\n" + row: 0 + col: 20 + width: 4 + height: 2 + - name: " (4)" + type: text + title_text: '' + subtitle_text: '' + body_text: "\n Send to Salesforce\n\ + " + row: 6 + col: 20 + width: 4 + height: 2 + filters: + - name: Customer Type + title: Customer Type + type: field_filter + default_value: Instore Only + allow_multiple_values: true + required: false + ui_config: + type: tag_list + display: popover + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: c360.customer_type + - name: Has Visited Website? + title: Has Visited Website? + type: field_filter + default_value: 'Yes' + allow_multiple_values: true + required: false + ui_config: + type: button_toggles + display: inline + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: c360.has_visited_website + - name: Likely to Interact With + title: Likely to Interact With + type: field_filter + default_value: '' + allow_multiple_values: true + required: false + ui_config: + type: tag_list + display: popover + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: c360.acquisition_source + - name: Likely to Purchase + title: Likely to Purchase + type: field_filter + default_value: '' + allow_multiple_values: true + required: false + ui_config: + type: tag_list + display: popover + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: omni_channel_transactions__transaction_details.product_category + - name: Likely to Respond To + title: Likely to Respond To + type: field_filter + default_value: '' + allow_multiple_values: true + required: false + ui_config: + type: tag_list + display: popover + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: omni_channel_transactions.offer_type + - name: Churn Risk + title: Churn Risk + type: field_filter + default_value: "[50,100]" + allow_multiple_values: true + required: false + ui_config: + type: range_slider + display: inline + options: + min: 0 + max: 100 + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: c360.risk_of_churn_100 + - name: Lifetime Purchases + title: Lifetime Purchases + type: field_filter + default_value: "[3,10]" + allow_multiple_values: true + required: false + ui_config: + type: range_slider + display: inline + options: + min: 0 + max: 10 + model: omni_channel + explore: omni_channel_transactions + listens_to_filters: [] + field: c360.transaction_count diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/cust_deep_dive.dashboard.lookml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/cust_deep_dive.dashboard.lookml new file mode 100644 index 0000000000000..434e707d2d4bf --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/cust_deep_dive.dashboard.lookml @@ -0,0 +1,478 @@ +- dashboard: customer_deep_dive + title: Customer Deep Dive + layout: newspaper + preferred_viewer: dashboards + elements: + - title: Customer Location + name: Customer Location + model: omni_channel + explore: omni_channel_transactions + type: looker_geo_coordinates + fields: [customers.location, c360.customer_count] + sorts: [customers.location] + limit: 500 + column_limit: 50 + map: usa + map_projection: '' + show_view_names: false + point_radius: 10 + map_plot_mode: points + heatmap_gridlines: false + heatmap_gridlines_empty: false + heatmap_opacity: 0.5 + show_region_field: true + draw_map_labels_above_data: true + map_tile_provider: light + map_position: fit_data + map_scale_indicator: 'off' + map_pannable: true + map_zoomable: true + map_marker_type: icon + map_marker_icon_name: default + map_marker_radius_mode: fixed + map_marker_units: pixels + map_marker_proportional_scale_type: linear + map_marker_color_mode: fixed + show_legend: true + quantize_map_value_colors: false + reverse_map_value_colors: false + map_zoom: 8 + map_marker_radius_fixed: 10 + defaults_version: 1 + series_types: {} + listen: + ID: c360.customer_id + row: 0 + col: 12 + width: 12 + height: 8 + - title: Customer Info + name: Customer Info + model: omni_channel + explore: omni_channel_transactions + type: looker_single_record + fields: [customers.id, customers.name, customers.registered_date, customers.email, + customers.age, customers.gender, customers.city, customers.state, customers.postcode] + sorts: [customers.registered_date desc] + limit: 500 + column_limit: 50 + show_view_names: false + show_sql_query_menu_options: false + show_totals: false + show_row_totals: false + show_row_numbers: false + transpose: false + truncate_text: true + size_to_fit: true + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + series_types: {} + hide_totals: false + hide_row_totals: false + defaults_version: 1 + listen: + ID: c360.customer_id + row: 0 + col: 0 + width: 6 + height: 6 + - title: Lifetime Purchases + name: Lifetime Purchases + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.transaction_count] + limit: 500 + column_limit: 50 + dynamic_fields: [{table_calculation: calculation_1, label: Calculation 1, expression: "${c360.purchases}\ + \ / ${c360.cart_adds}", value_format: !!null '', value_format_name: percent_1, + _kind_hint: dimension, _type_hint: number, is_disabled: true}] + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + comparison_label: Conversion Rate + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + hidden_fields: [] + listen: + ID: c360.customer_id + row: 0 + col: 6 + width: 6 + height: 2 + - title: Lifetime Returns + name: Lifetime Returns + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.return_count] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + listen: + ID: c360.customer_id + row: 6 + col: 6 + width: 6 + height: 2 + - title: Total Sales + name: Total Sales + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.total_sales] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + listen: + ID: c360.customer_id + row: 2 + col: 6 + width: 6 + height: 2 + - title: Predicted CLV + name: Predicted CLV + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.predicted_clv] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + listen: + ID: c360.customer_id + row: 4 + col: 6 + width: 6 + height: 2 + - title: Churn Risk + name: Churn Risk + model: omni_channel + explore: omni_channel_transactions + type: single_value + fields: [c360.risk_of_churn] + limit: 500 + column_limit: 50 + custom_color_enabled: true + show_single_value_title: true + show_comparison: false + comparison_type: value + comparison_reverse_colors: false + show_comparison_label: true + enable_conditional_formatting: false + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: true + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + point_style: none + show_value_labels: false + label_density: 25 + x_axis_scale: auto + y_axis_combined: true + ordering: none + show_null_labels: false + show_totals_labels: false + show_silhouette: false + totals_color: "#808080" + defaults_version: 1 + series_types: {} + listen: + ID: c360.customer_id + row: 6 + col: 0 + width: 6 + height: 2 + - title: Order History + name: Order History + model: omni_channel + explore: omni_channel_transactions + type: looker_grid + fields: [omni_channel_transactions.transaction_date, omni_channel_transactions.transaction_id, + omni_channel_transactions.purchase_channel, omni_channel_transactions.fulfillment_channel, + omni_channel_transactions.offer_type, omni_channel_transactions.store_name, + omni_channel_transactions__transaction_details.product_name, omni_channel_transactions__transaction_details.product_sku, + omni_channel_transactions__transaction_details.sale_price] + sorts: [omni_channel_transactions.transaction_date desc] + limit: 500 + column_limit: 50 + show_view_names: false + show_row_numbers: false + transpose: false + truncate_text: false + hide_totals: false + hide_row_totals: false + size_to_fit: true + table_theme: white + limit_displayed_rows: false + enable_conditional_formatting: false + header_text_alignment: left + header_font_size: '12' + rows_font_size: '12' + conditional_formatting_include_totals: false + conditional_formatting_include_nulls: false + show_sql_query_menu_options: false + show_totals: false + show_row_totals: false + series_labels: + omni_channel_transactions.transaction_date: Date + omni_channel_transactions.transaction_id: ID + series_types: {} + defaults_version: 1 + listen: + ID: omni_channel_transactions.customer_id + row: 8 + col: 0 + width: 12 + height: 14 + - name: Customer Timeline + title: Customer Timeline + merged_queries: + - model: omni_channel + explore: omni_channel_transactions + type: table + fields: [omni_channel_transactions.transaction_date, omni_channel_transactions.transaction_count] + fill_fields: [omni_channel_transactions.transaction_date] + limit: 500 + query_timezone: America/Los_Angeles + join_fields: [] + - model: omni_channel + explore: omni_channel_events + type: table + fields: [omni_channel_events.session_count, omni_channel_events.created_date] + fill_fields: [omni_channel_events.created_date] + limit: 500 + query_timezone: America/Los_Angeles + join_fields: + - field_name: omni_channel_events.created_date + source_field_name: omni_channel_transactions.transaction_date + - model: omni_channel + explore: omni_channel_support_calls + type: table + fields: [omni_channel_support_calls.conversation_start_date, omni_channel_support_calls.count] + fill_fields: [omni_channel_support_calls.conversation_start_date] + sorts: [omni_channel_support_calls.conversation_start_date desc] + limit: 500 + query_timezone: America/Los_Angeles + join_fields: + - field_name: omni_channel_support_calls.conversation_start_date + source_field_name: omni_channel_transactions.transaction_date + x_axis_gridlines: false + y_axis_gridlines: true + show_view_names: false + y_axes: [{label: '', orientation: left, series: [{axisId: omni_channel_transactions.transaction_count, + id: omni_channel_transactions.transaction_count, name: Purchases}, {axisId: omni_channel_events.session_count, + id: omni_channel_events.session_count, name: Web Visits}, {axisId: omni_channel_support_calls.count, + id: omni_channel_support_calls.count, name: Support Calls}], showLabels: false, + showValues: false, maxValue: 1, minValue: 1, unpinAxis: true, tickDensity: default, + tickDensityCustom: 5, type: linear}] + show_y_axis_labels: true + show_y_axis_ticks: true + y_axis_tick_density: default + y_axis_tick_density_custom: 5 + show_x_axis_label: false + show_x_axis_ticks: true + y_axis_scale_mode: linear + x_axis_reversed: false + y_axis_reversed: false + plot_size_by_field: false + trellis: '' + stacking: '' + limit_displayed_rows: false + legend_position: center + series_types: {} + point_style: circle + series_labels: + omni_channel_transactions.transaction_count: Purchases + omni_channel_events.session_count: Web Visits + omni_channel_support_calls.count: Support Calls + show_value_labels: false + label_density: 25 + x_axis_scale: auto + x_axis_datetime_label: "%b-%y" + y_axis_combined: true + show_null_points: false + type: looker_scatter + listen: + - ID: c360.customer_id + - ID: c360.customer_id + - ID: omni_channel_support_calls.client_id + row: 8 + col: 12 + width: 12 + height: 7 + filters: + - name: ID + title: ID + type: field_filter + default_value: '81359' + allow_multiple_values: true + required: false + model: omni_channel + explore: customer_transaction_fact + listens_to_filters: [] + field: customer_event_fact.customer_id diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_event_fact.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_event_fact.view.lkml new file mode 100644 index 0000000000000..80fb4f4c57ed0 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_event_fact.view.lkml @@ -0,0 +1,34 @@ +# If necessary, uncomment the line below to include explore_source. +include: "/explores/*.lkml" + +view: customer_event_fact { + derived_table: { + datagroup_trigger: new_day + explore_source: omni_channel_events_base { + column: customer_id {} + column: acquisition_source {} + column: cart_adds {} + column: event_count {} + column: purchases {} + column: session_count {} + } + } + dimension: customer_id { + type: number + } + dimension: acquisition_source { + type: string + } + dimension: cart_adds { + type: number + } + dimension: event_count { + type: number + } + dimension: purchases { + type: number + } + dimension: session_count { + type: number + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_support_fact.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_support_fact.view.lkml new file mode 100644 index 0000000000000..b84dd421aaee7 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_support_fact.view.lkml @@ -0,0 +1,18 @@ +# If necessary, uncomment the line below to include explore_source. +include: "/models/omni_channel.model.lkml" + +view: customer_support_fact { + derived_table: { + datagroup_trigger: new_day + explore_source: omni_channel_support_calls { + column: client_id {} + column: count {} + } + } + dimension: client_id { + type: number + } + dimension: count { + type: number + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_transaction_fact.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_transaction_fact.view.lkml new file mode 100644 index 0000000000000..103cd3989ddc8 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customer_transaction_fact.view.lkml @@ -0,0 +1,71 @@ +# If necessary, uncomment the line below to include explore_source. +include: "/models/omni_channel.model.lkml" + +view: customer_transaction_fact { + derived_table: { + datagroup_trigger: new_day + explore_source: omni_channel_transactions { + column: customer_id {} + column: curbside_transaction_count {} + column: first_purchase {} + column: instore_transaction_count {} + column: l180_transaction_count {} + column: l30_transaction_count {} + column: l360_transaction_count {} + column: l90_transaction_count {} + column: last_purchase {} + column: online_transaction_count {} + column: total_sales { field: omni_channel_transactions__transaction_details.total_sales } + column: item_count { field: omni_channel_transactions__transaction_details.item_count } + column: transaction_count {} + column: discounted_transaction_count {} + column: return_count {} + } + } + dimension: customer_id { + value_format_name: id + type: number + } + dimension: curbside_transaction_count { + type: number + } + dimension: first_purchase { + type: string + } + dimension: instore_transaction_count { + type: number + } + dimension: l180_transaction_count { + type: number + } + dimension: l30_transaction_count { + type: number + } + dimension: l360_transaction_count { + type: number + } + dimension: l90_transaction_count { + type: number + } + dimension: last_purchase { + type: string + } + dimension: online_transaction_count { + type: number + } + dimension: total_sales { + type: number + } + dimension: item_count { + type: number + } + dimension: transaction_count { + type: number + } + dimension: discounted_transaction_count { + type: number + } + dimension: return_count { + type: number + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customers.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customers.view.lkml new file mode 100644 index 0000000000000..03373e707a045 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/customers.view.lkml @@ -0,0 +1,193 @@ +view: customers { + sql_table_name: `looker-private-demo.retail.customers` ;; + drill_fields: [id] + + dimension: id { + tags: ["google-ads-uid"] # unique ID for audience builder extension + value_format_name: id + primary_key: yes + type: number + sql: ${TABLE}.ID ;; + } + + dimension: address { + tags: ["google-ads-street"] + type: string + sql: ${TABLE}.address ;; + group_label: "Address Info" + link: { + url: "/dashboards/3OmU04xQdYtSVeq2Kf2GIj?Address=%22{{value | encode_uri}}" + label: "Drill into this address" + icon_url: "https://img.icons8.com/cotton/2x/worldwide-location.png" + } + } + + dimension: address_street_view { + type: string + group_label: "Address Info" + sql: ${address} ;; + html: = 30000 + ;; + } + + dimension: id { + primary_key: yes + type: number + sql: ${TABLE}.ID ;; + } + + dimension: browser { + type: string + sql: ${TABLE}.BROWSER ;; + } + + dimension_group: created { + type: time + timeframes: [ + raw, + time, + date, + week, + month, + quarter, + year + ] + sql: ${TABLE}.CREATED_AT ;; + } + + dimension: customer_id { + type: number + sql: ${TABLE}.CUSTOMER_ID ;; + } + + dimension: event_type { + type: string + sql: ${TABLE}.EVENT_TYPE ;; + } + + dimension: ip_address { + type: string + sql: ${TABLE}.IP_ADDRESS ;; + } + + dimension: os { + type: string + sql: ${TABLE}.OS ;; + } + + dimension: sequence_number { + type: number + sql: ${TABLE}.SEQUENCE_NUMBER ;; + } + + dimension: session_id { + type: string + sql: ${TABLE}.SESSION_ID ;; + } + + dimension: traffic_source { + type: string + sql: ${TABLE}.TRAFFIC_SOURCE ;; + } + + dimension: uri { + type: string + sql: ${TABLE}.URI ;; + } + + measure: event_count { + type: count + } + + measure: cart_adds { + filters: [event_type: "Cart"] + type: count + } + + measure: purchases { + filters: [event_type: "Purchase"] + type: count + } + + measure: acquisition_source { + type: string + sql: SPLIT(MIN(CONCAT(CAST(${TABLE}.CREATED_AT as string),'|',${TABLE}.TRAFFIC_SOURCE)),'|')[OFFSET(1)] ;; + } + + measure: session_count { + type: count_distinct + sql: ${session_id} ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/omni_channel_support_calls.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/omni_channel_support_calls.view.lkml new file mode 100644 index 0000000000000..5d3bb69d85daa --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/omni_channel_support_calls.view.lkml @@ -0,0 +1,135 @@ +view: omni_channel_support_calls { + derived_table: { + datagroup_trigger: new_day + sql: + SELECT * except(client_id) + , cast(round(rand()*60000+25000,0) as INT64) as client_id + FROM `looker-private-demo.call_center.transcript_with_messages` + ;; + } + + dimension: agent_id { + type: string + sql: ${TABLE}.agent_id ;; + } + + dimension: client_id { + type: number + sql: ${TABLE}.client_id ;; + } + + dimension_group: conversation_end { + type: time + timeframes: [ + raw, + time, + date, + week, + month, + quarter, + year + ] + sql: ${TABLE}.conversation_end_at ;; + } + + dimension: conversation_id { + type: string + sql: ${TABLE}.conversation_id ;; + } + + dimension_group: conversation_start { + type: time + timeframes: [ + raw, + time, + date, + week, + month, + quarter, + year + ] + sql: ${TABLE}.conversation_start_at ;; + } + + dimension: messages { + hidden: yes + sql: ${TABLE}.messages ;; + } + + dimension: resolved_on_call { + type: string + sql: ${TABLE}.resolved_on_call ;; + } + + measure: count { + type: count + drill_fields: [] + } +} + +view: omni_channel_support_calls__messages { + dimension: answer_end { + type: number + sql: ${TABLE}.answer_end ;; + } + + dimension: answer_start { + type: number + sql: ${TABLE}.answer_start ;; + } + + dimension: intent_id { + type: string + sql: ${TABLE}.intent_id ;; + } + + dimension: issue_subtopic { + type: string + sql: ${TABLE}.issue_subtopic ;; + } + + dimension: issue_topic { + type: string + sql: ${TABLE}.issue_topic ;; + } + + dimension: live_agent_speaking { + type: yesno + sql: ${TABLE}.live_agent_speaking ;; + } + + dimension: message_id { + type: string + sql: ${TABLE}.message_id ;; + } + + dimension: response { + type: string + sql: ${TABLE}.response ;; + } + + dimension: row { + type: number + sql: ${TABLE}.row ;; + } + + dimension: sentiment { + type: number + sql: ${TABLE}.sentiment ;; + } + + dimension: user_end { + type: number + sql: ${TABLE}.user_end ;; + } + + dimension: user_question { + type: string + sql: ${TABLE}.user_question ;; + } + + dimension: user_start { + type: number + sql: ${TABLE}.user_start ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/omni_channel_transactions.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/omni_channel_transactions.view.lkml new file mode 100644 index 0000000000000..915933753896b --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/omni_channel_transactions.view.lkml @@ -0,0 +1,533 @@ +view: omni_channel_transactions { + derived_table: { + datagroup_trigger: new_day + sql: + -- adding comment to trigger pdt rebuild + SELECT * + FROM + ( + SELECT + purchase_channel, + fulfillment_channel, + transaction_date, + shipped_date, + delivered_date, + returned_date, + transaction_id, + store_name, + store_latitude, + store_longitude, + store_state, + Store_sq_ft, + CASE WHEN rand() < 0.5 THEN null + WHEN rand() < 0.5 AND fulfillment_channel = 'Delivery' THEN 'Free Delivery' + WHEN rand() < 0.5 THEN 'Single Item Discount' + WHEN rand() < 0.5 THEN 'BOGO' + WHEN rand() < 0.5 THEN 'Free Item' + WHEN rand() < 0.5 THEN 'Frequency Discount' + WHEN rand() < 0.5 THEN 'Multi-Item Discount' + ELSE 'Order Discount' + END AS offer_type, + CAST(ROUND(RAND()*85225 + 1.0,0) AS INT64) as customer_id, + ARRAY_AGG(STRUCT(gross_margin,sale_price,product_category,product_brand,product_department,product_area,product_name,product_sku)) as transaction_details + FROM + ( + SELECT + IF(channels.NAME in ('In-store Self-checkout','In-store Tills'),'In-store','Online') AS purchase_channel, + CASE + WHEN channels.NAME = 'In-store Self-checkout' THEN 'Self Checkout' + WHEN channels.NAME = 'In-store Tills' THEN 'Assisted Checkout' + WHEN channels.NAME in ('Click and Collect','Online') THEN 'In-store Pickup' + END AS fulfillment_channel, + transactions__line_items.gross_margin AS gross_margin, + transactions__line_items.sale_price * 1.2 AS sale_price, + transactions.transaction_timestamp AS transaction_date, + null as delivered_date, + null as shipped_date, + if(rand() < 0.02, TIMESTAMP_ADD(transaction_timestamp, INTERVAL cast(round(rand()*30,0) as INT64) DAY),null) as returned_date, + transactions.transaction_id AS transaction_id, + stores.NAME AS store_name, + stores.LATITUDE as store_latitude, + stores.LONGITUDE as store_longitude, + stores.State AS store_state, + stores.sq_ft AS store_sq_ft, + products.CATEGORY AS product_category, + products.BRAND AS product_brand, + CASE + WHEN products.CATEGORY IN ('Accessories', 'Swim', 'Socks', 'Socks & Hosiery', 'Leggings', 'Plus', 'Sleep & Lounge') THEN 'Accessories' + WHEN products.CATEGORY IN ('Jeans', 'Tops & Tees', 'Shorts', 'Sweaters', 'Underwear', 'Intimates', 'Jumpsuits & Rompers', 'Maternity') THEN 'Casual Wear' + WHEN products.CATEGORY IN ('Dresses', 'Skirts', 'Blazers & Jackets', 'Pants', 'Pants & Capris', 'Suits') THEN 'Formal Wear' + WHEN products.CATEGORY IN ('Clothing Sets', 'Suits & Sport Coats', 'Outerwear & Coats', 'Fashion Hoodies & Sweatshirts', 'Suits', 'Active') THEN 'Outerwear' + ELSE 'Other' + END AS product_area, + products.NAME AS product_name, + products.SKU AS product_sku, + products.DEPARTMENT AS product_department, + FROM `looker-private-demo.retail.transaction_detail` AS transactions + LEFT JOIN UNNEST(( + transactions.line_items + )) as transactions__line_items + LEFT JOIN `looker-private-demo.retail.products` AS products ON products.ID = transactions__line_items.product_id + LEFT JOIN `looker-private-demo.retail.us_stores` AS stores ON stores.ID = transactions.store_id + LEFT JOIN `looker-private-demo.retail.channels` AS channels ON channels.ID = transactions.channel_id + + UNION ALL + + SELECT + IF(channels.NAME in ('In-store Self-checkout','In-store Tills'),'In-store','Online') AS purchase_channel, + CASE + WHEN channels.NAME = 'In-store Self-checkout' THEN 'Self Checkout' + WHEN channels.NAME = 'In-store Tills' THEN 'Assisted Checkout' + WHEN channels.NAME in ('Click and Collect','Online') THEN 'In-store Pickup' + END AS fulfillment_channel, + Transactions__line_items.gross_margin * 1.1 AS gross_margin, + transactions__line_items.sale_price AS sale_price, + TIMESTAMP_SUB(CURRENT_TIMESTAMP, INTERVAL CAST(ROUND(RAND()*388800,0) AS INT64) MINUTE) AS transaction_date, + null as delivered_date, + null as shipped_date, + null as returned_date, + transactions.transaction_id AS transaction_id, + stores.NAME AS store_name, + stores.LATITUDE as store_latitude, + stores.LONGITUDE as store_longitude, + stores.State AS store_state, + stores.sq_ft AS store_sq_ft, + products.CATEGORY AS product_category, + products.BRAND AS product_brand, + CASE + WHEN products.CATEGORY IN ('Accessories', 'Swim', 'Socks', 'Socks & Hosiery', 'Leggings', 'Plus', 'Sleep & Lounge') THEN 'Accessories' + WHEN products.CATEGORY IN ('Jeans', 'Tops & Tees', 'Shorts', 'Sweaters', 'Underwear', 'Intimates', 'Jumpsuits & Rompers', 'Maternity') THEN 'Casual Wear' + WHEN products.CATEGORY IN ('Dresses', 'Skirts', 'Blazers & Jackets', 'Pants', 'Pants & Capris', 'Suits') THEN 'Formal Wear' + WHEN products.CATEGORY IN ('Clothing Sets', 'Suits & Sport Coats', 'Outerwear & Coats', 'Fashion Hoodies & Sweatshirts', 'Suits', 'Active') THEN 'Outerwear' + ELSE 'Other' + END AS product_area, + products.NAME AS product_name, + products.SKU AS product_sku, + products.DEPARTMENT AS product_department, + FROM `looker-private-demo.retail.transaction_detail` AS transactions + LEFT JOIN UNNEST(( + transactions.line_items + )) as transactions__line_items + LEFT JOIN `looker-private-demo.retail.products` AS products ON products.ID = transactions__line_items.product_id + LEFT JOIN `looker-private-demo.retail.us_stores` AS stores ON stores.ID = transactions.store_id + LEFT JOIN `looker-private-demo.retail.channels` AS channels ON channels.ID = transactions.channel_id + WHERE channels.NAME not in ('In-store Self-checkout','In-store Tills') + + UNION ALL + + SELECT + IF(channels.NAME in ('In-store Self-checkout','In-store Tills'),'In-store','Online') AS purchase_channel, + CASE + WHEN channels.NAME = 'In-store Self-checkout' THEN 'Self Checkout' + WHEN channels.NAME = 'In-store Tills' THEN 'Assisted Checkout' + WHEN channels.NAME in ('Click and Collect','Online') THEN 'In-store Pickup' + END AS fulfillment_channel, + transactions__line_items.gross_margin AS gross_margin, + transactions__line_items.sale_price * 1.2 AS sale_price, + transactions.transaction_timestamp AS transaction_date, + null as delivered_date, + null as shipped_date, + null as returned_date, + transactions.transaction_id AS transaction_id, + stores.NAME AS store_name, + stores.LATITUDE as store_latitude, + stores.LONGITUDE as store_longitude, + stores.State AS store_state, + stores.sq_ft AS store_sq_ft, + products.CATEGORY AS product_category, + products.BRAND AS product_brand, + CASE + WHEN products.CATEGORY IN ('Accessories', 'Swim', 'Socks', 'Socks & Hosiery', 'Leggings', 'Plus', 'Sleep & Lounge') THEN 'Accessories' + WHEN products.CATEGORY IN ('Jeans', 'Tops & Tees', 'Shorts', 'Sweaters', 'Underwear', 'Intimates', 'Jumpsuits & Rompers', 'Maternity') THEN 'Casual Wear' + WHEN products.CATEGORY IN ('Dresses', 'Skirts', 'Blazers & Jackets', 'Pants', 'Pants & Capris', 'Suits') THEN 'Formal Wear' + WHEN products.CATEGORY IN ('Clothing Sets', 'Suits & Sport Coats', 'Outerwear & Coats', 'Fashion Hoodies & Sweatshirts', 'Suits', 'Active') THEN 'Outerwear' + ELSE 'Other' + END AS product_area, + products.NAME AS product_name, + products.SKU AS product_sku, + products.DEPARTMENT AS product_department, + FROM `looker-private-demo.retail.transaction_detail` AS transactions + LEFT JOIN UNNEST(( + transactions.line_items + )) as transactions__line_items + LEFT JOIN `looker-private-demo.retail.products` AS products ON products.ID = transactions__line_items.product_id + LEFT JOIN `looker-private-demo.retail.us_stores` AS stores ON stores.ID = transactions.store_id + LEFT JOIN `looker-private-demo.retail.channels` AS channels ON channels.ID = transactions.channel_id + WHERE channels.NAME in ('In-store Self-checkout','In-store Tills') AND ((transactions.transaction_timestamp < (TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP('2020-03-14 00:00:00')), 'America/Los_Angeles')))) + + UNION ALL + + SELECT + 'Online' as purchase_channel, + 'Delivery' as fulfillment_channel, + (order_items.sale_price - inventory_items.cost) * 1.1 AS gross_magin, + order_items.sale_price AS sale_price, + order_items.created_at as transaction_date, + order_items.delivered_at as delivery_date, + order_items.shipped_at as shipped_date, + order_items.returned_at as returned_date, + order_items.order_id AS transaction_id, + null as store_name, + null as store_latitude, + null as store_longitude, + null AS store_state, + null AS store_sq_ft, + TRIM(products.category) AS product_category, + TRIM(products.brand) AS products_brand, + CASE + WHEN products.CATEGORY IN ('Accessories', 'Swim', 'Socks', 'Socks & Hosiery', 'Leggings', 'Plus', 'Sleep & Lounge') THEN 'Accessories' + WHEN products.CATEGORY IN ('Jeans', 'Tops & Tees', 'Shorts', 'Sweaters', 'Underwear', 'Intimates', 'Jumpsuits & Rompers', 'Maternity') THEN 'Casual Wear' + WHEN products.CATEGORY IN ('Dresses', 'Skirts', 'Blazers & Jackets', 'Pants', 'Pants & Capris', 'Suits') THEN 'Formal Wear' + WHEN products.CATEGORY IN ('Clothing Sets', 'Suits & Sport Coats', 'Outerwear & Coats', 'Fashion Hoodies & Sweatshirts', 'Suits', 'Active') THEN 'Outerwear' + ELSE 'Other' + END AS product_area, + TRIM(products.name) AS product_name, + products.sku AS product_sku, + TRIM(products.department) AS products_department + FROM looker-private-demo.ecomm.order_items AS order_items + FULL OUTER JOIN looker-private-demo.ecomm.inventory_items AS inventory_items ON inventory_items.id = order_items.inventory_item_id + LEFT JOIN looker-private-demo.ecomm.users AS users ON order_items.user_id = users.id + LEFT JOIN looker-private-demo.ecomm.products AS products ON products.id = inventory_items.product_id + WHERE users.country = 'USA' + + UNION ALL + + SELECT + 'Online' as purchase_channel, + 'Delivery' as fulfillment_channel, + (order_items.sale_price - inventory_items.cost) * 1.1 AS gross_magin, + order_items.sale_price AS sale_price, + order_items.created_at as transaction_date, + order_items.delivered_at as delivery_date, + order_items.shipped_at as shipped_date, + order_items.returned_at as returned_date, + order_items.order_id AS transaction_id, + null as store_name, + null as store_latitude, + null as store_longitude, + null AS store_state, + null AS store_sq_ft, + TRIM(products.category) AS product_category, + TRIM(products.brand) AS products_brand, + CASE + WHEN products.CATEGORY IN ('Accessories', 'Swim', 'Socks', 'Socks & Hosiery', 'Leggings', 'Plus', 'Sleep & Lounge') THEN 'Accessories' + WHEN products.CATEGORY IN ('Jeans', 'Tops & Tees', 'Shorts', 'Sweaters', 'Underwear', 'Intimates', 'Jumpsuits & Rompers', 'Maternity') THEN 'Casual Wear' + WHEN products.CATEGORY IN ('Dresses', 'Skirts', 'Blazers & Jackets', 'Pants', 'Pants & Capris', 'Suits') THEN 'Formal Wear' + WHEN products.CATEGORY IN ('Clothing Sets', 'Suits & Sport Coats', 'Outerwear & Coats', 'Fashion Hoodies & Sweatshirts', 'Suits', 'Active') THEN 'Outerwear' + ELSE 'Other' + END AS product_area, + TRIM(products.name) AS product_name, + products.sku AS product_sku, + TRIM(products.department) AS products_department + FROM looker-private-demo.ecomm.order_items AS order_items + FULL OUTER JOIN looker-private-demo.ecomm.inventory_items AS inventory_items ON inventory_items.id = order_items.inventory_item_id + LEFT JOIN looker-private-demo.ecomm.users AS users ON order_items.user_id = users.id + LEFT JOIN looker-private-demo.ecomm.products AS products ON products.id = inventory_items.product_id + WHERE + (order_items.created_at >= TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP('2020-03-13 00:00:00')), 'America/Los_Angeles')) AND users.country = 'USA' + ) + GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13 + ) + WHERE (customer_id <= 65000 and purchase_channel = 'In-store') OR (customer_id >= 60000 and fulfillment_channel = 'Delivery') OR (customer_id >= 45000 and customer_id <= 80000 and fulfillment_channel = 'In-store Pickup') + ;; + } + + dimension: reporting_period { + type: string + sql: CASE + WHEN EXTRACT(YEAR from ${transaction_raw}) = EXTRACT(YEAR from CURRENT_TIMESTAMP()) + AND ${transaction_raw} < CURRENT_TIMESTAMP() + THEN 'This Year to Date' + + WHEN EXTRACT(YEAR from ${transaction_raw}) + 1 = EXTRACT(YEAR from CURRENT_TIMESTAMP()) + AND CAST(FORMAT_TIMESTAMP('%j', ${transaction_raw}) AS INT64) <= CAST(FORMAT_TIMESTAMP('%j', CURRENT_TIMESTAMP()) AS INT64) + THEN 'Last Year to Date' + + END + ;; + } + + measure: customer_count { + type: count_distinct + sql: ${customer_id} ;; + } + + measure: first_purchase { + type: string + sql: min(${transaction_date}) ;; + } + + measure: last_purchase { + type: string + sql: max(${transaction_date}) ;; + } + + measure: transaction_count { + type: count + } + + measure: l30_transaction_count { + type: count + filters: [last_30: "Yes"] + } + + measure: l90_transaction_count { + type: count + filters: [last_90: "Yes"] + } + + measure: l180_transaction_count { + type: count + filters: [last_180: "Yes"] + } + + measure: l360_transaction_count { + type: count + filters: [last_360: "Yes"] + } + + measure: return_count { + type: count + filters: [is_returned: "Yes"] + } + + measure: discounted_transaction_count { + type: count + filters: [is_discounted: "Yes"] + } + + measure: online_transaction_count { + filters: [purchase_channel: "Online"] + type: count + } + + measure: curbside_transaction_count { + type: count + filters: [fulfillment_channel: "In-store Pickup"] + } + + measure: instore_transaction_count { + filters: [purchase_channel: "In-store"] + type: count + } + + dimension: is_discounted { + type: yesno + sql: ${offer_type} is not null ;; + } + + dimension: is_returned { + type: yesno + sql: ${returned_raw} is not null ;; + } + + dimension: last_30 { + type: yesno + sql: ${transaction_raw} >= ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -29 DAY)), 'America/Los_Angeles'))) AND ${transaction_raw} < ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -29 DAY), INTERVAL 30 DAY)), 'America/Los_Angeles'))) ;; + } + + dimension: last_90 { + type: yesno + sql: ${transaction_raw} >= ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -89 DAY)), 'America/Los_Angeles'))) AND ${transaction_raw} < ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -89 DAY), INTERVAL 90 DAY)), 'America/Los_Angeles'))) ;; + } + + dimension: last_180 { + type: yesno + sql: ${transaction_raw} >= ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -179 DAY)), 'America/Los_Angeles'))) AND ${transaction_raw} < ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -179 DAY), INTERVAL 180 DAY)), 'America/Los_Angeles'))) ;; + } + + dimension: last_360 { + type: yesno + sql: ${transaction_raw} >= ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -359 DAY)), 'America/Los_Angeles'))) AND ${transaction_raw} < ((TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', TIMESTAMP_ADD(TIMESTAMP_ADD(TIMESTAMP_TRUNC(TIMESTAMP(FORMAT_TIMESTAMP('%F %H:%M:%E*S', CURRENT_TIMESTAMP(), 'America/Los_Angeles')), DAY), INTERVAL -359 DAY), INTERVAL 360 DAY)), 'America/Los_Angeles'))) ;; + } + + dimension: customer_id { + value_format_name: id + type: number + sql: ${TABLE}.customer_id ;; + } + + dimension_group: delivered { + type: time + timeframes: [ + raw, + date, + week, + month, + quarter, + year + ] + convert_tz: no + datatype: date + sql: ${TABLE}.delivered_date ;; + } + + dimension: fulfillment_channel { + type: string + sql: ${TABLE}.fulfillment_channel ;; + } + + dimension: offer_type { + type: string + sql: COALESCE(${TABLE}.offer_type,'None') ;; + } + + dimension: purchase_channel { + type: string + sql: ${TABLE}.purchase_channel ;; + } + + dimension_group: returned { + type: time + timeframes: [ + raw, + time, + date, + week, + month, + quarter, + year + ] + sql: ${TABLE}.returned_date ;; + } + + dimension_group: shipped { + type: time + timeframes: [ + raw, + date, + week, + month, + quarter, + year + ] + convert_tz: no + datatype: date + sql: ${TABLE}.shipped_date ;; + } + + dimension: store_latitude { + type: number + sql: ${TABLE}.store_latitude ;; + } + + dimension: store_longitude { + type: number + sql: ${TABLE}.store_longitude ;; + } + + dimension: store_name { + type: string + sql: ${TABLE}.store_name ;; + } + + dimension: store_sq_ft { + type: number + sql: ${TABLE}.Store_sq_ft ;; + } + + dimension: store_state { + type: string + sql: ${TABLE}.store_state ;; + } + + dimension_group: transaction { + type: time + timeframes: [ + raw, + time, + date, + week, + month, + month_name, + quarter, + year + ] + sql: ${TABLE}.transaction_date ;; + } + + dimension: transaction_details { + hidden: yes + sql: ${TABLE}.transaction_details ;; + } + + dimension: transaction_id { + primary_key: yes + type: number + sql: ${TABLE}.transaction_id ;; + } +} + +view: omni_channel_transactions__transaction_details { + dimension: gross_margin { + type: number + sql: ${TABLE}.gross_margin ;; + } + + dimension: product_area { + type: string + sql: ${TABLE}.product_area ;; + } + + dimension: product_brand { + type: string + sql: ${TABLE}.product_brand ;; + } + + dimension: product_category { + bypass_suggest_restrictions: yes + suggestable: yes + full_suggestions: yes + type: string + sql: ${TABLE}.product_category ;; + } + + dimension: product_department { + type: string + sql: ${TABLE}.product_department ;; + } + + dimension: product_name { + type: string + sql: ${TABLE}.product_name ;; + } + + dimension: product_sku { + primary_key: yes + type: string + sql: ${TABLE}.product_sku ;; + } + + dimension: sale_price { + value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + type: number + sql: ${TABLE}.sale_price ;; + } + + measure: total_profit { + value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + type: sum + sql: ${gross_margin} ;; + } + + measure: total_sales { + value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + type: sum + sql: ${sale_price} ;; + } + + measure: profit_margin { + type: number + sql: ${total_profit} / nullif(${total_sales},0) ;; + value_format_name: percent_2 + } + + measure: recommended_products { + type: list + list_field: product_name + } + + measure: item_count { + type: count + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/retail_clv_predict.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/retail_clv_predict.view.lkml new file mode 100644 index 0000000000000..81bc806e47c81 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/omni_channel/retail_clv_predict.view.lkml @@ -0,0 +1,51 @@ +view: retail_clv_predict { + sql_table_name: `retail_ltv.lpd_retail_clv_predict_tbl` ;; + drill_fields: [customer_id] + + dimension: customer_id { + primary_key: yes + type: number + sql: ${TABLE}.id ;; + } + + dimension: email { + type: string + sql: ${TABLE}.email ;; + } + + dimension: first_name { + type: string + sql: ${TABLE}.first_name ;; + } + + dimension: last_name { + type: string + sql: ${TABLE}.last_name ;; + } + + dimension: monetary_future { + type: number + sql: ${TABLE}.monetary_future ;; + } + + dimension: monetary_so_far { + type: number + sql: ${TABLE}.monetary_so_far ;; + } + + dimension: predicted_clv { + alias: [c360.predicted_clv] + value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + label: "Predicted CLV" + type: number + sql: ${TABLE}.monetary_predicted ;; + } + + measure: average_clv { + alias: [c360.average_clv] + value_format:"[>=1000000]$0.0,,\"M\";[>=1000]$0.0,\"K\";$0.0" + label: "Average CLV" + type: average + sql: ${predicted_clv} ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/affinity_analysis.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/affinity_analysis.view.lkml new file mode 100644 index 0000000000000..1ab87d09b0161 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/affinity_analysis.view.lkml @@ -0,0 +1,643 @@ +view: order_items_base { + derived_table: { + #### TO DO: Create a transaction-item-level table with the following columns: + # -Transaction ID + # -Transaction timestamp + # -Base-level item ID and name + # -ID and name column for each of your hierarchy levels + # -[optional] Customer ID, keep as null otherwise + # -Transaction gross sale amount + # -Transaction margin amount + #### If this info is in different tables that you're joining together (e.g. a trx table to a product hierarchy, as in the example below), you may want to persist the joined table as a PDT if feasible + #### Make sure to not change the "AS [...]" in the query below, as these column names are used later on + sql: SELECT + transactions.transaction_id + ,transactions.transaction_timestamp AS order_created_time + ,products.name as product_id + ,products.name AS product_name + ,products.brand AS brand_id + ,products.brand AS brand_name + ,products.category AS category_id + ,products.category AS category_name + ,transactions.customer_id AS user_id + ,line_items.sale_price AS sale_amt + ,line_items.gross_margin AS margin_amt + FROM ${transactions.SQL_TABLE_NAME} AS transactions + LEFT JOIN UNNEST(transactions.line_items) line_items + LEFT JOIN ${products.SQL_TABLE_NAME} AS products + ON products.ID = line_items.product_id + LEFT JOIN ${stores.SQL_TABLE_NAME} AS stores + ON stores.ID = transactions.store_id + WHERE 1=1 + AND {% condition order_purchase_affinity.affinity_timeframe %} transactions.transaction_timestamp {% endcondition %} + AND {% condition order_purchase_affinity.store_name %} stores.name {% endcondition %};; + #### Uncomment the following line if you'd like to persist this table for faster query-time performance +# datagroup_trigger: daily + } + + #### TO DO: Edit the values of this parameter according to the hierarchy levels used in the base table above + parameter: product_level { + view_label: "Item Affinity" + type: unquoted + allowed_value: { + label: "Product" + value: "product" + } + allowed_value: { + label: "Brand" + value: "brand" + } + allowed_value: { + label: "Category" + value: "category" + } + } +} + + +view: order_items { + derived_table: { + sql: SELECT + CONCAT(CAST(transaction_id AS STRING),'_',{% parameter order_items_base.product_level %}_id) AS id + , transaction_id AS order_id + , {% parameter order_items_base.product_level %}_id as product_id + , {% parameter order_items_base.product_level %}_name AS product + , category_name AS product_category + , user_id as user_id + , MIN(order_created_time) AS created_at + , SUM(sale_amt) AS sale_price + , SUM(margin_amt) AS margin + FROM ${order_items_base.SQL_TABLE_NAME} + GROUP BY 1,2,3,4,5,6;; + } +} + +view: orders { + derived_table: { + sql: select + oi.order_id as id + , MIN(oi.created_at) as created_at + ,COUNT(distinct product_id) as distinct_products + FROM ${order_items.SQL_TABLE_NAME} oi + GROUP BY oi.order_id ;; + } +} + +view: order_product { + derived_table: { + sql: + SELECT oi.id as order_item_id + , o.id as order_id + , o.created_at + , oi.product as product + , oi.product_category + FROM ${order_items.SQL_TABLE_NAME} oi + JOIN ${orders.SQL_TABLE_NAME} o ON o.id = oi.order_id + GROUP BY oi.id,o.id, o.created_at, oi.product, oi.product_category + ;; + } +} + +view: order_metrics { + derived_table: { + sql: SELECT oi.id as order_item_id + , SUM(oi.sale_price) over (partition by oi.order_id) as basket_sales + , SUM(oi.margin) over (partition by oi.order_id) as basket_margin + FROM ${order_items.SQL_TABLE_NAME} oi;; + } +} + +view: total_order_product { + derived_table: { + sql: + SELECT oi.product as product + , count(distinct o.id) as product_order_count -- count of orders with product, not total order items + , SUM(oi.sale_price) as product_sales + , SUM(oi.margin) as product_margin + , SUM(om.basket_sales) as basket_sales + , SUM(om.basket_margin) as basket_margin + , COUNT(distinct (CASE WHEN o.distinct_products=1 THEN o.id ELSE NULL END)) as product_count_purchased_alone + , COUNT(distinct (CASE WHEN oi.user_id IS NOT NULL THEN o.id ELSE NULL END)) as product_count_purchased_by_loyalty_customer + FROM ${order_items.SQL_TABLE_NAME} oi + JOIN ${order_metrics.SQL_TABLE_NAME} om ON oi.id = om.order_item_id + JOIN ${orders.SQL_TABLE_NAME} o ON o.id = oi.order_id + WHERE {% condition order_purchase_affinity.affinity_timeframe %} o.created_at {% endcondition %} + GROUP BY oi.product + ;; + } +} + +view: product_loyal_users { + derived_table: { + sql: SELECT + oi.user_id + from ${order_items.SQL_TABLE_NAME} oi + WHERE {% condition order_purchase_affinity.affinity_timeframe %} oi.created_at {% endcondition %} + GROUP BY oi.user_id + HAVING COUNT(distinct oi.product) =1;; + } +} + +view: orders_by_product_loyal_users { + derived_table: { + sql: + SELECT + oi.product as product, + COUNT (distinct oi.order_id) as orders_by_loyal_customers + FROM ${order_items.SQL_TABLE_NAME} oi + INNER JOIN ${product_loyal_users.SQL_TABLE_NAME} plu on oi.user_id = plu.user_id + WHERE {% condition order_purchase_affinity.affinity_timeframe %} oi.created_at {% endcondition %} + GROUP BY oi.product + ;; + } +} + +view: total_orders { + derived_table: { + sql: + + SELECT count(*) as count + FROM ${orders.SQL_TABLE_NAME} + WHERE {% condition order_purchase_affinity.affinity_timeframe %} created_at {% endcondition %} + ;; + } + + dimension: count { + type: number + sql: ${TABLE}.count ;; + view_label: "Item Affinity" + label: "Total Order Count" + } +} + +view: order_purchase_affinity { + derived_table: { + sql: SELECT product_a + , product_b + , product_a_category + , product_b_category + , joint_order_count + , top1.product_order_count as product_a_order_count -- total number of orders with product A in them + , top2.product_order_count as product_b_order_count -- total number of orders with product B in them + , top1.product_count_purchased_alone as product_a_count_purchased_alone + , top2.product_count_purchased_alone as product_b_count_purchased_alone + , top1.product_count_purchased_by_loyalty_customer as product_a_count_purchased_by_loyalty_customer + , top2.product_count_purchased_by_loyalty_customer as product_b_count_purchased_by_loyalty_customer + , IFNULL(loy1.orders_by_loyal_customers,0) as product_a_count_orders_by_exclusive_customers + , IFNULL(loy2.orders_by_loyal_customers,0) as product_b_count_orders_by_exclusive_customers + , top1.product_sales as product_a_product_sales + , top2.product_sales as product_b_product_sales + , top1.product_margin as product_a_product_margin + , top2.product_margin as product_b_product_margin + , top1.basket_sales as product_a_basket_sales + , top2.basket_sales as product_b_basket_sales + , top1.basket_margin as product_a_basket_margin + , top2.basket_margin as product_b_basket_margin + FROM ( + SELECT op1.product as product_a + , op2.product as product_b + , op1.product_category AS product_a_category + , op2.product_category AS product_b_category + , count(*) as joint_order_count + FROM ${order_product.SQL_TABLE_NAME} as op1 + JOIN ${order_product.SQL_TABLE_NAME} op2 + ON op1.order_id = op2.order_id + AND op1.order_item_id <> op2.order_item_id -- ensures we don't match on the same order items in the same order, which would corrupt our frequency metrics + GROUP BY 1,2,3,4 + ) as prop + JOIN ${total_order_product.SQL_TABLE_NAME} as top1 ON prop.product_a = top1.product + JOIN ${total_order_product.SQL_TABLE_NAME} as top2 ON prop.product_b = top2.product + LEFT JOIN ${orders_by_product_loyal_users.SQL_TABLE_NAME} as loy1 ON prop.product_a = loy1.product + LEFT JOIN ${orders_by_product_loyal_users.SQL_TABLE_NAME} as loy2 ON prop.product_a = loy2.product + ;; + } + + ##### Filters ##### + + filter: affinity_timeframe { + type: date + } + + #### TO DO: [optional] add any store or other level filters here, or remove this one + + filter: store_name { + type: string + suggest_explore: transactions + suggest_dimension: stores.name + } + + ##### Dimensions ##### + + dimension: product_a { + group_label: "Product A" + type: string + sql: ${TABLE}.product_a ;; + link: { + label: "Focus on {{rendered_value}}" + #### TO DO: Replace "/3" with id of the [...] dashboards + url: "/dashboards/TSGWx3mvSYoyNKLKLDssXW?Focus%20Product={{ value | encode_uri }}&Product%20Level={{ _filters['order_items_base.product_level'] | url_encode }}&Analysis%20Timeframe={{ _filters['order_purchase_affinity.affinity_timeframe'] | url_encode }}&Store%20Name={{ _filters['order_purchase_affinity.store_name'] | url_encode }}&Focus%20Category={{ _filters['order_purchase_affinity.product_a_category'] | url_encode }}&Minimum%20Purchase%20Frequency={{ _filters['order_purchase_affinity.product_a_order_frequency'] | url_encode }}" + } + } + + dimension: product_a_image { + group_label: "Product A" + type: string + sql: ${product_a} ;; + html: ;; + } + + dimension: product_b { + group_label: "Product B" + type: string + sql: ${TABLE}.product_b ;; + } + + dimension: product_b_image { + group_label: "Product B" + type: string + sql: ${product_b} ;; + html: ;; + } + + dimension: product_a_category { + group_label: "Product A" + type: string + sql: ${TABLE}.product_a_category ;; + } + + dimension: product_b_category { + group_label: "Product B" + type: string + sql: ${TABLE}.product_b_category ;; + } + + dimension: joint_order_count { + description: "How many times item A and B were purchased in the same order" + type: number + sql: ${TABLE}.joint_order_count ;; + value_format: "#" + } + + dimension: product_a_order_count { + group_label: "Product A" + description: "Total number of orders with product A in them, during specified timeframe" + type: number + sql: ${TABLE}.product_a_order_count ;; + value_format: "#" + } + + dimension: product_b_order_count { + group_label: "Product B" + description: "Total number of orders with product B in them, during specified timeframe" + type: number + sql: ${TABLE}.product_b_order_count ;; + value_format: "#" + } + + # Frequencies + dimension: product_a_order_frequency { + group_label: "Product A" + description: "How frequently orders include product A as a percent of total orders" + type: number + sql: 1.0*${product_a_order_count}/${total_orders.count} ;; + value_format: "0.00%" + } + + dimension: product_b_order_frequency { + group_label: "Product B" + description: "How frequently orders include product B as a percent of total orders" + type: number + sql: 1.0*${product_b_order_count}/${total_orders.count} ;; + value_format: "0.00%" + } + + + dimension: joint_order_frequency { + description: "How frequently orders include both product A and B as a percent of total orders" + type: number + sql: 1.0*${joint_order_count}/${total_orders.count} ;; + value_format: "0.00%" + } + + # Affinity Metrics + + dimension: add_on_frequency { + description: "How many times both Products are purchased when Product A is purchased" + type: number + sql: 1.0*${joint_order_count}/${product_a_order_count} ;; + value_format: "0.00%" + } + + dimension: lift { + description: "The likelihood that buying product A drove the purchase of product B" + type: number + value_format_name: decimal_3 + sql: 1*${joint_order_frequency}/(${product_a_order_frequency} * ${product_b_order_frequency}) ;; + } + + dimension: product_a_count_purchased_alone { + type: number + hidden: yes + sql: ${TABLE}.product_a_count_purchased_alone ;; + } + + dimension: product_a_percent_purchased_alone { + group_label: "Product A" + description: "The % of times product A is purchased alone, over all transactions containing product A" + type: number + sql: 1.0*${product_a_count_purchased_alone}/(CASE WHEN ${product_a_order_count}=0 THEN NULL ELSE ${product_a_order_count} END);; + value_format_name: percent_1 + } + + dimension: product_a_count_orders_by_exclusive_customers { + group_label: "Product A" + type: number + hidden: yes + sql: ${TABLE}.product_a_count_orders_by_exclusive_customers ;; + } + + dimension: product_a_percent_customer_exclusivity{ + group_label: "Product A" + description: "% of times product A is purchased by customers who only bought product A in the timeframe" + type: number + sql: 1.0*${product_a_count_orders_by_exclusive_customers}/(CASE WHEN ${product_a_order_count}=0 THEN NULL ELSE ${product_a_order_count} END) ;; + value_format_name: percent_2 + } + + dimension: product_a_count_purchased_by_loyalty_customer { + type: number + hidden: yes + sql: ${TABLE}.product_a_count_purchased_by_loyalty_customer ;; + } + + dimension: product_a_percent_purchased_by_loyalty_customer { + group_label: "Product A" + description: "The % of times product A is purchased by a customer with a registered loyalty number" + type: number + sql: 1.0*${product_a_count_purchased_by_loyalty_customer}/(CASE WHEN ${product_a_order_count}=0 THEN NULL ELSE ${product_a_order_count} END);; + value_format_name: percent_1 + } + + dimension: product_b_count_purchased_alone { + type: number + hidden: yes + sql: ${TABLE}.product_b_count_purchased_alone ;; + } + + dimension: product_b_percent_purchased_alone { + group_label: "Product B" + description: "The % of times product B is purchased alone, over all transactions containing product B" + type: number + sql: 1.0*${product_b_count_purchased_alone}/(CASE WHEN ${product_b_order_count}=0 THEN NULL ELSE ${product_b_order_count} END);; + value_format_name: percent_1 + } + + dimension: product_b_count_orders_by_exclusive_customers { + type: number + hidden: yes + sql: ${TABLE}.product_b_count_orders_by_exclusive_customers ;; + } + + dimension: product_b_percent_customer_exclusivity{ + group_label: "Product B" + description: "% of times product B is purchased by customers who only bought product B in the timeframe" + type: number + sql: 1.0*${product_b_count_orders_by_exclusive_customers}/(CASE WHEN ${product_b_order_count}=0 THEN NULL ELSE ${product_b_order_count} END) ;; + value_format_name: percent_2 + } + + dimension: product_b_count_purchased_by_loyalty_customer { + type: number + hidden: yes + sql: ${TABLE}.product_b_count_purchased_by_loyalty_customer ;; + } + + dimension: product_b_percent_purchased_by_loyalty_customer { + group_label: "Product B" + description: "The % of times product B is purchased by a customer with a registered loyalty number" + type: number + sql: 1.0*${product_b_count_purchased_by_loyalty_customer}/(CASE WHEN ${product_b_order_count}=0 THEN NULL ELSE ${product_b_order_count} END);; + value_format_name: percent_1 + } + +## Do not display unless users have a solid understanding of statistics and probability models + dimension: jaccard_similarity { + description: "The probability both items would be purchased together, should be considered in relation to total order count, the highest score being 1" + type: number + sql: 1.0*${joint_order_count}/(${product_a_order_count} + ${product_b_order_count} - ${joint_order_count}) ;; + value_format: "#,##0.#0" + } + + # Sales Metrics - Totals + + dimension: product_a_total_sales { + view_label: "Sales and Margin - Total" + group_label: "Product A - Sales" + type: number + sql: ${TABLE}.product_a_product_sales ;; + value_format_name: usd + } + + dimension: product_a_total_basket_sales { + view_label: "Sales and Margin - Total" + group_label: "Product A - Sales" + type: number + sql: ${TABLE}.product_a_basket_sales ;; + value_format_name: usd + } + + dimension: product_a_total_rest_of_basket_sales { + view_label: "Sales and Margin - Total" + group_label: "Product A - Sales" + type: number + sql: ${product_a_total_basket_sales}-IFNULL(${product_a_total_sales},0) ;; + value_format_name: usd + } + + dimension: product_b_total_sales { + view_label: "Sales and Margin - Total" + group_label: "Product B - Sales" + type: number + sql: ${TABLE}.product_b_product_sales ;; + value_format_name: usd + } + + dimension: product_b_total_basket_sales { + view_label: "Sales and Margin - Total" + group_label: "Product B - Sales" + type: number + sql: ${TABLE}.product_b_basket_sales ;; + value_format_name: usd + } + + dimension: product_b_total_rest_of_basket_sales { + view_label: "Sales and Margin - Total" + group_label: "Product B - Sales" + type: number + sql: ${product_b_total_basket_sales}-IFNULL(${product_b_total_sales},0) ;; + value_format_name: usd + } + + # Margin Metrics - Totals + + dimension: product_a_total_margin { + view_label: "Sales and Margin - Total" + group_label: "Product A - Margin" + type: number + sql: ${TABLE}.product_a_product_margin ;; + value_format_name: usd + } + + dimension: product_a_total_basket_margin { + view_label: "Sales and Margin - Total" + group_label: "Product A - Margin" + type: number + sql: ${TABLE}.product_a_basket_margin ;; + value_format_name: usd + } + + dimension: product_a_total_rest_of_basket_margin { + view_label: "Sales and Margin - Total" + group_label: "Product A - Margin" + type: number + sql: ${product_a_total_basket_margin}-IFNULL(${product_a_total_margin},0) ;; + value_format_name: usd + } + + dimension: product_b_total_margin { + view_label: "Sales and Margin - Total" + group_label: "Product B - Margin" + type: number + sql: ${TABLE}.product_b_product_margin ;; + value_format_name: usd + } + + dimension: product_b_total_basket_margin { + view_label: "Sales and Margin - Total" + group_label: "Product B - Margin" + type: number + sql: ${TABLE}.product_b_basket_margin ;; + value_format_name: usd + } + + dimension: product_b_total_rest_of_basket_margin { + view_label: "Sales and Margin - Total" + group_label: "Product B - Margin" + type: number + sql: ${product_b_total_basket_margin}-IFNULL(${product_b_total_margin},0) ;; + value_format_name: usd + } + + # Sales Metrics - Average + + dimension: product_a_average_sales { + view_label: "Sales and Margin - Average" + group_label: "Product A - Sales" + type: number + sql: 1.0*${product_a_total_sales}/${product_a_order_count} ;; + value_format_name: usd + } + + dimension: product_a_average_basket_sales { + view_label: "Sales and Margin - Average" + group_label: "Product A - Sales" + type: number + sql: 1.0*${product_a_total_basket_sales}/${product_a_order_count} ;; + value_format_name: usd + } + + dimension: product_a_average_rest_of_basket_sales { + view_label: "Sales and Margin - Average" + group_label: "Product A - Sales" + type: number + sql: 1.0*${product_a_total_rest_of_basket_sales}/${product_a_order_count} ;; + value_format_name: usd + } + + dimension: product_b_average_sales { + view_label: "Sales and Margin - Average" + group_label: "Product B - Sales" + type: number + sql: 1.0*${product_b_total_sales}/${product_b_order_count} ;; + value_format_name: usd + } + + dimension: product_b_average_basket_sales { + view_label: "Sales and Margin - Average" + group_label: "Product B - Sales" + type: number + sql: 1.0*${product_b_total_basket_sales}/${product_b_order_count} ;; + value_format_name: usd + } + + dimension: product_b_average_rest_of_basket_sales { + view_label: "Sales and Margin - Average" + group_label: "Product B - Sales" + type: number + sql: 1.0*${product_b_total_rest_of_basket_sales}/${product_b_order_count} ;; + value_format_name: usd + } + + # Margin Metrics - Average + + dimension: product_a_average_margin { + view_label: "Sales and Margin - Average" + group_label: "Product A - Margin" + type: number + sql: 1.0*${product_a_total_margin}/${product_a_order_count} ;; + value_format_name: usd + } + + dimension: product_a_average_basket_margin { + view_label: "Sales and Margin - Average" + group_label: "Product A - Margin" + type: number + sql: 1.0*${product_a_total_basket_margin}/${product_a_order_count} ;; + value_format_name: usd + drill_fields: [product_a, product_a_percent_purchased_alone, product_a_percent_customer_exclusivity] + } + + dimension: product_a_average_rest_of_basket_margin { + view_label: "Sales and Margin - Average" + group_label: "Product A - Margin" + type: number + sql: 1.0*${product_a_total_rest_of_basket_margin}/${product_a_order_count} ;; + value_format_name: usd + drill_fields: [product_a, product_a_percent_purchased_alone, product_a_percent_customer_exclusivity] + } + + dimension: product_b_average_margin { + view_label: "Sales and Margin - Average" + group_label: "Product B - Margin" + type: number + sql: 1.0*${product_b_total_margin}/${product_b_order_count} ;; + value_format_name: usd + } + + dimension: product_b_average_basket_margin { + view_label: "Sales and Margin - Average" + group_label: "Product B - Margin" + type: number + sql: 1.0*${product_b_total_basket_margin}/${product_b_order_count} ;; + value_format_name: usd + drill_fields: [product_b, product_b_percent_purchased_alone, product_b_percent_customer_exclusivity] + } + + dimension: product_b_average_rest_of_basket_margin { + view_label: "Sales and Margin - Average" + group_label: "Product B - Margin" + type: number + sql: 1.0*${product_b_total_rest_of_basket_margin}/${product_b_order_count} ;; + value_format_name: usd + drill_fields: [product_b, product_b_percent_purchased_alone, product_b_percent_customer_exclusivity] + } + + # Aggregate Measures - ONLY TO BE USED WHEN FILTERING ON AN AGGREGATE DIMENSION (E.G. BRAND_A, CATEGORY_A) + + + measure: aggregated_joint_order_count { + description: "Only use when filtering on a rollup of product items, such as brand_a or category_a" + type: sum + sql: ${joint_order_count} ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_channels.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_channels.view.lkml new file mode 100644 index 0000000000000..8d9917323f841 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_channels.view.lkml @@ -0,0 +1,15 @@ +view: channels { + sql_table_name: `looker-private-demo.retail.channels` ;; + + dimension: id { + type: number + hidden: yes + sql: ${TABLE}.ID ;; + } + + dimension: name { + type: string + label: "Channel Name" + sql: ${TABLE}.NAME ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_customers.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_customers.view.lkml new file mode 100644 index 0000000000000..4db83a053870d --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_customers.view.lkml @@ -0,0 +1,150 @@ +view: customers { + sql_table_name: `looker-private-demo.retail.customers` ;; + drill_fields: [id] + + dimension: id { + primary_key: yes + type: number + sql: ${TABLE}.ID ;; + } + + dimension: address { + type: string + sql: ${TABLE}.address ;; + group_label: "Address Info" + link: { + url: "/dashboards/3OmU04xQdYtSVeq2Kf2GIj?Address=%22{{value | encode_uri}}%22&Date%20Range={{ _filters['transactions.transaction_date']}}" + label: "Drill into this address" + icon_url: "https://img.icons8.com/cotton/2x/worldwide-location.png" + } + } + + dimension: address_street_view { + type: string + group_label: "Address Info" + sql: ${address} ;; + html: ;; + } + + ##### MEASURES ##### + + measure: number_of_products { + type: count_distinct + sql: ${id} ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_stores.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_stores.view.lkml new file mode 100644 index 0000000000000..5a498fc721c27 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_dim_stores.view.lkml @@ -0,0 +1,153 @@ +view: stores { + label: "Stores 🏪" +# sql_table_name: looker-private-demo.retail.us_stores ;; + derived_table: { + datagroup_trigger: monthly + sql: SELECT * FROM `looker-private-demo.retail.us_stores` WHERE id IN (SELECT distinct store_id from ${transactions.SQL_TABLE_NAME});; + } + + dimension: id { + primary_key: yes + type: number + sql: ${TABLE}.ID ;; + } + + dimension: latitude { + type: number + hidden: yes + sql: ${TABLE}.LATITUDE ;; + + } + + dimension: longitude { + type: number + hidden: yes + sql: ${TABLE}.LONGITUDE ;; + } + + dimension: name { + drill_fields: [products.category] + label: "Store Name" + type: string + sql: ${TABLE}.NAME ;; + link: { + url: "/dashboards/EsHSwCce7zkZr7uz6X5kbO?Date={{ _filters['transactions.date_comparison_filter'] | encode_uri }}&Store={{value | encode_uri}}" + label: "Drill down into {{rendered_value}}" + } + link: { + url: "https://retail-demo-app-idhn2cvrpq-uc.a.run.app/api/contactStoreManager?store={{value | encode_uri}}" + label: "Text/Call {{rendered_value}} Store Manager via Google App Engine" + icon_url: "https://cdn.iconscout.com/icon/free/png-256/twilio-282195.png" + } +# action: { +# label: "Text/Call {{rendered_value}} Store Manager" +# icon_url: "https://cdn.iconscout.com/icon/free/png-256/twilio-282195.png" +# url: "https://retail-demo-app-idhn2cvrpq-uc.a.run.app/api/contactStoreManager?store={{value | encode_uri}}" +# param: { +# name: "store" +# value: "{{value | encode_uri}}" +# } +# form_param: { +# name: "message" +# type: textarea +# label: "Message" +# required: yes +# default: "Hi, can you please check out what's going on in {{rendered_value}}? /dashboards/WQKf302aPo8IEFvc2EkSQP?Store={{value | encode_uri}}" +# } +# } + } + + dimension: state { + type: string + group_label: "Store Info" + sql: ${TABLE}.State ;; + } + + dimension: sq_ft { + type: string + group_label: "Store Info" + sql: ${TABLE}.sq_ft ;; + } + + ##### DERIVED DIMENSIONS ##### + + dimension: location { + type: location + group_label: "Store Info" + sql_latitude: ${latitude} ;; + sql_longitude: ${longitude} ;; + } + + dimension: store_size_grouping { + type: string + sql: CASE + WHEN ${sq_ft} <= 70000 THEN 'S' + WHEN ${sq_ft} <= 100000 THEN 'M' + WHEN ${sq_ft} <= 130000 THEN 'L' + WHEN ${sq_ft} <= 160000 THEN 'XL' + END ;; + order_by_field: store_size_grouping_order + } + + dimension: store_size_grouping_order { + hidden: yes + type: number + sql: CASE ${store_size_grouping} + WHEN 'S' THEN 1 + WHEN 'M' THEN 2 + WHEN 'L' THEN 3 + WHEN 'XL' THEN 4 + END + ;; + } + + filter: store_for_comparison { + type: string + group_label: "Store Comparison" + suggest_dimension: stores.name + } + + dimension: store_comparison_vs_stores_in_tier { + type: string + group_label: "Store Comparison" + sql: CASE + WHEN {% condition store_for_comparison %} ${name} {% endcondition %} THEN CONCAT('1- ',${name}) + ELSE ${name} + END;; + } + + dimension: store_comparison_vs_stores_in_tier_with_weather { + type: string + group_label: "Store Comparison" + sql: CASE + WHEN {% condition store_for_comparison %} ${name} {% endcondition %} THEN CONCAT('1- ',${name}) + ELSE ${name} + END;; + html: {{rendered_value}}{% if store_weather.average_daily_precipitation._value < 2.0 %} - 🌞{% elsif store_weather.average_daily_precipitation._value < 4.0 %} - ☁️{% elsif store_weather.average_daily_precipitation._value > 4.0 %} - 🌧️️{% else %}{% endif %};; + action: { + label: "Text/Call {{rendered_value}} Store Manager" + icon_url: "https://cdn.iconscout.com/icon/free/png-256/twilio-282195.png" + url: "https://retail-demo-app-idhn2cvrpq-uc.a.run.app/api/contactStoreManager?store={{value | encode_uri}}" + param: { + name: "store" + value: "{{value | encode_uri}}" + } + form_param: { + name: "message" + type: textarea + label: "Message" + required: yes + default: "Hi, can you please check out what's going on in {{rendered_value}}? /dashboards/WQKf302aPo8IEFvc2EkSQP?Store={{value | encode_uri}}" + } + } + } + + dimension: store_comparison_vs_tier { + type: string + group_label: "Store Comparison" + sql: CASE + WHEN {% condition store_for_comparison %} ${name} {% endcondition %} THEN CONCAT('1- ',${name}) + ELSE '2- Rest of Stores in Tier' + END;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_events.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_events.view.lkml new file mode 100644 index 0000000000000..36ac149c7c663 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_events.view.lkml @@ -0,0 +1,106 @@ +view: events { + sql_table_name: `looker-private-demo.retail.events` ;; + drill_fields: [id] + + dimension: id { + primary_key: yes + type: number + sql: ${TABLE}.ID ;; + } + + dimension: browser { + type: string + sql: ${TABLE}.BROWSER ;; + } + + dimension: city { + type: string + sql: ${TABLE}.CITY ;; + } + + dimension: country { + type: string + map_layer_name: countries + sql: ${TABLE}.COUNTRY ;; + } + + dimension_group: created { + type: time + timeframes: [ + raw, + time, + date, + week, + month, + quarter, + year + ] + sql: ${TABLE}.CREATED_AT ;; + } + + dimension: event_type { + type: string + sql: ${TABLE}.EVENT_TYPE ;; + } + + dimension: ip_address { + type: string + sql: ${TABLE}.IP_ADDRESS ;; + } + + dimension: latitude { + type: number + sql: ${TABLE}.LATITUDE ;; + } + + dimension: longitude { + type: number + sql: ${TABLE}.LONGITUDE ;; + } + + dimension: os { + type: string + sql: ${TABLE}.OS ;; + } + + dimension: sequence_number { + type: number + sql: ${TABLE}.SEQUENCE_NUMBER ;; + } + + dimension: session_id { + type: string + sql: ${TABLE}.SESSION_ID ;; + } + + dimension: state { + type: string + sql: ${TABLE}.STATE ;; + } + + dimension: traffic_source { + type: string + sql: ${TABLE}.TRAFFIC_SOURCE ;; + } + + dimension: uri { + type: string + sql: ${TABLE}.URI ;; + } + + dimension: user_id { + type: number + # hidden: yes + sql: ${TABLE}.USER_ID ;; + } + + dimension: zip { + type: zipcode + sql: ${TABLE}.ZIP ;; + } + + measure: count { + type: count + drill_fields: [id, users.last_name, users.first_name, users.id] + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_transaction_line_items.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_transaction_line_items.view.lkml new file mode 100644 index 0000000000000..6438decf2fec3 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_transaction_line_items.view.lkml @@ -0,0 +1,107 @@ +view: transactions__line_items { + label: "Transactions" + + dimension: gross_margin { + type: number + sql: ${TABLE}.gross_margin ;; + } + + dimension: product_id { + type: number + hidden: yes + sql: ${TABLE}.product_id ;; + } + + dimension: sale_price { + type: number + sql: ${TABLE}.sale_price ;; + } + + ##### DERIVED DIMENSIONS ##### + + + ##### MEASURES ##### + + measure: total_sales { + type: sum + sql: ${sale_price} ;; + value_format_name: usd_0 + drill_fields: [transactions.drill_detail*] + } + + measure: total_gross_margin { + type: sum + sql: ${gross_margin} ;; + value_format_name: usd_0 + drill_fields: [transactions.drill_detail*] + } + + measure: total_quantity { + type: sum + sql: 1 ;; + value_format_name: decimal_0 + drill_fields: [transactions.drill_detail*] + } + + measure: average_basket_size { + type: number + sql: ${total_sales}/NULLIF(${transactions.number_of_transactions},0) ;; + value_format_name: usd + drill_fields: [transactions.drill_detail*] + } + + measure: average_item_price { + type: number + sql: ${total_sales}/NULLIF(${total_quantity},0) ;; + value_format_name: usd + drill_fields: [transactions.drill_detail*] + } + + ##### DATE COMPARISON MEASURES ##### + + measure: sales_change { + view_label: "Date Comparison" + label: "Sales Change (%)" + type: number + sql: SUM(CASE WHEN ${transactions.selected_comparison} LIKE 'This%' THEN ${transactions__line_items.sale_price} ELSE NULL END) / NULLIF(SUM(CASE WHEN ${transactions.selected_comparison} LIKE 'Prior%' THEN ${transactions__line_items.sale_price} ELSE NULL END),0) -1;; + value_format_name: percent_1 + drill_fields: [transactions.drill_detail*] + } + + ##### PER STORE MEASURES ##### + + measure: total_sales_per_store { + view_label: "Stores 🏪" + type: number + sql: ${total_sales}/NULLIF(${transactions.number_of_stores},0) ;; + value_format_name: usd_0 + drill_fields: [transactions.drill_detail*] + } + + measure: total_quantity_per_store { + view_label: "Stores 🏪" + type: number + sql: ${total_quantity}/NULLIF(${transactions.number_of_stores},0) ;; + value_format_name: decimal_0 + drill_fields: [transactions.drill_detail*] + } + + ##### PER ADDRESS MEASURES ##### + + measure: number_of_addresses { + hidden: yes + view_label: "Customers" + type: count_distinct + sql: ${customers.address};; + value_format_name: decimal_0 + drill_fields: [transactions.drill_detail*] + } + + measure: number_of_customers_per_address { + view_label: "Customers" + type: number + sql: ${transactions.number_of_customers}/NULLIF(${number_of_addresses},0) ;; + value_format_name: decimal_0 + drill_fields: [transactions.drill_detail*] + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_transactions.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_transactions.view.lkml new file mode 100644 index 0000000000000..0b5bac09edf0a --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/base_transactions.view.lkml @@ -0,0 +1,148 @@ +include: "date_comparison.view.lkml" + +view: transactions { + sql_table_name: `looker-private-demo.retail.transaction_detail` ;; + extends: [date_comparison] + + + dimension: transaction_id { + type: number + primary_key: yes + sql: ${TABLE}.transaction_id ;; + } + + dimension: channel_id { + type: number + hidden: yes + sql: ${TABLE}.channel_id ;; + } + + dimension: customer_id { + type: number + hidden: yes + sql: ${TABLE}.customer_id ;; + } + + dimension: line_items { + hidden: yes + sql: + -- spectacles: ignore + ${TABLE}.line_items ;; + } + + dimension: store_id { + type: number + hidden: yes + sql: ${TABLE}.store_id ;; + } + + dimension_group: transaction { + type: time + timeframes: [ + raw, + time, + date, + day_of_week, + hour_of_day, + week, + month, + quarter, + year, + week_of_year, + month_num + ] + sql: ${TABLE}.transaction_timestamp ;; + } + + ##### DERIVED DIMENSIONS ##### + + extends: [date_comparison] + + set: drill_detail { + fields: [transaction_date, stores.name, products.area, products.name, transactions__line_items.total_sales, number_of_transactions] + } + + dimension_group: since_first_customer_transaction { + type: duration + intervals: [month] + sql_start: ${customer_facts.customer_first_purchase_raw} ;; + sql_end: ${transaction_raw} ;; + } + + ##### MEASURES ##### + + measure: number_of_transactions { + type: count_distinct + sql: ${transactions.transaction_id} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: number_of_customers { + type: count_distinct + sql: ${transactions.customer_id} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: number_of_stores { + view_label: "Stores 🏪" + type: count_distinct + sql: ${transactions.store_id} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: number_of_customer_transactions { + hidden: yes + type: count_distinct + sql: ${transaction_id} ;; + filters: { + field: customer_id + value: "NOT NULL" + } + } + + measure: percent_customer_transactions { + type: number + sql: ${number_of_customer_transactions}/NULLIF(${number_of_transactions},0) ;; + value_format_name: percent_1 + drill_fields: [drill_detail*] + } + + measure: first_transaction { + type: date + sql: MIN(${transaction_raw}) ;; + drill_fields: [drill_detail*] + } + + ##### DATE COMPARISON MEASURES ##### + + measure: number_of_transactions_change { + view_label: "Date Comparison" + label: "Number of Transactions Change (%)" + type: number + sql: COUNT(distinct CASE WHEN ${transactions.selected_comparison} LIKE 'This%' THEN ${transaction_id} ELSE NULL END) / NULLIF(COUNT(distinct CASE WHEN ${transactions.selected_comparison} LIKE 'Prior%' THEN ${transaction_id} ELSE NULL END),0) -1;; + value_format_name: percent_1 + drill_fields: [drill_detail*] + } + + measure: number_of_customers_change { + view_label: "Date Comparison" + label: "Number of Customers Change (%)" + type: number + sql: COUNT(distinct CASE WHEN ${transactions.selected_comparison} LIKE 'This%' THEN ${customer_id} ELSE NULL END) / NULLIF(COUNT(distinct CASE WHEN ${transactions.selected_comparison} LIKE 'Prior%' THEN ${customer_id} ELSE NULL END),0) -1;; + value_format_name: percent_1 + drill_fields: [drill_detail*] + } + + ##### PER STORE MEASURES ##### + + measure: number_of_transactions_per_store { + view_label: "Stores 🏪" + type: number + sql: ${number_of_transactions}/NULLIF(${number_of_stores},0) ;; + value_format_name: decimal_0 + drill_fields: [transactions.drill_detail*] + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/category_lookup.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/category_lookup.view.lkml new file mode 100644 index 0000000000000..c032352b9c79b --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/category_lookup.view.lkml @@ -0,0 +1,23 @@ +view: category_lookup { + sql_table_name: `looker-private-demo.retail.category_lookup` ;; + + dimension: category { + type: string + sql: ${TABLE}.category ;; + } + + dimension: category_code { + type: number + sql: ${TABLE}.category_code ;; + } + + dimension: item_code { + type: number + sql: ${TABLE}.item_code ;; + } + + measure: count { + type: count + drill_fields: [] + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_clustering.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_clustering.view.lkml new file mode 100644 index 0000000000000..fe00988fcc77f --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_clustering.view.lkml @@ -0,0 +1,147 @@ +# If necessary, uncomment the line below to include explore_source. +# include: "../models/retail_block_model.model.lkml" + +view: customer_clustering_input { + view_label: "Customers" + derived_table: { + explore_source: transactions { + column: customer_id {} + column: age { field: customers.age } + column: total_sales { field: transactions__line_items.total_sales } + column: total_quantity { field: transactions__line_items.total_quantity } + column: total_gross_margin { field: transactions__line_items.total_gross_margin } + column: number_of_transactions {} + column: average_item_price { field: transactions__line_items.average_item_price } + column: average_basket_size { field: transactions__line_items.average_basket_size } + column: first_transaction {} + filters: [transactions.customer_id: "NOT NULL", stores.name: ""] + } + } +} + +view: customer_clustering_model { + derived_table: { + datagroup_trigger: monthly + sql_create: + CREATE OR REPLACE MODEL ${SQL_TABLE_NAME} + OPTIONS(model_type='kmeans', + num_clusters=4) AS + SELECT + * EXCEPT(customer_id) + FROM ${customer_clustering_input.SQL_TABLE_NAME};; + } +} + +view: customer_clustering_prediction_base { + label: "Customer Clusters 👤" + derived_table: { + datagroup_trigger: monthly + sql: SELECT * EXCEPT (nearest_centroids_distance) FROM ml.PREDICT( + MODEL ${customer_clustering_model.SQL_TABLE_NAME}, + (SELECT * FROM ${customer_clustering_input.SQL_TABLE_NAME}));; + } +} + +view: customer_clustering_prediction_aggregates { + derived_table: { + datagroup_trigger: monthly + sql: SELECT + CENTROID_ID, + AVG(age ) AS average_age, + AVG(average_basket_size ) AS average_basket_size, + AVG(number_of_transactions ) AS average_number_of_transactions, + AVG(total_quantity ) AS average_total_quantity, + AVG(total_sales ) AS average_total_sales, + COUNT(DISTINCT customer_id) AS customer_count + FROM ${customer_clustering_prediction_base.SQL_TABLE_NAME} + GROUP BY 1 ;; + } +} + +view: customer_clustering_prediction_centroid_ranks { + derived_table: { + datagroup_trigger: monthly + sql: SELECT + centroid_id, + RANK() OVER (ORDER BY average_age asc) as age_rank, + RANK() OVER (ORDER BY average_age desc) as inverse_age_rank, + RANK() OVER (ORDER BY average_basket_size desc) as average_basket_size_rank, + RANK() OVER (ORDER BY average_total_sales desc) as average_total_sales_rank + FROM ${customer_clustering_prediction_aggregates.SQL_TABLE_NAME} ;; + } +} + +view: customer_clustering_prediction { + derived_table: { + datagroup_trigger: monthly + sql: SELECT customer_clustering_prediction_base.* + ,CASE + WHEN customer_clustering_prediction_centroid_ranks.age_rank = 1 THEN 'Emerging Millennials 🥑' + WHEN customer_clustering_prediction_centroid_ranks.inverse_age_rank = 1 THEN 'Affluent Retirees 👴' + WHEN (customer_clustering_prediction_centroid_ranks.average_basket_size_rank = 1 AND customer_clustering_prediction_centroid_ranks.age_rank <> 1 AND customer_clustering_prediction_centroid_ranks.inverse_age_rank <> 1) + -- OR (customer_clustering_prediction_centroid_ranks.average_total_sales_rank = 1 AND customer_clustering_prediction_centroid_ranks.age_rank <> 1 AND customer_clustering_prediction_centroid_ranks.inverse_age_rank <> 1) + THEN 'Regular Gen Xers 🛒' + ELSE 'One-off locals 🏪' + END AS customer_segment + FROM ${customer_clustering_prediction_base.SQL_TABLE_NAME} customer_clustering_prediction_base + JOIN ${customer_clustering_prediction_centroid_ranks.SQL_TABLE_NAME} customer_clustering_prediction_centroid_ranks + ON customer_clustering_prediction_base.centroid_id = customer_clustering_prediction_centroid_ranks.centroid_id;; + } + + dimension: centroid_id { + type: number + hidden: yes + sql: ${TABLE}.CENTROID_ID ;; + } + + dimension: customer_id { + sql: ${TABLE}.customer_id ;; + } + + dimension: customer_segment { + type: string + sql: ${TABLE}.customer_segment ;; + order_by_field: centroid_id + link: { + url: "/dashboards/rPiqpL214a2t0oOeO58gso?Customer%20Segment={{value | encode_uri}}&Date%20Range={{ _filters['transactions.date_comparison_filter'] | url_encode }}" + label: "Drill into {{rendered_value}}" + } + } + + # Cluster Evaluation + + measure: customer_count { + type: count_distinct + sql: ${TABLE}.customer_id ;; + } + + measure: average_total_sales { + type: average + value_format_name: usd + sql: ${TABLE}.total_sales ;; + } + + measure: average_age { + type: average + value_format_name: decimal_0 + sql: ${TABLE}.age ;; + } + + measure: average_total_quantity { + type: average + value_format_name: usd + sql: ${TABLE}.total_quantity ;; + } + + measure: average_number_of_transactions { + type: average + value_format_name: decimal_1 + sql: ${TABLE}.number_of_transactions ;; + } + + measure: average_basket_size { + type: average + value_format_name: usd + sql: ${TABLE}.average_basket_size ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_facts.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_facts.view.lkml new file mode 100644 index 0000000000000..c976ec83c8ac6 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_facts.view.lkml @@ -0,0 +1,72 @@ +view: customer_facts { + view_label: "Customers" + derived_table: { + sql: SELECT + customer_id, + SUM(sale_price)/NULLIF(COUNT(DISTINCT transaction_id ),0) AS customer_average_basket_size, + SUM(gross_margin) AS customer_lifetime_gross_margin, + SUM(sale_price) AS customer_lifetime_sales, + COUNT(DISTINCT transaction_id) AS customer_lifetime_transactions, + SUM(1) AS customer_lifetime_quantity, + MIN(transaction_timestamp) AS customer_first_purchase_date, + SUM(CASE WHEN transaction_timestamp >= TIMESTAMP(DATE_ADD(CURRENT_DATE(),INTERVAL -6 MONTH)) AND transaction_timestamp < CURRENT_TIMESTAMP() THEN sale_price ELSE NULL END) + /NULLIF(SUM(CASE WHEN transaction_timestamp >= TIMESTAMP(DATE_ADD(CURRENT_DATE(),INTERVAL -12 MONTH)) AND transaction_timestamp < TIMESTAMP(DATE_ADD(CURRENT_DATE(),INTERVAL -6 MONTH)) THEN sale_price ELSE NULL END),0) -1 + AS customer_spend_trend_past_year + FROM ${transactions.SQL_TABLE_NAME} orders + LEFT JOIN UNNEST(line_items) line_items + GROUP BY 1 ;; + datagroup_trigger: daily + partition_keys: ["customer_first_purchase_date"] + cluster_keys: ["customer_id"] + } + + dimension: customer_id { + type: number + hidden: yes + sql: ${TABLE}.customer_id ;; + } + + dimension: customer_average_basket_size { + type: number + group_label: "Customer Lifetime" + sql: ${TABLE}.customer_average_basket_size ;; + } + + dimension: customer_lifetime_gross_margin { + type: number + group_label: "Customer Lifetime" + sql: ${TABLE}.customer_lifetime_gross_margin ;; + } + + dimension: customer_lifetime_sales { + type: number + group_label: "Customer Lifetime" + sql: ${TABLE}.customer_lifetime_sales ;; + } + + dimension: customer_lifetime_transactions { + type: number + group_label: "Customer Lifetime" + sql: ${TABLE}.customer_lifetime_transactions ;; + } + + dimension: customer_lifetime_quantity { + type: number + group_label: "Customer Lifetime" + sql: ${TABLE}.customer_lifetime_quantity ;; + } + + dimension_group: customer_first_purchase { + type: time + group_label: "Customer Lifetime" + timeframes: [raw,date,week,month] + sql: ${TABLE}.customer_first_purchase_date ;; + } + + dimension: customer_spend_trend_past_year { + type: number + group_label: "Customer Lifetime" + value_format_name: percent_1 + sql: ${TABLE}.customer_spend_trend_past_year ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_transaction_sequence.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_transaction_sequence.view.lkml new file mode 100644 index 0000000000000..22804dc3196bf --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/customer_transaction_sequence.view.lkml @@ -0,0 +1,102 @@ +view: customer_transaction_sequence { + # TO DO: replace with reference to NDT + derived_table: { + sql: SELECT customer_id, transaction_timestamp, category_list, transaction_sequence + ,MIN(CASE WHEN transaction_sequence=1 THEN category_list ELSE NULL END) OVER (PARTITION BY customer_id) AS category_list_transaction_1 + ,MIN(CASE WHEN transaction_sequence=2 THEN category_list ELSE NULL END) OVER (PARTITION BY customer_id) AS category_list_transaction_2 + ,MIN(CASE WHEN transaction_sequence=3 THEN category_list ELSE NULL END) OVER (PARTITION BY customer_id) AS category_list_transaction_3 + ,MIN(CASE WHEN transaction_sequence=4 THEN category_list ELSE NULL END) OVER (PARTITION BY customer_id) AS category_list_transaction_4 + ,MIN(CASE WHEN transaction_sequence=5 THEN category_list ELSE NULL END) OVER (PARTITION BY customer_id) AS category_list_transaction_5 + ,MIN(CASE WHEN transaction_sequence=1 THEN main_category ELSE NULL END) OVER (PARTITION BY customer_id) AS main_category_transaction_1 + ,MIN(CASE WHEN transaction_sequence=2 THEN main_category ELSE NULL END) OVER (PARTITION BY customer_id) AS main_category_transaction_2 + ,MIN(CASE WHEN transaction_sequence=3 THEN main_category ELSE NULL END) OVER (PARTITION BY customer_id) AS main_category_transaction_3 + ,MIN(CASE WHEN transaction_sequence=4 THEN main_category ELSE NULL END) OVER (PARTITION BY customer_id) AS main_category_transaction_4 + ,MIN(CASE WHEN transaction_sequence=5 THEN main_category ELSE NULL END) OVER (PARTITION BY customer_id) AS main_category_transaction_5 + FROM + (SELECT customer_id, transaction_timestamp, main_category, category_list + , DENSE_RANK() OVER (PARTITION BY customer_id ORDER BY transaction_timestamp ASC) AS transaction_sequence + FROM + (SELECT transactions.customer_id, transactions.transaction_timestamp + , STRING_AGG(products.category, ", " ORDER BY line_items.sale_price desc LIMIT 1) as main_category + , STRING_AGG(distinct products.category, ", " ORDER BY products.category asc) as category_list + FROM ${transactions.SQL_TABLE_NAME} transactions + LEFT JOIN UNNEST(transactions.line_items) line_items + LEFT JOIN ${products.SQL_TABLE_NAME} AS products + ON line_items.product_id = products.ID + WHERE {% condition transactions.transaction_date %} transaction_timestamp {% endcondition %} + GROUP BY 1,2));; + } + + dimension: customer_id { + hidden: yes + type: number + sql: ${TABLE}.customer_id ;; + } + + dimension_group: transaction { + hidden: yes + type: time + timeframes: [raw,date] + sql: ${TABLE}.transaction_timestamp ;; + } + + dimension: category_list { + type: string + sql: ${TABLE}.category_list ;; + } + + dimension: transaction_sequence { + type: number + sql: ${TABLE}.transaction_sequence ;; + } + + dimension: product_category_list_transaction_1 { + type: string + sql: ${TABLE}.category_list_transaction_1 ;; + } + + dimension: product_category_list_transaction_2 { + type: string + sql: ${TABLE}.category_list_transaction_2 ;; + } + + dimension: product_category_list_transaction_3 { + type: string + sql: ${TABLE}.category_list_transaction_3 ;; + } + + dimension: product_category_list_transaction_4 { + type: string + sql: ${TABLE}.category_list_transaction_4 ;; + } + + dimension: product_category_list_transaction_5 { + type: string + sql: ${TABLE}.category_list_transaction_5 ;; + } + + dimension: main_product_category_transaction_1 { + type: string + sql: ${TABLE}.main_category_transaction_1 ;; + } + + dimension: main_product_category_transaction_2 { + type: string + sql: ${TABLE}.main_category_transaction_2 ;; + } + + dimension: main_product_category_transaction_3 { + type: string + sql: ${TABLE}.main_category_transaction_3 ;; + } + + dimension: main_product_category_transaction_4 { + type: string + sql: ${TABLE}.main_category_transaction_4 ;; + } + + dimension: main_product_category_transaction_5 { + type: string + sql: ${TABLE}.main_category_transaction_5 ;; + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/date_comparison.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/date_comparison.view.lkml new file mode 100644 index 0000000000000..e148a4e8009d0 --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/date_comparison.view.lkml @@ -0,0 +1,95 @@ +view: date_comparison { + extension: required + + filter: date_comparison_filter { + view_label: "Date Comparison" + type: date + } + + parameter: comparison_type { + view_label: "Date Comparison" + type: unquoted + allowed_value: { + label: "Year" + value: "year" + } + allowed_value: { + label: "Week" + value: "week" + } + allowed_value: { + label: "YTD" + value: "YTD" + } + allowed_value: { + label: "WTD" + value: "WTD" + } + default_value: "year" + } + + dimension: selected_comparison { + view_label: "Date Comparison" + sql: {% if comparison_type._parameter_value == "year" %} + ${this_year_vs_last_year} + {% elsif comparison_type._parameter_value == "week" %} + ${this_week_vs_last_week} + {% elsif comparison_type._parameter_value == "YTD" %} + ${ytd_vs_lytd} + {% elsif comparison_type._parameter_value == "WTD" %} + ${wtd_vs_lwtd} + {% else %} + 0 + {% endif %};; + } + + dimension: this_year_vs_last_year { + hidden: yes + view_label: "Date Comparison" + type: string + sql: CASE + WHEN {% condition date_comparison_filter %} ${transaction_raw} {% endcondition %} THEN 'This Year' + WHEN ${transaction_raw} >= TIMESTAMP(DATE_ADD(CAST({% date_start date_comparison_filter %} AS DATE), INTERVAL -1 YEAR)) AND ${transaction_raw} <= TIMESTAMP(DATE_ADD(CAST({% date_end date_comparison_filter %} AS DATE), INTERVAL -365 DAY)) THEN 'Prior Year' + END;; + } + + dimension: this_week_vs_last_week { + hidden: yes + view_label: "Date Comparison" + type: string + sql: CASE + WHEN {% condition date_comparison_filter %} ${transaction_raw} {% endcondition %} THEN 'This Week' + WHEN ${transaction_raw} >= TIMESTAMP(DATE_ADD(CAST({% date_start date_comparison_filter %} AS DATE), INTERVAL -1 WEEK)) AND ${transaction_raw} < TIMESTAMP(DATE_ADD(CAST({% date_end date_comparison_filter %} AS DATE), INTERVAL -1 WEEK)) THEN 'Prior Week' + END;; + } + dimension: ytd_vs_lytd { + hidden: yes + view_label: "Date Comparison" + type: string + sql: CASE + WHEN EXTRACT(YEAR FROM ${transaction_date}) = EXTRACT(YEAR FROM CURRENT_DATE()) THEN 'This YTD' + WHEN EXTRACT(YEAR FROM ${transaction_date}) = EXTRACT(YEAR FROM DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)) AND ${transaction_date} <= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY) THEN 'Prior YTD' + END;; + } + + dimension: wtd_vs_lwtd { + hidden: yes + view_label: "Date Comparison" + type: string + sql: CASE + WHEN DATE_TRUNC(${transaction_date}, WEEK(MONDAY)) = DATE_TRUNC(CURRENT_DATE() , WEEK(MONDAY)) THEN 'This WTD' + WHEN EXTRACT(WEEK FROM ${transaction_date}) = EXTRACT(WEEK FROM DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY)) AND ${transaction_date} <= DATE_SUB(CURRENT_DATE(), INTERVAL 365 DAY) THEN 'Prior Year WTD' + END;; + + } + # dimension: wtd_vs_lywtd { + # hidden: yes + # view_label: "Date Comparison" + # type: string + # sql: CASE + # WHEN {% condition date_comparison_filter %} ${transaction_raw} {% endcondition %} THEN 'This Week' + # WHEN ${transaction_raw} >= TIMESTAMP(DATE_ADD(CAST({% date_start date_comparison_filter %} AS DATE), INTERVAL -1 WEEK)) AND ${transaction_raw} < TIMESTAMP(DATE_ADD(CAST({% date_end date_comparison_filter %} AS DATE), INTERVAL -1 WEEK)) THEN 'Prior Week' + # END;; + # } + +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/stock_forecasting.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/stock_forecasting.view.lkml new file mode 100644 index 0000000000000..4b148b3ec219d --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/stock_forecasting.view.lkml @@ -0,0 +1,468 @@ +view: stock_forecasting_explore_base { + view_label: "Stock Forecasting 🏭" + derived_table: { + explore_source: transactions { + column: transaction_week {} + column: transaction_week_of_year {} + column: product_name { field: products.name } + column: product_image { field: products.product_image } + column: department { field: products.department } + column: category { field: products.category } + column: brand { field: products.brand } + column: area { field: products.area } + column: store_id { field: stores.id } + column: store_name { field: stores.name } + column: number_of_customers {} + column: number_of_transactions {} + column: number_of_customer_transactions {} + column: gross_margin { field: transactions__line_items.total_gross_margin } + column: quantity { field: transactions__line_items.total_quantity } + column: sales { field: transactions__line_items.total_sales } + bind_filters: { + from_field: stock_forecasting_explore_base.transaction_week_filter + to_field: transactions.transaction_date + } + } + } + filter: transaction_week_filter { type: date} + dimension_group: transaction { + type: time + timeframes: [week,month,year] + sql: TIMESTAMP(CAST(${TABLE}.transaction_week AS DATE)) ;; + } + dimension: transaction_week_of_year { + group_label: "Transaction Date" + type: number + sql: IFNULL(${transaction_week_of_year_for_join},${stock_forecasting_prediction.transaction_week_of_year}) ;; + } + dimension: transaction_week_of_year_for_join { + hidden: yes + type: number + sql:${TABLE}.transaction_week_of_year;; + } + dimension: product_name { + sql: IFNULL(${product_name_for_join},${stock_forecasting_prediction.product_name}) ;; + link: { + label: "Drive attachments for {{rendered_value}}" + icon_url: "https://i.imgur.com/W4tVGrj.png" + url: "/dashboards/TSGWx3mvSYoyNKLKLDssXW?Focus%20Product={{value | encode_uri}}&Minimum%20Purchase%20Frequency=" + } + } + dimension: product_name_for_join { + hidden: yes + type: string + sql:${TABLE}.product_name;; + } + dimension: product_image { + view_label: "Product Detail 📦" + html: ;; + } + dimension: department { + view_label: "Product Detail 📦" + } + dimension: category { + view_label: "Product Detail 📦" + } + dimension: brand { + view_label: "Product Detail 📦" + } + dimension: area { + view_label: "Product Detail 📦" + } + dimension: store_id { + hidden: yes + type: number + sql: IFNULL(${store_id_for_join},${stock_forecasting_prediction.store_id}) ;; + } + dimension: store_id_for_join { + hidden: yes + type: number + sql: ${TABLE}.store_id ;; + } + dimension: store_name {} + dimension: number_of_customers { + value_format: "#,##0" + type: number + } + dimension: number_of_transactions { + value_format: "#,##0" + type: number + } + dimension: number_of_customer_transactions { + value_format: "#,##0" + type: number + } + dimension: gross_margin { + label: "Transactions Gross Margin" + value_format: "$#,##0" + type: number + } + dimension: quantity { + label: "Transactions Quantity" + value_format: "#,##0" + type: number + } + dimension: sales { + label: "Transactions Sales" + value_format: "$#,##0" + type: number + } + + ##### DERIVED DIMENSIONS ##### + + set: drill_detail { + fields: [transaction_week, store_id, product_name, total_quantity] + } + + ##### MEASURES ##### + + measure: total_number_of_transactions { + type: sum + sql: ${number_of_transactions} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: total_number_of_customer_transactions { + hidden: yes + type: sum + sql: ${number_of_customer_transactions} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: total_number_of_customers { + type: sum + sql: ${number_of_customers} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: percent_customer_transactions { + type: number + sql: ${number_of_customer_transactions}/NULLIF(${number_of_transactions},0) ;; + value_format_name: percent_1 + drill_fields: [drill_detail*] + } + + measure: total_sales { + type: sum + sql: ${sales} ;; + value_format_name: usd_0 + drill_fields: [drill_detail*] + } + + measure: total_gross_margin { + type: sum + sql: ${gross_margin} ;; + value_format_name: usd_0 + drill_fields: [drill_detail*] + } + + measure: total_quantity { + label: "Total Stock" + type: sum + sql: ${quantity} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: average_basket_size { + type: number + sql: ${total_sales}/NULLIF(${total_number_of_transactions},0) ;; + value_format_name: usd + drill_fields: [drill_detail*] + } + + measure: average_item_price { + type: number + sql: ${total_sales}/NULLIF(${total_quantity},0) ;; + value_format_name: usd + drill_fields: [transactions.drill_detail*] + } + + measure: stock_difference { + type: number + sql: ${stock_forecasting_prediction.forecasted_quantity}-${total_quantity} ;; + value_format_name: decimal_0 + } + + measure: stock_difference_value { + type: number + sql: ${stock_difference}*${average_item_price} ;; + value_format_name: usd + } +} + +view: stock_forecasting_product_store_week_facts { + derived_table: { + explore_source: transactions { + column: transaction_week_of_year {} + column: id { field: stores.id } + column: name { field: products.name } + column: category { field: products.category } + column: total_quantity { field: transactions__line_items.total_quantity } + column: store_size_grouping { field: stores.store_size_grouping } + column: sq_ft { field: stores.sq_ft } + filters: { + field: transactions.transaction_date + value: "1 years ago for 1 years" + } + } + } + dimension: transaction_week_of_year {} + dimension: id {} + dimension: name {} + dimension: category {} + dimension: total_quantity {} + dimension: store_size_grouping {} + dimension: sq_ft {} +} + +view: stock_forecasting_product_store_week_facts_prior_year { + derived_table: { + explore_source: transactions { + column: transaction_week_of_year {} + column: id { field: stores.id } + column: name { field: products.name } + column: total_quantity { field: transactions__line_items.total_quantity } + column: average_basket_size { field: transactions__line_items.average_basket_size } + column: average_item_price { field: transactions__line_items.average_item_price } + column: total_sales { field: transactions__line_items.total_sales } + column: total_gross_margin { field: transactions__line_items.total_gross_margin } + column: percent_customer_transactions {} + column: number_of_transactions {} + column: number_of_customers {} + filters: { + field: transactions.transaction_date + value: "2 years ago for 1 years" + } + } + } + dimension: transaction_week_of_year {} + dimension: id {} + dimension: name {} + dimension: total_quantity {} + dimension: average_basket_size {} + dimension: average_item_price {} + dimension: total_sales {} + dimension: total_gross_margin {} + dimension: percent_customer_transactions {} + dimension: number_of_transactions {} + dimension: number_of_customers {} +} + +view: stock_forecasting_store_week_facts_prior_year { + derived_table: { + explore_source: transactions { + column: transaction_week_of_year {} + column: id { field: stores.id } + column: name { field: products.name } + column: total_quantity { field: transactions__line_items.total_quantity } + column: average_basket_size { field: transactions__line_items.average_basket_size } + column: average_item_price { field: transactions__line_items.average_item_price } + column: average_daily_precipitation { field: store_weather.average_daily_precipitation } + column: average_max_temparature { field: store_weather.average_max_temparature } + column: average_min_temparature { field: store_weather.average_min_temparature } + column: total_sales { field: transactions__line_items.total_sales } + column: total_gross_margin { field: transactions__line_items.total_gross_margin } + column: percent_customer_transactions {} + column: number_of_transactions {} + column: number_of_customers {} + filters: { + field: transactions.transaction_date + value: "2 years ago for 1 years" + } + } + } + dimension: transaction_week_of_year {} + dimension: id {} + dimension: name {} + dimension: total_quantity {} + dimension: average_basket_size {} + dimension: average_item_price {} + dimension: average_daily_precipitation {} + dimension: average_max_temparature {} + dimension: average_min_temparature {} + dimension: total_sales {} + dimension: total_gross_margin {} + dimension: percent_customer_transactions {} + dimension: number_of_transactions {} + dimension: number_of_customers {} +} + +view: stock_forecasting_category_week_facts_prior_year { + derived_table: { + explore_source: transactions { + column: transaction_week_of_year {} + column: category { field: products.category } + column: total_quantity { field: transactions__line_items.total_quantity } + column: average_basket_size { field: transactions__line_items.average_basket_size } + column: average_item_price { field: transactions__line_items.average_item_price } + column: total_sales { field: transactions__line_items.total_sales } + column: total_gross_margin { field: transactions__line_items.total_gross_margin } + column: percent_customer_transactions {} + column: number_of_transactions {} + column: number_of_customers {} + filters: { + field: transactions.transaction_date + value: "2 years ago for 1 years" + } + } + } + dimension: transaction_week_of_year {} + dimension: category {} + dimension: total_quantity {} + dimension: average_basket_size {} + dimension: average_item_price {} + dimension: total_sales {} + dimension: total_gross_margin {} + dimension: percent_customer_transactions {} + dimension: number_of_transactions {} + dimension: number_of_customers {} +} + +explore: stock_forecasting_product_store_week_facts { + hidden: yes + join: stock_forecasting_product_store_week_facts_prior_year { + relationship: one_to_one + sql_on: ${stock_forecasting_product_store_week_facts.transaction_week_of_year} = ${stock_forecasting_product_store_week_facts_prior_year.transaction_week_of_year} + AND ${stock_forecasting_product_store_week_facts.id} = ${stock_forecasting_product_store_week_facts_prior_year.id} + AND ${stock_forecasting_product_store_week_facts.name} = ${stock_forecasting_product_store_week_facts_prior_year.name};; + } + join: stock_forecasting_store_week_facts_prior_year { + relationship: many_to_one + sql_on: ${stock_forecasting_product_store_week_facts.transaction_week_of_year} = ${stock_forecasting_store_week_facts_prior_year.transaction_week_of_year} + AND ${stock_forecasting_product_store_week_facts.id} = ${stock_forecasting_store_week_facts_prior_year.id};; + } + join: stock_forecasting_category_week_facts_prior_year { + relationship: many_to_one + sql_on: ${stock_forecasting_product_store_week_facts.transaction_week_of_year} = ${stock_forecasting_category_week_facts_prior_year.transaction_week_of_year} + AND ${stock_forecasting_product_store_week_facts.category} = ${stock_forecasting_category_week_facts_prior_year.category};; + } +} + +########################################################################################### +################################### BEGIN BQML MODEL #################################### +########################################################################################### + +view: stock_forecasting_input { + derived_table: { + explore_source: stock_forecasting_product_store_week_facts { + column: transaction_week_of_year {} + column: store_id { field: stock_forecasting_product_store_week_facts.id} + column: product_name { field: stock_forecasting_product_store_week_facts.name } + column: category {} + column: sq_ft {} + column: store_size_grouping {} + column: total_quantity {} + column: stock_forecasting_category_week_facts_prior_year__average_basket_size { field: stock_forecasting_category_week_facts_prior_year.average_basket_size } + column: stock_forecasting_category_week_facts_prior_year__average_item_price { field: stock_forecasting_category_week_facts_prior_year.average_item_price } + column: stock_forecasting_category_week_facts_prior_year__number_of_customers { field: stock_forecasting_category_week_facts_prior_year.number_of_customers } + column: stock_forecasting_category_week_facts_prior_year__number_of_transactions { field: stock_forecasting_category_week_facts_prior_year.number_of_transactions } + column: stock_forecasting_category_week_facts_prior_year__percent_customer_transactions { field: stock_forecasting_category_week_facts_prior_year.percent_customer_transactions } + column: stock_forecasting_category_week_facts_prior_year__total_gross_margin { field: stock_forecasting_category_week_facts_prior_year.total_gross_margin } + column: stock_forecasting_category_week_facts_prior_year__total_quantity { field: stock_forecasting_category_week_facts_prior_year.total_quantity } + column: stock_forecasting_category_week_facts_prior_year__total_sales { field: stock_forecasting_category_week_facts_prior_year.total_sales } + column: stock_forecasting_product_store_week_facts_prior_year__average_basket_size { field: stock_forecasting_product_store_week_facts_prior_year.average_basket_size } + column: stock_forecasting_product_store_week_facts_prior_year__average_item_price { field: stock_forecasting_product_store_week_facts_prior_year.average_item_price } + column: stock_forecasting_product_store_week_facts_prior_year__number_of_customers { field: stock_forecasting_product_store_week_facts_prior_year.number_of_customers } + column: stock_forecasting_product_store_week_facts_prior_year__number_of_transactions { field: stock_forecasting_product_store_week_facts_prior_year.number_of_transactions } + column: stock_forecasting_product_store_week_facts_prior_year__percent_customer_transactions { field: stock_forecasting_product_store_week_facts_prior_year.percent_customer_transactions } + column: stock_forecasting_product_store_week_facts_prior_year__total_gross_margin { field: stock_forecasting_product_store_week_facts_prior_year.total_gross_margin } + column: stock_forecasting_product_store_week_facts_prior_year__total_quantity { field: stock_forecasting_product_store_week_facts_prior_year.total_quantity } + column: stock_forecasting_product_store_week_facts_prior_year__total_sales { field: stock_forecasting_product_store_week_facts_prior_year.total_sales } + column: stock_forecasting_store_week_facts_prior_year__average_basket_size { field: stock_forecasting_store_week_facts_prior_year.average_basket_size } + column: stock_forecasting_store_week_facts_prior_year__average_daily_precipitation { field: stock_forecasting_store_week_facts_prior_year.average_daily_precipitation } + column: stock_forecasting_store_week_facts_prior_year__average_item_price { field: stock_forecasting_store_week_facts_prior_year.average_item_price } + column: stock_forecasting_store_week_facts_prior_year__average_max_temparature { field: stock_forecasting_store_week_facts_prior_year.average_max_temparature } + column: stock_forecasting_store_week_facts_prior_year__average_min_temparature { field: stock_forecasting_store_week_facts_prior_year.average_min_temparature } + column: stock_forecasting_store_week_facts_prior_year__number_of_customers { field: stock_forecasting_store_week_facts_prior_year.number_of_customers } + column: stock_forecasting_store_week_facts_prior_year__number_of_transactions { field: stock_forecasting_store_week_facts_prior_year.number_of_transactions } + column: stock_forecasting_store_week_facts_prior_year__percent_customer_transactions { field: stock_forecasting_store_week_facts_prior_year.percent_customer_transactions } + column: stock_forecasting_store_week_facts_prior_year__total_gross_margin { field: stock_forecasting_store_week_facts_prior_year.total_gross_margin } + column: stock_forecasting_store_week_facts_prior_year__total_quantity { field: stock_forecasting_store_week_facts_prior_year.total_quantity } + column: stock_forecasting_store_week_facts_prior_year__total_sales { field: stock_forecasting_store_week_facts_prior_year.total_sales } + } + } +} + +view: stock_forecasting_regression { + derived_table: { + datagroup_trigger: weekly + sql_create: + CREATE OR REPLACE MODEL ${SQL_TABLE_NAME} + OPTIONS(model_type='linear_reg' + , labels=['total_quantity'] + , min_rel_progress = 0.05 + , max_iteration = 50 + ) AS + SELECT + * EXCEPT(transaction_week_of_year, store_id, product_name) + FROM ${stock_forecasting_input.SQL_TABLE_NAME};; + } +} + +view: stock_forecasting_prediction { + derived_table: { + sql: SELECT transaction_week_of_year,store_id,product_name + ,CONCAT(CAST(transaction_week_of_year AS STRING),'_',CAST(store_id AS STRING),product_name) AS pk + ,category + ,predicted_total_quantity + FROM ml.PREDICT( + MODEL ${stock_forecasting_regression.SQL_TABLE_NAME}, + (SELECT * FROM ${stock_forecasting_input.SQL_TABLE_NAME}));; + datagroup_trigger: weekly + } + + dimension: pk { + hidden: yes + primary_key: yes + type: string + sql: ${TABLE}.pk ;; + } + + dimension: transaction_week_of_year { + hidden: yes + type: number + sql: ${TABLE}.transaction_week_of_year ;; + } + + dimension: store_id { + hidden: yes + type: number + sql: ${TABLE}.store_id ;; + } + + dimension: product_name { + hidden: yes + type: string + sql: ${TABLE}.product_name ;; + } + + dimension: category { + hidden: yes + type: string + sql: ${TABLE}.category ;; + } + + dimension: predicted_total_quantity { + hidden: yes + type: number + sql: ${TABLE}.predicted_total_quantity ;; + } + + dimension: predicted_total_quantity_rounded { + hidden: yes + type: number + sql: ROUND(${predicted_total_quantity},0) ;; + } + + measure: forecasted_quantity { + view_label: "Stock Forecasting 🏭" + label: "Forecasted Stock 📈" + type: sum + sql: ${predicted_total_quantity_rounded} ;; + value_format_name: decimal_0 + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/store_weather.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/store_weather.view.lkml new file mode 100644 index 0000000000000..115131b8551bb --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/store_weather.view.lkml @@ -0,0 +1,698 @@ +view: weather_raw { + derived_table: { + datagroup_trigger: daily + partition_keys: ["date"] + # requires ID, latitude, longitude columns in stores table + # TO DO: update DATE_ADD(,+1 YEAR) with 2020 table once available in BQ public dataset + sql: SELECT id,date,element,value,mflag,qflag,sflag,time FROM `bigquery-public-data.ghcn_d.ghcnd_202*` + UNION ALL SELECT id,date,element,value,mflag,qflag,sflag,time FROM `bigquery-public-data.ghcn_d.ghcnd_2019` + UNION ALL SELECT id,date,element,value,mflag,qflag,sflag,time FROM `bigquery-public-data.ghcn_d.ghcnd_2018` + UNION ALL SELECT id,date,element,value,mflag,qflag,sflag,time FROM `bigquery-public-data.ghcn_d.ghcnd_2017` + UNION ALL SELECT id,date,element,value,mflag,qflag,sflag,time FROM `bigquery-public-data.ghcn_d.ghcnd_2016` ;; + } +} + +view: weather_pivoted { + derived_table: { + datagroup_trigger: daily + partition_keys: ["date"] + # requires ID, latitude, longitude columns in stores table + # TO DO: update DATE_ADD(,+1 YEAR) with 2020 table once available in BQ public dataset + sql: SELECT date,id + ,AVG(CASE WHEN element="TMAX" THEN value ELSE NULL END) AS TMAX + ,AVG(CASE WHEN element="WESD" THEN value ELSE NULL END) AS WESD + ,AVG(CASE WHEN element="AWND" THEN value ELSE NULL END) AS AWND + ,AVG(CASE WHEN element="WDMV" THEN value ELSE NULL END) AS WDMV + ,AVG(CASE WHEN element="THIC" THEN value ELSE NULL END) AS THIC + ,AVG(CASE WHEN element="SX51" THEN value ELSE NULL END) AS SX51 + ,AVG(CASE WHEN element="TAVG" THEN value ELSE NULL END) AS TAVG + ,AVG(CASE WHEN element="TSUN" THEN value ELSE NULL END) AS TSUN + ,AVG(CASE WHEN element="DAPR" THEN value ELSE NULL END) AS DAPR + ,AVG(CASE WHEN element="WDF5" THEN value ELSE NULL END) AS WDF5 + ,AVG(CASE WHEN element="WT04" THEN value ELSE NULL END) AS WT04 + ,AVG(CASE WHEN element="MDSF" THEN value ELSE NULL END) AS MDSF + ,AVG(CASE WHEN element="SNWD" THEN value ELSE NULL END) AS SNWD + ,AVG(CASE WHEN element="MXPN" THEN value ELSE NULL END) AS MXPN + ,AVG(CASE WHEN element="WESF" THEN value ELSE NULL END) AS WESF + ,AVG(CASE WHEN element="MDTN" THEN value ELSE NULL END) AS MDTN + ,AVG(CASE WHEN element="SX36" THEN value ELSE NULL END) AS SX36 + ,AVG(CASE WHEN element="DASF" THEN value ELSE NULL END) AS DASF + ,AVG(CASE WHEN element="TMIN" THEN value ELSE NULL END) AS TMIN + ,AVG(CASE WHEN element="TOBS" THEN value ELSE NULL END) AS TOBS + ,AVG(CASE WHEN element="WSFG" THEN value ELSE NULL END) AS WSFG + ,AVG(CASE WHEN element="MNPN" THEN value ELSE NULL END) AS MNPN + ,AVG(CASE WHEN element="SN32" THEN value ELSE NULL END) AS SN32 + ,AVG(CASE WHEN element="SX31" THEN value ELSE NULL END) AS SX31 + ,AVG(CASE WHEN element="SX33" THEN value ELSE NULL END) AS SX33 + ,AVG(CASE WHEN element="WSF5" THEN value ELSE NULL END) AS WSF5 + ,AVG(CASE WHEN element="WDF2" THEN value ELSE NULL END) AS WDF2 + ,AVG(CASE WHEN element="SN55" THEN value ELSE NULL END) AS SN55 + ,AVG(CASE WHEN element="SN35" THEN value ELSE NULL END) AS SN35 + ,AVG(CASE WHEN element="AWDR" THEN value ELSE NULL END) AS AWDR + ,AVG(CASE WHEN element="DATN" THEN value ELSE NULL END) AS DATN + ,AVG(CASE WHEN element="WSFI" THEN value ELSE NULL END) AS WSFI + ,AVG(CASE WHEN element="SN36" THEN value ELSE NULL END) AS SN36 + ,AVG(CASE WHEN element="MDTX" THEN value ELSE NULL END) AS MDTX + ,AVG(CASE WHEN element="WT01" THEN value ELSE NULL END) AS WT01 + ,AVG(CASE WHEN element="WT11" THEN value ELSE NULL END) AS WT11 + ,AVG(CASE WHEN element="SN57" THEN value ELSE NULL END) AS SN57 + ,AVG(CASE WHEN element="WDFG" THEN value ELSE NULL END) AS WDFG + ,AVG(CASE WHEN element="SN56" THEN value ELSE NULL END) AS SN56 + ,AVG(CASE WHEN element="SX56" THEN value ELSE NULL END) AS SX56 + ,AVG(CASE WHEN element="WT02" THEN value ELSE NULL END) AS WT02 + ,AVG(CASE WHEN element="WT09" THEN value ELSE NULL END) AS WT09 + ,AVG(CASE WHEN element="WT10" THEN value ELSE NULL END) AS WT10 + ,AVG(CASE WHEN element="WT16" THEN value ELSE NULL END) AS WT16 + ,AVG(CASE WHEN element="PRCP" THEN value ELSE NULL END) AS PRCP + ,AVG(CASE WHEN element="MDPR" THEN value ELSE NULL END) AS MDPR + ,AVG(CASE WHEN element="SX52" THEN value ELSE NULL END) AS SX52 + ,AVG(CASE WHEN element="SX32" THEN value ELSE NULL END) AS SX32 + ,AVG(CASE WHEN element="SN53" THEN value ELSE NULL END) AS SN53 + ,AVG(CASE WHEN element="SX55" THEN value ELSE NULL END) AS SX55 + ,AVG(CASE WHEN element="WT03" THEN value ELSE NULL END) AS WT03 + ,AVG(CASE WHEN element="WT05" THEN value ELSE NULL END) AS WT05 + ,AVG(CASE WHEN element="WT17" THEN value ELSE NULL END) AS WT17 + ,AVG(CASE WHEN element="EVAP" THEN value ELSE NULL END) AS EVAP + ,AVG(CASE WHEN element="SN52" THEN value ELSE NULL END) AS SN52 + ,AVG(CASE WHEN element="SNOW" THEN value ELSE NULL END) AS SNOW + ,AVG(CASE WHEN element="SX53" THEN value ELSE NULL END) AS SX53 + ,AVG(CASE WHEN element="SN51" THEN value ELSE NULL END) AS SN51 + ,AVG(CASE WHEN element="PGTM" THEN value ELSE NULL END) AS PGTM + ,AVG(CASE WHEN element="WSF2" THEN value ELSE NULL END) AS WSF2 + ,AVG(CASE WHEN element="WT06" THEN value ELSE NULL END) AS WT06 + ,AVG(CASE WHEN element="WT08" THEN value ELSE NULL END) AS WT08 + ,AVG(CASE WHEN element="WT07" THEN value ELSE NULL END) AS WT07 + ,AVG(CASE WHEN element="SX57" THEN value ELSE NULL END) AS SX57 + ,AVG(CASE WHEN element="WT22" THEN value ELSE NULL END) AS WT22 + ,AVG(CASE WHEN element="WT15" THEN value ELSE NULL END) AS WT15 + ,AVG(CASE WHEN element="SN31" THEN value ELSE NULL END) AS SN31 + ,AVG(CASE WHEN element="SN33" THEN value ELSE NULL END) AS SN33 + ,AVG(CASE WHEN element="PSUN" THEN value ELSE NULL END) AS PSUN + ,AVG(CASE WHEN element="DATX" THEN value ELSE NULL END) AS DATX + ,AVG(CASE WHEN element="DWPR" THEN value ELSE NULL END) AS DWPR + ,AVG(CASE WHEN element="SX35" THEN value ELSE NULL END) AS SX35 + ,AVG(CASE WHEN element="WT18" THEN value ELSE NULL END) AS WT18 + FROM ${weather_raw.SQL_TABLE_NAME} + GROUP BY date,id ;; + } + } + +view: distances { + derived_table: { + datagroup_trigger: daily + sql: SELECT stores.id as store_id + ,stations.id AS station_id + ,ST_DISTANCE(ST_GEOGPOINT(stores.longitude,stores.latitude),ST_GEOGPOINT(stations.longitude,stations.latitude)) as dist + FROM ${stores.SQL_TABLE_NAME} stores + CROSS JOIN `bigquery-public-data.ghcn_d.ghcnd_stations` stations ;; + } +} + +view: store_weather { + label: "Store Weather ⛅" + derived_table: { + datagroup_trigger: daily + partition_keys: ["date"] + cluster_keys: ["store_id"] + # requires ID, latitude, longitude columns in stores table + # TO DO: update DATE_ADD(,+1 YEAR) with 2020 table once available in BQ public dataset + sql: SELECT distances.store_id + ,weather_pivoted.date + ,AVG(distances.dist/1000) AS average_distance_to_weather_stations_km + ,AVG(TMAX) AS TMAX + ,AVG(WESD) AS WESD + ,AVG(AWND) AS AWND + ,AVG(WDMV) AS WDMV + ,AVG(THIC) AS THIC + ,AVG(SX51) AS SX51 + ,AVG(TAVG) AS TAVG + ,AVG(TSUN) AS TSUN + ,AVG(DAPR) AS DAPR + ,AVG(WDF5) AS WDF5 + ,AVG(WT04) AS WT04 + ,AVG(MDSF) AS MDSF + ,AVG(SNWD) AS SNWD + ,AVG(MXPN) AS MXPN + ,AVG(WESF) AS WESF + ,AVG(MDTN) AS MDTN + ,AVG(SX36) AS SX36 + ,AVG(DASF) AS DASF + ,AVG(TMIN) AS TMIN + ,AVG(TOBS) AS TOBS + ,AVG(WSFG) AS WSFG + ,AVG(MNPN) AS MNPN + ,AVG(SN32) AS SN32 + ,AVG(SX31) AS SX31 + ,AVG(SX33) AS SX33 + ,AVG(WSF5) AS WSF5 + ,AVG(WDF2) AS WDF2 + ,AVG(SN55) AS SN55 + ,AVG(SN35) AS SN35 + ,AVG(AWDR) AS AWDR + ,AVG(DATN) AS DATN + ,AVG(WSFI) AS WSFI + ,AVG(SN36) AS SN36 + ,AVG(MDTX) AS MDTX + ,AVG(WT01) AS WT01 + ,AVG(WT11) AS WT11 + ,AVG(SN57) AS SN57 + ,AVG(WDFG) AS WDFG + ,AVG(SN56) AS SN56 + ,AVG(SX56) AS SX56 + ,AVG(WT02) AS WT02 + ,AVG(WT09) AS WT09 + ,AVG(WT10) AS WT10 + ,AVG(WT16) AS WT16 + ,AVG(PRCP) AS PRCP + ,AVG(MDPR) AS MDPR + ,AVG(SX52) AS SX52 + ,AVG(SX32) AS SX32 + ,AVG(SN53) AS SN53 + ,AVG(SX55) AS SX55 + ,AVG(WT03) AS WT03 + ,AVG(WT05) AS WT05 + ,AVG(WT17) AS WT17 + ,AVG(EVAP) AS EVAP + ,AVG(SN52) AS SN52 + ,AVG(SNOW) AS SNOW + ,AVG(SX53) AS SX53 + ,AVG(SN51) AS SN51 + ,AVG(PGTM) AS PGTM + ,AVG(WSF2) AS WSF2 + ,AVG(WT06) AS WT06 + ,AVG(WT08) AS WT08 + ,AVG(WT07) AS WT07 + ,AVG(SX57) AS SX57 + ,AVG(WT22) AS WT22 + ,AVG(WT15) AS WT15 + ,AVG(SN31) AS SN31 + ,AVG(SN33) AS SN33 + ,AVG(PSUN) AS PSUN + ,AVG(DATX) AS DATX + ,AVG(DWPR) AS DWPR + ,AVG(SX35) AS SX35 + ,AVG(WT18) AS WT18 + FROM ${distances.SQL_TABLE_NAME} distances + JOIN ${weather_pivoted.SQL_TABLE_NAME} weather_pivoted + ON distances.station_id = weather_pivoted.id + WHERE distances.dist < 30000 + GROUP BY 1,2;; + } + + dimension: awdr { + hidden: yes + type: number + sql: ${TABLE}.AWDR ;; + } + + dimension: awnd { + hidden: yes + type: number + sql: ${TABLE}.AWND ;; + } + + dimension: dapr { + hidden: yes + type: number + sql: ${TABLE}.DAPR ;; + } + + dimension: dasf { + hidden: yes + type: number + sql: ${TABLE}.DASF ;; + } + + dimension_group: weather { + hidden: yes + type: time + timeframes: [ + raw, + date, + week, + month, + quarter, + year + ] + convert_tz: no + datatype: date + sql: ${TABLE}.date ;; + } + + dimension: datn { + hidden: yes + type: number + sql: ${TABLE}.DATN ;; + } + + dimension: datx { + hidden: yes + type: number + sql: ${TABLE}.DATX ;; + } + + dimension: average_distance_to_weather_stations_km { + hidden: yes + type: number + sql: ${TABLE}.average_distance_to_weather_stations_km ;; + } + + dimension: dwpr { + hidden: yes + type: number + sql: ${TABLE}.DWPR ;; + } + + dimension: evap { + hidden: yes + type: number + sql: ${TABLE}.EVAP ;; + } + + dimension: mdpr { + hidden: yes + type: number + sql: ${TABLE}.MDPR ;; + } + + dimension: mdsf { + hidden: yes + type: number + sql: ${TABLE}.MDSF ;; + } + + dimension: mdtn { + hidden: yes + type: number + sql: ${TABLE}.MDTN ;; + } + + dimension: mdtx { + hidden: yes + type: number + sql: ${TABLE}.MDTX ;; + } + + dimension: mnpn { + hidden: yes + type: number + sql: ${TABLE}.MNPN ;; + } + + dimension: mxpn { + hidden: yes + type: number + sql: ${TABLE}.MXPN ;; + } + + dimension: pgtm { + hidden: yes + type: number + sql: ${TABLE}.PGTM ;; + } + + dimension: prcp { + label: "Precipitation (mm)" + type: number + sql: ${TABLE}.PRCP/10.0 ;; + } + + dimension: psun { + hidden: yes # not populated + label: "Percent of Day Sunny (%)" + value_format_name: percent_1 + type: number + sql: ${TABLE}.PSUN ;; + } + + dimension: sn31 { + hidden: yes + type: number + sql: ${TABLE}.SN31 ;; + } + + dimension: sn32 { + hidden: yes + type: number + sql: ${TABLE}.SN32 ;; + } + + dimension: sn33 { + hidden: yes + type: number + sql: ${TABLE}.SN33 ;; + } + + dimension: sn35 { + hidden: yes + type: number + sql: ${TABLE}.SN35 ;; + } + + dimension: sn36 { + hidden: yes + type: number + sql: ${TABLE}.SN36 ;; + } + + dimension: sn51 { + hidden: yes + type: number + sql: ${TABLE}.SN51 ;; + } + + dimension: sn52 { + hidden: yes + type: number + sql: ${TABLE}.SN52 ;; + } + + dimension: sn53 { + hidden: yes + type: number + sql: ${TABLE}.SN53 ;; + } + + dimension: sn55 { + hidden: yes + type: number + sql: ${TABLE}.SN55 ;; + } + + dimension: sn56 { + hidden: yes + type: number + sql: ${TABLE}.SN56 ;; + } + + dimension: sn57 { + hidden: yes + type: number + sql: ${TABLE}.SN57 ;; + } + + dimension: snow { + label: "Snowfall (mm)" + type: number + sql: ${TABLE}.SNOW ;; + } + + dimension: snwd { + label: "Snow Depth (mm)" + type: number + sql: ${TABLE}.SNWD ;; + } + + dimension: store_id { + hidden: yes + type: number + sql: ${TABLE}.store_id ;; + } + + dimension: sx31 { + hidden: yes + type: number + sql: ${TABLE}.SX31 ;; + } + + dimension: sx32 { + hidden: yes + type: number + sql: ${TABLE}.SX32 ;; + } + + dimension: sx33 { + hidden: yes + type: number + sql: ${TABLE}.SX33 ;; + } + + dimension: sx35 { + hidden: yes + type: number + sql: ${TABLE}.SX35 ;; + } + + dimension: sx36 { + hidden: yes + type: number + sql: ${TABLE}.SX36 ;; + } + + dimension: sx51 { + hidden: yes + type: number + sql: ${TABLE}.SX51 ;; + } + + dimension: sx52 { + hidden: yes + type: number + sql: ${TABLE}.SX52 ;; + } + + dimension: sx53 { + hidden: yes + type: number + sql: ${TABLE}.SX53 ;; + } + + dimension: sx55 { + hidden: yes + type: number + sql: ${TABLE}.SX55 ;; + } + + dimension: sx56 { + hidden: yes + type: number + sql: ${TABLE}.SX56 ;; + } + + dimension: sx57 { + hidden: yes + type: number + sql: ${TABLE}.SX57 ;; + } + + dimension: tavg { + hidden: yes + type: number + sql: ${TABLE}.TAVG ;; + } + + dimension: thic { + hidden: yes + type: number + sql: ${TABLE}.THIC ;; + } + + dimension: tmax { + label: "Max Temperature (°C)" + type: number + sql: ${TABLE}.TMAX/10.0 ;; + } + + dimension: tmin { + label: "Min Temperature (°C)" + type: number + sql: ${TABLE}.TMIN/10.0 ;; + } + + dimension: tobs { + hidden: yes + type: number + sql: ${TABLE}.TOBS ;; + } + + dimension: tsun { + hidden: yes + type: number + sql: ${TABLE}.TSUN ;; + } + + dimension: wdf2 { + hidden: yes + type: number + sql: ${TABLE}.WDF2 ;; + } + + dimension: wdf5 { + hidden: yes + type: number + sql: ${TABLE}.WDF5 ;; + } + + dimension: wdfg { + hidden: yes + type: number + sql: ${TABLE}.WDFG ;; + } + + dimension: wdmv { + hidden: yes + type: number + sql: ${TABLE}.WDMV ;; + } + + dimension: wesd { + hidden: yes + type: number + sql: ${TABLE}.WESD ;; + } + + dimension: wesf { + hidden: yes + type: number + sql: ${TABLE}.WESF ;; + } + + dimension: wsf2 { + hidden: yes + type: number + sql: ${TABLE}.WSF2 ;; + } + + dimension: wsf5 { + hidden: yes + type: number + sql: ${TABLE}.WSF5 ;; + } + + dimension: wsfg { + hidden: yes + type: number + sql: ${TABLE}.WSFG ;; + } + + dimension: wsfi { + hidden: yes + type: number + sql: ${TABLE}.WSFI ;; + } + + dimension: wt01 { + hidden: yes + type: number + sql: ${TABLE}.WT01 ;; + } + + dimension: wt02 { + hidden: yes + type: number + sql: ${TABLE}.WT02 ;; + } + + dimension: wt03 { + hidden: yes + type: number + sql: ${TABLE}.WT03 ;; + } + + dimension: wt04 { + hidden: yes + type: number + sql: ${TABLE}.WT04 ;; + } + + dimension: wt05 { + hidden: yes + type: number + sql: ${TABLE}.WT05 ;; + } + + dimension: wt06 { + hidden: yes + type: number + sql: ${TABLE}.WT06 ;; + } + + dimension: wt07 { + hidden: yes + type: number + sql: ${TABLE}.WT07 ;; + } + + dimension: wt08 { + hidden: yes + type: number + sql: ${TABLE}.WT08 ;; + } + + dimension: wt09 { + hidden: yes + type: number + sql: ${TABLE}.WT09 ;; + } + + dimension: wt10 { + hidden: yes + type: number + sql: ${TABLE}.WT10 ;; + } + + dimension: wt11 { + hidden: yes + type: number + sql: ${TABLE}.WT11 ;; + } + + dimension: wt15 { + hidden: yes + type: number + sql: ${TABLE}.WT15 ;; + } + + dimension: wt16 { + hidden: yes + type: number + sql: ${TABLE}.WT16 ;; + } + + dimension: wt17 { + hidden: yes + type: number + sql: ${TABLE}.WT17 ;; + } + + dimension: wt18 { + hidden: yes + type: number + sql: ${TABLE}.WT18 ;; + } + + dimension: wt22 { + hidden: yes + type: number + sql: ${TABLE}.WT22 ;; + } + + ##### DERIVED DIMENSIONS ##### + + dimension: pk { + hidden: yes + primary_key: yes + type: string + sql: CONCAT(CAST(${weather_date} AS STRING),'-',CAST(${store_id} AS STRING)) ;; + } + + ##### MEASURES ##### + + measure: average_max_temparature { + type: average + sql: ${tmax} ;; + value_format: "#,##0.0 \" °C\"" + } + + measure: average_min_temparature { + type: average + sql: ${tmin} ;; + value_format: "#,##0.0 \" °C\"" + } + + measure: average_daily_precipitation { + type: average + sql: ${prcp} ;; + value_format: "#,##0.0 \" mm\"" + } +} diff --git a/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/transaction_detail_ndt.view.lkml b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/transaction_detail_ndt.view.lkml new file mode 100644 index 0000000000000..657cc75c9dd4f --- /dev/null +++ b/python_modules/libraries/dagster-looker/dagster_looker_tests/looker_projects/retail_demo/views/transaction_detail_ndt.view.lkml @@ -0,0 +1,237 @@ +#### Keeping in case helps with block, otherwise delete view as no longer used + +include: "date_comparison.view.lkml" + +view: transaction_detail { + derived_table: { + explore_source: transactions { + column: transaction_id {} + column: customer_id {} + column: channel_id {} + column: gross_margin { field: transactions__line_items.gross_margin } + column: product_id { field: transactions__line_items.product_id } + column: store_id {} + column: sale_price { field: transactions__line_items.sale_price } + column: transaction_raw {} + column: latitude { field: stores.latitude } + column: longitude { field: stores.longitude } + column: store_name { field: stores.name } + column: store_state { field: stores.state } + column: store_sq_ft { field: stores.sq_ft } + column: brand { field: products.brand } + column: category { field: products.category } + column: department { field: products.department } + column: area { field: products.area } + column: product_name { field: products.name } + column: sku { field: products.sku } + column: channel_name { field: channels.name } + column: traffic_source { field: customers.traffic_source } + column: city { field: customers.city } + column: country { field: customers.country } + column: registered_date { field: customers.registered_date } + column: email { field: customers.email } + column: first_name { field: customers.first_name } + column: gender { field: customers.gender } + column: last_name { field: customers.last_name } + column: address_latitude { field: customers.latitude } + column: address_longitude { field: customers.longitude } + column: state { field: customers.state } + column: postcode { field: customers.postcode } + column: customer_average_basket_size { field: customer_facts.customer_average_basket_size } + column: customer_lifetime_gross_margin { field: customer_facts.customer_lifetime_gross_margin } + column: customer_lifetime_sales { field: customer_facts.customer_lifetime_sales } + column: customer_lifetime_transactions { field: customer_facts.customer_lifetime_transactions } + column: customer_lifetime_quantity { field: customer_facts.customer_lifetime_quantity } + column: customer_first_purchase_date { field: customer_facts.customer_first_purchase_date } + } +# datagroup_trigger: daily +# partition_keys: ["transaction_raw"] +# cluster_keys: ["store_name"] + } + + + dimension: transaction_id {type: number} + dimension: customer_id {type: number hidden: yes} + dimension: channel_id {type: number hidden: yes} + dimension: product_id {type: number hidden: yes} + dimension: store_id {type: number hidden: yes} + dimension: sale_price {type: number} + dimension: gross_margin {type: number} + dimension_group: transaction { + type: time + sql: ${TABLE}.transaction_raw ;; + } + dimension: latitude { + view_label: "Store" + type: number + hidden: yes + } + dimension: longitude { + view_label: "Store" + type: number + hidden: yes + } + dimension: store_name {view_label: "Store"} + dimension: store_state {view_label: "Store"} + dimension: store_sq_ft {view_label: "Store" type: number} + dimension: brand {view_label: "Product"} # Optional + dimension: category {view_label: "Product"} + dimension: department {view_label: "Product"} # Optional + dimension: area {view_label: "Product"} # Optional + dimension: product_name {view_label: "Product"} + dimension: sku {view_label: "Product"} # Optional + dimension: channel_name {view_label: "Channel"} + dimension: traffic_source {view_label: "Customer"} # Optional + dimension: city {view_label: "Customer"} # Optional + dimension: country {view_label: "Customer"} + dimension_group: registered { + view_label: "Customer" + type: time + timeframes: [raw,date,week,month,year,day_of_week,week_of_year,month_name,quarter,quarter_of_year] + sql: ${TABLE}.registered_date ;; + } + dimension: email {view_label: "Customer"} # Optional + dimension: first_name {view_label: "Customer"} # Optional + dimension: gender {view_label: "Customer"} # Optional + dimension: last_name {view_label: "Customer"} # Optional + dimension: address_latitude { + view_label: "Customer" + type: number + } # Optional + dimension: address_longitude { + view_label: "Customer" + type: number + } # Optional + dimension: state {view_label: "Customer"} # Optional + dimension: postcode { + view_label: "Customer" + type: zipcode + } + dimension: customer_average_basket_size { type: number view_label: "Customer" } + dimension: customer_lifetime_gross_margin { type: number view_label: "Customer" } + dimension: customer_lifetime_sales { type: number view_label: "Customer" } + dimension: customer_lifetime_transactions { type: number view_label: "Customer" } + dimension: customer_lifetime_quantity { type: number view_label: "Customer" } + dimension_group: customer_first_purchase { type: time timeframes:[raw,date,week,month] view_label: "Customer" } + + ##### DERIVED DIMENSIONS ##### + + set: drill_detail { + fields: [transaction_date,store_name, category, total_sales] + } + + extends: [date_comparison] + + ##### Product Hierarchy ##### + + ##### Stores ##### + + dimension: store_location { + view_label: "Store" + type: location + sql_latitude: ${latitude} ;; + sql_longitude: ${longitude} ;; + } + + dimension: store_size_grouping { + view_label: "Store" + type: string + sql: CASE + WHEN ${store_sq_ft} <= 70000 THEN 'S' + WHEN ${store_sq_ft} <= 100000 THEN 'M' + WHEN ${store_sq_ft} <= 130000 THEN 'L' + WHEN ${store_sq_ft} <= 160000 THEN 'XL' + END ;; + order_by_field: store_size_grouping_order + } + + dimension: store_size_grouping_order { + view_label: "Store" + hidden: yes + type: number + sql: CASE ${store_size_grouping} + WHEN 'S' THEN 1 + WHEN 'M' THEN 2 + WHEN 'L' THEN 3 + WHEN 'XL' THEN 4 + END + ;; + } + + + ##### MEASURES ##### + + measure: number_of_transactions { + type: count_distinct + sql: ${transaction_id} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: number_of_customers { + type: count_distinct + sql: ${customer_id} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: number_of_stores { + view_label: "Store" + type: count_distinct + sql: ${store_id} ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: total_sales { + type: sum + sql: ${sale_price} ;; + value_format_name: usd_0 + drill_fields: [drill_detail*] + } + + measure: total_gross_margin { + type: sum + sql: ${gross_margin} ;; + value_format_name: usd_0 + drill_fields: [drill_detail*] + } + + measure: total_quantity { + type: sum + sql: 1 ;; + value_format_name: decimal_0 + drill_fields: [drill_detail*] + } + + measure: average_basket_size { + type: number + sql: ${total_sales}/NULLIF(${number_of_transactions},0) ;; + value_format_name: usd + drill_fields: [drill_detail*] + } + + measure: average_item_price { + type: number + sql: ${total_sales}/NULLIF(${total_quantity},0) ;; + value_format_name: usd + drill_fields: [drill_detail*] + } + + measure: number_of_customer_transactions { + hidden: yes + type: count_distinct + sql: ${transaction_id} ;; + filters: { + field: customer_id + value: "NOT NULL" + } + } + + measure: percent_customer_transactions { + type: number + sql: ${number_of_customer_transactions}/NULLIF(${number_of_transactions},0) ;; + value_format_name: percent_1 + drill_fields: [drill_detail*] + } +}