From be5160b1430e12d45eec550184446529b470e7a7 Mon Sep 17 00:00:00 2001 From: "NucBox_EVO-X2\\robert" Date: Sat, 13 Dec 2025 12:21:31 -0800 Subject: [PATCH] Added Custom Demo questions and cleared print statemenent --- console_out_test-jobs.txt | 2392 +++++++++++++ job_available_devices-test.sh | 296 ++ postgress.sql | 5808 ++++++++++++++++++++++++++++++++ test_device_list.sh | 354 ++ test_welldrysense_api.py | 261 ++ test_welldrysense_api.sh | 305 ++ well-api.py | 2329 ++++++++++--- wellDbQuery.py | 545 +++ well_web_files/deployment.html | 6 +- welldrysense_job_db-update.sql | 111 + 10 files changed, 11962 insertions(+), 445 deletions(-) create mode 100644 console_out_test-jobs.txt create mode 100644 job_available_devices-test.sh create mode 100644 postgress.sql create mode 100644 test_device_list.sh create mode 100644 test_welldrysense_api.py create mode 100644 test_welldrysense_api.sh create mode 100644 wellDbQuery.py create mode 100644 welldrysense_job_db-update.sql diff --git a/console_out_test-jobs.txt b/console_out_test-jobs.txt new file mode 100644 index 0000000..c8a8501 --- /dev/null +++ b/console_out_test-jobs.txt @@ -0,0 +1,2392 @@ +=== Setting up WellDrySense Test Suite on Port 1998 === + +---------------------------------------------------------------- +[Test] Login +# Request: +{ + "function": "credentials", + "user_name": "jpeters", + "ps": "WellJson", + "clientId": "bash-suite", + "nonce": "test-nonce" +} + +# Response: +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs", + "privileges": "64", + "user_id": 68, + "max_role": 2, + "status": "200 OK" +} +PASS (User ID: 68) + +---------------------------------------------------------------- +[Test] job_create +# Request: +{ + "function": "job_create", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs", + "customer_name": "TEST_SUITE_CUSTOMER_BASH", + "address_street": "123 Bash Script Ln", + "address_city": "Shellville", + "devices": "[{\"mac\": \"TEST_MAC_VIRTUAL\", \"location\": \"Lab\"}]", + "lat": "34.05", + "lng": "-118.25" +} + +# Response: +{ + "ok": 1, + "job_id": 9, + "status": "200 OK" +} +PASS +-> Captured Job ID: 9 + +---------------------------------------------------------------- +[Test] job_list +# Request: +{ + "function": "job_list", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs" +} + +# Response: +{ + "ok": 1, + "jobs": [ + { + "job_id": 9, + "customer_name": "TEST_SUITE_CUSTOMER_BASH", + "address": "123 Bash Script Ln, Shellville", + "date_from": "2025-11-22T20:44:25.484569-08:00", + "job_status": "Active", + "mitigation_person": "Jason Peters" + }, + { + "job_id": 5, + "customer_name": "Test Job No Devices", + "address": "100 Example St, Demo City", + "date_from": "2025-11-22T02:58:15.989487-08:00", + "job_status": "Active", + "mitigation_person": "Jason Peters" + }, + { + "job_id": 6, + "customer_name": "Test Job No. 2", + "address": "105 Other St, Better City", + "date_from": "2025-11-22T06:23:09.902552-08:00", + "job_status": "Stopped", + "mitigation_person": "Jason Peters" + } + ], + "status": "200 OK" +} +PASS + +---------------------------------------------------------------- +[Test] job_details +# Request: +{ + "function": "job_details", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs", + "job_id": "9" +} + +# Response: +{ + "ok": 1, + "details": { + "job_id": 9, + "customer_name": "TEST_SUITE_CUSTOMER_BASH", + "mitigation_person_id": 68, + "key_person_name": null, + "key_person_mobile": null, + "key_person_email": null, + "address_street": "123 Bash Script Ln", + "address_city": "Shellville", + "address_zip": null, + "address_state": null, + "address_country": null, + "lat": 34.05, + "lng": -118.25, + "date_from": "2025-11-22T20:44:25.484569-08:00", + "date_to": null, + "job_status": "Active", + "devices": "[{\"mac\": \"TEST_MAC_VIRTUAL\", \"location\": \"Lab\"}]", + "alerts_config": {}, + "created_at": "2025-11-22T20:44:25.484569-08:00", + "user_edit": 68 + }, + "status": "200 OK" +} +PASS + +---------------------------------------------------------------- +[Test] job_edit +# Request: +{ + "function": "job_edit", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs", + "job_id": "9", + "job_status": "Stopped", + "date_to": "2025-12-31T23:59:59" +} + +# Response: +{ + "ok": 1, + "job_id": "9", + "status": "200 OK" +} +PASS + +---------------------------------------------------------------- +[Test] job_available_devices +# Request: +{ + "function": "job_available_devices", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs" +} + +# Response: +{ + "ok": 1, + "devices": [ + { + "device_id": 770, + "well_id": 524, + "device_mac": "142B2F81A2CC", + "description": "initial" + }, + { + "device_id": 771, + "well_id": 525, + "device_mac": "901506CA3A64", + "description": "initial" + }, + { + "device_id": 780, + "well_id": 533, + "device_mac": "10061C15C358", + "description": "initial" + }, + { + "device_id": 786, + "well_id": 538, + "device_mac": "142B2F81A12C", + "description": "initial" + }, + { + "device_id": 772, + "well_id": 526, + "device_mac": "10061C15C304", + "description": "initial" + }, + { + "device_id": 781, + "well_id": 534, + "device_mac": "142B2F81AA88", + "description": "initial" + }, + { + "device_id": 783, + "well_id": 535, + "device_mac": "142B2F81AA8C", + "description": "initial" + }, + { + "device_id": 773, + "well_id": 527, + "device_mac": "10061C15C234", + "description": "initial" + }, + { + "device_id": 784, + "well_id": 536, + "device_mac": "142B2F81A690", + "description": "initial" + }, + { + "device_id": 788, + "well_id": 540, + "device_mac": "142B2F81AA68", + "description": "initial" + }, + { + "device_id": 798, + "well_id": 549, + "device_mac": "901506CA3C24", + "description": "initial" + }, + { + "device_id": 496, + "well_id": 201, + "device_mac": "64B708890584", + "description": "" + }, + { + "device_id": 500, + "well_id": 205, + "device_mac": "64B708897018", + "description": "" + }, + { + "device_id": 505, + "well_id": 210, + "device_mac": "64B708890A38", + "description": "" + }, + { + "device_id": 498, + "well_id": 203, + "device_mac": "64B708890B14", + "description": "" + }, + { + "device_id": 703, + "well_id": 460, + "device_mac": "10061C159AA0", + "description": "" + }, + { + "device_id": 639, + "well_id": 407, + "device_mac": "901506CA3CFC", + "description": "" + }, + { + "device_id": 768, + "well_id": 522, + "device_mac": "142B2F81A688", + "description": "initial" + }, + { + "device_id": 785, + "well_id": 537, + "device_mac": "142B2F819F24", + "description": "initial" + }, + { + "device_id": 544, + "well_id": 251, + "device_mac": "64B7088909E8", + "description": "" + }, + { + "device_id": 546, + "well_id": 252, + "device_mac": "64B708890734", + "description": "" + }, + { + "device_id": 789, + "well_id": 541, + "device_mac": "142B2F819E18", + "description": "initial" + }, + { + "device_id": 792, + "well_id": 544, + "device_mac": "10061C15C21C", + "description": "initial" + }, + { + "device_id": 664, + "well_id": 432, + "device_mac": "10061C15C364", + "description": "initial" + }, + { + "device_id": 750, + "well_id": 504, + "device_mac": "142B2F81A3D8", + "description": "initial" + }, + { + "device_id": 643, + "well_id": 411, + "device_mac": "901506CA3CF8", + "description": "" + }, + { + "device_id": 662, + "well_id": 430, + "device_mac": "142B2F81AA6C", + "description": "" + }, + { + "device_id": 734, + "well_id": 488, + "device_mac": "10061C15C33C", + "description": "initial" + }, + { + "device_id": 758, + "well_id": 512, + "device_mac": "10061C159A9C", + "description": "initial" + }, + { + "device_id": 550, + "well_id": 257, + "device_mac": "64B708890248", + "description": null + }, + { + "device_id": 553, + "well_id": 260, + "device_mac": "64B708897074", + "description": null + }, + { + "device_id": 742, + "well_id": 496, + "device_mac": "901506CA3BAC", + "description": "initial" + }, + { + "device_id": 743, + "well_id": 497, + "device_mac": "142B2F81A14C", + "description": "initial" + }, + { + "device_id": 769, + "well_id": 523, + "device_mac": "142B2F81AAD4", + "description": "initial" + }, + { + "device_id": 357, + "well_id": 101, + "device_mac": "64B7088902B8", + "description": "null" + }, + { + "device_id": 358, + "well_id": 102, + "device_mac": "308398C72E58", + "description": "" + }, + { + "device_id": 359, + "well_id": 103, + "device_mac": "64B708896BD8", + "description": "" + }, + { + "device_id": 360, + "well_id": 104, + "device_mac": "64B708890988", + "description": "null" + }, + { + "device_id": 362, + "well_id": 106, + "device_mac": "64B708890298", + "description": "null" + }, + { + "device_id": 363, + "well_id": 107, + "device_mac": "4C75259783E0", + "description": "Malo " + }, + { + "device_id": 386, + "well_id": 130, + "device_mac": "64B7088905C8", + "description": "null" + }, + { + "device_id": 774, + "well_id": 528, + "device_mac": "901506CA42A8", + "description": "initial" + }, + { + "device_id": 499, + "well_id": 204, + "device_mac": "64B708890288", + "description": "" + }, + { + "device_id": 497, + "well_id": 202, + "device_mac": "64B7088906D8", + "description": "" + }, + { + "device_id": 704, + "well_id": 461, + "device_mac": "142B2F81A5B0", + "description": "" + }, + { + "device_id": 790, + "well_id": 542, + "device_mac": "10061C15C350", + "description": "initial" + }, + { + "device_id": 793, + "well_id": 545, + "device_mac": "10061C15C37C", + "description": "initial" + }, + { + "device_id": 669, + "well_id": 437, + "device_mac": "10061C15C218", + "description": "" + }, + { + "device_id": 652, + "well_id": 420, + "device_mac": "326638316134", + "description": "" + }, + { + "device_id": 748, + "well_id": 502, + "device_mac": "901506CA4700", + "description": "initial" + }, + { + "device_id": 660, + "well_id": 428, + "device_mac": "901506CA3CB8", + "description": "" + }, + { + "device_id": 661, + "well_id": 429, + "device_mac": "142B2F81AA64", + "description": "" + }, + { + "device_id": 571, + "well_id": 291, + "device_mac": "64B70888FA84", + "description": null + }, + { + "device_id": 666, + "well_id": 434, + "device_mac": "142B2F81A290", + "description": "initial" + }, + { + "device_id": 670, + "well_id": 438, + "device_mac": "142B2F81AAAC", + "description": "" + }, + { + "device_id": 675, + "well_id": 441, + "device_mac": "10061C15C278", + "description": "" + }, + { + "device_id": 677, + "well_id": 442, + "device_mac": "10061C159A0C", + "description": "" + }, + { + "device_id": 761, + "well_id": 515, + "device_mac": "142B2F81A4B0", + "description": "initial" + }, + { + "device_id": 689, + "well_id": 450, + "device_mac": "142B2F81A2A4", + "description": "" + }, + { + "device_id": 730, + "well_id": 484, + "device_mac": "10061C15C26C", + "description": "initial" + }, + { + "device_id": 724, + "well_id": 478, + "device_mac": "10061C1599AC", + "description": "initial" + }, + { + "device_id": 759, + "well_id": 513, + "device_mac": "142B2F81A67C", + "description": "initial" + }, + { + "device_id": 764, + "well_id": 518, + "device_mac": "10061C15C25C", + "description": "initial" + }, + { + "device_id": 763, + "well_id": 517, + "device_mac": "142B2F81A124", + "description": "initial" + }, + { + "device_id": 318, + "well_id": 37, + "device_mac": "98CDACD07240", + "description": "" + }, + { + "device_id": 319, + "well_id": 30, + "device_mac": "308398E05658", + "description": "" + }, + { + "device_id": 594, + "well_id": 296, + "device_mac": "4267426B7477", + "description": "" + }, + { + "device_id": 636, + "well_id": 404, + "device_mac": "10061C159A84", + "description": "" + }, + { + "device_id": 775, + "well_id": 529, + "device_mac": "142B2F819E10", + "description": "initial" + }, + { + "device_id": 640, + "well_id": 408, + "device_mac": "142B2F81A3E4", + "description": "" + }, + { + "device_id": 645, + "well_id": 413, + "device_mac": "202020202022", + "description": "" + }, + { + "device_id": 667, + "well_id": 435, + "device_mac": "142B2F81A4F8", + "description": "" + }, + { + "device_id": 791, + "well_id": 543, + "device_mac": "901506CA3BC8", + "description": "initial" + }, + { + "device_id": 741, + "well_id": 495, + "device_mac": "142B2F81A128", + "description": "initial" + }, + { + "device_id": 731, + "well_id": 485, + "device_mac": "142B2F819F74", + "description": "initial" + }, + { + "device_id": 710, + "well_id": 467, + "device_mac": "316331353961", + "description": "" + }, + { + "device_id": 762, + "well_id": 516, + "device_mac": "10061C15C32C", + "description": "initial" + }, + { + "device_id": 794, + "well_id": 546, + "device_mac": "10061C15C258", + "description": "initial" + }, + { + "device_id": 749, + "well_id": 503, + "device_mac": "10061C15C320", + "description": "initial" + }, + { + "device_id": 737, + "well_id": 491, + "device_mac": "10061C15C378", + "description": "initial" + }, + { + "device_id": 757, + "well_id": 511, + "device_mac": "142B2F81A160", + "description": "initial" + }, + { + "device_id": 744, + "well_id": 498, + "device_mac": "901506CA3DD0", + "description": "initial" + }, + { + "device_id": 728, + "well_id": 482, + "device_mac": "10061C159A10", + "description": "" + }, + { + "device_id": 296, + "well_id": 51, + "device_mac": "5002919DF284", + "description": "" + }, + { + "device_id": 297, + "well_id": 50, + "device_mac": "308398C717DC", + "description": "" + }, + { + "device_id": 298, + "well_id": 5, + "device_mac": "308398E056AC", + "description": "Small Bathroom" + }, + { + "device_id": 299, + "well_id": 38, + "device_mac": "308398C7245C", + "description": "Eye Tech" + }, + { + "device_id": 300, + "well_id": 35, + "device_mac": "308398C722F0", + "description": "" + }, + { + "device_id": 562, + "well_id": 269, + "device_mac": "64B708897428", + "description": "" + }, + { + "device_id": 559, + "well_id": 266, + "device_mac": "64B70888FAB0", + "description": "Small" + }, + { + "device_id": 524, + "well_id": 273, + "device_mac": "64B708896BDC", + "description": null + }, + { + "device_id": 560, + "well_id": 267, + "device_mac": "64B70888F6F0", + "description": "" + }, + { + "device_id": 705, + "well_id": 462, + "device_mac": "142B2F81A5AC", + "description": "Novi 462" + }, + { + "device_id": 641, + "well_id": 409, + "device_mac": "142B2F81A2D4", + "description": "" + }, + { + "device_id": 637, + "well_id": 405, + "device_mac": "303663613363", + "description": "" + }, + { + "device_id": 795, + "well_id": 547, + "device_mac": "901506CA4320", + "description": "initial" + }, + { + "device_id": 657, + "well_id": 425, + "device_mac": "142B2F81AA60", + "description": "initial" + }, + { + "device_id": 725, + "well_id": 479, + "device_mac": "142B2F819F78", + "description": "initial" + }, + { + "device_id": 732, + "well_id": 486, + "device_mac": "142B2F81A434", + "description": "initial" + }, + { + "device_id": 747, + "well_id": 501, + "device_mac": "10061C1599B0", + "description": "initial" + }, + { + "device_id": 711, + "well_id": 468, + "device_mac": "10061C159A30", + "description": "" + }, + { + "device_id": 561, + "well_id": 268, + "device_mac": "64B70889062C", + "description": null + }, + { + "device_id": 678, + "well_id": 443, + "device_mac": "10061C15C310", + "description": "initial" + }, + { + "device_id": 572, + "well_id": 290, + "device_mac": "64B70888F860", + "description": "" + }, + { + "device_id": 638, + "well_id": 406, + "device_mac": "10061C15C398", + "description": "" + }, + { + "device_id": 721, + "well_id": 476, + "device_mac": "901506CA3CE8", + "description": "" + }, + { + "device_id": 738, + "well_id": 492, + "device_mac": "901506CA3CC4", + "description": "" + }, + { + "device_id": 713, + "well_id": 469, + "device_mac": "142B2F81AAA8", + "description": "" + }, + { + "device_id": 646, + "well_id": 414, + "device_mac": "303663613362", + "description": "" + }, + { + "device_id": 672, + "well_id": 439, + "device_mac": "142B2F819F6C", + "description": "" + }, + { + "device_id": 674, + "well_id": 440, + "device_mac": "901506CA4070", + "description": "" + }, + { + "device_id": 564, + "well_id": 235, + "device_mac": "64B708890A68", + "description": "Yulic" + }, + { + "device_id": 642, + "well_id": 410, + "device_mac": "142B2F819FA0", + "description": "" + }, + { + "device_id": 797, + "well_id": 548, + "device_mac": "142B2F81A4D4", + "description": "initial" + }, + { + "device_id": 647, + "well_id": 415, + "device_mac": "303663613364", + "description": "" + }, + { + "device_id": 649, + "well_id": 417, + "device_mac": "316331353962", + "description": "" + }, + { + "device_id": 745, + "well_id": 499, + "device_mac": "142B2F81A2B8", + "description": "initial" + }, + { + "device_id": 766, + "well_id": 520, + "device_mac": "10061C15C29C", + "description": "initial" + }, + { + "device_id": 733, + "well_id": 487, + "device_mac": "142B2F819E34", + "description": "initial" + }, + { + "device_id": 760, + "well_id": 514, + "device_mac": "10061C159B0C", + "description": "initial" + }, + { + "device_id": 301, + "well_id": 13, + "device_mac": "308398C721E4", + "description": "" + }, + { + "device_id": 302, + "well_id": 3, + "device_mac": "308398C72CDC", + "description": "" + }, + { + "device_id": 303, + "well_id": 9, + "device_mac": "308398C721F4", + "description": "" + }, + { + "device_id": 304, + "well_id": 17, + "device_mac": "308398E05724", + "description": null + }, + { + "device_id": 305, + "well_id": 0, + "device_mac": "DE2F6273361B", + "description": null + }, + { + "device_id": 306, + "well_id": 41, + "device_mac": "308398E055C8", + "description": "ET-B4" + }, + { + "device_id": 307, + "well_id": 39, + "device_mac": "308398C71C84", + "description": "" + }, + { + "device_id": 308, + "well_id": 29, + "device_mac": "308398E055CC", + "description": "" + }, + { + "device_id": 483, + "well_id": 0, + "device_mac": "AB1173AB173C", + "description": null + }, + { + "device_id": 729, + "well_id": 483, + "device_mac": "10061C15C264", + "description": "" + }, + { + "device_id": 665, + "well_id": 433, + "device_mac": "142B2F81A020", + "description": "office temp" + }, + { + "device_id": 534, + "well_id": 236, + "device_mac": "64B708890F2C", + "description": "" + }, + { + "device_id": 746, + "well_id": 500, + "device_mac": "10061C15C340", + "description": "initial" + }, + { + "device_id": 765, + "well_id": 519, + "device_mac": "10061C159AB0", + "description": "initial" + }, + { + "device_id": 630, + "well_id": 400, + "device_mac": "901506CA3C7C", + "description": "initial" + }, + { + "device_id": 631, + "well_id": 401, + "device_mac": "142B2F81A104", + "description": "ok" + }, + { + "device_id": 632, + "well_id": 402, + "device_mac": "901506CA3BDC", + "description": "ok" + }, + { + "device_id": 726, + "well_id": 480, + "device_mac": "142B2F81AAD0", + "description": "initial" + }, + { + "device_id": 756, + "well_id": 510, + "device_mac": "10061C159B08", + "description": "initial" + }, + { + "device_id": 648, + "well_id": 416, + "device_mac": "10061C159B2C", + "description": "ok" + }, + { + "device_id": 655, + "well_id": 423, + "device_mac": "142B2F819E5C", + "description": "" + }, + { + "device_id": 668, + "well_id": 436, + "device_mac": "10061C1599C0", + "description": "" + }, + { + "device_id": 735, + "well_id": 489, + "device_mac": "10061C15C268", + "description": "initial" + }, + { + "device_id": 740, + "well_id": 494, + "device_mac": "142B2F819F54", + "description": "" + }, + { + "device_id": 767, + "well_id": 521, + "device_mac": "142B2F81A69C", + "description": "initial" + }, + { + "device_id": 309, + "well_id": 43, + "device_mac": "308398DF2FFC", + "description": "ET-B4" + }, + { + "device_id": 310, + "well_id": 20, + "device_mac": "308398C718E0", + "description": "Kitchen" + }, + { + "device_id": 311, + "well_id": 24, + "device_mac": "308398C71738", + "description": "" + }, + { + "device_id": 312, + "well_id": 36, + "device_mac": "308398DF3104", + "description": null + }, + { + "device_id": 313, + "well_id": 45, + "device_mac": "308398C727AC", + "description": "ET-B4" + }, + { + "device_id": 314, + "well_id": 26, + "device_mac": "308398E056A4", + "description": "" + }, + { + "device_id": 315, + "well_id": 23, + "device_mac": "308398DF2EDC", + "description": "" + }, + { + "device_id": 316, + "well_id": 14, + "device_mac": "308398C724FC", + "description": "U kuhinji" + }, + { + "device_id": 317, + "well_id": 42, + "device_mac": "308398C71B04", + "description": "" + }, + { + "device_id": 484, + "well_id": 0, + "device_mac": "CC70C4CD7937", + "description": null + }, + { + "device_id": 320, + "well_id": 1, + "device_mac": "308398DF3118", + "description": "Eye Tech" + }, + { + "device_id": 321, + "well_id": 2, + "device_mac": "308398DF2FC8", + "description": "ET-RB" + }, + { + "device_id": 322, + "well_id": 12, + "device_mac": "308398DF2F54", + "description": "Office" + }, + { + "device_id": 323, + "well_id": 15, + "device_mac": "308398C72754", + "description": "" + }, + { + "device_id": 324, + "well_id": 22, + "device_mac": "308398E05804", + "description": "Eye Tech" + }, + { + "device_id": 325, + "well_id": 0, + "device_mac": "CA09FD8223DB", + "description": null + }, + { + "device_id": 326, + "well_id": 40, + "device_mac": "308398C71EA8", + "description": "" + }, + { + "device_id": 327, + "well_id": 47, + "device_mac": "308398DF2EF4", + "description": "ET-B4" + }, + { + "device_id": 328, + "well_id": 4, + "device_mac": "98CDACD074A0", + "description": "" + }, + { + "device_id": 329, + "well_id": 31, + "device_mac": "308398DF2F0C", + "description": "" + }, + { + "device_id": 330, + "well_id": 28, + "device_mac": "308398DF3174", + "description": "" + }, + { + "device_id": 331, + "well_id": 0, + "device_mac": "308398C72040", + "description": "" + }, + { + "device_id": 332, + "well_id": 46, + "device_mac": "308398DF328C", + "description": "" + }, + { + "device_id": 333, + "well_id": 0, + "device_mac": "5A815C521647", + "description": null + }, + { + "device_id": 334, + "well_id": 27, + "device_mac": "308398C717A0", + "description": "Broken" + }, + { + "device_id": 335, + "well_id": 21, + "device_mac": "308398DF2FF0", + "description": "" + }, + { + "device_id": 336, + "well_id": 19, + "device_mac": "308398C727D0", + "description": "" + }, + { + "device_id": 337, + "well_id": 33, + "device_mac": "308398C721E0", + "description": "" + }, + { + "device_id": 338, + "well_id": 44, + "device_mac": "308398E055C0", + "description": "ET-B4" + }, + { + "device_id": 339, + "well_id": 25, + "device_mac": "308398C7293C", + "description": "" + }, + { + "device_id": 340, + "well_id": 49, + "device_mac": "308398C71B60", + "description": "" + }, + { + "device_id": 341, + "well_id": 16, + "device_mac": "308398E055E8", + "description": "" + }, + { + "device_id": 342, + "well_id": 22, + "device_mac": "308398C72140", + "description": "" + }, + { + "device_id": 343, + "well_id": 0, + "device_mac": "3C26406B9566", + "description": null + }, + { + "device_id": 344, + "well_id": 32, + "device_mac": "308398E056BC", + "description": "" + }, + { + "device_id": 345, + "well_id": 0, + "device_mac": "940E5EC56E2F", + "description": null + }, + { + "device_id": 346, + "well_id": 0, + "device_mac": "977C2C3D360A", + "description": null + }, + { + "device_id": 347, + "well_id": 0, + "device_mac": "9BFF446905F7", + "description": null + }, + { + "device_id": 348, + "well_id": 0, + "device_mac": "308398C71828", + "description": null + }, + { + "device_id": 349, + "well_id": 0, + "device_mac": "02C063642E4A", + "description": null + }, + { + "device_id": 350, + "well_id": 0, + "device_mac": "8BCD25F11DB9", + "description": null + }, + { + "device_id": 351, + "well_id": 0, + "device_mac": "AD685A81D9BF", + "description": null + }, + { + "device_id": 352, + "well_id": 0, + "device_mac": "861C78F35F75", + "description": null + }, + { + "device_id": 353, + "well_id": 0, + "device_mac": "1AC8CBC1FE2A", + "description": null + }, + { + "device_id": 354, + "well_id": 0, + "device_mac": "48249F9A13B3", + "description": null + }, + { + "device_id": 355, + "well_id": 0, + "device_mac": "D16D9B26CDB6", + "description": null + }, + { + "device_id": 356, + "well_id": 0, + "device_mac": "3F96C206BBF8", + "description": null + }, + { + "device_id": 361, + "well_id": 105, + "device_mac": "308398C7259C", + "description": "" + }, + { + "device_id": 364, + "well_id": 108, + "device_mac": "64B708890F88", + "description": null + }, + { + "device_id": 365, + "well_id": 109, + "device_mac": "64B708896BE0", + "description": "" + }, + { + "device_id": 366, + "well_id": 110, + "device_mac": "64B70888FB58", + "description": null + }, + { + "device_id": 368, + "well_id": 112, + "device_mac": "64B7088905D0", + "description": "" + }, + { + "device_id": 369, + "well_id": 113, + "device_mac": "64B7088905E0", + "description": "" + }, + { + "device_id": 370, + "well_id": 114, + "device_mac": "64B7088907C4", + "description": "" + }, + { + "device_id": 371, + "well_id": 115, + "device_mac": "64B70888FB84", + "description": "Kitchen" + }, + { + "device_id": 372, + "well_id": 116, + "device_mac": "64B7088904F0", + "description": "tree" + }, + { + "device_id": 373, + "well_id": 117, + "device_mac": "64B7088904F8", + "description": "" + }, + { + "device_id": 374, + "well_id": 118, + "device_mac": "64B708890F6C", + "description": null + }, + { + "device_id": 375, + "well_id": 119, + "device_mac": "64B70888FB80", + "description": "" + }, + { + "device_id": 376, + "well_id": 120, + "device_mac": "D8A01D4C79F0", + "description": "" + }, + { + "device_id": 377, + "well_id": 121, + "device_mac": "64B7088904D0", + "description": null + }, + { + "device_id": 597, + "well_id": 299, + "device_mac": "576C64435555", + "description": "" + }, + { + "device_id": 378, + "well_id": 122, + "device_mac": "64B7088902A4", + "description": "Master Bathroom" + }, + { + "device_id": 379, + "well_id": 123, + "device_mac": "308398C72C1C", + "description": "Living room" + }, + { + "device_id": 380, + "well_id": 124, + "device_mac": "64B7088905D8", + "description": "Office" + }, + { + "device_id": 381, + "well_id": 125, + "device_mac": "64B7088905DC", + "description": "Kitchen" + }, + { + "device_id": 383, + "well_id": 127, + "device_mac": "64B7088902AC", + "description": "Bathroom guest" + }, + { + "device_id": 384, + "well_id": 128, + "device_mac": "64B708890F74", + "description": "" + }, + { + "device_id": 385, + "well_id": 129, + "device_mac": "64B7088904E8", + "description": "moj" + }, + { + "device_id": 387, + "well_id": 131, + "device_mac": "4C7525A8454C", + "description": null + }, + { + "device_id": 388, + "well_id": 132, + "device_mac": "64B7088905FC", + "description": null + }, + { + "device_id": 389, + "well_id": 133, + "device_mac": "308398C724E4", + "description": null + }, + { + "device_id": 390, + "well_id": 134, + "device_mac": "64B708890F68", + "description": null + }, + { + "device_id": 391, + "well_id": 135, + "device_mac": "64B7088904EC", + "description": null + }, + { + "device_id": 392, + "well_id": 136, + "device_mac": "64B7088904DC", + "description": "" + }, + { + "device_id": 393, + "well_id": 137, + "device_mac": "64B7088907AC", + "description": "" + }, + { + "device_id": 394, + "well_id": 138, + "device_mac": "64B7088904F4", + "description": "" + }, + { + "device_id": 395, + "well_id": 139, + "device_mac": "64B7088902A8", + "description": "" + }, + { + "device_id": 396, + "well_id": 140, + "device_mac": "64B7088907D8", + "description": null + }, + { + "device_id": 397, + "well_id": 141, + "device_mac": "64B70888FB50", + "description": "null" + }, + { + "device_id": 403, + "well_id": 147, + "device_mac": "64B70888FB48", + "description": "" + }, + { + "device_id": 367, + "well_id": 111, + "device_mac": "64B708890F78", + "description": "Bedroom" + }, + { + "device_id": 382, + "well_id": 126, + "device_mac": "64B708890618", + "description": "Bedroom" + }, + { + "device_id": 404, + "well_id": 148, + "device_mac": "64B70889029C", + "description": "" + }, + { + "device_id": 405, + "well_id": 149, + "device_mac": "64B7088907B4", + "description": "" + }, + { + "device_id": 406, + "well_id": 150, + "device_mac": "64B70889028C", + "description": "" + }, + { + "device_id": 407, + "well_id": 151, + "device_mac": "64B708890290", + "description": "" + }, + { + "device_id": 408, + "well_id": 152, + "device_mac": "64B708890F84", + "description": "Na stolu" + }, + { + "device_id": 409, + "well_id": 153, + "device_mac": "64B70888FB68", + "description": "" + }, + { + "device_id": 411, + "well_id": 155, + "device_mac": "64B7088905F4", + "description": "Y" + }, + { + "device_id": 412, + "well_id": 156, + "device_mac": "308398C71D24", + "description": null + }, + { + "device_id": 413, + "well_id": 157, + "device_mac": "64B708896BD4", + "description": null + }, + { + "device_id": 414, + "well_id": 158, + "device_mac": "64B70888FB60", + "description": "" + }, + { + "device_id": 415, + "well_id": 159, + "device_mac": "64B708890500", + "description": "Mila" + }, + { + "device_id": 416, + "well_id": 160, + "device_mac": "64B70888FB54", + "description": "" + }, + { + "device_id": 417, + "well_id": 161, + "device_mac": "308398C7202C", + "description": "" + }, + { + "device_id": 418, + "well_id": 162, + "device_mac": "64B708890F60", + "description": null + }, + { + "device_id": 419, + "well_id": 163, + "device_mac": "D8A01D4DA814", + "description": null + }, + { + "device_id": 420, + "well_id": 164, + "device_mac": "64B7088907C0", + "description": "" + }, + { + "device_id": 421, + "well_id": 165, + "device_mac": "308398DF2F3C", + "description": null + }, + { + "device_id": 422, + "well_id": 166, + "device_mac": "64B708896C1C", + "description": "" + }, + { + "device_id": 423, + "well_id": 167, + "device_mac": "D8A01D692CA0", + "description": null + }, + { + "device_id": 424, + "well_id": 168, + "device_mac": "64B708896BCC", + "description": null + }, + { + "device_id": 425, + "well_id": 169, + "device_mac": "64B708890980", + "description": null + }, + { + "device_id": 426, + "well_id": 170, + "device_mac": "64B70888FB7C", + "description": "" + }, + { + "device_id": 427, + "well_id": 171, + "device_mac": "64B7088907B8", + "description": "" + }, + { + "device_id": 709, + "well_id": 466, + "device_mac": "316331356333", + "description": "" + }, + { + "device_id": 428, + "well_id": 172, + "device_mac": "64B7088907CC", + "description": "" + }, + { + "device_id": 429, + "well_id": 173, + "device_mac": "64B7088902CC", + "description": "" + }, + { + "device_id": 430, + "well_id": 174, + "device_mac": "D8A01D4DA4B8", + "description": null + }, + { + "device_id": 431, + "well_id": 175, + "device_mac": "308398C72DD4", + "description": null + }, + { + "device_id": 432, + "well_id": 176, + "device_mac": "308398C71E60", + "description": "" + }, + { + "device_id": 433, + "well_id": 177, + "device_mac": "64B7088907BC", + "description": "" + }, + { + "device_id": 434, + "well_id": 178, + "device_mac": "64B7088904E0", + "description": null + }, + { + "device_id": 435, + "well_id": 179, + "device_mac": "64B7088904D8", + "description": null + }, + { + "device_id": 436, + "well_id": 180, + "device_mac": "64B7088905CC", + "description": "" + }, + { + "device_id": 437, + "well_id": 181, + "device_mac": "64B708890F64", + "description": null + }, + { + "device_id": 438, + "well_id": 182, + "device_mac": "D8A01D4C7BE4", + "description": "" + }, + { + "device_id": 439, + "well_id": 183, + "device_mac": "24A1604E21D4", + "description": null + }, + { + "device_id": 440, + "well_id": 184, + "device_mac": "64B7088907DC", + "description": "" + }, + { + "device_id": 441, + "well_id": 185, + "device_mac": "64B7088904E4", + "description": null + }, + { + "device_id": 442, + "well_id": 186, + "device_mac": "64B708890F5C", + "description": null + }, + { + "device_id": 443, + "well_id": 187, + "device_mac": "64B7088907C8", + "description": "" + }, + { + "device_id": 444, + "well_id": 188, + "device_mac": "98CDACF0BCFC", + "description": "Master" + }, + { + "device_id": 445, + "well_id": 189, + "device_mac": "64B7088907D4", + "description": null + }, + { + "device_id": 446, + "well_id": 190, + "device_mac": "64B7088905D4", + "description": null + }, + { + "device_id": 447, + "well_id": 191, + "device_mac": "64B708890F80", + "description": null + }, + { + "device_id": 448, + "well_id": 192, + "device_mac": "64B7088907D0", + "description": "" + }, + { + "device_id": 449, + "well_id": 193, + "device_mac": "64B7088902B0", + "description": "" + }, + { + "device_id": 450, + "well_id": 194, + "device_mac": "64B70888FB74", + "description": "" + }, + { + "device_id": 451, + "well_id": 195, + "device_mac": "64B70889097C", + "description": null + }, + { + "device_id": 452, + "well_id": 196, + "device_mac": "64B708896C18", + "description": null + }, + { + "device_id": 453, + "well_id": 197, + "device_mac": "308398C72EF8", + "description": "" + }, + { + "device_id": 454, + "well_id": 198, + "device_mac": "64B70888FB78", + "description": "Ernie" + }, + { + "device_id": 455, + "well_id": 199, + "device_mac": "64B7088902A0", + "description": "" + }, + { + "device_id": 456, + "well_id": 100, + "device_mac": "308398DF3100", + "description": "" + }, + { + "device_id": 457, + "well_id": 0, + "device_mac": "16FC0BA69F9F", + "description": null + }, + { + "device_id": 458, + "well_id": 0, + "device_mac": "6B458FCE0E3C", + "description": null + }, + { + "device_id": 459, + "well_id": 0, + "device_mac": "D0B328108FEC", + "description": null + }, + { + "device_id": 460, + "well_id": 0, + "device_mac": "A3C8F90A1EE9", + "description": null + }, + { + "device_id": 461, + "well_id": 0, + "device_mac": "CD0299DEC7CB", + "description": null + }, + { + "device_id": 462, + "well_id": 0, + "device_mac": "AB287276CCDF", + "description": null + }, + { + "device_id": 463, + "well_id": 0, + "device_mac": "EBAC5CCE3B32", + "description": null + }, + { + "device_id": 464, + "well_id": 0, + "device_mac": "722AF199E6CE", + "description": null + }, + { + "device_id": 465, + "well_id": 0, + "device_mac": "16915C4271DE", + "description": null + }, + { + "device_id": 466, + "well_id": 0, + "device_mac": "2A0FFCC4C580", + "description": null + }, + { + "device_id": 467, + "well_id": 0, + "device_mac": "46FAE07996FD", + "description": null + }, + { + "device_id": 468, + "well_id": 0, + "device_mac": "5F37455807FB", + "description": null + }, + { + "device_id": 469, + "well_id": 0, + "device_mac": "3F5ECD608CD3", + "description": null + }, + { + "device_id": 470, + "well_id": 0, + "device_mac": "329E0C0D0AC7", + "description": null + }, + { + "device_id": 471, + "well_id": 0, + "device_mac": "17CA7205A8BB", + "description": null + }, + { + "device_id": 472, + "well_id": 0, + "device_mac": "2978FC972A13", + "description": null + }, + { + "device_id": 473, + "well_id": 0, + "device_mac": "AD520B68CC3F", + "description": null + }, + { + "device_id": 474, + "well_id": 0, + "device_mac": "6C19CEBE50B4", + "description": null + }, + { + "device_id": 475, + "well_id": 0, + "device_mac": "A33DA79FB4ED", + "description": null + }, + { + "device_id": 476, + "well_id": 0, + "device_mac": "DBF9D5E3D847", + "description": null + }, + { + "device_id": 477, + "well_id": 0, + "device_mac": "1EB4099F8C26", + "description": null + }, + { + "device_id": 478, + "well_id": 0, + "device_mac": "DF26BB5708A8", + "description": null + }, + { + "device_id": 479, + "well_id": 0, + "device_mac": "3C265D816C75", + "description": null + }, + { + "device_id": 480, + "well_id": 0, + "device_mac": "34927A9415AB", + "description": null + }, + { + "device_id": 481, + "well_id": 0, + "device_mac": "3D05EE0811F9", + "description": null + }, + { + "device_id": 482, + "well_id": 0, + "device_mac": "F9C881AA65E0", + "description": null + }, + { + "device_id": 485, + "well_id": 0, + "device_mac": "8D9B99C581FD", + "description": null + }, + { + "device_id": 486, + "well_id": 0, + "device_mac": "BBDA40EA0A86", + "description": null + }, + { + "device_id": 487, + "well_id": 0, + "device_mac": "81BC783E4996", + "description": null + }, + { + "device_id": 488, + "well_id": 0, + "device_mac": "FFCCAE8F0215", + "description": null + }, + { + "device_id": 489, + "well_id": 0, + "device_mac": "3BD19859E2E0", + "description": null + }, + { + "device_id": 490, + "well_id": 0, + "device_mac": "CCF8FB26533B", + "description": null + }, + { + "device_id": 491, + "well_id": 0, + "device_mac": "0E1EBDE28BA8", + "description": null + }, + { + "device_id": 492, + "well_id": 0, + "device_mac": "AF036F22BCB7", + "description": null + }, + { + "device_id": 493, + "well_id": 0, + "device_mac": "205D6834E4E8", + "description": null + }, + { + "device_id": 494, + "well_id": 0, + "device_mac": "B69C2BB08639", + "description": null + }, + { + "device_id": 495, + "well_id": 1, + "device_mac": "64B708890524", + "description": null + }, + { + "device_id": 502, + "well_id": 207, + "device_mac": "64B7088904B0", + "description": null + }, + { + "device_id": 503, + "well_id": 208, + "device_mac": "64B7088976BC", + "description": "" + }, + { + "device_id": 504, + "well_id": 209, + "device_mac": "64B708890874", + "description": "" + }, + { + "device_id": 506, + "well_id": 211, + "device_mac": "64B7088901E8", + "description": null + }, + { + "device_id": 508, + "well_id": 213, + "device_mac": "64B708890800", + "description": null + }, + { + "device_id": 509, + "well_id": 214, + "device_mac": "64B70889761C", + "description": null + }, + { + "device_id": 511, + "well_id": 216, + "device_mac": "64B708896E90", + "description": "master" + }, + { + "device_id": 512, + "well_id": 217, + "device_mac": "64B70889739C", + "description": null + }, + { + "device_id": 514, + "well_id": 294, + "device_mac": "64B708890888", + "description": "" + }, + { + "device_id": 516, + "well_id": 0, + "device_mac": "8FAB8CBDAA5F", + "description": null + }, + { + "device_id": 518, + "well_id": 220, + "device_mac": "64B708890F04", + "description": null + }, + { + "device_id": 519, + "well_id": 231, + "device_mac": "64B70889046C", + "description": null + }, + { + "device_id": 520, + "well_id": 221, + "device_mac": "64B708890954", + "description": null + }, + { + "device_id": 522, + "well_id": 274, + "device_mac": "64B708890614", + "description": null + }, + { + "device_id": 523, + "well_id": 278, + "device_mac": "64B70889050C", + "description": null + }, + { + "device_id": 528, + "well_id": 271, + "device_mac": "64B708890E54", + "description": "" + }, + { + "device_id": 545, + "well_id": 253, + "device_mac": "64B7088976A4", + "description": "" + }, + { + "device_id": 727, + "well_id": 481, + "device_mac": "10061C159AF4", + "description": "" + }, + { + "device_id": 554, + "well_id": 261, + "device_mac": "64B708897050", + "description": null + }, + { + "device_id": 555, + "well_id": 262, + "device_mac": "64B7088909C4", + "description": null + }, + { + "device_id": 573, + "well_id": 293, + "device_mac": "64B7088976A0", + "description": null + }, + { + "device_id": 574, + "well_id": 292, + "device_mac": "64B708890414", + "description": null + }, + { + "device_id": 708, + "well_id": 465, + "device_mac": "901506CA3A98", + "description": "" + }, + { + "device_id": 578, + "well_id": 0, + "device_mac": "7D57DEE3BC9F", + "description": null + }, + { + "device_id": 579, + "well_id": 0, + "device_mac": "AB200896C218", + "description": null + }, + { + "device_id": 580, + "well_id": 0, + "device_mac": "DE1D5CD557A3", + "description": null + }, + { + "device_id": 581, + "well_id": 0, + "device_mac": "5451F084ECA9", + "description": null + }, + { + "device_id": 582, + "well_id": 0, + "device_mac": "0C09228FE30E", + "description": null + }, + { + "device_id": 583, + "well_id": 0, + "device_mac": "47558F645D1D", + "description": null + }, + { + "device_id": 584, + "well_id": 0, + "device_mac": "4DBBC694C838", + "description": null + }, + { + "device_id": 585, + "well_id": 0, + "device_mac": "6EFE7FBFCE51", + "description": null + }, + { + "device_id": 586, + "well_id": 0, + "device_mac": "9AC12C2FF57B", + "description": null + }, + { + "device_id": 587, + "well_id": 0, + "device_mac": "F823C19B3A33", + "description": null + }, + { + "device_id": 588, + "well_id": 0, + "device_mac": "F958F64B9493", + "description": null + }, + { + "device_id": 589, + "well_id": 0, + "device_mac": "FC9107B47036", + "description": null + }, + { + "device_id": 590, + "well_id": 0, + "device_mac": "3C662CA26D6F", + "description": null + }, + { + "device_id": 736, + "well_id": 490, + "device_mac": "142B2F81A268", + "description": "" + }, + { + "device_id": 591, + "well_id": 295, + "device_mac": "64B708890784", + "description": null + }, + { + "device_id": 410, + "well_id": 154, + "device_mac": "64B708890F70", + "description": "" + }, + { + "device_id": 595, + "well_id": 297, + "device_mac": "4251426B7477", + "description": "" + }, + { + "device_id": 596, + "well_id": 298, + "device_mac": "643268435A30", + "description": "" + }, + { + "device_id": 739, + "well_id": 493, + "device_mac": "142B2F81A450", + "description": "" + }, + { + "device_id": 778, + "well_id": 532, + "device_mac": "901506CA3E88", + "description": "initial" + } + ], + "status": "200 OK" +} +PASS + +---------------------------------------------------------------- +[Test] job_weather +# Request: +{ + "function": "job_weather", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs", + "job_id": "9" +} + +# Response: +{ + "ok": 1, + "weather": { + "time": "2025-11-22 20:30", + "temp_c": 16.8, + "temp_f": 62.2, + "humidity": 44, + "condition": "Clear sky" + }, + "status": "200 OK" +} +PASS + +---------------------------------------------------------------- +[Test] get_job_sensor_bucketed_data +# Request: +{ + "function": "get_job_sensor_bucketed_data", + "user_name": "jpeters", + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImpwZXRlcnMiLCJleHAiOjE3NjM5NTk0NjV9.3aerTVK12fwVehD10QjV6pkLZz99DzPLBArotntUITs", + "job_id": "9", + "sensor": "temperature", + "date": "2025-11-22", + "to_date": "2025-11-22", + "bucket_size": "15m" +} + +# Response: +{ + "ok": 1, + "job_id": "9", + "sensor": "temperature", + "units": "°F", + "color": "red", + "bucket_size": "15m", + "date_from": "2025-11-22", + "date_to": "2025-11-22", + "time_zone": "America/Los_Angeles", + "chart_data": [], + "status": "200 OK" +} +PASS + +---------------------------------------------------------------- +[Test] Cleanup +-> Deleting Job ID: 9 from database... +Cleanup successful. Database restored. + +=== Test Suite Finished === \ No newline at end of file diff --git a/job_available_devices-test.sh b/job_available_devices-test.sh new file mode 100644 index 0000000..3542f5f --- /dev/null +++ b/job_available_devices-test.sh @@ -0,0 +1,296 @@ +#!/bin/bash + +# ============================================================================== +# WellDrySense API Test Suite (Bash Version) +# Functionality: Exercises Job APIs including Create, List, Edit, Details, Weather, +# Sensor Data, and performs Database Cleanup. +# ============================================================================== + +# --- Configuration --- +# Load .env file if it exists +if [ -f .env ]; then + export $(cat .env | xargs) +fi + +# Defaults (can be overridden by env vars) +PORT="${PORT:-8002}" +BASE_URL="http://localhost:$PORT/api/well_api" +API_USER="${API_USER:-jpeters}" +API_PASSWORD="${API_PASSWORD:-WellJson}" +DB_NAME="${DB_NAME:-wellnuo}" +DB_USER="${DB_USER:-postgres}" +DB_HOST="${DB_HOST:-localhost}" +DB_PORT="${DB_PORT:-5432}" +# DB_PASSWORD should be set in .env or exported + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Global Variables +TOKEN="" +USER_ID="" +JOB_ID="" + +# Check for jq +if ! command -v jq &> /dev/null; then + echo -e "${RED}Error: 'jq' is not installed. Please install it to run this script.${NC}" + exit 1 +fi + +echo -e "${BLUE}=== Setting up WellDrySense Test Suite on Port $PORT ===${NC}" + +# ============================================================================== +# Helper Functions +# ============================================================================== + +# Function to print section headers +print_header() { + echo -e "\n${BLUE}----------------------------------------------------------------${NC}" + echo -e "${BLUE}[Test] $1${NC}" +} + +# Function to perform a POST request +# Usage: perform_test "Test Name" "JSON_PAYLOAD_STRING" +perform_test() { + local test_name="$1" + local json_payload="$2" + + print_header "$test_name" + + # 1. Print Request + echo "# Request:" + echo "$json_payload" | jq '.' + + # 2. Convert JSON to Form Data for curl (flattening simple objects) + # Note: This simple conversion handles top-level keys. + # Complex nested JSON strings (like 'devices') need to be passed as strings in the input JSON. + local form_data="" + + # Extract keys and values and build form string + while IFS="=" read -r key value; do + if [ -n "$key" ]; then + # URL encode the value + encoded_value=$(printf '%s' "$value" | jq -sRr @uri) + if [ -z "$form_data" ]; then + form_data="${key}=${encoded_value}" + else + form_data="${form_data}&${key}=${encoded_value}" + fi + fi + done < <(echo "$json_payload" | jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]") + + # 3. Execute Request + response=$(curl -s -X POST "$BASE_URL" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "$form_data") + + # 4. Print Response + echo -e "\n# Response:" + if [ -z "$response" ]; then + echo "(Empty Response)" + echo -e "${RED}FAIL${NC}" + return 1 + else + echo "$response" | jq '.' 2>/dev/null || echo "$response" + fi + + # 5. Evaluate Pass/Fail based on "ok": 1 + ok_val=$(echo "$response" | jq -r '.ok // .status // 0') + + # Handle different response structures (some return {status: 200}, some {ok: 1}) + if [ "$ok_val" == "1" ] || [ "$ok_val" == "200" ] || [ "$ok_val" == "success" ]; then + echo -e "${GREEN}PASS${NC}" + + # Extract Job ID if this was the create step + if [ "$test_name" == "job_create" ]; then + JOB_ID=$(echo "$response" | jq -r '.job_id') + echo "-> Captured Job ID: $JOB_ID" + fi + return 0 + else + error_msg=$(echo "$response" | jq -r '.error // .message // "Unknown error"') + echo -e "${RED}FAIL: $error_msg${NC}" + return 1 + fi +} + +# ============================================================================== +# Test Execution +# ============================================================================== + +# 1. Login / Credentials +# ---------------------- +login_payload=$(jq -n \ + --arg fn "credentials" \ + --arg un "$API_USER" \ + --arg ps "$API_PASSWORD" \ + --arg cid "bash-suite" \ + --arg nonce "test-nonce" \ + '{function: $fn, user_name: $un, ps: $ps, clientId: $cid, nonce: $nonce}') + +print_header "Login" +echo "# Request:" +echo "$login_payload" | jq '.' + +# Special handling for login to capture token +response=$(curl -s -X POST "$BASE_URL" -d "function=credentials&user_name=$API_USER&ps=$API_PASSWORD&clientId=bash-suite&nonce=test-nonce") + +echo -e "\n# Response:" +echo "$response" | jq '.' + +TOKEN=$(echo "$response" | jq -r '.access_token // .data.access_token') +USER_ID=$(echo "$response" | jq -r '.user_id // .data.user_id') + +if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ]; then + echo -e "${GREEN}PASS${NC} (User ID: $USER_ID)" +else + echo -e "${RED}FATAL: Login failed. Check credentials.${NC}" + exit 1 +fi + +# # 2. Create Job +# # ---------------------- +# # Note: We pass JSON strings for complex fields like 'devices' and 'alerts_config' +# devices_json='[{"mac": "TEST_MAC_VIRTUAL", "location": "Lab"}]' +# alerts_json='{"temp_high": 30}' + +# create_payload=$(jq -n \ +# --arg fn "job_create" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg cn "TEST_SUITE_CUSTOMER_BASH" \ +# --arg as "123 Bash Script Ln" \ +# --arg ac "Shellville" \ +# --arg dev "$devices_json" \ +# --arg lat "34.05" \ +# --arg lng "-118.25" \ +# '{function: $fn, user_name: $un, token: $tk, customer_name: $cn, address_street: $as, address_city: $ac, devices: $dev, lat: $lat, lng: $lng}') + +# perform_test "job_create" "$create_payload" || exit 1 + +# # 3. Job List +# # ---------------------- +# list_payload=$(jq -n \ +# --arg fn "job_list" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# '{function: $fn, user_name: $un, token: $tk}') + +# perform_test "job_list" "$list_payload" + +# # 4. Job Details +# # ---------------------- +# details_payload=$(jq -n \ +# --arg fn "job_details" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid}') + +# perform_test "job_details" "$details_payload" + +# # 5. Job Edit (Stop Job) +# # ---------------------- +# edit_payload=$(jq -n \ +# --arg fn "job_edit" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# --arg st "Stopped" \ +# --arg dt "2025-12-31T23:59:59" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid, job_status: $st, date_to: $dt}') + +# perform_test "job_edit" "$edit_payload" + + +# ... previous parts of script ... + +# ... previous sections (Login, Create Job, etc.) ... + +# 6. Available Devices +# ---------------------- +# Note: The API now derives the deployment ID from the user_name -> person_details. +# well_id is not sent as a parameter. + +# This payload works correctly with the new implementation +avail_payload=$(jq -n \ + --arg fn "job_available_devices" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + '{function: $fn, user_name: $un, token: $tk}') + +perform_test "job_available_devices" "$avail_payload" + +# This payload works correctly with the new implementation +avail_payload=$(jq -n \ + --arg fn "job_available_devices2" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + '{function: $fn, user_name: $un, token: $tk}') + +perform_test "job_available_devices2" "$avail_payload" + +# ... remaining sections ... + +# ... rest of script ... + +# # 7. Job Weather +# # ---------------------- +# weather_payload=$(jq -n \ +# --arg fn "job_weather" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid}') + +# perform_test "job_weather" "$weather_payload" + +# # 8. Job Sensor Bucketed Data (New Test) +# # ---------------------- +# # Using dynamic dates for the test +# DATE_FROM=$(date +%Y-%m-%d) +# DATE_TO=$(date +%Y-%m-%d) + +# sensor_payload=$(jq -n \ +# --arg fn "get_job_sensor_bucketed_data" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# --arg sens "temperature" \ +# --arg dt "$DATE_FROM" \ +# --arg dtt "$DATE_TO" \ +# --arg bs "15m" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid, sensor: $sens, date: $dt, to_date: $dtt, bucket_size: $bs}') + +# perform_test "get_job_sensor_bucketed_data" "$sensor_payload" + +# ============================================================================== +# Cleanup +# ============================================================================== +print_header "Cleanup" + +if [ -n "$JOB_ID" ]; then + echo "-> Deleting Job ID: $JOB_ID from database..." + + # Use PGPASSWORD for non-interactive auth if set + export PGPASSWORD="${DB_PASSWORD}" + + psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "DELETE FROM public.jobs WHERE job_id = $JOB_ID;" > /dev/null 2>&1 + + if [ $? -eq 0 ]; then + echo -e "${GREEN}Cleanup successful. Database restored.${NC}" + else + echo -e "${RED}Cleanup failed. Please manually delete job_id $JOB_ID from public.jobs.${NC}" + echo "Command attempted: psql -h $DB_HOST -U $DB_USER -d $DB_NAME -c \"DELETE FROM public.jobs WHERE job_id = $JOB_ID;\"" + fi +else + echo "No Job ID created, skipping cleanup." +fi + +echo -e "\n${BLUE}=== Test Suite Finished ===${NC}" +# ============================================================================== +# well-api.py modifications to support WellDrySense API on port 1998 +# ============================================================================== \ No newline at end of file diff --git a/postgress.sql b/postgress.sql new file mode 100644 index 0000000..3099edb --- /dev/null +++ b/postgress.sql @@ -0,0 +1,5808 @@ +pg_dump: warning: there are circular foreign-key constraints on this table: +pg_dump: detail: hypertable +pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints. +pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem. +pg_dump: warning: there are circular foreign-key constraints on this table: +pg_dump: detail: chunk +pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints. +pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem. +pg_dump: warning: there are circular foreign-key constraints on this table: +pg_dump: detail: continuous_agg +pg_dump: hint: You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints. +pg_dump: hint: Consider using a full dump instead of a --data-only dump to avoid this problem. +-- +-- PostgreSQL database dump +-- + +\restrict nEgDhKD6B4nWaKSvB1ltprY3sHDNBEhrNLwMHP5zWu9gM4hHDn5zuiRnbZjRlH7 + +-- Dumped from database version 16.10 (Ubuntu 16.10-0ubuntu0.24.04.1) +-- Dumped by pg_dump version 16.10 (Ubuntu 16.10-0ubuntu0.24.04.1) + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: timescaledb; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS timescaledb WITH SCHEMA public; + + +-- +-- Name: EXTENSION timescaledb; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION timescaledb IS 'Enables scalable inserts and complex queries for time-series data (Apache 2 Edition)'; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- Name: radar_readings; Type: TABLE; Schema: public; Owner: well_app +-- + +CREATE TABLE public.radar_readings ( + "time" timestamp with time zone NOT NULL, + device_id integer, + absent double precision, + moving double precision, + stationary double precision, + "both" double precision, + m0 double precision, + m1 double precision, + m2 double precision, + m3 double precision, + m4 double precision, + m5 double precision, + m6 double precision, + m7 double precision, + m8 double precision, + s0 double precision, + s1 double precision, + s2 double precision, + s3 double precision, + s4 double precision, + s5 double precision, + s6 double precision, + s7 double precision, + s8 double precision +); + + +ALTER TABLE public.radar_readings OWNER TO well_app; + +-- +-- Name: _hyper_3_101_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_101_chunk ( + CONSTRAINT constraint_101 CHECK ((("time" >= '2025-08-20 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-27 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_101_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_103_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_103_chunk ( + CONSTRAINT constraint_103 CHECK ((("time" >= '2025-08-27 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-03 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_103_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_105_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_105_chunk ( + CONSTRAINT constraint_105 CHECK ((("time" >= '2025-09-03 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-10 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_105_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_107_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_107_chunk ( + CONSTRAINT constraint_107 CHECK ((("time" >= '2025-09-10 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-17 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_107_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_109_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_109_chunk ( + CONSTRAINT constraint_109 CHECK ((("time" >= '2025-09-17 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-24 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_109_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_10_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_10_chunk ( + CONSTRAINT constraint_10 CHECK ((("time" >= '2024-09-25 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-02 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_10_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_111_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_111_chunk ( + CONSTRAINT constraint_111 CHECK ((("time" >= '2025-09-24 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-01 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_111_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_113_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_113_chunk ( + CONSTRAINT constraint_113 CHECK ((("time" >= '2025-10-01 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-08 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_113_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_114_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_114_chunk ( + CONSTRAINT constraint_114 CHECK ((("time" >= '2025-10-08 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-15 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_114_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_117_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_117_chunk ( + CONSTRAINT constraint_117 CHECK ((("time" >= '2025-10-15 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-22 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_117_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_119_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_119_chunk ( + CONSTRAINT constraint_119 CHECK ((("time" >= '2025-10-22 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-29 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_119_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_11_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_11_chunk ( + CONSTRAINT constraint_11 CHECK ((("time" >= '2024-10-02 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_11_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_121_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_121_chunk ( + CONSTRAINT constraint_121 CHECK ((("time" >= '2025-10-29 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-11-05 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_121_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_123_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_123_chunk ( + CONSTRAINT constraint_123 CHECK ((("time" >= '2025-11-05 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-11-12 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_123_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_125_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_125_chunk ( + CONSTRAINT constraint_125 CHECK ((("time" >= '2025-11-12 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-11-19 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_125_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_127_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_127_chunk ( + CONSTRAINT constraint_127 CHECK ((("time" >= '2025-11-19 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-11-26 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_127_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_12_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_12_chunk ( + CONSTRAINT constraint_12 CHECK ((("time" >= '2024-10-09 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-16 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_12_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_13_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_13_chunk ( + CONSTRAINT constraint_13 CHECK ((("time" >= '2024-10-16 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-23 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_13_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_14_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_14_chunk ( + CONSTRAINT constraint_14 CHECK ((("time" >= '2024-10-23 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-30 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_14_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_15_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_15_chunk ( + CONSTRAINT constraint_15 CHECK ((("time" >= '2024-10-30 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-11-06 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_15_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_16_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_16_chunk ( + CONSTRAINT constraint_16 CHECK ((("time" >= '2024-11-06 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-11-13 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_16_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_17_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_17_chunk ( + CONSTRAINT constraint_17 CHECK ((("time" >= '2025-01-08 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-15 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_17_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_18_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_18_chunk ( + CONSTRAINT constraint_18 CHECK ((("time" >= '2025-01-15 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-22 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_18_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_19_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_19_chunk ( + CONSTRAINT constraint_19 CHECK ((("time" >= '2025-01-22 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-29 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_19_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_1_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_1_chunk ( + CONSTRAINT constraint_1 CHECK ((("time" >= '2024-11-13 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-11-20 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_1_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_20_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_20_chunk ( + CONSTRAINT constraint_20 CHECK ((("time" >= '2025-01-29 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-05 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_20_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_21_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_21_chunk ( + CONSTRAINT constraint_21 CHECK ((("time" >= '2025-02-05 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-12 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_21_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_22_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_22_chunk ( + CONSTRAINT constraint_22 CHECK ((("time" >= '2025-02-12 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-19 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_22_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_2_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_2_chunk ( + CONSTRAINT constraint_2 CHECK ((("time" >= '2024-11-20 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-11-27 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_2_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_3_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_3_chunk ( + CONSTRAINT constraint_3 CHECK ((("time" >= '2024-11-27 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-04 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_3_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_45_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_45_chunk ( + CONSTRAINT constraint_45 CHECK ((("time" >= '2025-02-19 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-26 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_45_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_48_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_48_chunk ( + CONSTRAINT constraint_48 CHECK ((("time" >= '2025-02-26 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-03-05 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_48_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_49_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_49_chunk ( + CONSTRAINT constraint_49 CHECK ((("time" >= '2025-03-05 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-03-12 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_49_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_4_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_4_chunk ( + CONSTRAINT constraint_4 CHECK ((("time" >= '2024-12-04 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-11 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_4_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_52_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_52_chunk ( + CONSTRAINT constraint_52 CHECK ((("time" >= '2025-03-12 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-03-19 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_52_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_53_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_53_chunk ( + CONSTRAINT constraint_53 CHECK ((("time" >= '2025-03-19 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-03-26 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_53_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_55_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_55_chunk ( + CONSTRAINT constraint_55 CHECK ((("time" >= '2025-03-26 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-02 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_55_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_57_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_57_chunk ( + CONSTRAINT constraint_57 CHECK ((("time" >= '2025-04-02 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_57_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_59_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_59_chunk ( + CONSTRAINT constraint_59 CHECK ((("time" >= '2025-04-09 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-16 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_59_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_5_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_5_chunk ( + CONSTRAINT constraint_5 CHECK ((("time" >= '2024-12-11 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-18 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_5_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_61_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_61_chunk ( + CONSTRAINT constraint_61 CHECK ((("time" >= '2025-04-16 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-23 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_61_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_63_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_63_chunk ( + CONSTRAINT constraint_63 CHECK ((("time" >= '2025-04-23 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-30 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_63_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_65_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_65_chunk ( + CONSTRAINT constraint_65 CHECK ((("time" >= '2025-04-30 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-07 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_65_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_68_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_68_chunk ( + CONSTRAINT constraint_68 CHECK ((("time" >= '2025-05-07 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-14 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_68_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_6_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_6_chunk ( + CONSTRAINT constraint_6 CHECK ((("time" >= '2024-12-18 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-25 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_6_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_70_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_70_chunk ( + CONSTRAINT constraint_70 CHECK ((("time" >= '2025-05-14 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-21 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_70_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_72_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_72_chunk ( + CONSTRAINT constraint_72 CHECK ((("time" >= '2025-05-21 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-28 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_72_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_74_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_74_chunk ( + CONSTRAINT constraint_74 CHECK ((("time" >= '2025-05-28 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-04 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_74_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_75_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_75_chunk ( + CONSTRAINT constraint_75 CHECK ((("time" >= '2025-06-04 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-11 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_75_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_77_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_77_chunk ( + CONSTRAINT constraint_77 CHECK ((("time" >= '2025-06-11 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-18 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_77_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_79_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_79_chunk ( + CONSTRAINT constraint_79 CHECK ((("time" >= '2025-06-18 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-25 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_79_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_7_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_7_chunk ( + CONSTRAINT constraint_7 CHECK ((("time" >= '2024-12-25 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-01 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_7_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_82_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_82_chunk ( + CONSTRAINT constraint_82 CHECK ((("time" >= '2025-06-25 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-02 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_82_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_84_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_84_chunk ( + CONSTRAINT constraint_84 CHECK ((("time" >= '2025-07-02 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_84_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_85_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_85_chunk ( + CONSTRAINT constraint_85 CHECK ((("time" >= '2025-07-09 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-16 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_85_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_88_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_88_chunk ( + CONSTRAINT constraint_88 CHECK ((("time" >= '2025-07-16 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-23 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_88_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_8_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_8_chunk ( + CONSTRAINT constraint_8 CHECK ((("time" >= '2025-01-01 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-08 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_8_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_90_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_90_chunk ( + CONSTRAINT constraint_90 CHECK ((("time" >= '2025-07-23 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-30 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_90_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_95_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_95_chunk ( + CONSTRAINT constraint_95 CHECK ((("time" >= '2025-07-30 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-06 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_95_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_97_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_97_chunk ( + CONSTRAINT constraint_97 CHECK ((("time" >= '2025-08-06 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-13 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_97_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_99_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_99_chunk ( + CONSTRAINT constraint_99 CHECK ((("time" >= '2025-08-13 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-20 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_99_chunk OWNER TO well_app; + +-- +-- Name: _hyper_3_9_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_3_9_chunk ( + CONSTRAINT constraint_9 CHECK ((("time" >= '1969-12-31 16:00:00-08'::timestamp with time zone) AND ("time" < '1970-01-07 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.radar_readings); + + +ALTER TABLE _timescaledb_internal._hyper_3_9_chunk OWNER TO well_app; + +-- +-- Name: sensor_readings; Type: TABLE; Schema: public; Owner: well_app +-- + +CREATE TABLE public.sensor_readings ( + "time" timestamp with time zone NOT NULL, + device_id integer NOT NULL, + temperature double precision, + humidity double precision, + pressure double precision, + light double precision, + s0 double precision, + s1 double precision, + s2 double precision, + s3 double precision, + s4 double precision, + s5 double precision, + s6 double precision, + s7 double precision, + s8 double precision, + s9 double precision, + mtype smallint DEFAULT 0 +); + + +ALTER TABLE public.sensor_readings OWNER TO well_app; + +-- +-- Name: _hyper_4_100_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_100_chunk ( + CONSTRAINT constraint_100 CHECK ((("time" >= '2025-08-20 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-27 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_100_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_102_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_102_chunk ( + CONSTRAINT constraint_102 CHECK ((("time" >= '2025-08-27 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-03 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_102_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_104_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_104_chunk ( + CONSTRAINT constraint_104 CHECK ((("time" >= '2025-09-03 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-10 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_104_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_106_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_106_chunk ( + CONSTRAINT constraint_106 CHECK ((("time" >= '2025-09-10 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-17 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_106_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_108_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_108_chunk ( + CONSTRAINT constraint_108 CHECK ((("time" >= '2025-09-17 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-09-24 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_108_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_110_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_110_chunk ( + CONSTRAINT constraint_110 CHECK ((("time" >= '2025-09-24 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-01 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_110_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_112_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_112_chunk ( + CONSTRAINT constraint_112 CHECK ((("time" >= '2025-10-01 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-08 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_112_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_115_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_115_chunk ( + CONSTRAINT constraint_115 CHECK ((("time" >= '2025-10-08 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-15 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_115_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_116_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_116_chunk ( + CONSTRAINT constraint_116 CHECK ((("time" >= '2025-10-15 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-22 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_116_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_118_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_118_chunk ( + CONSTRAINT constraint_118 CHECK ((("time" >= '2025-10-22 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-10-29 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_118_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_120_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_120_chunk ( + CONSTRAINT constraint_120 CHECK ((("time" >= '2025-10-29 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-11-05 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_120_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_122_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_122_chunk ( + CONSTRAINT constraint_122 CHECK ((("time" >= '2025-11-05 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-11-12 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_122_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_124_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_124_chunk ( + CONSTRAINT constraint_124 CHECK ((("time" >= '2025-11-12 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-11-19 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_124_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_126_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_126_chunk ( + CONSTRAINT constraint_126 CHECK ((("time" >= '2025-11-19 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-11-26 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_126_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_23_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_23_chunk ( + CONSTRAINT constraint_23 CHECK ((("time" >= '1969-12-31 16:00:00-08'::timestamp with time zone) AND ("time" < '1970-01-07 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_23_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_24_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_24_chunk ( + CONSTRAINT constraint_24 CHECK ((("time" >= '2024-09-25 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-02 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_24_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_25_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_25_chunk ( + CONSTRAINT constraint_25 CHECK ((("time" >= '2024-10-02 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_25_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_26_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_26_chunk ( + CONSTRAINT constraint_26 CHECK ((("time" >= '2024-10-09 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-16 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_26_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_27_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_27_chunk ( + CONSTRAINT constraint_27 CHECK ((("time" >= '2024-10-16 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-23 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_27_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_28_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_28_chunk ( + CONSTRAINT constraint_28 CHECK ((("time" >= '2024-10-23 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-10-30 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_28_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_29_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_29_chunk ( + CONSTRAINT constraint_29 CHECK ((("time" >= '2024-10-30 17:00:00-07'::timestamp with time zone) AND ("time" < '2024-11-06 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_29_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_30_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_30_chunk ( + CONSTRAINT constraint_30 CHECK ((("time" >= '2024-11-06 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-11-13 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_30_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_31_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_31_chunk ( + CONSTRAINT constraint_31 CHECK ((("time" >= '2024-11-13 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-11-20 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_31_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_32_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_32_chunk ( + CONSTRAINT constraint_32 CHECK ((("time" >= '2024-11-20 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-11-27 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_32_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_33_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_33_chunk ( + CONSTRAINT constraint_33 CHECK ((("time" >= '2024-11-27 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-04 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_33_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_34_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_34_chunk ( + CONSTRAINT constraint_34 CHECK ((("time" >= '2024-12-04 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-11 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_34_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_35_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_35_chunk ( + CONSTRAINT constraint_35 CHECK ((("time" >= '2024-12-11 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-18 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_35_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_36_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_36_chunk ( + CONSTRAINT constraint_36 CHECK ((("time" >= '2024-12-18 16:00:00-08'::timestamp with time zone) AND ("time" < '2024-12-25 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_36_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_37_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_37_chunk ( + CONSTRAINT constraint_37 CHECK ((("time" >= '2024-12-25 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-01 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_37_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_38_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_38_chunk ( + CONSTRAINT constraint_38 CHECK ((("time" >= '2025-01-01 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-08 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_38_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_39_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_39_chunk ( + CONSTRAINT constraint_39 CHECK ((("time" >= '2025-01-08 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-15 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_39_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_40_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_40_chunk ( + CONSTRAINT constraint_40 CHECK ((("time" >= '2025-01-15 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-22 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_40_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_41_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_41_chunk ( + CONSTRAINT constraint_41 CHECK ((("time" >= '2025-01-22 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-01-29 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_41_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_42_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_42_chunk ( + CONSTRAINT constraint_42 CHECK ((("time" >= '2025-01-29 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-05 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_42_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_43_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_43_chunk ( + CONSTRAINT constraint_43 CHECK ((("time" >= '2025-02-05 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-12 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_43_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_44_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_44_chunk ( + CONSTRAINT constraint_44 CHECK ((("time" >= '2025-02-12 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-19 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_44_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_46_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_46_chunk ( + CONSTRAINT constraint_46 CHECK ((("time" >= '2025-02-19 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-02-26 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_46_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_47_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_47_chunk ( + CONSTRAINT constraint_47 CHECK ((("time" >= '2025-02-26 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-03-05 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_47_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_50_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_50_chunk ( + CONSTRAINT constraint_50 CHECK ((("time" >= '2025-03-05 16:00:00-08'::timestamp with time zone) AND ("time" < '2025-03-12 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_50_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_51_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_51_chunk ( + CONSTRAINT constraint_51 CHECK ((("time" >= '2025-03-12 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-03-19 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_51_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_54_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_54_chunk ( + CONSTRAINT constraint_54 CHECK ((("time" >= '2025-03-19 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-03-26 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_54_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_56_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_56_chunk ( + CONSTRAINT constraint_56 CHECK ((("time" >= '2025-03-26 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-02 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_56_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_58_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_58_chunk ( + CONSTRAINT constraint_58 CHECK ((("time" >= '2025-04-02 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_58_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_60_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_60_chunk ( + CONSTRAINT constraint_60 CHECK ((("time" >= '2025-04-09 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-16 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_60_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_62_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_62_chunk ( + CONSTRAINT constraint_62 CHECK ((("time" >= '2025-04-16 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-23 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_62_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_64_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_64_chunk ( + CONSTRAINT constraint_64 CHECK ((("time" >= '2025-04-23 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-04-30 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_64_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_66_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_66_chunk ( + CONSTRAINT constraint_66 CHECK ((("time" >= '2025-04-30 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-07 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_66_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_67_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_67_chunk ( + CONSTRAINT constraint_67 CHECK ((("time" >= '2025-05-07 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-14 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_67_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_69_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_69_chunk ( + CONSTRAINT constraint_69 CHECK ((("time" >= '2025-05-14 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-21 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_69_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_71_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_71_chunk ( + CONSTRAINT constraint_71 CHECK ((("time" >= '2025-05-21 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-05-28 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_71_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_73_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_73_chunk ( + CONSTRAINT constraint_73 CHECK ((("time" >= '2025-05-28 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-04 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_73_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_76_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_76_chunk ( + CONSTRAINT constraint_76 CHECK ((("time" >= '2025-06-04 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-11 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_76_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_78_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_78_chunk ( + CONSTRAINT constraint_78 CHECK ((("time" >= '2025-06-11 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-18 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_78_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_80_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_80_chunk ( + CONSTRAINT constraint_80 CHECK ((("time" >= '2025-06-18 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-06-25 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_80_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_81_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_81_chunk ( + CONSTRAINT constraint_81 CHECK ((("time" >= '2025-06-25 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-02 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_81_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_83_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_83_chunk ( + CONSTRAINT constraint_83 CHECK ((("time" >= '2025-07-02 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_83_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_86_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_86_chunk ( + CONSTRAINT constraint_86 CHECK ((("time" >= '2025-07-09 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-16 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_86_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_87_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_87_chunk ( + CONSTRAINT constraint_87 CHECK ((("time" >= '2025-07-16 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-23 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_87_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_89_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_89_chunk ( + CONSTRAINT constraint_89 CHECK ((("time" >= '2025-07-23 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-07-30 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_89_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_91_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_91_chunk ( + CONSTRAINT constraint_91 CHECK ((("time" >= '1970-01-07 16:00:00-08'::timestamp with time zone) AND ("time" < '1970-01-14 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_91_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_92_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_92_chunk ( + CONSTRAINT constraint_92 CHECK ((("time" >= '1970-09-02 17:00:00-07'::timestamp with time zone) AND ("time" < '1970-09-09 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_92_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_93_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_93_chunk ( + CONSTRAINT constraint_93 CHECK ((("time" >= '2023-03-01 16:00:00-08'::timestamp with time zone) AND ("time" < '2023-03-08 16:00:00-08'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_93_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_94_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_94_chunk ( + CONSTRAINT constraint_94 CHECK ((("time" >= '2025-07-30 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-06 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_94_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_96_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_96_chunk ( + CONSTRAINT constraint_96 CHECK ((("time" >= '2025-08-06 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-13 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_96_chunk OWNER TO well_app; + +-- +-- Name: _hyper_4_98_chunk; Type: TABLE; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE TABLE _timescaledb_internal._hyper_4_98_chunk ( + CONSTRAINT constraint_98 CHECK ((("time" >= '2025-08-13 17:00:00-07'::timestamp with time zone) AND ("time" < '2025-08-20 17:00:00-07'::timestamp with time zone))) +) +INHERITS (public.sensor_readings); + + +ALTER TABLE _timescaledb_internal._hyper_4_98_chunk OWNER TO well_app; + +-- +-- Name: alarms_voice; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.alarms_voice ( + index integer NOT NULL, + "time" timestamp with time zone, + call_session_id text, + initiated text, + answered text, + playback_started text, + playback_ended text, + hangup text +); + + +ALTER TABLE public.alarms_voice OWNER TO postgres; + +-- +-- Name: arti_reports; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.arti_reports ( + index integer NOT NULL, + content_raw text +); + + +ALTER TABLE public.arti_reports OWNER TO postgres; + +-- +-- Name: arti_reports_index_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.arti_reports ALTER COLUMN index ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.arti_reports_index_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: azureq_2_rabbit_instances; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.azureq_2_rabbit_instances ( + instance_id text NOT NULL, + last_heartbeat timestamp with time zone, + status text, + container_id text +); + + +ALTER TABLE public.azureq_2_rabbit_instances OWNER TO postgres; + +-- +-- Name: deployment_details; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.deployment_details ( + deployment_id integer NOT NULL, + beneficiary_id integer, + caretaker_id integer, + owner_id integer, + installer_id integer, + devices text, + address_street text, + address_city text, + address_zip text, + address_state text, + address_country text, + wifis text, + lat real, + lng real, + gps_age integer, + note text, + floor_plan text, + overlapps text +); + + +ALTER TABLE public.deployment_details OWNER TO postgres; + +-- +-- Name: TABLE deployment_details; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON TABLE public.deployment_details IS 'secret!'; + + +-- +-- Name: deployment_history_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.deployment_history_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.deployment_history_id_seq OWNER TO postgres; + +-- +-- Name: deployment_history; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.deployment_history ( + id integer DEFAULT nextval('public.deployment_history_id_seq'::regclass) NOT NULL, + deployment_id integer, + "time" real, + proximity text +); + + +ALTER TABLE public.deployment_history OWNER TO postgres; + +-- +-- Name: deployments; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.deployments ( + deployment_id integer NOT NULL, + time_edit real, + user_edit integer, + time_zone_s text, + persons integer, + gender integer, + race integer, + born integer, + pets integer, + context text, + alarm_details text +); + + +ALTER TABLE public.deployments OWNER TO postgres; + +-- +-- Name: deployments_deployment_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.deployments ALTER COLUMN deployment_id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.deployments_deployment_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: device_first_seen; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.device_first_seen ( + device_id integer NOT NULL, + first_seen_at timestamp with time zone NOT NULL +); + + +ALTER TABLE public.device_first_seen OWNER TO postgres; + +-- +-- Name: devices; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.devices ( + device_id integer NOT NULL, + device_mac text NOT NULL, + well_id integer, + description text, + location integer, + close_to text, + radar_threshold text, + fw_version text, + hw_version text, + ble_scan_period integer, + ble_scan_duration integer, + temperature_calib text, + humidity_calib text, + reporting_period_s integer, + reboot_time real, + led_schema text, + alert_details text, + other text, + group_id integer +); + + +ALTER TABLE public.devices OWNER TO postgres; + +-- +-- Name: COLUMN devices.description; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.devices.description IS 'User-defined name for the specific location of the device on a job site (e.g., Kitchen Under Sink).'; + + +-- +-- Name: devices_device_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.devices_device_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.devices_device_id_seq OWNER TO postgres; + +-- +-- Name: devices_device_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.devices_device_id_seq OWNED BY public.devices.device_id; + + +-- +-- Name: devices_last_reads; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.devices_last_reads ( + device_id integer NOT NULL, + temperature text, + humidity text, + light text, + pressure_blip text, + radar text, + smell text, + last_ssids text, + last_btles text +); + + +ALTER TABLE public.devices_last_reads OWNER TO postgres; + +-- +-- Name: disclaimers; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.disclaimers ( + id integer NOT NULL, + first_name text, + last_name text, + user_name text, + email text, + devices text, + date timestamp with time zone, + policy_version text +); + + +ALTER TABLE public.disclaimers OWNER TO postgres; + +-- +-- Name: disclaimers_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.disclaimers ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.disclaimers_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: format_contexts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.format_contexts ( + index integer NOT NULL, + voice_query text, + context text +); + + +ALTER TABLE public.format_contexts OWNER TO postgres; + +-- +-- Name: format_contexts_index_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.format_contexts_index_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.format_contexts_index_seq OWNER TO postgres; + +-- +-- Name: format_contexts_index_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.format_contexts_index_seq OWNED BY public.format_contexts.index; + + +-- +-- Name: jobs; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.jobs ( + job_id integer NOT NULL, + customer_name text, + mitigation_person_id integer, + key_person_name text, + key_person_mobile text, + key_person_email text, + address_street text, + address_city text, + address_zip text, + address_state text, + address_country text, + lat real, + lng real, + date_from timestamp with time zone DEFAULT CURRENT_TIMESTAMP, + date_to timestamp with time zone, + job_status text DEFAULT 'Active'::text NOT NULL, + devices text, + alerts_config jsonb, + created_at timestamp with time zone DEFAULT CURRENT_TIMESTAMP, + user_edit integer +); + + +ALTER TABLE public.jobs OWNER TO postgres; + +-- +-- Name: TABLE jobs; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON TABLE public.jobs IS 'Stores job information for the WellDrySense water damage mitigation product.'; + + +-- +-- Name: COLUMN jobs.customer_name; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.customer_name IS 'The name of the client for whom the job is being done.'; + + +-- +-- Name: COLUMN jobs.mitigation_person_id; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.mitigation_person_id IS 'The user (from person_details) responsible for the job.'; + + +-- +-- Name: COLUMN jobs.key_person_name; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.key_person_name IS 'The name of the primary contact person at the client site.'; + + +-- +-- Name: COLUMN jobs.date_to; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.date_to IS 'The date the job was stopped or archived.'; + + +-- +-- Name: COLUMN jobs.job_status; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.job_status IS 'Lifecycle status of the job: Active, Stopped, Archived.'; + + +-- +-- Name: COLUMN jobs.devices; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.devices IS 'A JSON array of device MAC addresses assigned to this job.'; + + +-- +-- Name: COLUMN jobs.alerts_config; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON COLUMN public.jobs.alerts_config IS 'JSON object storing alert thresholds, e.g., {"temp_abs_high": 30, "hum_rel_above": 15}.'; + + +-- +-- Name: jobs_job_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.jobs_job_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.jobs_job_id_seq OWNER TO postgres; + +-- +-- Name: jobs_job_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.jobs_job_id_seq OWNED BY public.jobs.job_id; + + +-- +-- Name: mobile_clients; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.mobile_clients ( + mqtt_id text NOT NULL, + user_name text, + user_id integer, + last_message_time bigint, + last_message text +); + + +ALTER TABLE public.mobile_clients OWNER TO postgres; + +-- +-- Name: mobile_clients_messages; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.mobile_clients_messages ( + id integer NOT NULL, + "time" bigint, + mqtt_id text, + message text, + function text +); + + +ALTER TABLE public.mobile_clients_messages OWNER TO postgres; + +-- +-- Name: mobile_clients_messages_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.mobile_clients_messages_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.mobile_clients_messages_id_seq OWNER TO postgres; + +-- +-- Name: mobile_clients_messages_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.mobile_clients_messages_id_seq OWNED BY public.mobile_clients_messages.id; + + +-- +-- Name: node_reds; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.node_reds ( + user_name text NOT NULL, + port integer, + status integer, + pid integer, + flow text, + last_activity integer, + time_on integer +); + + +ALTER TABLE public.node_reds OWNER TO postgres; + +-- +-- Name: node_reds_usage; Type: TABLE; Schema: public; Owner: well_app +-- + +CREATE TABLE public.node_reds_usage ( + index integer NOT NULL, + user_name text, + port integer, + pid integer, + time_on integer, + time_off integer +); + + +ALTER TABLE public.node_reds_usage OWNER TO well_app; + +-- +-- Name: node_reds_usage_index_seq; Type: SEQUENCE; Schema: public; Owner: well_app +-- + +CREATE SEQUENCE public.node_reds_usage_index_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.node_reds_usage_index_seq OWNER TO well_app; + +-- +-- Name: node_reds_usage_index_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: well_app +-- + +ALTER SEQUENCE public.node_reds_usage_index_seq OWNED BY public.node_reds_usage.index; + + +-- +-- Name: person_details; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.person_details ( + user_id integer NOT NULL, + role_ids text, + access_to_deployments text, + email text, + user_name text, + first_name text, + last_name text, + address_street text, + address_city text, + address_zip text, + address_state text, + address_country text, + time_edit real, + user_edit integer, + phone_number text, + picture text, + key text +); + + +ALTER TABLE public.person_details OWNER TO postgres; + +-- +-- Name: TABLE person_details; Type: COMMENT; Schema: public; Owner: postgres +-- + +COMMENT ON TABLE public.person_details IS 'secret!'; + + +-- +-- Name: person_details_person_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +ALTER TABLE public.person_details ALTER COLUMN user_id ADD GENERATED ALWAYS AS IDENTITY ( + SEQUENCE NAME public.person_details_person_id_seq + START WITH 34 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1 +); + + +-- +-- Name: queue_muncher_instances; Type: TABLE; Schema: public; Owner: well_app +-- + +CREATE TABLE public.queue_muncher_instances ( + instance_id character varying NOT NULL, + last_heartbeat timestamp without time zone, + status character varying, + container_id character varying +); + + +ALTER TABLE public.queue_muncher_instances OWNER TO well_app; + +-- +-- Name: race; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.race ( + race_id integer NOT NULL, + race_name text, + race_description text +); + + +ALTER TABLE public.race OWNER TO postgres; + +-- +-- Name: roles; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.roles ( + role_id integer NOT NULL, + role_name text, + role_description text +); + + +ALTER TABLE public.roles OWNER TO postgres; + +-- +-- Name: telnyx_events; Type: TABLE; Schema: public; Owner: well_app +-- + +CREATE TABLE public.telnyx_events ( + id integer NOT NULL, + "time" integer, + report text +); + + +ALTER TABLE public.telnyx_events OWNER TO well_app; + +-- +-- Name: temp_radar_readings; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.temp_radar_readings ( + "time" timestamp with time zone, + device_id integer, + absent double precision, + moving double precision, + stationary double precision, + "both" double precision, + m0 double precision, + m1 double precision, + m2 double precision, + m3 double precision, + m4 double precision, + m5 double precision, + m6 double precision, + m7 double precision, + m8 double precision, + s0 double precision, + s1 double precision, + s2 double precision, + s3 double precision, + s4 double precision, + s5 double precision, + s6 double precision, + s7 double precision, + s8 double precision +); + + +ALTER TABLE public.temp_radar_readings OWNER TO postgres; + +-- +-- Name: temp_sensor_readings; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.temp_sensor_readings ( + "time" timestamp with time zone, + device_id integer, + temperature double precision, + humidity double precision, + pressure double precision, + light double precision, + s0 double precision, + s1 double precision, + s2 double precision, + s3 double precision, + s4 double precision, + s5 double precision, + s6 double precision, + s7 double precision, + s8 double precision, + s9 double precision +); + + +ALTER TABLE public.temp_sensor_readings OWNER TO postgres; + +-- +-- Name: user_tokens; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.user_tokens ( + id integer NOT NULL, + username character varying(255) NOT NULL, + token_id character varying(36) NOT NULL, + expires_at timestamp without time zone NOT NULL, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + revoked boolean DEFAULT false +); + + +ALTER TABLE public.user_tokens OWNER TO postgres; + +-- +-- Name: user_tokens_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.user_tokens_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.user_tokens_id_seq OWNER TO postgres; + +-- +-- Name: user_tokens_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.user_tokens_id_seq OWNED BY public.user_tokens.id; + + +-- +-- Name: user_topics; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.user_topics ( + id integer NOT NULL, + user_name character varying(255) NOT NULL, + topic character varying(255) NOT NULL, + permission character varying(2) NOT NULL, + created_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + updated_at timestamp without time zone DEFAULT CURRENT_TIMESTAMP +); + + +ALTER TABLE public.user_topics OWNER TO postgres; + +-- +-- Name: user_topics_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.user_topics_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.user_topics_id_seq OWNER TO postgres; + +-- +-- Name: user_topics_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.user_topics_id_seq OWNED BY public.user_topics.id; + + +-- +-- Name: voice_contexts; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.voice_contexts ( + index integer NOT NULL, + voice_query text, + context text +); + + +ALTER TABLE public.voice_contexts OWNER TO postgres; + +-- +-- Name: voice_contexts_index_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.voice_contexts_index_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.voice_contexts_index_seq OWNER TO postgres; + +-- +-- Name: voice_contexts_index_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.voice_contexts_index_seq OWNED BY public.voice_contexts.index; + + +-- +-- Name: _hyper_4_100_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_100_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_102_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_102_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_104_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_104_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_106_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_106_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_108_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_108_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_110_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_110_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_112_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_112_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_115_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_115_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_116_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_116_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_118_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_118_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_120_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_120_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_122_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_122_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_124_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_124_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_126_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_126_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_23_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_23_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_24_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_24_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_25_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_25_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_26_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_26_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_27_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_27_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_28_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_28_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_29_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_29_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_30_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_30_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_31_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_31_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_32_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_32_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_33_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_33_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_34_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_34_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_35_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_35_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_36_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_36_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_37_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_37_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_38_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_38_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_39_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_39_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_40_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_40_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_41_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_41_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_42_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_42_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_43_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_43_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_44_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_44_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_46_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_46_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_47_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_47_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_50_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_50_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_51_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_51_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_54_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_54_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_56_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_56_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_58_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_58_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_60_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_60_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_62_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_62_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_64_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_64_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_66_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_66_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_67_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_67_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_69_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_69_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_71_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_71_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_73_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_73_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_76_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_76_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_78_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_78_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_80_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_80_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_81_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_81_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_83_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_83_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_86_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_86_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_87_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_87_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_89_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_89_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_91_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_91_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_92_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_92_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_93_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_93_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_94_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_94_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_96_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_96_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: _hyper_4_98_chunk mtype; Type: DEFAULT; Schema: _timescaledb_internal; Owner: well_app +-- + +ALTER TABLE ONLY _timescaledb_internal._hyper_4_98_chunk ALTER COLUMN mtype SET DEFAULT 0; + + +-- +-- Name: devices device_id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices ALTER COLUMN device_id SET DEFAULT nextval('public.devices_device_id_seq'::regclass); + + +-- +-- Name: format_contexts index; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.format_contexts ALTER COLUMN index SET DEFAULT nextval('public.format_contexts_index_seq'::regclass); + + +-- +-- Name: jobs job_id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.jobs ALTER COLUMN job_id SET DEFAULT nextval('public.jobs_job_id_seq'::regclass); + + +-- +-- Name: mobile_clients_messages id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mobile_clients_messages ALTER COLUMN id SET DEFAULT nextval('public.mobile_clients_messages_id_seq'::regclass); + + +-- +-- Name: node_reds_usage index; Type: DEFAULT; Schema: public; Owner: well_app +-- + +ALTER TABLE ONLY public.node_reds_usage ALTER COLUMN index SET DEFAULT nextval('public.node_reds_usage_index_seq'::regclass); + + +-- +-- Name: user_tokens id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.user_tokens ALTER COLUMN id SET DEFAULT nextval('public.user_tokens_id_seq'::regclass); + + +-- +-- Name: user_topics id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.user_topics ALTER COLUMN id SET DEFAULT nextval('public.user_topics_id_seq'::regclass); + + +-- +-- Name: voice_contexts index; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.voice_contexts ALTER COLUMN index SET DEFAULT nextval('public.voice_contexts_index_seq'::regclass); + + +-- +-- Name: alarms_voice alarms_voice_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.alarms_voice + ADD CONSTRAINT alarms_voice_pkey PRIMARY KEY (index); + + +-- +-- Name: arti_reports arti_reports_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.arti_reports + ADD CONSTRAINT arti_reports_pkey PRIMARY KEY (index); + + +-- +-- Name: azureq_2_rabbit_instances azureq_2_rabbit_instances_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.azureq_2_rabbit_instances + ADD CONSTRAINT azureq_2_rabbit_instances_pkey PRIMARY KEY (instance_id); + + +-- +-- Name: deployment_details deployment_details_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.deployment_details + ADD CONSTRAINT deployment_details_pkey PRIMARY KEY (deployment_id); + + +-- +-- Name: deployment_history deployment_history_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.deployment_history + ADD CONSTRAINT deployment_history_pkey PRIMARY KEY (id); + + +-- +-- Name: deployments deployments_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.deployments + ADD CONSTRAINT deployments_pkey PRIMARY KEY (deployment_id); + + +-- +-- Name: device_first_seen device_first_seen_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.device_first_seen + ADD CONSTRAINT device_first_seen_pkey PRIMARY KEY (device_id); + + +-- +-- Name: devices_last_reads devices_last_reads_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices_last_reads + ADD CONSTRAINT devices_last_reads_pkey PRIMARY KEY (device_id); + + +-- +-- Name: devices devices_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices + ADD CONSTRAINT devices_pkey PRIMARY KEY (device_id); + + +-- +-- Name: devices devices_well_mac_unique; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.devices + ADD CONSTRAINT devices_well_mac_unique UNIQUE (well_id, device_mac); + + +-- +-- Name: disclaimers disclaimers_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.disclaimers + ADD CONSTRAINT disclaimers_pkey PRIMARY KEY (id); + + +-- +-- Name: format_contexts format_contexts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.format_contexts + ADD CONSTRAINT format_contexts_pkey PRIMARY KEY (index); + + +-- +-- Name: telnyx_events id; Type: CONSTRAINT; Schema: public; Owner: well_app +-- + +ALTER TABLE ONLY public.telnyx_events + ADD CONSTRAINT id PRIMARY KEY (id); + + +-- +-- Name: jobs jobs_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.jobs + ADD CONSTRAINT jobs_pkey PRIMARY KEY (job_id); + + +-- +-- Name: mobile_clients_messages mobile_clients_messages_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mobile_clients_messages + ADD CONSTRAINT mobile_clients_messages_pkey PRIMARY KEY (id); + + +-- +-- Name: mobile_clients mobile_clients_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.mobile_clients + ADD CONSTRAINT mobile_clients_pkey PRIMARY KEY (mqtt_id); + + +-- +-- Name: node_reds node-reds_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.node_reds + ADD CONSTRAINT "node-reds_pkey" PRIMARY KEY (user_name); + + +-- +-- Name: node_reds_usage node_reds_usage_pkey; Type: CONSTRAINT; Schema: public; Owner: well_app +-- + +ALTER TABLE ONLY public.node_reds_usage + ADD CONSTRAINT node_reds_usage_pkey PRIMARY KEY (index); + + +-- +-- Name: person_details person_details_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.person_details + ADD CONSTRAINT person_details_pkey PRIMARY KEY (user_id); + + +-- +-- Name: queue_muncher_instances queue_muncher_instances_pkey; Type: CONSTRAINT; Schema: public; Owner: well_app +-- + +ALTER TABLE ONLY public.queue_muncher_instances + ADD CONSTRAINT queue_muncher_instances_pkey PRIMARY KEY (instance_id); + + +-- +-- Name: race race_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.race + ADD CONSTRAINT race_pkey PRIMARY KEY (race_id); + + +-- +-- Name: roles roles_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.roles + ADD CONSTRAINT roles_pkey PRIMARY KEY (role_id); + + +-- +-- Name: user_tokens unique_token_id; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.user_tokens + ADD CONSTRAINT unique_token_id UNIQUE (token_id); + + +-- +-- Name: user_topics unique_user_topic; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.user_topics + ADD CONSTRAINT unique_user_topic UNIQUE (user_name, topic); + + +-- +-- Name: user_tokens user_tokens_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.user_tokens + ADD CONSTRAINT user_tokens_pkey PRIMARY KEY (id); + + +-- +-- Name: user_topics user_topics_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.user_topics + ADD CONSTRAINT user_topics_pkey PRIMARY KEY (id); + + +-- +-- Name: voice_contexts voice_contexts_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.voice_contexts + ADD CONSTRAINT voice_contexts_pkey PRIMARY KEY (index); + + +-- +-- Name: _hyper_3_101_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_101_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_101_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_101_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_101_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_101_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_103_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_103_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_103_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_103_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_103_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_103_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_105_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_105_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_105_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_105_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_105_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_105_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_107_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_107_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_107_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_107_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_107_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_107_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_109_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_109_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_109_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_109_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_109_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_109_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_10_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_10_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_10_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_10_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_10_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_10_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_111_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_111_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_111_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_111_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_111_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_111_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_113_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_113_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_113_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_113_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_113_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_113_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_114_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_114_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_114_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_114_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_114_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_114_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_117_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_117_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_117_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_117_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_117_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_117_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_119_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_119_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_119_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_119_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_119_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_119_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_11_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_11_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_11_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_11_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_11_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_11_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_121_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_121_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_121_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_121_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_121_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_121_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_123_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_123_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_123_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_123_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_123_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_123_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_125_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_125_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_125_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_125_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_125_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_125_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_127_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_127_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_127_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_127_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_127_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_127_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_12_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_12_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_12_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_12_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_12_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_12_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_13_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_13_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_13_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_13_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_13_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_13_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_14_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_14_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_14_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_14_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_14_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_14_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_15_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_15_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_15_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_15_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_15_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_15_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_16_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_16_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_16_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_16_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_16_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_16_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_17_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_17_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_17_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_17_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_17_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_17_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_18_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_18_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_18_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_18_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_18_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_18_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_19_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_19_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_19_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_19_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_19_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_19_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_1_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_1_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_1_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_1_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_1_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_1_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_20_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_20_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_20_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_20_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_20_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_20_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_21_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_21_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_21_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_21_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_21_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_21_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_22_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_22_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_22_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_22_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_22_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_22_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_2_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_2_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_2_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_2_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_2_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_2_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_3_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_3_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_3_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_3_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_3_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_3_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_45_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_45_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_45_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_45_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_45_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_45_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_48_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_48_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_48_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_48_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_48_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_48_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_49_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_49_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_49_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_49_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_49_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_49_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_4_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_4_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_4_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_4_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_4_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_4_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_52_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_52_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_52_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_52_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_52_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_52_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_53_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_53_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_53_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_53_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_53_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_53_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_55_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_55_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_55_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_55_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_55_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_55_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_57_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_57_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_57_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_57_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_57_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_57_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_59_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_59_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_59_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_59_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_59_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_59_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_5_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_5_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_5_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_5_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_5_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_5_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_61_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_61_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_61_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_61_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_61_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_61_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_63_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_63_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_63_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_63_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_63_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_63_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_65_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_65_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_65_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_65_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_65_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_65_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_68_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_68_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_68_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_68_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_68_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_68_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_6_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_6_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_6_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_6_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_6_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_6_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_70_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_70_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_70_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_70_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_70_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_70_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_72_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_72_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_72_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_72_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_72_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_72_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_74_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_74_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_74_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_74_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_74_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_74_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_75_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_75_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_75_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_75_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_75_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_75_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_77_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_77_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_77_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_77_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_77_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_77_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_79_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_79_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_79_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_79_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_79_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_79_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_7_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_7_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_7_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_7_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_7_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_7_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_82_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_82_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_82_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_82_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_82_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_82_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_84_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_84_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_84_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_84_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_84_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_84_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_85_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_85_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_85_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_85_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_85_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_85_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_88_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_88_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_88_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_88_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_88_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_88_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_8_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_8_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_8_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_8_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_8_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_8_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_90_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_90_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_90_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_90_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_90_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_90_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_95_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_95_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_95_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_95_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_95_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_95_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_97_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_97_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_97_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_97_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_97_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_97_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_99_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_99_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_99_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_99_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_99_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_99_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_3_9_chunk_idx_unified_radar_device_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_9_chunk_idx_unified_radar_device_utc ON _timescaledb_internal._hyper_3_9_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_3_9_chunk_unified_radar_readings_time_idx_utc; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_3_9_chunk_unified_radar_readings_time_idx_utc ON _timescaledb_internal._hyper_3_9_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_100_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_100_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_100_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_100_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_100_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_100_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_102_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_102_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_102_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_102_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_102_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_102_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_104_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_104_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_104_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_104_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_104_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_104_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_106_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_106_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_106_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_106_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_106_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_106_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_108_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_108_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_108_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_108_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_108_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_108_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_110_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_110_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_110_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_110_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_110_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_110_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_112_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_112_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_112_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_112_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_112_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_112_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_115_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_115_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_115_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_115_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_115_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_115_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_116_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_116_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_116_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_116_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_116_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_116_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_118_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_118_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_118_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_118_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_118_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_118_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_120_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_120_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_120_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_120_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_120_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_120_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_122_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_122_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_122_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_122_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_122_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_122_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_124_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_124_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_124_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_124_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_124_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_124_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_126_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_126_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_126_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_126_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_126_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_126_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_23_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_23_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_23_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_23_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_23_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_23_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_24_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_24_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_24_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_24_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_24_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_24_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_25_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_25_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_25_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_25_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_25_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_25_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_26_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_26_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_26_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_26_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_26_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_26_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_27_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_27_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_27_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_27_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_27_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_27_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_28_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_28_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_28_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_28_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_28_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_28_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_29_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_29_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_29_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_29_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_29_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_29_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_30_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_30_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_30_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_30_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_30_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_30_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_31_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_31_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_31_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_31_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_31_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_31_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_32_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_32_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_32_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_32_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_32_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_32_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_33_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_33_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_33_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_33_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_33_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_33_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_34_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_34_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_34_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_34_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_34_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_34_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_35_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_35_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_35_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_35_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_35_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_35_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_36_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_36_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_36_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_36_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_36_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_36_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_37_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_37_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_37_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_37_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_37_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_37_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_38_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_38_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_38_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_38_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_38_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_38_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_39_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_39_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_39_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_39_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_39_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_39_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_40_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_40_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_40_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_40_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_40_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_40_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_41_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_41_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_41_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_41_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_41_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_41_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_42_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_42_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_42_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_42_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_42_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_42_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_43_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_43_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_43_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_43_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_43_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_43_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_44_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_44_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_44_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_44_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_44_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_44_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_46_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_46_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_46_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_46_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_46_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_46_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_47_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_47_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_47_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_47_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_47_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_47_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_50_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_50_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_50_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_50_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_50_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_50_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_51_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_51_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_51_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_51_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_51_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_51_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_54_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_54_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_54_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_54_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_54_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_54_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_56_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_56_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_56_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_56_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_56_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_56_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_58_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_58_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_58_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_58_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_58_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_58_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_60_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_60_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_60_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_60_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_60_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_60_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_62_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_62_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_62_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_62_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_62_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_62_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_64_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_64_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_64_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_64_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_64_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_64_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_66_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_66_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_66_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_66_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_66_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_66_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_67_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_67_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_67_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_67_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_67_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_67_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_69_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_69_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_69_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_69_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_69_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_69_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_71_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_71_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_71_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_71_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_71_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_71_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_73_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_73_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_73_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_73_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_73_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_73_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_76_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_76_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_76_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_76_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_76_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_76_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_78_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_78_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_78_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_78_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_78_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_78_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_80_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_80_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_80_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_80_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_80_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_80_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_81_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_81_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_81_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_81_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_81_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_81_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_83_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_83_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_83_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_83_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_83_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_83_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_86_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_86_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_86_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_86_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_86_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_86_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_87_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_87_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_87_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_87_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_87_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_87_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_89_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_89_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_89_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_89_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_89_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_89_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_91_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_91_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_91_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_91_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_91_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_91_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_92_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_92_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_92_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_92_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_92_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_92_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_93_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_93_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_93_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_93_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_93_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_93_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_94_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_94_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_94_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_94_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_94_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_94_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_96_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_96_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_96_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_96_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_96_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_96_chunk USING btree ("time" DESC); + + +-- +-- Name: _hyper_4_98_chunk_idx_unified_sensor_device; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_98_chunk_idx_unified_sensor_device ON _timescaledb_internal._hyper_4_98_chunk USING btree (device_id, "time" DESC); + + +-- +-- Name: _hyper_4_98_chunk_unified_sensor_readings_time_idx; Type: INDEX; Schema: _timescaledb_internal; Owner: well_app +-- + +CREATE INDEX _hyper_4_98_chunk_unified_sensor_readings_time_idx ON _timescaledb_internal._hyper_4_98_chunk USING btree ("time" DESC); + + +-- +-- Name: idx_jobs_job_status; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_jobs_job_status ON public.jobs USING btree (job_status); + + +-- +-- Name: idx_unified_radar_device_utc; Type: INDEX; Schema: public; Owner: well_app +-- + +CREATE INDEX idx_unified_radar_device_utc ON public.radar_readings USING btree (device_id, "time" DESC); + + +-- +-- Name: idx_unified_sensor_device; Type: INDEX; Schema: public; Owner: well_app +-- + +CREATE INDEX idx_unified_sensor_device ON public.sensor_readings USING btree (device_id, "time" DESC); + + +-- +-- Name: idx_user_tokens_token_id; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_user_tokens_token_id ON public.user_tokens USING btree (token_id); + + +-- +-- Name: idx_user_tokens_username; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_user_tokens_username ON public.user_tokens USING btree (username); + + +-- +-- Name: idx_user_topics_username; Type: INDEX; Schema: public; Owner: postgres +-- + +CREATE INDEX idx_user_topics_username ON public.user_topics USING btree (user_name); + + +-- +-- Name: unified_radar_readings_time_idx_utc; Type: INDEX; Schema: public; Owner: well_app +-- + +CREATE INDEX unified_radar_readings_time_idx_utc ON public.radar_readings USING btree ("time" DESC); + + +-- +-- Name: unified_sensor_readings_time_idx; Type: INDEX; Schema: public; Owner: well_app +-- + +CREATE INDEX unified_sensor_readings_time_idx ON public.sensor_readings USING btree ("time" DESC); + + +-- +-- Name: radar_readings ts_insert_blocker; Type: TRIGGER; Schema: public; Owner: well_app +-- + +CREATE TRIGGER ts_insert_blocker BEFORE INSERT ON public.radar_readings FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker(); + + +-- +-- Name: sensor_readings ts_insert_blocker; Type: TRIGGER; Schema: public; Owner: well_app +-- + +CREATE TRIGGER ts_insert_blocker BEFORE INSERT ON public.sensor_readings FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker(); + + +-- +-- Name: jobs jobs_mitigation_person_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.jobs + ADD CONSTRAINT jobs_mitigation_person_id_fkey FOREIGN KEY (mitigation_person_id) REFERENCES public.person_details(user_id); + + +-- +-- Name: jobs jobs_user_edit_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.jobs + ADD CONSTRAINT jobs_user_edit_fkey FOREIGN KEY (user_edit) REFERENCES public.person_details(user_id); + + +-- +-- Name: FUNCTION add_compression_policy(hypertable regclass, compress_after "any", if_not_exists boolean, schedule_interval interval, initial_start timestamp with time zone, timezone text, compress_created_before interval, hypercore_use_access_method boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_compression_policy(hypertable regclass, compress_after "any", if_not_exists boolean, schedule_interval interval, initial_start timestamp with time zone, timezone text, compress_created_before interval, hypercore_use_access_method boolean) TO well_app; + + +-- +-- Name: FUNCTION add_continuous_aggregate_policy(continuous_aggregate regclass, start_offset "any", end_offset "any", schedule_interval interval, if_not_exists boolean, initial_start timestamp with time zone, timezone text, include_tiered_data boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_continuous_aggregate_policy(continuous_aggregate regclass, start_offset "any", end_offset "any", schedule_interval interval, if_not_exists boolean, initial_start timestamp with time zone, timezone text, include_tiered_data boolean) TO well_app; + + +-- +-- Name: FUNCTION add_dimension(hypertable regclass, dimension _timescaledb_internal.dimension_info, if_not_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_dimension(hypertable regclass, dimension _timescaledb_internal.dimension_info, if_not_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION add_dimension(hypertable regclass, column_name name, number_partitions integer, chunk_time_interval anyelement, partitioning_func regproc, if_not_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_dimension(hypertable regclass, column_name name, number_partitions integer, chunk_time_interval anyelement, partitioning_func regproc, if_not_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION add_job(proc regproc, schedule_interval interval, config jsonb, initial_start timestamp with time zone, scheduled boolean, check_config regproc, fixed_schedule boolean, timezone text); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_job(proc regproc, schedule_interval interval, config jsonb, initial_start timestamp with time zone, scheduled boolean, check_config regproc, fixed_schedule boolean, timezone text) TO well_app; + + +-- +-- Name: FUNCTION add_reorder_policy(hypertable regclass, index_name name, if_not_exists boolean, initial_start timestamp with time zone, timezone text); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_reorder_policy(hypertable regclass, index_name name, if_not_exists boolean, initial_start timestamp with time zone, timezone text) TO well_app; + + +-- +-- Name: FUNCTION add_retention_policy(relation regclass, drop_after "any", if_not_exists boolean, schedule_interval interval, initial_start timestamp with time zone, timezone text, drop_created_before interval); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.add_retention_policy(relation regclass, drop_after "any", if_not_exists boolean, schedule_interval interval, initial_start timestamp with time zone, timezone text, drop_created_before interval) TO well_app; + + +-- +-- Name: FUNCTION alter_job(job_id integer, schedule_interval interval, max_runtime interval, max_retries integer, retry_period interval, scheduled boolean, config jsonb, next_start timestamp with time zone, if_exists boolean, check_config regproc, fixed_schedule boolean, initial_start timestamp with time zone, timezone text); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.alter_job(job_id integer, schedule_interval interval, max_runtime interval, max_retries integer, retry_period interval, scheduled boolean, config jsonb, next_start timestamp with time zone, if_exists boolean, check_config regproc, fixed_schedule boolean, initial_start timestamp with time zone, timezone text) TO well_app; + + +-- +-- Name: FUNCTION approximate_row_count(relation regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.approximate_row_count(relation regclass) TO well_app; + + +-- +-- Name: FUNCTION attach_tablespace(tablespace name, hypertable regclass, if_not_attached boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.attach_tablespace(tablespace name, hypertable regclass, if_not_attached boolean) TO well_app; + + +-- +-- Name: FUNCTION by_hash(column_name name, number_partitions integer, partition_func regproc); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.by_hash(column_name name, number_partitions integer, partition_func regproc) TO well_app; + + +-- +-- Name: FUNCTION by_range(column_name name, partition_interval anyelement, partition_func regproc); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.by_range(column_name name, partition_interval anyelement, partition_func regproc) TO well_app; + + +-- +-- Name: FUNCTION chunk_columnstore_stats(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.chunk_columnstore_stats(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION chunk_compression_stats(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.chunk_compression_stats(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION chunks_detailed_size(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.chunks_detailed_size(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION compress_chunk(uncompressed_chunk regclass, if_not_compressed boolean, recompress boolean, hypercore_use_access_method boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.compress_chunk(uncompressed_chunk regclass, if_not_compressed boolean, recompress boolean, hypercore_use_access_method boolean) TO well_app; + + +-- +-- Name: FUNCTION create_hypertable(relation regclass, dimension _timescaledb_internal.dimension_info, create_default_indexes boolean, if_not_exists boolean, migrate_data boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.create_hypertable(relation regclass, dimension _timescaledb_internal.dimension_info, create_default_indexes boolean, if_not_exists boolean, migrate_data boolean) TO well_app; + + +-- +-- Name: FUNCTION create_hypertable(relation regclass, time_column_name name, partitioning_column name, number_partitions integer, associated_schema_name name, associated_table_prefix name, chunk_time_interval anyelement, create_default_indexes boolean, if_not_exists boolean, partitioning_func regproc, migrate_data boolean, chunk_target_size text, chunk_sizing_func regproc, time_partitioning_func regproc); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.create_hypertable(relation regclass, time_column_name name, partitioning_column name, number_partitions integer, associated_schema_name name, associated_table_prefix name, chunk_time_interval anyelement, create_default_indexes boolean, if_not_exists boolean, partitioning_func regproc, migrate_data boolean, chunk_target_size text, chunk_sizing_func regproc, time_partitioning_func regproc) TO well_app; + + +-- +-- Name: FUNCTION decompress_chunk(uncompressed_chunk regclass, if_compressed boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.decompress_chunk(uncompressed_chunk regclass, if_compressed boolean) TO well_app; + + +-- +-- Name: FUNCTION delete_job(job_id integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.delete_job(job_id integer) TO well_app; + + +-- +-- Name: FUNCTION detach_tablespace(tablespace name, hypertable regclass, if_attached boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.detach_tablespace(tablespace name, hypertable regclass, if_attached boolean) TO well_app; + + +-- +-- Name: FUNCTION detach_tablespaces(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.detach_tablespaces(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION disable_chunk_skipping(hypertable regclass, column_name name, if_not_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.disable_chunk_skipping(hypertable regclass, column_name name, if_not_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION drop_chunks(relation regclass, older_than "any", newer_than "any", "verbose" boolean, created_before "any", created_after "any"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.drop_chunks(relation regclass, older_than "any", newer_than "any", "verbose" boolean, created_before "any", created_after "any") TO well_app; + + +-- +-- Name: FUNCTION enable_chunk_skipping(hypertable regclass, column_name name, if_not_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.enable_chunk_skipping(hypertable regclass, column_name name, if_not_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION hypertable_approximate_detailed_size(relation regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_approximate_detailed_size(relation regclass) TO well_app; + + +-- +-- Name: FUNCTION hypertable_approximate_size(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_approximate_size(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION hypertable_columnstore_stats(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_columnstore_stats(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION hypertable_compression_stats(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_compression_stats(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION hypertable_detailed_size(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_detailed_size(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION hypertable_index_size(index_name regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_index_size(index_name regclass) TO well_app; + + +-- +-- Name: FUNCTION hypertable_size(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.hypertable_size(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION interpolate(value real, prev record, next record); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.interpolate(value real, prev record, next record) TO well_app; + + +-- +-- Name: FUNCTION interpolate(value double precision, prev record, next record); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.interpolate(value double precision, prev record, next record) TO well_app; + + +-- +-- Name: FUNCTION interpolate(value smallint, prev record, next record); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.interpolate(value smallint, prev record, next record) TO well_app; + + +-- +-- Name: FUNCTION interpolate(value integer, prev record, next record); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.interpolate(value integer, prev record, next record) TO well_app; + + +-- +-- Name: FUNCTION interpolate(value bigint, prev record, next record); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.interpolate(value bigint, prev record, next record) TO well_app; + + +-- +-- Name: FUNCTION locf(value anyelement, prev anyelement, treat_null_as_missing boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.locf(value anyelement, prev anyelement, treat_null_as_missing boolean) TO well_app; + + +-- +-- Name: FUNCTION move_chunk(chunk regclass, destination_tablespace name, index_destination_tablespace name, reorder_index regclass, "verbose" boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.move_chunk(chunk regclass, destination_tablespace name, index_destination_tablespace name, reorder_index regclass, "verbose" boolean) TO well_app; + + +-- +-- Name: FUNCTION remove_compression_policy(hypertable regclass, if_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.remove_compression_policy(hypertable regclass, if_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION remove_continuous_aggregate_policy(continuous_aggregate regclass, if_not_exists boolean, if_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.remove_continuous_aggregate_policy(continuous_aggregate regclass, if_not_exists boolean, if_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION remove_reorder_policy(hypertable regclass, if_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.remove_reorder_policy(hypertable regclass, if_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION remove_retention_policy(relation regclass, if_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.remove_retention_policy(relation regclass, if_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION reorder_chunk(chunk regclass, index regclass, "verbose" boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.reorder_chunk(chunk regclass, index regclass, "verbose" boolean) TO well_app; + + +-- +-- Name: FUNCTION set_adaptive_chunking(hypertable regclass, chunk_target_size text, INOUT chunk_sizing_func regproc, OUT chunk_target_size bigint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.set_adaptive_chunking(hypertable regclass, chunk_target_size text, INOUT chunk_sizing_func regproc, OUT chunk_target_size bigint) TO well_app; + + +-- +-- Name: FUNCTION set_chunk_time_interval(hypertable regclass, chunk_time_interval anyelement, dimension_name name); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.set_chunk_time_interval(hypertable regclass, chunk_time_interval anyelement, dimension_name name) TO well_app; + + +-- +-- Name: FUNCTION set_integer_now_func(hypertable regclass, integer_now_func regproc, replace_if_exists boolean); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.set_integer_now_func(hypertable regclass, integer_now_func regproc, replace_if_exists boolean) TO well_app; + + +-- +-- Name: FUNCTION set_number_partitions(hypertable regclass, number_partitions integer, dimension_name name); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.set_number_partitions(hypertable regclass, number_partitions integer, dimension_name name) TO well_app; + + +-- +-- Name: FUNCTION set_partitioning_interval(hypertable regclass, partition_interval anyelement, dimension_name name); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.set_partitioning_interval(hypertable regclass, partition_interval anyelement, dimension_name name) TO well_app; + + +-- +-- Name: FUNCTION show_chunks(relation regclass, older_than "any", newer_than "any", created_before "any", created_after "any"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.show_chunks(relation regclass, older_than "any", newer_than "any", created_before "any", created_after "any") TO well_app; + + +-- +-- Name: FUNCTION show_tablespaces(hypertable regclass); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.show_tablespaces(hypertable regclass) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width smallint, ts smallint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width smallint, ts smallint) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width integer, ts integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width integer, ts integer) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width bigint, ts bigint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width bigint, ts bigint) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts date); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts date) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp without time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp without time zone) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp with time zone) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width smallint, ts smallint, "offset" smallint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width smallint, ts smallint, "offset" smallint) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width integer, ts integer, "offset" integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width integer, ts integer, "offset" integer) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width bigint, ts bigint, "offset" bigint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width bigint, ts bigint, "offset" bigint) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts date, origin date); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts date, origin date) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts date, "offset" interval); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts date, "offset" interval) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp without time zone, "offset" interval); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp without time zone, "offset" interval) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp without time zone, origin timestamp without time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp without time zone, origin timestamp without time zone) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp with time zone, "offset" interval); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp with time zone, "offset" interval) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp with time zone, origin timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp with time zone, origin timestamp with time zone) TO well_app; + + +-- +-- Name: FUNCTION time_bucket(bucket_width interval, ts timestamp with time zone, timezone text, origin timestamp with time zone, "offset" interval); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket(bucket_width interval, ts timestamp with time zone, timezone text, origin timestamp with time zone, "offset" interval) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width smallint, ts smallint, start smallint, finish smallint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width smallint, ts smallint, start smallint, finish smallint) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width integer, ts integer, start integer, finish integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width integer, ts integer, start integer, finish integer) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width bigint, ts bigint, start bigint, finish bigint); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width bigint, ts bigint, start bigint, finish bigint) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width interval, ts date, start date, finish date); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width interval, ts date, start date, finish date) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width interval, ts timestamp without time zone, start timestamp without time zone, finish timestamp without time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width interval, ts timestamp without time zone, start timestamp without time zone, finish timestamp without time zone) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width interval, ts timestamp with time zone, start timestamp with time zone, finish timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width interval, ts timestamp with time zone, start timestamp with time zone, finish timestamp with time zone) TO well_app; + + +-- +-- Name: FUNCTION time_bucket_gapfill(bucket_width interval, ts timestamp with time zone, timezone text, start timestamp with time zone, finish timestamp with time zone); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.time_bucket_gapfill(bucket_width interval, ts timestamp with time zone, timezone text, start timestamp with time zone, finish timestamp with time zone) TO well_app; + + +-- +-- Name: FUNCTION timescaledb_post_restore(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.timescaledb_post_restore() TO well_app; + + +-- +-- Name: FUNCTION timescaledb_pre_restore(); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.timescaledb_pre_restore() TO well_app; + + +-- +-- Name: FUNCTION first(anyelement, "any"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.first(anyelement, "any") TO well_app; + + +-- +-- Name: FUNCTION histogram(double precision, double precision, double precision, integer); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.histogram(double precision, double precision, double precision, integer) TO well_app; + + +-- +-- Name: FUNCTION last(anyelement, "any"); Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON FUNCTION public.last(anyelement, "any") TO well_app; + + +-- +-- Name: TABLE alarms_voice; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE public.alarms_voice TO well_app; + + +-- +-- Name: TABLE arti_reports; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.arti_reports TO well_app; + + +-- +-- Name: SEQUENCE arti_reports_index_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,USAGE ON SEQUENCE public.arti_reports_index_seq TO well_app; + + +-- +-- Name: TABLE azureq_2_rabbit_instances; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.azureq_2_rabbit_instances TO well_app; + + +-- +-- Name: TABLE deployment_details; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.deployment_details TO well_app; + + +-- +-- Name: SEQUENCE deployment_history_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,USAGE ON SEQUENCE public.deployment_history_id_seq TO well_app; + + +-- +-- Name: TABLE deployment_history; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE public.deployment_history TO well_app; + + +-- +-- Name: TABLE deployments; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.deployments TO well_app; + + +-- +-- Name: SEQUENCE deployments_deployment_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.deployments_deployment_id_seq TO well_app; + + +-- +-- Name: TABLE device_first_seen; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.device_first_seen TO well_app; + + +-- +-- Name: TABLE devices; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.devices TO well_app; + + +-- +-- Name: SEQUENCE devices_device_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.devices_device_id_seq TO well_app; + + +-- +-- Name: TABLE devices_last_reads; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.devices_last_reads TO well_app; + + +-- +-- Name: TABLE disclaimers; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.disclaimers TO well_app; + + +-- +-- Name: SEQUENCE disclaimers_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,USAGE ON SEQUENCE public.disclaimers_id_seq TO well_app; + + +-- +-- Name: SEQUENCE format_contexts_index_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,USAGE ON SEQUENCE public.format_contexts_index_seq TO well_app; + + +-- +-- Name: TABLE jobs; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.jobs TO well_app; + + +-- +-- Name: SEQUENCE jobs_job_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,USAGE ON SEQUENCE public.jobs_job_id_seq TO well_app; + + +-- +-- Name: TABLE mobile_clients; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.mobile_clients TO well_app; + + +-- +-- Name: TABLE mobile_clients_messages; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.mobile_clients_messages TO well_app; + + +-- +-- Name: SEQUENCE mobile_clients_messages_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.mobile_clients_messages_id_seq TO well_app; + + +-- +-- Name: TABLE node_reds; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,INSERT,DELETE,UPDATE ON TABLE public.node_reds TO well_app; + + +-- +-- Name: TABLE person_details; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.person_details TO well_app; + + +-- +-- Name: SEQUENCE person_details_person_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.person_details_person_id_seq TO well_app; + + +-- +-- Name: TABLE race; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.race TO well_app; + + +-- +-- Name: TABLE roles; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.roles TO well_app; + + +-- +-- Name: TABLE user_tokens; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.user_tokens TO well_app; + + +-- +-- Name: SEQUENCE user_tokens_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.user_tokens_id_seq TO well_app; + + +-- +-- Name: TABLE user_topics; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON TABLE public.user_topics TO well_app; + + +-- +-- Name: SEQUENCE user_topics_id_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT ALL ON SEQUENCE public.user_topics_id_seq TO well_app; + + +-- +-- Name: SEQUENCE voice_contexts_index_seq; Type: ACL; Schema: public; Owner: postgres +-- + +GRANT SELECT,USAGE ON SEQUENCE public.voice_contexts_index_seq TO well_app; + + +-- +-- Name: DEFAULT PRIVILEGES FOR SEQUENCES; Type: DEFAULT ACL; Schema: public; Owner: postgres +-- + +ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT,USAGE ON SEQUENCES TO well_app; + + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict nEgDhKD6B4nWaKSvB1ltprY3sHDNBEhrNLwMHP5zWu9gM4hHDn5zuiRnbZjRlH7 + diff --git a/test_device_list.sh b/test_device_list.sh new file mode 100644 index 0000000..11b7a0b --- /dev/null +++ b/test_device_list.sh @@ -0,0 +1,354 @@ +#!/bin/bash + +# ============================================================================== +# WellDrySense API Test Suite (Bash Version) +# Functionality: Exercises Job APIs including Create, List, Edit, Details, Weather, +# Sensor Data, and performs Database Cleanup. +# ============================================================================== + +# --- Configuration --- +# Load .env file if it exists +if [ -f .env ]; then + export $(cat .env | xargs) +fi + +# Defaults (can be overridden by env vars) +PORT="${PORT:-8002}" +BASE_URL="http://localhost:$PORT/api/well_api" +#API_USER="${API_USER:-jpeters}" +#API_PASSWORD="${API_PASSWORD:-WellJson}" +API_USER="${API_USER:-brankol}" +API_PASSWORD="${API_PASSWORD:-branko_2025!}" +DB_NAME="${DB_NAME:-wellnuo}" +DB_USER="${DB_USER:-postgres}" +DB_HOST="${DB_HOST:-localhost}" +DB_PORT="${DB_PORT:-5432}" +# DB_PASSWORD should be set in .env or exported + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Global Variables +TOKEN="" +USER_ID="" +JOB_ID="" + +# Check for jq +if ! command -v jq &> /dev/null; then + echo -e "${RED}Error: 'jq' is not installed. Please install it to run this script.${NC}" + exit 1 +fi + +echo -e "${BLUE}=== Setting up WellDrySense Test Suite on Port $PORT ===${NC}" + +# ============================================================================== +# Helper Functions +# ============================================================================== + +# Function to print section headers +print_header() { + echo -e "\n${BLUE}----------------------------------------------------------------${NC}" + echo -e "${BLUE}[Test] $1${NC}" +} + +# Function to perform a POST request +# Usage: perform_test "Test Name" "JSON_PAYLOAD_STRING" +perform_test() { + local test_name="$1" + local json_payload="$2" + + print_header "$test_name" + + # 1. Print Request + echo "# Request:" + echo "$json_payload" | jq '.' + + # 2. Convert JSON to Form Data for curl (flattening simple objects) + # Note: This simple conversion handles top-level keys. + # Complex nested JSON strings (like 'devices') need to be passed as strings in the input JSON. + local form_data="" + + # Extract keys and values and build form string + while IFS="=" read -r key value; do + if [ -n "$key" ]; then + # URL encode the value + encoded_value=$(printf '%s' "$value" | jq -sRr @uri) + if [ -z "$form_data" ]; then + form_data="${key}=${encoded_value}" + else + form_data="${form_data}&${key}=${encoded_value}" + fi + fi + done < <(echo "$json_payload" | jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]") + + # 3. Execute Request + response=$(curl -s -X POST "$BASE_URL" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "$form_data") + + # 4. Print Response + echo -e "\n# Response:" + if [ -z "$response" ]; then + echo "(Empty Response)" + echo -e "${RED}FAIL${NC}" + return 1 + else + echo "$response" | jq '.' 2>/dev/null || echo "$response" + fi + + # 5. Evaluate Pass/Fail based on "ok": 1 + ok_val=$(echo "$response" | jq -r '.ok // .status // 0') + + # Handle different response structures (some return {status: 200}, some {ok: 1}) + if [ "$ok_val" == "1" ] || [ "$ok_val" == "200" ] || [ "$ok_val" == "success" ]; then + echo -e "${GREEN}PASS${NC}" + + # Extract Job ID if this was the create step + if [ "$test_name" == "job_create" ]; then + JOB_ID=$(echo "$response" | jq -r '.job_id') + echo "-> Captured Job ID: $JOB_ID" + fi + return 0 + else + error_msg=$(echo "$response" | jq -r '.error // .message // "Unknown error"') + echo -e "${RED}FAIL: $error_msg${NC}" + return 1 + fi +} + +# ============================================================================== +# Test Execution +# ============================================================================== + +# 1. Login / Credentials +# ---------------------- +login_payload=$(jq -n \ + --arg fn "credentials" \ + --arg un "$API_USER" \ + --arg ps "$API_PASSWORD" \ + --arg cid "bash-suite" \ + --arg nonce "test-nonce" \ + '{function: $fn, user_name: $un, ps: $ps, clientId: $cid, nonce: $nonce}') + +print_header "Login" +echo "# Request:" +echo "$login_payload" | jq '.' + +# Special handling for login to capture token +response=$(curl -s -X POST "$BASE_URL" -d "function=credentials&user_name=$API_USER&ps=$API_PASSWORD&clientId=bash-suite&nonce=test-nonce") + +echo -e "\n# Response:" +echo "$response" | jq '.' + +TOKEN=$(echo "$response" | jq -r '.access_token // .data.access_token') +USER_ID=$(echo "$response" | jq -r '.user_id // .data.user_id') + +if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ]; then + echo -e "${GREEN}PASS${NC} (User ID: $USER_ID)" +else + echo -e "${RED}FATAL: Login failed. Check credentials.${NC}" + exit 1 +fi + +# # 2. Create Job +# # ---------------------- +# # Note: We pass JSON strings for complex fields like 'devices' and 'alerts_config' +# devices_json='[{"mac": "TEST_MAC_VIRTUAL", "location": "Lab"}]' +# alerts_json='{"temp_high": 30}' + +# create_payload=$(jq -n \ +# --arg fn "job_create" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg cn "TEST_SUITE_CUSTOMER_BASH" \ +# --arg as "123 Bash Script Ln" \ +# --arg ac "Shellville" \ +# --arg dev "$devices_json" \ +# --arg lat "34.05" \ +# --arg lng "-118.25" \ +# '{function: $fn, user_name: $un, token: $tk, customer_name: $cn, address_street: $as, address_city: $ac, devices: $dev, lat: $lat, lng: $lng}') + +# perform_test "job_create" "$create_payload" || exit 1 + +# # 3. Job List +# # ---------------------- +# list_payload=$(jq -n \ +# --arg fn "job_list" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# '{function: $fn, user_name: $un, token: $tk}') + +# perform_test "job_list" "$list_payload" + +# # 3. Job List 2 (with added search) +# # ---------------------- +# list_payload=$(jq -n \ +# --arg fn "job_list2" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg sch "" \ +# '{function: $fn, user_name: $un, token: $tk, search: $sch}') + +# perform_test "job_list2" "$list_payload" + +# # 4. Job Details +# # ---------------------- +# details_payload=$(jq -n \ +# --arg fn "job_details" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid}') + +# perform_test "job_details" "$details_payload" + +# # 5. Job Edit (Stop Job) +# # ---------------------- +# edit_payload=$(jq -n \ +# --arg fn "job_edit" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# --arg st "Stopped" \ +# --arg dt "2025-12-31T23:59:59" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid, job_status: $st, date_to: $dt}') + +# perform_test "job_edit" "$edit_payload" + +# # 6. Available Devices (two versions, with direct SQL and via GetProximityList) +# # ---------------------- +# avail_payload=$(jq -n \ +# --arg fn "job_available_devices" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# '{function: $fn, user_name: $un, token: $tk}') + +# perform_test "job_available_devices" "$avail_payload" + +# # 6. Available Devices (Alternative Test using job_available_devices2, which is using GetProximityList) +# avail_payload=$(jq -n \ +# --arg fn "job_available_devices2" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# '{function: $fn, user_name: $un, token: $tk}') + +# perform_test "job_available_devices2" "$avail_payload" + +# # 6. Available Devices(made reusing job_user_all_devices2, adding filter and search) +# avail_payload=$(jq -n \ +# --arg fn "job_devices" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg fl "all" \ +# --arg sch "" \ +# '{function: $fn, user_name: $un, token: $tk, filter: $fl, search: $sch}') + +# perform_test "job_devices" "$avail_payload" + +# # 7. Job Weather +# # ---------------------- +# weather_payload=$(jq -n \ +# --arg fn "job_weather" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid}') + +# perform_test "job_weather" "$weather_payload" + +# # 8. Job Sensor Bucketed Data (New Test) +# # ---------------------- +# # Using dynamic dates for the test +# DATE_FROM=$(date +%Y-%m-%d) +# DATE_TO=$(date +%Y-%m-%d) + +# sensor_payload=$(jq -n \ +# --arg fn "get_job_sensor_bucketed_data" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg jid "$JOB_ID" \ +# --arg sens "temperature" \ +# --arg dt "$DATE_FROM" \ +# --arg dtt "$DATE_TO" \ +# --arg bs "15m" \ +# '{function: $fn, user_name: $un, token: $tk, job_id: $jid, sensor: $sens, date: $dt, to_date: $dtt, bucket_size: $bs}') + +# perform_test "get_job_sensor_bucketed_data" "$sensor_payload" + +# 9. device_list (New Test) +# based on name=devices_list +# &token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvYnN0ZXIiLCJleHAiOjE3NjQ3OTYwNzh9.t9rKVDRpSYOZGJMm2G0HYKSOOeaLypKwRGIJHehJBFE +# &user_id=32 +# &user_name=robster +# &first=0 +# &last=1000 +# &privileges=-1 +# +# robster test +device_list_payload=$(jq -n \ + --arg fn "devices_list" \ + --arg fid "0" \ + --arg lid "1000" \ + --arg priv "-1" \ + --arg uid "32" \ + --arg un "robster" \ + --arg tk "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvYnN0ZXIiLCJleHAiOjE3NjQ3OTYwNzh9.t9rKVDRpSYOZGJMm2G0HYKSOOeaLypKwRGIJHehJBFE" \ + '{function: $fn, name: $fn, user_id: $uid, user_name: $un, token: $tk, first: $fid, last: $lid, privileges: $priv}') +perform_test "devices_list" "$device_list_payload" +# brankol test +device_list_payload=$(jq -n \ + --arg fn "devices_list" \ + --arg fid "0" \ + --arg lid "1000" \ + --arg priv "-1" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + '{function: $fn, name: $fn, user_name: $un, token: $tk, first: $fid, last: $lid, privileges: $priv}') + # --arg uid "32" \ + # --arg un "robster" \ + # --arg tk "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvYnN0ZXIiLCJleHAiOjE3NjQ3OTYwNzh9.t9rKVDRpSYOZGJMm2G0HYKSOOeaLypKwRGIJHehJBFE" \ + #'{name: $fn, user_id: $uid, user_name: $un, token: $tk, first: $fid, last: $lid, privileges: $priv}') +perform_test "devices_list" "$device_list_payload" + + +# device_list_payload=$(jq -n \ +# --arg fn "devices_list" \ +# --arg un "$API_USER" \ +# --arg tk "$TOKEN" \ +# --arg fid "0" \ +# --arg lid "1000" \ +# --arg priv "-1" \ +# '{function: $fn, user_name: $un, token: $tk, first: $fid, last: $lid, privileges: $priv}') +# perform_test "devices_list" "$device_list_payload" + +# # ============================================================================== +# # Cleanup +# # ============================================================================== +# print_header "Cleanup" + +# if [ -n "$JOB_ID" ]; then +# echo "-> Deleting Job ID: $JOB_ID from database..." + +# # Use PGPASSWORD for non-interactive auth if set +# export PGPASSWORD="${DB_PASSWORD}" + +# psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "DELETE FROM public.jobs WHERE job_id = $JOB_ID;" > /dev/null 2>&1 + +# if [ $? -eq 0 ]; then +# echo -e "${GREEN}Cleanup successful. Database restored.${NC}" +# else +# echo -e "${RED}Cleanup failed. Please manually delete job_id $JOB_ID from public.jobs.${NC}" +# echo "Command attempted: psql -h $DB_HOST -U $DB_USER -d $DB_NAME -c \"DELETE FROM public.jobs WHERE job_id = $JOB_ID;\"" +# fi +# else +# echo "No Job ID created, skipping cleanup." +# fi + + +echo -e "\n${BLUE}=== Test Suite Finished ===${NC}" +# ============================================================================== +# well-api.py modifications to support WellDrySense API on port 1998 +# ============================================================================== \ No newline at end of file diff --git a/test_welldrysense_api.py b/test_welldrysense_api.py new file mode 100644 index 0000000..fe0e10c --- /dev/null +++ b/test_welldrysense_api.py @@ -0,0 +1,261 @@ +#!/usr/bin/env python3 +import unittest +import requests +import os +import json +import psycopg2 +import sys +import time +from dotenv import load_dotenv + +# --- Configuration --- +# Load environment variables from .env file in the same directory +load_dotenv() + +# Configuration with fallbacks +PORT = os.getenv('PORT', '8002') +BASE_URL = f"http://localhost:{PORT}" +API_USER = os.getenv('API_USER', 'jpeters') +API_PASSWORD = os.getenv('API_PASSWORD', 'WellJson') + +# ANSI Colors for better readability +GREEN = '\033[92m' +RED = '\033[91m' +YELLOW = '\033[93m' +RESET = '\033[0m' + +class TestWellDrySenseAPI(unittest.TestCase): + """ + Test suite for WellDrySense. + Ensures zero-impact on existing data by cleaning up created records. + """ + + token = None + user_id = None + job_id_to_test = None + db_conn_params = {} + + @classmethod + def setUpClass(cls): + print(f"\n{GREEN}=== Setting up WellDrySense Test Suite on Port {PORT} ==={RESET}") + + # Setup DB Params + cls.db_conn_params = { + 'dbname': os.getenv('DB_NAME'), + 'user': os.getenv('DB_USER'), + 'password': os.getenv('DB_PASSWORD'), + 'host': os.getenv('DB_HOST'), + 'port': os.getenv('DB_PORT') + } + + # Authenticate + print(f"-> Logging in as: {API_USER}...") + url = f"{BASE_URL}/api/well_api" + payload = { + "function": "credentials", + "user_name": API_USER, + "ps": API_PASSWORD, + "clientId": "test-suite", + "nonce": "test-nonce" + } + + try: + response = requests.post(url, data=payload) + if response.status_code != 200: + print(f"{RED}FATAL: Login failed. Status: {response.status_code}{RESET}") + print(f"Response: {response.text}") + sys.exit(1) + + data = response.json() + + # Handle different response structures + if 'access_token' in data: + cls.token = data['access_token'] + cls.user_id = data.get('user_id') + elif 'data' in data and 'access_token' in data['data']: + cls.token = data['data']['access_token'] + cls.user_id = data['data'].get('user_id') + else: + print(f"{RED}FATAL: Token not found in response.{RESET}") + sys.exit(1) + + print(f"{GREEN}-> Login successful. User ID: {cls.user_id}{RESET}") + + except requests.exceptions.ConnectionError: + print(f"{RED}FATAL: Could not connect to {BASE_URL}. Ensure well-api.py is running.{RESET}") + sys.exit(1) + + @classmethod + def tearDownClass(cls): + print(f"\n{GREEN}=== Tearing Down Test Suite ==={RESET}") + if cls.job_id_to_test: + print(f"-> Cleaning up Job ID: {cls.job_id_to_test}...") + conn = None + try: + conn = psycopg2.connect(**cls.db_conn_params) + with conn.cursor() as cur: + cur.execute("DELETE FROM public.jobs WHERE job_id = %s;", (cls.job_id_to_test,)) + conn.commit() + print(f"{GREEN}-> Cleanup successful. Database restored.{RESET}") + except Exception as e: + print(f"{RED}CRITICAL: DB Cleanup failed. Manually delete job {cls.job_id_to_test}. Error: {e}{RESET}") + finally: + if conn: conn.close() + else: + print("-> No job created, skipping cleanup.") + + def _post_api(self, form_data): + """Helper to send authenticated POST requests""" + form_data['user_name'] = API_USER + form_data['token'] = self.token + + try: + response = requests.post(f"{BASE_URL}/api/well_api", data=form_data) + self.assertEqual(response.status_code, 200, f"API HTTP Error {response.status_code}: {response.text}") + + try: + json_resp = response.json() + if 'data' in json_resp and 'status' in json_resp: + return json_resp['data'] + return json_resp + except json.JSONDecodeError: + self.fail(f"API returned invalid JSON: {response.text}") + except requests.exceptions.ConnectionError: + self.fail("Connection refused. API server is down.") + + # --- TESTS --- + + def test_01_create_job(self): + """Test creating a new job""" + print("\n[Test] job_create") + payload = { + "function": "job_create", + "customer_name": "TEST_SUITE_CUSTOMER", + "address_street": "123 Python Way", + "address_city": "Codeville", + "address_state": "CA", + "address_country": "USA", + "lat": 34.05, + "lng": -118.25, + "key_person_name": "Test Runner", + "key_person_email": "test@wellnuo.com", + "devices": json.dumps([{"mac": "TEST_MAC_VIRTUAL", "location": "Lab"}]), + "alerts_config": json.dumps({"temp_high": 30}) + } + + data = self._post_api(payload) + self.assertEqual(data.get('ok'), 1, f"Job creation failed: {data.get('error')}") + self.assertIn('job_id', data) + self.__class__.job_id_to_test = data['job_id'] + print(f"-> Job created with ID: {self.job_id_to_test}") + + def test_02_job_list(self): + """Test retrieving the job list""" + print("\n[Test] job_list") + payload = {"function": "job_list"} + data = self._post_api(payload) + + self.assertEqual(data.get('ok'), 1, f"List failed: {data.get('error')}") + self.assertIn('jobs', data) + + found = any(j.get('job_id') == self.job_id_to_test for j in data['jobs']) + self.assertTrue(found, f"Created Job ID {self.job_id_to_test} not found in job_list") + + def test_03_job_details(self): + """Test retrieving single job details""" + print("\n[Test] job_details") + if not self.job_id_to_test: self.skipTest("No job ID available") + + payload = {"function": "job_details", "job_id": self.job_id_to_test} + data = self._post_api(payload) + + self.assertEqual(data.get('ok'), 1, f"Details failed: {data.get('error')}") + self.assertEqual(data['details']['customer_name'], "TEST_SUITE_CUSTOMER") + + # Verify JSON parsing of devices + devices = data['details'].get('devices') + if isinstance(devices, str): devices = json.loads(devices) + self.assertEqual(devices[0]['mac'], "TEST_MAC_VIRTUAL") + + def test_04_job_edit(self): + """Test updating a job (Stop the job)""" + print("\n[Test] job_edit") + if not self.job_id_to_test: self.skipTest("No job ID available") + + payload = { + "function": "job_edit", + "job_id": self.job_id_to_test, + "customer_name": "UPDATED_CUSTOMER_NAME", + "job_status": "Stopped", + "date_to": "2025-12-31T23:59:59" + } + data = self._post_api(payload) + self.assertEqual(data.get('ok'), 1, f"Edit failed: {data.get('error')}") + + # Verify + v_payload = {"function": "job_details", "job_id": self.job_id_to_test} + v_data = self._post_api(v_payload) + self.assertEqual(v_data['details']['customer_name'], "UPDATED_CUSTOMER_NAME") + self.assertEqual(v_data['details']['job_status'], "Stopped") + + def test_05_available_devices(self): + """Test fetching available devices""" + print("\n[Test] job_available_devices") + payload = {"function": "job_available_devices"} + data = self._post_api(payload) + self.assertEqual(data.get('ok'), 1, f"Available devices failed: {data.get('error')}") + self.assertIsInstance(data['devices'], list) + + def test_06_job_weather(self): + """Test fetching weather""" + print("\n[Test] job_weather") + if not self.job_id_to_test: self.skipTest("No job ID available") + + #if not os.getenv('WEATHER_API_KEY'): print(f"{YELLOW}-> Warning: WEATHER_API_KEY not found in .env{RESET}") + + payload = {"function": "job_weather", "job_id": self.job_id_to_test} + data = self._post_api(payload) + + if data.get('ok') == 0: + print(f"-> Weather API returned error (Expected if key invalid): {data.get('error')}") + else: + self.assertIn('weather', data) + print(f"-> Weather received: {data['weather']}") + + def test_07_job_sensor_data(self): + """Test fetching bucketed sensor data for a job""" + print("\n[Test] get_job_sensor_bucketed_data") + if not self.job_id_to_test: self.skipTest("No job ID available") + + # Use a date range likely to cover "now" or recent past for testing + today = datetime.datetime.now().strftime("%Y-%m-%d") + + payload = { + "function": "get_job_sensor_bucketed_data", + "job_id": self.job_id_to_test, + "sensor": "temperature", + "date": today, + "bucket_size": "1h" + } + + data = self._post_api(payload) + + self.assertEqual(data.get('ok'), 1, f"Sensor data fetch failed: {data.get('error')}") + self.assertIn('chart_data', data) + self.assertIn('units', data) + self.assertIn('time_zone', data) + + # Since we created the job with a virtual MAC in test_01, + # we expect chart_data to contain an entry for that device (even if data list is empty) + # Note: The virtual MAC likely won't exist in the 'devices' table unless pre-seeded, + # so the API might skip it or return empty. We check structure primarily. + if data['chart_data']: + print(f"-> Retrieved data for {len(data['chart_data'])} locations") + first_loc = data['chart_data'][0] + self.assertIn('name', first_loc) + self.assertIn('data', first_loc) + else: + print("-> No device data found (Expected if test MAC is not in DB)") + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test_welldrysense_api.sh b/test_welldrysense_api.sh new file mode 100644 index 0000000..a3c9be6 --- /dev/null +++ b/test_welldrysense_api.sh @@ -0,0 +1,305 @@ +#!/bin/bash + +# ============================================================================== +# WellDrySense API Test Suite (Bash Version) +# Functionality: Exercises Job APIs including Create, List, Edit, Details, Weather, +# Sensor Data, and performs Database Cleanup. +# ============================================================================== + +# --- Configuration --- +# Load .env file if it exists +if [ -f .env ]; then + export $(cat .env | xargs) +fi + +# Defaults (can be overridden by env vars) +PORT="${PORT:-8002}" +BASE_URL="http://localhost:$PORT/api/well_api" +API_USER="${API_USER:-jpeters}" +API_PASSWORD="${API_PASSWORD:-WellJson}" +DB_NAME="${DB_NAME:-wellnuo}" +DB_USER="${DB_USER:-postgres}" +DB_HOST="${DB_HOST:-localhost}" +DB_PORT="${DB_PORT:-5432}" +# DB_PASSWORD should be set in .env or exported + +# Colors +GREEN='\033[0;32m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Global Variables +TOKEN="" +USER_ID="" +JOB_ID="" + +# Check for jq +if ! command -v jq &> /dev/null; then + echo -e "${RED}Error: 'jq' is not installed. Please install it to run this script.${NC}" + exit 1 +fi + +echo -e "${BLUE}=== Setting up WellDrySense Test Suite on Port $PORT ===${NC}" + +# ============================================================================== +# Helper Functions +# ============================================================================== + +# Function to print section headers +print_header() { + echo -e "\n${BLUE}----------------------------------------------------------------${NC}" + echo -e "${BLUE}[Test] $1${NC}" +} + +# Function to perform a POST request +# Usage: perform_test "Test Name" "JSON_PAYLOAD_STRING" +perform_test() { + local test_name="$1" + local json_payload="$2" + + print_header "$test_name" + + # 1. Print Request + echo "# Request:" + echo "$json_payload" | jq '.' + + # 2. Convert JSON to Form Data for curl (flattening simple objects) + # Note: This simple conversion handles top-level keys. + # Complex nested JSON strings (like 'devices') need to be passed as strings in the input JSON. + local form_data="" + + # Extract keys and values and build form string + while IFS="=" read -r key value; do + if [ -n "$key" ]; then + # URL encode the value + encoded_value=$(printf '%s' "$value" | jq -sRr @uri) + if [ -z "$form_data" ]; then + form_data="${key}=${encoded_value}" + else + form_data="${form_data}&${key}=${encoded_value}" + fi + fi + done < <(echo "$json_payload" | jq -r "to_entries|map(\"\(.key)=\(.value)\")|.[]") + + # 3. Execute Request + response=$(curl -s -X POST "$BASE_URL" \ + -H "Content-Type: application/x-www-form-urlencoded" \ + -d "$form_data") + + # 4. Print Response + echo -e "\n# Response:" + if [ -z "$response" ]; then + echo "(Empty Response)" + echo -e "${RED}FAIL${NC}" + return 1 + else + echo "$response" | jq '.' 2>/dev/null || echo "$response" + fi + + # 5. Evaluate Pass/Fail based on "ok": 1 + ok_val=$(echo "$response" | jq -r '.ok // .status // 0') + + # Handle different response structures (some return {status: 200}, some {ok: 1}) + if [ "$ok_val" == "1" ] || [ "$ok_val" == "200" ] || [ "$ok_val" == "success" ]; then + echo -e "${GREEN}PASS${NC}" + + # Extract Job ID if this was the create step + if [ "$test_name" == "job_create" ]; then + JOB_ID=$(echo "$response" | jq -r '.job_id') + echo "-> Captured Job ID: $JOB_ID" + fi + return 0 + else + error_msg=$(echo "$response" | jq -r '.error // .message // "Unknown error"') + echo -e "${RED}FAIL: $error_msg${NC}" + return 1 + fi +} + +# ============================================================================== +# Test Execution +# ============================================================================== + +# 1. Login / Credentials +# ---------------------- +login_payload=$(jq -n \ + --arg fn "credentials" \ + --arg un "$API_USER" \ + --arg ps "$API_PASSWORD" \ + --arg cid "bash-suite" \ + --arg nonce "test-nonce" \ + '{function: $fn, user_name: $un, ps: $ps, clientId: $cid, nonce: $nonce}') + +print_header "Login" +echo "# Request:" +echo "$login_payload" | jq '.' + +# Special handling for login to capture token +response=$(curl -s -X POST "$BASE_URL" -d "function=credentials&user_name=$API_USER&ps=$API_PASSWORD&clientId=bash-suite&nonce=test-nonce") + +echo -e "\n# Response:" +echo "$response" | jq '.' + +TOKEN=$(echo "$response" | jq -r '.access_token // .data.access_token') +USER_ID=$(echo "$response" | jq -r '.user_id // .data.user_id') + +if [ -n "$TOKEN" ] && [ "$TOKEN" != "null" ]; then + echo -e "${GREEN}PASS${NC} (User ID: $USER_ID)" +else + echo -e "${RED}FATAL: Login failed. Check credentials.${NC}" + exit 1 +fi + +# 2. Create Job +# ---------------------- +# Note: We pass JSON strings for complex fields like 'devices' and 'alerts_config' +devices_json='[{"mac": "TEST_MAC_VIRTUAL", "location": "Lab"}]' +alerts_json='{"temp_high": 30}' + +create_payload=$(jq -n \ + --arg fn "job_create" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg cn "TEST_SUITE_CUSTOMER_BASH" \ + --arg as "123 Bash Script Ln" \ + --arg ac "Shellville" \ + --arg dev "$devices_json" \ + --arg lat "34.05" \ + --arg lng "-118.25" \ + '{function: $fn, user_name: $un, token: $tk, customer_name: $cn, address_street: $as, address_city: $ac, devices: $dev, lat: $lat, lng: $lng}') + +perform_test "job_create" "$create_payload" || exit 1 + +# 3. Job List +# ---------------------- +list_payload=$(jq -n \ + --arg fn "job_list" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + '{function: $fn, user_name: $un, token: $tk}') + +perform_test "job_list" "$list_payload" + +# 3. Job List 2 (with added search) +# ---------------------- +list_payload=$(jq -n \ + --arg fn "job_list2" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg sch "" \ + '{function: $fn, user_name: $un, token: $tk, search: $sch}') + +perform_test "job_list2" "$list_payload" + +# 4. Job Details +# ---------------------- +details_payload=$(jq -n \ + --arg fn "job_details" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg jid "$JOB_ID" \ + '{function: $fn, user_name: $un, token: $tk, job_id: $jid}') + +perform_test "job_details" "$details_payload" + +# 5. Job Edit (Stop Job) +# ---------------------- +edit_payload=$(jq -n \ + --arg fn "job_edit" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg jid "$JOB_ID" \ + --arg st "Stopped" \ + --arg dt "2025-12-31T23:59:59" \ + '{function: $fn, user_name: $un, token: $tk, job_id: $jid, job_status: $st, date_to: $dt}') + +perform_test "job_edit" "$edit_payload" + +# 6. Available Devices (two versions, with direct SQL and via GetProximityList) +# ---------------------- +avail_payload=$(jq -n \ + --arg fn "job_available_devices" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + '{function: $fn, user_name: $un, token: $tk}') + +perform_test "job_available_devices" "$avail_payload" + +# 6. Available Devices (Alternative Test using job_available_devices2, which is using GetProximityList) +avail_payload=$(jq -n \ + --arg fn "job_available_devices2" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + '{function: $fn, user_name: $un, token: $tk}') + +perform_test "job_available_devices2" "$avail_payload" + +# 6. Available Devices(made reusing job_user_all_devices2, adding filter and search) +avail_payload=$(jq -n \ + --arg fn "job_devices" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg fl "all" \ + --arg sch "" \ + '{function: $fn, user_name: $un, token: $tk, filter: $fl, search: $sch}') + +perform_test "job_devices" "$avail_payload" + +# 7. Job Weather +# ---------------------- +weather_payload=$(jq -n \ + --arg fn "job_weather" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg jid "$JOB_ID" \ + '{function: $fn, user_name: $un, token: $tk, job_id: $jid}') + +perform_test "job_weather" "$weather_payload" + +# 8. Job Sensor Bucketed Data (New Test) +# ---------------------- +# Using dynamic dates for the test +DATE_FROM=$(date +%Y-%m-%d) +DATE_TO=$(date +%Y-%m-%d) + +sensor_payload=$(jq -n \ + --arg fn "get_job_sensor_bucketed_data" \ + --arg un "$API_USER" \ + --arg tk "$TOKEN" \ + --arg jid "$JOB_ID" \ + --arg sens "temperature" \ + --arg dt "$DATE_FROM" \ + --arg dtt "$DATE_TO" \ + --arg bs "15m" \ + '{function: $fn, user_name: $un, token: $tk, job_id: $jid, sensor: $sens, date: $dt, to_date: $dtt, bucket_size: $bs}') + +perform_test "get_job_sensor_bucketed_data" "$sensor_payload" + +# ============================================================================== +# Cleanup +# ============================================================================== +print_header "Cleanup" + +if [ -n "$JOB_ID" ]; then + echo "-> Deleting Job ID: $JOB_ID from database..." + + # Use PGPASSWORD for non-interactive auth if set + export PGPASSWORD="${DB_PASSWORD}" + + psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "DELETE FROM public.jobs WHERE job_id = $JOB_ID;" > /dev/null 2>&1 + + if [ $? -eq 0 ]; then + echo -e "${GREEN}Cleanup successful. Database restored.${NC}" + else + echo -e "${RED}Cleanup failed. Please manually delete job_id $JOB_ID from public.jobs.${NC}" + echo "Command attempted: psql -h $DB_HOST -U $DB_USER -d $DB_NAME -c \"DELETE FROM public.jobs WHERE job_id = $JOB_ID;\"" + fi +else + echo "No Job ID created, skipping cleanup." +fi + +echo -e "\n${BLUE}=== Test Suite Finished ===${NC}" +# ============================================================================== +# well-api.py modifications to support WellDrySense API on port 1998 +# ============================================================================== \ No newline at end of file diff --git a/well-api.py b/well-api.py index c35fa16..34cc602 100644 --- a/well-api.py +++ b/well-api.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 #Version 2.0.2 9/25/2025 + import os import sys import ast @@ -377,7 +378,7 @@ def read_file(file_name, source = "LOCAL", type_ = "TEXT", bucket_name="daily-ma login_file = login_file.replace("\\","/") logger.debug(f"Full file path: {login_file}") logger.debug(f"File exists: {os.path.exists(login_file)}") - #print(login_file) + #logger.debug(login_file) if type_ == "TEXT": with open(login_file, encoding="utf8") as f: blob_data = f.read() @@ -1130,7 +1131,7 @@ def FillFields(blob_data, record, form_type): def StoreThresholds2DB(device_id, TR, BR, TLIFE, BLIFE): - #print('\nCreating create_caretaker\n') + #logger.debug('\nCreating create_caretaker\n') # Create a caretaker object. This object has nested properties and various types including numbers, DateTimes and strings. # This can be saved as JSON as is without converting into rows/columns. conn = get_db_connection() @@ -1151,7 +1152,7 @@ def StoreThresholds2DB(device_id, TR, BR, TLIFE, BLIFE): logger.debug(f"sql= {sql}") # Execute update query - print(sql) + logger.debug(sql) cur.execute(sql) # Commit the changes to the database @@ -1169,7 +1170,7 @@ def StoreThresholds2DB(device_id, TR, BR, TLIFE, BLIFE): #def StoreBeneficiary2DB(parameters, editing_user_id, user_id): - ##print('\nCreating create_caretaker\n') + ##logger.debug('\nCreating create_caretaker\n') ## Create a caretaker object. This object has nested properties and various types including numbers, DateTimes and strings. ## This can be saved as JSON as is without converting into rows/columns. @@ -1851,7 +1852,7 @@ def StoreFlow2DB(user_name, time_s, flow_json): return True except Exception as e: conn.rollback() - print(f"Error storing flow: {e}") + logger.debug(f"Error storing flow: {e}") logger.debug(f"Error storing flow: {e}") return False finally: @@ -1875,7 +1876,7 @@ def PersonInDB(f_l_name): cur.execute(sql) user_id = cur.fetchone()[0] except Exception as err: - print(str(err)) + logger.debug(str(err)) return user_id def GetDepoymentId(beneficiary_id): @@ -1892,7 +1893,7 @@ def GetDepoymentId(beneficiary_id): deployments_id = cur.fetchall() result = [item[0] for item in deployments_id] except Exception as err: - print(str(err)) + logger.debug(str(err)) return result @@ -2136,7 +2137,7 @@ def StoreDevice2DB(parameters, editing_device_id): logger.debug(f"[{call_id}] StoreDevice2DB ENTRY - editing_device_id: {editing_device_id}") logger.debug(f"[{call_id}] Parameters: {parameters}") - print(f"[{call_id}] StoreDevice2DB ENTRY - editing_device_id: {editing_device_id}") + logger.debug(f"[{call_id}] StoreDevice2DB ENTRY - editing_device_id: {editing_device_id}") # Database connection conn = get_db_connection() @@ -2211,11 +2212,11 @@ def StoreDevice2DB(parameters, editing_device_id): logger.debug(f"[{call_id}] Using UPSERT with composite key (well_id, device_mac)") logger.debug(f"[{call_id}] SQL: {sql}") - print(f"[{call_id}] SQL: {sql}") + logger.debug(f"[{call_id}] SQL: {sql}") logger.debug(f"[{call_id}] SQL: {sql}") - print(f"[{call_id}] SQL: {sql}") + logger.debug(f"[{call_id}] SQL: {sql}") # === ADD THIS NEW DEBUGGING BLOCK === final_well_id = CleanObject(parameters.get('well_id')) @@ -2235,7 +2236,7 @@ def StoreDevice2DB(parameters, editing_device_id): conn.close() logger.debug(f"[{call_id}] StoreDevice2DB SUCCESS - returning 1") - print(f"[{call_id}] StoreDevice2DB SUCCESS - returning 1") + logger.debug(f"[{call_id}] StoreDevice2DB SUCCESS - returning 1") AddToLog("Written/updated!") return 1 @@ -2251,7 +2252,7 @@ def StoreDevice2DB(parameters, editing_device_id): except: pass logger.debug(f"[{call_id}] StoreDevice2DB ERROR - returning 0") - print(f"[{call_id}] StoreDevice2DB ERROR - returning 0") + logger.debug(f"[{call_id}] StoreDevice2DB ERROR - returning 0") return 0 def StoreGroupToDevice(editing_device_id_or_mac, group_id, user_name): @@ -2301,7 +2302,7 @@ def StoreGroupToDevice(editing_device_id_or_mac, group_id, user_name): break try: record = json.loads(item_json) - print(record) + logger.debug(record) break except: pass @@ -2395,7 +2396,7 @@ def StoreWellIdToDevice(editing_device_id_or_mac, well_id, user_name): break try: record = json.loads(item_json) - print(record) + logger.debug(record) break except: pass @@ -2486,7 +2487,7 @@ def GetDeviceLive(editing_device_id_or_mac, user_name): break try: record = json.loads(item_json) - print(record) + logger.debug(record) break except: pass @@ -2545,7 +2546,7 @@ def StoreNetworkIdToDevice(editing_device_id_or_mac, network_id, user_name): break try: record = json.loads(item_json) - print(record) + logger.debug(record) break except: pass @@ -2603,7 +2604,7 @@ def DeviceReboot(editing_device_id_or_mac, user_name): break try: record = json.loads(item_json) - print(record) + logger.debug(record) break except: pass @@ -2660,7 +2661,7 @@ def UpdateDevicesTable(html_string, devices, users): mac = device[2] if mac == "64B7088903B4": - print("stop") + logger.debug("stop") mac_row_string = f' {mac}\n' age = time.time() - device[3] @@ -2672,7 +2673,7 @@ def UpdateDevicesTable(html_string, devices, users): row_ending = f' \n \n' for col_cnt in range(1, len(device)): column_value = device[col_cnt] - #print(column_value) + #logger.debug(column_value) if col_cnt == 2: col_string_template = mac_row_string elif col_cnt == 3: @@ -2696,7 +2697,7 @@ def UpdateDevicesTable(html_string, devices, users): row_sting = row_sting + col_string_template row_sting = row_sting + row_ending table_rows_string = table_rows_string + row_sting - #print(table_rows_string) + #logger.debug(table_rows_string) html_string = html_string.replace("###ROWS###",table_rows_string) return html_string @@ -2720,7 +2721,7 @@ def UpdateDeploymentsSelector(html_string, deployments, include_all=True, select else: choice_string = f' \n' selector_string = selector_string + choice_string - #print(selector_string) + #logger.debug(selector_string) html_string = html_string.replace("###INSTALLS###",selector_string) return html_string @@ -2759,7 +2760,7 @@ def GetDeviceDetails(cur, deployment_ids, location_id): """ cur.execute(sql) - print(sql) + logger.debug(sql) devices_ids_records = cur.fetchall() all_details = [] @@ -2769,7 +2770,7 @@ def GetDeviceDetails(cur, deployment_ids, location_id): #sql = f"SELECT device_id, MAX(time) as last_reading_time FROM sensor_readings WHERE device_id IN ({device_ids_string}) GROUP BY device_id" #to slow sql = f"SELECT DISTINCT ON (device_id) device_id, time as last_reading_time FROM sensor_readings WHERE device_id IN ({device_ids_string}) AND time > now() - INTERVAL '1 day' ORDER BY device_id, time DESC" cur.execute(sql) - print(sql) + logger.debug(sql) devices_times = cur.fetchall()#cur.fetchone() found_device_details = {} for device_record in devices_times: @@ -2792,8 +2793,8 @@ def GetDeviceDetails(cur, deployment_ids, location_id): last_message_time = 0 last_message_epoch = 0 - #print(last_message_epoch) - #print(type(last_message_epoch)) + #logger.debug(last_message_epoch) + #logger.debug(type(last_message_epoch)) device_id = device_table_record[0] mac = device_table_record[1] well_id = device_table_record[2] @@ -2849,7 +2850,7 @@ def GetDeviceDetailsComplete(cur, deployment_ids, location_id): """ cur.execute(sql) - print(sql) + logger.debug(sql) devices_ids_records = cur.fetchall() all_details = [] @@ -2859,7 +2860,7 @@ def GetDeviceDetailsComplete(cur, deployment_ids, location_id): #sql = f"SELECT device_id, MAX(time) as last_reading_time FROM sensor_readings WHERE device_id IN ({device_ids_string}) GROUP BY device_id" #to slow sql = f"SELECT DISTINCT ON (device_id) device_id, time as last_reading_time FROM sensor_readings WHERE device_id IN ({device_ids_string}) AND time > now() - INTERVAL '1 day' ORDER BY device_id, time DESC" cur.execute(sql) - print(sql) + logger.debug(sql) devices_times = cur.fetchall()#cur.fetchone() found_device_details = {} for device_record in devices_times: @@ -2882,8 +2883,8 @@ def GetDeviceDetailsComplete(cur, deployment_ids, location_id): last_message_time = 0 last_message_epoch = 0 - #print(last_message_epoch) - #print(type(last_message_epoch)) + #logger.debug(last_message_epoch) + #logger.debug(type(last_message_epoch)) device_id = device_table_record[0] mac = device_table_record[1] well_id = device_table_record[2] @@ -2917,18 +2918,22 @@ def GetVisibleDevices(deployments): sql = "SELECT device_mac FROM public.devices ORDER BY device_id ASC"# SELECT deployment_id, devices FROM public.deployment_details" macs_group = [] deployment_ids = [] - print(sql) + logger.debug(f"{sql}") cur.execute(sql) + logger.debug("$1") macs_records = cur.fetchall()#cur.fetchone() + logger.debug("$1a") for record in macs_records: deployment_ids.append((0, record[0])) devices_details = GetDeviceDetails(cur, deployment_ids, -1) else: sql = f"SELECT deployment_id, devices FROM public.deployment_details WHERE deployment_id IN ({deployments})" - print(sql) + logger.debug(sql) cur.execute(sql) + logger.debug("$2") devices_groups = cur.fetchall()#cur.fetchone() + logger.debug("$2a") deployment_ids = [] for deployment_id, dev_group in devices_groups: if dev_group != None and dev_group != "": @@ -2946,7 +2951,7 @@ def GetVisibleDevices(deployments): for mac in macs_group: deployment_ids.append((deployment_id, mac)) else: - print(f"Deployment {deployment_id} has dev_group empty") + logger.debug(f"Deployment {deployment_id} has dev_group empty") devices_details = [] if deployment_ids != []: @@ -3038,7 +3043,7 @@ def GetUsersFromDeployments(deployments): """ with conn.cursor() as cur: cur.execute(sql) - print(sql) + logger.debug(sql) deployments_dets = cur.fetchall()#cur.fetchone() except Exception as err: logger.error("GetUsersFromDeployments "+str(err) +" "+sql) @@ -3065,6 +3070,79 @@ def CovertToIsoTime(date_s, n_minute): def sleep_length(presence_list, short_absence_threshold=15): + """ + Calculate the last sleep duration - the sleep period that ends after midnight (8640 decas). + """ + MIDNIGHT_DECA = 8640 # Midnight of the reporting day + + in_bed_periods = [] + + for i in range(len(presence_list)): + deca_index, deca_count = presence_list[i] + + if deca_count == 0: + continue + + if deca_count > 0: # In bed + if i == 0 and deca_index == 0: + start_deca = -deca_count + end_deca = 0 + else: + start_deca = deca_index + end_deca = deca_index + deca_count + + in_bed_periods.append({ + 'start': start_deca, + 'end': end_deca, + 'duration': deca_count + }) + + in_bed_periods.sort(key=lambda p: p['start']) + + # Merge periods separated by short absences + merged_periods = [] + current_period = None + + for period in in_bed_periods: + if current_period is None: + current_period = period.copy() + else: + gap = period['start'] - current_period['end'] + if gap < 0: + gap = 0 + if gap <= short_absence_threshold: + current_period['end'] = period['end'] + current_period['duration'] += period['duration'] + gap + else: + merged_periods.append(current_period) + current_period = period.copy() + + if current_period is not None: + merged_periods.append(current_period) + + # Find significant sleep (30+ min) that ENDS after midnight + significant_sleep_threshold = 180 + + # Filter for periods that end after midnight (the "morning" wake-up) + morning_sleep_periods = [ + p for p in merged_periods + if p['duration'] >= significant_sleep_threshold and p['end'] > MIDNIGHT_DECA + ] + + if morning_sleep_periods: + # Get the FIRST one that ends after midnight (the night's sleep) + main_sleep_period = min(morning_sleep_periods, key=lambda p: p['end']) + + sleep_duration_minutes = round(main_sleep_period['duration'] / 6) + + # Wake time relative to midnight of reporting day + wake_time_deca = main_sleep_period['end'] - MIDNIGHT_DECA + wake_time_minutes = round(wake_time_deca / 6) + + return (sleep_duration_minutes, wake_time_minutes) + + return (0, 0) +def sleep_length_old(presence_list, short_absence_threshold=15): """ Calculate the total sleep duration and wake time based on presence data. @@ -3194,7 +3272,7 @@ def sleep_length(presence_list, short_absence_threshold=15): # [4603, 0], [4604, 1], [4604, 0], [4965, -361], [4965, 0], [4966, 1], [4966, 0], # [4984, -18], [4984, 0], [4985, 1], [4985, 0], [8639, -3654] # ] -# print(f"Sleep duration: {sleep_length(presence_list)} minutes") +# logger.debug(f"Sleep duration: {sleep_length(presence_list)} minutes") # Example usage: @@ -3212,7 +3290,7 @@ def sleep_length(presence_list, short_absence_threshold=15): # [4603, 0], [4604, 1], [4604, 0], [4965, -361], [4965, 0], [4966, 1], [4966, 0], # [4984, -18], [4984, 0], [4985, 1], [4985, 0], [8639, -3654] # ] -# print(f"Sleep duration: {sleep_length(presence_list)} minutes") +# logger.debug(f"Sleep duration: {sleep_length(presence_list)} minutes") # Example usage: @@ -3230,7 +3308,7 @@ def sleep_length(presence_list, short_absence_threshold=15): # [4603, 0], [4604, 1], [4604, 0], [4965, -361], [4965, 0], [4966, 1], [4966, 0], # [4984, -18], [4984, 0], [4985, 1], [4985, 0], [8639, -3654] # ] -# print(f"Sleep duration: {sleep_length(presence_list)} minutes") +# logger.debug(f"Sleep duration: {sleep_length(presence_list)} minutes") def filter_short_groups_c_wc_old(presence_list, filter_size, device_id_str, from_date, to_date, time_zone_s, refresh = False): @@ -3262,12 +3340,12 @@ def filter_short_groups_c_wc_old(presence_list, filter_size, device_id_str, from while current_date <= end_date: current_date_str = current_date.strftime("%Y-%m-%d") - print(current_date_str) + logger.debug(current_date_str) dates_list.append(current_date_str) current_date += timedelta(days=1) for day in range(1, days_difference-last_offset+1): - print(day) + logger.debug(day) end_index = (1 + day) * 6 * 1440 if end_index > len(presence_list): end_index = len(presence_list) @@ -3316,7 +3394,7 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat dates_list = [] while current_date <= end_date: current_date_str = current_date.strftime("%Y-%m-%d") - print(current_date_str) + logger.debug(current_date_str) dates_list.append(current_date_str) current_date += timedelta(days=1) @@ -3332,7 +3410,7 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat current_minute_of_day = current_time.hour * 60 + current_time.minute current_sample_of_day = min(current_minute_of_day * 6, samples_per_day) effective_total_samples = (days_difference - 1) * samples_per_day + current_sample_of_day - print(f"Today detected: limiting to {current_sample_of_day} samples for last day") + logger.debug(f"Today detected: limiting to {current_sample_of_day} samples for last day") # Initialize result - use effective total samples whole_result = [0] * effective_total_samples @@ -3340,7 +3418,7 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat # Process each day (0-indexed to avoid confusion) for day_idx in range(days_difference): current_date_str = dates_list[day_idx] - print(f"Processing day {day_idx + 1}: {current_date_str}") + logger.debug(f"Processing day {day_idx + 1}: {current_date_str}") # Calculate result array indices for this day result_start_idx = day_idx * samples_per_day @@ -3371,7 +3449,7 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat # Skip if no input data available if input_start_idx >= input_end_idx or input_start_idx >= len(presence_list): - print(f"No input data available for {current_date_str}") + logger.debug(f"No input data available for {current_date_str}") continue # Try to load cached data @@ -3389,7 +3467,7 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat if filtered_day_str is None or filtered_day_str == "": # Filter the input data input_data = presence_list[input_start_idx:input_end_idx] - print(f"Input range: {input_start_idx}:{input_end_idx}, length: {len(input_data)}, has_previous_day: {has_previous_day}") + logger.debug(f"Input range: {input_start_idx}:{input_end_idx}, length: {len(input_data)}, has_previous_day: {has_previous_day}") filtered_data = filter_short_groups_c(input_data, filter_size, device_id_str, from_date) @@ -3400,18 +3478,18 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat if has_previous_day and len(filtered_data) >= 2 * samples_per_day: # We processed [previous_day + current_day], take the second day (current_day) day_data_start = samples_per_day # Skip the first day (previous day context) - print(f"Extracting day 1 from 2-day filtered result: [{day_data_start}:{day_data_start + needed_samples}]") + logger.debug(f"Extracting day 1 from 2-day filtered result: [{day_data_start}:{day_data_start + needed_samples}]") elif has_previous_day and len(filtered_data) >= samples_per_day: # We have previous day context but less than 2 full days # Take from the portion that corresponds to current day available_current_day_samples = len(filtered_data) - samples_per_day day_data_start = samples_per_day needed_samples = min(needed_samples, available_current_day_samples) - print(f"Extracting partial day 1: [{day_data_start}:{day_data_start + needed_samples}]") + logger.debug(f"Extracting partial day 1: [{day_data_start}:{day_data_start + needed_samples}]") else: # First day or single day processing, take from beginning day_data_start = 0 - print(f"Extracting day 0 (first/single day): [{day_data_start}:{day_data_start + needed_samples}]") + logger.debug(f"Extracting day 0 (first/single day): [{day_data_start}:{day_data_start + needed_samples}]") day_data_end = day_data_start + needed_samples @@ -3421,8 +3499,8 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat else: filtered_day = [] - print(f"Filtered data length: {len(filtered_data)}") - print(f"Extracted day data: start={day_data_start}, end={day_data_end}, length={len(filtered_day)}") + logger.debug(f"Filtered data length: {len(filtered_data)}") + logger.debug(f"Extracted day data: start={day_data_start}, end={day_data_end}, length={len(filtered_day)}") # Cache the result SaveGenericObjectInBlob("filtered-presence", filename_day_presence, filtered_day) @@ -3434,7 +3512,7 @@ def filter_short_groups_c_wc(presence_list, filter_size, device_id_str, from_dat if copy_length > 0: whole_result[result_start_idx:result_start_idx + copy_length] = filtered_day[:copy_length] - print(f"Completed {current_date_str}: copied {copy_length} samples") + logger.debug(f"Completed {current_date_str}: copied {copy_length} samples") return whole_result @@ -3514,7 +3592,7 @@ def GetLastDurationMinutes(deployment_id, selected_devices, filter, ddate): else: radar_threshold_group = ["s3_max",12] - print(well_id, radar_threshold_group) + logger.debug(f"{well_id}, {radar_threshold_group}") device_id_2_location[device_id] = location_name device_id_2_threshold[device_id] = radar_threshold_group @@ -3535,7 +3613,7 @@ def GetLastDurationMinutes(deployment_id, selected_devices, filter, ddate): if is_number(threshold_str): threshold_lst = ["s3",float(threshold_str)] - print(threshold_lst) + logger.debug(threshold_lst) radar_field = threshold_lst[0] #since we are getting 10 sec dat, no more need for min or max... radar_field = radar_field.split("_")[0] @@ -3553,11 +3631,11 @@ def GetLastDurationMinutes(deployment_id, selected_devices, filter, ddate): devices_list_str = ','.join(str(device[1]) for device in devices_list) #sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) #sql = get_deployment_radar_10sec_snapped_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) - #print(sql) + #logger.debug(sql) if data_type == "z-graph" or data_type == "all" or data_type == "multiple": #zsql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_z_str, time_to_str, ids_list, radar_fields_of_interest) zsql = get_deployment_radar_10sec_snapped_query(devices_list_str, time_from_z_str, time_to_str, ids_list, radar_fields_of_interest) - print(zsql) + logger.debug(zsql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -3641,9 +3719,9 @@ def GetLastDurationMinutes(deployment_id, selected_devices, filter, ddate): temporary_map_day_plus[well_id] = [0] * 6 * 1440 * days_difference_long presence_map['longpresence'][well_id] = [0] * 6 * 1440 * days_difference_long #just place holder - print(deployment_id) - print(time_from_z_str) - print(devices_list) + logger.debug(deployment_id) + logger.debug(time_from_z_str) + logger.debug(devices_list) parsed_time = datetime.datetime.strptime(time_from_z_str, '%Y-%m-%d %H:%M:%S%z') start_time = datetime.datetime( @@ -3684,7 +3762,7 @@ def GetLastDurationMinutes(deployment_id, selected_devices, filter, ddate): max_device_id = 0 max_woke_up = 0 for device_id in ids_list: - #print(device_id_2_threshold[id2well_id[device_id]]) + #logger.debug(device_id_2_threshold[id2well_id[device_id]]) z_graph = CreateZGraphAI(presence_map["longpresence"][id2well_id[device_id]]) #temporary_map_day_plus[id2well_id[device_id]]) sleep_minutes, woke_up = sleep_length(z_graph) if sleep_minutes > max_sleep: @@ -4089,7 +4167,7 @@ def GetSensorsDetailsFromDeployment(deployment_id, ddate, filter_minutes, fast=F "sleep_hours": round(sleep_hours, 2) } except Exception as e: - print(traceback.format_exc()) + logger.debug(traceback.format_exc()) return report def FindPreviousLocation(locations_list, last_present_device, start_index): @@ -4108,7 +4186,7 @@ def FindPreviousLocation(locations_list, last_present_device, start_index): total_minutes = 0 found_device = None end_index = -len(locations_list) - 1 - print(locations_list[-4:]) + logger.debug(locations_list[-4:]) # Count backwards from start_index for i in range(start_index, end_index, -1): device_id = locations_list[i][0] @@ -4168,7 +4246,7 @@ def MACsToWellIds(cur, macs_list): for mac in macs_list: if mac != "": - print(mac) + logger.debug(mac) device_ids.append(macs_map[mac][1]) device_list.append(macs_map[mac]) @@ -4212,7 +4290,7 @@ def WellId2Details(well_ids): #sql = f"SELECT deployment_id, devices FROM public.deployment_details WHERE deployment_id IN ({deployments})" #device_ids_string = ",".join(map(str, devices_ids_list)) sqlr = f"SELECT well_id, device_id, device_mac, location, description FROM public.devices WHERE well_id IN ({well_ids})" - print(sqlr) + logger.debug(sqlr) with conn.cursor() as cur: cur.execute(sqlr) @@ -4352,12 +4430,12 @@ def GetMatchingDevicesComplete(privileges, group, deployment, location): def getOldestDeploymentHistoryFromBeneficiary(deployment_id): #this will return oldest entry as well as last proximity (devices) st = time.time() - print(f"*0 ----{time.time() - st}") + logger.debug(f"*0 ----{time.time() - st}") results=[] well_ids_last = [] #this needs to be list of tuples (well_id, Location_st, Description) oldest_time = None try: - print(f"*0a ----{time.time() - st}") + logger.debug(f"*0a ----{time.time() - st}") with get_db_connection() as conn: sqlr = f""" SELECT * FROM ( @@ -4368,21 +4446,21 @@ def getOldestDeploymentHistoryFromBeneficiary(deployment_id): ) AS latest_deployment """ print (sqlr) - print(f"*1 ----{time.time() - st}") + logger.debug(f"*1 ----{time.time() - st}") with conn.cursor() as cur: cur.execute(sqlr) - print(f"*2 ----{time.time() - st}") + logger.debug(f"*2 ----{time.time() - st}") results = cur.fetchall() - print(f"*3 ----{time.time() - st}") + logger.debug(f"*3 ----{time.time() - st}") #lets find which of historical sets has data in DB if results == None or results == []: #look in deployment_details sqlr = f"SELECT devices from public.deployment_details WHERE deployment_id ={deployment_id}" #print (sqlr) - print(f"*4 ----{time.time() - st}") + logger.debug(f"*4 ----{time.time() - st}") devices_string = ReadCleanStringDB(cur, sqlr) - print(f"*5 ----{time.time() - st}") + logger.debug(f"*5 ----{time.time() - st}") macs_list = ToList(devices_string) - print(f"*6 ----{time.time() - st}") + logger.debug(f"*6 ----{time.time() - st}") device_ids_last, device_alls_last = MACsToWellIds(cur, macs_list) sql_query = """ SELECT device_id, first_seen_at @@ -4390,7 +4468,7 @@ def getOldestDeploymentHistoryFromBeneficiary(deployment_id): WHERE device_id = ANY(%s) GROUP BY device_id; """ - print(f"*7 ----{time.time() - st}") + logger.debug(f"*7 ----{time.time() - st}") try: cur.execute(sql_query, (device_ids_last,)) results1 = cur.fetchall() @@ -4442,19 +4520,19 @@ def getOldestDeploymentHistoryFromBeneficiary(deployment_id): except Exception as e: AddToLog(traceback.format_exc()) AddToLog(str(e)) - print(f"*8 ----{time.time() - st}") + logger.debug(f"*8 ----{time.time() - st}") else: history_entry = results[-1] macs_list = ToList(history_entry[1]) - print(f"*9 ----{time.time() - st}") + logger.debug(f"*9 ----{time.time() - st}") device_ids_last, device_alls_last = MACsToWellIds(cur, macs_list) for history_entry in results: macs_list = ToList(history_entry[1]) - print(f"*10 ----{time.time() - st}") + logger.debug(f"*10 ----{time.time() - st}") device_ids, device_alls = MACsToWellIds(cur, macs_list) - #print(f"*11 ----{time.time() - st}") + #logger.debug(f"*11 ----{time.time() - st}") #sql_query = """ #SELECT time as oldest_record_time #FROM sensor_readings @@ -4462,10 +4540,10 @@ def getOldestDeploymentHistoryFromBeneficiary(deployment_id): #ORDER BY time ASC #LIMIT 1; #""" - print(f"*12 ----{time.time() - st}") - print("Getting oldest record time for devices:", device_ids_last) + logger.debug(f"*12 ----{time.time() - st}") + #logger.debug("Getting oldest record time for devices:", device_ids_last) - #print(sql_query, device_ids_last) + #logger.debug(sql_query, device_ids_last) #try: #cur.execute(sql_query, (device_ids_last,)) #results1 = cur.fetchall() @@ -4475,22 +4553,22 @@ def getOldestDeploymentHistoryFromBeneficiary(deployment_id): #except Exception as e: - #print(str(e)) + #logger.debug(str(e)) try: oldest_time = get_oldest_record_time_optimized(cur, device_ids_last) if oldest_time is not None: break except Exception as e: - print(str(e)) + logger.debug(str(e)) - print(f"*13 ----{time.time() - st}") + logger.debug(f"*13 ----{time.time() - st}") except Exception as e: - print(f"*0b ----{time.time() - st}") + logger.debug(f"*0b ----{time.time() - st}") AddToLog(traceback.format_exc()) - print(f"*14 ----{time.time() - st}") + logger.debug(f"*14 ----{time.time() - st}") return oldest_time, device_alls_last def get_oldest_record_time_optimized(cur, device_ids): @@ -4546,7 +4624,7 @@ def get_oldest_record_time_optimized(cur, device_ids): cached_device_times[device_id] = oldest_time except Exception as e: - print(f"Error processing device_id {device_id}: {str(e)}") + logger.debug(f"Error processing device_id {device_id}: {str(e)}") continue # Return the earliest time among all devices that have data @@ -4569,7 +4647,7 @@ def getLastEditedBeneficiary(beneficiary): if response.status_code == 200: text = response.text - #print(text) + #logger.debug(text) if text == "Log-Out": return text if text[0] == "{": @@ -4683,11 +4761,11 @@ def GetDeploymentDatesBoth(deployment_in): #Lets take oldest data from first member of deployment st = time.time() date_list = [] - print(f"&0 ----{time.time() - st}") + logger.debug(f"&0 ----{time.time() - st}") time_zone_st = GetTimeZoneOfDeployment(deployment_in) - print(f"&1 ----{time.time() - st}") + logger.debug(f"&1 ----{time.time() - st}") oldest_date_dt_utc, devices_all = getOldestDeploymentHistoryFromBeneficiary(deployment_in) - print(f"&2 ----{time.time() - st}") + logger.debug(f"&2 ----{time.time() - st}") if oldest_date_dt_utc != None: #get date in local time zone from UTC datetime @@ -4699,7 +4777,7 @@ def GetDeploymentDatesBoth(deployment_in): # Generate a list of date strings from oldest_date to today in inverted order date_list = [(today_date - timedelta(days=x)).strftime('%Y-%m-%d') for x in range((today_date - oldest_date_dt_local).days + 1)] - print(f"&3 ----{time.time() - st}") + logger.debug(f"&3 ----{time.time() - st}") return date_list, devices_all, time_zone_st def check_file_exists(file_name, bucket_name="daily-maps"): @@ -5122,7 +5200,7 @@ def fast_fill_smell_array_from_timescale(day_data, time_from_str, device_to_inde # Calculate all minute deltas at once times = records_array[:, 0] - #print(times[0], start_time, (times[0] - start_time).total_seconds()) + #logger.debug(times[0], start_time, (times[0] - start_time).total_seconds()) minute_deltas = np.array([(t - start_time).total_seconds() / 60 for t in times], dtype=int) # Filter valid minute deltas @@ -5190,7 +5268,7 @@ def fast_fill_radar_array_from_timescale(day_data, time_from_str, devices_list, # Calculate all minute deltas at once times = records_array[:, 0] - #print(times[0], start_time, (times[0] - start_time).total_seconds()) + #logger.debug(times[0], start_time, (times[0] - start_time).total_seconds()) minute_deltas = np.array([(t - start_time).total_seconds() / 60 for t in times], dtype=int) # Filter valid minute deltas @@ -5262,8 +5340,8 @@ def BestColor(in_val): b = 255 #if (r > 255): - # print(in_val) - # print(int(r),int(g),int(b)) + # logger.debug(in_val) + # logger.debug(int(r),int(g),int(b)) return(int(r),int(g),int(b)) def GrayColor(in_val): @@ -5674,7 +5752,7 @@ def plot(arr, filename="histogram.png", title="Histogram Plot", figsize=(12, 6), plt.tight_layout() plt.savefig(filename) plt.close() - print(f"Plot saved to: {filename}") + logger.debug(f"Plot saved to: {filename}") #plt.show() def ShowArray(arr, threshold, filename="histogram.png", title="Histogram Plot", figsize=(12, 6), @@ -5716,7 +5794,7 @@ def ShowArray(arr, threshold, filename="histogram.png", title="Histogram Plot", plt.tight_layout() plt.savefig(filename) plt.close() - print(f"Plot saved to: {filename}") + logger.debug(f"Plot saved to: {filename}") #plt.show() @@ -5896,14 +5974,14 @@ def AddLimits(arr_source, devices_c, sensors_c, percentile): min_ok, max_ok, window = sensor_legal_values[s_table[sensor_index]] #if EnablePlot: #if (y == 33): - #print("stop") + #logger.debug("stop") #plot(arr_source[y, :1440], "before_clean_sensor.png") if window > 2: arr_source[y, :1440] = clean_data_vectorized(arr_source[y, :1440], window, percentile) #if EnablePlot: #if (y == 33): - #print("stop") + #logger.debug("stop") #plot(arr_source[y, :1440], "after_clean_sensor.png") arr_source[y][1440] = min_ok @@ -6005,14 +6083,14 @@ def FillImage_backward_compatible(scaled_day, devices_c, arr_stretched_template, #device_offset = device_idx * measurements_per_device #if device_idx == 0: # Debug first device only - #print(f"\nDevice {device_idx} smell sensor debug (minute 0):") + #logger.debug(f"\nDevice {device_idx} smell sensor debug (minute 0):") #for sensor_idx in range(10): #row_idx = device_offset + 5 + sensor_idx #if row_idx < scaled_day.shape[0]: #value = scaled_day[row_idx, 0] #min_val = scaled_day[row_idx, 1442] if scaled_day.shape[1] > 1442 else 0 #max_val = scaled_day[row_idx, 1443] if scaled_day.shape[1] > 1443 else 1 - #print(f" s{sensor_idx}: row={row_idx}, value={value}, min={min_val},max={max_val}") + #logger.debug(f" s{sensor_idx}: row={row_idx}, value={value}, min={min_val},max={max_val}") minutes = scaled_day.shape[1] - 4 @@ -6362,9 +6440,9 @@ def FillImage_mixed_formats(scaled_day, devices_c, arr_stretched_template, bw, d device_y_offset = device_idx * 150 device_format = device_formats.get(device_idx, 'old') - print(f"\nDevice {device_idx} (format={device_format}):") - print(f" device_offset={device_offset}, device_y_offset={device_y_offset}") - print(f" Expected Y range: {device_y_offset} to {device_y_offset + 149}") + logger.debug(f"\nDevice {device_idx} (format={device_format}):") + logger.debug(f" device_offset={device_offset}, device_y_offset={device_y_offset}") + logger.debug(f" Expected Y range: {device_y_offset} to {device_y_offset + 149}") for minute in range(minutes): @@ -6374,7 +6452,7 @@ def FillImage_mixed_formats(scaled_day, devices_c, arr_stretched_template, bw, d row_idx = device_offset + env_idx value = scaled_day[row_idx, minute] pixel_y_start = device_y_offset + env_idx * 10 - print(f" Env sensor {env_idx}: row_idx={row_idx}, value={value:.2f}, pixel_y={pixel_y_start}-{pixel_y_start+9}") + logger.debug(f" Env sensor {env_idx}: row_idx={row_idx}, value={value:.2f}, pixel_y={pixel_y_start}-{pixel_y_start+9}") # Environmental sensors (same for both formats) for env_idx in range(5): @@ -6916,7 +6994,7 @@ def FillImage(scaled_day, devices_c, sensors_c, arr_stretched, group_by, bw): row_data = scaled_day[row, :minutes] # Get minute data #if row == 33: - # print("stop") + # logger.debug("stop") # plot(row_data, "row_data.png") big_min = scaled_day[row, 1442] # min value big_max = scaled_day[row, 1443] # max value @@ -7011,7 +7089,7 @@ def FillRadarImage(scaled_day, devices_c, bands, arr_stretched, group_by, map_ty row_data = scaled_day[row, :minutes] # Get minute data #if row == 33: - # print("stop") + # logger.debug("stop") # plot(row_data, "row_data.png") big_min = 0 #scaled_day[row, 1442] # min value big_max = 255 #scaled_day[row, 1443] # max value @@ -7102,7 +7180,7 @@ def GetFullLocMapDetails(map_file): def median_filter(data, window_size): filtered_data = [] - print(len(data)) + logger.debug(len(data)) window = deque(maxlen=window_size) last_value = -1 offset = 0 @@ -7185,13 +7263,13 @@ def ReadDailyRadar(MAC, current_date): file1 = os.path.join(scriptDir, "DB/processed_db/"+MAC.upper() +"_"+str(current_date.year)+"_"+str(current_date.month).rjust(2, '0')+".db") file1 = file1.replace("\\","/") if (not path.exists(file) and not path.exists(file1)): - print(file + " and " + file1 + " are not found") + logger.debug(file + " and " + file1 + " are not found") return [] result = [] min_OK = "0" sqlr = "SELECT * FROM radars WHERE time >= "+str(start_of_day) +" and time < "+str(end_of_day) +" ORDER BY time ASC" #sqlr = "SELECT Date, high, low from "+sensor.lower()+"s1Min"+" WHERE low >= "+min_OK+" and Date >= "+str(start_of_day) +" and Date < "+str(end_of_day) - print(sqlr) + logger.debug(sqlr) if os.path.exists(file): result = QuerrySql(file, sqlr) elif os.path.exists(file1): @@ -7224,8 +7302,8 @@ def ReadDailyRadar(MAC, current_date): def FromLocalMidnight(epoch_time, local_delta): # Convert epoch time to UTC datetime object - print(type(epoch_time)) - print(epoch_time) + logger.debug(type(epoch_time)) + logger.debug(epoch_time) local_datetime = datetime.datetime.utcfromtimestamp(epoch_time+local_delta).replace(tzinfo=pytz.UTC) # Calculate minute count from midnight @@ -7317,7 +7395,7 @@ def ReadDailyCollapsedFastRadar(MAC, time_from_str, time_to_str): result = [] min_OK = "0" sqlr = "SELECT radar_max FROM devices WHERE MAC = '"+MAC +"'" - print(sqlr) + logger.debug(sqlr) DB_to_be_found_in_full = os.path.join(scriptDir, "main.db") DB_to_be_found_in_full = DB_to_be_found_in_full.replace("\\","/") result = QuerrySql(DB_to_be_found_in_full, sqlr) @@ -7326,7 +7404,7 @@ def ReadDailyCollapsedFastRadar(MAC, time_from_str, time_to_str): if result[0][0] == 1: sqlr = "SELECT date, high FROM radars1Min WHERE date >= "+str(start_of_day) +" and date < "+str(end_of_day) + " ORDER BY date" - print(sqlr) + logger.debug(sqlr) if os.path.exists(file): result = QuerrySql(file, sqlr) elif os.path.exists(file1): @@ -7747,7 +7825,7 @@ def create_smell_optimized_heatmap(arr_stretched, my_data, bw, fields_s, device_ # # --- EXECUTE THE NEW 80-SENSOR PIPELINE --- # - print("New smell format detected. Running 80-sensor visualization.") + logger.debug("New smell format detected. Running 80-sensor visualization.") devices_c = len(device_to_index) devices_list = list(device_to_index.keys()) @@ -7779,7 +7857,7 @@ def create_smell_optimized_heatmap(arr_stretched, my_data, bw, fields_s, device_ # # --- EXECUTE THE ORIGINAL 10-SENSOR PIPELINE --- # - print("Old smell format detected. Running original 10-sensor visualization.") + logger.debug("Old smell format detected. Running original 10-sensor visualization.") minutes = 1440 devices_c = len(device_to_index) @@ -8154,7 +8232,7 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, for details in devices_list: sql = get_device_radar_only_query(str(details[1]), time_from_str, time_to_str, [details[1]]) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -8169,25 +8247,25 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, # Example of printing results for minute in location_analysis: - print(f"Time: {minute['timestamp']}") - print(f"Status: {minute['status']}") - print(f"Present in: {', '.join(minute['locations'])}") + logger.debug(f"Time: {minute['timestamp']}") + logger.debug(f"Status: {minute['status']}") + logger.debug(f"Present in: {', '.join(minute['locations'])}") if minute['moving_locations']: - print(f"Movement in: {', '.join(minute['moving_locations'])}") - print("---") + logger.debug(f"Movement in: {', '.join(minute['moving_locations'])}") + logger.debug("---") - print(f"Dictionary size: {get_size(data_sets)} bytes") + logger.debug(f"Dictionary size: {get_size(data_sets)} bytes") devices_list_str = ','.join(str(device[1]) for device in devices_list) time_from_str, time_to_str = GetLocalTimeForDate(selected_date, time_zone_s) sql = get_device_radar_only_query(devices_list_str, time_from_str, time_to_str, ids_list) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None: return False @@ -8259,12 +8337,12 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, cnt += 1 sql = get_deployment_radar_only_detailed_query(devices_list_str, time_from_str, time_to_str, ids_list) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None: return False @@ -8286,22 +8364,22 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, # Example of printing results for minute in location_analysis: - print(f"Time: {minute['timestamp']}") - print(f"Status: {minute['status']}") - print(f"Present in: {', '.join(minute['locations'])}") + logger.debug(f"Time: {minute['timestamp']}") + logger.debug(f"Status: {minute['status']}") + logger.debug(f"Present in: {', '.join(minute['locations'])}") if minute['moving_locations']: - print(f"Movement in: {', '.join(minute['moving_locations'])}") - print("---") + logger.debug(f"Movement in: {', '.join(minute['moving_locations'])}") + logger.debug("---") #---------------------------------------------------------------------------------------------------- - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None: return False @@ -8314,7 +8392,7 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, if True: wave_m = np.zeros((stripes, 1440, 3), dtype=np.uint8) wave_m = create_radar_optimized_heatmap(my_data, bw, fields, wave_m, device_to_index, base_minute, time_zone_s) - print(time.time()-st) + logger.debug(time.time()-st) if False: #base_minute = my_data[0][0]# min(record[0] for record in my_data) @@ -8351,7 +8429,7 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, # Set RGB values (all same for grayscale) wave_m[y, x] = BestColor(gray_value) - print(time.time()-st) + logger.debug(time.time()-st) st = time.time() for yy in range(stripes): @@ -8360,7 +8438,7 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, y = yy * stretch_by + stretch_index arr_stretched[y, :] = rgb_row - print(time.time()-st) + logger.debug(time.time()-st) SaveImageInBlob(image_file, arr_stretched, labels) #arr_source[2*gate, :] = wave_m @@ -8371,12 +8449,12 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, #if sens_val != 0: #r,g,b=BestColor(km*(sens_val-m_min)) #if r > 255 or g > 255 or b > 255: - #print(r,g,b) + #logger.debug(r,g,b) #rgb_row[col] = r,g,b #for stretch_index in range(stretch_by): #y = device_counter * (18*stretch_by) + 2*gate * stretch_by + stretch_index - ##print(y, row, devices_c, sensor_index, location_index, stretch_index) + ##logger.debug(y, row, devices_c, sensor_index, location_index, stretch_index) ##arr_stretched[y, :] = rgb_row #if gate > 1: @@ -8406,7 +8484,7 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, #b = r #else: #r,g,b=BestColor(ks*(sens_val-s_min)) - ##print(r,g,b) + ##logger.debug(r,g,b) #rgb_row[col] = r,g,b #for stretch_index in range(stretch_by): @@ -8415,10 +8493,10 @@ def CreatePresenceMap(location_image_file, devices_list, selected_date, #y = device_counter * (18*stretch_by) + (2*(gate)) * stretch_by + stretch_index #arr_stretched[y, :] = rgb_row - print("stop") + logger.debug("stop") def ConvertToBase(time_from_str, time_zone_s): - print(time_from_str) + logger.debug(time_from_str) dt = datetime.datetime.strptime(time_from_str, "%Y-%m-%d %H:%M:%S%z") return dt @@ -8483,7 +8561,7 @@ def GetActivities(device_id, well_id, date_str, filter_size, refresh, timezone_s devices_list_str = device_id_str #sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) sql = get_deployment_radar_10sec_snapped_query_min_max(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -8537,9 +8615,9 @@ def GetActivities(device_id, well_id, date_str, filter_size, refresh, timezone_s return(non_zeros / 360, events) #decas to hours except Exception as e: - print(filename_day_presence) - print(filtered_day_str) - print(traceback.format_exc()) + logger.debug(filename_day_presence) + logger.debug(filtered_day_str) + logger.debug(traceback.format_exc()) return(0, 0) def CreateFullLocationMap(location_image_file, devices_list, selected_date, map_type, force_recreate, chart_type, bw, motion, scale_global, fast, filter_minutes, time_zone_s): @@ -8644,12 +8722,12 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, labels.append((descriptor, (10, 10 + text_height + (cnt)*fields_n*stretch_by), label_font, label_font_scale, label_font_color, label_font_thickness, label_font_line)) cnt += 1 sql = get_deployment_radar_only_detailed_query(devices_list_str, time_from_str, time_to_str, ids_list) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None or my_data == []: return False @@ -8663,7 +8741,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, if True: wave_m = np.zeros((stripes, 1440, 3), dtype=np.uint8) wave_m = create_radar_optimized_heatmap(my_data, bw, fields, wave_m, device_to_index, base_minute, time_zone_s) - print(time.time()-st) + logger.debug(time.time()-st) if False: #base_minute = my_data[0][0]# min(record[0] for record in my_data) @@ -8700,7 +8778,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, # Set RGB values (all same for grayscale) wave_m[y, x] = BestColor(gray_value) - print(time.time()-st) + logger.debug(time.time()-st) st = time.time() for yy in range(stripes): @@ -8709,7 +8787,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, y = yy * stretch_by + stretch_index arr_stretched[y, :] = rgb_row - print(time.time()-st) + logger.debug(time.time()-st) SaveImageInBlob(image_file, arr_stretched, labels) #arr_source[2*gate, :] = wave_m @@ -8720,12 +8798,12 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, #if sens_val != 0: #r,g,b=BestColor(km*(sens_val-m_min)) #if r > 255 or g > 255 or b > 255: - #print(r,g,b) + #logger.debug(r,g,b) #rgb_row[col] = r,g,b #for stretch_index in range(stretch_by): #y = device_counter * (18*stretch_by) + 2*gate * stretch_by + stretch_index - ##print(y, row, devices_c, sensor_index, location_index, stretch_index) + ##logger.debug(y, row, devices_c, sensor_index, location_index, stretch_index) ##arr_stretched[y, :] = rgb_row #if gate > 1: @@ -8755,7 +8833,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, #b = r #else: #r,g,b=BestColor(ks*(sens_val-s_min)) - ##print(r,g,b) + ##logger.debug(r,g,b) #rgb_row[col] = r,g,b #for stretch_index in range(stretch_by): @@ -8764,7 +8842,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, #y = device_counter * (18*stretch_by) + (2*(gate)) * stretch_by + stretch_index #arr_stretched[y, :] = rgb_row - print("stop") + logger.debug("stop") elif (chart_type == 3): #"digital" device_counter = 0 for details in devices_list: @@ -8828,12 +8906,12 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, sens_val = wave_m[col] if sens_val != 0: r,g,b=BestColor(km*(sens_val-m_min)) - #print(r,g,b) + #logger.debug(r,g,b) rgb_row[col] = r,g,b for stretch_index in range(stretch_by): y = device_counter * (18*stretch_by) + 2*gate * stretch_by + stretch_index - #print(y, row, devices_c, sensor_index, location_index, stretch_index) + #logger.debug(y, row, devices_c, sensor_index, location_index, stretch_index) #arr_stretched[y, :] = rgb_row @@ -8864,7 +8942,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, b = r else: r,g,b=BestColor(ks*(sens_val-s_min)) - #print(r,g,b) + #logger.debug(r,g,b) rgb_row[col] = r,g,b for stretch_index in range(stretch_by): @@ -8874,7 +8952,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, arr_stretched[y, :] = rgb_row device_counter += 1 - print("stop") + logger.debug("stop") elif (chart_type == 4): #"collapsed" @@ -8908,12 +8986,12 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, ids_list.append(details[1]) sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None: return False @@ -8948,9 +9026,9 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, result_np = None try: result_np = process_wave_data_numpy(image_file, my_data, time_zone_s, device_id_2_threshold, radar_fields_of_interest) - print(time.time() - st) + logger.debug(time.time() - st) except Exception as err: - print(str(err)) + logger.debug(str(err)) if False: for record in my_data: @@ -8971,7 +9049,7 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, - print(time.time()-st) + logger.debug(time.time()-st) if result_np is not None: wave_m = result_np rgb_row = np.zeros(( 1440, 3), dtype=np.uint8) @@ -9006,14 +9084,14 @@ def CreateFullLocationMap(location_image_file, devices_list, selected_date, present_at[-1][2] += 1 except Exception as err: - print(str(err)) + logger.debug(str(err)) for stretch_index in range(stretch_by): y = stretch_index arr_stretched[y, :] = rgb_row - #print("stop") - #print(r,g,b) + #logger.debug("stop") + #logger.debug(r,g,b) SaveObjectInBlob(image_file+".bin", present_at) SaveImageInBlob(image_file, arr_stretched, []) @@ -9216,12 +9294,12 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d labels.append((descriptor, (10, vertical_offset + title_labels_height+40+text_height + (cnt)*fields_n*stretch_by), label_font, label_font_scale, label_font_color, label_font_thickness, label_font_line)) cnt += 1 sql = get_deployment_radar_only_detailed_query(devices_list_str, time_from_str, time_to_str, ids_list) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data != None and my_data != []: device_to_index = {device: idx for idx, device in enumerate(ids_list)} @@ -9232,7 +9310,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d if True: wave_m = np.zeros((stripes, 1440, 3), dtype=np.uint8) wave_m = create_radar_optimized_heatmap(my_data, bw, fields_s, wave_m, device_to_index, base_minute, time_zone_s) - print(time.time()-st) + logger.debug(time.time()-st) st = time.time() @@ -9242,7 +9320,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d y = yy * radar_stretch_by + stretch_index arr_stretched[title_labels_height+y, 200:] = rgb_row - print(time.time()-st) + logger.debug(time.time()-st) vertical_offset = vertical_offset + title_labels_height + stripes*radar_stretch_by ######################################## LIGHT ################################################################## if show_light: @@ -9285,7 +9363,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d # Get light data using the existing query function sql = get_deployment_light_only_query(devices_list_str, time_from_str, time_to_str, light_ids_list) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) @@ -9306,7 +9384,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d wave_m = create_light_optimized_heatmap(my_data, bw, fields_s, wave_m, device_to_index, base_minute, time_zone_s, min_val, max_val) - print(f"Light heatmap creation time: {time.time()-st:.4f} seconds") + logger.debug(f"Light heatmap creation time: {time.time()-st:.4f} seconds") # Stretch the heatmap vertically st = time.time() @@ -9322,10 +9400,10 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d if target_y < arr_stretched.shape[0]: arr_stretched[target_y, labels_width:] = rgb_row else: - print(f"Warning: Row {target_y} is out of bounds (max: {arr_stretched.shape[0]-1})") + logger.debug(f"Warning: Row {target_y} is out of bounds (max: {arr_stretched.shape[0]-1})") vertical_offset = vertical_offset + title_labels_height + stripes*stretch_by - print(f"Light stretching time: {time.time()-st:.4f} seconds") + logger.debug(f"Light stretching time: {time.time()-st:.4f} seconds") ######################################## TEMPERATURE ################################################################## if show_temperature: @@ -9380,7 +9458,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d # Get temperature data sql = get_deployment_temperature_only_query(devices_list_str, time_from_str, time_to_str, temp_ids_list, temp_offset) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) @@ -9414,7 +9492,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d wave_m = create_temperature_optimized_heatmap(my_data, bw, fields_s, wave_m, device_to_index, base_minute, time_zone_s, min_val, max_val) - print(f"Temperature heatmap creation time: {time.time()-st:.4f} seconds") + logger.debug(f"Temperature heatmap creation time: {time.time()-st:.4f} seconds") # Stretch the heatmap with different heights for temperature and alarm st = time.time() @@ -9441,7 +9519,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d if target_y < arr_stretched.shape[0]: arr_stretched[target_y, labels_width:] = alarm_row - print(f"Temperature stretching time: {time.time()-st:.4f} seconds") + logger.debug(f"Temperature stretching time: {time.time()-st:.4f} seconds") ######################################## HUMIDITY ################################################################## ''' @@ -9502,7 +9580,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d # Get humidity data sql = get_deployment_humidity_only_query(devices_list_str, time_from_str, time_to_str, temp_ids_list, humidity_offset) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) @@ -9536,7 +9614,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d wave_m = create_humidity_optimized_heatmap(my_data, bw, fields_s, wave_m, device_to_index, base_minute, time_zone_s, min_val, max_val) - print(f"Humidity heatmap creation time: {time.time()-st:.4f} seconds") + logger.debug(f"Humidity heatmap creation time: {time.time()-st:.4f} seconds") # Stretch the heatmap with different heights for humidity and alarm st = time.time() @@ -9563,7 +9641,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d if target_y < arr_stretched.shape[0]: arr_stretched[target_y, labels_width:] = alarm_row - print(f"Temperature stretching time: {time.time()-st:.4f} seconds") + logger.debug(f"Temperature stretching time: {time.time()-st:.4f} seconds") ######################################## SMELL ################################################################## if show_smell: @@ -9606,7 +9684,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d # Get smell data sql = get_deployment_smell_only_query(devices_list_str, time_from_str, time_to_str, temp_ids_list, smell_offset) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) @@ -9643,7 +9721,7 @@ def CreateFullLocationMapLabelsOut(location_image_file, devices_list, selected_d cnt += 1 SaveImageInBlobLabelsOut(image_file, arr_stretched, labels, title_labels) - print("stop") + logger.debug("stop") @@ -9694,12 +9772,12 @@ def CreateDailyLocationMap(location_image_file, devices_list, selected_date, fil ids_list.append(details[1]) sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None: return False @@ -9734,9 +9812,9 @@ def CreateDailyLocationMap(location_image_file, devices_list, selected_date, fil try: result_np = process_wave_data_numpy(image_file, my_data, time_zone_s, device_id_2_threshold, radar_fields_of_interest) - print(time.time() - st) + logger.debug(time.time() - st) except Exception as err: - print(str(err)) + logger.debug(str(err)) if False: for record in my_data: @@ -9757,7 +9835,7 @@ def CreateDailyLocationMap(location_image_file, devices_list, selected_date, fil - print(time.time()-st) + logger.debug(time.time()-st) wave_m = result_np rgb_row = np.zeros(( 1440, 3), dtype=np.uint8) rgbsorted_row = np.zeros(( 1440, 3), dtype=np.uint8) @@ -9797,11 +9875,11 @@ def CreateDailyLocationMap(location_image_file, devices_list, selected_date, fil present_at[-1][2] += 1 except Exception as err: - print(str(err)) + logger.debug(str(err)) start_minute = 0 for color_key in sorted(presence_minutes): - print(color_key, presence_minutes[color_key]) + #logger.debug(color_key, presence_minutes[color_key]) rgbsorted_row[start_minute:start_minute+presence_minutes[color_key][0]] = presence_minutes[color_key][1][::-1] start_minute += presence_minutes[color_key][0] @@ -9809,13 +9887,13 @@ def CreateDailyLocationMap(location_image_file, devices_list, selected_date, fil SaveObjectInBlob(image_file+".bin", present_at) #present_at_back_s = ReadObjectMinIO("daily-maps", image_file+".bin") #present_at_back = json.loads(present_at_back_s) - #print(present_at_back) + #logger.debug(present_at_back) for stretch_index in range(stretch_by): y = stretch_index arr_stretched[y, :] = rgb_row arr_stretched_sorted[y, :] = rgbsorted_row - #print("stop") - #print(r,g,b) + #logger.debug("stop") + #logger.debug(r,g,b) SaveImageInBlob(image_file, arr_stretched, []) SaveImageInBlob(image_file[:-4]+"S.png", arr_stretched_sorted, []) @@ -10052,7 +10130,7 @@ def CreateMapFast_hybrid(map_file, devices_list, selected_date, bw, time_zone_s, time_from_str, time_to_str = GetLocalTimeForDate(selected_date, time_zone_s) temp_offset = -10 sql = get_deployment_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_part, temp_offset) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) @@ -10065,7 +10143,7 @@ def CreateMapFast_hybrid(map_file, devices_list, selected_date, bw, time_zone_s, has_old_format = any(record[17] in [0, 17] for record in day_data if len(record) > 17 and record[17] is not None) has_new_format = any(100 <= record[17] <= 170 for record in day_data if len(record) > 17 and record[17] is not None) - #print(f"Data format detected: old={has_old_format}, new={has_new_format}") + #logger.debug(f"Data format detected: old={has_old_format}, new={has_new_format}") # Don't support mixed formats #if has_old_format and has_new_format: @@ -10141,7 +10219,7 @@ def CreateMapFast(map_file, devices_list, selected_date, bw, time_zone_s, radar_ time_from_str, time_to_str = GetLocalTimeForDate(selected_date, time_zone_s) temp_offset = -10 sql = get_deployment_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_part, temp_offset) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -10424,6 +10502,273 @@ def get_deployment_query_80(devices_list_str, time_from_str, time_to_str, ids_li """ return sql +def get_deployment_deca_query_80(devices_list_str, time_from_str, time_to_str, ids_list, temp_offset): + """ + Generate a TimeScaleDB query for sensor and radar readings with 80 smell components. + Uses 10-second time buckets. + + Maps mtype values to column ranges: + - mtype 0, 17, 100 -> s0-s9 + - mtype 110 -> s10-s19 + - mtype 120 -> s20-s29 + - ... up to mtype 170 -> s70-s79 + """ + # Generate the CASE statement for ordering based on the provided ids_list + case_statements = [] + for index, device_id in enumerate(ids_list, start=1): + case_statements.append(f"WHEN {device_id} THEN {index}") + case_order = "\n ".join(case_statements) + + # Generate column mappings for each mtype range + column_mappings = [] + + # S0-S9 (mtype = 0, 17, or 100) + for i in range(10): + column_mappings.append(f"MIN(CASE WHEN mtype IN (0, 17, 100) AND s{i} > 0 THEN s{i} END) AS min_s{i}") + + # S10-S79 (mtype = 110 to 170) + for mtype_offset in range(11, 18): # 110, 120, 130, 140, 150, 160, 170 + mtype_value = mtype_offset * 10 + base_col = (mtype_offset - 10) * 10 + for i in range(10): + source_col = i + target_col = base_col + i + column_mappings.append(f"MIN(CASE WHEN mtype = {mtype_value} AND s{source_col} > 0 THEN s{source_col} END) AS min_s{target_col}") + + columns_sql = ",\n ".join(column_mappings) + + # Generate the smell columns for the outer SELECT + outer_smell_columns = [] + for i in range(80): + outer_smell_columns.append(f"sr.min_s{i} as smell_s{i}") + outer_smell_str = ",\n ".join(outer_smell_columns) + + sql = f""" + SELECT + COALESCE(sr.minute, rr.minute) as minute, + COALESCE(sr.device_id, rr.device_id) as device_id, + sr.avg_temperature+{temp_offset} as avg_temperature, + sr.avg_humidity, + sr.avg_pressure, + sr.max_light, + {outer_smell_str}, + rr.absent as radar_absent, + rr.moving as radar_moving, + rr.stationary as radar_stationary, + rr.both as radar_both, + rr.m0 as radar_m0, + rr.m1 as radar_m1, + rr.m2 as radar_m2, + rr.m3 as radar_m3, + rr.m4 as radar_m4, + rr.m5 as radar_m5, + rr.m6 as radar_m6, + rr.m7 as radar_m7, + rr.m8 as radar_m8, + rr.s2 as radar_s2, + rr.s3 as radar_s3, + rr.s4 as radar_s4, + rr.s5 as radar_s5, + rr.s6 as radar_s6, + rr.s7 as radar_s7, + rr.s8 as radar_s8 + FROM ( + SELECT + time_bucket('10 seconds', time) AS minute, + device_id, + AVG(temperature) AS avg_temperature, + AVG(humidity) AS avg_humidity, + AVG(pressure) AS avg_pressure, + MAX(light) AS max_light, + {columns_sql} + FROM + sensor_readings + WHERE + device_id IN ({devices_list_str}) + AND time >= '{time_from_str}' + AND time < '{time_to_str}' + AND mtype IN (0, 17, 100, 110, 120, 130, 140, 150, 160, 170) + GROUP BY + minute, + device_id + ) sr + FULL OUTER JOIN ( + SELECT + time_bucket('10 seconds', time) AS minute, + device_id, + MAX(absent) AS absent, + MAX(moving) AS moving, + MAX(stationary) AS stationary, + MAX(\"both\") AS both, + MAX(m0) AS m0, + MAX(m1) AS m1, + MAX(m2) AS m2, + MAX(m3) AS m3, + MAX(m4) AS m4, + MAX(m5) AS m5, + MAX(m6) AS m6, + MAX(m7) AS m7, + MAX(m8) AS m8, + MAX(s2) AS s2, + MAX(s3) AS s3, + MAX(s4) AS s4, + MAX(s5) AS s5, + MAX(s6) AS s6, + MAX(s7) AS s7, + MAX(s8) AS s8 + FROM + radar_readings + WHERE + device_id IN ({devices_list_str}) + AND time >= '{time_from_str}' + AND time < '{time_to_str}' + GROUP BY + minute, + device_id + ) rr + ON sr.minute = rr.minute AND sr.device_id = rr.device_id + ORDER BY + CASE COALESCE(sr.device_id, rr.device_id) + {case_order} + END, + COALESCE(sr.minute, rr.minute); + """ + return sql + + +def get_deployment_rd_query_80(devices_list_str, time_from_str, time_to_str, ids_list, temp_offset): + """ + Generate a TimeScaleDB query for sensor and radar readings with 80 smell components. + Uses 1-minute time buckets (radar detailed). + + Maps mtype values to column ranges: + - mtype 0, 17, 100 -> s0-s9 + - mtype 110 -> s10-s19 + - mtype 120 -> s20-s29 + - ... up to mtype 170 -> s70-s79 + """ + # Generate the CASE statement for ordering based on the provided ids_list + case_statements = [] + for index, device_id in enumerate(ids_list, start=1): + case_statements.append(f"WHEN {device_id} THEN {index}") + case_order = "\n ".join(case_statements) + + # Generate column mappings for each mtype range + column_mappings = [] + + # S0-S9 (mtype = 0, 17, or 100) + for i in range(10): + column_mappings.append(f"MIN(CASE WHEN mtype IN (0, 17, 100) AND s{i} > 0 THEN s{i} END) AS min_s{i}") + + # S10-S79 (mtype = 110 to 170) + for mtype_offset in range(11, 18): # 110, 120, 130, 140, 150, 160, 170 + mtype_value = mtype_offset * 10 + base_col = (mtype_offset - 10) * 10 + for i in range(10): + source_col = i + target_col = base_col + i + column_mappings.append(f"MIN(CASE WHEN mtype = {mtype_value} AND s{source_col} > 0 THEN s{source_col} END) AS min_s{target_col}") + + columns_sql = ",\n ".join(column_mappings) + + # Generate the smell columns for the outer SELECT + outer_smell_columns = [] + for i in range(80): + outer_smell_columns.append(f"sr.min_s{i} as smell_s{i}") + outer_smell_str = ",\n ".join(outer_smell_columns) + + sql = f""" + SELECT + COALESCE(sr.minute, rr.minute) as minute, + COALESCE(sr.device_id, rr.device_id) as device_id, + sr.avg_temperature+{temp_offset} as avg_temperature, + sr.avg_humidity, + sr.avg_pressure, + sr.max_light, + {outer_smell_str}, + rr.absent as radar_absent, + rr.moving as radar_moving, + rr.stationary as radar_stationary, + rr.both as radar_both, + rr.m0 as radar_m0, + rr.m1 as radar_m1, + rr.m2 as radar_m2, + rr.m3 as radar_m3, + rr.m4 as radar_m4, + rr.m5 as radar_m5, + rr.m6 as radar_m6, + rr.m7 as radar_m7, + rr.m8 as radar_m8, + rr.s2 as radar_s2, + rr.s3 as radar_s3, + rr.s4 as radar_s4, + rr.s5 as radar_s5, + rr.s6 as radar_s6, + rr.s7 as radar_s7, + rr.s8 as radar_s8 + FROM ( + SELECT + time_bucket('1 minute', time) AS minute, + device_id, + AVG(temperature) AS avg_temperature, + AVG(humidity) AS avg_humidity, + AVG(pressure) AS avg_pressure, + MAX(light) AS max_light, + {columns_sql} + FROM + sensor_readings + WHERE + device_id IN ({devices_list_str}) + AND time >= '{time_from_str}' + AND time < '{time_to_str}' + AND mtype IN (0, 17, 100, 110, 120, 130, 140, 150, 160, 170) + GROUP BY + minute, + device_id + ) sr + FULL OUTER JOIN ( + SELECT + time_bucket('1 minute', time) AS minute, + device_id, + MAX(absent) AS absent, + MAX(moving) AS moving, + MAX(stationary) AS stationary, + MAX(\"both\") AS both, + MAX(m0) AS m0, + MAX(m1) AS m1, + MAX(m2) AS m2, + MAX(m3) AS m3, + MAX(m4) AS m4, + MAX(m5) AS m5, + MAX(m6) AS m6, + MAX(m7) AS m7, + MAX(m8) AS m8, + MAX(s2) AS s2, + MAX(s3) AS s3, + MAX(s4) AS s4, + MAX(s5) AS s5, + MAX(s6) AS s6, + MAX(s7) AS s7, + MAX(s8) AS s8 + FROM + radar_readings + WHERE + device_id IN ({devices_list_str}) + AND time >= '{time_from_str}' + AND time < '{time_to_str}' + GROUP BY + minute, + device_id + ) rr + ON sr.minute = rr.minute AND sr.device_id = rr.device_id + ORDER BY + CASE COALESCE(sr.device_id, rr.device_id) + {case_order} + END, + COALESCE(sr.minute, rr.minute); + """ + return sql + def unwrap_smell_only_data(day_data, devices_list, time_from_str): """ A specialized version of unwrap_sensor_data that handles a data format @@ -10505,7 +10850,7 @@ def unwrap_sensor_data(day_data, devices_list, time_from_str): device_id = record[1] device_ids_in_raw[device_id] = device_ids_in_raw.get(device_id, 0) + 1 - #print(f"Raw data device distribution: {device_ids_in_raw}") + #logger.debug(f"Raw data device distribution: {device_ids_in_raw}") start_time = datetime.datetime.strptime(time_from_str, '%Y-%m-%d %H:%M:%S%z') data_by_minute = defaultdict(list) @@ -10602,7 +10947,7 @@ def unwrap_sensor_data(day_data, devices_list, time_from_str): # Track device distribution in unwrapped data device_ids_in_unwrapped[device_id] = device_ids_in_unwrapped.get(device_id, 0) + 1 - #print(f"Unwrapped data device distribution: {device_ids_in_unwrapped}") + #logger.debug(f"Unwrapped data device distribution: {device_ids_in_unwrapped}") return unwrapped_data def fast_fill_array_from_unwrapped(unwrapped_data, devices_list, arr_source, time_from_str): @@ -10612,7 +10957,7 @@ def fast_fill_array_from_unwrapped(unwrapped_data, devices_list, arr_source, tim """ device_to_index = {device_id: idx for idx, device_id in enumerate(devices_list)} - #print(f"Device mapping: {device_to_index}") + #logger.debug(f"Device mapping: {device_to_index}") start_time = datetime.datetime.strptime(time_from_str, '%Y-%m-%d %H:%M:%S%z') @@ -10624,7 +10969,7 @@ def fast_fill_array_from_unwrapped(unwrapped_data, devices_list, arr_source, tim for record in unwrapped_data: device_id = record[1] if device_id not in device_to_index: - #print(f"Skipping device_id {device_id} - not in device_to_index") + #logger.debug(f"Skipping device_id {device_id} - not in device_to_index") continue # Calculate minute from timestamp @@ -10632,12 +10977,12 @@ def fast_fill_array_from_unwrapped(unwrapped_data, devices_list, arr_source, tim minute = int((record_time - start_time).total_seconds() / 60) if minute < 0 or minute >= arr_source.shape[1]: - #print(f"Skipping minute {minute} - out of bounds") + #logger.debug(f"Skipping minute {minute} - out of bounds") continue #records_processed += 1 #if records_processed <= 5: # Debug first 5 records - # print(f"Processing record {records_processed}: device_id={device_id}, minute={minute}") + # logger.debug(f"Processing record {records_processed}: device_id={device_id}, minute={minute}") device_idx = device_to_index[device_id] @@ -10661,10 +11006,10 @@ def fast_fill_array_from_unwrapped(unwrapped_data, devices_list, arr_source, tim arr_source[row_idx, minute] = value #values_written += 1 - #print(f"Data distribution: {device_data_count}") - #print(f"devices_list: {devices_list}") - #print(f"Total records processed: {records_processed}") - #print(f"Total values written: {values_written}") + #logger.debug(f"Data distribution: {device_data_count}") + #logger.debug(f"devices_list: {devices_list}") + #logger.debug(f"Total records processed: {records_processed}") + #logger.debug(f"Total values written: {values_written}") return arr_source def get_deployment_single_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_part, temp_offset, sensor_in): @@ -12091,12 +12436,12 @@ def export_query_to_minio_chunked(connection_params, query, minio_client, bucket len(csv_bytes) ) - print(f"Data exported successfully to MinIO: {bucket_name}/{blob_name}") + logger.debug(f"Data exported successfully to MinIO: {bucket_name}/{blob_name}") return blob_name except Exception as e: - print(f"Error exporting data: {str(e)}") - print(f"Traceback: {traceback.format_exc()}") + logger.debug(f"Error exporting data: {str(e)}") + logger.debug(f"Traceback: {traceback.format_exc()}") raise finally: @@ -12131,11 +12476,11 @@ def export_query_to_csv_pandas(connection_params, query, output_path=None): # Export to CSV with all headers df.to_csv(output_path, index=False) - print(f"Data exported successfully to {output_path}") + logger.debug(f"Data exported successfully to {output_path}") return output_path except Exception as e: - print(f"Error exporting data: {str(e)}") + logger.debug(f"Error exporting data: {str(e)}") raise finally: @@ -12179,11 +12524,11 @@ def CreateDailyCSV(csv_file, devices_list, selected_date, vocs_scaled, time_zone if consolidated_by == "by_minute_rc": sql = get_deployment_query_80(devices_list_str, time_from_str, time_to_str, ids_list, radar_part, temp_offset) elif consolidated_by == "by_deca_rd": - sql = get_deployment_deca_query(devices_list_str, time_from_str, time_to_str, ids_list, temp_offset) + sql = get_deployment_deca_query_80(devices_list_str, time_from_str, time_to_str, ids_list, temp_offset) elif consolidated_by == "by_minute_rd": - sql = get_deployment_rd_query(devices_list_str, time_from_str, time_to_str, ids_list, temp_offset) + sql = get_deployment_rd_query_80(devices_list_str, time_from_str, time_to_str, ids_list, temp_offset) - print(sql) + logger.debug(sql) connection_params = { 'host': DB_HOST, @@ -12810,7 +13155,7 @@ def clean_data_with_rolling_spline(line_part_t, window=5, threshold=2.0): y_cleaned = y.copy() y_cleaned[outlier_mask] = spline(x[outlier_mask]) except Exception as e: - print(f"Spline interpolation failed: {e}") + logger.debug(f"Spline interpolation failed: {e}") return line_part_t # Return in the same format as input @@ -12896,7 +13241,7 @@ def zip_blobs(blob_paths, zip_blob_name, bucket_name, minio_client=None): return True except Exception as e: - print(f"Error creating zip file: {str(e)}") + logger.debug(f"Error creating zip file: {str(e)}") return False finally: # Clean up @@ -12972,7 +13317,7 @@ def clean_data(line_part_t, window=5, threshold=2.0): if deviation <= threshold * window_median: cleaned_data.append((x[i], y[i])) #else: - #print(window_values) + #logger.debug(window_values) return cleaned_data def clean_data_fast(line_part_t, window=5, threshold=2.0): @@ -13022,7 +13367,7 @@ def clean_data_pd(line_part_t, window=5, percentile=99): deviations = np.abs(series - medians) largest_deviations = deviations.nlargest(10) - #print(largest_deviations) + #logger.debug(largest_deviations) # Create mask for good points deviation_threshold = np.percentile(deviations, percentile) @@ -13061,7 +13406,7 @@ def CombineStripes(result_filename, stripes_files): return True except Exception as e: - print("Error:", e) + logger.debug(f"Error:{e}") return False def FindFirstLocalMinimum(counts, bins): @@ -13392,7 +13737,7 @@ def FindZeroIntersection(counts, bins, save_plot, device_id): return zero_intersections, peak_position except RuntimeError: - print("Warning: Failed to fit parabola") + logger.debug("Warning: Failed to fit parabola") return None, peak_position @@ -13475,7 +13820,7 @@ def AddText(room_image_cv2, x, y, room_name, font_size): pil_im = Image.fromarray(room_image_cv2) draw = ImageDraw.Draw(pil_im) font_path = os.path.join(os.path.dirname(__file__), "fonts", "Poppins-Regular.ttf") - #print(f"Attempting to load font from: {font_path}") + #logger.debug(f"Attempting to load font from: {font_path}") try: font = ImageFont.truetype(font_path, font_size) # 12px size except: @@ -13513,7 +13858,7 @@ def AddRoomData(room_image, room_name, data): x_offset = 12 room_image = AddText(room_image, 13, 20, room_name, 50) - print(data) + logger.debug(data) for present in data["presence"]: device_id, minute, duration = present #duration = 10 @@ -13634,7 +13979,7 @@ def GenerateLocationsMap(date_st, devices_list, devices_map, locations_list, tim entry[2] = (current_minute_of_day - entry[2]) if entry[2] > 0: filtered_presence_data.append(entry) - #print(presence_data) + #logger.debug(presence_data) else: filtered_presence_data = presence_data @@ -13682,7 +14027,7 @@ def CreateDailyLocationChart(filename_chart_image_day, locations): SaveImageInBlob(filename_chart_image_day, final_image, []) result = True #cv2.imwrite(filename_chart_image_day, final_image) - #print(rooms_count) + #logger.debug(rooms_count) return result @@ -14309,7 +14654,7 @@ def AskGPT(in_prompt, language_from, language_to): prompt = in_prompt + " Answer in " + language_to - print(prompt) + logger.debug(prompt) #lets see if question is looking for OSM query pattern = "what is only the node line for query for * on openstreetmap api? do not answer with url to nominatim, but with query!" @@ -14320,11 +14665,11 @@ def AskGPT(in_prompt, language_from, language_to): if differing_part != "": - print(differing_part) + logger.debug(differing_part) if differing_part in searches_dict: response = searches_dict[differing_part] - print(response) + logger.debug(response) return response, language_to else: #check if one of synonims: @@ -14333,7 +14678,7 @@ def AskGPT(in_prompt, language_from, language_to): if differing_part != "": if differing_part in searches_dict[differing_part]: response = searches_dict[differing_part] - print(response) + logger.debug(response) return response, language_to hash_string = hashlib.sha256(str(prompt).encode('utf-8')).hexdigest() @@ -14363,7 +14708,7 @@ def AskGPT(in_prompt, language_from, language_to): #run = wandb.init(project='GPT-4 in Python') #prediction_table = wandb.Table(columns=["prompt", "prompt tokens", "completion", "completion tokens", "model", "total tokens"]) - print(time.time() - st) + logger.debug(time.time() - st) openai.api_key = OPENAI_API_KEY client = OpenAI( @@ -14393,7 +14738,7 @@ def AskGPT(in_prompt, language_from, language_to): response = ("question is too short", language_to) - print(response) + logger.debug(response) return response def AskGPTPure(in_prompt): @@ -14402,11 +14747,11 @@ def AskGPTPure(in_prompt): prompt = in_prompt.lower() - print(prompt) + logger.debug(prompt) st = time.time() - print(time.time() - st) + logger.debug(time.time() - st) openai.api_key = OPENAI_API_KEY client = OpenAI( @@ -14431,7 +14776,7 @@ def AskGPTPure(in_prompt): response = "question is too short" - print(response) + logger.debug(response) return response def get_last_n_days(n=14, timezone_str='America/Los_Angeles'): @@ -14561,7 +14906,7 @@ def RunCommand(commmand, args_dictionary, deployment_id): locations_list_s = ReadObjectMinIO("daily-maps", filename_day) locations_list = ast.literal_eval(locations_list_s) minutes_locations_list.append((ddate, locations_list)) - #print(locations_list_s) + #logger.debug(locations_list_s) minutes_spent_there = {} @@ -14570,11 +14915,11 @@ def RunCommand(commmand, args_dictionary, deployment_id): minutes_spent_there[Id2Location[0]] = 0 for loc in locations_list: - #print(loc[0]) + #logger.debug(loc[0]) #if loc[0] == 559: - # print("Stop") + # logger.debug("Stop") if loc[0] in Id2Location: - print(Id2Location[loc[0]]) + logger.debug(Id2Location[loc[0]]) minutes_spent_there[Id2Location[loc[0]]] += loc[2] for loc in minutes_spent_there: @@ -14583,9 +14928,9 @@ def RunCommand(commmand, args_dictionary, deployment_id): data_part = str(minutes_spent_there_list) minutes_locations_list_str = str(minutes_locations_list) obj_to_save = {"Location_indexes": str(Id2Location), "Locations": minutes_locations_list_str} - print(obj_to_save) + logger.debug(obj_to_save) #SaveObjectInBlob(filename_4w, obj_to_save) - #print(data_part) + #logger.debug(data_part) #prompt = "Attached is 4 weeks of data representing % of time where person living alone is spending each day" #prompt = prompt + " Assess his last week compared to previous 3 weeks. Comment only on significant changes." @@ -14615,11 +14960,32 @@ def RunCommand(commmand, args_dictionary, deployment_id): elif commmand == "#HELP#": to_return = "There is number of things you can ask me about. For example: 'how is my dad doing?' Or 'How is his environment' or any other question you like" elif commmand == "#SLEEP#": - to_return = "Your dad slept approximately 8 hours last night, took a shower before bed, and got up 4 times during the night." + to_return = "Your dad slept approximately 11.4 hours last night, took a shower before bed, and got up 4 times during the night." elif commmand == "#SLEEP_F#": - to_return = "Your mom slept approximately 8 hours last night, took a shower before bed, and got up 4 times during the night." + to_return = "Your mom slept approximately 11.4 hours last night, took a shower before bed, and got up 4 times during the night." elif commmand == "#ENVIRONMENT#": to_return = "The temperature in the house is 23 degrees Celsius, CO2 level is 662 ppm, and I can smell coffee brewing. Your dad slept approximately 8 hours last night, took a shower before bed, and got up 4 times during the night." + elif commmand == "#SASHASENVIRONMENT#": + filterr = 5 + deployment_id = 45 + time_zone_s = GetTimeZoneOfDeployment(deployment_id) + local_tz = pytz.timezone(time_zone_s) + + filter_minutes = 5 + dates = get_last_n_days(28, time_zone_s) + ddate = dates[0] #2025-02-02 req.params.get("date") + to_date = dates[-1] + date_s = datetime.datetime.now(pytz.UTC).astimezone(local_tz).date().strftime("%Y-%m-%d") + details = GetSensorsDetailsFromDeployment(deployment_id, date_s, filterr) + current_time = datetime.datetime.now(datetime.timezone.utc) + last_location = details["last_location"] + temperature = int(details["temperature"]) + if "America" in time_zone_s or "US/" in time_zone_s: + temperature_sentence = f"{int(CelsiusToFahrenheit(temperature))} degrees Farenhight" + else: + temperature_sentence = f"{temperature} degrees Celsius." + to_return = f"The temperature in the {last_location} is {temperature_sentence}, CO2 level is 662 ppm.." + #to_return = "The temperature in the bedroom is 68 degrees Farenhight, CO2 level is 662 ppm." elif commmand == "#WEEK#": to_return = "Showing his weekly activity" elif commmand == "#WEEK_F#": @@ -14645,6 +15011,30 @@ def RunCommand(commmand, args_dictionary, deployment_id): # Convert to UTC detected_utc_time = local_time.astimezone(pytz.UTC) + time_diff = current_time - detected_utc_time + minutes = time_diff.total_seconds() / 60 + #patch... needs investigating todo + if minutes > 1400: + minutes = 0 + time_sentence = format_time_difference(minutes) + if minutes < 2: + to_return = f"He is now in the {last_location} for {last_present_duration} minutes. Before that he was in {before_last_location}" + else: + to_return = f"He was last detected in the {last_location} {time_sentence} ago" + elif commmand == "#SASHALOCATION#": + filterr = 5 + deployment_id = 45 + details = GetSensorsDetailsFromDeployment(deployment_id, date_s, filterr) + #current_time = datetime.datetime.now() + current_time = datetime.datetime.now(datetime.timezone.utc) + last_location = details["last_location"] + before_last_location = details["before_last_location"] + last_present_duration = details["last_present_duration"] + detected_time = datetime.datetime.fromisoformat(details["last_detected_time"]) + local_time = local_tz.localize(detected_time) + # Convert to UTC + detected_utc_time = local_time.astimezone(pytz.UTC) + time_diff = current_time - detected_utc_time minutes = time_diff.total_seconds() / 60 #patch... needs investigating todo @@ -14656,9 +15046,9 @@ def RunCommand(commmand, args_dictionary, deployment_id): else: to_return = f"He was last detected in the {last_location} {time_sentence} ago" elif commmand == "#SHOWER#": - to_return = "In the last 7 days, your Dad took a shower on Friday, Sunday and Tuesday" + to_return = "In the last 7 days, your Dad took a shower on Friday" elif commmand == "#SHOWER_F#": - to_return = "The last time your mom took a shower was Yesterda at 9:33AM" + to_return = "The last time your mom took a shower was Yesterday at 9:33AM" elif commmand == "#BATHROOM#": to_return = "Last night your Dad used the restroom only once at 6.10am" elif commmand == "#KITCHEN#": @@ -14687,9 +15077,9 @@ def RunCommand(commmand, args_dictionary, deployment_id): elif commmand == "#HEART_RATE#": to_return = "His last heart rate was 74 bpm." elif commmand == "#BLOOD_PRESSURE#": - to_return = "His latest blood pressure was measured 5 hours ago and it was 137 over 83." + to_return = "His latest blood pressure was measured yesterda 11:45 AM and it was 137 over 83." elif commmand == "#BLOOD_PRESSURE_F#": - to_return = "Her latest blood pressure was measured 5 hours ago and it was 137 over 83." + to_return = "Her latest blood pressure was measured yesterda 11:45 AM and it was 137 over 83." elif commmand == "#EKG#": to_return = "His latest HeartBeam EKG was done on Monday and it was within his baseline!" elif commmand == "#EKG_F#": @@ -14904,21 +15294,21 @@ def convert_timestamps_lc(data, time_zone_s): subbedToL = [("/wellget",1),("/wellget_cmp",1),("/well_hub",1)] def on_connectL(client_, userdata, flags, rc): - print(MQTTSERVERL + " L. Connected with result code "+str(rc)) + logger.debug(MQTTSERVERL + " L. Connected with result code "+str(rc)) # Subscribing in on_connect() means that if we lose the connection and # reconnect then subscriptions will be renewed. client_.subscribe(subbedToL) - print("SubscribedL to: "+str(subbedToL)) + logger.debug("SubscribedL to: "+str(subbedToL)) def on_messageL(client_, userdata, msg): #message from GUI - print(msg.topic+" "+str(msg.payload)) + logger.debug(msg.topic+" "+str(msg.payload)) #msga = msg.payload.decode("ascii") - #print(msg.timestamp) + #logger.debug(msg.timestamp) #in_queue.append((str(time.time()), msg.topic, msg.payload)) def MQSendL(topic, content, qos=1): - print(topic, content[0:100]) + #print(topic, content[0:100]) #return MQSend(topic, content) #currentTime = int(time.time()) try: @@ -14940,7 +15330,7 @@ def StoreFloorPlan(deployment_id, layout): conn = get_db_connection() cur = conn.cursor() - print(layout) + logger.debug(layout) data = json.loads(layout) # Extract the overlapping list @@ -15139,7 +15529,7 @@ def FindDeviceByRole(deployment_id, location_list): #we need to find beneficiaries from list of deployments #sql = f'SELECT device_id FROM public.devices where device_id in {device_ids} and other="other"' sql = "SELECT device_id, location, well_id FROM public.devices WHERE device_id = ANY(%s) AND other = %s" - #print(sql) + #logger.debug(sql) cur.execute(sql, (device_ids, "other")) result = cur.fetchall()#cur.fetchone() if len(result) > 0: @@ -15230,7 +15620,7 @@ def filter_short_groups_numpy_orig(presence_list, filter_size, device_id, dates_ st = time.time() if not presence_list or filter_size <= 1: - # print(f"NumPy: Early exit/no processing time: {time.time() - st:.6f}s") + # logger.debug(f"NumPy: Early exit/no processing time: {time.time() - st:.6f}s") # Return a copy to avoid modifying the original list return presence_list[:] if isinstance(presence_list, list) else list(presence_list) @@ -15244,7 +15634,7 @@ def filter_short_groups_numpy_orig(presence_list, filter_size, device_id, dates_ while True: current_state_tuple = tuple(result) if current_state_tuple in previous_states: - # print("NumPy: Cycle detected, breaking.") + # logger.debug("NumPy: Cycle detected, breaking.") break previous_states.add(current_state_tuple) @@ -15313,9 +15703,9 @@ def filter_short_groups_numpy_orig(presence_list, filter_size, device_id, dates_ result[start:end] = replacement_value # Loop continues because a change was made - print(f"filter_short_groups_numpy time: {time.time() - st:.6f}s") + logger.debug(f"filter_short_groups_numpy time: {time.time() - st:.6f}s") if (time.time() - st) > 40: - print(presence_list) + logger.debug(presence_list) # Convert back to a standard Python list for the return value return result.tolist() @@ -15335,7 +15725,7 @@ def filter_short_groups_numpy(presence_list, filter_size, device_id, dates_str): st = time.time() if not presence_list or filter_size <= 1: - # print(f"NumPy Optimized: Early exit/no processing time: {time.time() - st:.6f}s") + # logger.debug(f"NumPy Optimized: Early exit/no processing time: {time.time() - st:.6f}s") return presence_list[:] if isinstance(presence_list, list) else list(presence_list) result = np.array(presence_list, dtype=float) @@ -15347,7 +15737,7 @@ def filter_short_groups_numpy(presence_list, filter_size, device_id, dates_str): # Cycle detection current_state_tuple = tuple(result) if current_state_tuple in previous_states: - # print("NumPy Optimized: Cycle detected, breaking.") + # logger.debug("NumPy Optimized: Cycle detected, breaking.") break previous_states.add(current_state_tuple) @@ -15409,9 +15799,9 @@ def filter_short_groups_numpy(presence_list, filter_size, device_id, dates_str): # End timer and print (optional) # Your original print statements for timing: - print(f"filter_short_groups_numpy time: {time.time() - st:.6f}s") + logger.debug(f"filter_short_groups_numpy time: {time.time() - st:.6f}s") # if (time.time() - st) > 40: - # print(presence_list) # This would print the original input on long runs + # logger.debug(presence_list) # This would print the original input on long runs return result.tolist() @@ -15431,7 +15821,7 @@ def filter_short_groups(presence_list, filter_size): st = time.time() if not presence_list or filter_size <= 1: - # print(f"filter_short_groups: Early exit/no processing time: {time.time() - st:.6f}s") + # logger.debug(f"filter_short_groups: Early exit/no processing time: {time.time() - st:.6f}s") return presence_list.copy() result = presence_list.copy() @@ -15443,7 +15833,7 @@ def filter_short_groups(presence_list, filter_size): while True: current_state_tuple = tuple(result) if current_state_tuple in previous_states: - # print("Cycle detected in filter_short_groups, breaking.") + # logger.debug("Cycle detected in filter_short_groups, breaking.") break previous_states.add(current_state_tuple) @@ -15504,7 +15894,7 @@ def filter_short_groups(presence_list, filter_size): # If we went through all segments and made no changes, we're done. break - print(f"filter_short_groups time: {time.time() - st:.6f}s") + logger.debug(f"filter_short_groups time: {time.time() - st:.6f}s") return result @@ -15561,10 +15951,10 @@ def filter_short_groupss(presence_list, filter_size): for j in range(start, end+1): result[j] = replacement changes_made = True - #print(start, end) + #logger.debug(start, end) break # Break after making a change and restart - print("s", time.time()-st) + logger.debug(f"s {time.time()-st}") return result def filter_short_segments(segments, filter_size): @@ -15881,7 +16271,7 @@ def filter_short_high_groups_iterative_analog(presence_list, filter_size): # Don't break - process all short segments in this pass # If we've made changes, we need to check again for newly formed short segments - print(f"filter_short_high_groups_iterative_analog time: {time.time() - st:.6f}s") + logger.debug(f"filter_short_high_groups_iterative_analog time: {time.time() - st:.6f}s") return result def filter_short_high_groups_iterative_analog_orig(presence_list, filter_size): @@ -15926,9 +16316,9 @@ def filter_short_high_groups_iterative_analog_orig(presence_list, filter_size): changes_made = True break - print(f"filter_short_high_groups_iterative_analog time: {time.time() - st:.6f}s") + logger.debug(f"filter_short_high_groups_iterative_analog time: {time.time() - st:.6f}s") #if (time.time() - st) > 40: - # print(presence_list) + # logger.debug(presence_list) return result @@ -16006,10 +16396,10 @@ def CreateZGraph(well_id, presence_list): return presence_list.copy() #if well_id == 290: - # print("Stop") + # logger.debug("Stop") dekas_in_day = 6 * 1440 result = [] - print(well_id) + logger.debug(well_id) #result will look like this: [(0,34),(34,-56),(92,6),...] where (A,B) #A: is minute of section, B: height of section +=presence -=absence #lets find point 0 first moving backward in time @@ -16272,7 +16662,7 @@ def ClearOverlaps(temporary_map_day_plus, overlaps_str_lst): for i in range(decas_in_data): if data_list[i] > 0: # Presence interval #if i == (8721): - # print("stop") + # logger.debug("stop") seen_where_list[i].append((location_id, data_list[i])) # Parse overlap pairs @@ -16290,9 +16680,9 @@ def ClearOverlaps(temporary_map_day_plus, overlaps_str_lst): continue #if i == (5713 + 8640): - # print("stop") + # logger.debug("stop") #if i == (8721): - # print("stop") + # logger.debug("stop") # Create a new list to store the filtered results filtered_list = [] @@ -16334,7 +16724,7 @@ def ClearOverlaps(temporary_map_day_plus, overlaps_str_lst): # Update the original list with filtered results #if i == (8721): - # print("stop") + # logger.debug("stop") seen_where_list[i] = filtered_list # Create a new temporary_map_day_plus with the filtered data @@ -16346,8 +16736,8 @@ def ClearOverlaps(temporary_map_day_plus, overlaps_str_lst): for i in range(decas_in_data): #if len(seen_where_list[i]) > 1: #if i == (8721): - # print("stop") - #print(i, decas_to_time(i), seen_where_list[i]) + # logger.debug("stop") + #logger.debug(i, decas_to_time(i), seen_where_list[i]) for device_id, signal_strength in seen_where_list[i]: result[device_id][i] = signal_strength @@ -16437,7 +16827,7 @@ def optimized_processing(myz_data, start_time, id2well_id, device_id_2_threshold if process_all and radar_val > threshold and deca < days_decas: temporary_map_day_plus[well_id][deca] = radar_val #if well_id == 269: - # print(local_time) + # logger.debug(local_time) return temporary_map_day_plus @@ -16504,7 +16894,7 @@ def optimized_radar_processing(my_data, start_time, id2well_id, device_id_2_thre if well_id == 475: - print(".") + logger.debug(".") # Process presence data if radar_val > threshold: if deca < days_decas: @@ -16560,11 +16950,11 @@ def store_to_file(my_list, filename): try: with open(filename, 'w') as f: json.dump(my_list, f, indent=4) # indent for pretty printing - print(f"List saved to {filename} using JSON") + logger.debug(f"List saved to {filename} using JSON") except IOError: - print(f"Error: Could not write to file {filename}") + logger.debug(f"Error: Could not write to file {filename}") except TypeError as e: - print(f"Error: Could not serialize list to JSON. {e}") # e.g. if list contains unsupported types like sets + logger.debug(f"Error: Could not serialize list to JSON. {e}") # e.g. if list contains unsupported types like sets def find_custom_header(headers, name): """Helper to find a custom header value (case-insensitive name).""" @@ -16661,7 +17051,7 @@ def ParseAddress(address_string): ) return response.json() except Exception as e: - print(f"Error: {e}") + logger.debug(f"Error: {e}") return {} def GetTZFromGPS(latitude, longitude): @@ -16680,7 +17070,7 @@ def GetTZFromGPS(latitude, longitude): return "" except Exception as e: - print(f"Error: {e}") + logger.debug(f"Error: {e}") return "" def JoinAddress(address_map): @@ -16694,7 +17084,7 @@ def JoinAddress(address_map): ) return response.json() except Exception as e: - print(f"Error: {e}") + logger.debug(f"Error: {e}") return {} @@ -16747,7 +17137,7 @@ def StoreToDB(data): if sql != "": with get_db_connection() as conn: with conn.cursor() as cur: - print(sql) + logger.debug(sql) cur.execute(sql) except Exception as e: print ("Error in StoreToDB:", e) @@ -17443,7 +17833,7 @@ def CreateSensorsMapFast(map_file, devices_list, selected_date, bw, time_zone_s, filtered_s_table[0] ) - print(sql) + logger.debug(sql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -17800,7 +18190,7 @@ def GetCalibMaps(device_ids_list): with conn.cursor() as cur: #list all devices that user has access to sql = f"SELECT device_id, temperature_calib, humidity_calib FROM public.devices WHERE device_id in ({str(device_ids_list)[1:-1]})" - print(sql) + logger.debug(sql) cur.execute(sql) calib_records = cur.fetchall()#cur.fetchone() for record in calib_records: @@ -17808,6 +18198,978 @@ def GetCalibMaps(device_ids_list): humid_calib[record[0]] = record[2] return temp_calib, humid_calib + +# Place these new function definitions before the "class WellApi:" line. + +#=== WellDry API functions definitions ===# + +# -------------------------------------------------------------------------------------------------- +# - API: job_list2 +# - Fetches a list of jobs with optional search filtering. +# - POST: function: job_list2 +# - Parameters: +# - user_name: (CSP) The requesting user's username. +# - token: (CSP) The authentication token. +# - search: search string to be searched over concatenated with spaces +# (job_id+" "+customer_name+" "+address+date_from+" "+job_status+" "+mitigation_person), +# if empty return all +# - Output (JSON): +# - On Success: { +# "ok": 1, +# "jobs": [{"job_id": 1, "customer_name": "...", "address": "...", "date_from": "...", "job_status": "Active", "mitigation_person": "..."}, ...]} +# - On Failure: {"ok": 0, "error": "Error message"} +# -------------------------------------------------------------------------------------------------- +def get_job_list2(user_id, privileges, search_term=""): + """Fetches a list of jobs based on user privileges and optional search term.""" + conn = get_db_connection() + try: + with conn.cursor() as cur: + # Base query columns + base_cols = """ + j.job_id, j.customer_name, j.address_street, j.address_city, + j.date_from, j.job_status, p.first_name, p.last_name + """ + + # Base joins + joins = "FROM public.jobs j LEFT JOIN public.person_details p ON j.mitigation_person_id = p.user_id" + + # Build Where Clause + where_clauses = [] + params = [] + + # 1. Privilege Filter + if privileges != "-1": + where_clauses.append("j.mitigation_person_id = %s") + params.append(user_id) + + # 2. Search Filter + if search_term and search_term.strip(): + # Concatenate fields for searching. COALESCE ensures NULLs don't break the concat. + # Fields: job_id, customer_name, address, date_from, job_status, mitigation_person + search_sql = """ + CONCAT_WS(' ', + j.job_id::text, + COALESCE(j.customer_name, ''), + COALESCE(j.address_street, ''), + COALESCE(j.address_city, ''), + COALESCE(j.date_from::text, ''), + COALESCE(j.job_status, ''), + COALESCE(p.first_name, ''), + COALESCE(p.last_name, '') + ) ILIKE %s + """ + where_clauses.append(search_sql) + params.append(f"%{search_term.strip()}%") + + # Construct Final SQL + where_str = " WHERE " + " AND ".join(where_clauses) if where_clauses else "" + sql = f""" + SELECT {base_cols} + {joins} + {where_str} + ORDER BY CASE j.job_status WHEN 'Active' THEN 1 WHEN 'Stopped' THEN 2 ELSE 3 END, j.date_from DESC; + """ + + cur.execute(sql, tuple(params)) + + jobs = [] + for row in cur.fetchall(): + mitigation_person = f"{row[6]} {row[7]}" if row[6] and row[7] else "N/A" + jobs.append({ + "job_id": row[0], + "customer_name": row[1], + "address": f"{row[2] or ''}, {row[3] or ''}".strip(', '), + "date_from": row[4].isoformat() if row[4] else None, + "job_status": row[5], + "mitigation_person": mitigation_person + }) + return {"ok": 1, "jobs": jobs} + except Exception as e: + logger.error(f"Error in get_job_list2: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_list +# - Fetches a list of jobs for the dashboard. +# - POST: function: job_list +# - Parameters: +# - user_name: (CSP) The requesting user's username. +# - token: (CSP) The authentication token. +# - Output (JSON): +# - On Success: {"ok": 1, "jobs": [{"job_id": 1, "customer_name": "...", "address": "...", "date_from": "...", "job_status": "Active", "mitigation_person": "..."}, ...]} +# - On Failure: {"ok": 0, "error": "Error message"} +# - Explanation: +# - Retrieves a list of jobs based on user privileges. Admins (privileges='-1') see all jobs, +# - while regular users see only jobs assigned to them via 'mitigation_person_id'. +# - The list is sorted to show 'Active' jobs first, then by the most recent start date. +# -------------------------------------------------------------------------------------------------- +def get_job_list(user_id, privileges): + """Fetches a list of jobs based on user privileges.""" + conn = get_db_connection() + try: + with conn.cursor() as cur: + if privileges == "-1": + sql = """ + SELECT j.job_id, j.customer_name, j.address_street, j.address_city, j.date_from, j.job_status, p.first_name, p.last_name + FROM public.jobs j + LEFT JOIN public.person_details p ON j.mitigation_person_id = p.user_id + ORDER BY CASE j.job_status WHEN 'Active' THEN 1 WHEN 'Stopped' THEN 2 ELSE 3 END, j.date_from DESC; + """ + cur.execute(sql) + else: + sql = """ + SELECT j.job_id, j.customer_name, j.address_street, j.address_city, j.date_from, j.job_status, p.first_name, p.last_name + FROM public.jobs j + LEFT JOIN public.person_details p ON j.mitigation_person_id = p.user_id + WHERE j.mitigation_person_id = %s + ORDER BY CASE j.job_status WHEN 'Active' THEN 1 WHEN 'Stopped' THEN 2 ELSE 3 END, j.date_from DESC; + """ + cur.execute(sql, (user_id,)) + + jobs = [] + for row in cur.fetchall(): + mitigation_person = f"{row[6]} {row[7]}" if row[6] and row[7] else "N/A" + jobs.append({ + "job_id": row[0], + "customer_name": row[1], + "address": f"{row[2] or ''}, {row[3] or ''}".strip(', '), + "date_from": row[4].isoformat() if row[4] else None, + "job_status": row[5], + "mitigation_person": mitigation_person + }) + return {"ok": 1, "jobs": jobs} + except Exception as e: + logger.error(f"Error in get_job_list: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_details +# - Fetches all details for a single job. +# - POST: function: job_details +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - job_id: (Required) integer - The ID of the job to fetch. +# - Output (JSON): +# - On Success: {"ok": 1, "details": {}} +# - On Failure: {"ok": 0, "error": "Job not found" | "Access denied"} +# - Explanation: +# - Retrieves a complete record for a single job_id. It performs an authorization check +# - to ensure the user is either an admin or the assigned mitigation person. +# -------------------------------------------------------------------------------------------------- +def get_job_details(job_id, user_id, privileges): + """Fetches all details for a single job, checking for user access.""" + conn = get_db_connection() + try: + with conn.cursor() as cur: + sql = "SELECT * FROM public.jobs WHERE job_id = %s;" + cur.execute(sql, (job_id,)) + job_data = cur.fetchone() + + if not job_data: + return {"ok": 0, "error": "Job not found"} + + colnames = [desc[0] for desc in cur.description] + job_dict = dict(zip(colnames, job_data)) + + if privileges != "-1" and job_dict.get('mitigation_person_id') != int(user_id): + return {"ok": 0, "error": "Access denied"} + + # Convert datetime objects to ISO 8601 strings for JSON serialization + for key in ['date_from', 'date_to', 'created_at']: + if key in job_dict and isinstance(job_dict[key], datetime.datetime): + job_dict[key] = job_dict[key].isoformat() + + return {"ok": 1, "details": job_dict} + except Exception as e: + logger.error(f"Error in get_job_details: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_create +# - Creates a new job. +# - POST: function: job_create +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - customer_name: (Required) string +# - address_street, address_city, etc.: (Optional) strings +# - devices: (Optional) JSON string representing an array of objects, e.g., '[{"mac": "mac1", "location": "Kitchen"}, ...]' +# - alerts_config: (Optional) JSON string object, e.g., '{"temp_abs_high": 30}' +# - Output (JSON): +# - On Success: {"ok": 1, "job_id": } +# - On Failure: {"ok": 0, "error": "Error message"} +# - Explanation: +# - Inserts a new record into the 'jobs' table. The 'mitigation_person_id' is automatically +# - set to the ID of the authenticated user making the request. +# -------------------------------------------------------------------------------------------------- +def create_job(form_data, user_id): + """Creates a new job in the jobs table.""" + conn = get_db_connection() + try: + with conn.cursor() as cur: + sql = """ + INSERT INTO public.jobs ( + customer_name, mitigation_person_id, address_street, address_city, + address_zip, address_state, address_country, lat, lng, + devices, alerts_config, user_edit, key_person_name, key_person_mobile, key_person_email + ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) + RETURNING job_id; + """ + # Ensure JSON fields are properly formatted strings + alerts_config_json = form_data.get('alerts_config', '{}') + devices_json = form_data.get('devices', '[]') + + cur.execute(sql, ( + form_data.get('customer_name'), user_id, form_data.get('address_street'), + form_data.get('address_city'), form_data.get('address_zip'), + form_data.get('address_state'), form_data.get('address_country'), + form_data.get('lat'), form_data.get('lng'), + devices_json, alerts_config_json, user_id, + form_data.get('key_person_name'), form_data.get('key_person_mobile'), form_data.get('key_person_email') + )) + new_job_id = cur.fetchone()[0] + conn.commit() + return {"ok": 1, "job_id": new_job_id} + except Exception as e: + conn.rollback() + logger.error(f"Error in create_job: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_edit +# - Updates an existing job. +# - POST: function: job_edit +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - job_id: (Required) integer +# - Any other field from the 'jobs' table to be updated (e.g., customer_name, devices). +# - To stop a job, send 'job_status': 'Stopped' and 'date_to': ''. +# - Output (JSON): +# - On Success: {"ok": 1, "job_id": } +# - On Failure: {"ok": 0, "error": "Error message"} +# - Explanation: +# - Dynamically builds an UPDATE statement based on the provided fields. +# - Verifies that the user has permission to edit the job. +# -------------------------------------------------------------------------------------------------- +def edit_job(form_data, user_id, privileges): + """Updates an existing job.""" + conn = get_db_connection() + job_id = form_data.get('job_id') + if not job_id: + return {"ok": 0, "error": "job_id is required"} + + try: + with conn.cursor() as cur: + # Authorization check + if privileges != "-1": + cur.execute("SELECT mitigation_person_id FROM public.jobs WHERE job_id = %s", (job_id,)) + owner = cur.fetchone() + if not owner or owner[0] != int(user_id): + return {"ok": 0, "error": "Access denied"} + + update_fields = [] + update_values = [] + allowed_fields = [ + 'customer_name', 'address_street', 'address_city', 'address_zip', + 'address_state', 'address_country', 'lat', 'lng', 'devices', + 'alerts_config', 'job_status', 'date_to', 'key_person_name', + 'key_person_mobile', 'key_person_email' + ] + + for field in allowed_fields: + if field in form_data: + update_fields.append(f"{field} = %s") + value = form_data[field] + # Ensure JSON fields are passed as strings + if field in ['alerts_config', 'devices'] and not isinstance(value, str): + value = json.dumps(value) + # Automatically set status to 'Stopped' if date_to is provided + if field == 'date_to' and value and 'job_status' not in form_data: + update_fields.append("job_status = %s") + update_values.append('Stopped') + update_values.append(value) + + if not update_fields: + return {"ok": 1, "message": "No fields to update"} + + # Always track who made the edit + update_fields.append("user_edit = %s") + update_values.append(user_id) + update_values.append(job_id) + + sql = f"UPDATE public.jobs SET {', '.join(update_fields)} WHERE job_id = %s;" + cur.execute(sql, tuple(update_values)) + conn.commit() + + return {"ok": 1, "job_id": job_id} + except Exception as e: + conn.rollback() + logger.error(f"Error in edit_job: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_devices +# - Fetches devices with filtering (assigned/unassigned) and search. +# - POST: function: job_devices +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - filter "all", "assigned", "unassigned" (if not passed or “” is equivalent to “all”) +# - search: search string to be searched over concatenated with spaces +# (device_id+" "+well_id+" "+device_mac+location+" "+description+" "+customer_name), +# if empty return all +# - Output (JSON): +# - On Success: {"ok": 1, "devices": [{"device_id": 1, "well_id": 101, "device_mac": "...", "description": "...", "customer_name": "...", ...}]} +# -------------------------------------------------------------------------------------------------- +def get_job_devices(user_id, privileges, filter_type="all", search_term=""): + """ + Returns devices visible to user, marked as assigned/unassigned based on Active jobs. + Supports filtering and searching. + """ + conn = get_db_connection() + try: + # --- Step 1: Get User's Allowed Deployments --- + deployment_ids = [] + with conn.cursor() as cur: + if str(privileges) == "-1": + cur.execute("SELECT deployment_id FROM public.deployment_details") + rows = cur.fetchall() + deployment_ids = [row[0] for row in rows] + else: + cur.execute("SELECT access_to_deployments FROM public.person_details WHERE user_id = %s", (user_id,)) + row = cur.fetchone() + if row and row[0]: + try: + raw_access = str(row[0]) + deployment_ids = [int(x.strip()) for x in raw_access.split(',') if x.strip().isdigit()] + except ValueError: + pass + + if not deployment_ids: + return {"ok": 1, "devices": []} + + # --- Step 2: Fetch Inventory (All devices in allowed deployments) --- + # We fetch location (int) and description to help with search + inventory = [] + with conn.cursor() as cur: + sql_inventory = """ + SELECT d.device_id, d.well_id, d.device_mac, d.description, d.location + FROM public.devices d + JOIN public.deployment_details dd ON dd.deployment_id = ANY(%s) + WHERE dd.devices LIKE '%%' || d.device_mac || '%%' + """ + cur.execute(sql_inventory, (deployment_ids,)) + inventory = cur.fetchall() + + # --- Step 3: Identify Assigned Devices (Active Jobs) --- + # Map MAC -> Customer Name for active jobs + assigned_map = {} + with conn.cursor() as cur: + sql_busy = """ + SELECT value->>'mac', customer_name + FROM public.jobs, jsonb_array_elements(devices::jsonb) as value + WHERE job_status = 'Active' + """ + cur.execute(sql_busy) + rows = cur.fetchall() + for row in rows: + if row[0]: + assigned_map[row[0]] = row[1] if row[1] else "" + + # --- Step 4: Merge, Filter, and Search --- + final_devices = [] + search_lower = search_term.lower().strip() if search_term else "" + + # Normalize filter + if not filter_type: filter_type = "all" + filter_type = filter_type.lower() + + seen_macs = set() + + for row in inventory: + d_id, d_well, d_mac, d_desc, d_loc_id = row + + if not d_mac or d_mac in seen_macs: + continue + seen_macs.add(d_mac) + + # Determine Status + is_assigned = d_mac in assigned_map + customer_name = assigned_map.get(d_mac, "") + + # Apply Type Filter + if filter_type == "assigned" and not is_assigned: + continue + if filter_type == "unassigned" and is_assigned: + continue + + # Resolve Location Name from ID (using global location_names dict) + loc_name = location_names.get(d_loc_id, "?") if d_loc_id is not None else "?" + + # Apply Search Filter + if search_lower: + # Concatenate: device_id well_id device_mac location description customer_name + search_source = f"{d_id} {d_well} {d_mac} {loc_name} {d_desc or ''} {customer_name}".lower() + if search_lower not in search_source: + continue + + # Add to result + final_devices.append({ + "device_id": d_id, + "well_id": d_well, + "device_mac": d_mac, + "location": loc_name, + "description": d_desc or "", + "status": "assigned" if is_assigned else "unassigned", + "customer_name": customer_name + }) + + return {"ok": 1, "devices": final_devices} + + except Exception as e: + logger.error(f"Error in get_job_devices: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_available_devices2 +# - Finds devices available for a new job using GetProximityList for inventory lookup. +# - POST: function: job_available_devices2 +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - Output (JSON): +# - On Success: {"ok": 1, "devices": [{"device_id": 1, "well_id": 101, "device_mac": "...", ...}]} +# - Explanation: +# - 1. Identifies deployments the user has access to. +# - 2. Calls GetProximityList and parses the result to extract device_id and well_id (QR code). +# - 3. Filters out devices currently assigned to 'Active' jobs. +# -------------------------------------------------------------------------------------------------- +def get_available_devices2(user_id, privileges): + """Finds devices in user's inventory not assigned to active jobs.""" + conn = get_db_connection() + try: + deployment_ids = [] + + # 1. Determine Deployment IDs + with conn.cursor() as cur: + if privileges == "-1": + cur.execute("SELECT deployment_id FROM public.deployment_details") + rows = cur.fetchall() + deployment_ids = [row[0] for row in rows] + else: + cur.execute("SELECT access_to_deployments FROM public.person_details WHERE user_id = %s", (user_id,)) + row = cur.fetchone() + if row and row[0]: + try: + deployment_ids = [int(x.strip()) for x in str(row[0]).split(',') if x.strip().isdigit()] + except ValueError: + logger.error(f"Error parsing access_to_deployments for user {user_id}") + + if not deployment_ids: + return {"ok": 1, "devices": []} + + # 2. Collect Candidate Devices via GetProximityList + candidate_devices = [] + current_time = time.time() + + for dep_id in deployment_ids: + try: + prox_result = GetProximityList(dep_id, current_time) + + # Unwrap logic + device_rows = [] + if isinstance(prox_result, (list, tuple)): + if len(prox_result) > 0 and isinstance(prox_result[0], list): + device_rows = prox_result[0] + else: + device_rows = prox_result + else: + continue + + for dev in device_rows: + d_mac = None + d_id = None + d_desc = "" + d_well = None # Initialize as None, do not default to dep_id + + # --- Robust Parsing Logic --- + if isinstance(dev, dict): + d_mac = dev.get('device_mac') + d_id = dev.get('device_id') + d_desc = dev.get('description', '') + d_well = dev.get('well_id') + + elif hasattr(dev, 'device_mac'): + d_mac = dev.device_mac + d_id = dev.device_id + d_desc = getattr(dev, 'description', '') + d_well = getattr(dev, 'well_id', None) + + elif isinstance(dev, (list, tuple)): + # Structure from MACsToWellIds is: (well_id, device_id, location_name, description, mac, ...) + + # 1. Find MAC (usually at index 4) + for idx, val in enumerate(dev): + if isinstance(val, str) and len(val) == 12 and all(c in '0123456789ABCDEFabcdef' for c in val): + d_mac = val + break + + # 2. Extract ID and Details based on tuple structure + if d_mac: + # Index 0 is well_id, Index 1 is device_id + if len(dev) > 0 and isinstance(dev[0], int): + d_well = dev[0] + if len(dev) > 1 and isinstance(dev[1], int): + d_id = dev[1] + + # Index 2 is Location Name, Index 3 is Description + if len(dev) > 3 and isinstance(dev[3], str): + d_desc = dev[3] + elif len(dev) > 2 and isinstance(dev[2], str): + # Fallback if description is missing but location is present + # d_desc = dev[2] # Uncomment if you want location as fallback + pass + + if d_mac: + candidate_devices.append({ + "device_id": d_id, + "well_id": d_well, # This is the device specific QR ID + "device_mac": d_mac, + "description": d_desc + }) + except Exception as e: + logger.error(f"Error calling GetProximityList for deployment {dep_id}: {e}") + continue + + if not candidate_devices: + return {"ok": 1, "devices": []} + + # 3. Identify "Busy" MACs + busy_macs = set() + with conn.cursor() as cur: + sql_busy = """ + SELECT value->>'mac' + FROM public.jobs, jsonb_array_elements(devices::jsonb) as value + WHERE job_status = 'Active' + """ + cur.execute(sql_busy) + rows = cur.fetchall() + for row in rows: + if row[0]: + busy_macs.add(row[0]) + + # 4. Filter Candidates + available_devices = [d for d in candidate_devices if d['device_mac'] not in busy_macs] + + return {"ok": 1, "devices": available_devices} + + except Exception as e: + logger.error(f"Error in get_available_devices2: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_available_devices +# - Finds devices available for a new job based on user's access rights. +# - POST: function: job_available_devices +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - Output (JSON): +# - On Success: {"ok": 1, "devices": [{"device_id": 1, "well_id": 101, "device_mac": "...", ...}]} +# - Explanation: +# - 1. Determines which deployments (well_ids) the user is allowed to access. +# - 2. Fetches all devices belonging to those deployments. +# - 3. Filters out devices that are currently assigned to 'Active' jobs. +# -------------------------------------------------------------------------------------------------- +def get_available_devices(user_id, privileges): + """Finds devices in user's inventory not assigned to active jobs.""" + conn = get_db_connection() + try: + deployment_ids = [] + + with conn.cursor() as cur: + # --- Step 1: Get User's Allowed Deployments --- + if str(privileges) == "-1": + # Admin: Get ALL deployment IDs + cur.execute("SELECT deployment_id FROM public.deployment_details") + rows = cur.fetchall() + deployment_ids = [row[0] for row in rows] + else: + # Regular User: Get assigned deployments from person_details + cur.execute("SELECT access_to_deployments FROM public.person_details WHERE user_id = %s", (user_id,)) + row = cur.fetchone() + if row and row[0]: + # Parse CSV string "101, 102" -> [101, 102] + try: + raw_access = str(row[0]) + deployment_ids = [int(x.strip()) for x in raw_access.split(',') if x.strip().isdigit()] + except ValueError: + logger.error(f"Error parsing access_to_deployments for user {user_id}") + + # If user has no deployments, they have no devices. + if not deployment_ids: + return {"ok": 1, "devices": []} + + with conn.cursor() as cur: + # --- Step 2: Fetch Inventory (Devices in allowed deployments) --- + # FIX: Explicitly select d.well_id (QR Code) instead of dd.deployment_id + sql_inventory = """ + SELECT d.device_id, d.well_id, d.device_mac, d.description + FROM public.devices d + JOIN public.deployment_details dd ON dd.deployment_id = ANY(%s) + WHERE dd.devices LIKE '%%' || d.device_mac || '%%' + """ + cur.execute(sql_inventory, (deployment_ids,)) + inventory_rows = cur.fetchall() + + if not inventory_rows: + return {"ok": 1, "devices": []} + + # --- Step 3: Identify "Busy" MACs (Currently in Active Jobs) --- + # We extract MACs from the JSONB 'devices' column in the jobs table + sql_busy = """ + SELECT value->>'mac' + FROM public.jobs, jsonb_array_elements(devices::jsonb) as value + WHERE job_status = 'Active' + """ + cur.execute(sql_busy) + busy_rows = cur.fetchall() + busy_macs = set(row[0] for row in busy_rows if row[0]) + + # --- Step 4: Filter and Format --- + available_devices = [] + seen_macs = set() + for row in inventory_rows: + d_id, d_well, d_mac, d_desc = row + + # Only add if NOT in the busy list and NOT duplicate + if d_mac and d_mac not in busy_macs and d_mac not in seen_macs: + available_devices.append({ + "device_id": d_id, + "well_id": d_well, # This is the unique device QR ID + "device_mac": d_mac, + "description": d_desc or "" + }) + seen_macs.add(d_mac) + + return {"ok": 1, "devices": available_devices} + + except Exception as e: + logger.error(f"Error in get_available_devices: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: + conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_weather +# - Fetches current weather for a job's location using its stored latitude and longitude. +# - POST: function: job_weather +# - Parameters: +# - user_name: (CSP) The requesting user. +# - token: (CSP) The authentication token. +# - job_id: (Required) integer - The ID of the job. +# - Output (JSON): +# - On Success: {"ok": 1, "weather": {"temp_c": 15.0, "temp_f": 59.0, "humidity": 82, "condition": "Partly cloudy"}} +# - On Failure: {"ok": 0, "error": "Error message"} +# - Explanation: +# - Retrieves the lat/lng from the 'jobs' table for the given job_id. +# - Makes a server-side request to a third-party weather API (WeatherAPI.com). +# - Requires a 'WEATHER_API_KEY' to be set in the server's environment variables. +# -------------------------------------------------------------------------------------------------- +def get_wmo_condition_text(code): + """Maps WMO weather codes to human readable strings""" + codes = { + 0: "Clear sky", + 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast", + 45: "Fog", 48: "Depositing rime fog", + 51: "Light drizzle", 53: "Moderate drizzle", 55: "Dense drizzle", + 56: "Light freezing drizzle", 57: "Dense freezing drizzle", + 61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain", + 66: "Light freezing rain", 67: "Heavy freezing rain", + 71: "Slight snow fall", 73: "Moderate snow fall", 75: "Heavy snow fall", + 77: "Snow grains", + 80: "Slight rain showers", 81: "Moderate rain showers", 82: "Violent rain showers", + 85: "Slight snow showers", 86: "Heavy snow showers", + 95: "Thunderstorm", 96: "Thunderstorm with slight hail", 99: "Thunderstorm with heavy hail" + } + return codes.get(code, "Unknown") +# -------------------------------------------------------------------------------------------------- +# - API: job_weather2 (alternative) +# - POST: function: job_weather, job_id +# - Returns: Weather data from WeatherAPI.com based on job Lat/Lng. +# -------------------------------------------------------------------------------------------------- +def get_job_weather2(job_id): + conn = get_db_connection() + try: + with conn.cursor() as cur: + cur.execute("SELECT lat, lng FROM public.jobs WHERE job_id = %s", (job_id,)) + res = cur.fetchone() + if not res or not res[0] or not res[1]: + return {"ok": 0, "error": "Job location (lat/lng) not set."} + lat, lng = res + + api_key = os.getenv('WEATHER_API_KEY') + if not api_key: + logger.error("WEATHER_API_KEY environment variable not set.") + return {"ok": 0, "error": "Weather service is not configured on the server."} + + # External call to WeatherAPI + url = f"http://api.weatherapi.com/v1/current.json?key={api_key}&q={lat},{lng}&aqi=no" + r = requests.get(url, timeout=4) + r.raise_for_status() + data = r.json().get('current', {}) + + return {"ok": 1, "weather": { + "time": data.get('last_updated'), # e.g., "2025-11-01 10:00" + "temp_c": data.get('temp_c'), + "temp_f": data.get('temp_f'), + "humidity": data.get('humidity'), + "condition": data.get('condition', {}).get('text') + }} + except Exception as e: + logger.error(f"WeatherAPI error: {e}") + # Fallback to Open-Meteo if key fails or limit reached + return get_job_weather2(job_id) + finally: + if conn: conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: job_weather +# - POST: function: job_weather (swappable), job_id +# - Returns: Weather data from Open-Meteo (Free) based on job Lat/Lng. +# -------------------------------------------------------------------------------------------------- +def get_job_weather(job_id): + """ + Alternative weather fetch using Open-Meteo (No API Key required). + Adapted to use standard requests to avoid extra dependencies. + """ + conn = get_db_connection() + try: + with conn.cursor() as cur: + cur.execute("SELECT lat, lng FROM public.jobs WHERE job_id = %s", (job_id,)) + res = cur.fetchone() + if not res or not res[0] or not res[1]: + return {"ok": 0, "error": "Job location (lat/lng) not set."} + lat, lng = res + + # Open-Meteo Endpoint + url = "https://api.open-meteo.com/v1/forecast" + params = { + "latitude": lat, + "longitude": lng, + "current": "temperature_2m,relative_humidity_2m,weather_code", + "timezone": "auto" + } + + r = requests.get(url, params=params, timeout=5) + r.raise_for_status() + data = r.json() + + current = data.get('current', {}) + + # Convert WMO code to text + wmo_code = current.get('weather_code') + condition = get_wmo_condition_text(wmo_code) + + # Temps + temp_c = current.get('temperature_2m') + temp_f = (temp_c * 9/5) + 32 if temp_c is not None else None + + # Time formatting (Open-Meteo returns ISO like "2025-11-18T14:00") + time_str = current.get('time', '').replace('T', ' ') + + return {"ok": 1, "weather": { + "time": time_str, + "temp_c": temp_c, + "temp_f": round(temp_f, 1) if temp_f else None, + "humidity": current.get('relative_humidity_2m'), + "condition": condition + }} + + except Exception as e: + logger.error(f"Open-Meteo error: {traceback.format_exc()}") + return {"ok": 0, "error": "Weather service unavailable"} + finally: + if conn: conn.close() + +# -------------------------------------------------------------------------------------------------- +# - API: get_job_sensor_bucketed_data +# - Fetches historical sensor data for all devices assigned to a specific job. +# - POST: function: get_job_sensor_bucketed_data +# - Parameters: +# - user_name: (CSP) Requesting user. +# - token: (CSP) Auth token. +# - job_id: (Required) ID of the job. +# - sensor: (Required) Sensor type (temperature, humidity, light, pressure, voc, radar, co2). +# - date: (Required) Start date (ISO string or YYYY-MM-DD). +# - to_date: (Optional) End date. Defaults to 'date' if not provided. +# - bucket_size: (Optional) Aggregation window (10s, 1m, 5m, 10m, 15m, 30m, 1h). Default '15m'. +# - data_type: (Optional) 'ML' or standard. +# - radar_part: (Optional) Specific radar column (e.g., 's28'). +# - Output (JSON): +# - {"ok": 1, "chart_data": [{ "name": "Kitchen", "data": [[time, value], ...] }, ...], ...} +# - Explanation: +# - Looks up the job to find assigned devices and their job-specific locations. +# - Resolves device MACs to internal DB IDs. +# - Queries TimeScaleDB (via ReadSensor3) for each device. +# - Returns time-series data formatted for frontend charting. +# -------------------------------------------------------------------------------------------------- +def get_job_sensor_bucketed_data(form_data): + conn = get_db_connection() + try: + job_id = form_data.get('job_id') + sensor = form_data.get('sensor') + date_from_str = form_data.get('date') + date_to_str = form_data.get('to_date') or date_from_str + bucket_size = form_data.get('bucket_size', '15m') + data_type = form_data.get('data_type') + radar_part = form_data.get('radar_part') + + # 1. Fetch Job Details (Devices and Location) + with conn.cursor() as cur: + cur.execute("SELECT devices, lat, lng FROM public.jobs WHERE job_id = %s", (job_id,)) + job_row = cur.fetchone() + if not job_row: + return {"ok": 0, "error": "Job not found"} + + devices_json = job_row[0] # JSONB: [{"mac": "...", "location": "..."}] + lat, lng = job_row[1], job_row[2] + + # 2. Determine Timezone (Default to LA if GPS lookup fails/not implemented locally) + # In a full implementation, use GetTZFromGPS(lat, lng) if available, or store TZ in jobs table. + time_zone_s = 'America/Los_Angeles' + if lat and lng: + tz_check = GetTZFromGPS(lat, lng) + if tz_check: time_zone_s = tz_check + + # 3. Parse Dates to Epochs + # Handle ISO format or YYYY-MM-DD + try: + if "T" in date_from_str: + dt_from = datetime.datetime.fromisoformat(date_from_str.replace('Z', '+00:00')) + else: + dt_from = datetime.datetime.strptime(date_from_str, "%Y-%m-%d").replace(tzinfo=pytz.timezone(time_zone_s)) + + if "T" in date_to_str: + dt_to = datetime.datetime.fromisoformat(date_to_str.replace('Z', '+00:00')) + else: + dt_to = datetime.datetime.strptime(date_to_str, "%Y-%m-%d").replace(tzinfo=pytz.timezone(time_zone_s)) + datetime.timedelta(days=1) + + epoch_from = dt_from.timestamp() + epoch_to = dt_to.timestamp() + except Exception as e: + return {"ok": 0, "error": f"Date parse error: {str(e)}"} + + # 4. Prepare Metadata + units = "°C" + if "America" in time_zone_s or "US/" in time_zone_s: + units = "°F" + + sensor_props = { + "temperature": ["red", units], "humidity": ["blue", "%"], "voc": ["orange", "PPM"], + "co2": ["orange", "PPM"], "pressure": ["magenta", "Bar"], "radar": ["cyan", "%"], "light": ["yellow", "Lux"] + } + + chart_data = [] + + # 5. Iterate Devices assigned to Job + if devices_json: + if isinstance(devices_json, str): + devices_list = json.loads(devices_json) + else: + devices_list = devices_json + + for dev_obj in devices_list: + mac = dev_obj.get('mac') + loc_name = dev_obj.get('location', 'Unknown') + + # Resolve MAC to Device ID + dev_details = GetDeviceDetailsSingleFromMac(mac) + if not dev_details: + continue + + device_id = dev_details.get('device_id') + + # Get Calibration data + temp_calib, humid_calib = GetCalibMaps([device_id]) + + # Read Data + line_part = ReadSensor3(device_id, sensor, epoch_from, epoch_to, data_type, radar_part, bucket_size) + + # Apply Calibration / Conversions + if sensor == "temperature": + # Extract offset from string "0.0,1.0,-10" -> -10 + t_cal_str = temp_calib.get(device_id, "0,1,-10") + offset = float(t_cal_str.split(',')[2]) if ',' in str(t_cal_str) else -10 + line_part = [(ts, val + offset) for ts, val in line_part] + elif sensor == "humidity": + # Using global humidity_offset from existing code + line_part = [(ts, val + 34) for ts, val in line_part] + + # Clean Data + window = sensor_legal_values.get(sensor, [0,0,5])[2] + line_part_t = [(x[0].timestamp(), x[1]) for x in line_part] + cleaned_values = clean_data_pd(line_part_t, window=window, percentile=99) + + # Convert timestamps to local string format + compressed_readings = convert_timestamps_lc(cleaned_values, time_zone_s) + + # F/C Conversion for display + if sensor == "temperature" and units == "°F": + compressed_readings = CelsiusToFahrenheitList(compressed_readings) + + chart_data.append({ + "name": loc_name, + "device_mac": mac, + "location": loc_name, + "data": compressed_readings + }) + + return { + "ok": 1, + "job_id": job_id, + "sensor": sensor, + "units": units if sensor in sensor_props else "", + "color": sensor_props.get(sensor, ["grey"])[0], + "bucket_size": bucket_size, + "date_from": date_from_str, + "date_to": date_to_str, + "time_zone": time_zone_s, + "chart_data": chart_data + } + + except Exception as e: + logger.error(f"Error in get_job_sensor_bucketed_data: {traceback.format_exc()}") + return {"ok": 0, "error": str(e)} + finally: + if conn: conn.close() + +#=== End WellDry API functions ===# + #==================================== ADD FUNCTIONS BEFORE ============================================ # Main API class @@ -17925,7 +19287,7 @@ class WellApi: blob_data = UpdateDeploymentsSelector(blob_data, users) resp.content_type = "text/html" resp.text = blob_data - #print(blob_data) + #logger.debug(blob_data) return elif get_function_name == "deployment_edit": @@ -18016,7 +19378,7 @@ class WellApi: unique_identifier = req.params.get("unique_identifier") filename = f"/{deployment_id}/{deployment_id}_{ddate}_{group_by}_{radar_part}_{map_type}_{bw}_dayly_image.png" - #print(check_file_exists(filename)) + #logger.debug(check_file_exists(filename)) if not force_recreate: file_exists, time_modified_utc = check_file_exists(filename) if file_exists: @@ -18053,7 +19415,7 @@ class WellApi: #lets send over MQTT vocs_scaled json_data = numpy_to_json(vocs_scaled, devices_list) MQSendL("/"+unique_identifier, json_data) - #print(time.time() - st) + #logger.debug(time.time() - st) #lets read and send image from blob image_bytes, content_type, metadata = GetBlob(filename) @@ -18397,9 +19759,9 @@ class WellApi: st = time.time() if CreatePresenceMap(filename, devices_list, ddate, 1, force_recreate, chart_type, bw, motion, scale_global, fast, filter_minutes, time_zone_s) == 0: #"[bit] 1=same sensors together, 2=same device together, 4=1 der, 8=2 der - print(ddate, "Not found") + logger.debug(f"{ddate} Not found") else: - print(ddate, time.time() - st) + logger.debug(f"{ddate}, {time.time() - st}") #lets read and send image from blob image_bytes, content_type, metadata = GetBlob(filename) @@ -18430,7 +19792,7 @@ class WellApi: radar_part = req.params.get("radar_part") zip_filename = f"/{deployment_id}/{deployment_id}_{date_from}_{date_to}_{consolidated_by}_data.zip" - #print(check_file_exists(filename)) + #logger.debug(check_file_exists(filename)) if not force_recreate: file_exists, time_modified_utc = check_file_exists(zip_filename, bucket_name="data-downloads") if file_exists: @@ -18483,9 +19845,9 @@ class WellApi: ) if success: - print("Files successfully zipped") + logger.debug("Files successfully zipped") else: - print("Error occurred while zipping files") + logger.debug("Error occurred while zipping files") #pack CSV files from BLOB into ZIP #lets read and send image from blob @@ -18692,6 +20054,83 @@ class WellApi: resp.status = falcon.HTTP_200 return + #=== WellDry API function routing ===# + elif function == "job_list": + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + result = get_job_list(user_id, privileges) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_list2": + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + search_term = form_data.get('search', '') + result = get_job_list2(user_id, privileges, search_term) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_devices": + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + filter_type = form_data.get('filter', 'all') + search_term = form_data.get('search', '') + result = get_job_devices(user_id, privileges, filter_type, search_term) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_details": + job_id = int(form_data.get('job_id')) + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + result = get_job_details(job_id, user_id, privileges) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_create": + user_id = GetUserId(user_name) + result = create_job(form_data, user_id) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_edit": + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + result = edit_job(form_data, user_id, privileges) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_available_devices2": + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + result = get_available_devices2(user_id, privileges) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_available_devices": + # No parameters needed from form_data, uses token info + user_id = GetUserId(user_name) + privileges = GetPriviledgesOnly(user_name) + result = get_available_devices(user_id, privileges) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + + elif function == "job_weather": + job_id = int(form_data.get('job_id')) + result = get_job_weather(job_id) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return + #=== End WellDry API functions ===# + # Handle token-protected functions elif function == "messages_age": @@ -18827,16 +20266,16 @@ class WellApi: location = devices_list[device_index][2] sql = get_device_radar_s28_only_query(time_from_str, time_to_str, device_id) - print(sql) + logger.debug(sql) #sql1 = get_deployment_radar_only_colapsed_query(str(device_id), time_from_str, time_to_str, [device_id]) - #print(sql1) + #logger.debug(sql1) st = time.time() cur.execute(sql) my_data = cur.fetchall() timestamps, stationary, motion = process_raw_data(my_data) - print(type(stationary)) + logger.debug(type(stationary)) # Find threshold above which 20% of points lie AveragePercentSpendsThere = AveragePercentPerLocation[Consolidataed_locations[location]] threshold_high, threshold_low = FindThreshold(stationary, AveragePercentSpendsThere) @@ -18845,10 +20284,10 @@ class WellApi: threshold2, x_percent, y_percent = ShowThresholdGraph(stationary, file_save, threshold_low, threshold_high, title, AveragePercentSpendsThere, location) - print(f"Maximum curvature point found at:") - print(f"Threshold value: {threshold2:.3f}") - print(f"X: {x_percent:.1f}% of range") - print(f"Y: {y_percent:.1f}% of points above") + logger.debug(f"Maximum curvature point found at:") + logger.debug(f"Threshold value: {threshold2:.3f}") + logger.debug(f"X: {x_percent:.1f}% of range") + logger.debug(f"Y: {y_percent:.1f}% of points above") ShowArray(stationary, threshold2, filename=f"stationary_{devices_list[device_index][0]}.png", title=f"stationary_{devices_list[device_index][0]}_{devices_list[device_index][2]}", style='line') @@ -18862,11 +20301,11 @@ class WellApi: #cur.execute(sql1) #my_data1 = cur.fetchall()#cur.fetchone() - #print(time.time() - st) + #logger.debug(time.time() - st) #if my_data == None or my_data1 == None: #logger.warning(f"No data found for device_id {device_id}") #else: - #print(type(my_data)) + #logger.debug(type(my_data)) ##minute, ##device_id, ##s_min as radar_s_min, @@ -18898,19 +20337,19 @@ class WellApi: sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list) - print(sql) + logger.debug(sql) my_data = [] with get_db_connection() as conn: with conn.cursor() as cur: cur.execute(sql) my_data = cur.fetchall()#cur.fetchone() - #print(result) + #logger.debug(result) if my_data == None: return False fields_n = len(fields) stripes = devices_c * fields_n #radar_min and radar_max - print(my_data) + logger.debug(my_data) base_minute = ConvertToBase(time_from_str, time_zone_s) #base_minute = my_data[0][0]# min(record[0] for record in my_data) #remember: base_minute is offset (smaller) by numbr of minutes in stdev_range @@ -18931,7 +20370,7 @@ class WellApi: y = device_idx * fields_n + field_idx wave_m[y, x] = value - print(time.time()-st) + logger.debug(time.time()-st) #we need to reliably determine presence and LIFE (motion) in every 5 minutes of data... #presence is determined by average value being significntly different from last known base @@ -18987,7 +20426,7 @@ class WellApi: title="Radar Signal Histogram Max(1000 bins)", style='line') - print(wave) + logger.debug(wave) device_to_index += 1 #lets see this map @@ -19004,7 +20443,7 @@ class WellApi: y = yy * stretch_by + stretch_index arr_stretched[y, :] = rgb_row - print(time.time()-st) + logger.debug(time.time()-st) filename = f"{deployment_id}/{deployment_id}_{ddate}_min_max_radar.png" SaveImageInBlob(filename, arr_stretched, []) @@ -19047,7 +20486,7 @@ class WellApi: (line_part[i][0], (line_part[i][0] - line_part[i-1][0]).total_seconds() * 1000) for i in range(1, len(line_part)) ] - print(time.time()-st) + logger.debug(time.time()-st) if True: # Create CSV content as a string @@ -19075,7 +20514,7 @@ class WellApi: with open(f'time_differences_{sensor}_{device_id}.csv', 'w', encoding='utf-8') as f: f.write(csv_content) - print(f"CSV file 'time_differences_{sensor}_{device_id}.csv' created successfully!") + logger.debug(f"CSV file 'time_differences_{sensor}_{device_id}.csv' created successfully!") line_part_t = [(x[0].timestamp(), x[1]) for x in cleaned_values] @@ -19128,7 +20567,7 @@ class WellApi: #if (line_part[i][0] - line_part[i-1][0]).total_seconds() > 0 #and abs((line_part[i][1] - line_part[i-1][1]) / (line_part[i][0] - line_part[i-1][0]).total_seconds()) <= 100 #] - #print(time.time()-st) + #logger.debug(time.time()-st) @@ -19278,31 +20717,31 @@ class WellApi: with open('time_differences.csv', 'w', encoding='utf-8') as f: f.write(csv_content) - print("CSV file 'time_differences.csv' created successfully!") + logger.debug("CSV file 'time_differences.csv' created successfully!") - #print("@1", time.time() - st) + #logger.debug("@1", time.time() - st) #first = 3300 #last = 3400 #line_part = line_part[first:last] line_part_t = [] #st = time.time() #line_part_t = [tuple(x[:2]) for x in line_part] - #print(time.time() - st) + #logger.debug(time.time() - st) #st = time.time() #line_part_t = list({(dt.timestamp(), value) for dt, value in line_part}) - #print(time.time() - st) + #logger.debug(time.time() - st) line_part_t = [(x[0].timestamp(), x[1]) for x in line_part] st = time.time() cleaned_values_t = clean_data_pd(line_part_t, window=window, percentile=99) cleaned_values = cleaned_values_t #add_boundary_points(cleaned_values_t, time_zone_s) - #print("@2", time.time() - st) + #logger.debug("@2", time.time() - st) #Lets add point in minute 0 and minute 1439 #st = time.time() #cleaned_values = clean_data_fast(line_part_t, window=5, threshold=2.0) - #print("@3", time.time() - st) + #logger.debug("@3", time.time() - st) sensor_data[sensor] = cleaned_values @@ -19382,10 +20821,10 @@ class WellApi: #sensor_mapping = {"co2": "s4", "voc": "s9"} #sensor = sensor_mapping.get(sensor, sensor) temp_calib, humid_calib = GetCalibMaps(device_ids) - #print(device_ids) - #print(temp_calib) - #print(humid_calib) - #print("++++++++++++++++++") + #logger.debug(device_ids) + #logger.debug(temp_calib) + #logger.debug(humid_calib) + #logger.debug("++++++++++++++++++") chart_data = [] # example data in each element of devices_list is (266, 559, 'Bathroom', None, '64B70888FAB0', '["s3_max",12]') for well_id, device_id, location_name, description, MAC, radar_threshold_group_st, close_to in devices_list: @@ -19426,6 +20865,12 @@ class WellApi: payload = result_dictionary resp.media = package_response(payload) resp.status = falcon.HTTP_200 + # WellDry API + elif function == "get_job_sensor_bucketed_data": + result = get_job_sensor_bucketed_data(form_data) + resp.media = package_response(result) + resp.status = falcon.HTTP_200 + return elif function == "get_sensor_data_by_deployment_id": # Inputs: @@ -19660,29 +21105,29 @@ class WellApi: st = time.time() line_part = ReadSensor(device_id, sensor, epoch_from_utc, epoch_to_utc, data_type, radar_part, temp_offset) window = sensor_legal_values[sensor][2] - #print("@1", time.time() - st) + #logger.debug("@1", time.time() - st) #first = 3300 #last = 3400 #line_part = line_part[first:last] line_part_t = [] #st = time.time() #line_part_t = [tuple(x[:2]) for x in line_part] - #print(time.time() - st) + #logger.debug(time.time() - st) #st = time.time() #line_part_t = list({(dt.timestamp(), value) for dt, value in line_part}) - #print(time.time() - st) + #logger.debug(time.time() - st) line_part_t = [(x[0].timestamp(), x[1]) for x in line_part] st = time.time() cleaned_values_t = clean_data_pd(line_part_t, window=window, percentile=99) #cleaned_values = cleaned_values_t #add_boundary_points(cleaned_values_t, time_zone_s) - #print("@2", time.time() - st) + #logger.debug("@2", time.time() - st) #Lets add point in minute 0 and minute 1439 #st = time.time() #cleaned_values = clean_data_fast(line_part_t, window=5, threshold=2.0) - #print("@3", time.time() - st) + #logger.debug("@3", time.time() - st) cleaned_values = ScaleToCommon(cleaned_values_t, sensor) sensor_data[sensor] = cleaned_values all_slices[device_id] = sensor_data @@ -19897,7 +21342,7 @@ class WellApi: if device_mac != None: device_det = GetDeviceDetailsSingleFromMac(device_mac) - print(device_det) + logger.debug(device_det) dataa = {} dataa['Function'] = "device_details" dataa['device_details'] = device_det @@ -19952,7 +21397,7 @@ class WellApi: beneficiary_user_name = form_data.get('beneficiary_user_name') password = form_data.get('beneficiary_password') address_map = ParseAddress(beneficiary_address) - #print(address_map) + #logger.debug(address_map) #{'component_count': 6, 'parsed_components': {'city': 'saratoga', 'country': 'united states', 'house_number': '18569', 'postcode': '95070', 'road': 'allendale avenue', 'state': 'ca'}, 'success': True} email = form_data.get('beneficiary_email') @@ -20010,7 +21455,7 @@ class WellApi: if in_db_user_name != None: #this email exists in DB, so cannot be used for NEW deployment! error_string = f"This email already has associated account!" - print(error_string) + logger.debug(error_string) payload = {'ok': 0, 'error': error_string} resp.media = package_response(payload) resp.status = falcon.HTTP_200 @@ -20048,7 +21493,7 @@ class WellApi: form_data['first_name'] = form_data['firstName'] form_data['last_name'] = form_data['lastName'] - print(address_map) + logger.debug(address_map) #{'component_count': 6, 'parsed_components': {'city': 'saratoga', 'country': 'united states', 'house_number': '18569', 'postcode': '95070', 'road': 'allendale avenue', 'state': 'ca'}, 'success': True} #{'component_count': 5, 'parsed_components': {'city': 'rijeka', 'country': 'croatia', 'house_number': '51a', 'postcode': '51000', 'road': 'labinska ul.'}, 'success': True} @@ -20074,7 +21519,7 @@ class WellApi: if success: #this should always be true! if result["deployed"]: error_string = f"These devices are already deployed: {result['deployed']}" - print(error_string) + logger.debug(error_string) payload = {'ok': 0, 'error': error_string} resp.media = package_response(payload) resp.status = falcon.HTTP_200 @@ -20082,14 +21527,14 @@ class WellApi: if result["not_found"]: error_string = f"These devices are not available: {result['not_found']}" - print(error_string) + logger.debug(error_string) payload = {'ok': 0, 'error': error_string} resp.media = package_response(payload) resp.status = falcon.HTTP_200 return if not result["deployed"] and not result["not_found"]: - print("All devices are available for deployment") + logger.debug("All devices are available for deployment") #ok, error_string = StoreDisclaimer2DB(form_data) #In DB, we need to update or insert into: deployments, deployment_details and deployment_history @@ -20152,7 +21597,7 @@ class WellApi: ok = StoreToDeploymentHistory(editing_deployment_id, form_data_temp['devices']) devices_changed = True - print(devices_in_history_last) + logger.debug(devices_in_history_last) ok = 1 @@ -20255,10 +21700,10 @@ class WellApi: elif function == "request_deployment_map_new": st = time.time() - print(f"$0 ----{time.time() - st}") + logger.debug(f"$0 ----{time.time() - st}") deployment_id = form_data.get('deployment_id') map_type = form_data.get('map_type') - print(f"$1 ----{time.time() - st}") + logger.debug(f"$1 ----{time.time() - st}") datee = form_data.get('date') @@ -20286,7 +21731,7 @@ class WellApi: MAC = details[4] locations_desc_map[well_id] = location - print(f"$3 ----{time.time() - st}") + logger.debug(f"$3 ----{time.time() - st}") dataa = {} dataa['Function'] = "deployments_maps_report" @@ -20311,7 +21756,7 @@ class WellApi: #dataa['MACs_map'] = MACs_map dataa['locations_desc_map'] = locations_desc_map #proximity_list = proximity.split(",") - print(f"$4 ----{time.time() - st}") + logger.debug(f"$4 ----{time.time() - st}") if id < 200: checkmarks_string = 'T>\n' @@ -20349,13 +21794,13 @@ class WellApi: if details[3] != None and details[3] != "": location = location + " " + details[3] - if details[6] != None and details[6] != "": - location = location + " " + details[6] + #if details[6] != None and details[6] != "": + # location = location + " " + details[6] checkmarks_string = checkmarks_string + str(device_id) + '>\n' checked_or_not = '' - print(f"$5 ----{time.time() - st}") + logger.debug(f"$5 ----{time.time() - st}") dataa['checkmarks'] = checkmarks_string resp.media = package_response(dataa) @@ -20365,9 +21810,9 @@ class WellApi: deployment = form_data.get('deployment_id') timee = form_data.get('time') #timee = StringToEpoch(datee) - #print(deployment, timee) + #logger.debug(deployment, timee) well_ids, device_ids = GetProximityList(deployment, timee) - #print(proximity) + #logger.debug(proximity) dataa = {} dataa['Function'] = "proximity_report" if len(well_ids) > 0: @@ -20605,7 +22050,7 @@ class WellApi: ok = DeviceReboot(device_id_or_mac, user_name) break - print(f"OK = {ok}") + logger.debug(f"OK = {ok}") if ok != "": payload = ok @@ -20950,7 +22395,7 @@ class WellApi: else: radar_threshold_group = ["s3_max",12] - print(well_id, radar_threshold_group) + #logger.debug(well_id, radar_threshold_group) device_id_2_location[device_id] = location_name device_id_2_threshold[device_id] = radar_threshold_group @@ -20988,9 +22433,9 @@ class WellApi: devices_list_str = ','.join(str(device[1]) for device in devices_list) #sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) #sql = get_deployment_radar_10sec_snapped_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) - #print(sql) + #logger.debug(sql) zsql = get_deployment_radar_10sec_snapped_query(devices_list_str, time_from_z_str, time_to_str, ids_list, radar_fields_of_interest) - print(zsql) + logger.debug(zsql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -21050,9 +22495,9 @@ class WellApi: presence_map['raw'][well_id] = [0] * 6 * 1440 * days_difference_long #just place holder - print(f"start_time: {start_time}") - print(f"epoch_time being sent: {epoch_time}") - print(f"epoch_time as date: {datetime.datetime.fromtimestamp(epoch_time, tz=pytz.UTC)}") + logger.debug(f"start_time: {start_time}") + logger.debug(f"epoch_time being sent: {epoch_time}") + logger.debug(f"epoch_time as date: {datetime.datetime.fromtimestamp(epoch_time, tz=pytz.UTC)}") #start_time_ = myz_data[0][0] @@ -21220,7 +22665,7 @@ class WellApi: else: radar_threshold_group = ["s3_max",12] - print(well_id, radar_threshold_group) + #logger.debug(well_id, radar_threshold_group) device_id_2_location[device_id] = location_name device_id_2_threshold[device_id] = radar_threshold_group @@ -21254,11 +22699,11 @@ class WellApi: devices_list_str = ','.join(str(device[1]) for device in devices_list) #sql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) sql = get_deployment_radar_10sec_snapped_query(devices_list_str, time_from_str, time_to_str, ids_list, radar_fields_of_interest) - print(sql) + logger.debug(sql) if data_type == "z-graph" or data_type == "all" or data_type == "multiple": #zsql = get_deployment_radar_only_colapsed_query(devices_list_str, time_from_z_str, time_to_str, ids_list, radar_fields_of_interest) zsql = get_deployment_radar_10sec_snapped_query(devices_list_str, time_from_z_str, time_to_str, ids_list, radar_fields_of_interest) - print(zsql) + logger.debug(zsql) with get_db_connection() as conn: with conn.cursor() as cur: @@ -21408,13 +22853,13 @@ class WellApi: #presence_list = filter_short_groups_c_wc(presence_map["presence"][id2well_id[device_id]], filter, device_id_str, ddate, to_date, time_zone_s) #presence_listt = filter_short_groupss(presence_map["presence"][id2well_id[device_id]], filter) #if presence_list != presence_listt: - # print("stop") + # logger.debug("stop") #if data_type != "presence": #longpresence_list = filter_short_groups_numpy(presence_map["longpresence"][id2well_id[device_id]], filter, device_id, ddate+"-"+to_date) longpresence_list = filter_short_groups_c_wc(presence_map["longpresence"][id2well_id[device_id]], filter, device_id_str, prev_date, to_date, time_zone_s) #longpresence_listt = filter_short_groupss(presence_map["longpresence"][id2well_id[device_id]], filter) #if longpresence_list != longpresence_listt: - # print("stop") + # logger.debug("stop") # store_to_file(presence_map["longpresence"][id2well_id[device_id]], "test_list") #presence_map["presence"][id2well_id[device_id]] = presence_list #if data_type != "presence": @@ -21439,7 +22884,7 @@ class WellApi: #if data_type == "z-graph" or data_type == "all" or data_type == "multiple": for device_id in ids_list: - #print(device_id_2_threshold[id2well_id[device_id]]) + #logger.debug(device_id_2_threshold[id2well_id[device_id]]) z_graph = CreateZGraphAI(presence_map["longpresence"][id2well_id[device_id]]) #temporary_map_day_plus[id2well_id[device_id]]) presence_map["z_graph"][id2well_id[device_id]] = z_graph @@ -21574,14 +23019,14 @@ class WellApi: cnt = 0 for deployment in all_deployments: - #print(deployment) + #logger.debug(deployment) #if deployment['deployment_id'] == 40: - # print("stop") + # logger.debug("stop") cnt += 1 if cnt >= first: #print (deployment['beneficiary_id']) if deployment['beneficiary_id'] in user_id_2_user.keys(): - #print(user_id_2_user[deployment['beneficiary_id']]) + #logger.debug(user_id_2_user[deployment['beneficiary_id']]) caretaker_min_object = {"deployment_id": deployment['deployment_id'], "email": user_id_2_user[deployment['beneficiary_id']][3], "first_name": user_id_2_user[deployment['beneficiary_id']][5], "last_name": user_id_2_user[deployment['beneficiary_id']][6]} result_list.append(caretaker_min_object) #else: @@ -21621,7 +23066,7 @@ class WellApi: elif function == "get_devices_locations": well_ids = form_data.get('well_ids') details_list = WellId2Details(well_ids) - #print(details_list) + #logger.debug(details_list) to_report = [] @@ -21637,7 +23082,7 @@ class WellApi: else: to_report.append((details[0], location_names[details[3]], details[4], report_record)) - #print(to_report) + #logger.debug(to_report) payload = {'deployments': to_report} resp.media = package_response(payload) resp.status = falcon.HTTP_200 @@ -21669,7 +23114,7 @@ class WellApi: with get_db_connection() as conn: with conn.cursor() as cur: sql = f"SELECT deployment_id, beneficiary_id, devices FROM public.deployment_details WHERE devices::text ~* ANY(ARRAY['{macs_formatted}']);" - print(sql) + logger.debug(sql) cur.execute(sql) result = cur.fetchall() @@ -21724,7 +23169,7 @@ class WellApi: device_list_to_report.append((device[0], device[2], device[3])) sql = f"SELECT first_name, last_name, email, picture FROM public.person_details WHERE user_id IN ({users_formatted});" - print(sql) + logger.debug(sql) cur.execute(sql) result1 = cur.fetchall() counter = 0 @@ -21738,7 +23183,7 @@ class WellApi: to_report.append((entry, device_list_to_report, deployment_t)) - print(to_report) + logger.debug(to_report) payload = {'deployments': to_report} resp.media = package_response(payload) resp.status = falcon.HTTP_200 @@ -21831,7 +23276,7 @@ class WellApi: resp.media = package_response("Missing or illegal 'email' parameter", HTTP_400) return - print(privileges) + logger.debug(privileges) if privileges == "-1": ok = StoreCaretaker2DB(form_data, editing_user_id, user_id) if ok == 1: @@ -21928,7 +23373,7 @@ class WellApi: if success: if result["deployed"]: error_string = f"These devices are already deployed: {result['deployed']}" - print(error_string) + logger.debug(error_string) payload = {'ok': 0, 'error': error_string} resp.media = package_response(payload) resp.status = falcon.HTTP_200 @@ -21936,13 +23381,13 @@ class WellApi: if result["not_found"]: error_string = f"These devices are not available: {result['not_found']}" - print(error_string) + logger.debug(error_string) payload = {'ok': 0, 'error': error_string} resp.media = package_response(payload) resp.status = falcon.HTTP_200 return - print("All devices are available for deployment") + logger.debug("All devices are available for deployment") ok, error_string = StoreDisclaimer2DB(form_data) if ok == False: @@ -22153,7 +23598,7 @@ class WellApi: hours, events_count = GetActivities(device_id, well_id, datee, filterr, refresh, time_zone_s, radar_threshold_group_st) if hours > 18: - print("Too long 6m!!!", device_id, well_id, datee, filterr, refresh, time_zone_s, radar_threshold_group_st) + logger.debug(f"Too long 6m!!! {device_id}, {well_id}, {datee}, {filterr}, {refresh}, {time_zone_s}, {radar_threshold_group_st}") data_record = { "title": str(day_activity[2]), "events": events_count, "hours": hours} data.append(data_record) @@ -22192,9 +23637,9 @@ class WellApi: datee = day_activity[0] hours, events_count = GetActivities(device_id, well_id, datee, filterr, refresh, time_zone_s, radar_threshold_group_st) #if datee == "2025-05-20" and device_id == 572: - # print(hours) + # logger.debug(hours) if hours > 18: - print("Too long m!!!", device_id, well_id, datee, filterr, refresh, time_zone_s, radar_threshold_group_st) + logger.debug(f"oo long m!!! {device_id}, {well_id}, {datee}, {filterr}, {refresh}, {time_zone_s}, {radar_threshold_group_st}") data_record = { "title": str(day_activity[2]), "events": events_count, "hours": hours} data.append(data_record) @@ -22742,7 +24187,7 @@ class WellApi: return except Exception as e: - print(traceback.format_exc()) + logger.debug(traceback.format_exc()) resp.media = package_response(f"Error: {str(e)} {traceback.format_exc()}", HTTP_500) @@ -22978,11 +24423,11 @@ def FixDeploymentHistorySequence(connection) -> bool: connection.commit() cursor.close() - print("Deployment history sequence has been fixed") + logger.debug("Deployment history sequence has been fixed") return True except psycopg2.Error as e: - print(f"Error fixing sequence: {e}") + logger.debug(f"Error fixing sequence: {e}") connection.rollback() return False @@ -23020,12 +24465,12 @@ def StoreToDeploymentHistory(deployment_id: int, proximity: str) -> bool: cursor.close() - print(f"Successfully inserted record: deployment_id={deployment_id}, time={current_epoch_time}, proximity='{proximity}'") + logger.debug(f"Successfully inserted record: deployment_id={deployment_id}, time={current_epoch_time}, proximity='{proximity}'") return True except psycopg2.IntegrityError as e: if "duplicate key value violates unique constraint" in str(e): - print("Sequence appears to be out of sync. Attempting to fix...") + logger.debug("Sequence appears to be out of sync. Attempting to fix...") connection.rollback() # Try to fix the sequence @@ -23037,25 +24482,25 @@ def StoreToDeploymentHistory(deployment_id: int, proximity: str) -> bool: cursor.execute(query, (deployment_id, current_epoch_time, proximity)) connection.commit() cursor.close() - print(f"Successfully inserted record after sequence fix: deployment_id={deployment_id}") + logger.debug(f"Successfully inserted record after sequence fix: deployment_id={deployment_id}") return True except Exception as retry_error: - print(f"Failed to insert even after sequence fix: {retry_error}") + logger.debug(f"Failed to insert even after sequence fix: {retry_error}") connection.rollback() return False else: - print("Failed to fix sequence") + logger.debug("Failed to fix sequence") return False else: - print(f"Database integrity error in StoreToDeploymentHistory: {e}") + logger.debug(f"Database integrity error in StoreToDeploymentHistory: {e}") connection.rollback() return False except psycopg2.Error as e: - print(f"Database error in StoreToDeploymentHistory: {e}") + logger.debug(f"Database error in StoreToDeploymentHistory: {e}") connection.rollback() return False except Exception as e: - print(f"Unexpected error in StoreToDeploymentHistory: {e}") + logger.debug(f"Unexpected error in StoreToDeploymentHistory: {e}") connection.rollback() return False @@ -23085,7 +24530,7 @@ def ListsSame(s1: str, s2: str) -> bool: return set1 == set2 except (json.JSONDecodeError, TypeError) as e: - print(f"Error parsing JSON strings: {e}") + logger.debug(f"Error parsing JSON strings: {e}") return False def WellIDs2MAC(devices_details_str: str) -> str: @@ -23132,7 +24577,7 @@ def WellIDs2MAC(devices_details_str: str) -> str: return json.dumps(device_macs) except (json.JSONDecodeError, ValueError, psycopg2.Error) as e: - print(f"Error in WellIDs2MAC: {e}") + logger.debug(f"Error in WellIDs2MAC: {e}") return "[]" @@ -23276,17 +24721,17 @@ def handle_multipart_request(request): elif 'CONTENT-TYPE' in request.headers: content_type = request.headers['CONTENT-TYPE'] - print(f"Content-Type: {content_type}") + logger.debug(f"Content-Type: {content_type}") if 'boundary=' not in content_type: - print("No boundary found, trying alternative header access...") + logger.debug("No boundary found, trying alternative header access...") # Debug: print all headers to see the structure - print(f"Headers type: {type(request.headers)}") - print(f"Headers: {request.headers}") + logger.debug(f"Headers type: {type(request.headers)}") + logger.debug(f"Headers: {request.headers}") return {"error": "No boundary found in multipart request"} boundary = content_type.split('boundary=')[1] - print(f"Found boundary: {boundary}") + logger.debug(f"Found boundary: {boundary}") # Convert to bytes boundary = boundary.encode() @@ -23302,7 +24747,7 @@ def handle_multipart_request(request): else: content_length = 0 - print(f"Content length: {content_length}") + logger.debug(f"Content length: {content_length}") if content_length > 50 * 1024 * 1024: # 50MB limit return {"error": "Request too large"} @@ -23315,49 +24760,49 @@ def handle_multipart_request(request): bytes_read = 0 chunk_size = 8192 - print("Starting to read body data...") + logger.debug("Starting to read body data...") while bytes_read < content_length: remaining = content_length - bytes_read to_read = min(chunk_size, remaining) chunk = request.bounded_stream.read(to_read) if not chunk: - print(f"No more data at {bytes_read} bytes") + logger.debug(f"No more data at {bytes_read} bytes") break body_data += chunk bytes_read += len(chunk) if bytes_read % (chunk_size * 10) == 0: # Log every 80KB - print(f"Read {bytes_read}/{content_length} bytes") + logger.debug(f"Read {bytes_read}/{content_length} bytes") - print(f"Finished reading {len(body_data)} bytes of multipart data") + logger.debug(f"Finished reading {len(body_data)} bytes of multipart data") # Parse multipart data form_data, files = parse_multipart_data_manual(body_data, boundary) - print(f"Parsed form fields: {list(form_data.keys())}") - print(f"Parsed files: {list(files.keys())}") + logger.debug(f"Parsed form fields: {list(form_data.keys())}") + logger.debug(f"Parsed files: {list(files.keys())}") # Handle photo file if 'beneficiary_photo' in files: photo = files['beneficiary_photo'] photo_data = photo['data'] - print(f"Received photo: {photo['filename']}, {len(photo_data)} bytes") + logger.debug(f"Received photo: {photo['filename']}, {len(photo_data)} bytes") # Validate and save JPEG if photo_data[:3] == b'\xff\xd8\xff' and photo_data.endswith(b'\xff\xd9'): with open(photo['filename'], 'wb') as f: f.write(photo_data) - print("✅ Multipart photo saved successfully") + logger.debug("✅ Multipart photo saved successfully") else: - print(f"❌ Invalid JPEG data - starts with: {photo_data[:10].hex()}") + logger.debug(f"❌ Invalid JPEG data - starts with: {photo_data[:10].hex()}") return {"status": "success", "form_data": form_data, "files": files, "filename": photo['filename']} except Exception as e: - print(f"Error processing multipart request: {e}") + logger.debug(f"Error processing multipart request: {e}") traceback.print_exc() return {"error": str(e)} @@ -23441,7 +24886,7 @@ def quick_fix_base64_photo(form_data, file_name): with open(file_name, 'wb') as f: f.write(image_data) - print(f"Base64 photo {file_name} fixed and saved: {len(image_data)} bytes") + logger.debug(f"Base64 photo {file_name} fixed and saved: {len(image_data)} bytes") return True else: logger.error("Invalid JPEG header in Base64 data") @@ -23459,26 +24904,26 @@ def quick_fix_base64_photo(form_data, file_name): #beneficiary_photo_b64 = form_data.get('beneficiary_photo') #if not beneficiary_photo_b64: - #print("❌ No photo data received") + #logger.debug("❌ No photo data received") #return None - #print("=== PYTHON RECEIVE DEBUGGING ===") - #print(f"Received base64 length: {len(beneficiary_photo_b64)}") - #print(f"First 50 chars: {beneficiary_photo_b64[:50]}") - #print(f"Last 50 chars: {beneficiary_photo_b64[-50:]}") + #logger.debug("=== PYTHON RECEIVE DEBUGGING ===") + #logger.debug(f"Received base64 length: {len(beneficiary_photo_b64)}") + #logger.debug(f"First 50 chars: {beneficiary_photo_b64[:50]}") + #logger.debug(f"Last 50 chars: {beneficiary_photo_b64[-50:]}") ## Check for whitespace #whitespace_count = len(beneficiary_photo_b64) - len(beneficiary_photo_b64.replace(' ', '').replace('\n', '').replace('\r', '').replace('\t', '')) - #print(f"Whitespace characters: {whitespace_count}") + #logger.debug(f"Whitespace characters: {whitespace_count}") ## Clean and check length #clean_b64 = beneficiary_photo_b64.replace(' ', '').replace('\n', '').replace('\r', '').replace('\t', '') - #print(f"Clean base64 length: {len(clean_b64)}") - #print(f"Clean mod 4: {len(clean_b64) % 4}") + #logger.debug(f"Clean base64 length: {len(clean_b64)}") + #logger.debug(f"Clean mod 4: {len(clean_b64) % 4}") ## Calculate expected original size #expected_bytes = (len(clean_b64) * 3) // 4 - #print(f"Expected decoded size: {expected_bytes} bytes") + #logger.debug(f"Expected decoded size: {expected_bytes} bytes") ## Try to decode with minimal processing #try: @@ -23489,89 +24934,89 @@ def quick_fix_base64_photo(form_data, file_name): #else: #padded_b64 = clean_b64 - #print(f"After padding: {len(padded_b64)} chars") + #logger.debug(f"After padding: {len(padded_b64)} chars") #decoded_bytes = base64.b64decode(padded_b64) - #print(f"✅ Decoded successfully: {len(decoded_bytes)} bytes") + #logger.debug(f"✅ Decoded successfully: {len(decoded_bytes)} bytes") ## Check first and last bytes #if len(decoded_bytes) > 10: #first_bytes = ' '.join(f'{b:02x}' for b in decoded_bytes[:10]) #last_bytes = ' '.join(f'{b:02x}' for b in decoded_bytes[-10:]) - #print(f"First 10 bytes: {first_bytes}") - #print(f"Last 10 bytes: {last_bytes}") + #logger.debug(f"First 10 bytes: {first_bytes}") + #logger.debug(f"Last 10 bytes: {last_bytes}") ## Calculate hash for comparison #data_hash = hashlib.md5(decoded_bytes).hexdigest() - #print(f"MD5 hash: {data_hash}") + #logger.debug(f"MD5 hash: {data_hash}") ## Save raw decoded data #with open('raw_decoded.jpg', 'wb') as f: #f.write(decoded_bytes) - #print("Saved raw decoded data to raw_decoded.jpg") + #logger.debug("Saved raw decoded data to raw_decoded.jpg") ## Try to analyze the structure #if decoded_bytes[:3] == b'\xff\xd8\xff': - #print("✅ Valid JPEG header") + #logger.debug("✅ Valid JPEG header") ## Look for JPEG end marker #if decoded_bytes.endswith(b'\xff\xd9'): - #print("✅ Valid JPEG end marker") + #logger.debug("✅ Valid JPEG end marker") #else: ## Find where JPEG data actually ends #last_ffd9 = decoded_bytes.rfind(b'\xff\xd9') #if last_ffd9 != -1: - #print(f"⚠️ JPEG end marker found at position {last_ffd9}, but file continues for {len(decoded_bytes) - last_ffd9 - 2} more bytes") + #logger.debug(f"⚠️ JPEG end marker found at position {last_ffd9}, but file continues for {len(decoded_bytes) - last_ffd9 - 2} more bytes") ## Try extracting just the JPEG part #jpeg_only = decoded_bytes[:last_ffd9 + 2] #with open('jpeg_extracted.jpg', 'wb') as f: #f.write(jpeg_only) - #print(f"Extracted JPEG part ({len(jpeg_only)} bytes) to jpeg_extracted.jpg") + #logger.debug(f"Extracted JPEG part ({len(jpeg_only)} bytes) to jpeg_extracted.jpg") #else: - #print("❌ No JPEG end marker found anywhere") + #logger.debug("❌ No JPEG end marker found anywhere") #else: - #print("❌ Invalid JPEG header") + #logger.debug("❌ Invalid JPEG header") #return decoded_bytes #except Exception as e: - #print(f"❌ Decode failed: {e}") + #logger.debug(f"❌ Decode failed: {e}") #return None def fix_incomplete_jpeg(image_bytes): """Add missing JPEG end marker if needed""" - print(f"Original image size: {len(image_bytes)} bytes") - print(f"Ends with: {image_bytes[-10:].hex()}") + logger.debug(f"Original image size: {len(image_bytes)} bytes") + logger.debug(f"Ends with: {image_bytes[-10:].hex()}") # Check if JPEG end marker is present if not image_bytes.endswith(b'\xff\xd9'): - print("❌ Missing JPEG end marker, adding it...") + logger.debug("❌ Missing JPEG end marker, adding it...") fixed_bytes = image_bytes + b'\xff\xd9' - print(f"Fixed image size: {len(fixed_bytes)} bytes") + logger.debug(f"Fixed image size: {len(fixed_bytes)} bytes") # Test if this fixes the image try: with Image.open(io.BytesIO(fixed_bytes)) as img: img.load() # Force load to verify - print(f"✅ Successfully repaired JPEG: {img.format} {img.size}") + logger.debug(f"✅ Successfully repaired JPEG: {img.format} {img.size}") return fixed_bytes except Exception as e: - print(f"Adding end marker didn't work: {e}") + logger.debug(f"Adding end marker didn't work: {e}") return image_bytes def robust_base64_decode_v3(base64_string): """Fixed version that handles the padding correctly""" - print(f"Original string length: {len(base64_string)}") + logger.debug(f"Original string length: {len(base64_string)}") # Clean the string clean_string = re.sub(r'\s+', '', base64_string) - print(f"After cleaning: {len(clean_string)}") + logger.debug(f"After cleaning: {len(clean_string)}") # The issue is with padding calculation # Let's try different approaches @@ -23592,15 +25037,15 @@ def robust_base64_decode_v3(base64_string): for strategy_name, test_string in strategies: try: - print(f"Trying {strategy_name}") + logger.debug(f"Trying {strategy_name}") # Decode image_bytes = base64.b64decode(test_string) - print(f" Decoded to {len(image_bytes)} bytes") + logger.debug(f" Decoded to {len(image_bytes)} bytes") # Check JPEG header if image_bytes[:3] == b'\xff\xd8\xff': - print(f" ✅ Valid JPEG header") + logger.debug(f" ✅ Valid JPEG header") # Try to fix missing end marker fixed_bytes = fix_incomplete_jpeg(image_bytes) @@ -23609,16 +25054,16 @@ def robust_base64_decode_v3(base64_string): try: with Image.open(io.BytesIO(fixed_bytes)) as img: img.load() - print(f" ✅ Valid complete JPEG: {img.format} {img.size}") + logger.debug(f" ✅ Valid complete JPEG: {img.format} {img.size}") return fixed_bytes except Exception as e: - print(f" ❌ Still invalid: {e}") + logger.debug(f" ❌ Still invalid: {e}") continue else: - print(f" ❌ Invalid JPEG header: {image_bytes[:10].hex()}") + logger.debug(f" ❌ Invalid JPEG header: {image_bytes[:10].hex()}") except Exception as e: - print(f" {strategy_name} failed: {e}") + logger.debug(f" {strategy_name} failed: {e}") return None @@ -24131,7 +25576,7 @@ def ExtractAddress(deployment_map): "state": "ca" } ''' - print(deployment_map) #{'deployment_id': 24, 'time_edit': 1753129300.0, 'user_edit': 32, 'persons': 2, 'gender': 1, 'race': 1, 'born': 1972, 'pets': 0, + logger.debug(deployment_map) #{'deployment_id': 24, 'time_edit': 1753129300.0, 'user_edit': 32, 'persons': 2, 'gender': 1, 'race': 1, 'born': 1972, 'pets': 0, # 'address_street': '', 'address_city': '', 'address_zip': '95070', 'address_state': '', 'address_country': '', 'wifis': '{"CBX_F": "69696969", "CBX": "69696969"}', 'lat': 37.267117, 'lng': -121.99548, 'gps_age': 0, 'note': 'me', 'overlapps': None}' address_map["city"] = deployment_map["address_city"] address_map["country"] = deployment_map["address_country"] @@ -24162,7 +25607,7 @@ def PurgeDeployment(deployment): deployment.pop('caretaker_id', None) deployment.pop('owner_id', None) deployment.pop('installer_id', None) - #print(deployment) + #logger.debug(deployment) deployment.pop('address_street', None) deployment.pop('address_city', None) deployment.pop('address_zip', None) @@ -24187,7 +25632,7 @@ def save_list_to_csv_method1(data_list, filename): # Write each item in a separate row for item in data_list: writer.writerow([item]) - print(f"Data saved to {filename} using csv module") + logger.debug(f"Data saved to {filename} using csv module") logger.error(f"------------------------------- STARTED ------------------------------------------") @@ -24270,11 +25715,11 @@ if __name__ == "__main__": redis_conn = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, db=REDIS_DB) # Use port 8000 for local debugging port = int(os.environ.get('PORT', 8000)) - #port = int(os.environ.get('PORT', 1998)) + #port = int(os.environ.get('PORT', 8002)) # Create a WSGI server with make_server('', port, app) as httpd: - print(f'Serving on port {port}...') + logger.debug(f'Serving on port {port}...') # Serve until process is killed httpd.serve_forever() diff --git a/wellDbQuery.py b/wellDbQuery.py new file mode 100644 index 0000000..fa5ee05 --- /dev/null +++ b/wellDbQuery.py @@ -0,0 +1,545 @@ +#!/usr/bin/env python3 +import os +import sys +import csv +import json +import zipfile +import argparse +import datetime +import logging +import ast +import io +from collections import defaultdict +from dotenv import load_dotenv + +# Try to import psycopg2 +try: + import psycopg2 + from psycopg2.extras import RealDictCursor +except ImportError: + print("Error: psycopg2 module not found. Please install it: pip install psycopg2-binary") + sys.exit(1) + +# ========================================== +# Configuration & Defaults +# ========================================== + +load_dotenv() + +DEFAULTS = { + 'DB_NAME': os.getenv('DB_NAME', 'wellnuo'), + 'DB_USER': os.getenv('DB_USER', 'well_app'), + 'DB_PASS': os.getenv('DB_PASSWORD', 'well_app_2024'), + 'DB_HOST': os.getenv('DB_HOST', '192.168.68.70'), + 'DB_PORT': os.getenv('DB_PORT', '5432'), + 'OUT_FILE': "out.zip", + 'RADAR_PART': "s28", + 'GROUP_BY': "by_minute" +} + +# Custom Logging Levels +LOG_INFO = 20 +LOG_STEPS = 15 # Level 1 (-d 1) +LOG_DATA = 12 # Level 2 (-d 2) +LOG_SQL = 5 # Level 3 (-d 3) + +logging.addLevelName(LOG_STEPS, "STEPS") +logging.addLevelName(LOG_DATA, "DATA") +logging.addLevelName(LOG_SQL, "SQL") + +logger = logging.getLogger("wellDbQuery") +handler = logging.StreamHandler(sys.stdout) +formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') +handler.setFormatter(formatter) +logger.addHandler(handler) + +# ========================================== +# Database Abstraction +# ========================================== + +class Database: + def __init__(self, args): + self.conn_params = { + "host": args.db_host, + "port": args.db_port, + "dbname": args.db_name, + "user": args.db_username, + "password": args.db_password + } + self.conn = None + + def connect(self): + if self.conn is None or self.conn.closed: + try: + logger.log(LOG_STEPS, f"Connecting to database {self.conn_params['host']}...") + self.conn = psycopg2.connect(**self.conn_params) + except Exception as e: + logger.error(f"Database connection failed: {e}") + sys.exit(1) + + def close(self): + if self.conn: + self.conn.close() + logger.log(LOG_STEPS, "Database connection closed.") + + def execute(self, query, params=None): + self.connect() + try: + with self.conn.cursor(cursor_factory=RealDictCursor) as cur: + logger.log(LOG_SQL, f"EXECUTING SQL: {query}") + if params: + logger.log(LOG_SQL, f"PARAMS: {params}") + + cur.execute(query, params) + + # If query returns rows + if cur.description: + return cur.fetchall() + return None + except Exception as e: + logger.error(f"Query execution failed: {e}") + if logger.level <= LOG_SQL: + import traceback + traceback.print_exc() + sys.exit(1) + +# ========================================== +# Data Processing Logic +# ========================================== + +class DataProcessor: + """ + Handles the complexity of unwrapping 'mtype' sensor readings + and merging them with radar data. + """ + + @staticmethod + def get_bucket_interval(group_by): + if group_by == 'by_10_seconds': + return '10 seconds' + elif group_by == 'by_hour': + return '1 hour' + elif group_by == 'by_10_minute': + return '10 minutes' + return '1 minute' + + @staticmethod + def build_fetch_query(device_ids, start_time, end_time, radar_part, all_radar, group_by): + """ + Builds a query that fetches raw rows. We perform the pivoting in Python + to dynamically handle the sparse s10-s79 columns. + """ + device_list_str = ",".join(map(str, device_ids)) + bucket_interval = DataProcessor.get_bucket_interval(group_by) + + # Radar Selection Logic + if all_radar: + radar_select = """ + MAX(absent) AS radar_absent, MAX(moving) AS radar_moving, MAX(stationary) AS radar_stationary, MAX("both") AS radar_both, + MAX(m0) AS m0, MAX(m1) AS m1, MAX(m2) AS m2, MAX(m3) AS m3, MAX(m4) AS m4, MAX(m5) AS m5, MAX(m6) AS m6, MAX(m7) AS m7, MAX(m8) AS m8, + MAX(s2) AS radar_s2, MAX(s3) AS radar_s3, MAX(s4) AS radar_s4, MAX(s5) AS radar_s5, MAX(s6) AS radar_s6, MAX(s7) AS radar_s7, MAX(s8) AS radar_s8 + """ + radar_outer = """ + rr.radar_absent, rr.radar_moving, rr.radar_stationary, rr.radar_both, + rr.m0, rr.m1, rr.m2, rr.m3, rr.m4, rr.m5, rr.m6, rr.m7, rr.m8, + rr.radar_s2, rr.radar_s3, rr.radar_s4, rr.radar_s5, rr.radar_s6, rr.radar_s7, rr.radar_s8 + """ + else: + radar_expr = radar_part + if radar_expr == "s28": + radar_expr = "(s2+s3+s4+s5+s6+s7+s8)/7" + radar_select = f"MAX({radar_expr}) AS radar" + radar_outer = "rr.radar" + + # We fetch s0-s9 and mtype. Python will map these to s0-s79. + # We use MAX/MIN aggregation to flatten duplicates within the time bucket if any exist + # though typically mtypes are distinct rows. + sql = f""" + SELECT + COALESCE(sr.minute, rr.minute) as time, + COALESCE(sr.device_id, rr.device_id) as device_id, + sr.mtype, + sr.temperature, sr.humidity, sr.pressure, sr.light, + sr.s0, sr.s1, sr.s2, sr.s3, sr.s4, sr.s5, sr.s6, sr.s7, sr.s8, sr.s9, + {radar_outer} + FROM ( + SELECT + time_bucket('{bucket_interval}', time) AS minute, + device_id, + mtype, + AVG(temperature) AS temperature, + AVG(humidity) AS humidity, + AVG(pressure) AS pressure, + MAX(light) AS light, + MIN(s0) as s0, MIN(s1) as s1, MIN(s2) as s2, MIN(s3) as s3, MIN(s4) as s4, + MIN(s5) as s5, MIN(s6) as s6, MIN(s7) as s7, MIN(s8) as s8, MIN(s9) as s9 + FROM sensor_readings + WHERE device_id IN ({device_list_str}) + AND time >= '{start_time}' + AND time < '{end_time}' + GROUP BY minute, device_id, mtype + ) sr + FULL OUTER JOIN ( + SELECT + time_bucket('{bucket_interval}', time) AS minute, + device_id, + {radar_select} + FROM radar_readings + WHERE device_id IN ({device_list_str}) + AND time >= '{start_time}' + AND time < '{end_time}' + GROUP BY minute, device_id + ) rr + ON sr.minute = rr.minute AND sr.device_id = rr.device_id + ORDER BY time ASC, device_id ASC; + """ + return sql + + @staticmethod + def process_rows(rows): + """ + Pivots the rows. + Input: Multiple rows per timestamp (one for each mtype). + Output: Single row per timestamp with columns s0...s79 populated dynamically. + """ + if not rows: + return [], [] + + # Dictionary to hold merged records: key = (time, device_id) + merged_data = defaultdict(dict) + + # Track which sensor columns actually have data to optimize CSV headers + active_s_columns = set() + + # Base headers that are always present + base_headers = ['time', 'device_id', 'temperature', 'humidity', 'pressure', 'light'] + + # Radar headers depend on query, extract them from the first row excluding known sensor/base cols + first_row_keys = list(rows[0].keys()) + radar_headers = [k for k in first_row_keys if k not in base_headers and k not in ['mtype'] and not (k.startswith('s') and k[1:].isdigit())] + + for row in rows: + key = (row['time'], row['device_id']) + + # Initialize base data if not present + if key not in merged_data: + for h in base_headers: + merged_data[key][h] = row.get(h) + for h in radar_headers: + merged_data[key][h] = row.get(h) + + # Merge Base Sensor Data (Temp/Hum/etc) if current row has it and stored is None + # (This handles FULL OUTER JOIN nulls) + if row.get('temperature') is not None: + for h in base_headers[2:]: # Skip time/id + merged_data[key][h] = row.get(h) + + # Merge Radar Data + if any(row.get(h) is not None for h in radar_headers): + for h in radar_headers: + if row.get(h) is not None: + merged_data[key][h] = row.get(h) + + # Process Extended Sensors (s0-s79) based on mtype + mtype = row.get('mtype') + + # Logic from well-api.py: + # mtype 0, 17, 100 -> s0-s9 + # mtype 110 -> s10-s19 ... mtype 170 -> s70-s79 + + if mtype is not None: + base_offset = 0 + if mtype in [0, 17, 100]: + base_offset = 0 + elif 110 <= mtype <= 170: + base_offset = mtype - 100 + else: + # Unknown mtype, skip sensor mapping or log debug + continue + + for i in range(10): + val = row.get(f's{i}') + if val is not None: + target_idx = base_offset + i + col_name = f's{target_idx}' + merged_data[key][col_name] = val + active_s_columns.add(target_idx) + + # Sort active sensor columns numerically + sorted_s_cols = [f's{i}' for i in sorted(list(active_s_columns))] + + # Final Header List + final_headers = base_headers + radar_headers + sorted_s_cols + + # Flatten dictionary to list of dicts + final_rows = [] + for key in sorted(merged_data.keys()): # Sort by time, device_id + row_data = merged_data[key] + # Ensure all columns exist in dict for CSV writer + for col in final_headers: + if col not in row_data: + row_data[col] = None + final_rows.append(row_data) + + return final_rows, final_headers + +# ========================================== +# Main Application Logic +# ========================================== + +class WellExporter: + def __init__(self, args): + self.args = args + self.db = Database(args) + self.target_device_ids = [] + self.file_identifier = "unknown" # Used for filename prefix + + def resolve_devices(self): + """Resolves command line arguments into a list of internal device_ids.""" + ids = set() + + # 1. Explicit Device ID + if self.args.device_id: + ids.add(int(self.args.device_id)) + self.file_identifier = str(self.args.device_id) + logger.log(LOG_STEPS, f"Resolved Device ID: {self.args.device_id}") + + # 2. Well ID + if self.args.well_id: + rows = self.db.execute("SELECT device_id FROM public.devices WHERE well_id = %s", (self.args.well_id,)) + if rows: + ids.add(rows[0]['device_id']) + self.file_identifier = str(self.args.well_id) + logger.log(LOG_STEPS, f"Resolved Well ID {self.args.well_id} -> Device ID {rows[0]['device_id']}") + else: + logger.warning(f"Well ID {self.args.well_id} not found.") + + # 3. MAC Address + if self.args.mac: + rows = self.db.execute("SELECT device_id FROM public.devices WHERE device_mac = %s", (self.args.mac,)) + if rows: + ids.add(rows[0]['device_id']) + self.file_identifier = self.args.mac + logger.log(LOG_STEPS, f"Resolved MAC {self.args.mac} -> Device ID {rows[0]['device_id']}") + else: + logger.warning(f"MAC {self.args.mac} not found.") + + # 4. Deployment ID + if self.args.deployment_id: + self.file_identifier = str(self.args.deployment_id) + logger.log(LOG_STEPS, f"Resolving devices for Deployment ID: {self.args.deployment_id}") + rows = self.db.execute("SELECT devices FROM public.deployment_details WHERE deployment_id = %s", (self.args.deployment_id,)) + + if rows and rows[0]['devices']: + raw_devices = rows[0]['devices'] + macs_to_find = [] + + try: + # Handle various formats stored in DB (JSON list, string representation, nested lists) + # Format seen in well-api: '["MAC1", "MAC2"]' or '[[id,id,loc,desc,MAC],...]' + try: + device_data = json.loads(raw_devices) + except json.JSONDecodeError: + try: + device_data = ast.literal_eval(raw_devices) + except: + # Fallback for simple comma separated string + device_data = [d.strip() for d in raw_devices.strip('[]"\'').split(',')] + + for item in device_data: + # Format: [[id, id, "Loc", "Desc", "MAC"], ...] + if isinstance(item, list) and len(item) > 4: + macs_to_find.append(item[4]) + # Format: ["MAC1", "MAC2"] + elif isinstance(item, str): + # Clean potential quotes + clean_mac = item.strip().replace('"', '').replace("'", "") + if len(clean_mac) in [12, 17]: + macs_to_find.append(clean_mac) + + if macs_to_find: + placeholders = ','.join(['%s'] * len(macs_to_find)) + sql = f"SELECT device_id FROM public.devices WHERE device_mac IN ({placeholders})" + d_rows = self.db.execute(sql, tuple(macs_to_find)) + if d_rows: + for r in d_rows: + ids.add(r['device_id']) + logger.info(f"Found {len(d_rows)} devices in deployment {self.args.deployment_id}") + else: + logger.warning("No matching devices found in DB for the MACs in deployment.") + except Exception as e: + logger.error(f"Failed to parse deployment devices string: {e}") + else: + logger.warning(f"Deployment {self.args.deployment_id} not found or empty.") + + self.target_device_ids = sorted(list(ids)) + if not self.target_device_ids: + logger.error("No valid devices found based on input parameters.") + sys.exit(1) + + logger.log(LOG_DATA, f"Target Device IDs: {self.target_device_ids}") + + def run(self): + # 1. Setup + self.resolve_devices() + + # 2. Parse Dates + try: + start_date = datetime.datetime.strptime(self.args.date_from, "%Y-%m-%d") + end_date = datetime.datetime.strptime(self.args.date_to, "%Y-%m-%d") + except ValueError: + logger.error("Invalid date format. Use YYYY-MM-DD") + sys.exit(1) + + if start_date > end_date: + start_date, end_date = end_date, start_date + + # 3. Open Zip File + try: + logger.info(f"Creating output file: {self.args.outFile}") + zip_buffer = zipfile.ZipFile(self.args.outFile, 'w', zipfile.ZIP_DEFLATED) + except Exception as e: + logger.error(f"Failed to create zip file: {e}") + sys.exit(1) + + # 4. Iterate Days + current_date = start_date + total_rows_exported = 0 + + try: + while current_date <= end_date: + day_str = current_date.strftime("%Y-%m-%d") + + # Define 24h window (UTC assumed based on schema usage in well-api) + t_start = f"{day_str} 00:00:00" + t_end = (current_date + datetime.timedelta(days=1)).strftime("%Y-%m-%d 00:00:00") + + logger.info(f"Processing date: {day_str}...") + + # Build Query + sql = DataProcessor.build_fetch_query( + self.target_device_ids, + t_start, + t_end, + self.args.radar_part, + self.args.allRadar, + self.args.group_by + ) + + # Execute + raw_rows = self.db.execute(sql) + + if raw_rows: + logger.log(LOG_DATA, f" -> Fetched {len(raw_rows)} raw rows (including mtype splits).") + + # Process / Pivot Data + processed_rows, headers = DataProcessor.process_rows(raw_rows) + + count = len(processed_rows) + total_rows_exported += count + logger.log(LOG_STEPS, f" -> Processed into {count} unique time records.") + + # Generate CSV in memory + csv_buffer = io.StringIO() + writer = csv.DictWriter(csv_buffer, fieldnames=headers) + writer.writeheader() + writer.writerows(processed_rows) + + # Add to Zip + # Format: {ID}_{DATE}_{GROUP}_rc_data.csv + csv_filename = f"{self.file_identifier}_{day_str}_{self.args.group_by}_rc_data.csv" + zip_buffer.writestr(csv_filename, csv_buffer.getvalue()) + logger.log(LOG_STEPS, f" -> Added {csv_filename} to zip.") + else: + logger.log(LOG_STEPS, " -> No data found for this date.") + + current_date += datetime.timedelta(days=1) + + except KeyboardInterrupt: + logger.warning("Operation cancelled by user.") + except Exception as e: + logger.error(f"An error occurred during processing: {e}") + if self.args.debug > 0: + import traceback + traceback.print_exc() + finally: + zip_buffer.close() + self.db.close() + + logger.info(f"Export complete. Total records: {total_rows_exported}. File: {self.args.outFile}") + +# ========================================== +# Argument Parsing +# ========================================== + +def parse_arguments(): + parser = argparse.ArgumentParser( + description="Wellnuo Database Export Tool", + formatter_class=argparse.RawTextHelpFormatter, + epilog="""Examples: + # Query by Device ID + wellDbQuery.py --device_id 560 --date_from 2025-03-09 --date_to 2025-04-22 --outFile c.zip + + # Query by Deployment ID (all devices in deployment) + wellDbQuery.py --deployment_id 21 --date_from 2025-06-01 --date_to 2025-06-01 --outFile deployment_21.zip + + # Query by MAC with all radar columns and high debug + wellDbQuery.py --mac 64B70888FAB0 --date_from 2025-01-01 --date_to 2025-01-01 --allRadar -d 2 + """ + ) + + # Selection Group (Mutually Exclusive logic handled in code) + sel = parser.add_argument_group('Target Selection (One required)') + sel.add_argument('--device_id', '-di', type=int, help='Target Device ID (internal DB id)') + sel.add_argument('--well_id', '-wi', type=int, help='Target Well ID (external id)') + sel.add_argument('--mac', '-m', type=str, help='Target Device MAC Address') + sel.add_argument('--deployment_id', '-depid', type=int, help='Target Deployment ID (fetches all devices in deployment)') + + # Date Group + date = parser.add_argument_group('Date Range') + date.add_argument('--date_from', '-df', type=str, required=True, help='Start Date (YYYY-MM-DD)') + date.add_argument('--date_to', '-dt', type=str, required=True, help='End Date (YYYY-MM-DD)') + + # DB Config + db = parser.add_argument_group('Database Configuration') + db.add_argument('--db_name' , default=DEFAULTS['DB_NAME'], help=f"Default: {DEFAULTS['DB_NAME']}") + db.add_argument('--db_username' , default=DEFAULTS['DB_USER'], help=f"Default: {DEFAULTS['DB_USER']}") + db.add_argument('--db_password' , default=DEFAULTS['DB_PASS'], help="Default: from .env") + db.add_argument('--db_host' , default=DEFAULTS['DB_HOST'], help=f"Default: {DEFAULTS['DB_HOST']}") + db.add_argument('--db_port' , default=DEFAULTS['DB_PORT'], help=f"Default: {DEFAULTS['DB_PORT']}") + + # Options + opts = parser.add_argument_group('Export Options') + opts.add_argument('--outFile', '-o' , default=DEFAULTS['OUT_FILE'], help=f"Output ZIP filename (default: {DEFAULTS['OUT_FILE']})") + opts.add_argument('--radar_part', '-radar', default=DEFAULTS['RADAR_PART'], help=f"Radar column expression (default: {DEFAULTS['RADAR_PART']})") + opts.add_argument('--allRadar', '-allr', action='store_true', help="Retrieve all raw radar columns instead of calculated part") + opts.add_argument('--group_by', '-g', default=DEFAULTS['GROUP_BY'], choices=['by_minute', 'by_10_seconds', 'by_hour', 'by_10_minute'], help="Time aggregation bucket") + opts.add_argument('-d', '--debug', type=int, default=0, choices=[0, 1, 2, 3], help="Debug level: 0=Info, 1=Steps, 2=Data, 3=SQL") + + args = parser.parse_args() + + if not any([args.device_id, args.well_id, args.mac, args.deployment_id]): + parser.error("You must provide one of --device_id, --well_id, --mac, or --deployment_id") + + return args + +def setup_logging_level(level): + if level == 0: + logger.setLevel(logging.INFO) + elif level == 1: + logger.setLevel(LOG_STEPS) + elif level == 2: + logger.setLevel(LOG_DATA) + elif level == 3: + logger.setLevel(LOG_SQL) + +# ========================================== +# Entry Point +# ========================================== + +if __name__ == "__main__": + args = parse_arguments() + setup_logging_level(args.debug) + + exporter = WellExporter(args) + exporter.run() \ No newline at end of file diff --git a/well_web_files/deployment.html b/well_web_files/deployment.html index bfd2b2c..db389a8 100644 --- a/well_web_files/deployment.html +++ b/well_web_files/deployment.html @@ -4676,9 +4676,9 @@ async function DeploymentChange() { } } - groups = "0"; // Document.getElementById("group_id").value; - deployments = "0"; // Document.getElementById("Deployments").value; - locations = "0"; // Document.getElementById("Locations").value; + groups = "0"; // document.getElementById("group_id").value; + deployments = document.getElementById("Deployments").value; + locations = "0"; // document.getElementById("Locations").value; RequestFilteredDevices(groups, deployments, locations, fresh); ShowHistoryMap(); //PrepareChart(devices_count); //that is called inside ShowHistoryMap diff --git a/welldrysense_job_db-update.sql b/welldrysense_job_db-update.sql new file mode 100644 index 0000000..9af8b0c --- /dev/null +++ b/welldrysense_job_db-update.sql @@ -0,0 +1,111 @@ +-- ============================================================================= +-- SQL Changes for WellDrySense Product +-- Description: Creates a dedicated 'jobs' table for the Water Damage Mitigation +-- product without altering any existing tables. This script is +-- idempotent and safe to run multiple times. +-- ============================================================================= + +-- 1. Create the new 'jobs' table for the Water Damage Mitigation product. +CREATE TABLE IF NOT EXISTS public.jobs ( + job_id SERIAL PRIMARY KEY, + customer_name TEXT, + mitigation_person_id INTEGER REFERENCES public.person_details(user_id) ON DELETE SET NULL, + key_person_name TEXT, + key_person_mobile TEXT, + key_person_email TEXT, + address_street TEXT, + address_city TEXT, + address_zip TEXT, + address_state TEXT, + address_country TEXT, + lat REAL, + lng REAL, + date_from TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + date_to TIMESTAMP WITH TIME ZONE, + job_status TEXT DEFAULT 'Active' NOT NULL, -- Can be 'Active', 'Stopped', 'Archived' + devices JSONB, -- Stores an array of device objects, e.g., [{"mac": "...", "location": "..."}] + alerts_config JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + user_edit INTEGER REFERENCES public.person_details(user_id) ON DELETE SET NULL +); + +-- Add comments for clarity +COMMENT ON TABLE public.jobs IS 'Stores job information for the WellDrySense water damage mitigation product.'; +COMMENT ON COLUMN public.jobs.devices IS 'A JSON array of device objects assigned to this job. Structure: [{"mac": "AA:BB:CC:DD:EE:FF", "location": "Kitchen Under Sink"}, ...].'; +COMMENT ON COLUMN public.jobs.job_status IS 'The current lifecycle state of the job: Active, Stopped, or Archived.'; +COMMENT ON COLUMN public.jobs.mitigation_person_id IS 'The user from person_details responsible for this job.'; + +-- 2. Add indexes for efficient querying. +CREATE INDEX IF NOT EXISTS idx_jobs_job_status ON public.jobs(job_status); +CREATE INDEX IF NOT EXISTS idx_jobs_mitigation_person_id ON public.jobs(mitigation_person_id); + +-- 3. Add a GIN index for efficient searching within the 'devices' JSONB column. +-- This is crucial for the job_available_devices API. +CREATE INDEX IF NOT EXISTS idx_jobs_devices_gin ON public.jobs USING GIN (devices); + +-- 4. Grant necessary permissions to the application user. +GRANT ALL ON TABLE public.jobs TO well_app; +GRANT USAGE, SELECT ON SEQUENCE jobs_job_id_seq TO well_app; + +-- --- End of Script --- + +-- -- ============================================================================= +-- -- SQL Changes for WellDrySense Product (Version 1.1 - Idempotent) +-- -- File: drysense_db_update.sql +-- -- Description: Creates and configures the 'jobs' table for the DrySense product. +-- -- ============================================================================= + +-- -- 1. Create the 'jobs' table if it does not already exist. +-- CREATE TABLE IF NOT EXISTS public.jobs ( +-- job_id SERIAL PRIMARY KEY, +-- customer_name TEXT, +-- mitigation_person_id INTEGER REFERENCES public.person_details(user_id), +-- key_person_name TEXT, +-- key_person_mobile TEXT, +-- key_person_email TEXT, +-- address_street TEXT, +-- address_city TEXT, +-- address_zip TEXT, +-- address_state TEXT, +-- address_country TEXT, +-- lat REAL, +-- lng REAL, +-- date_from TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, +-- date_to TIMESTAMP WITH TIME ZONE, +-- job_status TEXT DEFAULT 'Active' NOT NULL, -- e.g., 'Active', 'Stopped', 'Archived' +-- devices TEXT, -- Storing as a JSON string of device MACs +-- alerts_config JSONB, -- Store alert settings as a JSON object +-- created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, +-- user_edit INTEGER REFERENCES public.person_details(user_id) +-- ); + +-- -- Add comments to the table and columns for documentation purposes. +-- -- These commands are safe to re-run. +-- COMMENT ON TABLE public.jobs IS 'Stores job information for the WellDrySense water damage mitigation product.'; +-- COMMENT ON COLUMN public.jobs.customer_name IS 'The name of the client for whom the job is being done.'; +-- COMMENT ON COLUMN public.jobs.mitigation_person_id IS 'The user (from person_details) responsible for the job.'; +-- COMMENT ON COLUMN public.jobs.key_person_name IS 'The name of the primary contact person at the client site.'; +-- COMMENT ON COLUMN public.jobs.job_status IS 'Lifecycle status of the job: Active, Stopped, Archived.'; +-- COMMENT ON COLUMN public.jobs.date_to IS 'The date the job was stopped or archived.'; +-- COMMENT ON COLUMN public.jobs.devices IS 'A JSON array of device MAC addresses assigned to this job.'; +-- COMMENT ON COLUMN public.jobs.alerts_config IS 'JSON object storing alert thresholds, e.g., {"temp_abs_high": 30, "hum_rel_above": 15}.'; + +-- -- 2. Add an index for performance if it does not already exist. +-- CREATE INDEX IF NOT EXISTS idx_jobs_job_status ON public.jobs(job_status); + +-- -- 3. Rename the 'description' column to 'location_name' in the 'devices' table if it exists. +-- -- This DO block ensures the ALTER command only runs if the 'description' column exists. +-- DO $$ +-- BEGIN +-- IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name='devices' AND column_name='description') THEN +-- ALTER TABLE public.devices RENAME COLUMN description TO location_name; +-- COMMENT ON COLUMN public.devices.location_name IS 'User-defined name for the specific location of the device on a job site (e.g., Kitchen Under Sink).'; +-- END IF; +-- END $$; + +-- -- 4. Grant necessary permissions to the application user 'well_app'. +-- -- These commands are safe to re-run. +-- GRANT ALL ON TABLE public.jobs TO well_app; +-- GRANT USAGE, SELECT ON SEQUENCE jobs_job_id_seq TO well_app; + +-- -- --- End of Script ---