mirror of
https://github.com/xbgmsharp/postgsail.git
synced 2025-09-17 11:17:46 +00:00
Compare commits
46 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
65d0a6fe4b | ||
![]() |
f7724db62a | ||
![]() |
01c20651a4 | ||
![]() |
57d38ba893 | ||
![]() |
b817a837d0 | ||
![]() |
e1fccabba5 | ||
![]() |
b386e307f9 | ||
![]() |
53b25e1656 | ||
![]() |
9c7301deac | ||
![]() |
0f08667d3f | ||
![]() |
baea4031b8 | ||
![]() |
3dcae9199f | ||
![]() |
e8259d231e | ||
![]() |
dd81d49895 | ||
![]() |
b861e4151c | ||
![]() |
42cfa34de8 | ||
![]() |
fa48d23b1a | ||
![]() |
a28ea4631b | ||
![]() |
1793dba64f | ||
![]() |
b8c70f43b9 | ||
![]() |
be5c3e9a6f | ||
![]() |
427d30681e | ||
![]() |
3130394ab0 | ||
![]() |
4e1e890ef7 | ||
![]() |
f46787ca72 | ||
![]() |
6bb3fd7243 | ||
![]() |
27ab0d590f | ||
![]() |
e295380bcf | ||
![]() |
f9cebf1bda | ||
![]() |
51bfc3ca9a | ||
![]() |
7d3667726b | ||
![]() |
5ec987e6bc | ||
![]() |
cbef039a26 | ||
![]() |
23780e2c01 | ||
![]() |
a1306f06e2 | ||
![]() |
ed90fdd01d | ||
![]() |
23bce1ad26 | ||
![]() |
093992443b | ||
![]() |
99dea0dbc8 | ||
![]() |
7edd2be1fd | ||
![]() |
e8a899f36c | ||
![]() |
35940917e0 | ||
![]() |
ecb6e666d2 | ||
![]() |
7b11de9d0d | ||
![]() |
788b6f160b | ||
![]() |
cad4d38595 |
Binary file not shown.
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 222 KiB |
Binary file not shown.
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 195 KiB |
13
README.md
13
README.md
@@ -96,12 +96,12 @@ Notice, that `PGRST_JWT_SECRET` must be at least 32 characters long.
|
||||
|
||||
### Deploy
|
||||
|
||||
By default there is no network set and the postgresql data are store in a docker volume.
|
||||
You can update the default settings by editing `docker-compose.yml` to your need.
|
||||
By default there is no network set and all data are store in a docker volume.
|
||||
You can update the default settings by editing `docker-compose.yml` and `docker-compose.dev.yml` to your need.
|
||||
|
||||
First let's initialize the database.
|
||||
|
||||
#### Initialize database
|
||||
#### Step 1. Initialize database
|
||||
|
||||
First let's import the SQL schema, execute:
|
||||
|
||||
@@ -109,7 +109,7 @@ First let's import the SQL schema, execute:
|
||||
$ docker-compose up db
|
||||
```
|
||||
|
||||
#### Start backend (db, api)
|
||||
#### Step 2. Start backend (db, api)
|
||||
|
||||
Then launch the full stack (db, api) backend, execute:
|
||||
|
||||
@@ -147,7 +147,8 @@ You might want to import your influxdb1 data as well, [outflux](https://github.c
|
||||
Any taker on influxdb2 to PostgSail? It is definitely possible.
|
||||
|
||||
Last, if you like, you can import the sample data from Signalk NMEA Plaka by running the tests.
|
||||
If everything goes well all tests pass successfully and you should receive a few notifications by email or PushOver.
|
||||
If everything goes well all tests pass successfully and you should receive a few notifications by email or PushOver or Telegram.
|
||||
[End-to-End (E2E) Testing.](https://github.com/xbgmsharp/postgsail/blob/main/tests/)
|
||||
|
||||
```
|
||||
$ docker-compose up tests
|
||||
@@ -179,7 +180,7 @@ $ curl http://localhost:3000/ -H 'Authorization: Bearer my_token_from_register_v
|
||||
|
||||
#### API main workflow
|
||||
|
||||
Check the [e2e unit test sample](https://github.com/xbgmsharp/postgsail/blob/main/tests/).
|
||||
Check the [End-to-End (E2E) test sample](https://github.com/xbgmsharp/postgsail/blob/main/tests/).
|
||||
|
||||
### Docker dependencies
|
||||
|
||||
|
2
frontend
2
frontend
Submodule frontend updated: 8bcf7ca2a6...ac25a3afb9
@@ -20,8 +20,21 @@
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 6,
|
||||
"links": [],
|
||||
"id": 1,
|
||||
"links": [
|
||||
{
|
||||
"asDropdown": false,
|
||||
"icon": "external link",
|
||||
"includeVars": true,
|
||||
"keepTime": false,
|
||||
"tags": [],
|
||||
"targetBlank": true,
|
||||
"title": "New link",
|
||||
"tooltip": "",
|
||||
"type": "dashboards",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
@@ -83,7 +96,7 @@
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true
|
||||
},
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -220,7 +233,7 @@
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -346,7 +359,7 @@
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -481,7 +494,7 @@
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -591,7 +604,7 @@
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -719,7 +732,7 @@
|
||||
},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -766,50 +779,108 @@
|
||||
"type": "stat"
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"electrical.batteries.256.current.mean": "blue"
|
||||
},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "line+area"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "transparent",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": -1
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "amp"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "electrical.batteries.256.current.mean"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "color",
|
||||
"value": {
|
||||
"fixedColor": "blue",
|
||||
"mode": "fixed"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 5
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 47,
|
||||
"legend": {
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"mean",
|
||||
"max",
|
||||
"min"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "9.5.1",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -835,7 +906,8 @@
|
||||
"measurement": "electrical.batteries.256.current",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"rawSql": "",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SET vessel.id = '${__user.login}';\nSELECT m.time, cast(m.metrics->'electrical.batteries.House.current' as NUMERIC) as current FROM api.metrics m WHERE $__timeFilter(time) AND m.vessel_id = '${boat}';\n",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
@@ -872,56 +944,379 @@
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"thresholds": [
|
||||
{
|
||||
"$$hashKey": "object:8288",
|
||||
"colorMode": "critical",
|
||||
"fill": true,
|
||||
"line": true,
|
||||
"op": "gt",
|
||||
"value": -1,
|
||||
"yaxis": "left"
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:8294",
|
||||
"colorMode": "ok",
|
||||
"fill": true,
|
||||
"line": true,
|
||||
"op": "gt",
|
||||
"value": 1,
|
||||
"yaxis": "left"
|
||||
}
|
||||
],
|
||||
"timeRegions": [],
|
||||
"title": "House Amps",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"mode": "time",
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:8148",
|
||||
"format": "amp",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 0,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "auto",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 10,
|
||||
"x": 12,
|
||||
"y": 5
|
||||
},
|
||||
"id": 48,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"$$hashKey": "object:8149",
|
||||
"format": "short",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SET vessel.id = '${__user.login}';\nSELECT m.time, cast(m.metrics->'electrical.batteries.House.capacity.stateOfCharge' as NUMERIC) * 100 as stateOfCharge FROM api.metrics m WHERE $__timeFilter(time) AND m.vessel_id = '${boat}';\n",
|
||||
"refId": "A",
|
||||
"sql": {
|
||||
"columns": [
|
||||
{
|
||||
"parameters": [],
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"groupBy": [
|
||||
{
|
||||
"property": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "groupBy"
|
||||
}
|
||||
],
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false
|
||||
}
|
||||
"title": "System - Battery SOC (State of Charge)",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"description": "",
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "Volts",
|
||||
"axisPlacement": "auto",
|
||||
"barAlignment": 0,
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"gradientMode": "none",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"showPoints": "never",
|
||||
"spanNulls": false,
|
||||
"stacking": {
|
||||
"group": "A",
|
||||
"mode": "none"
|
||||
},
|
||||
"thresholdsStyle": {
|
||||
"mode": "off"
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unit": "volt"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "current"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "color",
|
||||
"value": {
|
||||
"fixedColor": "blue",
|
||||
"mode": "fixed"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "voltage"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "color",
|
||||
"value": {
|
||||
"fixedColor": "yellow",
|
||||
"mode": "fixed"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "current"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "unit",
|
||||
"value": "amp"
|
||||
},
|
||||
{
|
||||
"id": "custom.axisLabel",
|
||||
"value": "Amps"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 13
|
||||
},
|
||||
"id": 37,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [
|
||||
"mean",
|
||||
"max",
|
||||
"min"
|
||||
],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"tooltip": {
|
||||
"mode": "multi",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
"$__interval"
|
||||
],
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"null"
|
||||
],
|
||||
"type": "fill"
|
||||
}
|
||||
],
|
||||
"measurement": "electrical.batteries.256.voltage",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SET vessel.id = '${__user.login}';\nSELECT m.time, cast(m.metrics->'electrical.batteries.House.voltage' as NUMERIC) as voltage FROM api.metrics m WHERE $__timeFilter(time) AND m.vessel_id = '${boat}';\n",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"value"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [],
|
||||
"type": "mean"
|
||||
}
|
||||
]
|
||||
],
|
||||
"sql": {
|
||||
"columns": [
|
||||
{
|
||||
"parameters": [],
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"groupBy": [
|
||||
{
|
||||
"property": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "groupBy"
|
||||
}
|
||||
],
|
||||
"limit": 50
|
||||
},
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
"$__interval"
|
||||
],
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"null"
|
||||
],
|
||||
"type": "fill"
|
||||
}
|
||||
],
|
||||
"hide": false,
|
||||
"measurement": "electrical.batteries.256.current",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"query": "SELECT mean(\"value\") FROM \"electrical.batteries.256.current\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SET vessel.id = '${__user.login}';\nSELECT m.time, cast(m.metrics->'electrical.batteries.House.current' as NUMERIC) as current FROM api.metrics m WHERE $__timeFilter(time) AND m.vessel_id = '${boat}';\n",
|
||||
"refId": "B",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"value"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [],
|
||||
"type": "mean"
|
||||
}
|
||||
]
|
||||
],
|
||||
"sql": {
|
||||
"columns": [
|
||||
{
|
||||
"parameters": [],
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"groupBy": [
|
||||
{
|
||||
"property": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "groupBy"
|
||||
}
|
||||
],
|
||||
"limit": 50
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"title": "Battery Voltage and Current",
|
||||
"type": "timeseries"
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
@@ -938,9 +1333,9 @@
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"w": 10,
|
||||
"x": 12,
|
||||
"y": 5
|
||||
"y": 13
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 45,
|
||||
@@ -960,7 +1355,7 @@
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
@@ -1042,180 +1437,6 @@
|
||||
"align": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"electrical.batteries.256.current.mean": "blue",
|
||||
"electrical.batteries.256.voltage.mean": "yellow"
|
||||
},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"description": "",
|
||||
"fill": 1,
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"x": 0,
|
||||
"y": 13
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 37,
|
||||
"legend": {
|
||||
"alignAsTable": false,
|
||||
"avg": true,
|
||||
"current": false,
|
||||
"max": true,
|
||||
"min": true,
|
||||
"rightSide": false,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": true
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 1,
|
||||
"nullPointMode": "null",
|
||||
"options": {
|
||||
"alertThreshold": true
|
||||
},
|
||||
"percentage": false,
|
||||
"pluginVersion": "9.5.1",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [
|
||||
{
|
||||
"$$hashKey": "object:5017",
|
||||
"alias": "electrical.batteries.256.current.mean",
|
||||
"yaxis": 2
|
||||
}
|
||||
],
|
||||
"spaceLength": 10,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
"$__interval"
|
||||
],
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"null"
|
||||
],
|
||||
"type": "fill"
|
||||
}
|
||||
],
|
||||
"measurement": "electrical.batteries.256.voltage",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"refId": "A",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"value"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [],
|
||||
"type": "mean"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"groupBy": [
|
||||
{
|
||||
"params": [
|
||||
"$__interval"
|
||||
],
|
||||
"type": "time"
|
||||
},
|
||||
{
|
||||
"params": [
|
||||
"null"
|
||||
],
|
||||
"type": "fill"
|
||||
}
|
||||
],
|
||||
"hide": false,
|
||||
"measurement": "electrical.batteries.256.current",
|
||||
"orderByTime": "ASC",
|
||||
"policy": "default",
|
||||
"query": "SELECT mean(\"value\") FROM \"electrical.batteries.256.current\" WHERE $timeFilter GROUP BY time($__interval) fill(null)",
|
||||
"rawQuery": true,
|
||||
"refId": "B",
|
||||
"resultFormat": "time_series",
|
||||
"select": [
|
||||
[
|
||||
{
|
||||
"params": [
|
||||
"value"
|
||||
],
|
||||
"type": "field"
|
||||
},
|
||||
{
|
||||
"params": [],
|
||||
"type": "mean"
|
||||
}
|
||||
]
|
||||
],
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeRegions": [],
|
||||
"title": "House Bank Voltage vs Current",
|
||||
"tooltip": {
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "individual"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"mode": "time",
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"$$hashKey": "object:4372",
|
||||
"format": "volt",
|
||||
"label": "Volts",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"$$hashKey": "object:4373",
|
||||
"format": "amp",
|
||||
"label": "Amps",
|
||||
"logBase": 1,
|
||||
"show": true
|
||||
}
|
||||
],
|
||||
"yaxis": {
|
||||
"align": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"aliasColors": {
|
||||
"From grid": "#1f78c1",
|
||||
@@ -1232,9 +1453,9 @@
|
||||
"fillGradient": 0,
|
||||
"gridPos": {
|
||||
"h": 8,
|
||||
"w": 12,
|
||||
"w": 10,
|
||||
"x": 12,
|
||||
"y": 13
|
||||
"y": 21
|
||||
},
|
||||
"hiddenSeries": false,
|
||||
"id": 10,
|
||||
@@ -1258,7 +1479,7 @@
|
||||
},
|
||||
"paceLength": 10,
|
||||
"percentage": false,
|
||||
"pluginVersion": "9.5.1",
|
||||
"pluginVersion": "10.1.0",
|
||||
"pointradius": 2,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
@@ -1416,6 +1637,25 @@
|
||||
"skipUrlSync": false,
|
||||
"sort": 0,
|
||||
"type": "query"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"definition": "SET vessel.id = '${__user.login}';\nSELECT rtrim(key, 'voltage') AS __text ,key AS __value FROM api.monitoring_view2 where key ILIKE 'electrical.batteries%voltage';",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Batteries",
|
||||
"multi": false,
|
||||
"name": "batteries",
|
||||
"options": [],
|
||||
"query": "SET vessel.id = '${__user.login}';\nSELECT rtrim(key, 'voltage') AS __text ,key AS __value FROM api.monitoring_view2 where key ILIKE 'electrical.batteries%voltage';",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 0,
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1447,9 +1687,9 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"timezone": "",
|
||||
"timezone": "utc",
|
||||
"title": "Electrical System",
|
||||
"uid": "rk0FTiIMk",
|
||||
"version": 1,
|
||||
"version": 11,
|
||||
"weekStart": ""
|
||||
}
|
@@ -25,7 +25,7 @@
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 2,
|
||||
"id": 3,
|
||||
"links": [
|
||||
{
|
||||
"asDropdown": false,
|
||||
@@ -92,7 +92,7 @@
|
||||
"text": {},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.4.3",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -198,7 +198,7 @@
|
||||
"text": {},
|
||||
"textMode": "auto"
|
||||
},
|
||||
"pluginVersion": "9.4.3",
|
||||
"pluginVersion": "10.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
@@ -279,6 +279,7 @@
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
@@ -439,6 +440,7 @@
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
@@ -573,6 +575,7 @@
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"insertNulls": false,
|
||||
"lineInterpolation": "linear",
|
||||
"lineWidth": 1,
|
||||
"pointSize": 5,
|
||||
@@ -638,7 +641,7 @@
|
||||
"group": [],
|
||||
"metricColumn": "none",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SET vessel.id = '${__user.login}';\nwith config as (select set_config('vessel.id', '${boat}', false) ) select * from api.monitoring_view",
|
||||
"rawSql": "SET vessel.id = '${__user.login}';\nselect * from api.monitoring_humidity;\n",
|
||||
"refId": "A",
|
||||
"select": [
|
||||
[
|
||||
@@ -679,11 +682,11 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Title",
|
||||
"title": "environment.%.humidity",
|
||||
"type": "timeseries"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"refresh": "5m",
|
||||
"revision": 1,
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1936,7 +1936,7 @@
|
||||
"yBucketBound": "auto"
|
||||
}
|
||||
],
|
||||
"refresh": "1m",
|
||||
"refresh": "5m",
|
||||
"schemaVersion": 37,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
|
@@ -1,134 +1,293 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
},
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"target": {
|
||||
"limit": 100,
|
||||
"matchAny": false,
|
||||
"tags": [],
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "OIttR1sVk"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 3,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "OIttR1sVk"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"type": "welcome"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "OIttR1sVk"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 3
|
||||
},
|
||||
"id": 3,
|
||||
"links": [],
|
||||
"options": {
|
||||
"folderId": 0,
|
||||
"maxItems": 30,
|
||||
"query": "",
|
||||
"showHeadings": true,
|
||||
"showRecentlyViewed": true,
|
||||
"showSearch": false,
|
||||
"showStarred": true,
|
||||
"tags": []
|
||||
},
|
||||
"pluginVersion": "9.4.3",
|
||||
"tags": [],
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "OIttR1sVk"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "Dashboards",
|
||||
"type": "dashlist"
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": 5,
|
||||
"links": [
|
||||
{
|
||||
"asDropdown": false,
|
||||
"icon": "external link",
|
||||
"includeVars": true,
|
||||
"keepTime": false,
|
||||
"tags": [],
|
||||
"targetBlank": true,
|
||||
"title": "New link",
|
||||
"tooltip": "",
|
||||
"type": "dashboards",
|
||||
"url": ""
|
||||
}
|
||||
],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "OIttR1sVk"
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 13,
|
||||
"w": 10,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 3,
|
||||
"links": [],
|
||||
"options": {
|
||||
"folderId": 0,
|
||||
"includeVars": false,
|
||||
"keepTime": false,
|
||||
"maxItems": 30,
|
||||
"query": "",
|
||||
"showHeadings": true,
|
||||
"showRecentlyViewed": true,
|
||||
"showSearch": false,
|
||||
"showStarred": true,
|
||||
"tags": []
|
||||
},
|
||||
"pluginVersion": "10.1.4",
|
||||
"tags": [],
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "OIttR1sVk"
|
||||
},
|
||||
"refId": "A"
|
||||
}
|
||||
],
|
||||
"title": "PostgSail Dashboards",
|
||||
"type": "dashlist"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "thresholds"
|
||||
},
|
||||
"custom": {
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
}
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 13,
|
||||
"w": 12,
|
||||
"x": 10,
|
||||
"y": 0
|
||||
},
|
||||
"id": 5,
|
||||
"maxDataPoints": 500,
|
||||
"options": {
|
||||
"basemap": {
|
||||
"config": {},
|
||||
"name": "Layer 0",
|
||||
"type": "default"
|
||||
},
|
||||
"controls": {
|
||||
"mouseWheelZoom": true,
|
||||
"showAttribution": true,
|
||||
"showDebug": false,
|
||||
"showMeasure": false,
|
||||
"showScale": false,
|
||||
"showZoom": true
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"config": {
|
||||
"showLegend": true,
|
||||
"style": {
|
||||
"color": {
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"opacity": 0.4,
|
||||
"rotation": {
|
||||
"fixed": 0,
|
||||
"max": 360,
|
||||
"min": -360,
|
||||
"mode": "mod"
|
||||
},
|
||||
"size": {
|
||||
"fixed": 5,
|
||||
"max": 15,
|
||||
"min": 2
|
||||
},
|
||||
"symbol": {
|
||||
"fixed": "img/icons/marker/circle.svg",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"textConfig": {
|
||||
"fontSize": 12,
|
||||
"offsetX": 0,
|
||||
"offsetY": 0,
|
||||
"textAlign": "center",
|
||||
"textBaseline": "middle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"filterData": {
|
||||
"id": "byRefId",
|
||||
"options": "A"
|
||||
},
|
||||
"location": {
|
||||
"latitude": "value",
|
||||
"longitude": "value",
|
||||
"mode": "auto"
|
||||
},
|
||||
"name": "Boat",
|
||||
"tooltip": true,
|
||||
"type": "markers"
|
||||
}
|
||||
],
|
||||
"tooltip": {
|
||||
"mode": "details"
|
||||
},
|
||||
"view": {
|
||||
"allLayers": true,
|
||||
"id": "fit",
|
||||
"lat": 0,
|
||||
"lon": 0,
|
||||
"zoom": 5
|
||||
}
|
||||
},
|
||||
"pluginVersion": "10.1.4",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "postgres",
|
||||
"uid": "PCC52D03280B7034C"
|
||||
},
|
||||
"editorMode": "code",
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"rawSql": "SELECT latitude, longitude FROM api.metrics WHERE vessel_id = '${boat}' ORDER BY time ASC LIMIT 1;",
|
||||
"refId": "A",
|
||||
"sql": {
|
||||
"columns": [
|
||||
{
|
||||
"parameters": [],
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"groupBy": [
|
||||
{
|
||||
"property": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "groupBy"
|
||||
}
|
||||
],
|
||||
"limit": 50
|
||||
}
|
||||
}
|
||||
],
|
||||
"title": "Location",
|
||||
"type": "geomap"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"revision": 1,
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"definition": "SET \"user.email\" = '${__user.email}';\nSET vessel.id = '${__user.login}';\nSELECT\n v.name AS __text,\n m.vessel_id AS __value\n FROM auth.vessels v\n JOIN api.metadata m ON v.owner_email = '${__user.email}' and m.vessel_id = v.vessel_id;",
|
||||
"description": "Vessel Name",
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Boat",
|
||||
"multi": false,
|
||||
"name": "boat",
|
||||
"options": [],
|
||||
"query": "SET \"user.email\" = '${__user.email}';\nSET vessel.id = '${__user.login}';\nSELECT\n v.name AS __text,\n m.vessel_id AS __value\n FROM auth.vessels v\n JOIN api.metadata m ON v.owner_email = '${__user.email}' and m.vessel_id = v.vessel_id;",
|
||||
"refresh": 1,
|
||||
"regex": "",
|
||||
"skipUrlSync": false,
|
||||
"sort": 0,
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-90d",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"hidden": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"refresh": "",
|
||||
"revision": 1,
|
||||
"schemaVersion": 38,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"hidden": true,
|
||||
"refresh_intervals": [
|
||||
"5s",
|
||||
"10s",
|
||||
"30s",
|
||||
"1m",
|
||||
"5m",
|
||||
"15m",
|
||||
"30m",
|
||||
"1h",
|
||||
"2h",
|
||||
"1d"
|
||||
],
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
],
|
||||
"type": "timepicker"
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "Home",
|
||||
"version": 0,
|
||||
"weekStart": ""
|
||||
}
|
||||
"time_options": [
|
||||
"5m",
|
||||
"15m",
|
||||
"1h",
|
||||
"6h",
|
||||
"12h",
|
||||
"24h",
|
||||
"2d",
|
||||
"7d",
|
||||
"30d"
|
||||
],
|
||||
"type": "timepicker"
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "Home",
|
||||
"uid": "d81aa15b",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
@@ -7,6 +7,7 @@ auto_assign_org_role = Editor
|
||||
enabled = true
|
||||
header_name = X-WEBAUTH-USER
|
||||
header_property = email
|
||||
headers = Login:X-WEBAUTH-LOGIN
|
||||
auto_sign_up = true
|
||||
enable_login_token = true
|
||||
login_maximum_inactive_lifetime_duration = 12h
|
||||
@@ -14,3 +15,7 @@ login_maximum_lifetime_duration = 1d
|
||||
|
||||
[dashboards]
|
||||
default_home_dashboard_path = /etc/grafana/dashboards/home.json
|
||||
|
||||
[analytics]
|
||||
feedback_links_enabled = false
|
||||
reporting_enabled = false
|
||||
|
@@ -51,6 +51,10 @@ CREATE DATABASE signalk;
|
||||
ALTER DATABASE signalk WITH CONNECTION LIMIT = 100;
|
||||
-- Set timezone to UTC
|
||||
ALTER DATABASE signalk SET TIMEZONE='UTC';
|
||||
-- Set datestyle output
|
||||
ALTER DATABASE signalk SET datestyle TO "ISO, DMY";
|
||||
-- Set intervalstyle output
|
||||
ALTER DATABASE signalk SET intervalstyle TO 'iso_8601';
|
||||
|
||||
-- connect to the DB
|
||||
\c signalk
|
||||
|
@@ -55,8 +55,8 @@ CREATE TABLE IF NOT EXISTS api.metrics (
|
||||
status status NULL,
|
||||
metrics jsonb NULL,
|
||||
--CONSTRAINT valid_client_id CHECK (length(client_id) > 10),
|
||||
CONSTRAINT valid_latitude CHECK (latitude >= -90 and latitude <= 90),
|
||||
CONSTRAINT valid_longitude CHECK (longitude >= -180 and longitude <= 180),
|
||||
--CONSTRAINT valid_latitude CHECK (latitude >= -90 and latitude <= 90),
|
||||
--CONSTRAINT valid_longitude CHECK (longitude >= -180 and longitude <= 180),
|
||||
PRIMARY KEY (time, vessel_id)
|
||||
);
|
||||
-- Description
|
||||
@@ -179,7 +179,7 @@ CREATE TABLE IF NOT EXISTS api.moorages(
|
||||
--client_id VARCHAR(255) NULL,
|
||||
vessel_id TEXT NOT NULL REFERENCES api.metadata(vessel_id) ON DELETE RESTRICT,
|
||||
name TEXT,
|
||||
country TEXT, -- todo need to update reverse_geocode_py_fn
|
||||
country TEXT,
|
||||
stay_id INT NOT NULL, -- needed?
|
||||
stay_code INT DEFAULT 1, -- needed? REFERENCES api.stays_at(stay_code)
|
||||
stay_duration INTERVAL NULL,
|
||||
@@ -211,7 +211,7 @@ CREATE TABLE IF NOT EXISTS api.stays_at(
|
||||
COMMENT ON TABLE api.stays_at IS 'Stay Type';
|
||||
-- Insert default possible values
|
||||
INSERT INTO api.stays_at(stay_code, description) VALUES
|
||||
(1, 'Unknow'),
|
||||
(1, 'Unknown'),
|
||||
(2, 'Anchor'),
|
||||
(3, 'Mooring Buoy'),
|
||||
(4, 'Dock');
|
||||
@@ -357,13 +357,31 @@ CREATE FUNCTION metrics_trigger_fn() RETURNS trigger AS $metrics$
|
||||
END IF;
|
||||
IF previous_time > NEW.time THEN
|
||||
-- Ignore entry if new time is later than previous time
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], new time is older [%] > [%]', NEW.vessel_id, previous_time, NEW.time;
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], new time is older than previous_time [%] > [%]', NEW.vessel_id, previous_time, NEW.time;
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
-- Check if latitude or longitude are type double
|
||||
--IF public.isdouble(NEW.latitude::TEXT) IS False OR public.isdouble(NEW.longitude::TEXT) IS False THEN
|
||||
-- -- Ignore entry if null latitude,longitude
|
||||
-- RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], not a double type for latitude or longitude [%] [%]', NEW.vessel_id, NEW.latitude, NEW.longitude;
|
||||
-- RETURN NULL;
|
||||
--END IF;
|
||||
-- Check if latitude or longitude are null
|
||||
IF NEW.latitude IS NULL OR NEW.longitude IS NULL THEN
|
||||
-- Ignore entry if null latitude,longitude
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], null latitude,longitude [%] [%]', NEW.vessel_id, NEW.latitude, NEW.longitude;
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], null latitude or longitude [%] [%]', NEW.vessel_id, NEW.latitude, NEW.longitude;
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
-- Check if valid latitude
|
||||
IF NEW.latitude >= 90 OR NEW.latitude <= -90 THEN
|
||||
-- Ignore entry if invalid latitude,longitude
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], invalid latitude >= 90 OR <= -90 [%] [%]', NEW.vessel_id, NEW.latitude, NEW.longitude;
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
-- Check if valid longitude
|
||||
IF NEW.longitude >= 180 OR NEW.longitude <= -180 THEN
|
||||
-- Ignore entry if invalid latitude,longitude
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], invalid longitude >= 180 OR <= -180 [%] [%]', NEW.vessel_id, NEW.latitude, NEW.longitude;
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
-- Check if status is null
|
||||
@@ -396,6 +414,12 @@ CREATE FUNCTION metrics_trigger_fn() RETURNS trigger AS $metrics$
|
||||
RAISE WARNING 'Metrics Ignoring metric, invalid status [%]', NEW.status;
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
-- Check if speedOverGround is valid value
|
||||
IF NEW.speedoverground >= 40 THEN
|
||||
-- Ignore entry as speedOverGround is invalid
|
||||
RAISE WARNING 'Metrics Ignoring metric, vessel_id [%], speedOverGround is invalid, over 40 < [%]', NEW.vessel_id, NEW.speedoverground;
|
||||
RETURN NULL;
|
||||
END IF;
|
||||
|
||||
-- Check the state and if any previous/current entry
|
||||
-- If change of state and new status is sailing or motoring
|
||||
|
@@ -278,6 +278,32 @@ COMMENT ON FUNCTION
|
||||
api.logs_by_month_fn
|
||||
IS 'logbook by month for web charts';
|
||||
|
||||
-- logs_by_day_fn
|
||||
DROP FUNCTION IF EXISTS api.logs_by_day_fn;
|
||||
CREATE FUNCTION api.logs_by_day_fn(OUT charts JSONB) RETURNS JSONB AS $logs_by_day$
|
||||
DECLARE
|
||||
data JSONB;
|
||||
BEGIN
|
||||
-- Query logs by day
|
||||
SELECT json_object_agg(day,count) INTO data
|
||||
FROM (
|
||||
SELECT
|
||||
to_char(date_trunc('day', _from_time), 'D') as day,
|
||||
count(*) as count
|
||||
FROM api.logbook
|
||||
GROUP BY day
|
||||
ORDER BY day
|
||||
) AS t;
|
||||
-- Merge jsonb to get all 7 days
|
||||
SELECT '{"01": 0, "02": 0, "03": 0, "04": 0, "05": 0, "06": 0, "07": 0}'::jsonb ||
|
||||
data::jsonb INTO charts;
|
||||
END;
|
||||
$logs_by_day$ LANGUAGE plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
api.logs_by_day_fn
|
||||
IS 'logbook by day for web charts';
|
||||
|
||||
-- moorage_geojson_fn
|
||||
DROP FUNCTION IF EXISTS api.export_moorages_geojson_fn;
|
||||
CREATE FUNCTION api.export_moorages_geojson_fn(OUT geojson JSONB) RETURNS JSONB AS $export_moorages_geojson$
|
||||
@@ -290,7 +316,7 @@ CREATE FUNCTION api.export_moorages_geojson_fn(OUT geojson JSONB) RETURNS JSONB
|
||||
json_agg(ST_AsGeoJSON(m.*)::JSON) as moorages_geojson
|
||||
FROM
|
||||
( SELECT
|
||||
id,name,
|
||||
id,name,stay_code,
|
||||
EXTRACT(DAY FROM justify_hours ( stay_duration )) AS Total_Stay,
|
||||
geog
|
||||
FROM api.moorages
|
||||
@@ -349,6 +375,7 @@ COMMENT ON FUNCTION
|
||||
api.export_moorages_gpx_fn
|
||||
IS 'Export moorages as gpx';
|
||||
|
||||
----------------------------------------------------------------------------------------------
|
||||
-- Statistics
|
||||
DROP FUNCTION IF EXISTS api.stats_logs_fn;
|
||||
CREATE OR REPLACE FUNCTION api.stats_logs_fn(
|
||||
@@ -362,16 +389,24 @@ CREATE OR REPLACE FUNCTION api.stats_logs_fn(
|
||||
IF start_date IS NOT NULL AND public.isdate(start_date::text) AND public.isdate(end_date::text) THEN
|
||||
RAISE WARNING '--> stats_fn, filter result stats by date [%]', start_date;
|
||||
_start_date := start_date::TIMESTAMP WITHOUT TIME ZONE;
|
||||
_end_date := end_date::TIMESTAMP WITHOUT TIME ZONE;
|
||||
_end_date := end_date::TIMESTAMP WITHOUT TIME ZONE;
|
||||
END IF;
|
||||
RAISE WARNING '--> stats_fn, _start_date [%], _end_date [%]', _start_date, _end_date;
|
||||
RAISE NOTICE '--> stats_fn, _start_date [%], _end_date [%]', _start_date, _end_date;
|
||||
WITH
|
||||
meta AS (
|
||||
SELECT m.name FROM api.metadata m ),
|
||||
logs_view AS (
|
||||
SELECT *
|
||||
FROM api.logbook l
|
||||
WHERE _from_time >= _start_date::TIMESTAMP WITHOUT TIME ZONE
|
||||
AND _to_time <= _end_date::TIMESTAMP WITHOUT TIME ZONE + interval '23 hours 59 minutes'
|
||||
),
|
||||
first_date AS (
|
||||
SELECT _from_time as first_date from logs_view ORDER BY first_date ASC LIMIT 1
|
||||
),
|
||||
last_date AS (
|
||||
SELECT _to_time as last_date from logs_view ORDER BY _to_time DESC LIMIT 1
|
||||
),
|
||||
max_speed_id AS (
|
||||
SELECT id FROM logs_view WHERE max_speed = (SELECT max(max_speed) FROM logs_view) ),
|
||||
max_wind_speed_id AS (
|
||||
@@ -386,16 +421,22 @@ CREATE OR REPLACE FUNCTION api.stats_logs_fn(
|
||||
max(max_speed) AS max_speed,
|
||||
max(max_wind_speed) AS max_wind_speed,
|
||||
max(distance) AS max_distance,
|
||||
sum(distance) AS sum_distance,
|
||||
max(duration) AS max_duration,
|
||||
sum(duration) AS sum_duration
|
||||
FROM logs_view l )
|
||||
--select * from logbook;
|
||||
-- Return a JSON
|
||||
SELECT jsonb_build_object(
|
||||
'name', meta.name,
|
||||
'first_date', first_date.first_date,
|
||||
'last_date', last_date.last_date,
|
||||
'max_speed_id', max_speed_id.id,
|
||||
'max_wind_speed_id', max_wind_speed_id.id,
|
||||
'max_duration_id', max_duration_id.id,
|
||||
'max_distance_id', max_distance_id.id)::jsonb || to_jsonb(logs_stats.*)::jsonb INTO stats
|
||||
FROM max_speed_id, max_wind_speed_id, max_distance_id, logs_stats, max_duration_id;
|
||||
FROM max_speed_id, max_wind_speed_id, max_distance_id, max_duration_id,
|
||||
logs_stats, meta, logs_view, first_date, last_date;
|
||||
-- TODO Add moorages
|
||||
END;
|
||||
$stats_logs$ LANGUAGE plpgsql;
|
||||
@@ -403,3 +444,61 @@ $stats_logs$ LANGUAGE plpgsql;
|
||||
COMMENT ON FUNCTION
|
||||
api.stats_logs_fn
|
||||
IS 'Logs stats by date';
|
||||
|
||||
DROP FUNCTION IF EXISTS api.stats_stays_fn;
|
||||
CREATE OR REPLACE FUNCTION api.stats_stays_fn(
|
||||
IN start_date TEXT DEFAULT NULL,
|
||||
IN end_date TEXT DEFAULT NULL,
|
||||
OUT stats JSON) RETURNS JSON AS $stats_stays$
|
||||
DECLARE
|
||||
_start_date TIMESTAMP WITHOUT TIME ZONE DEFAULT '1970-01-01';
|
||||
_end_date TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW();
|
||||
BEGIN
|
||||
IF start_date IS NOT NULL AND public.isdate(start_date::text) AND public.isdate(end_date::text) THEN
|
||||
RAISE NOTICE '--> stats_stays_fn, custom filter result stats by date [%]', start_date;
|
||||
_start_date := start_date::TIMESTAMP WITHOUT TIME ZONE;
|
||||
_end_date := end_date::TIMESTAMP WITHOUT TIME ZONE;
|
||||
END IF;
|
||||
RAISE NOTICE '--> stats_stays_fn, _start_date [%], _end_date [%]', _start_date, _end_date;
|
||||
WITH
|
||||
moorages_log AS (
|
||||
SELECT s.id as stays_id, m.id as moorages_id, *
|
||||
FROM api.stays s, api.moorages m
|
||||
WHERE arrived >= _start_date::TIMESTAMP WITHOUT TIME ZONE
|
||||
AND departed <= _end_date::TIMESTAMP WITHOUT TIME ZONE + interval '23 hours 59 minutes'
|
||||
AND s.id = m.stay_id
|
||||
),
|
||||
home_ports AS (
|
||||
select count(*) as home_ports from moorages_log m where home_flag is true
|
||||
),
|
||||
unique_moorage AS (
|
||||
select count(*) as unique_moorage from moorages_log m
|
||||
),
|
||||
time_at_home_ports AS (
|
||||
select sum(m.stay_duration) as time_at_home_ports from moorages_log m where home_flag is true
|
||||
),
|
||||
sum_stay_duration AS (
|
||||
select sum(m.stay_duration) as sum_stay_duration from moorages_log m where home_flag is false
|
||||
),
|
||||
time_spent_away AS (
|
||||
select m.stay_code,sum(m.stay_duration) as stay_duration from api.moorages m where home_flag is false group by m.stay_code order by m.stay_code
|
||||
),
|
||||
time_spent as (
|
||||
select jsonb_agg(t.*) as time_spent_away from time_spent_away t
|
||||
)
|
||||
-- Return a JSON
|
||||
SELECT jsonb_build_object(
|
||||
'home_ports', home_ports.home_ports,
|
||||
'unique_moorage', unique_moorage.unique_moorage,
|
||||
'time_at_home_ports', time_at_home_ports.time_at_home_ports,
|
||||
'sum_stay_duration', sum_stay_duration.sum_stay_duration,
|
||||
'time_spent_away', time_spent.time_spent_away) INTO stats
|
||||
FROM moorages_log, home_ports, unique_moorage,
|
||||
time_at_home_ports, sum_stay_duration, time_spent;
|
||||
-- TODO Add moorages
|
||||
END;
|
||||
$stats_stays$ LANGUAGE plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
api.stats_stays_fn
|
||||
IS 'Stays/Moorages stats by date';
|
@@ -192,9 +192,9 @@ CREATE OR REPLACE VIEW api.moorages_view WITH (security_invoker=true,security_ba
|
||||
-- m.stay_duration,
|
||||
-- justify_hours ( m.stay_duration )
|
||||
FROM api.moorages m, api.stays_at sa
|
||||
WHERE m.name is not null
|
||||
AND m.stay_code = sa.stay_code
|
||||
WHERE m.name IS NOT NULL
|
||||
AND geog IS NOT NULL
|
||||
AND m.stay_code = sa.stay_code
|
||||
GROUP BY m.id,m.name,sa.description,m.stay_duration,m.reference_count,m.geog,sa.stay_code
|
||||
-- ORDER BY 4 DESC;
|
||||
ORDER BY m.reference_count DESC;
|
||||
@@ -207,15 +207,17 @@ DROP VIEW IF EXISTS api.moorage_view;
|
||||
CREATE OR REPLACE VIEW api.moorage_view WITH (security_invoker=true,security_barrier=true) AS -- TODO
|
||||
SELECT id,
|
||||
m.name AS Name,
|
||||
m.stay_code AS Default_Stay,
|
||||
sa.description AS Default_Stay,
|
||||
sa.stay_code AS Default_Stay_Id,
|
||||
m.home_flag AS Home,
|
||||
EXTRACT(DAY FROM justify_hours ( m.stay_duration )) AS Total_Stay,
|
||||
m.reference_count AS Arrivals_Departures,
|
||||
m.notes
|
||||
-- m.geog
|
||||
FROM api.moorages m
|
||||
FROM api.moorages m, api.stays_at sa
|
||||
WHERE m.name IS NOT NULL
|
||||
AND geog IS NOT NULL;
|
||||
AND geog IS NOT NULL
|
||||
AND m.stay_code = sa.stay_code;
|
||||
-- Description
|
||||
COMMENT ON VIEW
|
||||
api.moorage_view
|
||||
|
@@ -14,13 +14,13 @@ declare
|
||||
process_rec record;
|
||||
begin
|
||||
-- Check for new logbook pending update
|
||||
RAISE NOTICE 'cron_process_new_logbook_fn';
|
||||
RAISE NOTICE 'cron_process_new_logbook_fn init loop';
|
||||
FOR process_rec in
|
||||
SELECT * FROM process_queue
|
||||
WHERE channel = 'new_logbook' AND processed IS NULL
|
||||
ORDER BY stored ASC LIMIT 100
|
||||
LOOP
|
||||
RAISE NOTICE '-> cron_process_new_logbook_fn [%]', process_rec.payload;
|
||||
RAISE NOTICE 'cron_process_new_logbook_fn processing queue [%] for logbook id [%]', process_rec.id, process_rec.payload;
|
||||
-- update logbook
|
||||
PERFORM process_logbook_queue_fn(process_rec.payload::INTEGER);
|
||||
-- update process_queue table , processed
|
||||
@@ -28,7 +28,7 @@ begin
|
||||
SET
|
||||
processed = NOW()
|
||||
WHERE id = process_rec.id;
|
||||
RAISE NOTICE '-> cron_process_new_logbook_fn updated process_queue table [%]', process_rec.id;
|
||||
RAISE NOTICE 'cron_process_new_logbook_fn processed queue [%] for logbook id [%]', process_rec.id, process_rec.payload;
|
||||
END LOOP;
|
||||
END;
|
||||
$$ language plpgsql;
|
||||
@@ -43,13 +43,13 @@ declare
|
||||
process_rec record;
|
||||
begin
|
||||
-- Check for new stay pending update
|
||||
RAISE NOTICE 'cron_process_new_stay_fn';
|
||||
RAISE NOTICE 'cron_process_new_stay_fn init loop';
|
||||
FOR process_rec in
|
||||
SELECT * FROM process_queue
|
||||
WHERE channel = 'new_stay' AND processed IS NULL
|
||||
ORDER BY stored ASC LIMIT 100
|
||||
LOOP
|
||||
RAISE NOTICE '-> cron_process_new_stay_fn [%]', process_rec.payload;
|
||||
RAISE NOTICE 'cron_process_new_stay_fn processing queue [%] for stay id [%]', process_rec.id, process_rec.payload;
|
||||
-- update stay
|
||||
PERFORM process_stay_queue_fn(process_rec.payload::INTEGER);
|
||||
-- update process_queue table , processed
|
||||
@@ -57,7 +57,7 @@ begin
|
||||
SET
|
||||
processed = NOW()
|
||||
WHERE id = process_rec.id;
|
||||
RAISE NOTICE '-> cron_process_new_stay_fn updated process_queue table [%]', process_rec.id;
|
||||
RAISE NOTICE 'cron_process_new_stay_fn processed queue [%] for stay id [%]', process_rec.id, process_rec.payload;
|
||||
END LOOP;
|
||||
END;
|
||||
$$ language plpgsql;
|
||||
@@ -73,13 +73,13 @@ declare
|
||||
process_rec record;
|
||||
begin
|
||||
-- Check for new moorage pending update
|
||||
RAISE NOTICE 'cron_process_new_moorage_fn';
|
||||
RAISE NOTICE 'cron_process_new_moorage_fn init loop';
|
||||
FOR process_rec in
|
||||
SELECT * FROM process_queue
|
||||
WHERE channel = 'new_moorage' AND processed IS NULL
|
||||
ORDER BY stored ASC LIMIT 100
|
||||
LOOP
|
||||
RAISE NOTICE '-> cron_process_new_moorage_fn [%]', process_rec.payload;
|
||||
RAISE NOTICE 'cron_process_new_moorage_fn processing queue [%] for moorage id [%]', process_rec.id, process_rec.payload;
|
||||
-- update moorage
|
||||
PERFORM process_moorage_queue_fn(process_rec.payload::INTEGER);
|
||||
-- update process_queue table , processed
|
||||
@@ -87,7 +87,7 @@ begin
|
||||
SET
|
||||
processed = NOW()
|
||||
WHERE id = process_rec.id;
|
||||
RAISE NOTICE '-> cron_process_new_moorage_fn updated process_queue table [%]', process_rec.id;
|
||||
RAISE NOTICE 'cron_process_new_moorage_fn processed queue [%] for moorage id [%]', process_rec.id, process_rec.payload;
|
||||
END LOOP;
|
||||
END;
|
||||
$$ language plpgsql;
|
||||
@@ -127,12 +127,12 @@ begin
|
||||
IF metadata_rec.vessel_id IS NULL OR metadata_rec.vessel_id = '' THEN
|
||||
RAISE WARNING '-> cron_process_monitor_offline_fn invalid metadata record vessel_id %', vessel_id;
|
||||
RAISE EXCEPTION 'Invalid metadata'
|
||||
USING HINT = 'Unknow vessel_id';
|
||||
USING HINT = 'Unknown vessel_id';
|
||||
RETURN;
|
||||
END IF;
|
||||
PERFORM set_config('vessel.id', metadata_rec.vessel_id, false);
|
||||
RAISE DEBUG '-> DEBUG cron_process_monitor_offline_fn vessel.id %', current_setting('vessel.id', false);
|
||||
RAISE NOTICE '-> cron_process_monitor_offline_fn updated api.metadata table to inactive for [%] [%]', metadata_rec.id, metadata_rec.vessel_id;
|
||||
RAISE NOTICE 'cron_process_monitor_offline_fn updated api.metadata table to inactive for [%] [%]', metadata_rec.id, metadata_rec.vessel_id;
|
||||
|
||||
-- Gather email and pushover app settings
|
||||
--app_settings = get_app_settings_fn();
|
||||
@@ -182,7 +182,7 @@ begin
|
||||
IF metadata_rec.vessel_id IS NULL OR metadata_rec.vessel_id = '' THEN
|
||||
RAISE WARNING '-> cron_process_monitor_online_fn invalid metadata record vessel_id %', vessel_id;
|
||||
RAISE EXCEPTION 'Invalid metadata'
|
||||
USING HINT = 'Unknow vessel_id';
|
||||
USING HINT = 'Unknown vessel_id';
|
||||
RETURN;
|
||||
END IF;
|
||||
PERFORM set_config('vessel.id', metadata_rec.vessel_id, false);
|
||||
@@ -385,4 +385,91 @@ $$ language plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
public.cron_process_alerts_fn
|
||||
IS 'init by pg_cron to check for alerts, if so perform process_alerts_queue_fn';
|
||||
IS 'init by pg_cron to check for alerts';
|
||||
|
||||
-- CRON for no vessel notification
|
||||
CREATE FUNCTION cron_process_no_vessel_fn() RETURNS void AS $no_vessel$
|
||||
DECLARE
|
||||
no_vessel record;
|
||||
user_settings jsonb;
|
||||
BEGIN
|
||||
-- Check for user with no vessel register
|
||||
RAISE NOTICE 'cron_process_no_vessel_fn';
|
||||
FOR no_vessel in
|
||||
SELECT a.user_id,a.email,a.first
|
||||
FROM auth.accounts a
|
||||
WHERE NOT EXISTS (
|
||||
SELECT *
|
||||
FROM auth.vessels v
|
||||
WHERE v.owner_email = a.email)
|
||||
LOOP
|
||||
RAISE NOTICE '-> cron_process_no_vessel_rec_fn for [%]', no_vessel;
|
||||
SELECT json_build_object('email', no_vessel.email, 'recipient', a.first) into user_settings;
|
||||
RAISE NOTICE '-> debug cron_process_no_vessel_rec_fn [%]', user_settings;
|
||||
-- Send notification
|
||||
PERFORM send_notification_fn('no_vessel'::TEXT, user_settings::JSONB);
|
||||
END LOOP;
|
||||
END;
|
||||
$no_vessel$ language plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
public.cron_process_no_vessel_fn
|
||||
IS 'init by pg_cron, check for user with no vessel register then send notification';
|
||||
|
||||
-- CRON for no metadata notification
|
||||
CREATE FUNCTION cron_process_no_metadata_fn() RETURNS void AS $no_metadata$
|
||||
DECLARE
|
||||
no_metadata_rec record;
|
||||
user_settings jsonb;
|
||||
BEGIN
|
||||
-- Check for vessel register but with no metadata
|
||||
RAISE NOTICE 'cron_process_no_metadata_fn';
|
||||
FOR no_metadata_rec in
|
||||
SELECT
|
||||
a.user_id,a.email,a.first
|
||||
FROM auth.accounts a, auth.vessels v
|
||||
WHERE NOT EXISTS (
|
||||
SELECT *
|
||||
FROM api.metadata m
|
||||
WHERE v.vessel_id = m.vessel_id) AND v.owner_email = a.email
|
||||
LOOP
|
||||
RAISE NOTICE '-> cron_process_no_activity_rec_fn for [%]', no_metadata_rec;
|
||||
SELECT json_build_object('email', no_metadata_rec.email, 'recipient', a.first) into user_settings;
|
||||
RAISE NOTICE '-> debug cron_process_no_metadata_rec_fn [%]', user_settings;
|
||||
-- Send notification
|
||||
PERFORM send_notification_fn('no_metadata'::TEXT, user_settings::JSONB);
|
||||
END LOOP;
|
||||
END;
|
||||
$no_metadata$ language plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
public.cron_process_no_metadata_fn
|
||||
IS 'init by pg_cron, check for vessel with no metadata then send notification';
|
||||
|
||||
-- CRON for no activity notification
|
||||
CREATE FUNCTION cron_process_no_activity_fn() RETURNS void AS $no_activity$
|
||||
DECLARE
|
||||
no_activity_rec record;
|
||||
user_settings jsonb;
|
||||
BEGIN
|
||||
-- Check for vessel with no activity for more than 200 days
|
||||
RAISE NOTICE 'cron_process_no_activity_fn';
|
||||
FOR no_activity_rec in
|
||||
SELECT
|
||||
v.owner_email,m.name,m.vessel_id,m.time
|
||||
FROM api.metadata m
|
||||
LEFT JOIN auth.vessels v ON v.vessel_id = m.vessel_id
|
||||
WHERE m.time <= NOW() AT TIME ZONE 'UTC' - INTERVAL '200 DAYS'
|
||||
LOOP
|
||||
RAISE NOTICE '-> cron_process_no_activity_rec_fn for [%]', no_activity_rec;
|
||||
SELECT json_build_object('email', no_activity_rec.owner_email, 'recipient', a.first) into user_settings;
|
||||
RAISE NOTICE '-> debug cron_process_no_activity_rec_fn [%]', user_settings;
|
||||
-- Send notification
|
||||
PERFORM send_notification_fn('no_activity'::TEXT, user_settings::JSONB);
|
||||
END LOOP;
|
||||
END;
|
||||
$no_activity$ language plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
public.cron_process_no_activity_fn
|
||||
IS 'init by pg_cron, check for vessel with no activity for more than 200 days then send notification';
|
||||
|
@@ -112,7 +112,22 @@ INSERT INTO email_templates VALUES
|
||||
'Telegram bot',
|
||||
E'Hello __RECIPIENT__,\nCongratulations! You have just connect your account to your vessel, @postgsail_bot.\n\nThe PostgSail Team',
|
||||
'Telegram bot!',
|
||||
E'Congratulations!\nYou have just connect your account to your vessel, @postgsail_bot.\n');
|
||||
E'Congratulations!\nYou have just connect your account to your vessel, @postgsail_bot.\n'),
|
||||
('no_vessel',
|
||||
'PostgSail add your boat',
|
||||
E'Hello __RECIPIENT__,\nYou have created an account on PostgSail but you have not created your boat yet.\nIf you need any assistance we would be happy to help. It is free and an open-source.\nThe PostgSail Team',
|
||||
'PostgSail next step',
|
||||
E'Hello,\nYou should create your vessel. Check your email!\n'),
|
||||
('no_metadata',
|
||||
'PostgSail connect your boat',
|
||||
E'Hello __RECIPIENT__,\nYou have created an account on PostgSail but you have not connected your boat yet.\nIf you need any assistance we would be happy to help. It is free and an open-source.\nThe PostgSail Team',
|
||||
'PostgSail next step',
|
||||
E'Hello,\nYou should connect your vessel. Check your email!\n'),
|
||||
('no_activity',
|
||||
'PostgSail boat inactivity',
|
||||
E'Hello __RECIPIENT__,\nWe don\'t see any activity on your account, do you need any assistance?\nIf you need any assistance we would be happy to help. It is free and an open-source.\nThe PostgSail Team',
|
||||
'PostgSail inactivity!',
|
||||
E'Congratulations!\nWe detected inactivity. Check your email!\n');
|
||||
|
||||
---------------------------------------------------------------------------
|
||||
-- Queue handling
|
||||
|
@@ -54,7 +54,7 @@ CREATE OR REPLACE FUNCTION logbook_update_avg_fn(
|
||||
OUT count_metric integer
|
||||
) AS $logbook_update_avg$
|
||||
BEGIN
|
||||
RAISE NOTICE '-> Updating avg for logbook id=%, start:"%", end:"%"', _id, _start, _end;
|
||||
RAISE NOTICE '-> logbook_update_avg_fn calculate avg for logbook id=%, start:"%", end:"%"', _id, _start, _end;
|
||||
SELECT AVG(speedoverground), MAX(speedoverground), MAX(windspeedapparent), COUNT(*) INTO
|
||||
avg_speed, max_speed, max_wind_speed, count_metric
|
||||
FROM api.metrics m
|
||||
@@ -63,7 +63,7 @@ CREATE OR REPLACE FUNCTION logbook_update_avg_fn(
|
||||
AND m.time >= _start::TIMESTAMP WITHOUT TIME ZONE
|
||||
AND m.time <= _end::TIMESTAMP WITHOUT TIME ZONE
|
||||
AND vessel_id = current_setting('vessel.id', false);
|
||||
RAISE NOTICE '-> Updated avg for logbook id=%, avg_speed:%, max_speed:%, max_wind_speed:%, count:%', _id, avg_speed, max_speed, max_wind_speed, count_metric;
|
||||
RAISE NOTICE '-> logbook_update_avg_fn avg for logbook id=%, avg_speed:%, max_speed:%, max_wind_speed:%, count:%', _id, avg_speed, max_speed, max_wind_speed, count_metric;
|
||||
END;
|
||||
$logbook_update_avg$ LANGUAGE plpgsql;
|
||||
-- Description
|
||||
@@ -256,25 +256,25 @@ AS $logbook_get_extra_json$
|
||||
AND time = _start::timestamp without time zone
|
||||
AND vessel_id = current_setting('vessel.id', false)
|
||||
LOOP
|
||||
-- Engine Hours in seconds
|
||||
raise notice '-> logbook_get_extra_json_fn metric: %', metric_rec;
|
||||
with
|
||||
end_metric AS (
|
||||
-- Fetch 'tanks.%.currentVolume' last entry
|
||||
SELECT key, value
|
||||
FROM api.metrics m,
|
||||
jsonb_each_text(m.metrics)
|
||||
WHERE key ILIKE metric_rec.key
|
||||
AND time = _end::timestamp without time zone
|
||||
AND vessel_id = current_setting('vessel.id', false)
|
||||
),
|
||||
metric AS (
|
||||
-- Subtract
|
||||
SELECT (end_metric.value::numeric - metric_rec.value::numeric) AS value FROM end_metric
|
||||
)
|
||||
-- Generate JSON
|
||||
SELECT jsonb_build_object(metric_rec.key, metric.value) INTO metric_json FROM metrics;
|
||||
raise notice '-> logbook_get_extra_json_fn key: %, value: %', metric_rec.key, metric_json;
|
||||
-- Engine Hours in seconds
|
||||
RAISE NOTICE '-> logbook_get_extra_json_fn metric: %', metric_rec;
|
||||
WITH
|
||||
end_metric AS (
|
||||
-- Fetch 'tanks.%.currentVolume' last entry
|
||||
SELECT key, value
|
||||
FROM api.metrics m,
|
||||
jsonb_each_text(m.metrics)
|
||||
WHERE key ILIKE metric_rec.key
|
||||
AND time = _end::timestamp without time zone
|
||||
AND vessel_id = current_setting('vessel.id', false)
|
||||
),
|
||||
metric AS (
|
||||
-- Subtract
|
||||
SELECT (end_metric.value::numeric - metric_rec.value::numeric) AS value FROM end_metric
|
||||
)
|
||||
-- Generate JSON
|
||||
SELECT jsonb_build_object(metric_rec.key, metric.value) INTO metric_json FROM metrics;
|
||||
RAISE NOTICE '-> logbook_get_extra_json_fn key: %, value: %', metric_rec.key, metric_json;
|
||||
END LOOP;
|
||||
END;
|
||||
$logbook_get_extra_json$ LANGUAGE plpgsql;
|
||||
@@ -319,7 +319,7 @@ CREATE FUNCTION logbook_update_extra_json_fn(IN _id integer, IN _start text, IN
|
||||
)
|
||||
-- Generate JSON
|
||||
SELECT jsonb_build_object('navigation.log', trip) INTO log_json FROM nm;
|
||||
raise notice '-> logbook_update_extra_json_fn navigation.log: %', log_json;
|
||||
RAISE NOTICE '-> logbook_update_extra_json_fn navigation.log: %', log_json;
|
||||
|
||||
-- Calculate engine hours from propulsion.%.runTime first entry
|
||||
FOR metric_rec IN
|
||||
@@ -331,7 +331,7 @@ CREATE FUNCTION logbook_update_extra_json_fn(IN _id integer, IN _start text, IN
|
||||
AND vessel_id = current_setting('vessel.id', false)
|
||||
LOOP
|
||||
-- Engine Hours in seconds
|
||||
raise notice '-> logbook_update_extra_json_fn propulsion.*.runTime: %', metric_rec;
|
||||
RAISE NOTICE '-> logbook_update_extra_json_fn propulsion.*.runTime: %', metric_rec;
|
||||
with
|
||||
end_runtime AS (
|
||||
-- Fetch 'propulsion.*.runTime' last entry
|
||||
@@ -348,13 +348,13 @@ CREATE FUNCTION logbook_update_extra_json_fn(IN _id integer, IN _start text, IN
|
||||
)
|
||||
-- Generate JSON
|
||||
SELECT jsonb_build_object(metric_rec.key, runtime.value) INTO runtime_json FROM runtime;
|
||||
raise notice '-> logbook_update_extra_json_fn key: %, value: %', metric_rec.key, runtime_json;
|
||||
RAISE NOTICE '-> logbook_update_extra_json_fn key: %, value: %', metric_rec.key, runtime_json;
|
||||
END LOOP;
|
||||
|
||||
-- Update logbook with extra value and return json
|
||||
SELECT COALESCE(log_json::JSONB, '{}'::jsonb) || COALESCE(runtime_json::JSONB, '{}'::jsonb) INTO metrics_json;
|
||||
SELECT jsonb_build_object('metrics', metrics_json, 'observations', obs_json) INTO _extra_json;
|
||||
raise notice '-> logbook_update_extra_json_fn log_json: %, runtime_json: %, _extra_json: %', log_json, runtime_json, _extra_json;
|
||||
RAISE NOTICE '-> logbook_update_extra_json_fn log_json: %, runtime_json: %, _extra_json: %', log_json, runtime_json, _extra_json;
|
||||
END;
|
||||
$logbook_extra_json$ LANGUAGE plpgsql;
|
||||
-- Description
|
||||
@@ -385,6 +385,7 @@ CREATE OR REPLACE FUNCTION process_logbook_queue_fn(IN _id integer) RETURNS void
|
||||
current_stays_id numeric;
|
||||
current_stays_active boolean;
|
||||
extra_json jsonb;
|
||||
geo jsonb;
|
||||
BEGIN
|
||||
-- If _id is not NULL
|
||||
IF _id IS NULL OR _id < 1 THEN
|
||||
@@ -475,8 +476,10 @@ CREATE OR REPLACE FUNCTION process_logbook_queue_fn(IN _id integer) RETURNS void
|
||||
-- Generate logbook name, concat _from_location and _to_location
|
||||
-- geo reverse _from_lng _from_lat
|
||||
-- geo reverse _to_lng _to_lat
|
||||
from_name := reverse_geocode_py_fn('nominatim', logbook_rec._from_lng::NUMERIC, logbook_rec._from_lat::NUMERIC);
|
||||
to_name := reverse_geocode_py_fn('nominatim', logbook_rec._to_lng::NUMERIC, logbook_rec._to_lat::NUMERIC);
|
||||
geo := reverse_geocode_py_fn('nominatim', logbook_rec._from_lng::NUMERIC, logbook_rec._from_lat::NUMERIC);
|
||||
from_name := geo->>'name';
|
||||
geo := reverse_geocode_py_fn('nominatim', logbook_rec._to_lng::NUMERIC, logbook_rec._to_lat::NUMERIC);
|
||||
to_name := geo->>'name';
|
||||
SELECT CONCAT(from_name, ' to ' , to_name) INTO log_name;
|
||||
|
||||
-- Process `propulsion.*.runTime` and `navigation.log`
|
||||
@@ -516,12 +519,12 @@ CREATE OR REPLACE FUNCTION process_logbook_queue_fn(IN _id integer) RETURNS void
|
||||
SELECT json_build_object('logbook_name', log_name, 'logbook_link', logbook_rec.id) into log_settings;
|
||||
user_settings := get_user_settings_from_vesselid_fn(logbook_rec.vessel_id::TEXT);
|
||||
SELECT user_settings::JSONB || log_settings::JSONB into user_settings;
|
||||
RAISE DEBUG '-> debug process_logbook_queue_fn get_user_settings_from_vesselid_fn [%]', user_settings;
|
||||
RAISE DEBUG '-> debug process_logbook_queue_fn log_settings [%]', log_settings;
|
||||
RAISE NOTICE '-> debug process_logbook_queue_fn get_user_settings_from_vesselid_fn [%]', user_settings;
|
||||
RAISE NOTICE '-> debug process_logbook_queue_fn log_settings [%]', log_settings;
|
||||
-- Send notification
|
||||
PERFORM send_notification_fn('logbook'::TEXT, user_settings::JSONB);
|
||||
-- Process badges
|
||||
RAISE DEBUG '-> debug process_logbook_queue_fn user_settings [%]', user_settings->>'email'::TEXT;
|
||||
RAISE NOTICE '-> debug process_logbook_queue_fn user_settings [%]', user_settings->>'email'::TEXT;
|
||||
PERFORM set_config('user.email', user_settings->>'email'::TEXT, false);
|
||||
PERFORM badges_logbook_fn(logbook_rec.id);
|
||||
PERFORM badges_geom_fn(logbook_rec.id);
|
||||
@@ -537,7 +540,7 @@ DROP FUNCTION IF EXISTS process_stay_queue_fn;
|
||||
CREATE OR REPLACE FUNCTION process_stay_queue_fn(IN _id integer) RETURNS void AS $process_stay_queue$
|
||||
DECLARE
|
||||
stay_rec record;
|
||||
_name varchar;
|
||||
geo jsonb;
|
||||
BEGIN
|
||||
RAISE NOTICE 'process_stay_queue_fn';
|
||||
-- If _id is valid, not NULL
|
||||
@@ -559,12 +562,12 @@ CREATE OR REPLACE FUNCTION process_stay_queue_fn(IN _id integer) RETURNS void AS
|
||||
|
||||
PERFORM set_config('vessel.id', stay_rec.vessel_id, false);
|
||||
-- geo reverse _lng _lat
|
||||
_name := reverse_geocode_py_fn('nominatim', stay_rec.longitude::NUMERIC, stay_rec.latitude::NUMERIC);
|
||||
geo := reverse_geocode_py_fn('nominatim', stay_rec.longitude::NUMERIC, stay_rec.latitude::NUMERIC);
|
||||
|
||||
RAISE NOTICE 'Updating stay entry [%]', stay_rec.id;
|
||||
UPDATE api.stays
|
||||
SET
|
||||
name = _name,
|
||||
name = coalesce(geo->>'name', null),
|
||||
geog = Geography(ST_MakePoint(stay_rec.longitude, stay_rec.latitude))
|
||||
WHERE id = stay_rec.id;
|
||||
|
||||
@@ -585,6 +588,7 @@ CREATE OR REPLACE FUNCTION process_moorage_queue_fn(IN _id integer) RETURNS void
|
||||
stay_rec record;
|
||||
moorage_rec record;
|
||||
user_settings jsonb;
|
||||
geo jsonb;
|
||||
BEGIN
|
||||
RAISE NOTICE 'process_moorage_queue_fn';
|
||||
-- If _id is not NULL
|
||||
@@ -647,16 +651,19 @@ CREATE OR REPLACE FUNCTION process_moorage_queue_fn(IN _id integer) RETURNS void
|
||||
WHERE id = moorage_rec.id;
|
||||
ELSE
|
||||
RAISE NOTICE 'Insert new moorage entry from stay %', stay_rec;
|
||||
-- Ensure the stay as a name if lat,lon
|
||||
IF stay_rec.name IS NULL AND stay_rec.longitude IS NOT NULL AND stay_rec.latitude IS NOT NULL THEN
|
||||
stay_rec.name := reverse_geocode_py_fn('nominatim', stay_rec.longitude::NUMERIC, stay_rec.latitude::NUMERIC);
|
||||
-- Set the moorage name and country if lat,lon
|
||||
IF stay_rec.longitude IS NOT NULL AND stay_rec.latitude IS NOT NULL THEN
|
||||
geo := reverse_geocode_py_fn('nominatim', stay_rec.longitude::NUMERIC, stay_rec.latitude::NUMERIC);
|
||||
moorage_rec.name = geo->>'name';
|
||||
moorage_rec.country = geo->>'country_code';
|
||||
END IF;
|
||||
-- Insert new moorage from stay
|
||||
INSERT INTO api.moorages
|
||||
(vessel_id, name, stay_id, stay_code, stay_duration, reference_count, latitude, longitude, geog)
|
||||
(vessel_id, name, country, stay_id, stay_code, stay_duration, reference_count, latitude, longitude, geog)
|
||||
VALUES (
|
||||
stay_rec.vessel_id,
|
||||
stay_rec.name,
|
||||
coalesce(moorage_rec.name, null),
|
||||
coalesce(moorage_rec.country, null),
|
||||
stay_rec.id,
|
||||
stay_rec.stay_code,
|
||||
(stay_rec.departed::timestamp without time zone - stay_rec.arrived::timestamp without time zone),
|
||||
@@ -1232,7 +1239,7 @@ CREATE OR REPLACE FUNCTION public.badges_geom_fn(IN logbook_id integer) RETURNS
|
||||
user_settings jsonb;
|
||||
badge_tmp text;
|
||||
begin
|
||||
RAISE WARNING '--> user.email [%], vessel.id [%]', current_setting('user.email', false), current_setting('vessel.id', false);
|
||||
RAISE NOTICE '--> public.badges_geom_fn user.email [%], vessel.id [%]', current_setting('user.email', false), current_setting('vessel.id', false);
|
||||
-- Tropical & Alaska zone manually add into ne_10m_geography_marine_polys
|
||||
-- Check if each geographic marine zone exist as a badge
|
||||
FOR marine_rec IN
|
||||
@@ -1411,3 +1418,33 @@ BEGIN
|
||||
RETURN True;
|
||||
END
|
||||
$delete_account$ language plpgsql security definer;
|
||||
|
||||
-- Dump all data for a account by email and vessel_id
|
||||
CREATE OR REPLACE FUNCTION public.dump_account_fn(IN _email TEXT, IN _vessel_id TEXT) RETURNS BOOLEAN
|
||||
AS $dump_account$
|
||||
BEGIN
|
||||
-- TODO use COPY but we can't all in one?
|
||||
RETURN True;
|
||||
select count(*) from api.metrics m where vessel_id = _vessel_id;
|
||||
select * from api.metadata m where vessel_id = _vessel_id;
|
||||
select * from api.logbook l where vessel_id = _vessel_id;
|
||||
select * from api.moorages m where vessel_id = _vessel_id;
|
||||
select * from api.stays s where vessel_id = _vessel_id;
|
||||
select * from auth.vessels v where vessel_id = _vessel_id;
|
||||
select * from auth.accounts a where email = _email;
|
||||
END
|
||||
$dump_account$ language plpgsql security definer;
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.delete_vessel_fn(IN _vessel_id TEXT) RETURNS BOOLEAN
|
||||
AS $delete_account$
|
||||
BEGIN
|
||||
select count(*) from api.metrics m where vessel_id = _vessel_id;
|
||||
delete from api.metrics m where vessel_id = _vessel_id;
|
||||
select * from api.metadata m where vessel_id = _vessel_id;
|
||||
delete from api.logbook l where vessel_id = _vessel_id;
|
||||
delete from api.moorages m where vessel_id = _vessel_id;
|
||||
delete from api.stays s where vessel_id = _vessel_id;
|
||||
delete from api.metadata m where vessel_id = _vessel_id;
|
||||
RETURN True;
|
||||
END
|
||||
$delete_account$ language plpgsql security definer;
|
@@ -13,6 +13,23 @@ CREATE SCHEMA IF NOT EXISTS public;
|
||||
---------------------------------------------------------------------------
|
||||
-- basic helpers to check type and more
|
||||
--
|
||||
CREATE OR REPLACE FUNCTION public.isdouble(text) RETURNS BOOLEAN AS
|
||||
$isdouble$
|
||||
DECLARE x DOUBLE PRECISION;
|
||||
BEGIN
|
||||
x = $1::DOUBLE PRECISION;
|
||||
RETURN TRUE;
|
||||
EXCEPTION WHEN others THEN
|
||||
RETURN FALSE;
|
||||
END;
|
||||
$isdouble$
|
||||
STRICT
|
||||
LANGUAGE plpgsql IMMUTABLE;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
public.isdouble
|
||||
IS 'Check typeof value is double';
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.isnumeric(text) RETURNS BOOLEAN AS
|
||||
$isnumeric$
|
||||
DECLARE x NUMERIC;
|
||||
|
@@ -17,7 +17,7 @@ CREATE SCHEMA IF NOT EXISTS public;
|
||||
--
|
||||
DROP FUNCTION IF EXISTS reverse_geocode_py_fn;
|
||||
CREATE OR REPLACE FUNCTION reverse_geocode_py_fn(IN geocoder TEXT, IN lon NUMERIC, IN lat NUMERIC,
|
||||
OUT geo_name TEXT)
|
||||
OUT geo jsonb)
|
||||
AS $reverse_geocode_py$
|
||||
import requests
|
||||
|
||||
@@ -42,37 +42,44 @@ AS $reverse_geocode_py$
|
||||
# Make the request to the geocoder API
|
||||
# https://operations.osmfoundation.org/policies/nominatim/
|
||||
payload = {"lon": lon, "lat": lat, "format": "jsonv2", "zoom": 18}
|
||||
r = requests.get(url, params=payload)
|
||||
# https://nominatim.org/release-docs/latest/api/Reverse/
|
||||
r = requests.get(url, headers = {"Accept-Language": "en-US,en;q=0.5"}, params=payload)
|
||||
|
||||
# Return the full address or nothing if not found
|
||||
# Parse response
|
||||
# Option1: If name is null fallback to address field road,neighbourhood,suburb
|
||||
# Option2: Return the json for future reference like country
|
||||
if r.status_code == 200 and "name" in r.json():
|
||||
r_dict = r.json()
|
||||
#plpy.notice('reverse_geocode_py_fn Parameters [{}] [{}] Response'.format(lon, lat, r_dict))
|
||||
output = None
|
||||
country_code = None
|
||||
if "country_code" in r_dict["address"] and r_dict["address"]["country_code"]:
|
||||
country_code = r_dict["address"]["country_code"]
|
||||
if r_dict["name"]:
|
||||
return r_dict["name"]
|
||||
return { "name": r_dict["name"], "country_code": country_code }
|
||||
elif "address" in r_dict and r_dict["address"]:
|
||||
if "road" in r_dict["address"] and r_dict["address"]["road"]:
|
||||
return r_dict["address"]["road"]
|
||||
elif "neighbourhood" in r_dict["address"] and r_dict["address"]["neighbourhood"]:
|
||||
return r_dict["address"]["neighbourhood"]
|
||||
if "neighbourhood" in r_dict["address"] and r_dict["address"]["neighbourhood"]:
|
||||
return { "name": r_dict["address"]["neighbourhood"], "country_code": country_code }
|
||||
elif "road" in r_dict["address"] and r_dict["address"]["road"]:
|
||||
return { "name": r_dict["address"]["road"], "country_code": country_code }
|
||||
elif "suburb" in r_dict["address"] and r_dict["address"]["suburb"]:
|
||||
return r_dict["address"]["suburb"]
|
||||
return { "name": r_dict["address"]["suburb"], "country_code": country_code }
|
||||
elif "residential" in r_dict["address"] and r_dict["address"]["residential"]:
|
||||
return r_dict["address"]["residential"]
|
||||
return { "name": r_dict["address"]["residential"], "country_code": country_code }
|
||||
elif "village" in r_dict["address"] and r_dict["address"]["village"]:
|
||||
return r_dict["address"]["village"]
|
||||
return { "name": r_dict["address"]["village"], "country_code": country_code }
|
||||
elif "town" in r_dict["address"] and r_dict["address"]["town"]:
|
||||
return r_dict["address"]["town"]
|
||||
return { "name": r_dict["address"]["town"], "country_code": country_code }
|
||||
else:
|
||||
return 'n/a'
|
||||
return { "name": "n/a", "country_code": country_code }
|
||||
else:
|
||||
return 'n/a'
|
||||
return { "name": "n/a", "country_code": country_code }
|
||||
else:
|
||||
plpy.warning('Failed to received a geo full address %s', r.json())
|
||||
#plpy.error('Failed to received a geo full address %s', r.json())
|
||||
return 'unknow'
|
||||
$reverse_geocode_py$ LANGUAGE plpython3u;
|
||||
return { "name": "unknown", "country_code": "unknown" }
|
||||
$reverse_geocode_py$ TRANSFORM FOR TYPE jsonb LANGUAGE plpython3u;
|
||||
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
public.reverse_geocode_py_fn
|
||||
@@ -157,7 +164,7 @@ AS $send_email_py$
|
||||
# Send the message via our own SMTP server.
|
||||
try:
|
||||
# send your message with credentials specified above
|
||||
with smtplib.SMTP(server_smtp, 25) as server:
|
||||
with smtplib.SMTP(server_smtp, 587) as server:
|
||||
if 'app.email_user' in app and app['app.email_user'] \
|
||||
and 'app.email_pass' in app and app['app.email_pass']:
|
||||
server.starttls()
|
||||
@@ -358,7 +365,7 @@ AS $reverse_geoip_py$
|
||||
r = requests.get(url)
|
||||
#print(r.text)
|
||||
# Return something boolean?
|
||||
#plpy.notice('IP [{}] [{}]'.format(_ip, r.status_code))
|
||||
plpy.warning('IP [{}] [{}]'.format(_ip, r.status_code))
|
||||
if r.status_code == 200:
|
||||
#plpy.notice('Got [{}] [{}]'.format(r.text, r.status_code))
|
||||
return r.text;
|
||||
|
@@ -183,7 +183,10 @@ begin
|
||||
-- check email and password
|
||||
select auth.user_role(email, pass) into _role;
|
||||
if _role is null then
|
||||
raise invalid_password using message = 'invalid user or password';
|
||||
-- HTTP/403
|
||||
--raise invalid_password using message = 'invalid user or password';
|
||||
-- HTTP/401
|
||||
raise insufficient_privilege using message = 'invalid user or password';
|
||||
end if;
|
||||
|
||||
-- Get app_jwt_secret
|
||||
|
@@ -38,7 +38,9 @@ CREATE OR REPLACE VIEW api.vessels_view AS
|
||||
v.name as name,
|
||||
v.mmsi as mmsi,
|
||||
v.created_at::timestamp(0) as created_at,
|
||||
m.last_contact as last_contact
|
||||
m.last_contact as last_contact,
|
||||
((NOW() AT TIME ZONE 'UTC' - m.last_contact::timestamp without time zone) > INTERVAL '70 MINUTES') as offline,
|
||||
(NOW() AT TIME ZONE 'UTC' - m.last_contact::timestamp without time zone) as duration
|
||||
FROM auth.vessels v, metadata m
|
||||
WHERE v.owner_email = current_setting('user.email');
|
||||
-- Description
|
||||
@@ -230,15 +232,16 @@ $vessel_details$
|
||||
DECLARE
|
||||
BEGIN
|
||||
RETURN ( WITH tbl AS (
|
||||
SELECT mmsi,ship_type,length,beam,height FROM api.metadata WHERE vessel_id = current_setting('vessel.id', false)
|
||||
SELECT mmsi,ship_type,length,beam,height,plugin_version FROM api.metadata WHERE vessel_id = current_setting('vessel.id', false)
|
||||
)
|
||||
SELECT json_build_object(
|
||||
'ship_type', (SELECT ais.description FROM aistypes ais, tbl WHERE t.ship_type = ais.id),
|
||||
'country', (SELECT mid.country FROM mid, tbl WHERE LEFT(cast(mmsi as text), 3)::NUMERIC = mid.id),
|
||||
'alpha_2', (SELECT o.alpha_2 FROM mid m, iso3166 o, tbl WHERE LEFT(cast(mmsi as text), 3)::NUMERIC = m.id AND m.country_id = o.id),
|
||||
'ship_type', (SELECT ais.description FROM aistypes ais, tbl t WHERE t.ship_type = ais.id),
|
||||
'country', (SELECT mid.country FROM mid, tbl t WHERE LEFT(cast(t.mmsi as text), 3)::NUMERIC = mid.id),
|
||||
'alpha_2', (SELECT o.alpha_2 FROM mid m, iso3166 o, tbl t WHERE LEFT(cast(t.mmsi as text), 3)::NUMERIC = m.id AND m.country_id = o.id),
|
||||
'length', t.ship_type,
|
||||
'beam', t.beam,
|
||||
'height', t.height)
|
||||
'height', t.height,
|
||||
'plugin_version', t.plugin_version)
|
||||
FROM tbl t
|
||||
);
|
||||
END;
|
||||
@@ -258,4 +261,27 @@ CREATE VIEW api.eventlogs_view WITH (security_invoker=true,security_barrier=true
|
||||
-- Description
|
||||
COMMENT ON VIEW
|
||||
api.eventlogs_view
|
||||
IS 'Event logs view';
|
||||
IS 'Event logs view';
|
||||
|
||||
DROP FUNCTION IF EXISTS api.update_logbook_observations_fn;
|
||||
-- Update/Add a specific user observations into logbook
|
||||
CREATE OR REPLACE FUNCTION api.update_logbook_observations_fn(IN _id INT, IN observations TEXT) RETURNS BOOLEAN AS
|
||||
$update_logbook_observations$
|
||||
DECLARE
|
||||
_value TEXT := NULL;
|
||||
BEGIN
|
||||
RAISE NOTICE '-> update_logbook_extra_fn id:[%] observations:[%]', _id, observations;
|
||||
_value := to_jsonb(observations)::jsonb;
|
||||
-- { 'observations': { 'seaState': -1, 'cloudCoverage': -1, 'visibility': -1 } }
|
||||
UPDATE api.logbook SET extra = public.jsonb_recursive_merge(extra, _value) WHERE id = _id;
|
||||
IF FOUND IS True THEN
|
||||
RETURN True;
|
||||
END IF;
|
||||
RETURN False;
|
||||
END;
|
||||
$update_logbook_observations$ language plpgsql security definer;
|
||||
|
||||
-- Description
|
||||
COMMENT ON FUNCTION
|
||||
api.update_logbook_observations_fn
|
||||
IS 'Update logbook observations jsonb key pair value';
|
||||
|
@@ -115,8 +115,9 @@ COMMENT ON FUNCTION
|
||||
DROP FUNCTION IF EXISTS api.reset;
|
||||
CREATE OR REPLACE FUNCTION api.reset(in pass text, in token text, in uuid text) returns BOOLEAN
|
||||
AS $reset_fn$
|
||||
DECLARE
|
||||
DECLARE
|
||||
_email TEXT := NULL;
|
||||
_pass TEXT := pass;
|
||||
BEGIN
|
||||
-- Check parameters
|
||||
IF token IS NULL OR uuid IS NULL OR pass IS NULL THEN
|
||||
@@ -124,25 +125,25 @@ AS $reset_fn$
|
||||
END IF;
|
||||
-- Verify token
|
||||
SELECT auth.verify_otp_fn(token) INTO _email;
|
||||
IF _email IS NOT NULL THEN
|
||||
IF _email IS NOT NULL THEN
|
||||
SELECT email INTO _email FROM auth.accounts WHERE user_id = uuid;
|
||||
IF _email IS NULL THEN
|
||||
RETURN False;
|
||||
END IF;
|
||||
-- Set user new password
|
||||
UPDATE auth.accounts
|
||||
SET pass = pass
|
||||
SET pass = _pass
|
||||
WHERE email = _email;
|
||||
-- Enable email_validation into user preferences
|
||||
-- Enable email_validation into user preferences
|
||||
PERFORM api.update_user_preferences_fn('{email_valid}'::TEXT, True::TEXT);
|
||||
-- Enable email_notifications
|
||||
PERFORM api.update_user_preferences_fn('{email_notifications}'::TEXT, True::TEXT);
|
||||
-- Delete token when validated
|
||||
DELETE FROM auth.otp
|
||||
WHERE user_email = _email;
|
||||
RETURN True;
|
||||
END IF;
|
||||
RETURN False;
|
||||
RETURN True;
|
||||
END IF;
|
||||
RETURN False;
|
||||
END;
|
||||
$reset_fn$ language plpgsql security definer;
|
||||
-- Description
|
||||
|
@@ -58,6 +58,8 @@ GRANT SELECT ON TABLE api.metrics,api.logbook,api.moorages,api.stays,api.metadat
|
||||
GRANT SELECT ON TABLE api.logs_view,api.moorages_view,api.stays_view TO grafana;
|
||||
GRANT SELECT ON TABLE api.log_view,api.moorage_view,api.stay_view,api.vessels_view TO grafana;
|
||||
GRANT SELECT ON TABLE api.metrics,api.logbook,api.moorages,api.stays,api.metadata,api.stays_at TO grafana;
|
||||
GRANT SELECT ON TABLE api.monitoring_view,api.monitoring_view2,api.monitoring_view3 TO grafana;
|
||||
GRANT SELECT ON TABLE api.monitoring_humidity,api.monitoring_voltage,api.monitoring_temperatures TO grafana;
|
||||
-- Allow Auth schema and Tables
|
||||
GRANT USAGE ON SCHEMA auth TO grafana;
|
||||
GRANT SELECT ON TABLE auth.vessels TO grafana;
|
||||
|
@@ -64,6 +64,12 @@ SELECT cron.schedule('cron_prune_otp', '*/15 * * * *', 'select public.cron_proce
|
||||
-- Create a every 11 minute job cron_process_alerts_fn
|
||||
--SELECT cron.schedule('cron_alerts', '*/11 * * * *', 'select public.cron_process_alerts_fn()');
|
||||
|
||||
-- Notifications/Reminders of no vessel & no metadata & no activity
|
||||
-- At 08:05 on Sunday.
|
||||
SELECT cron.schedule('cron_no_vessel', '05 08 * * 0', 'select public.cron_process_no_vessel_fn()');
|
||||
SELECT cron.schedule('cron_no_metadata', '05 08 * * 0', 'select public.cron_process_no_metadata_fn()');
|
||||
SELECT cron.schedule('cron_no_activity', '05 08 * * 0', 'select public.cron_process_no_activity_fn()');
|
||||
|
||||
-- Cron job settings
|
||||
UPDATE cron.job SET database = 'signalk';
|
||||
UPDATE cron.job SET username = 'username'; -- TODO update to scheduler, pending process_queue update
|
||||
|
@@ -1 +1 @@
|
||||
0.2.3
|
||||
0.3.0
|
||||
|
File diff suppressed because one or more lines are too long
@@ -35,9 +35,25 @@ SELECT active,name,geog,stay_code FROM api.stays WHERE vessel_id = current_setti
|
||||
|
||||
-- Test event logs view for user
|
||||
\echo 'eventlogs_view'
|
||||
select count(*) from api.eventlogs_view;
|
||||
SELECT count(*) from api.eventlogs_view;
|
||||
|
||||
-- Test event logs view for user
|
||||
\echo 'stats_logs_fn'
|
||||
select api.stats_logs_fn(null, null);
|
||||
select api.stats_logs_fn('2022-01-01'::text,'2022-06-12'::text);
|
||||
SELECT api.stats_logs_fn(null, null) INTO stats_jsonb;
|
||||
SELECT stats_logs_fn->'name' AS name,
|
||||
stats_logs_fn->'count' AS count,
|
||||
stats_logs_fn->'max_speed' As max_speed,
|
||||
stats_logs_fn->'max_distance' AS max_distance,
|
||||
stats_logs_fn->'max_duration' AS max_duration,
|
||||
stats_logs_fn->'max_speed_id',
|
||||
stats_logs_fn->'sum_distance',
|
||||
stats_logs_fn->'sum_duration',
|
||||
stats_logs_fn->'max_wind_speed',
|
||||
stats_logs_fn->'max_distance_id',
|
||||
stats_logs_fn->'max_duration_id',
|
||||
stats_logs_fn->'max_wind_speed_id',
|
||||
stats_logs_fn->'first_date' IS NOT NULL AS first_date,
|
||||
stats_logs_fn->'last_date' IS NOT NULL AS last_date
|
||||
FROM stats_jsonb;
|
||||
DROP TABLE stats_jsonb;
|
||||
SELECT api.stats_logs_fn('2022-01-01'::text,'2022-06-12'::text);
|
||||
|
@@ -17,14 +17,14 @@ count | 2
|
||||
|
||||
logbook
|
||||
-[ RECORD 1 ]--+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
name | Bollsta to Strandallén
|
||||
name | Bollsta to Slottsbacken
|
||||
_from_time | t
|
||||
_to_time | t
|
||||
track_geojson | t
|
||||
track_gpx | t
|
||||
track_geom | 0102000020E61000001A00000020D26F5F0786374030BB270F0B094E400C6E7ED60F843740AA60545227084E40D60FC48C03823740593CE27D42074E407B39D9F322803740984C158C4A064E4091ED7C3F357E3740898BB63D54054E40A8A1208B477C37404BA3DC9059044E404C5CB4EDA17A3740C4F856115B034E40A9A44E4013793740D8F0F44A59024E40E4839ECDAA773740211FF46C56014E405408D147067637408229F03B73004E40787AA52C43743740F90FE9B7AFFF4D40F8098D4D18723740C217265305FF4D4084E82303537037409A2D464AA0FE4D4022474DCE636F37402912396A72FE4D408351499D806E374088CFB02B40FE4D4076711B0DE06D3740B356C7040FFE4D404EAC66B0BC6E374058A835CD3BFE4D40D7A3703D0A6F3740D3E10EC15EFE4D4087602F277B6E3740A779C7293AFE4D4087602F277B6E3740A779C7293AFE4D402063EE5A426E3740B5A679C729FE4D40381DEE10EC6D37409ECA7C1A0AFE4D40E2C46A06CB6B37400A43F7BF36FD4D4075931804566E3740320BDAD125FD4D409A2D464AA06E37404A5658830AFD4D40029A081B9E6E37404A5658830AFD4D40
|
||||
distance | 7.17
|
||||
duration | 00:25:00
|
||||
duration | PT25M
|
||||
avg_speed | 3.6961538461538455
|
||||
max_speed | 6.1
|
||||
max_wind_speed | 22.1
|
||||
@@ -38,7 +38,7 @@ track_geojson | t
|
||||
track_gpx | t
|
||||
track_geom | 0102000020E6100000130000004806A6C0EF6C3740DA1B7C6132FD4D40FE65F7E461693740226C787AA5FC4D407DD3E10EC1663740B29DEFA7C6FB4D40898BB63D5465374068479724BCFA4D409A5271F6E1633740B6847CD0B3F94D40431CEBE236623740E9263108ACF84D402C6519E2585F37407E678EBFC7F74D4096218E75715B374027C5B45C23F74D402AA913D044583740968DE1C46AF64D405AF5B9DA8A5537407BEF829B9FF54D407449C2ABD253374086C954C1A8F44D407D1A0AB278543740F2B0506B9AF34D409D11A5BDC15737406688635DDCF24D4061C3D32B655937402CAF6F3ADCF14D408988888888583740B3319C58CDF04D4021FAC8C0145837408C94405DB7EF4D40B8F9593F105B37403DC0804BEDEE4D40DE4C5FE2A25D3740AE47E17A14EE4D40DE4C5FE2A25D3740AE47E17A14EE4D40
|
||||
distance | 8.6862
|
||||
duration | 00:18:00
|
||||
duration | PT18M
|
||||
avg_speed | 6.026315789473684
|
||||
max_speed | 6.5
|
||||
max_wind_speed | 37.2
|
||||
@@ -57,7 +57,7 @@ geog | 0101000020E6100000B0DEBBE0E68737404DA938FBF0094E40
|
||||
stay_code | 2
|
||||
-[ RECORD 2 ]-------------------------------------------------
|
||||
active | f
|
||||
name | Strandallén
|
||||
name | Slottsbacken
|
||||
geog | 0101000020E6100000029A081B9E6E37404A5658830AFD4D40
|
||||
stay_code | 1
|
||||
-[ RECORD 3 ]-------------------------------------------------
|
||||
@@ -71,9 +71,24 @@ eventlogs_view
|
||||
count | 13
|
||||
|
||||
stats_logs_fn
|
||||
-[ RECORD 1 ]-+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
stats_logs_fn | {"count": 4, "max_speed": 7.1, "max_distance": 8.6862, "max_duration": "01:11:00", "max_speed_id": 3, "sum_duration": "02:37:00", "max_wind_speed": 44.2, "max_distance_id": 2, "max_wind_speed_id": 4}
|
||||
SELECT 1
|
||||
-[ RECORD 1 ]+----------
|
||||
name | "kapla"
|
||||
count | 4
|
||||
max_speed | 7.1
|
||||
max_distance | 8.6862
|
||||
max_duration | "PT1H11M"
|
||||
?column? | 3
|
||||
?column? | 29.2865
|
||||
?column? | "PT2H37M"
|
||||
?column? | 44.2
|
||||
?column? | 2
|
||||
?column? | 4
|
||||
?column? | 4
|
||||
first_date | t
|
||||
last_date | t
|
||||
|
||||
DROP TABLE
|
||||
-[ RECORD 1 ]-+-
|
||||
stats_logs_fn |
|
||||
|
||||
|
@@ -131,14 +131,14 @@ Name | Knipan to Ekenäs
|
||||
From | Knipan
|
||||
To | Ekenäs
|
||||
Distance | 8.6862
|
||||
Duration | 00:18:00
|
||||
Duration | PT18M
|
||||
-[ RECORD 2 ]--------------
|
||||
id | 1
|
||||
Name | patch log name 3
|
||||
From | Bollsta
|
||||
To | Strandallén
|
||||
To | Slottsbacken
|
||||
Distance | 7.17
|
||||
Duration | 00:25:00
|
||||
Duration | PT25M
|
||||
|
||||
api.stays
|
||||
-[ RECORD 1 ]-------------------------------------------------
|
||||
@@ -158,7 +158,7 @@ notes | new stay note 3
|
||||
id | 2
|
||||
vessel_id | t
|
||||
active | f
|
||||
name | Strandallén
|
||||
name | Slottsbacken
|
||||
latitude | 59.97688333333333
|
||||
longitude | 23.4321
|
||||
geog | 0101000020E6100000029A081B9E6E37404A5658830AFD4D40
|
||||
@@ -185,10 +185,10 @@ stays_view
|
||||
-[ RECORD 1 ]+------------------
|
||||
id | 2
|
||||
name | t
|
||||
moorage | Strandallén
|
||||
moorage | Slottsbacken
|
||||
moorage_id | 2
|
||||
duration | 00:03:00
|
||||
stayed_at | Unknow
|
||||
duration | PT3M
|
||||
stayed_at | Unknown
|
||||
stayed_at_id | 1
|
||||
arrived | t
|
||||
departed | t
|
||||
@@ -198,7 +198,7 @@ id | 1
|
||||
name | t
|
||||
moorage | patch stay name 3
|
||||
moorage_id | 1
|
||||
duration | 00:02:00
|
||||
duration | PT2M
|
||||
stayed_at | Anchor
|
||||
stayed_at_id | 2
|
||||
arrived | t
|
||||
@@ -210,10 +210,10 @@ api.moorages
|
||||
id | 1
|
||||
vessel_id | t
|
||||
name | patch moorage name 3
|
||||
country |
|
||||
country | fi
|
||||
stay_id | 1
|
||||
stay_code | 2
|
||||
stay_duration | 00:02:00
|
||||
stay_duration | PT2M
|
||||
reference_count | 1
|
||||
latitude | 60.077666666666666
|
||||
longitude | 23.530866666666668
|
||||
@@ -223,11 +223,11 @@ notes | new moorage note 3
|
||||
-[ RECORD 2 ]---+---------------------------------------------------
|
||||
id | 2
|
||||
vessel_id | t
|
||||
name | Strandallén
|
||||
country |
|
||||
name | Slottsbacken
|
||||
country | fi
|
||||
stay_id | 2
|
||||
stay_code | 1
|
||||
stay_duration | 00:03:00
|
||||
stay_duration | PT3M
|
||||
reference_count | 1
|
||||
latitude | 59.97688333333333
|
||||
longitude | 23.4321
|
||||
@@ -245,8 +245,8 @@ total_stay | 0
|
||||
arrivals_departures | 1
|
||||
-[ RECORD 2 ]-------+---------------------
|
||||
id | 2
|
||||
moorage | Strandallén
|
||||
default_stay | Unknow
|
||||
moorage | Slottsbacken
|
||||
default_stay | Unknown
|
||||
default_stay_id | 1
|
||||
total_stay | 0
|
||||
arrivals_departures | 1
|
||||
|
@@ -6,7 +6,7 @@
|
||||
You are now connected to database "signalk" as user "username".
|
||||
Expanded display is on.
|
||||
-[ RECORD 1 ]--+-------------------------------
|
||||
server_version | 15.4 (Debian 15.4-1.pgdg110+1)
|
||||
server_version | 15.4 (Debian 15.4-2.pgdg110+1)
|
||||
|
||||
-[ RECORD 1 ]--------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
postgis_full_version | POSTGIS="3.4.0 0874ea3" [EXTENSION] PGSQL="150" GEOS="3.9.0-CAPI-1.16.2" PROJ="7.2.1 NETWORK_ENABLED=OFF URL_ENDPOINT=https://cdn.proj.org USER_WRITABLE_DIRECTORY=/var/lib/postgresql/.local/share/proj DATABASE_PATH=/usr/share/proj/proj.db" LIBXML="2.9.10" LIBJSON="0.15" LIBPROTOBUF="1.3.3" WAGYU="0.5.0 (Internal)"
|
||||
@@ -53,7 +53,7 @@ Schema | public
|
||||
Description | PostGIS geometry and geography spatial types and functions
|
||||
-[ RECORD 9 ]--------------------------------------------------------------------------------------
|
||||
Name | timescaledb
|
||||
Version | 2.11.2
|
||||
Version | 2.12.0
|
||||
Schema | public
|
||||
Description | Enables scalable inserts and complex queries for time-series data (Community Edition)
|
||||
-[ RECORD 10 ]-------------------------------------------------------------------------------------
|
||||
@@ -106,14 +106,14 @@ laninline | 13540
|
||||
lanvalidator | 13541
|
||||
lanacl |
|
||||
-[ RECORD 5 ]-+-----------
|
||||
oid | 18174
|
||||
oid | 18283
|
||||
lanname | plpython3u
|
||||
lanowner | 10
|
||||
lanispl | t
|
||||
lanpltrusted | t
|
||||
lanplcallfoid | 18171
|
||||
laninline | 18172
|
||||
lanvalidator | 18173
|
||||
lanplcallfoid | 18280
|
||||
laninline | 18281
|
||||
lanvalidator | 18282
|
||||
lanacl |
|
||||
|
||||
-[ RECORD 1 ]+-----------
|
||||
@@ -592,17 +592,17 @@ qual | true
|
||||
with_check | false
|
||||
|
||||
Test nominatim reverse_geocode_py_fn
|
||||
-[ RECORD 1 ]---------+-------
|
||||
reverse_geocode_py_fn | España
|
||||
-[ RECORD 1 ]---------+----------------------------------------
|
||||
reverse_geocode_py_fn | {"name": "Spain", "country_code": "es"}
|
||||
|
||||
Test geoip reverse_geoip_py_fn
|
||||
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------------------------
|
||||
versions_fn | {"api_version" : "0.2.3", "sys_version" : "PostgreSQL 15.4", "timescaledb" : "2.11.2", "postgis" : "3.4.0", "postgrest" : "PostgREST 11.2.0"}
|
||||
versions_fn | {"api_version" : "0.3.0", "sys_version" : "PostgreSQL 15.4", "timescaledb" : "2.12.0", "postgis" : "3.4.0", "postgrest" : "PostgREST 11.2.1"}
|
||||
|
||||
-[ RECORD 1 ]-----------------
|
||||
api_version | 0.2.3
|
||||
api_version | 0.3.0
|
||||
sys_version | PostgreSQL 15.4
|
||||
timescaledb | 2.11.2
|
||||
timescaledb | 2.12.0
|
||||
postgis | 3.4.0
|
||||
postgrest | PostgREST 11.2.0
|
||||
postgrest | PostgREST 11.2.1
|
||||
|
||||
|
Reference in New Issue
Block a user