From f17ea87a363a58d82d707cee82909c0ef72b3c2d Mon Sep 17 00:00:00 2001 From: Tsvetan Stoychev Date: Sun, 14 Jan 2024 11:26:47 +0100 Subject: [PATCH 1/2] Fixed a problem where we replace way too much where we replace some branding info in Grafana 10.2.2, Added schema upates ClickHouse tables in order to reflect the latest data we expect. Fixed errors in few Plotly panels where we were getting JS errors when there was no data returned. Now we create empty buckets for the days that do not have data for the Bar Chart panels. --- .env | 2 +- Dockerfile | 4 +- build/dashboards/General.json | 40 ++- build/dashboards/Metrics.json | 2 +- build/dashboards/Summary.json | 4 +- default.xml | 8 + docker-compose.quick-test.yaml | 20 +- docker-compose.yaml | 14 +- fill-test-data.js | 123 +++++++ init-schema/1.sql | 68 ++++ init-schema/2.sql | 1 + init-schema/3.sql | 8 + init-schema/4.sql | 12 + init-schema/5.sql | 11 + init-schema/6.sql | 6 + .../ae3e-plotly-panel/screen-width/script.js | 2 - templates/barchart/default/query.sql | 6 +- templates/barchart/fid/query.sql | 6 +- templates/bouncerate/default/script.js | 340 ++++++++++-------- templates/elements/barchart.json | 2 + 20 files changed, 485 insertions(+), 194 deletions(-) create mode 100644 default.xml create mode 100644 fill-test-data.js create mode 100644 init-schema/1.sql create mode 100644 init-schema/2.sql create mode 100644 init-schema/3.sql create mode 100644 init-schema/4.sql create mode 100644 init-schema/5.sql create mode 100644 init-schema/6.sql diff --git a/.env b/.env index b712695..36ba175 100644 --- a/.env +++ b/.env @@ -8,7 +8,7 @@ GF_SECURITY_ADMIN_PASSWORD=password CLICKHOUSE_DB=default CLICKHOUSE_USER=basicrumreader CLICKHOUSE_PASSWORD=testpassword -CLICKHOUSE_CONNECTION_URL="https://basicrum_clickhouse_server_build:8123" +CLICKHOUSE_CONNECTION_URL="http://basicrum_clickhouse_server_build:8123" ### CLICKHOUSE_USER used in: diff --git a/Dockerfile b/Dockerfile index 1a76f3e..f588349 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM grafana/grafana:10.0.5 +FROM grafana/grafana:10.2.2 ###### Customization ######################################## USER root @@ -29,7 +29,7 @@ RUN find /usr/share/grafana/public/build/ -name *.js -exec sed -i 's|({target:"_ RUN find /usr/share/grafana/public/build/ -name *.js -exec sed -i 's|({target:"_blank",id:"version",.*CHANGELOG.md":void 0})|()|g' {} \; ## Remove New Version is available in the Footer -RUN find /usr/share/grafana/public/build/ -name *.js -exec sed -i 's|({target:"_blank",id:"updateVersion",.*grafana_footer"})|()|g' {} \; +RUN find /usr/share/grafana/public/build/ -name *.js -exec sed -i 's|({target:"_blank",id:"updateVersion",text:"New version available!",icon:"download-alt",url:"https://grafana.com/grafana/download?utm_source=grafana_footer"})|()|g' {} \; ## Remove News icon RUN find /usr/share/grafana/public/build/ -name *.js -exec sed -i 's|..createElement(....,{className:.,onClick:.,iconOnly:!0,icon:"rss","aria-label":"News"})|null|g' {} \; diff --git a/build/dashboards/General.json b/build/dashboards/General.json index 4e9c961..1360020 100644 --- a/build/dashboards/General.json +++ b/build/dashboards/General.json @@ -71,6 +71,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -236,7 +238,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(largest_contentful_paint <= 2500) as good,\n countIf(largest_contentful_paint > 2500 AND largest_contentful_paint <= 4000) as needs_improvement,\n countIf(largest_contentful_paint > 4000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND largest_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(largest_contentful_paint <= 2500) as good,\n countIf(largest_contentful_paint > 2500 AND largest_contentful_paint <= 4000) as needs_improvement,\n countIf(largest_contentful_paint > 4000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND largest_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -281,6 +283,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -450,7 +454,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(cumulative_layout_shift <= 0.1) as good,\n countIf(cumulative_layout_shift > 0.1 AND cumulative_layout_shift <= 0.25) as needs_improvement,\n countIf(cumulative_layout_shift > 0.25) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND cumulative_layout_shift is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(cumulative_layout_shift <= 0.1) as good,\n countIf(cumulative_layout_shift > 0.1 AND cumulative_layout_shift <= 0.25) as needs_improvement,\n countIf(cumulative_layout_shift > 0.25) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND cumulative_layout_shift is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -495,6 +499,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -660,7 +666,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(first_byte_duration <= 800) as good,\n countIf(first_byte_duration > 800 AND first_byte_duration <= 1800) as needs_improvement,\n countIf(first_byte_duration > 1800) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND first_byte_duration is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(first_byte_duration <= 800) as good,\n countIf(first_byte_duration > 800 AND first_byte_duration <= 1800) as needs_improvement,\n countIf(first_byte_duration > 1800) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND first_byte_duration is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -705,6 +711,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -870,7 +878,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(first_contentful_paint <= 1800) as good,\n countIf(first_contentful_paint > 1800 AND first_contentful_paint <= 3000) as needs_improvement,\n countIf(first_contentful_paint > 3000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND first_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(first_contentful_paint <= 1800) as good,\n countIf(first_contentful_paint > 1800 AND first_contentful_paint <= 3000) as needs_improvement,\n countIf(first_contentful_paint > 3000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND first_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -915,6 +923,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -1074,7 +1084,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(first_input_delay <= 100) as good,\n countIf(first_input_delay > 100 AND first_input_delay <= 300) as needs_improvement,\n countIf(first_input_delay > 300) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(first_input_delay <= 100) as good,\n countIf(first_input_delay > 100 AND first_input_delay <= 300) as needs_improvement,\n countIf(first_input_delay > 300) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome Mobile'\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -1132,6 +1142,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -1297,7 +1309,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(largest_contentful_paint <= 2500) as good,\n countIf(largest_contentful_paint > 2500 AND largest_contentful_paint <= 4000) as needs_improvement,\n countIf(largest_contentful_paint > 4000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND largest_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(largest_contentful_paint <= 2500) as good,\n countIf(largest_contentful_paint > 2500 AND largest_contentful_paint <= 4000) as needs_improvement,\n countIf(largest_contentful_paint > 4000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND largest_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -1342,6 +1354,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -1511,7 +1525,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(cumulative_layout_shift <= 0.1) as good,\n countIf(cumulative_layout_shift > 0.1 AND cumulative_layout_shift <= 0.25) as needs_improvement,\n countIf(cumulative_layout_shift > 0.25) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND cumulative_layout_shift is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(cumulative_layout_shift <= 0.1) as good,\n countIf(cumulative_layout_shift > 0.1 AND cumulative_layout_shift <= 0.25) as needs_improvement,\n countIf(cumulative_layout_shift > 0.25) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND cumulative_layout_shift is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -1556,6 +1570,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -1721,7 +1737,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(first_byte_duration <= 800) as good,\n countIf(first_byte_duration > 800 AND first_byte_duration <= 1800) as needs_improvement,\n countIf(first_byte_duration > 1800) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND first_byte_duration is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(first_byte_duration <= 800) as good,\n countIf(first_byte_duration > 800 AND first_byte_duration <= 1800) as needs_improvement,\n countIf(first_byte_duration > 1800) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND first_byte_duration is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -1766,6 +1782,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -1931,7 +1949,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(largest_contentful_paint <= 1800) as good,\n countIf(largest_contentful_paint > 1800 AND largest_contentful_paint <= 3000) as needs_improvement,\n countIf(largest_contentful_paint > 3000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND largest_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(largest_contentful_paint <= 1800) as good,\n countIf(largest_contentful_paint > 1800 AND largest_contentful_paint <= 3000) as needs_improvement,\n countIf(largest_contentful_paint > 3000) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND largest_contentful_paint is not null\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", @@ -1976,6 +1994,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ @@ -2135,7 +2155,7 @@ "format": "table", "formattedQuery": "", "intervalFactor": 1, - "query": "SELECT\n toDate(event_date) as t, \n countIf(first_input_delay <= 100) as good,\n countIf(first_input_delay > 100 AND first_input_delay <= 300) as needs_improvement,\n countIf(first_input_delay > 300) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t", + "query": "SELECT\n toDate(event_date) as t, \n countIf(first_input_delay <= 100) as good,\n countIf(first_input_delay > 100 AND first_input_delay <= 300) as needs_improvement,\n countIf(first_input_delay > 300) as poor\n\nFROM $table\n\nWHERE\n $timeFilter\n AND event_type = 'visit_page'\n AND browser_name = 'Chrome'\n AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname))\nGROUP BY t\nORDER BY t\nWITH FILL\nFROM toDate($from)\nTO toDate($to)", "rawQuery": "", "refId": "A", "round": "0s", diff --git a/build/dashboards/Metrics.json b/build/dashboards/Metrics.json index 5fa647c..3a027c6 100644 --- a/build/dashboards/Metrics.json +++ b/build/dashboards/Metrics.json @@ -601,7 +601,7 @@ } }, "onclick": "", - "script": "var xBouncedValue = data.series[0].fields[0].values.buffer;\nvar yBouncedValue = data.series[0].fields[1].values.buffer;\n\nvar xAllValue = data.series[0].fields[0].values.buffer;\nvar yAllValue = data.series[0].fields[2].values.buffer;\n\n// Start: Round bounce rate\nyBouncedValue = yBouncedValue.map(function(val){\n return val.toFixed(2);\n});\n// End: Round bounce rate\n\n// Start: calculate min and max of bounce rate in first 5 sec.\nvar minBounceRate = 999;\nvar maxBounceRate = 0;\n\nvar step = xBouncedValue[1];\nvar limit = 0;\n\nfor (var cnt = 0; limit <= 5000; cnt++) {\n limit = limit + step;\n var val = parseInt(yBouncedValue[cnt]);\n\n if (val > maxBounceRate) {\n maxBounceRate = val;\n }\n\n if (val < minBounceRate) {\n minBounceRate = val;\n }\n}\n// End: calculate min and max of bounce rate in first 5 sec\n\n// Start: calculate range\nvar rangeDistance = 10;\nvar bottomBoundary = 0;\nvar upperBoundary = 100;\n\nif (rangeDistance - minBounceRate > bottomBoundary) {\n bottomBoundary = minBounceRate | 0;\n}\n\nif (maxBounceRate + rangeDistance < upperBoundary) {\n upperBoundary = (maxBounceRate | 0) + rangeDistance;\n}\n\nupperBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reduce(function(prev, curr) {\n return (Math.abs(curr - upperBoundary) < Math.abs(prev - upperBoundary) ? curr : prev);\n});\n\nbottomBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reverse().reduce(function(prev, curr) {\n return (Math.abs(curr - bottomBoundary) < Math.abs(prev - bottomBoundary) ? curr : prev);\n});\n\nif (bottomBoundary == 10) {\n bottomBoundary = 0;\n}\n// End: calculate range\n\n// Start: Add annotations\n// - Iterate yAllValue values\n// - Add annotation on every 5th value\n\nvar annotations = [];\n\nvar i = 0;\nwhile (true) {\n var x = xBouncedValue[i];\n\n if (i % 5 == 0) {\n var y = yBouncedValue[i];\n var annotationText = y + \"%\";\n\n annotations.push({\n \"xref\": \"x\",\n \"yref\": \"y2\",\n \"x\": x,\n \"y\": y,\n \"xanchor\": \"center\",\n \"yanchor\": \"bottom\",\n \"text\": annotationText,\n \"showarrow\": false,\n \"font\": {\n \"family\": \"Arial\",\n \"size\": 12,\n \"color\": \"white\"\n }\n });\n }\n\n i++;\n\n if (x > 5000) {\n break;\n }\n}\n\n// End: Add annotations\n\nvar trace1 = {\n x: xBouncedValue,\n y: yBouncedValue,\n type: 'scatter',\n name: 'Bounce Rate',\n yaxis: \"y2\",\n line: {\n color: 'rgb(219, 64, 82)',\n width: 3\n }\n};\n\nvar trace2 = {\n x: xAllValue,\n y: yAllValue,\n type: 'scatter',\n name: \"Metric - Landing page\",\n yaxis: \"y1\",\n line: {\n color:'rgb(55, 128, 191)',\n width: 3\n }\n};\n\nvar layout = {\n title:'',\n xaxis: {\n fixedrange: true\n },\n yaxis: {\n fixedrange: true\n },\n xaxis2: {\n fixedrange: true\n },\n yaxis2: {\n overlaying: \"y\",\n side: \"right\",\n showgrid: false,\n tickvals: [\n 0,\n 10,\n 20,\n 30,\n 40,\n 50,\n 60,\n 70,\n 80,\n 90,\n 100\n ],\n ticktext: [\n \"10 %\",\n \"20 %\",\n \"30 %\",\n \"40 %\",\n \"50 %\",\n \"60 %\",\n \"70 %\",\n \"80 %\",\n \"90 %\",\n \"100 %\"\n ],\n range: [\n bottomBoundary,\n upperBoundary\n ],\n fixedrange: true,\n }\n};\n\nif (annotations.length > 0) {\n layout[\"annotations\"] = annotations;\n}\n\nreturn { data: [trace1, trace2], layout: layout };" + "script": "var dataFound = true;\n\nvar layout = {\n title: ''\n}\n\nvar trace1 = {};\nvar trace2 = {};\n\nif (!data.series[0]) {\n dataFound = false;\n}\n\nif (dataFound) {\n var xBouncedValue = data.series[0].fields[0].values.buffer;\n var yBouncedValue = data.series[0].fields[1].values.buffer;\n\n var xAllValue = data.series[0].fields[0].values.buffer;\n var yAllValue = data.series[0].fields[2].values.buffer;\n\n // Start: Round bounce rate\n yBouncedValue = yBouncedValue.map(function(val){\n return val.toFixed(2);\n });\n // End: Round bounce rate\n\n // Start: calculate min and max of bounce rate in first 5 sec.\n var minBounceRate = 999;\n var maxBounceRate = 0;\n\n var step = xBouncedValue[1];\n var limit = 0;\n\n for (var cnt = 0; limit <= 5000; cnt++) {\n limit = limit + step;\n var val = parseInt(yBouncedValue[cnt]);\n\n if (val > maxBounceRate) {\n maxBounceRate = val;\n }\n\n if (val < minBounceRate) {\n minBounceRate = val;\n }\n }\n // End: calculate min and max of bounce rate in first 5 sec\n\n // Start: calculate range\n var rangeDistance = 10;\n var bottomBoundary = 0;\n var upperBoundary = 100;\n\n if (rangeDistance - minBounceRate > bottomBoundary) {\n bottomBoundary = minBounceRate | 0;\n }\n\n if (maxBounceRate + rangeDistance < upperBoundary) {\n upperBoundary = (maxBounceRate | 0) + rangeDistance;\n }\n\n upperBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reduce(function(prev, curr) {\n return (Math.abs(curr - upperBoundary) < Math.abs(prev - upperBoundary) ? curr : prev);\n });\n\n bottomBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reverse().reduce(function(prev, curr) {\n return (Math.abs(curr - bottomBoundary) < Math.abs(prev - bottomBoundary) ? curr : prev);\n });\n\n if (bottomBoundary == 10) {\n bottomBoundary = 0;\n }\n // End: calculate range\n\n // Start: Add annotations\n // - Iterate yAllValue values\n // - Add annotation on every 5th value\n\n var annotations = [];\n\n var i = 0;\n while (true) {\n var x = xBouncedValue[i];\n\n if (i % 5 == 0) {\n var y = yBouncedValue[i];\n var annotationText = y + \"%\";\n\n annotations.push({\n \"xref\": \"x\",\n \"yref\": \"y2\",\n \"x\": x,\n \"y\": y,\n \"xanchor\": \"center\",\n \"yanchor\": \"bottom\",\n \"text\": annotationText,\n \"showarrow\": false,\n \"font\": {\n \"family\": \"Arial\",\n \"size\": 12,\n \"color\": \"white\"\n }\n });\n }\n\n i++;\n\n if (x > 5000) {\n break;\n }\n }\n\n // End: Add annotations\n\n var trace1 = {\n x: xBouncedValue,\n y: yBouncedValue,\n type: 'scatter',\n name: 'Bounce Rate',\n yaxis: \"y2\",\n line: {\n color: 'rgb(219, 64, 82)',\n width: 3\n }\n };\n\n var trace2 = {\n x: xAllValue,\n y: yAllValue,\n type: 'scatter',\n name: \"Metric - Landing page\",\n yaxis: \"y1\",\n line: {\n color:'rgb(55, 128, 191)',\n width: 3\n }\n };\n\n var layout = {\n title:'',\n xaxis: {\n fixedrange: true\n },\n yaxis: {\n fixedrange: true\n },\n xaxis2: {\n fixedrange: true\n },\n yaxis2: {\n overlaying: \"y\",\n side: \"right\",\n showgrid: false,\n tickvals: [\n 0,\n 10,\n 20,\n 30,\n 40,\n 50,\n 60,\n 70,\n 80,\n 90,\n 100\n ],\n ticktext: [\n \"10 %\",\n \"20 %\",\n \"30 %\",\n \"40 %\",\n \"50 %\",\n \"60 %\",\n \"70 %\",\n \"80 %\",\n \"90 %\",\n \"100 %\"\n ],\n range: [\n bottomBoundary,\n upperBoundary\n ],\n fixedrange: true,\n }\n };\n\n if (annotations.length > 0) {\n layout[\"annotations\"] = annotations;\n }\n}\n\nif (!dataFound) {\n // Idea from: https://community.plotly.com/t/replacing-an-empty-graph-with-a-message/31497/2\n layout['annotations'] = [\n {\n \"text\": \"No data\",\n \"xref\": \"paper\",\n \"yref\": \"paper\",\n \"showarrow\": false,\n \"font\": {\n \"size\": 28\n }\n }\n ];\n\n layout['xaxis'] = {\n \"visible\": false\n };\n\n layout['yaxis'] = {\n \"visible\": false\n };\n}\n\nreturn { data: [trace1, trace2], layout: layout };" }, "targets": [ { diff --git a/build/dashboards/Summary.json b/build/dashboards/Summary.json index 47e787b..82e3721 100644 --- a/build/dashboards/Summary.json +++ b/build/dashboards/Summary.json @@ -345,7 +345,7 @@ } }, "onclick": "", - "script": "console.log(data);\n\nvar xValue = data.series[0].fields[0].values.buffer;\n\nvar yValue = data.series[0].fields[1].values.buffer;\n\nvar trace1 = {\n x: xValue,\n y: yValue,\n type: \"bar\",\n textposition: \"auto\",\n hoverinfo: \"none\",\n marker: {\n opacity: 0.6,\n line: {\n color: \"rgb(8,48,107)\",\n width: 1.5,\n },\n },\n};\n\nreturn { data: [trace1], layout: { title: \"\" } };\n" + "script": "var xValue = data.series[0].fields[0].values.buffer;\n\nvar yValue = data.series[0].fields[1].values.buffer;\n\nvar trace1 = {\n x: xValue,\n y: yValue,\n type: \"bar\",\n textposition: \"auto\",\n hoverinfo: \"none\",\n marker: {\n opacity: 0.6,\n line: {\n color: \"rgb(8,48,107)\",\n width: 1.5,\n },\n },\n};\n\nreturn { data: [trace1], layout: { title: \"\" } };\n" }, "targets": [ { @@ -536,6 +536,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ diff --git a/default.xml b/default.xml new file mode 100644 index 0000000..6e73aa9 --- /dev/null +++ b/default.xml @@ -0,0 +1,8 @@ + + + + + TTT87mQQi033W*68 + + + \ No newline at end of file diff --git a/docker-compose.quick-test.yaml b/docker-compose.quick-test.yaml index a6e7f94..37d9107 100644 --- a/docker-compose.quick-test.yaml +++ b/docker-compose.quick-test.yaml @@ -6,7 +6,7 @@ services: volumes: - ./clickhouse/data:/var/lib/clickhouse - ./clickhouse/users.d/:/etc/clickhouse-server/users.d/ - - ./clickhouse/start-data/:/start-data/ + # - ./init-schema/:/docker-entrypoint-initdb.d/ ulimits: nproc: 65535 @@ -16,22 +16,8 @@ services: basicrum_grafana_quicktest: build: . - # We need this in order to make the grafana container happy when we have volumes. - # If we don't set the user then we get: - # -------------------------------------------------------------------------------------------------------- - # basicrum_grafana_1 | GF_PATHS_DATA='/var/lib/grafana' is not writable. - # basicrum_grafana_1 | You may have issues with file permissions, more information here: http://docs.grafana.org/installation/docker/#migrate-to-v51-or-later - # basicrum_grafana_1 | mkdir: cannot create directory '/var/lib/grafana/plugins': Permission denied - # -------------------------------------------------------------------------------------------------------- - # - # $UID is passed by the host: - # - # 1.| UID := $(shell id -u) - # 2.| env UID=${UID} docker-compose -f ${dc_path} up -d - # - user: "$UID" - environment: - - GF_AUTH_ANONYMOUS_ENABLED=true + env_file: + - .env ports: - 3300:3000 volumes: diff --git a/docker-compose.yaml b/docker-compose.yaml index f0c3af6..9e72dcc 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -5,19 +5,19 @@ services: build: . ports: - 3200:3000 - environment: - GF_SECURITY_ADMIN_USER: ${GF_SECURITY_ADMIN_USER} - GF_SECURITY_ADMIN_PASSWORD: ${GF_SECURITY_ADMIN_PASSWORD} - CLICKHOUSE_USER: ${CLICKHOUSE_USER} - CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD} - CLICKHOUSE_CONNECTION_URL: ${CLICKHOUSE_CONNECTION_URL} - CLICKHOUSE_CONNECTION_PORT: ${CLICKHOUSE_CONNECTION_PORT} + env_file: + - .env + volumes: + # Mount provisioning related stuff + - ./build/datasources:/etc/grafana/provisioning/datasources + - ./build/dashboards:/etc/grafana/provisioning/dashboards basicrum_clickhouse_server_build: image: clickhouse/clickhouse-server:22.8 volumes: - ./clickhouse/data:/var/lib/clickhouse - ./clickhouse/users.d/:/etc/clickhouse-server/users.d/ + - ./init-schema/:/docker-entrypoint-initdb.d/ ulimits: nproc: 65535 diff --git a/fill-test-data.js b/fill-test-data.js new file mode 100644 index 0000000..3760995 --- /dev/null +++ b/fill-test-data.js @@ -0,0 +1,123 @@ +const fetch = require('node-fetch'); + +const params = { + "mob.etype": "4g", + "mob.dl": "7.2", + "mob.rtt": "100", + "c.e": "lp7es7qn", + "c.tti.m": "lt", + "pt.lcp": "2730", + "rt.start": "navigation", + "vrt.bmr": "2708%2C3%2C1%2C1", + "rt.tstart": "1700515098671", + "rt.bstart": "1700515101486", + "rt.blstart": "1700515101379", + "rt.end": "1700515101494", + "t_resp": "2280", + "t_page": "543", + "t_done": "2823", + "t_other": "boomerang%7C7%2Cboomr_fb%7C2815%2Cboomr_ld%7C2708%2Cboomr_lat%7C107", + "rt.tt": "2823", + "rt.obo": "0", + "pt.fp": "2730", + "pt.fcp": "2730", + "nt_nav_st": "1700515098671", + "nt_red_st": "1700515098677", + "nt_red_end": "1700515100544", + "nt_fet_st": "1700515100544", + "nt_dns_st": "1700515100544", + "nt_dns_end": "1700515100544", + "nt_con_st": "1700515100544", + "nt_con_end": "1700515100544", + "nt_req_st": "1700515100545", + "nt_res_st": "1700515100951", + "nt_res_end": "1700515100952", + "nt_domloading": "1700515100955", + "nt_domint": "1700515101379", + "nt_domcontloaded_st": "1700515101379", + "nt_domcontloaded_end": "1700515101397", + "nt_domcomp": "1700515101493", + "nt_load_st": "1700515101493", + "nt_load_end": "1700515101494", + "nt_ssl_st": "1700515100544", + "nt_enc_size": "12686", + "nt_dec_size": "46328", + "nt_trn_size": "12986", + "nt_protocol": "h2", + "nt_first_paint": "1700515101401", + "nt_red_cnt": "1", + "nt_nav_type": "0", + "u": "https://calendar.perfplanet.com/", + "v": "1.737.60", + "sv": "14", + "vsm": "p", + "rt.si": "8f761221-e36e-4a8b-91d0-bbd9cd6a4bbd-s4fxuj", + "rt.ss": "1700515098671", + "rt.sl": "1", + "vis.st": "visible", + "ua.plt": "Linux%20x86_64", + "ua.vnd": "Google%20Inc.", + "pid": "q7ompqto", + "n": "1", + "c.l": "22d8%2Cx6q%2Cy70%7C22k6%2Cx5g%2Cy3k%7C243q%2Cx88%2Cyeo%7C24ao%2Cxkf%2Cyhl%7C24hm%2Cxve%2Cyj4", + "c.t.fps": "07*j*65*a*64045*a*65*4*63", + "c.t.inter": "0*4*01", + "c.t.mouse": "0.j8..1s..2y._2*j*0.bg..4y..6t..3x..7a.p", + "c.t.mousepct": "20033030603*k*00200912071401", + "c.tti.vr": "2731", + "c.tti": "2731", + "c.f": "57", + "c.f.d": "5052", + "c.f.m": "3", + "c.f.l": "1", + "c.f.s": "lp7es9wx", + "c.m.p": "108", + "c.m.n": "2190", + "c.i.a": "3", + "c.fid": "3", + "c.ttfi": "3258.89990234375", + "dom.res": "37", + "dom.doms": "1", + "mem.total": "11471320", + "mem.limit": "4294705152", + "mem.used": "9987268", + "mem.lsln": "1", + "mem.ssln": "0", + "mem.lssz": "548", + "mem.sssz": "2", + "scr.xy": "1920x1080", + "scr.bpp": "24%2F24", + "scr.orn": "0%2Flandscape-primary", + "cpu.cnc": "8", + "dom.ln": "503", + "dom.sz": "44904", + "dom.ck": "163", + "dom.img": "34", + "dom.img.uniq": "30", + "dom.script": "7", + "dom.script.ext": "3", + "dom.iframe": "0", + "dom.link": "9", + "dom.link.css": "2", + "sb": "1" +} + +console.log(params); + +var formBody = []; +for (var property in params) { + var encodedKey = encodeURIComponent(property); + var encodedValue = encodeURIComponent(params[property]); + formBody.push(encodedKey + "=" + encodedValue); +} +formBody = formBody.join("&"); + +console.log(formBody); + +fetch('https://ocetinssl.basicrum.com/beacon/catcher', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' + }, + body: formBody +}) diff --git a/init-schema/1.sql b/init-schema/1.sql new file mode 100644 index 0000000..a281373 --- /dev/null +++ b/init-schema/1.sql @@ -0,0 +1,68 @@ +CREATE TABLE IF NOT EXISTS default.webperf_rum_events ( + event_date Date DEFAULT toDate(created_at), + hostname LowCardinality(String), + created_at DateTime, + event_type LowCardinality(String), + browser_name LowCardinality(String), + browser_version Nullable(String), + ua_vnd LowCardinality(Nullable(String)), + ua_plt LowCardinality(Nullable(String)), + device_type LowCardinality(String), + device_manufacturer LowCardinality(Nullable(String)), + operating_system LowCardinality(String), + operating_system_version Nullable(String), + user_agent Nullable(String), + next_hop_protocol LowCardinality(String), + visibility_state LowCardinality(String), + + session_id FixedString(43), + session_length UInt8, + url String, + connect_duration Nullable(UInt16), + dns_duration Nullable(UInt16), + first_byte_duration Nullable(UInt16), + redirect_duration Nullable(UInt16), + redirects_count UInt8, + + first_contentful_paint Nullable(UInt16), + first_paint Nullable(UInt16), + + cumulative_layout_shift Nullable(Float32), + first_input_delay Nullable(UInt16), + largest_contentful_paint Nullable(UInt16), + + geo_country_code FixedString(2), + geo_city_name Nullable(String), + page_id FixedString(8), + + data_saver_on Nullable(UInt8), + + boomerang_version LowCardinality(String), + screen_width Nullable(UInt16), + screen_height Nullable(UInt16), + + dom_res Nullable(UInt16), + dom_doms Nullable(UInt16), + mem_total Nullable(UInt32), + mem_limit Nullable(UInt32), + mem_used Nullable(UInt32), + mem_lsln Nullable(UInt32), + mem_ssln Nullable(UInt32), + mem_lssz Nullable(UInt32), + scr_bpp Nullable(String), + scr_orn Nullable(String), + cpu_cnc Nullable(UInt8), + dom_ln Nullable(UInt16), + dom_sz Nullable(UInt16), + dom_ck Nullable(UInt16), + dom_img Nullable(UInt16), + dom_img_uniq Nullable(UInt16), + dom_script Nullable(UInt16), + dom_iframe Nullable(UInt16), + dom_link Nullable(UInt16), + dom_link_css Nullable(UInt16) +) +ENGINE = MergeTree() +PARTITION BY toYYYYMMDD(event_date) +ORDER BY (hostname, event_date) +SETTINGS index_granularity = 8192 \ No newline at end of file diff --git a/init-schema/2.sql b/init-schema/2.sql new file mode 100644 index 0000000..0537e13 --- /dev/null +++ b/init-schema/2.sql @@ -0,0 +1 @@ +ALTER TABLE default.webperf_rum_events ADD COLUMN mob_dl Nullable(UInt16) , ADD COLUMN mob_rtt Nullable(UInt16), ADD COLUMN mob_etype LowCardinality(Nullable(String)) \ No newline at end of file diff --git a/init-schema/3.sql b/init-schema/3.sql new file mode 100644 index 0000000..f2980c9 --- /dev/null +++ b/init-schema/3.sql @@ -0,0 +1,8 @@ +CREATE TABLE IF NOT EXISTS default.webperf_rum_hostnames ( + hostname LowCardinality(String), + updated_at DateTime64(3) DEFAULT now() +) +ENGINE = ReplacingMergeTree +PARTITION BY hostname +ORDER BY hostname +SETTINGS index_granularity = 8192 \ No newline at end of file diff --git a/init-schema/4.sql b/init-schema/4.sql new file mode 100644 index 0000000..983a93a --- /dev/null +++ b/init-schema/4.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS default.webperf_rum_own_hostnames ( + username LowCardinality(String), + hostname LowCardinality(String), + subscription_id String, + subscription_expire_at DateTime64(3) NOT NULL, + updated_at DateTime64(3) DEFAULT now(), + INDEX index_username username TYPE bloom_filter GRANULARITY 1 +) +ENGINE = ReplacingMergeTree(updated_at) +ORDER BY hostname +PARTITION BY hostname +PRIMARY KEY hostname \ No newline at end of file diff --git a/init-schema/5.sql b/init-schema/5.sql new file mode 100644 index 0000000..a6a9fd1 --- /dev/null +++ b/init-schema/5.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS default.webperf_rum_grant_hostnames ( + username LowCardinality(String), + hostname LowCardinality(String), + owner_username LowCardinality(String), + updated_at DateTime64(3) DEFAULT now(), + INDEX index_owner owner_username TYPE bloom_filter GRANULARITY 1 +) +ENGINE = ReplacingMergeTree(updated_at) +ORDER BY (username, hostname) +PARTITION BY username +PRIMARY KEY (username, hostname) \ No newline at end of file diff --git a/init-schema/6.sql b/init-schema/6.sql new file mode 100644 index 0000000..e0b4ae3 --- /dev/null +++ b/init-schema/6.sql @@ -0,0 +1,6 @@ +CREATE OR REPLACE VIEW default.webperf_rum_view_hostnames AS +SELECT username, hostname, 'owner' as role_name +FROM default.webperf_rum_own_hostnames FINAL +UNION ALL +SELECT username, hostname, 'granted' as role_name +FROM default.webperf_rum_grant_hostnames FINAL \ No newline at end of file diff --git a/templates/ae3e-plotly-panel/screen-width/script.js b/templates/ae3e-plotly-panel/screen-width/script.js index 6b42073..c2221f3 100644 --- a/templates/ae3e-plotly-panel/screen-width/script.js +++ b/templates/ae3e-plotly-panel/screen-width/script.js @@ -1,5 +1,3 @@ -console.log(data); - var xValue = data.series[0].fields[0].values.buffer; var yValue = data.series[0].fields[1].values.buffer; diff --git a/templates/barchart/default/query.sql b/templates/barchart/default/query.sql index 8e09d5e..0718cf6 100644 --- a/templates/barchart/default/query.sql +++ b/templates/barchart/default/query.sql @@ -12,4 +12,8 @@ WHERE AND browser_name = '$$browser_name' AND $$column_name is not null AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname)) -GROUP BY t \ No newline at end of file +GROUP BY t +ORDER BY t +WITH FILL +FROM toDate($from) +TO toDate($to) \ No newline at end of file diff --git a/templates/barchart/fid/query.sql b/templates/barchart/fid/query.sql index 017f618..6cdb0d6 100644 --- a/templates/barchart/fid/query.sql +++ b/templates/barchart/fid/query.sql @@ -11,4 +11,8 @@ WHERE AND event_type = 'visit_page' AND browser_name = '$$browser_name' AND hostname in (SELECT hostname from webperf_rum_view_hostnames where username = '${__user.login}' $conditionalTest(AND hostname in($hostname), $hostname)) -GROUP BY t \ No newline at end of file +GROUP BY t +ORDER BY t +WITH FILL +FROM toDate($from) +TO toDate($to) \ No newline at end of file diff --git a/templates/bouncerate/default/script.js b/templates/bouncerate/default/script.js index dbb818d..dc1c542 100644 --- a/templates/bouncerate/default/script.js +++ b/templates/bouncerate/default/script.js @@ -1,176 +1,214 @@ -var xBouncedValue = data.series[0].fields[0].values.buffer; -var yBouncedValue = data.series[0].fields[1].values.buffer; +var dataFound = true; -var xAllValue = data.series[0].fields[0].values.buffer; -var yAllValue = data.series[0].fields[2].values.buffer; +var layout = { + title: '' +} -// Start: Round bounce rate -yBouncedValue = yBouncedValue.map(function(val){ - return val.toFixed(2); -}); -// End: Round bounce rate +var trace1 = {}; +var trace2 = {}; -// Start: calculate min and max of bounce rate in first 5 sec. -var minBounceRate = 999; -var maxBounceRate = 0; +if (!data.series[0]) { + dataFound = false; +} -var step = xBouncedValue[1]; -var limit = 0; +if (dataFound) { + var xBouncedValue = data.series[0].fields[0].values.buffer; + var yBouncedValue = data.series[0].fields[1].values.buffer; -for (var cnt = 0; limit <= 5000; cnt++) { - limit = limit + step; - var val = parseInt(yBouncedValue[cnt]); + var xAllValue = data.series[0].fields[0].values.buffer; + var yAllValue = data.series[0].fields[2].values.buffer; - if (val > maxBounceRate) { - maxBounceRate = val; - } + // Start: Round bounce rate + yBouncedValue = yBouncedValue.map(function(val){ + return val.toFixed(2); + }); + // End: Round bounce rate - if (val < minBounceRate) { - minBounceRate = val; - } -} -// End: calculate min and max of bounce rate in first 5 sec + // Start: calculate min and max of bounce rate in first 5 sec. + var minBounceRate = 999; + var maxBounceRate = 0; -// Start: calculate range -var rangeDistance = 10; -var bottomBoundary = 0; -var upperBoundary = 100; + var step = xBouncedValue[1]; + var limit = 0; -if (rangeDistance - minBounceRate > bottomBoundary) { - bottomBoundary = minBounceRate | 0; -} + for (var cnt = 0; limit <= 5000; cnt++) { + limit = limit + step; + var val = parseInt(yBouncedValue[cnt]); -if (maxBounceRate + rangeDistance < upperBoundary) { - upperBoundary = (maxBounceRate | 0) + rangeDistance; -} + if (val > maxBounceRate) { + maxBounceRate = val; + } -upperBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reduce(function(prev, curr) { - return (Math.abs(curr - upperBoundary) < Math.abs(prev - upperBoundary) ? curr : prev); -}); + if (val < minBounceRate) { + minBounceRate = val; + } + } + // End: calculate min and max of bounce rate in first 5 sec -bottomBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reverse().reduce(function(prev, curr) { - return (Math.abs(curr - bottomBoundary) < Math.abs(prev - bottomBoundary) ? curr : prev); -}); + // Start: calculate range + var rangeDistance = 10; + var bottomBoundary = 0; + var upperBoundary = 100; -if (bottomBoundary == 10) { - bottomBoundary = 0; -} -// End: calculate range - -// Start: Add annotations -// - Iterate yAllValue values -// - Add annotation on every 5th value - -var annotations = []; - -var i = 0; -while (true) { - var x = xBouncedValue[i]; - - if (i % 5 == 0) { - var y = yBouncedValue[i]; - var annotationText = y + "%"; - - annotations.push({ - "xref": "x", - "yref": "y2", - "x": x, - "y": y, - "xanchor": "center", - "yanchor": "bottom", - "text": annotationText, - "showarrow": false, - "font": { - "family": "Arial", - "size": 12, - "color": "white" - } - }); + if (rangeDistance - minBounceRate > bottomBoundary) { + bottomBoundary = minBounceRate | 0; } - i++; - - if (x > 5000) { - break; + if (maxBounceRate + rangeDistance < upperBoundary) { + upperBoundary = (maxBounceRate | 0) + rangeDistance; } -} -// End: Add annotations - -var trace1 = { - x: xBouncedValue, - y: yBouncedValue, - type: 'scatter', - name: 'Bounce Rate', - yaxis: "y2", - line: { - color: 'rgb(219, 64, 82)', - width: 3 + upperBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reduce(function(prev, curr) { + return (Math.abs(curr - upperBoundary) < Math.abs(prev - upperBoundary) ? curr : prev); + }); + + bottomBoundary = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reverse().reduce(function(prev, curr) { + return (Math.abs(curr - bottomBoundary) < Math.abs(prev - bottomBoundary) ? curr : prev); + }); + + if (bottomBoundary == 10) { + bottomBoundary = 0; } -}; - -var trace2 = { - x: xAllValue, - y: yAllValue, - type: 'scatter', - name: "Metric - Landing page", - yaxis: "y1", - line: { - color:'rgb(55, 128, 191)', - width: 3 + // End: calculate range + + // Start: Add annotations + // - Iterate yAllValue values + // - Add annotation on every 5th value + + var annotations = []; + + var i = 0; + while (true) { + var x = xBouncedValue[i]; + + if (i % 5 == 0) { + var y = yBouncedValue[i]; + var annotationText = y + "%"; + + annotations.push({ + "xref": "x", + "yref": "y2", + "x": x, + "y": y, + "xanchor": "center", + "yanchor": "bottom", + "text": annotationText, + "showarrow": false, + "font": { + "family": "Arial", + "size": 12, + "color": "white" + } + }); + } + + i++; + + if (x > 5000) { + break; + } } -}; -var layout = { - title:'', - xaxis: { - fixedrange: true - }, - yaxis: { - fixedrange: true - }, - xaxis2: { - fixedrange: true - }, - yaxis2: { - overlaying: "y", - side: "right", - showgrid: false, - tickvals: [ - 0, - 10, - 20, - 30, - 40, - 50, - 60, - 70, - 80, - 90, - 100 - ], - ticktext: [ - "10 %", - "20 %", - "30 %", - "40 %", - "50 %", - "60 %", - "70 %", - "80 %", - "90 %", - "100 %" - ], - range: [ - bottomBoundary, - upperBoundary - ], - fixedrange: true, + // End: Add annotations + + var trace1 = { + x: xBouncedValue, + y: yBouncedValue, + type: 'scatter', + name: 'Bounce Rate', + yaxis: "y2", + line: { + color: 'rgb(219, 64, 82)', + width: 3 + } + }; + + var trace2 = { + x: xAllValue, + y: yAllValue, + type: 'scatter', + name: "Metric - Landing page", + yaxis: "y1", + line: { + color:'rgb(55, 128, 191)', + width: 3 + } + }; + + var layout = { + title:'', + xaxis: { + fixedrange: true + }, + yaxis: { + fixedrange: true + }, + xaxis2: { + fixedrange: true + }, + yaxis2: { + overlaying: "y", + side: "right", + showgrid: false, + tickvals: [ + 0, + 10, + 20, + 30, + 40, + 50, + 60, + 70, + 80, + 90, + 100 + ], + ticktext: [ + "10 %", + "20 %", + "30 %", + "40 %", + "50 %", + "60 %", + "70 %", + "80 %", + "90 %", + "100 %" + ], + range: [ + bottomBoundary, + upperBoundary + ], + fixedrange: true, + } + }; + + if (annotations.length > 0) { + layout["annotations"] = annotations; } -}; +} + +if (!dataFound) { + // Idea from: https://community.plotly.com/t/replacing-an-empty-graph-with-a-message/31497/2 + layout['annotations'] = [ + { + "text": "No data", + "xref": "paper", + "yref": "paper", + "showarrow": false, + "font": { + "size": 28 + } + } + ]; + + layout['xaxis'] = { + "visible": false + }; -if (annotations.length > 0) { - layout["annotations"] = annotations; + layout['yaxis'] = { + "visible": false + }; } return { data: [trace1, trace2], layout: layout }; \ No newline at end of file diff --git a/templates/elements/barchart.json b/templates/elements/barchart.json index 5942086..8b8e9c2 100644 --- a/templates/elements/barchart.json +++ b/templates/elements/barchart.json @@ -29,6 +29,8 @@ } }, "mappings": [], + "max": 1, + "min": 0, "thresholds": { "mode": "absolute", "steps": [ From 5d0ecdf9c04bffce3dd7f89700e41f5dae154425 Mon Sep 17 00:00:00 2001 From: Tsvetan Stoychev Date: Sat, 20 Jan 2024 20:31:36 +0100 Subject: [PATCH 2/2] Removed not needed shema updates. --- init-schema/3.sql | 8 -------- init-schema/4.sql | 12 ------------ init-schema/5.sql | 11 ----------- init-schema/6.sql | 6 ------ 4 files changed, 37 deletions(-) delete mode 100644 init-schema/3.sql delete mode 100644 init-schema/4.sql delete mode 100644 init-schema/5.sql delete mode 100644 init-schema/6.sql diff --git a/init-schema/3.sql b/init-schema/3.sql deleted file mode 100644 index f2980c9..0000000 --- a/init-schema/3.sql +++ /dev/null @@ -1,8 +0,0 @@ -CREATE TABLE IF NOT EXISTS default.webperf_rum_hostnames ( - hostname LowCardinality(String), - updated_at DateTime64(3) DEFAULT now() -) -ENGINE = ReplacingMergeTree -PARTITION BY hostname -ORDER BY hostname -SETTINGS index_granularity = 8192 \ No newline at end of file diff --git a/init-schema/4.sql b/init-schema/4.sql deleted file mode 100644 index 983a93a..0000000 --- a/init-schema/4.sql +++ /dev/null @@ -1,12 +0,0 @@ -CREATE TABLE IF NOT EXISTS default.webperf_rum_own_hostnames ( - username LowCardinality(String), - hostname LowCardinality(String), - subscription_id String, - subscription_expire_at DateTime64(3) NOT NULL, - updated_at DateTime64(3) DEFAULT now(), - INDEX index_username username TYPE bloom_filter GRANULARITY 1 -) -ENGINE = ReplacingMergeTree(updated_at) -ORDER BY hostname -PARTITION BY hostname -PRIMARY KEY hostname \ No newline at end of file diff --git a/init-schema/5.sql b/init-schema/5.sql deleted file mode 100644 index a6a9fd1..0000000 --- a/init-schema/5.sql +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE IF NOT EXISTS default.webperf_rum_grant_hostnames ( - username LowCardinality(String), - hostname LowCardinality(String), - owner_username LowCardinality(String), - updated_at DateTime64(3) DEFAULT now(), - INDEX index_owner owner_username TYPE bloom_filter GRANULARITY 1 -) -ENGINE = ReplacingMergeTree(updated_at) -ORDER BY (username, hostname) -PARTITION BY username -PRIMARY KEY (username, hostname) \ No newline at end of file diff --git a/init-schema/6.sql b/init-schema/6.sql deleted file mode 100644 index e0b4ae3..0000000 --- a/init-schema/6.sql +++ /dev/null @@ -1,6 +0,0 @@ -CREATE OR REPLACE VIEW default.webperf_rum_view_hostnames AS -SELECT username, hostname, 'owner' as role_name -FROM default.webperf_rum_own_hostnames FINAL -UNION ALL -SELECT username, hostname, 'granted' as role_name -FROM default.webperf_rum_grant_hostnames FINAL \ No newline at end of file