From 94167144516d8e06c13ca0c8e2369aa96aeff0c0 Mon Sep 17 00:00:00 2001 From: bendavidson Date: Tue, 6 Sep 2022 09:46:05 +0100 Subject: [PATCH 1/8] Add convert_km function to the database --- ...6fbdea639cc5_create_convert_km_function.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py diff --git a/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py b/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py new file mode 100644 index 00000000..4546fc96 --- /dev/null +++ b/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py @@ -0,0 +1,23 @@ +"""create convert_km function + +Revision ID: 6fbdea639cc5 +Revises: f917fc564c1d +Create Date: 2022-09-02 20:49:51.707701 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '6fbdea639cc5' +down_revision = 'f917fc564c1d' +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute("CREATE FUNCTION convert_km(n double precision, unit text) RETURNS double precision AS $$ SELECT CASE WHEN $2 = 'km' THEN $1 WHEN $2 = 'mi' THEN $1 / 1.60934 END; $$ LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;") + +def downgrade(): + op.execute("DROP FUNCTION convert_km;") From b321c81fca0c6b04a874999e454751c18fea0da1 Mon Sep 17 00:00:00 2001 From: bendavidson Date: Wed, 7 Sep 2022 14:04:25 +0100 Subject: [PATCH 2/8] Added context check to alembic migration --- .../6fbdea639cc5_create_convert_km_function.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py b/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py index 4546fc96..7105fc87 100644 --- a/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py +++ b/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py @@ -17,7 +17,21 @@ def upgrade(): - op.execute("CREATE FUNCTION convert_km(n double precision, unit text) RETURNS double precision AS $$ SELECT CASE WHEN $2 = 'km' THEN $1 WHEN $2 = 'mi' THEN $1 / 1.60934 END; $$ LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;") + if op.get_context().dialect.name == 'postgresql': + op.execute(""" + CREATE FUNCTION convert_km(n double precision, unit text) + RETURNS double precision + AS $$ + SELECT + CASE WHEN $2 = 'km' THEN $1 + WHEN $2 = 'mi' THEN $1 / 1.60934 + END; + $$ + LANGUAGE SQL + IMMUTABLE + RETURNS NULL ON NULL INPUT; + """) def downgrade(): - op.execute("DROP FUNCTION convert_km;") + if op.get_context().dialect.name == 'postgresql': + op.execute("DROP FUNCTION convert_km;") From 6016fd0c94988bcfb226b71b5aa40aded7b5079a Mon Sep 17 00:00:00 2001 From: Ben Davidson Date: Fri, 9 Sep 2022 09:54:02 +0100 Subject: [PATCH 3/8] Alter convert_km function to user upper case values matching unit_of_length type --- .../versions/6fbdea639cc5_create_convert_km_function.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py b/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py index 7105fc87..2e56a509 100644 --- a/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py +++ b/vwsfriend/vwsfriend/model/vwsfriend-schema/versions/6fbdea639cc5_create_convert_km_function.py @@ -23,8 +23,8 @@ def upgrade(): RETURNS double precision AS $$ SELECT - CASE WHEN $2 = 'km' THEN $1 - WHEN $2 = 'mi' THEN $1 / 1.60934 + CASE WHEN $2 = 'KM' THEN $1 + WHEN $2 = 'MI' THEN $1 / 1.60934 END; $$ LANGUAGE SQL From 4b8780469818a89b90bd3773c1d1d5ee7cb4a896 Mon Sep 17 00:00:00 2001 From: Ben Davidson Date: Fri, 9 Sep 2022 15:50:29 +0100 Subject: [PATCH 4/8] Add length of unit changes to Overview Dashboard --- .../vwsfriend/VWsFriend/overview.json | 227 ++++++++++++++++-- 1 file changed, 204 insertions(+), 23 deletions(-) diff --git a/grafana/dashboards/vwsfriend/VWsFriend/overview.json b/grafana/dashboards/vwsfriend/VWsFriend/overview.json index ec5f72f1..20278733 100644 --- a/grafana/dashboards/vwsfriend/VWsFriend/overview.json +++ b/grafana/dashboards/vwsfriend/VWsFriend/overview.json @@ -356,7 +356,7 @@ { "matcher": { "id": "byRegexp", - "options": "/.*Range/" + "options": ".*_KM$" }, "properties": [ { @@ -366,6 +366,30 @@ { "id": "custom.axisLabel", "value": "Range" + }, + { + "id": "displayName", + "value": "Electric Range" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": ".*_MI$" + }, + "properties": [ + { + "id": "unit", + "value": "lengthmi" + }, + { + "id": "custom.axisLabel", + "value": "Range" + }, + { + "id": "displayName", + "value": "Electric Range" } ] } @@ -462,7 +486,7 @@ "hide": false, "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n \"cruisingRangeElectric_km\" AS \"Electric Range\"\nFROM battery\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1 ", + "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n convert_km(\"cruisingRangeElectric_km\",'${length_unit.value}') AS \"Electric Range_${length_unit.value}\"\nFROM battery\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1 ", "refId": "1", "select": [ [ @@ -709,7 +733,20 @@ }, "unit": "lengthkm" }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": ".*_MI$" + }, + "properties": [ + { + "id": "unit", + "value": "lengthmi" + } + ] + } + ] }, "gridPos": { "h": 8, @@ -744,7 +781,7 @@ "query": "", "queryType": "randomWalk", "rawQuery": true, - "rawSql": "(SELECT\n to_timestamp(0) AS \"time\",\n CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_wltp_range\"::NUMERIC\n ELSE vehicle_settings.\"secondary_wltp_range\"::NUMERIC END AS \"Electric Range\"\nFROM vehicles\nLEFT JOIN vehicle_settings ON vehicles.vin = vehicle_settings.vehicle_vin\nWHERE\n vehicles.vin = '$VIN')\nUNION\n(SELECT\n \"carCapturedTimestamp\" AS \"time\",\n \"cruisingRangeElectric_km\" AS \"Electric Range\"\nFROM battery\nWHERE\n battery.vehicle_vin = '$VIN'\nORDER BY \"time\" DESC\nLIMIT 1)", + "rawSql": "(SELECT\n to_timestamp(0) AS \"time\",\n convert_km(CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_wltp_range\"::NUMERIC\n ELSE vehicle_settings.\"secondary_wltp_range\"::NUMERIC END,'${length_unit.value}') AS \"Electric Range_${length_unit.value}\"\nFROM vehicles\nLEFT JOIN vehicle_settings ON vehicles.vin = vehicle_settings.vehicle_vin\nWHERE\n vehicles.vin = '$VIN')\nUNION\n(SELECT\n \"carCapturedTimestamp\" AS \"time\",\n convert_km(\"cruisingRangeElectric_km\",'${length_unit.value}') AS \"Electric Range_${length_unit.value}\"\nFROM battery\nWHERE\n battery.vehicle_vin = '$VIN'\nORDER BY \"time\" DESC\nLIMIT 1)", "refId": "A", "resultFormat": "time_series", "select": [ @@ -929,7 +966,7 @@ { "matcher": { "id": "byRegexp", - "options": "/.*Range/" + "options": "/.*KM/" }, "properties": [ { @@ -941,6 +978,58 @@ "value": "Range" } ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*MI/" + }, + "properties": [ + { + "id": "unit", + "value": "lengthmi" + }, + { + "id": "custom.axisLabel", + "value": "Range" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/Primary Engine Range.*/" + }, + "properties": [ + { + "id": "displayName", + "value": "Primary Engine Range" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/Total Range.*/" + }, + "properties": [ + { + "id": "displayName", + "value": "Total Range" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/Secondary Engine Range.*/" + }, + "properties": [ + { + "id": "displayName", + "value": "Secondary Engine Range" + } + ] } ] }, @@ -1035,7 +1124,7 @@ "hide": false, "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n \"totalRange_km\" AS \"Total Range\"\nFROM ranges\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1 ", + "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n convert_km(\"totalRange_km\",'${length_unit.value}') AS \"Total Range_${length_unit.value}\"\nFROM ranges\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1", "refId": "1", "select": [ [ @@ -1080,7 +1169,7 @@ "policy": "default", "query": "", "rawQuery": true, - "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n \"primary_remainingRange_km\" AS \"Primary Engine Range\"\nFROM ranges\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1", + "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n convert_km(\"primary_remainingRange_km\",'${length_unit.value}') AS \"Primary Engine Range_${length_unit.value}\"\nFROM ranges\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1", "refId": "3", "resultFormat": "time_series", "select": [ @@ -1199,7 +1288,7 @@ "policy": "default", "query": "", "rawQuery": true, - "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n \"secondary_remainingRange_km\" AS \"Secondary Engine Range\"\nFROM ranges\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1", + "rawSql": "SELECT\n \"carCapturedTimestamp\" AS \"time\",\n convert_km(\"secondary_remainingRange_km\",'${length_unit.value}') AS \"Secondary Engine Range_${length_unit.value}\"\nFROM ranges\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n vehicle_vin = '$VIN'\nORDER BY 1", "refId": "5", "resultFormat": "time_series", "select": [ @@ -1618,35 +1707,63 @@ { "matcher": { "id": "byName", - "options": "WLTP" + "options": "Calculated consumption_Metric" + }, + "properties": [ + { + "id": "unit", + "value": "kWh/100km" + }, + { + "id": "custom.axisLabel", + "value": "Calculated consumption" + }, + { + "id": "displayName", + "value": "Calculated Consumption" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*_KM/" }, "properties": [ { "id": "unit", - "value": "km" + "value": "lengthkm" } ] }, { "matcher": { - "id": "byName", - "options": "Calculated consumption" + "id": "byRegexp", + "options": "/.*_MI/" }, "properties": [ { "id": "unit", - "value": "kWh/100km" - }, + "value": "lengthmi" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/Current Range at 100.%*/" + }, + "properties": [ { - "id": "custom.axisLabel", - "value": "Calculated consumption" + "id": "displayName", + "value": "Current Range at 100%" } ] }, { "matcher": { - "id": "byName", - "options": "WLTP" + "id": "byRegexp", + "options": "/WLTP.*/" }, "properties": [ { @@ -1663,8 +1780,32 @@ { "id": "custom.lineWidth", "value": 0 + }, + { + "id": "displayName", + "value": "WLTP" } ] + }, + { + "matcher": { + "id": "byName", + "options": "Calculated consumption_Imperial" + }, + "properties": [ + { + "id": "unit", + "value": "mi/kWh" + }, + { + "id": "custom.axisLabel", + "value": "Calculated consumption" + }, + { + "id": "displayName", + "value": "Calculated Consumption" + } + ] } ] }, @@ -1724,7 +1865,7 @@ "query": "", "queryType": "randomWalk", "rawQuery": true, - "rawSql": "SELECT\n $__time(\"carCapturedTimestamp\"),\n CASE WHEN \"currentSOC_pct\"::NUMERIC > 0 THEN \"cruisingRangeElectric_km\"::NUMERIC / \"currentSOC_pct\"::NUMERIC * 100::NUMERIC END AS \"Current Range at 100%\",\n CASE WHEN \"currentSOC_pct\"::NUMERIC = 0 OR \"cruisingRangeElectric_km\"::NUMERIC = 0 THEN NULL\n ELSE\n CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_capacity\"::NUMERIC\n ELSE vehicle_settings.\"secondary_capacity\"::NUMERIC END / (\"cruisingRangeElectric_km\"::NUMERIC / \"currentSOC_pct\"::NUMERIC * 100::NUMERIC) * 100::NUMERIC END AS \"Calculated consumption\"\nFROM\n battery\nLEFT JOIN vehicles ON battery.vehicle_vin = vehicles.vin\nLEFT JOIN vehicle_settings ON battery.vehicle_vin = vehicle_settings.vehicle_vin\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n battery.vehicle_vin = '$VIN'\nORDER BY 1", + "rawSql": "SELECT\n $__time(\"carCapturedTimestamp\"),\n CASE WHEN \"currentSOC_pct\"::NUMERIC > 0 THEN convert_km(\"cruisingRangeElectric_km\"::NUMERIC,'${length_unit.value}') / \"currentSOC_pct\"::NUMERIC * 100::NUMERIC END AS \"Current Range at 100%_${length_unit.value}\",\n CASE WHEN \"currentSOC_pct\"::NUMERIC = 0 OR \"cruisingRangeElectric_km\"::NUMERIC = 0 THEN NULL\n ELSE\n CASE WHEN '$length_unit' = 'MI' THEN\n (convert_km(\"cruisingRangeElectric_km\"::NUMERIC,'${length_unit.value}') / \"currentSOC_pct\"::NUMERIC * 100::NUMERIC) / CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_capacity\"::NUMERIC\nELSE vehicle_settings.\"secondary_capacity\"::NUMERIC END\n ELSE\n CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_capacity\"::NUMERIC\n ELSE vehicle_settings.\"secondary_capacity\"::NUMERIC END / (\"cruisingRangeElectric_km\"::NUMERIC / \"currentSOC_pct\"::NUMERIC * 100::NUMERIC) * 100::NUMERIC\n END\n END AS \"Calculated consumption_${length_unit:text}\"\nFROM\n battery\nLEFT JOIN vehicles ON battery.vehicle_vin = vehicles.vin\nLEFT JOIN vehicle_settings ON battery.vehicle_vin = vehicle_settings.vehicle_vin\nWHERE\n $__timeFilter(\"carCapturedTimestamp\") AND\n battery.vehicle_vin = '$VIN'\nORDER BY 1", "refId": "A", "resultFormat": "time_series", "select": [ @@ -1781,7 +1922,7 @@ "query": "", "queryType": "randomWalk", "rawQuery": true, - "rawSql": "SELECT\n unnest(ARRAY[to_timestamp(${__from}/1000), to_timestamp(${__to}/1000)]) AS time,\n CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_wltp_range\"::NUMERIC\n ELSE vehicle_settings.\"secondary_wltp_range\"::NUMERIC END AS \"WLTP\"\nFROM\n vehicles\nLEFT JOIN vehicle_settings ON vehicles.vin = vehicle_settings.vehicle_vin\nWHERE\n vehicles.vin = '$VIN'\nORDER BY 1", + "rawSql": "SELECT\n unnest(ARRAY[to_timestamp(${__from}/1000), to_timestamp(${__to}/1000)]) AS time,\n convert_km(CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_wltp_range\"::NUMERIC\n ELSE vehicle_settings.\"secondary_wltp_range\"::NUMERIC END,'${length_unit.value}') AS \"WLTP_${length_unit.value}\"\nFROM\n vehicles\nLEFT JOIN vehicle_settings ON vehicles.vin = vehicle_settings.vehicle_vin\nWHERE\n vehicles.vin = '$VIN'\nORDER BY 1", "refId": "B", "resultFormat": "time_series", "select": [ @@ -2153,8 +2294,8 @@ "overrides": [ { "matcher": { - "id": "byName", - "options": "Charge Rate" + "id": "byRegexp", + "options": "/.*KM/" }, "properties": [ { @@ -2164,6 +2305,30 @@ { "id": "custom.axisLabel", "value": "Charge Rate" + }, + { + "id": "displayName", + "value": "Charge Rate" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "/.*MI/" + }, + "properties": [ + { + "id": "unit", + "value": "velocitymph" + }, + { + "id": "custom.axisLabel", + "value": "Charge Rate" + }, + { + "id": "displayName", + "value": "Charge Rate" } ] } @@ -2220,7 +2385,7 @@ "query": "", "queryType": "randomWalk", "rawQuery": true, - "rawSql": "(SELECT\r\n \"carCapturedTimestamp\" AS \"time\",\r\n \"chargePower_kW\" AS \"Charge Power\",\r\n \"chargeRate_kmph\" AS \"Charge Rate\"\r\nFROM charges\r\nWHERE\r\n $__timeFilter(\"carCapturedTimestamp\") AND\r\n vehicle_vin = '$VIN'\r\nORDER BY \"carCapturedTimestamp\")\r\nUNION ALL\r\n(SELECT\r\n \"carCapturedTimestamp\" AS \"time\",\r\n CASE WHEN \"chargingState\" = 'CHARGING' THEN \"delta\"::decimal*( CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_capacity\"::NUMERIC\r\n ELSE vehicle_settings.\"secondary_capacity\"::NUMERIC END::decimal/100::decimal) / (EXTRACT(epoch FROM \"timedelta\") / 3600)\r\n ELSE NULL END AS \"Charge Power\",\r\n CASE WHEN \"chargingState\" = 'CHARGING' THEN \"deltakm\"::decimal / (EXTRACT(epoch FROM \"timedelta\") / 3600)\r\n ELSE NULL END AS \"Charge Rate\"\r\nFROM\r\n (SELECT *,\r\n DATE_TRUNC('minute', \"carCapturedTimestamp\") AS \"batteryTime\",\r\n (\"carCapturedTimestamp\" - LAG(\"carCapturedTimestamp\") OVER (ORDER BY \"carCapturedTimestamp\")) AS \"timedelta\",\r\n (\"currentSOC_pct\" - LAG(\"currentSOC_pct\") OVER (ORDER BY \"carCapturedTimestamp\")) AS \"delta\",\r\n (\"cruisingRangeElectric_km\" - LAG(\"cruisingRangeElectric_km\") OVER (ORDER BY \"carCapturedTimestamp\")) AS \"deltakm\"\r\nFROM\r\n battery\r\nWHERE\r\n $__timeFilter(\"carCapturedTimestamp\") AND\r\n vehicle_vin = '$VIN'\r\nORDER BY \"carCapturedTimestamp\") AS delta\r\nLEFT JOIN\r\n(\r\nSELECT\r\n\"chargingState\",\r\nDATE_TRUNC('minute', charges.\"carCapturedTimestamp\") AS \"chargeTime\"\r\nFROM charges\r\nWHERE\r\n $__timeFilter(\"carCapturedTimestamp\") AND\r\n vehicle_vin = '$VIN') as charging ON \"chargeTime\" = \"batteryTime\"\r\nLEFT JOIN vehicles ON delta.vehicle_vin = vehicles.vin\r\nLEFT JOIN vehicle_settings ON delta.vehicle_vin = vehicle_settings.vehicle_vin\r\nWHERE\r\n \"delta\" > 0 AND\r\n NOT EXISTS (SELECT 1 FROM charges WHERE \"chargePower_kW\" IS NOT NULL AND \"chargeRate_kmph\" IS NOT NULL AND $__timeFilter(\"carCapturedTimestamp\") AND vehicle_vin = '$VIN' LIMIT 1)\r\nORDER BY \"carCapturedTimestamp\")", + "rawSql": "(SELECT\r\n \"carCapturedTimestamp\" AS \"time\",\r\n \"chargePower_kW\" AS \"Charge Power\",\r\n convert_km(\"chargeRate_kmph\",'${length_unit.value}') AS \"Charge Rate_${length_unit.value}\"\r\nFROM charges\r\nWHERE\r\n $__timeFilter(\"carCapturedTimestamp\") AND\r\n vehicle_vin = '$VIN'\r\nORDER BY \"carCapturedTimestamp\")\r\nUNION ALL\r\n(SELECT\r\n \"carCapturedTimestamp\" AS \"time\",\r\n CASE WHEN \"chargingState\" = 'CHARGING' THEN \"delta\"::decimal*( CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_capacity\"::NUMERIC\r\n ELSE vehicle_settings.\"secondary_capacity\"::NUMERIC END::decimal/100::decimal) / (EXTRACT(epoch FROM \"timedelta\") / 3600)\r\n ELSE NULL END AS \"Charge Power\",\r\n CASE WHEN \"chargingState\" = 'CHARGING' THEN \"deltakm\"::decimal / (EXTRACT(epoch FROM \"timedelta\") / 3600)\r\n ELSE NULL END AS \"Charge Rate\"\r\nFROM\r\n (SELECT *,\r\n DATE_TRUNC('minute', \"carCapturedTimestamp\") AS \"batteryTime\",\r\n (\"carCapturedTimestamp\" - LAG(\"carCapturedTimestamp\") OVER (ORDER BY \"carCapturedTimestamp\")) AS \"timedelta\",\r\n (\"currentSOC_pct\" - LAG(\"currentSOC_pct\") OVER (ORDER BY \"carCapturedTimestamp\")) AS \"delta\",\r\n (\"cruisingRangeElectric_km\" - LAG(\"cruisingRangeElectric_km\") OVER (ORDER BY \"carCapturedTimestamp\")) AS \"deltakm\"\r\nFROM\r\n battery\r\nWHERE\r\n $__timeFilter(\"carCapturedTimestamp\") AND\r\n vehicle_vin = '$VIN'\r\nORDER BY \"carCapturedTimestamp\") AS delta\r\nLEFT JOIN\r\n(\r\nSELECT\r\n\"chargingState\",\r\nDATE_TRUNC('minute', charges.\"carCapturedTimestamp\") AS \"chargeTime\"\r\nFROM charges\r\nWHERE\r\n $__timeFilter(\"carCapturedTimestamp\") AND\r\n vehicle_vin = '$VIN') as charging ON \"chargeTime\" = \"batteryTime\"\r\nLEFT JOIN vehicles ON delta.vehicle_vin = vehicles.vin\r\nLEFT JOIN vehicle_settings ON delta.vehicle_vin = vehicle_settings.vehicle_vin\r\nWHERE\r\n \"delta\" > 0 AND\r\n NOT EXISTS (SELECT 1 FROM charges WHERE \"chargePower_kW\" IS NOT NULL AND \"chargeRate_kmph\" IS NOT NULL AND $__timeFilter(\"carCapturedTimestamp\") AND vehicle_vin = '$VIN' LIMIT 1)\r\nORDER BY \"carCapturedTimestamp\")", "refId": "A", "resultFormat": "time_series", "select": [ @@ -2558,6 +2723,22 @@ "skipUrlSync": false, "sort": 0, "type": "query" + }, + { + "current": { + }, + "definition": "SELECT u.unnest|| ':' || CASE u.unnest WHEN 'KM' THEN 'Metric' WHEN 'MI' THEN 'Imperial' END\nFROM (SELECT unnest(enum_range(NULL::unitoflength))) u \nLEFT JOIN settings ON settings.unit_of_length = u.unnest \nORDER BY coalesce(settings.id,0) DESC", + "hide": 1, + "includeAll": false, + "multi": false, + "name": "length_unit", + "options": [], + "query": "SELECT u.unnest|| ':' || CASE u.unnest WHEN 'KM' THEN 'Metric' WHEN 'MI' THEN 'Imperial' END\nFROM (SELECT unnest(enum_range(NULL::unitoflength))) u \nLEFT JOIN settings ON settings.unit_of_length = u.unnest \nORDER BY coalesce(settings.id,0) DESC", + "refresh": 1, + "regex": "/(?[A-Z0-9]+):(?.*)/", + "skipUrlSync": false, + "sort": 0, + "type": "query" } ] }, From 1763e3f46cd0756611f24088e7277e2dbac5bf31 Mon Sep 17 00:00:00 2001 From: Ben Davidson Date: Wed, 14 Sep 2022 13:38:44 +0100 Subject: [PATCH 5/8] Add imperial / metric option to Live dashboard --- .../dashboards/vwsfriend/VWsFriend/live.json | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/grafana/dashboards/vwsfriend/VWsFriend/live.json b/grafana/dashboards/vwsfriend/VWsFriend/live.json index e7b2e834..3c65918f 100644 --- a/grafana/dashboards/vwsfriend/VWsFriend/live.json +++ b/grafana/dashboards/vwsfriend/VWsFriend/live.json @@ -798,7 +798,20 @@ }, "unit": "lengthkm" }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "/.*_MI/" + }, + "properties": [ + { + "id": "unit", + "value": "lengthmi" + } + ] + } + ] }, "gridPos": { "h": 7, @@ -829,8 +842,9 @@ }, "fields": [ { - "jsonPath": "$.vehicles.$VIN.domains.charging.batteryStatus.cruisingRangeElectric_km", - "name": "Electric Range" + "jsonPath": "$.vehicles.$VIN.domains.charging.batteryStatus.cruisingRangeElectric_km","jsonPath": "$.$lookup(vehicles,$VIN[0]).domains.charging.batteryStatus.cruisingRangeElectric_km/($length_unit[0]=\"MI\"?1.609:1)", + "language": "jsonata", + "name": "Electric Range_$length_unit" } ], "hide": false, @@ -855,7 +869,7 @@ "query": "", "queryType": "randomWalk", "rawQuery": true, - "rawSql": "SELECT\n CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_wltp_range\"::NUMERIC\n ELSE vehicle_settings.\"secondary_wltp_range\"::NUMERIC END AS \"Electric Range\"\nFROM vehicles\nLEFT JOIN vehicle_settings ON vehicles.vin = vehicle_settings.vehicle_vin\nWHERE\n vehicles.vin = '$VIN'\n", + "rawSql": "SELECT\n convert_km(CASE WHEN vehicles.\"carType\" = 'ELECTRIC' THEN vehicle_settings.\"primary_wltp_range\"::NUMERIC\n ELSE vehicle_settings.\"secondary_wltp_range\"::NUMERIC END,'${length_unit.value}') AS \"Electric Range_${length_unit.value}\"\nFROM vehicles\nLEFT JOIN vehicle_settings ON vehicles.vin = vehicle_settings.vehicle_vin\nWHERE\n vehicles.vin = '$VIN'\n", "refId": "WLTP", "resultFormat": "time_series", "select": [ @@ -897,14 +911,22 @@ "id": "configFromData", "options": { "applyTo": { - "id": "byName", - "options": "A Electric Range" + "id": "byRegexp", + "options": "/Electric Range.*/" }, "configRefId": "WLTP", "mappings": [ { "fieldName": "Electric Range", "handlerKey": "max" + }, + { + "fieldName": "Electric Range_MI", + "handlerKey": "max" + }, + { + "fieldName": "Electric Range_KM", + "handlerKey": "max" } ] } @@ -1028,6 +1050,22 @@ "skipUrlSync": false, "sort": 0, "type": "query" + }, + { + "current": { + }, + "definition": "SELECT u.unnest|| ':' || CASE u.unnest WHEN 'KM' THEN 'Metric' WHEN 'MI' THEN 'Imperial' END FROM (SELECT unnest(enum_range(NULL::unitoflength))) u LEFT JOIN settings ON settings.unit_of_length = u.unnest ORDER BY coalesce(settings.id,0) DESC", + "hide": 1, + "includeAll": false, + "multi": false, + "name": "length_unit", + "options": [], + "query": "SELECT u.unnest|| ':' || CASE u.unnest WHEN 'KM' THEN 'Metric' WHEN 'MI' THEN 'Imperial' END FROM (SELECT unnest(enum_range(NULL::unitoflength))) u LEFT JOIN settings ON settings.unit_of_length = u.unnest ORDER BY coalesce(settings.id,0) DESC", + "refresh": 1, + "regex": "/(?[A-Z0-9]+):(?.*)/", + "skipUrlSync": false, + "sort": 0, + "type": "query" } ] }, From d2ac26f3341590d8ea0d15fd0715ad2a3d6d67ff Mon Sep 17 00:00:00 2001 From: Ben Davidson Date: Wed, 14 Sep 2022 13:41:49 +0100 Subject: [PATCH 6/8] Removed duplicate jsonPath in Live dashboard --- grafana/dashboards/vwsfriend/VWsFriend/live.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/grafana/dashboards/vwsfriend/VWsFriend/live.json b/grafana/dashboards/vwsfriend/VWsFriend/live.json index 3c65918f..27b5da77 100644 --- a/grafana/dashboards/vwsfriend/VWsFriend/live.json +++ b/grafana/dashboards/vwsfriend/VWsFriend/live.json @@ -842,7 +842,7 @@ }, "fields": [ { - "jsonPath": "$.vehicles.$VIN.domains.charging.batteryStatus.cruisingRangeElectric_km","jsonPath": "$.$lookup(vehicles,$VIN[0]).domains.charging.batteryStatus.cruisingRangeElectric_km/($length_unit[0]=\"MI\"?1.609:1)", + "jsonPath": "$.$lookup(vehicles,$VIN[0]).domains.charging.batteryStatus.cruisingRangeElectric_km/($length_unit[0]=\"MI\"?1.609:1)", "language": "jsonata", "name": "Electric Range_$length_unit" } From 10e22655f31cefc4131facdf0c464de7079915a5 Mon Sep 17 00:00:00 2001 From: Ben Davidson Date: Fri, 23 Sep 2022 16:36:42 +0100 Subject: [PATCH 7/8] Add ConvertKM function to model --- vwsfriend/vwsfriend/model/__init__.py | 1 + vwsfriend/vwsfriend/model/helper_functions.py | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 vwsfriend/vwsfriend/model/helper_functions.py diff --git a/vwsfriend/vwsfriend/model/__init__.py b/vwsfriend/vwsfriend/model/__init__.py index b9ba4117..395ea744 100644 --- a/vwsfriend/vwsfriend/model/__init__.py +++ b/vwsfriend/vwsfriend/model/__init__.py @@ -18,3 +18,4 @@ from .tag import Tag from .warning_light import WarningLight from .maintenance import Maintenance +from .helper_functions import ConvertKM \ No newline at end of file diff --git a/vwsfriend/vwsfriend/model/helper_functions.py b/vwsfriend/vwsfriend/model/helper_functions.py new file mode 100644 index 00000000..6a9a8e29 --- /dev/null +++ b/vwsfriend/vwsfriend/model/helper_functions.py @@ -0,0 +1,10 @@ +import sqlalchemy +from sqlalchemy.schema import DDL +from .base import Base + +class ConvertKM(): + sqlalchemy.event.listen( + Base.metadata, + 'before_create', + DDL("CREATE OR REPLACE FUNCTION convert_km(n double precision, unit text) RETURNS double precision AS $$ SELECT CASE WHEN $2 = 'KM' THEN $1 WHEN $2 = 'MI' THEN $1 / 1.60934 END; $$ LANGUAGE SQL IMMUTABLE RETURNS NULL ON NULL INPUT;") + ) \ No newline at end of file From dfc593be8ccd962f371521ff0903ff4aec9eee17 Mon Sep 17 00:00:00 2001 From: Ben Davidson Date: Mon, 26 Sep 2022 10:36:58 +0100 Subject: [PATCH 8/8] Add unit of length to edit settings page --- vwsfriend/vwsfriend/ui/database.py | 12 +++++++++--- .../ui/templates/database/settings_edit.html | 13 ++++++++++++- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/vwsfriend/vwsfriend/ui/database.py b/vwsfriend/vwsfriend/ui/database.py index f4b33536..4f4e13bf 100644 --- a/vwsfriend/vwsfriend/ui/database.py +++ b/vwsfriend/vwsfriend/ui/database.py @@ -36,7 +36,7 @@ class SettingsEditForm(FlaskForm): id = HiddenField('id', validators=[Optional()]) - unit_of_length = HiddenField('unit_of_length', validators=[Optional()]) + unit_of_length = SelectField('Unit of Length used in Grafana', validators=[DataRequired()]) unit_of_temperature = HiddenField('unit_of_temperature', validators=[Optional()]) grafana_url = StringField('URL where the Grafana installation is reachable', validators=[DataRequired()]) vwsfriend_url = StringField('URL where the VWsFriend installation is reachable', validators=[DataRequired()]) @@ -230,8 +230,14 @@ def settingsEdit(): requestHost = request.headers.get('Host') grafanaHost = requestHost.replace("4000", "3000") settings = Settings(grafana_url=f'http://{grafanaHost}', vwsfriend_url=f'http://{requestHost}') - - form = SettingsEditForm() + + form = SettingsEditForm() + + unit_of_length_choices = [] + result = current_app.db.session.execute('SELECT unnest(enum_range(NULL::unitoflength))') + for row in result: + unit_of_length_choices.append((row[0].lower(), row[0])) + form.unit_of_length.choices = unit_of_length_choices if form.validate_on_submit(): with current_app.db.session.begin_nested(): diff --git a/vwsfriend/vwsfriend/ui/templates/database/settings_edit.html b/vwsfriend/vwsfriend/ui/templates/database/settings_edit.html index d5482007..35bacab2 100644 --- a/vwsfriend/vwsfriend/ui/templates/database/settings_edit.html +++ b/vwsfriend/vwsfriend/ui/templates/database/settings_edit.html @@ -25,7 +25,6 @@

{% block title %}Edit Settings{% endblock %}

{{ form.csrf_token }} {{ form.id }} - {{ form.unit_of_length }} {{ form.unit_of_temperature }} {{ form.locale }} @@ -52,6 +51,18 @@

{% block title %}Edit Settings{% endblock %}

{% endif %} + +
+ {{ form.unit_of_length.label }} + {{ form.unit_of_length }} + {% if form.unit_of_length.errors %} +
    + {% for error in form.unit_of_length.errors %} +
  • {{ error }}
  • + {% endfor %} +
+ {% endif %} +
{{ form.save }}