mirror of
https://github.com/xbgmsharp/postgsail.git
synced 2025-09-17 19:27:49 +00:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
7744ad4af9 | ||
![]() |
5c70a9a453 | ||
![]() |
6635015dbf | ||
![]() |
f285fcddb0 | ||
![]() |
cabf405648 | ||
![]() |
b2f3372b26 | ||
![]() |
754c9bb6e7 | ||
![]() |
f9238c62dd | ||
![]() |
4a294674e8 | ||
![]() |
83e92cfd6c | ||
![]() |
718ca6d6ea | ||
![]() |
a07f4f181c | ||
![]() |
72b06f9eb9 | ||
![]() |
598a789d36 | ||
![]() |
37e948cb20 | ||
![]() |
f26ece878b | ||
![]() |
9f8b43577e | ||
![]() |
0c76edf793 | ||
![]() |
0cac828347 | ||
![]() |
9e9189ac36 |
@@ -142,5 +142,6 @@ An out of the box IoT platform using Docker (could be extend to K3 or K8) with t
|
||||
- [PostgreSQL, open source object-relational database system](https://postgresql.org)
|
||||
- [TimescaleDB, Time-series data extends PostgreSQL](https://www.timescale.com)
|
||||
- [PostGIS, a spatial database extender for PostgreSQL object-relational database.](https://postgis.net/)
|
||||
- [MobilityDB, An open source geospatial trajectory data management & analysis platform.](https://mobilitydb.com/)
|
||||
- [Grafana, open observability platform | Grafana Labs](https://grafana.com)
|
||||
- And many more
|
||||
|
@@ -1,5 +1,3 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
db:
|
||||
image: xbgmsharp/timescaledb-postgis
|
||||
|
@@ -42,9 +42,13 @@ erDiagram
|
||||
api_metadata {
|
||||
boolean active "trigger monitor online/offline"
|
||||
boolean active
|
||||
jsonb available_keys "Signalk paths with unit for custom mapping"
|
||||
jsonb available_keys
|
||||
double_precision beam
|
||||
text client_id "Deprecated client_id to be removed"
|
||||
text client_id
|
||||
text configuration
|
||||
jsonb configuration "Signalk path mapping for metrics"
|
||||
jsonb configuration
|
||||
timestamp_with_time_zone created_at "{NOT_NULL}"
|
||||
double_precision height
|
||||
integer id "{NOT_NULL}"
|
||||
@@ -63,7 +67,7 @@ erDiagram
|
||||
|
||||
api_metrics {
|
||||
double_precision anglespeedapparent
|
||||
text client_id
|
||||
text client_id "Deprecated client_id to be removed"
|
||||
double_precision courseovergroundtrue
|
||||
double_precision latitude "With CONSTRAINT but allow NULL value to be ignored silently by trigger"
|
||||
double_precision longitude "With CONSTRAINT but allow NULL value to be ignored silently by trigger"
|
||||
|
2
frontend
2
frontend
Submodule frontend updated: 2fb525adad...89726c7d21
@@ -267,7 +267,16 @@ BEGIN
|
||||
COALESCE((m.metrics->'environment.outside.relativeHumidity')::NUMERIC, NULL) as outsidehumidity,
|
||||
COALESCE((m.metrics->'environment.outside.pressure')::NUMERIC, NULL) as outsidepressure,
|
||||
COALESCE((m.metrics->'environment.outside.temperature')::NUMERIC, NULL) as outsidetemperature,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC, NULL) as stateofcharge,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (m.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(m.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.voltage')::NUMERIC, NULL) as voltage,
|
||||
ST_MakePoint(m.longitude, m.latitude) AS geo_point
|
||||
FROM api.metrics m
|
||||
@@ -362,7 +371,16 @@ BEGIN
|
||||
COALESCE((t.metrics->'environment.outside.relativeHumidity')::NUMERIC, NULL) as outsidehumidity,
|
||||
COALESCE((t.metrics->'environment.outside.pressure')::NUMERIC, NULL) as outsidepressure,
|
||||
COALESCE((t.metrics->'environment.outside.temperature')::NUMERIC, NULL) as outsidetemperature,
|
||||
COALESCE((t.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC, NULL) as stateofcharge,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (t.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(t.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE((t.metrics->'electrical.batteries.House.voltage')::NUMERIC, NULL) as voltage,
|
||||
ST_MakePoint(t.longitude, t.latitude) AS geo_point
|
||||
FROM (
|
||||
@@ -395,7 +413,16 @@ BEGIN
|
||||
COALESCE((m.metrics->'environment.outside.relativeHumidity')::NUMERIC, NULL) as outsidehumidity,
|
||||
COALESCE((m.metrics->'environment.outside.pressure')::NUMERIC, NULL) as outsidepressure,
|
||||
COALESCE((m.metrics->'environment.outside.temperature')::NUMERIC, NULL) as outsidetemperature,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC, NULL) as stateofcharge,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (m.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(m.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.voltage')::NUMERIC, NULL) as voltage,
|
||||
ST_MakePoint(m.longitude, m.latitude) AS geo_point
|
||||
FROM api.metrics m
|
||||
@@ -424,7 +451,16 @@ BEGIN
|
||||
COALESCE((m.metrics->'environment.outside.relativeHumidity')::NUMERIC, NULL) as outsidehumidity,
|
||||
COALESCE((m.metrics->'environment.outside.pressure')::NUMERIC, NULL) as outsidepressure,
|
||||
COALESCE((m.metrics->'environment.outside.temperature')::NUMERIC, NULL) as outsidetemperature,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC, NULL) as stateofcharge,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (m.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(m.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.voltage')::NUMERIC, NULL) as voltage,
|
||||
ST_MakePoint(m.longitude, m.latitude) AS geo_point
|
||||
FROM api.metrics m
|
||||
@@ -513,7 +549,7 @@ BEGIN
|
||||
RETURN QUERY
|
||||
WITH metrics AS (
|
||||
-- Extract metrics base the total of entry ignoring first and last 10 minutes metrics
|
||||
SELECT time_bucket(bucket_interval::INTERVAL, m.time) AS time_bucket, -- Time-bucketed period
|
||||
SELECT time_bucket(bucket_interval::INTERVAL, m.time) AS time_bucket, -- Time-bucketed period
|
||||
avg(m.courseovergroundtrue) as courseovergroundtrue,
|
||||
avg(m.speedoverground) as speedoverground,
|
||||
avg(m.windspeedapparent) as windspeedapparent,
|
||||
@@ -527,7 +563,16 @@ BEGIN
|
||||
COALESCE(avg((m.metrics->'environment.outside.relativeHumidity')::NUMERIC), NULL) as outsidehumidity,
|
||||
COALESCE(avg((m.metrics->'environment.outside.pressure')::NUMERIC), NULL) as outsidepressure,
|
||||
COALESCE(avg((m.metrics->'environment.outside.temperature')::NUMERIC), NULL) as outsidetemperature,
|
||||
COALESCE(avg((m.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC), NULL) as stateofcharge,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (m.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(m.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE(avg((m.metrics->'electrical.batteries.House.voltage')::NUMERIC), NULL) as voltage,
|
||||
ST_MakePoint(last(m.longitude, m.time),last(m.latitude, m.time)) AS geo_point
|
||||
FROM api.metrics m
|
||||
@@ -552,13 +597,22 @@ BEGIN
|
||||
m.status,
|
||||
COALESCE(metersToKnots((m.metrics->'environment.wind.speedTrue')::NUMERIC), NULL) AS truewindspeed,
|
||||
COALESCE(radiantToDegrees((m.metrics->'environment.wind.directionTrue')::NUMERIC), NULL) AS truewinddirection,
|
||||
COALESCE(avg((m.metrics->'environment.water.temperature')::NUMERIC), NULL) as watertemperature,
|
||||
COALESCE(avg((m.metrics->'environment.depth.belowTransducer')::NUMERIC), NULL) as depth,
|
||||
COALESCE(avg((m.metrics->'environment.outside.relativeHumidity')::NUMERIC), NULL) as outsidehumidity,
|
||||
COALESCE(avg((m.metrics->'environment.outside.pressure')::NUMERIC), NULL) as outsidepressure,
|
||||
COALESCE(avg((m.metrics->'environment.outside.temperature')::NUMERIC), NULL) as outsidetemperature,
|
||||
COALESCE(avg((m.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC), NULL) as stateofcharge,
|
||||
COALESCE(avg((m.metrics->'electrical.batteries.House.voltage')::NUMERIC), NULL) as voltage,
|
||||
COALESCE((m.metrics->'environment.water.temperature')::NUMERIC, NULL) as watertemperature,
|
||||
COALESCE((m.metrics->'environment.depth.belowTransducer')::NUMERIC, NULL) as depth,
|
||||
COALESCE((m.metrics->'environment.outside.relativeHumidity')::NUMERIC, NULL) as outsidehumidity,
|
||||
COALESCE((m.metrics->'environment.outside.pressure')::NUMERIC, NULL) as outsidepressure,
|
||||
COALESCE((m.metrics->'environment.outside.temperature')::NUMERIC, NULL) as outsidetemperature,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (m.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(m.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.voltage')::NUMERIC, NULL) as voltage,
|
||||
ST_MakePoint(m.longitude, m.latitude) AS geo_point
|
||||
FROM api.metrics m
|
||||
WHERE m.latitude IS NOT NULL
|
||||
@@ -581,13 +635,22 @@ BEGIN
|
||||
m.status,
|
||||
COALESCE(metersToKnots((m.metrics->'environment.wind.speedTrue')::NUMERIC), NULL) AS truewindspeed,
|
||||
COALESCE(radiantToDegrees((m.metrics->'environment.wind.directionTrue')::NUMERIC), NULL) AS truewinddirection,
|
||||
COALESCE(avg((m.metrics->'environment.water.temperature')::NUMERIC), NULL) as watertemperature,
|
||||
COALESCE(avg((m.metrics->'environment.depth.belowTransducer')::NUMERIC), NULL) as depth,
|
||||
COALESCE(avg((m.metrics->'environment.outside.relativeHumidity')::NUMERIC), NULL) as outsidehumidity,
|
||||
COALESCE(avg((m.metrics->'environment.outside.pressure')::NUMERIC), NULL) as outsidepressure,
|
||||
COALESCE(avg((m.metrics->'environment.outside.temperature')::NUMERIC), NULL) as outsidetemperature,
|
||||
COALESCE(avg((m.metrics->'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC), NULL) as stateofcharge,
|
||||
COALESCE(avg((m.metrics->'electrical.batteries.House.voltage')::NUMERIC), NULL) as voltage,
|
||||
COALESCE((m.metrics->'environment.water.temperature')::NUMERIC, NULL) as watertemperature,
|
||||
COALESCE((m.metrics->'environment.depth.belowTransducer')::NUMERIC, NULL) as depth,
|
||||
COALESCE((m.metrics->'environment.outside.relativeHumidity')::NUMERIC, NULL) as outsidehumidity,
|
||||
COALESCE((m.metrics->'environment.outside.pressure')::NUMERIC, NULL) as outsidepressure,
|
||||
COALESCE((m.metrics->'environment.outside.temperature')::NUMERIC, NULL) as outsidetemperature,
|
||||
COALESCE(
|
||||
NULLIF(
|
||||
CASE
|
||||
WHEN (m.metrics->>'electrical.batteries.House.capacity.stateOfCharge') ~ '^-?[0-9]+(\.[0-9]+)?$' THEN
|
||||
(m.metrics->>'electrical.batteries.House.capacity.stateOfCharge')::NUMERIC
|
||||
END,
|
||||
NULL
|
||||
),
|
||||
NULL
|
||||
) as stateofcharge,
|
||||
COALESCE((m.metrics->'electrical.batteries.House.voltage')::NUMERIC, NULL) as voltage,
|
||||
ST_MakePoint(m.longitude, m.latitude) AS geo_point
|
||||
FROM api.metrics m
|
||||
WHERE m.latitude IS NOT NULL
|
||||
@@ -2170,6 +2233,7 @@ AS SELECT id,
|
||||
_to_moorage_id AS to_moorage_id
|
||||
FROM api.logbook l
|
||||
WHERE _to_time IS NOT NULL
|
||||
AND trip IS NOT NULL
|
||||
ORDER BY _from_time DESC;
|
||||
-- Description
|
||||
COMMENT ON VIEW api.log_view IS 'Log web view';
|
||||
@@ -2201,6 +2265,7 @@ BEGIN
|
||||
WHERE id = _id;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION api.delete_trip_entry_fn IS 'Delete at a specific time a temporal sequence for all trip_* column from a logbook';
|
||||
|
||||
-- Update export_logbooks_geojson_point_trips_fn, replace timelapse2_fn, Generate the GeoJSON from the time sequence value
|
||||
@@ -2239,6 +2304,7 @@ BEGIN
|
||||
LATERAL jsonb_array_elements(l.log_geojson) AS feature_element; -- Flatten the arrays and create a GeoJSON FeatureCollection
|
||||
END;
|
||||
$function$;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION api.export_logbooks_geojson_point_trips_fn IS 'Export all selected logs into a geojson `trip` to a geojson as points including properties';
|
||||
|
||||
-- Update api role SQL connection to 40
|
||||
@@ -2268,7 +2334,3 @@ UPDATE public.app_settings
|
||||
SET value='0.8.1'
|
||||
WHERE "name"='app.version';
|
||||
|
||||
\c postgres
|
||||
UPDATE cron.job SET username = 'scheduler'; -- Update to scheduler, pending process_queue update
|
||||
UPDATE cron.job SET username = 'username' WHERE jobname = 'cron_vacuum'; -- Update to superuser for vacuum permissions
|
||||
UPDATE cron.job SET username = 'username' WHERE jobname = 'job_run_details_cleanup';
|
||||
|
216
initdb/99_migrations_202501.sql
Normal file
216
initdb/99_migrations_202501.sql
Normal file
@@ -0,0 +1,216 @@
|
||||
---------------------------------------------------------------------------
|
||||
-- Copyright 2021-2025 Francois Lacroix <xbgmsharp@gmail.com>
|
||||
-- This file is part of PostgSail which is released under Apache License, Version 2.0 (the "License").
|
||||
-- See file LICENSE or go to http://www.apache.org/licenses/LICENSE-2.0 for full license details.
|
||||
--
|
||||
-- Migration January-March 2025
|
||||
--
|
||||
-- List current database
|
||||
select current_database();
|
||||
|
||||
-- connect to the DB
|
||||
\c signalk
|
||||
|
||||
\echo 'Timing mode is enabled'
|
||||
\timing
|
||||
|
||||
\echo 'Force timezone, just in case'
|
||||
set timezone to 'UTC';
|
||||
|
||||
-- Mark client_id as deprecated
|
||||
COMMENT ON COLUMN api.metadata.client_id IS 'Deprecated client_id to be removed';
|
||||
COMMENT ON COLUMN api.metrics.client_id IS 'Deprecated client_id to be removed';
|
||||
|
||||
-- Update metadata table COLUMN type to jsonb
|
||||
ALTER TABLE api.metadata ALTER COLUMN "configuration" TYPE jsonb USING "configuration"::jsonb;
|
||||
COMMENT ON COLUMN api.metadata.configuration IS 'Signalk path mapping for metrics';
|
||||
|
||||
-- Add new column available_keys
|
||||
ALTER TABLE api.metadata ADD available_keys jsonb NULL;
|
||||
COMMENT ON COLUMN api.metadata.available_keys IS 'Signalk paths with unit for custom mapping';
|
||||
|
||||
--DROP FUNCTION public.metadata_upsert_trigger_fn();
|
||||
CREATE OR REPLACE FUNCTION public.metadata_upsert_trigger_fn()
|
||||
RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
DECLARE
|
||||
metadata_id integer;
|
||||
metadata_active boolean;
|
||||
BEGIN
|
||||
-- Require Signalk plugin version 0.4.0
|
||||
-- Set client_id to new value to allow RLS
|
||||
--PERFORM set_config('vessel.client_id', NEW.client_id, false);
|
||||
-- UPSERT - Insert vs Update for Metadata
|
||||
--RAISE NOTICE 'metadata_upsert_trigger_fn';
|
||||
--PERFORM set_config('vessel.id', NEW.vessel_id, true);
|
||||
--RAISE WARNING 'metadata_upsert_trigger_fn [%] [%]', current_setting('vessel.id', true), NEW;
|
||||
SELECT m.id,m.active INTO metadata_id, metadata_active
|
||||
FROM api.metadata m
|
||||
WHERE m.vessel_id IS NOT NULL AND m.vessel_id = current_setting('vessel.id', true);
|
||||
--RAISE NOTICE 'metadata_id is [%]', metadata_id;
|
||||
IF metadata_id IS NOT NULL THEN
|
||||
-- send notification if boat is back online
|
||||
IF metadata_active is False THEN
|
||||
-- Add monitor online entry to process queue for later notification
|
||||
INSERT INTO process_queue (channel, payload, stored, ref_id)
|
||||
VALUES ('monitoring_online', metadata_id, now(), current_setting('vessel.id', true));
|
||||
END IF;
|
||||
-- Update vessel metadata
|
||||
UPDATE api.metadata
|
||||
SET
|
||||
name = NEW.name,
|
||||
mmsi = NEW.mmsi,
|
||||
--client_id = NEW.client_id,
|
||||
length = NEW.length,
|
||||
beam = NEW.beam,
|
||||
height = NEW.height,
|
||||
ship_type = NEW.ship_type,
|
||||
plugin_version = NEW.plugin_version,
|
||||
signalk_version = NEW.signalk_version,
|
||||
platform = REGEXP_REPLACE(NEW.platform, '[^a-zA-Z0-9\(\) ]', '', 'g'),
|
||||
-- configuration = NEW.configuration, -- ignore configuration from vessel, it is manage by user
|
||||
-- time = NEW.time, ignore the time sent by the vessel as it is out of sync sometimes.
|
||||
time = NOW(), -- overwrite the time sent by the vessel
|
||||
available_keys = NEW.available_keys,
|
||||
active = true
|
||||
WHERE id = metadata_id;
|
||||
RETURN NULL; -- Ignore insert
|
||||
ELSE
|
||||
IF NEW.vessel_id IS NULL THEN
|
||||
-- set vessel_id from jwt if not present in INSERT query
|
||||
NEW.vessel_id := current_setting('vessel.id');
|
||||
END IF;
|
||||
-- Ignore and overwrite the time sent by the vessel
|
||||
NEW.time := NOW();
|
||||
-- Insert new vessel metadata
|
||||
RETURN NEW; -- Insert new vessel metadata
|
||||
END IF;
|
||||
END;
|
||||
$function$
|
||||
;
|
||||
COMMENT ON FUNCTION public.metadata_upsert_trigger_fn() IS 'process metadata from vessel, upsert';
|
||||
|
||||
-- Create or replace the function that will be executed by the trigger
|
||||
CREATE OR REPLACE FUNCTION api.update_metadata_configuration()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- Require Signalk plugin version 0.4.0
|
||||
-- Update the configuration field with current date in ISO format
|
||||
-- Using jsonb_set if configuration is already a JSONB field
|
||||
IF NEW.configuration IS NOT NULL AND
|
||||
jsonb_typeof(NEW.configuration) = 'object' THEN
|
||||
NEW.configuration = jsonb_set(
|
||||
NEW.configuration,
|
||||
'{update_at}',
|
||||
to_jsonb(to_char(NOW(), 'YYYY-MM-DD"T"HH24:MI:SS"Z"'))
|
||||
);
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
COMMENT ON FUNCTION api.update_metadata_configuration() IS 'Update the configuration field with current date in ISO format';
|
||||
|
||||
-- Create the trigger
|
||||
CREATE TRIGGER metadata_update_configuration_trigger
|
||||
BEFORE UPDATE ON api.metadata
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION api.update_metadata_configuration();
|
||||
|
||||
-- Update api.export_logbook_geojson_linestring_trip_fn, add metadata
|
||||
CREATE OR REPLACE FUNCTION api.export_logbooks_geojson_linestring_trips_fn(
|
||||
start_log integer DEFAULT NULL::integer,
|
||||
end_log integer DEFAULT NULL::integer,
|
||||
start_date text DEFAULT NULL::text,
|
||||
end_date text DEFAULT NULL::text,
|
||||
OUT geojson jsonb
|
||||
) RETURNS jsonb
|
||||
LANGUAGE plpgsql
|
||||
AS $function$
|
||||
DECLARE
|
||||
logs_geojson jsonb;
|
||||
BEGIN
|
||||
-- Normalize start and end values
|
||||
IF start_log IS NOT NULL AND end_log IS NULL THEN end_log := start_log; END IF;
|
||||
IF start_date IS NOT NULL AND end_date IS NULL THEN end_date := start_date; END IF;
|
||||
|
||||
WITH logbook_data AS (
|
||||
-- get the logbook geometry and metadata, an array for each log
|
||||
SELECT id, name,
|
||||
starttimestamp(trip),
|
||||
endtimestamp(trip),
|
||||
--speed(trip_sog),
|
||||
duration(trip),
|
||||
--length(trip) as length, -- Meters
|
||||
(length(trip) * 0.0005399568)::numeric as distance, -- NM
|
||||
twavg(trip_sog) as avg_sog,
|
||||
maxValue(trip_sog) as max_sog,
|
||||
maxValue(trip_depth) as max_depth, -- Depth
|
||||
maxValue(trip_batt_charge) as max_batt_charge, -- Battery Charge
|
||||
maxValue(trip_batt_voltage) as max_batt_voltage, -- Battery Voltage
|
||||
maxValue(trip_temp_water) as max_temp_water, -- Temperature water
|
||||
maxValue(trip_temp_out) as max_temp_out, -- Temperature outside
|
||||
maxValue(trip_pres_out) as max_pres_out, -- Pressure outside
|
||||
maxValue(trip_hum_out) as max_hum_out, -- Humidity outside
|
||||
twavg(trip_depth) as avg_depth, -- Depth
|
||||
twavg(trip_batt_charge) as avg_batt_charge, -- Battery Charge
|
||||
twavg(trip_batt_voltage) as avg_batt_voltage, -- Battery Voltage
|
||||
twavg(trip_temp_water) as avg_temp_water, -- Temperature water
|
||||
twavg(trip_temp_out) as avg_temp_out, -- Temperature outside
|
||||
twavg(trip_pres_out) as avg_pres_out, -- Pressure outside
|
||||
twavg(trip_hum_out) as avg_hum_out, -- Humidity outside
|
||||
trajectory(l.trip)::geometry as track_geog -- extract trip to geography
|
||||
FROM api.logbook l
|
||||
WHERE (start_log IS NULL OR l.id >= start_log) AND
|
||||
(end_log IS NULL OR l.id <= end_log) AND
|
||||
(start_date IS NULL OR l._from_time >= start_date::TIMESTAMPTZ) AND
|
||||
(end_date IS NULL OR l._to_time <= end_date::TIMESTAMPTZ + interval '23 hours 59 minutes') AND
|
||||
l.trip IS NOT NULL
|
||||
ORDER BY l._from_time ASC
|
||||
),
|
||||
collect as (
|
||||
SELECT ST_Collect(
|
||||
ARRAY(
|
||||
SELECT track_geog FROM logbook_data))
|
||||
)
|
||||
-- Create the GeoJSON response
|
||||
SELECT jsonb_build_object(
|
||||
'type', 'FeatureCollection',
|
||||
'features', json_agg(ST_AsGeoJSON(logs.*)::json)) INTO geojson FROM logbook_data logs;
|
||||
END;
|
||||
$function$;
|
||||
-- Description
|
||||
COMMENT ON FUNCTION api.export_logbooks_geojson_linestring_trips_fn IS 'Generate geojson geometry LineString from trip with the corresponding properties';
|
||||
|
||||
-- Add public.get_season, return the season based on the input date for logbook tag
|
||||
CREATE OR REPLACE FUNCTION public.get_season(input_date TIMESTAMPTZ)
|
||||
RETURNS TEXT AS $$
|
||||
BEGIN
|
||||
CASE
|
||||
WHEN (EXTRACT(MONTH FROM input_date) = 3 AND EXTRACT(DAY FROM input_date) >= 1) OR
|
||||
(EXTRACT(MONTH FROM input_date) BETWEEN 4 AND 5) THEN
|
||||
RETURN 'Spring';
|
||||
WHEN (EXTRACT(MONTH FROM input_date) = 6 AND EXTRACT(DAY FROM input_date) >= 1) OR
|
||||
(EXTRACT(MONTH FROM input_date) BETWEEN 7 AND 8) THEN
|
||||
RETURN 'Summer';
|
||||
WHEN (EXTRACT(MONTH FROM input_date) = 9 AND EXTRACT(DAY FROM input_date) >= 1) OR
|
||||
(EXTRACT(MONTH FROM input_date) BETWEEN 10 AND 11) THEN
|
||||
RETURN 'Fall';
|
||||
ELSE
|
||||
RETURN 'Winter';
|
||||
END CASE;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql IMMUTABLE;
|
||||
|
||||
-- Refresh permissions
|
||||
GRANT SELECT ON TABLE api.metrics,api.metadata TO scheduler;
|
||||
GRANT INSERT, UPDATE, SELECT ON TABLE api.logbook,api.moorages,api.stays TO scheduler;
|
||||
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO scheduler;
|
||||
GRANT SELECT ON ALL TABLES IN SCHEMA public TO scheduler;
|
||||
GRANT SELECT, UPDATE ON TABLE public.process_queue TO scheduler;
|
||||
|
||||
-- Update version
|
||||
UPDATE public.app_settings
|
||||
SET value='0.9.0'
|
||||
WHERE "name"='app.version';
|
@@ -1 +1 @@
|
||||
0.8.1
|
||||
0.9.0
|
||||
|
File diff suppressed because one or more lines are too long
@@ -27,6 +27,7 @@ const metrics_aava = require('./metrics_sample_aava.json');
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
let configtime = new Date().toISOString();
|
||||
|
||||
// CNAMEs Array
|
||||
[
|
||||
@@ -39,7 +40,7 @@ const fs = require('fs');
|
||||
vessel_metadata: {
|
||||
name: "kapla",
|
||||
mmsi: "123456789",
|
||||
client_id: "vessels.urn:mrn:signalk:uuid:5b4f7543-7153-4840-b139-761310b242fd",
|
||||
//client_id: "vessels.urn:mrn:signalk:uuid:5b4f7543-7153-4840-b139-761310b242fd",
|
||||
length: "12",
|
||||
beam: "10",
|
||||
height: "24",
|
||||
@@ -59,8 +60,8 @@ const fs = require('fs');
|
||||
user_views: [
|
||||
// not processed yet, { url: '/stays_view', res_body_length: 1},
|
||||
// not processed yet, { url: '/moorages_view', res_body_length: 1},
|
||||
{ url: '/logs_view', res_body_length: 0},
|
||||
{ url: '/log_view', res_body_length: 2},
|
||||
{ url: '/logs_view', res_body_length: 0}, // not processed yet so empty
|
||||
{ url: '/log_view', res_body_length: 0}, // not processed yet so empty
|
||||
//{ url: '/stats_view', res_body_length: 1},
|
||||
{ url: '/vessels_view', res_body_length: 1},
|
||||
],
|
||||
@@ -177,6 +178,18 @@ const fs = require('fs');
|
||||
obj_name: 'settings'
|
||||
}
|
||||
}
|
||||
],
|
||||
config_fn: [
|
||||
{ url: '/metadata?select=configuration',
|
||||
res: {
|
||||
obj_name: 'configuration'
|
||||
}
|
||||
},
|
||||
{ url: `/metadata?select=configuration&configuration->>update_at=gt.${configtime}`,
|
||||
res: {
|
||||
obj_name: 'settings'
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{ cname: process.env.PGSAIL_API_URI, name: "PostgSail unit test, aava",
|
||||
@@ -187,7 +200,7 @@ const fs = require('fs');
|
||||
vessel_metadata: {
|
||||
name: "aava",
|
||||
mmsi: "787654321",
|
||||
client_id: "vessels.urn:mrn:imo:mmsi:787654321",
|
||||
//client_id: "vessels.urn:mrn:imo:mmsi:787654321",
|
||||
length: "12",
|
||||
beam: "10",
|
||||
height: "24",
|
||||
@@ -206,8 +219,8 @@ const fs = require('fs');
|
||||
user_views: [
|
||||
// not processed yet, { url: '/stays_view', res_body_length: 1},
|
||||
// not processed yet, { url: '/moorages_view', res_body_length: 1},
|
||||
{ url: '/logs_view', res_body_length: 0},
|
||||
{ url: '/log_view', res_body_length: 1},
|
||||
{ url: '/logs_view', res_body_length: 0}, // not processed yet so empty
|
||||
{ url: '/log_view', res_body_length: 0}, // not processed yet so empty
|
||||
//{ url: '/stats_view', res_body_length: 1},
|
||||
{ url: '/vessels_view', res_body_length: 1},
|
||||
],
|
||||
@@ -318,6 +331,18 @@ const fs = require('fs');
|
||||
obj_name: 'settings'
|
||||
}
|
||||
},
|
||||
],
|
||||
config_fn: [
|
||||
{ url: '/metadata?select=configuration',
|
||||
res: {
|
||||
obj_name: 'configuration'
|
||||
}
|
||||
},
|
||||
{ url: `/metadata?select=configuration&configuration->>update_at=gt.${configtime}`,
|
||||
res: {
|
||||
obj_name: 'settings'
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
].forEach( function(test){
|
||||
@@ -843,6 +868,37 @@ request.set('User-Agent', 'PostgSail unit tests');
|
||||
}); // Function OTP endpoint
|
||||
*/
|
||||
|
||||
describe("Function Metadata configuration endpoint, JWT vessel_role", function(){
|
||||
|
||||
let otp = null;
|
||||
test.config_fn.forEach(function (subtest) {
|
||||
it(`${subtest.url}`, function(done) {
|
||||
try {
|
||||
//console.log(`${subtest.url} ${subtest.res}`);
|
||||
// Reset agent so we do not save cookies
|
||||
request = supertest.agent(test.cname);
|
||||
request
|
||||
.get(subtest.url)
|
||||
.set('Authorization', `Bearer ${vessel_jwt}`)
|
||||
.set('Accept', 'application/json')
|
||||
.end(function(err,res){
|
||||
res.status.should.equal(200);
|
||||
should.exist(res.header['content-type']);
|
||||
should.exist(res.header['server']);
|
||||
res.header['content-type'].should.match(new RegExp('json','g'));
|
||||
res.header['server'].should.match(new RegExp('postgrest','g'));
|
||||
console.log(res.body);
|
||||
should.exist(res.body);
|
||||
done(err);
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
}); // Function metadata configuration endpoint
|
||||
|
||||
}); // OpenAPI description
|
||||
|
||||
}); // CNAMEs Array
|
||||
|
@@ -28,14 +28,15 @@ const metrics_simulator = require('./metrics_sample_simulator.json');
|
||||
vessel_metadata: {
|
||||
name: "aava",
|
||||
mmsi: "787654321",
|
||||
client_id: "vessels.urn:mrn:imo:mmsi:787654321",
|
||||
//client_id: "vessels.urn:mrn:imo:mmsi:787654321",
|
||||
length: "12",
|
||||
beam: "10",
|
||||
height: "24",
|
||||
ship_type: "37",
|
||||
plugin_version: "1.0.2",
|
||||
signalk_version: "1.20.0",
|
||||
time: moment().subtract(69, 'minutes').format()
|
||||
time: moment().subtract(69, 'minutes').format(),
|
||||
available_keys: [],
|
||||
},
|
||||
vessel_metrics: metrics_simulator,
|
||||
user_tables: [
|
||||
@@ -48,7 +49,7 @@ const metrics_simulator = require('./metrics_sample_simulator.json');
|
||||
// not processed yet, { url: '/stays_view', res_body_length: 1},
|
||||
// not processed yet, { url: '/moorages_view', res_body_length: 1},
|
||||
{ url: '/logs_view', res_body_length: 1},
|
||||
{ url: '/log_view', res_body_length: 2},
|
||||
{ url: '/log_view', res_body_length: 0}, // not processed yet so empty
|
||||
//{ url: '/stats_view', res_body_length: 1},
|
||||
{ url: '/vessels_view', res_body_length: 1},
|
||||
],
|
||||
@@ -422,8 +423,9 @@ request.set('User-Agent', 'PostgSail unit tests');
|
||||
.set('Content-Type', 'application/json')
|
||||
.set('Prefer', 'return=headers-only')
|
||||
.end(function(err,res){
|
||||
res.status.should.equal(201);
|
||||
//console.log(res.body);
|
||||
//console.log(res.header);
|
||||
res.status.should.equal(201);
|
||||
should.exist(res.header['server']);
|
||||
res.header['server'].should.match(new RegExp('postgrest','g'));
|
||||
done(err);
|
||||
|
@@ -31,7 +31,7 @@ var moment = require('moment');
|
||||
vessel_metadata: {
|
||||
name: "kapla",
|
||||
mmsi: "123456789",
|
||||
client_id: "vessels.urn:mrn:imo:mmsi:123456789",
|
||||
//client_id: "vessels.urn:mrn:imo:mmsi:123456789",
|
||||
length: "12",
|
||||
beam: "10",
|
||||
height: "24",
|
||||
@@ -249,7 +249,7 @@ var moment = require('moment');
|
||||
vessel_metadata: {
|
||||
name: "aava",
|
||||
mmsi: "787654321",
|
||||
client_id: "vessels.urn:mrn:imo:mmsi:787654321",
|
||||
//client_id: "vessels.urn:mrn:imo:mmsi:787654321",
|
||||
length: "12",
|
||||
beam: "10",
|
||||
height: "24",
|
||||
|
@@ -71,7 +71,7 @@ count | 11
|
||||
stats_logs_fn
|
||||
SELECT 1
|
||||
-[ RECORD 1 ]+----------
|
||||
name | "kapla"
|
||||
name | "aava"
|
||||
count | 4
|
||||
max_speed | 9.5
|
||||
max_distance | 68.8677
|
||||
|
@@ -20,7 +20,7 @@ SELECT current_user, current_setting('user.email', true), current_setting('vesse
|
||||
--SELECT a.pass,v.name,m.client_id FROM auth.accounts a JOIN auth.vessels v ON a.email = 'demo+kapla@openplotter.cloud' AND a.role = 'user_role' AND cast(a.preferences->>'email_valid' as Boolean) = True AND v.owner_email = a.email JOIN api.metadata m ON m.vessel_id = v.vessel_id;
|
||||
--SELECT a.pass,v.name,m.client_id FROM auth.accounts a JOIN auth.vessels v ON a.email = 'demo+kapla@openplotter.cloud' AND a.role = 'user_role' AND v.owner_email = a.email JOIN api.metadata m ON m.vessel_id = v.vessel_id;
|
||||
\echo 'link vessel and user based on current_setting'
|
||||
SELECT v.name,m.client_id FROM auth.accounts a JOIN auth.vessels v ON a.role = 'user_role' AND v.owner_email = a.email JOIN api.metadata m ON m.vessel_id = v.vessel_id ORDER BY a.id DESC;
|
||||
SELECT v.name, m.vessel_id IS NOT NULL AS vessel_id FROM auth.accounts a JOIN auth.vessels v ON a.role = 'user_role' AND v.owner_email = a.email JOIN api.metadata m ON m.vessel_id = v.vessel_id ORDER BY a.id DESC;
|
||||
|
||||
\echo 'auth.accounts details'
|
||||
SELECT a.user_id IS NOT NULL AS user_id, a.email, a.first, a.last, a.pass IS NOT NULL AS pass, a.role, a.preferences->'telegram'->'chat' AS telegram, a.preferences->'pushover_user_key' AS pushover_user_key FROM auth.accounts AS a ORDER BY a.id DESC;
|
||||
@@ -29,7 +29,7 @@ SELECT a.user_id IS NOT NULL AS user_id, a.email, a.first, a.last, a.pass IS NOT
|
||||
SELECT v.vessel_id IS NOT NULL AS vessel_id, v.owner_email, v.mmsi, v.name, v.role FROM auth.vessels AS v;
|
||||
\echo 'api.metadata details'
|
||||
--
|
||||
SELECT m.id, m.name, m.mmsi, m.client_id, m.length, m.beam, m.height, m.ship_type, m.plugin_version, m.signalk_version, m.time IS NOT NULL AS time, m.active FROM api.metadata AS m;
|
||||
SELECT m.id, m.name, m.mmsi, m.length, m.beam, m.height, m.ship_type, m.plugin_version, m.signalk_version, m.time IS NOT NULL AS time, m.active, configuration IS NOT NULL AS configuration, available_keys FROM api.metadata AS m ORDER BY m.name DESC;
|
||||
|
||||
--
|
||||
-- grafana
|
||||
@@ -48,14 +48,14 @@ SELECT set_config('vessel.id', :'vessel_id', false) IS NOT NULL as vessel_id;
|
||||
--SELECT current_user, current_setting('user.email', true), current_setting('vessel.client_id', true), current_setting('vessel.id', true);
|
||||
SELECT current_user, current_setting('user.email', true);
|
||||
|
||||
SELECT v.name AS __text, m.client_id AS __value FROM auth.vessels v JOIN api.metadata m ON v.owner_email = 'demo+kapla@openplotter.cloud' and m.vessel_id = v.vessel_id;
|
||||
SELECT v.name AS __text, m.vessel_id IS NOT NULL AS __value FROM auth.vessels v JOIN api.metadata m ON v.owner_email = 'demo+kapla@openplotter.cloud' and m.vessel_id = v.vessel_id;
|
||||
|
||||
\echo 'auth.vessels details'
|
||||
--SELECT * FROM auth.vessels v;
|
||||
SELECT v.vessel_id IS NOT NULL AS vessel_id, v.owner_email, v.mmsi, v.name, v.role FROM auth.vessels AS v;
|
||||
--SELECT * FROM api.metadata m;
|
||||
\echo 'api.metadata details'
|
||||
SELECT m.id, m.name, m.mmsi, m.client_id, m.length, m.beam, m.height, m.ship_type, m.plugin_version, m.signalk_version, m.time IS NOT NULL AS time, m.active FROM api.metadata AS m;
|
||||
SELECT m.id, m.name, m.mmsi, m.length, m.beam, m.height, m.ship_type, m.plugin_version, m.signalk_version, m.time IS NOT NULL AS time, m.active, configuration IS NOT NULL AS configuration, available_keys FROM api.metadata AS m;
|
||||
|
||||
\echo 'api.logs_view'
|
||||
--SELECT * FROM api.logbook l;
|
||||
|
@@ -13,12 +13,12 @@ current_setting |
|
||||
current_setting |
|
||||
|
||||
link vessel and user based on current_setting
|
||||
-[ RECORD 1 ]----------------------------------------------------------------
|
||||
-[ RECORD 1 ]----
|
||||
name | aava
|
||||
client_id | vessels.urn:mrn:imo:mmsi:787654321
|
||||
-[ RECORD 2 ]----------------------------------------------------------------
|
||||
vessel_id | t
|
||||
-[ RECORD 2 ]----
|
||||
name | kapla
|
||||
client_id | vessels.urn:mrn:signalk:uuid:5b4f7543-7153-4840-b139-761310b242fd
|
||||
vessel_id | t
|
||||
|
||||
auth.accounts details
|
||||
-[ RECORD 1 ]-----+-----------------------------
|
||||
@@ -55,11 +55,10 @@ name | aava
|
||||
role | vessel_role
|
||||
|
||||
api.metadata details
|
||||
-[ RECORD 1 ]---+------------------------------------------------------------------
|
||||
-[ RECORD 1 ]---+----------------
|
||||
id | 1
|
||||
name | kapla
|
||||
mmsi | 123456789
|
||||
client_id | vessels.urn:mrn:signalk:uuid:5b4f7543-7153-4840-b139-761310b242fd
|
||||
length | 12
|
||||
beam | 10
|
||||
height | 24
|
||||
@@ -68,11 +67,12 @@ plugin_version | 0.0.1
|
||||
signalk_version | signalk_version
|
||||
time | t
|
||||
active | t
|
||||
-[ RECORD 2 ]---+------------------------------------------------------------------
|
||||
configuration | t
|
||||
available_keys |
|
||||
-[ RECORD 2 ]---+----------------
|
||||
id | 2
|
||||
name | aava
|
||||
mmsi | 787654321
|
||||
client_id | vessels.urn:mrn:imo:mmsi:787654321
|
||||
length | 12
|
||||
beam | 10
|
||||
height | 24
|
||||
@@ -81,6 +81,8 @@ plugin_version | 1.0.2
|
||||
signalk_version | 1.20.0
|
||||
time | t
|
||||
active | t
|
||||
configuration | f
|
||||
available_keys | []
|
||||
|
||||
SET
|
||||
ROLE grafana current_setting
|
||||
@@ -93,9 +95,9 @@ vessel_id | t
|
||||
current_user | grafana
|
||||
current_setting | demo+kapla@openplotter.cloud
|
||||
|
||||
-[ RECORD 1 ]--------------------------------------------------------------
|
||||
-[ RECORD 1 ]--
|
||||
__text | kapla
|
||||
__value | vessels.urn:mrn:signalk:uuid:5b4f7543-7153-4840-b139-761310b242fd
|
||||
__value | t
|
||||
|
||||
auth.vessels details
|
||||
-[ RECORD 1 ]-----------------------------
|
||||
@@ -106,11 +108,10 @@ name | kapla
|
||||
role | vessel_role
|
||||
|
||||
api.metadata details
|
||||
-[ RECORD 1 ]---+------------------------------------------------------------------
|
||||
-[ RECORD 1 ]---+----------------
|
||||
id | 1
|
||||
name | kapla
|
||||
mmsi | 123456789
|
||||
client_id | vessels.urn:mrn:signalk:uuid:5b4f7543-7153-4840-b139-761310b242fd
|
||||
length | 12
|
||||
beam | 10
|
||||
height | 24
|
||||
@@ -119,6 +120,8 @@ plugin_version | 0.0.1
|
||||
signalk_version | signalk_version
|
||||
time | t
|
||||
active | t
|
||||
configuration | t
|
||||
available_keys |
|
||||
|
||||
api.logs_view
|
||||
-[ RECORD 1 ]----+-----------------------
|
||||
|
32
tests/sql/metadata.sql
Normal file
32
tests/sql/metadata.sql
Normal file
@@ -0,0 +1,32 @@
|
||||
---------------------------------------------------------------------------
|
||||
-- Listing
|
||||
--
|
||||
|
||||
-- List current database
|
||||
select current_database();
|
||||
|
||||
-- connect to the DB
|
||||
\c signalk
|
||||
|
||||
-- output display format
|
||||
\x on
|
||||
|
||||
SELECT v.vessel_id as "vessel_id" FROM auth.vessels v WHERE v.owner_email = 'demo+kapla@openplotter.cloud' \gset
|
||||
--\echo :"vessel_id"
|
||||
SELECT set_config('vessel.id', :'vessel_id', false) IS NOT NULL as vessel_id;
|
||||
|
||||
--SELECT * FROM api.metadata m;
|
||||
\echo 'api.metadata details'
|
||||
SELECT m.id, m.name, m.mmsi, m.length, m.beam, m.height, m.ship_type, m.plugin_version, m.signalk_version, m.time IS NOT NULL AS time, m.active, configuration, available_keys FROM api.metadata AS m ORDER BY m.name ASC;
|
||||
|
||||
\echo 'api.metadata get configuration'
|
||||
select configuration from api.metadata WHERE vessel_id = current_setting('vessel.id', false);
|
||||
|
||||
\echo 'api.metadata update configuration'
|
||||
UPDATE api.metadata SET configuration = '{ "depthKey": "environment.depth.belowTransducer" }' WHERE vessel_id = current_setting('vessel.id', false);
|
||||
|
||||
\echo 'api.metadata get configuration with new value'
|
||||
select configuration->'depthKey' AS depthKey, configuration->'update_at' IS NOT NULL AS update_at from api.metadata WHERE vessel_id = current_setting('vessel.id', false);
|
||||
|
||||
\echo 'api.metadata get configuration base on update_at value'
|
||||
select configuration->'depthKey' AS depthKey, configuration->'update_at' IS NOT NULL AS update_at from api.metadata WHERE vessel_id = current_setting('vessel.id', false) AND configuration->>'update_at' = to_char(NOW(), 'YYYY-MM-DD"T"HH24:MI:SS"Z"');
|
56
tests/sql/metadata.sql.output
Normal file
56
tests/sql/metadata.sql.output
Normal file
@@ -0,0 +1,56 @@
|
||||
current_database
|
||||
------------------
|
||||
signalk
|
||||
(1 row)
|
||||
|
||||
You are now connected to database "signalk" as user "username".
|
||||
Expanded display is on.
|
||||
-[ RECORD 1 ]
|
||||
vessel_id | t
|
||||
|
||||
api.metadata details
|
||||
-[ RECORD 1 ]---+----------------
|
||||
id | 2
|
||||
name | aava
|
||||
mmsi | 787654321
|
||||
length | 12
|
||||
beam | 10
|
||||
height | 24
|
||||
ship_type | 37
|
||||
plugin_version | 1.0.2
|
||||
signalk_version | 1.20.0
|
||||
time | t
|
||||
active | t
|
||||
configuration |
|
||||
available_keys | []
|
||||
-[ RECORD 2 ]---+----------------
|
||||
id | 1
|
||||
name | kapla
|
||||
mmsi | 123456789
|
||||
length | 12
|
||||
beam | 10
|
||||
height | 24
|
||||
ship_type | 36
|
||||
plugin_version | 0.0.1
|
||||
signalk_version | signalk_version
|
||||
time | t
|
||||
active | t
|
||||
configuration |
|
||||
available_keys |
|
||||
|
||||
api.metadata get configuration
|
||||
-[ RECORD 1 ]-+-
|
||||
configuration |
|
||||
|
||||
api.metadata update configuration
|
||||
UPDATE 1
|
||||
api.metadata get configuration with new value
|
||||
-[ RECORD 1 ]----------------------------------
|
||||
depthkey | "environment.depth.belowTransducer"
|
||||
update_at | t
|
||||
|
||||
api.metadata get configuration base on update_at value
|
||||
-[ RECORD 1 ]----------------------------------
|
||||
depthkey | "environment.depth.belowTransducer"
|
||||
update_at | t
|
||||
|
@@ -67,7 +67,21 @@ SELECT set_config('vessel.id', :'vessel_id_kapla', false) IS NOT NULL as vessel_
|
||||
|
||||
-- Export timelapse as Geometry LineString from a trip
|
||||
\echo 'Export timelapse as Geometry LineString from a trip'
|
||||
SELECT api.export_logbooks_geojson_linestring_trips_fn(1,2) FROM api.logbook WHERE vessel_id = current_setting('vessel.id', false);
|
||||
--SELECT api.export_logbooks_geojson_linestring_trips_fn(1,2) FROM api.logbook WHERE vessel_id = current_setting('vessel.id', false);
|
||||
-- Test geometry_type and num_properties
|
||||
-- propoerties include endtimestamp and starttimestamp
|
||||
WITH geojson_output AS (
|
||||
SELECT api.export_logbooks_geojson_linestring_trips_fn(1, 2) AS geojson
|
||||
FROM api.logbook
|
||||
WHERE vessel_id = current_setting('vessel.id', false)
|
||||
)
|
||||
SELECT
|
||||
--geojson
|
||||
geojson->'features'->0->'geometry'->>'type' AS geometry_type,
|
||||
--jsonb_array_length(jsonb_object_keys(geojson->'features'->0->'properties')::JSONB),
|
||||
--jsonb_array_length(jsonb_object_keys(geojson->'features')) AS num_geometry,
|
||||
(SELECT COUNT(*) FROM jsonb_object_keys(geojson->'features'->0->'properties')) AS num_properties
|
||||
FROM geojson_output;
|
||||
|
||||
-- Export timelapse as Geometry Point from a trip
|
||||
\echo 'Export timelapse as Geometry Point from a trip'
|
||||
|
@@ -52,8 +52,9 @@ Export KML from a trip
|
||||
vessel_id | t
|
||||
|
||||
Export timelapse as Geometry LineString from a trip
|
||||
-[ RECORD 1 ]-------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
export_logbooks_geojson_linestring_trips_fn | {"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {"type": "LineString", "coordinates": [[23.530866667, 60.077666667], [23.52355, 60.07065], [23.515866667, 60.0637], [23.507866667, 60.056716667], [23.500533333, 60.04915], [23.493, 60.041633333], [23.485466667, 60.033983333], [23.479033333, 60.026216667], [23.47295, 60.01835], [23.461033333, 60.003516667], [23.45415, 59.99755], [23.445683333, 59.99235], [23.438766667, 59.989266667], [23.435116667, 59.987866667], [23.43165, 59.986333333], [23.4292, 59.984833333], [23.432566667, 59.9862], [23.43375, 59.987266667], [23.431566667, 59.98615], [23.4307, 59.98565], [23.429383333, 59.984683333], [23.421066667, 59.978233333], [23.431, 59.977716667], [23.432133333, 59.976883333], [23.4321, 59.976883333], [23.425533333, 59.9781], [23.41165, 59.9738], [23.401383333, 59.967], [23.395816667, 59.958866667], [23.390166667, 59.9508], [23.38365, 59.94275], [23.37245, 59.935783333], [23.3572, 59.930766667], [23.33415, 59.918933333], [23.327433333, 59.9114], [23.329966667, 59.90315], [23.3428, 59.89735], [23.3492, 59.889533333], [23.345833333, 59.881266667], [23.344066667, 59.872783333], [23.355716667, 59.866616667], [23.365766667, 59.86]]}, "properties": {}}]}
|
||||
-[ RECORD 1 ]--+-----------
|
||||
geometry_type | LineString
|
||||
num_properties | 22
|
||||
|
||||
Export timelapse as Geometry Point from a trip
|
||||
-[ RECORD 1 ]
|
||||
|
@@ -6,10 +6,10 @@
|
||||
You are now connected to database "signalk" as user "username".
|
||||
Expanded display is on.
|
||||
-[ RECORD 1 ]--+-------------------------------
|
||||
server_version | 16.6 (Debian 16.6-1.pgdg120+1)
|
||||
server_version | 16.8 (Debian 16.8-1.pgdg120+1)
|
||||
|
||||
-[ RECORD 1 ]--------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
postgis_full_version | POSTGIS="3.5.1 48ab069" [EXTENSION] PGSQL="160" GEOS="3.11.1-CAPI-1.17.1" PROJ="9.1.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" (compiled against PROJ 9.1.1) LIBXML="2.9.14" LIBJSON="0.16" LIBPROTOBUF="1.4.1" WAGYU="0.5.0 (Internal)"
|
||||
postgis_full_version | POSTGIS="3.5.2 dea6d0a" [EXTENSION] PGSQL="160" GEOS="3.11.1-CAPI-1.17.1" PROJ="9.1.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" (compiled against PROJ 9.1.1) LIBXML="2.9.14" LIBJSON="0.16" LIBPROTOBUF="1.4.1" WAGYU="0.5.0 (Internal)"
|
||||
|
||||
-[ RECORD 1 ]--------------------------------------------------------------------------------------
|
||||
Name | citext
|
||||
@@ -53,12 +53,12 @@ Schema | pg_catalog
|
||||
Description | PL/Python3U untrusted procedural language
|
||||
-[ RECORD 9 ]--------------------------------------------------------------------------------------
|
||||
Name | postgis
|
||||
Version | 3.5.1
|
||||
Version | 3.5.2
|
||||
Schema | public
|
||||
Description | PostGIS geometry and geography spatial types and functions
|
||||
-[ RECORD 10 ]-------------------------------------------------------------------------------------
|
||||
Name | timescaledb
|
||||
Version | 2.17.2
|
||||
Version | 2.19.0
|
||||
Schema | public
|
||||
Description | Enables scalable inserts and complex queries for time-series data (Community Edition)
|
||||
-[ RECORD 11 ]-------------------------------------------------------------------------------------
|
||||
@@ -111,14 +111,14 @@ laninline | 13566
|
||||
lanvalidator | 13567
|
||||
lanacl |
|
||||
-[ RECORD 5 ]-+-----------
|
||||
oid | 18190
|
||||
oid | 18225
|
||||
lanname | plpython3u
|
||||
lanowner | 10
|
||||
lanispl | t
|
||||
lanpltrusted | t
|
||||
lanplcallfoid | 18187
|
||||
laninline | 18188
|
||||
lanvalidator | 18189
|
||||
lanplcallfoid | 18222
|
||||
laninline | 18223
|
||||
lanvalidator | 18224
|
||||
lanacl |
|
||||
|
||||
-[ RECORD 1 ]+-----------
|
||||
@@ -675,12 +675,12 @@ overpass_py_fn | {"name": "Port de la Ginesta", "type": "multipolygon", "leisure
|
||||
overpass_py_fn | {"name": "Norra hamnen", "leisure": "marina"}
|
||||
|
||||
-[ RECORD 1 ]----------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
versions_fn | {"api_version" : "0.8.1", "sys_version" : "PostgreSQL 16.6", "mobilitydb" : "1.2.0", "timescaledb" : "2.17.2", "postgis" : "3.5.1", "postgrest" : "PostgREST 12.2.3"}
|
||||
versions_fn | {"api_version" : "0.9.0", "sys_version" : "PostgreSQL 16.8", "mobilitydb" : "1.2.0", "timescaledb" : "2.19.0", "postgis" : "3.5.2", "postgrest" : "PostgREST 12.2.8"}
|
||||
|
||||
-[ RECORD 1 ]-----------------
|
||||
api_version | 0.8.1
|
||||
sys_version | PostgreSQL 16.6
|
||||
timescaledb | 2.17.2
|
||||
postgis | 3.5.1
|
||||
postgrest | PostgREST 12.2.3
|
||||
api_version | 0.9.0
|
||||
sys_version | PostgreSQL 16.8
|
||||
timescaledb | 2.19.0
|
||||
postgis | 3.5.2
|
||||
postgrest | PostgREST 12.2.8
|
||||
|
||||
|
@@ -49,6 +49,19 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# metadata and vessel configuration unit tests
|
||||
psql ${PGSAIL_DB_URI} < sql/metadata.sql > output/metadata.sql.output
|
||||
diff sql/metadata.sql.output output/metadata.sql.output > /dev/null
|
||||
#diff -u sql/metadata.sql.output output/metadata.sql.output | wc -l
|
||||
#echo 0
|
||||
if [ $? -eq 0 ]; then
|
||||
echo OK
|
||||
else
|
||||
echo SQL metadata.sql FAILED
|
||||
diff -u sql/metadata.sql.output output/metadata.sql.output
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# https://www.postgresql.org/docs/current/app-psql.html
|
||||
# run cron jobs
|
||||
#psql -U ${POSTGRES_USER} -h 172.30.0.1 signalk < sql/cron_run_jobs.sql > output/cron_run_jobs.sql.output
|
||||
|
Reference in New Issue
Block a user