-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathflags.sql
244 lines (215 loc) · 10.2 KB
/
flags.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
-- flagging is a way that we can provide more detail about the data we collect
-- flagging data could be imported automatically via ingestion or
-- from user audit methods
-- suggesting the type instead of the table for a few reasons
-- 1) I dont expect these to ever change
-- 2) dont need descriptions and and could just use the type directly instead of having a `invalidates` field
-- 3) I want to create special functions for each type and dont want to depend on a flag_levels_id
CREATE TYPE flag_level AS ENUM(
'INFO'
, 'WARNING'
, 'ERROR'
);
-- info, warning, error
CREATE SEQUENCE IF NOT EXISTS flag_types_sq START 10;
CREATE TABLE IF NOT EXISTS flag_types (
flag_types_id int PRIMARY KEY DEFAULT nextval('flag_types_sq')
, flag_level flag_level NOT NULL
, label text NOT NULL
, description text
, ingest_id text NOT NULL UNIQUE
);
INSERT INTO flag_types (flag_types_id, flag_level, label, description, ingest_id) VALUES
(1, 'INFO', 'Read me', 'important information to pass on to the user', 'info')
, (2, 'WARNING', 'Settings updated', 'Suggests to the user that the data may have issues', 'settings_changed')
, (3, 'ERROR', 'Error', 'Unknown error', 'error')
, (4, 'ERROR', 'Limits exceeded', 'The lower or upper limits of the instrument were exceeded', 'exceedance')
ON CONFLICT (flag_types_id) DO UPDATE
SET label = EXCLUDED.label
, flag_level = EXCLUDED.flag_level
, description = EXCLUDED.description
, ingest_id = EXCLUDED.ingest_id;
-- Measurements will be flagged at the station level
-- with the option to specifiy which sensors the flag affects
-- Other options are to do the flagging at the sensor level but that could
-- be laborious for errors like `power out` and others that affect everything
-- Or we could just do the station level but that has issues when we want to
-- invalidate just one part of the station (temp but not wind speed) and
-- leave the rest of the measurements intact
-- as for storing the parameters we could do the array method, which is easy ui
-- but lacks constraints/checks
-- or the child table which has checks and constraints but more involved ui
-- choosing array with a trigger to check the parameter ids
CREATE SEQUENCE IF NOT EXISTS flags_sq START 10;
CREATE TABLE IF NOT EXISTS flags (
flags_id int PRIMARY KEY DEFAULT nextval('flags_sq')
, flag_types_id int NOT NULL REFERENCES flag_types
, sensor_nodes_id int NOT NULL REFERENCES sensor_nodes
, period tstzrange NOT NULL
, sensors_ids int[] --NOT NULL DEFAULT '{}'::int[]
, note text
, added_on timestamptz DEFAULT now()
, modified_on timestamptz
);
CREATE INDEX flags_period_idx ON flags USING GiST (period);
-- CREATE OR REPLACE FUNCTION check_flags() RETURNS TRIGGER AS $$
-- DECLARE
-- measurands_check boolean;
-- BEGIN
-- IF NEW.measurands_id IS NOT NULL THEN
-- -- -- check against current ids
-- SELECT NEW.measurands_id <@ array_agg(measurands_id)
-- INTO measurands_check
-- FROM measurands;
-- IF NOT measurands_check THEN
-- RAISE EXCEPTION 'Measurand not found';
-- END IF;
-- END IF;
-- RETURN NEW;
-- END;
-- $$ LANGUAGE plpgsql;
-- DROP TRIGGER IF EXISTS check_flags_tgr ON flags;
-- CREATE TRIGGER check_flags_tgr
-- BEFORE INSERT OR UPDATE ON flags
-- FOR EACH ROW EXECUTE PROCEDURE check_flags();
-- A few functions that will let check if a sensor/node + period has a flag
CREATE OR REPLACE FUNCTION sensor_node_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
WHERE f.sensor_nodes_id = $1
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE s.sensors_id = $1
AND (sensors_ids IS NULL OR ARRAY[$1] @> sensors_ids)
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION flags_exist(int, int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE f.sensor_nodes_id = $1
AND ((sensors_ids IS NULL AND $2 IS NULL) OR ARRAY[$2] @> sensors_ids)
AND tstzrange(LEAST($3, $3 + $4), GREATEST($3, $3 + $4), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_node_info_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
WHERE f.sensor_nodes_id = $1
AND ft.flag_level = 'INFO'::flag_level
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_info_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE s.sensors_id = $1
AND ft.flag_level = 'INFO'::flag_level
AND (sensors_ids IS NULL OR ARRAY[$1] @> sensors_ids)
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION info_flags_exist(int, int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE f.sensor_nodes_id = $1
AND ft.flag_level = 'INFO'::flag_level
AND ((sensors_ids IS NULL AND $2 IS NULL) OR ARRAY[$2] @> sensors_ids)
AND tstzrange(LEAST($3, $3 + $4), GREATEST($3, $3 + $4), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_node_warning_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
WHERE f.sensor_nodes_id = $1
AND ft.flag_level = 'WARNING'::flag_level
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_warning_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE s.sensors_id = $1
AND ft.flag_level = 'WARNING'::flag_level
AND (sensors_ids IS NULL OR ARRAY[$1] @> sensors_ids)
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION warning_flags_exist(int, int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE f.sensor_nodes_id = $1
AND ft.flag_level = 'WARNING'::flag_level
AND ((sensors_ids IS NULL AND $2 IS NULL) OR ARRAY[$2] @> sensors_ids)
AND tstzrange(LEAST($3, $3 + $4), GREATEST($3, $3 + $4), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_node_error_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
WHERE f.sensor_nodes_id = $1
AND ft.flag_level = 'ERROR'::flag_level
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION sensor_error_flags_exist(int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE s.sensors_id = $1
AND ft.flag_level = 'ERROR'::flag_level
AND (sensors_ids IS NULL OR ARRAY[$1] @> sensors_ids)
AND tstzrange(LEAST($2, $2 + $3), GREATEST($2, $2 + $3), '[]') && f.period)
$$ LANGUAGE SQL;
CREATE OR REPLACE FUNCTION error_flags_exist(int, int, timestamptz, interval DEFAULT '-1h') RETURNS bool AS $$
SELECT EXISTS(SELECT 1
FROM flags f
JOIN flag_types ft ON (f.flag_types_id = ft.flag_types_id)
JOIN sensor_systems sy ON (f.sensor_nodes_id = sy.sensor_nodes_id)
JOIN sensors s ON (sy.sensor_systems_id = s.sensor_systems_id)
WHERE f.sensor_nodes_id = $1
AND ft.flag_level = 'ERROR'::flag_level
AND ((sensors_ids IS NULL AND $2 IS NULL) OR ARRAY[$2] @> sensors_ids)
AND tstzrange(LEAST($3, $3 + $4), GREATEST($3, $3 + $4), '[]') && f.period)
$$ LANGUAGE SQL;
SELECT t.*
-- Flags exist over hour (defaults to hourly)
, flags_exist(sensor_nodes_id, sensors_id, datetime)
, sensor_node_flags_exist(sensor_nodes_id, datetime)
, sensor_flags_exist(sensors_id, datetime)
-- INFO flags exist
, info_flags_exist(sensor_nodes_id, sensors_id, datetime)
, sensor_node_info_flags_exist(sensor_nodes_id, datetime)
, sensor_info_flags_exist(sensors_id, datetime)
-- WARNNING flags exist
, warning_flags_exist(sensor_nodes_id, sensors_id, datetime)
, sensor_node_warning_flags_exist(sensor_nodes_id, datetime)
, sensor_warning_flags_exist(sensors_id, datetime)
-- ERROR flags exists
, error_flags_exist(sensor_nodes_id, sensors_id, datetime)
, sensor_node_error_flags_exist(sensor_nodes_id, datetime)
, sensor_error_flags_exist(sensors_id, datetime)
-- Daily flags exist
, flags_exist(sensor_nodes_id, sensors_id, datetime, '1d'::interval)
FROM (VALUES
(1, 1, '2024-01-01'::timestamptz)
, (1, NULL, '2024-01-01'::timestamptz)
, (2, NULL, '2024-01-03'::timestamptz)
, (2, 3, '2024-01-03'::timestamptz)
, (2, 4, '2024-01-03'::timestamptz)
)as t(sensor_nodes_id, sensors_id, datetime);