From 14b058757670c1c79d3efc002f7d41fc7a63b634 Mon Sep 17 00:00:00 2001 From: Vadim Melnik Date: Wed, 6 Sep 2023 16:46:29 +0300 Subject: [PATCH] PostgreSQL migration: now both SQLite3 and PostgreSQL 15 databases are supported by server and DAO layer. --- db/db_dev_postgres15.sql | 735 +++++++++++++++++++++++++++++++++++++++ db/db_dev_sqlite3.sql | 175 ++++++++++ docs/install.txt | 4 + poetry.lock | 127 ++++++- pyproject.toml | 3 +- repromon.ini | 2 + repromon_app/config.py | 1 + repromon_app/dao.py | 66 ++-- repromon_app/db.py | 8 +- repromon_app/model.py | 2 +- 10 files changed, 1075 insertions(+), 48 deletions(-) create mode 100644 db/db_dev_postgres15.sql create mode 100644 db/db_dev_sqlite3.sql diff --git a/db/db_dev_postgres15.sql b/db/db_dev_postgres15.sql new file mode 100644 index 0000000..bb9f9c6 --- /dev/null +++ b/db/db_dev_postgres15.sql @@ -0,0 +1,735 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 15.4 +-- Dumped by pg_dump version 15.4 + +-- Started on 2023-09-05 23:33:29 EEST + +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; + +-- +-- TOC entry 5 (class 2615 OID 2200) +-- Name: repromon; Type: SCHEMA; Schema: -; Owner: pg_database_owner +-- + +CREATE SCHEMA repromon; + + +ALTER SCHEMA repromon OWNER TO pg_database_owner; + +-- +-- TOC entry 3684 (class 0 OID 0) +-- Dependencies: 5 +-- Name: SCHEMA repromon; Type: COMMENT; Schema: -; Owner: pg_database_owner +-- + +COMMENT ON SCHEMA repromon IS 'ReproMon app schema'; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- TOC entry 214 (class 1259 OID 16470) +-- Name: data_provider; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.data_provider ( + id integer NOT NULL, + provider character varying(15) NOT NULL +); + + +ALTER TABLE repromon.data_provider OWNER TO postgres; + +-- +-- TOC entry 215 (class 1259 OID 16473) +-- Name: device; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.device ( + id integer NOT NULL, + kind character varying(15) NOT NULL, + description character varying(128) NOT NULL +); + + +ALTER TABLE repromon.device OWNER TO postgres; + +-- +-- TOC entry 216 (class 1259 OID 16476) +-- Name: message_category; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.message_category ( + id integer NOT NULL, + category character varying(45) NOT NULL +); + + +ALTER TABLE repromon.message_category OWNER TO postgres; + +-- +-- TOC entry 217 (class 1259 OID 16479) +-- Name: message_level; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.message_level ( + id integer NOT NULL, + level character varying(8) NOT NULL +); + + +ALTER TABLE repromon.message_level OWNER TO postgres; + +-- +-- TOC entry 221 (class 1259 OID 16536) +-- Name: message_log; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.message_log ( + id integer NOT NULL, + level_id integer NOT NULL, + category_id integer NOT NULL, + device_id integer NOT NULL, + provider_id integer NOT NULL, + study_id integer, + study_name character varying(255), + is_visible character(1) DEFAULT 'Y'::bpchar NOT NULL, + visible_updated_on timestamp without time zone, + visible_updated_by character varying(15), + description character varying(255), + payload json, + event_on timestamp without time zone NOT NULL, + registered_on timestamp without time zone NOT NULL, + recorded_on timestamp without time zone NOT NULL, + recorded_by character varying(15) NOT NULL +); + + +ALTER TABLE repromon.message_log OWNER TO postgres; + +-- +-- TOC entry 220 (class 1259 OID 16535) +-- Name: message_log_id_seq; Type: SEQUENCE; Schema: repromon; Owner: postgres +-- + +CREATE SEQUENCE repromon.message_log_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE repromon.message_log_id_seq OWNER TO postgres; + +-- +-- TOC entry 3685 (class 0 OID 0) +-- Dependencies: 220 +-- Name: message_log_id_seq; Type: SEQUENCE OWNED BY; Schema: repromon; Owner: postgres +-- + +ALTER SEQUENCE repromon.message_log_id_seq OWNED BY repromon.message_log.id; + + +-- +-- TOC entry 218 (class 1259 OID 16487) +-- Name: role; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.role ( + id integer NOT NULL, + rolename character varying(45) NOT NULL, + description character varying(128) NOT NULL +); + + +ALTER TABLE repromon.role OWNER TO postgres; + +-- +-- TOC entry 223 (class 1259 OID 16551) +-- Name: sec_user_device; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.sec_user_device ( + id integer NOT NULL, + user_id integer NOT NULL, + device_id integer NOT NULL +); + + +ALTER TABLE repromon.sec_user_device OWNER TO postgres; + +-- +-- TOC entry 222 (class 1259 OID 16550) +-- Name: sec_user_device_id_seq; Type: SEQUENCE; Schema: repromon; Owner: postgres +-- + +CREATE SEQUENCE repromon.sec_user_device_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE repromon.sec_user_device_id_seq OWNER TO postgres; + +-- +-- TOC entry 3686 (class 0 OID 0) +-- Dependencies: 222 +-- Name: sec_user_device_id_seq; Type: SEQUENCE OWNED BY; Schema: repromon; Owner: postgres +-- + +ALTER SEQUENCE repromon.sec_user_device_id_seq OWNED BY repromon.sec_user_device.id; + + +-- +-- TOC entry 225 (class 1259 OID 16560) +-- Name: sec_user_role; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.sec_user_role ( + id integer NOT NULL, + user_id integer NOT NULL, + role_id integer NOT NULL +); + + +ALTER TABLE repromon.sec_user_role OWNER TO postgres; + +-- +-- TOC entry 224 (class 1259 OID 16559) +-- Name: sec_user_role_id_seq; Type: SEQUENCE; Schema: repromon; Owner: postgres +-- + +CREATE SEQUENCE repromon.sec_user_role_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE repromon.sec_user_role_id_seq OWNER TO postgres; + +-- +-- TOC entry 3687 (class 0 OID 0) +-- Dependencies: 224 +-- Name: sec_user_role_id_seq; Type: SEQUENCE OWNED BY; Schema: repromon; Owner: postgres +-- + +ALTER SEQUENCE repromon.sec_user_role_id_seq OWNED BY repromon.sec_user_role.id; + + +-- +-- TOC entry 227 (class 1259 OID 16569) +-- Name: study_data; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.study_data ( + id integer NOT NULL, + name character varying(128) NOT NULL, + device_id integer, + status_id integer, + start_ts timestamp without time zone, + end_ts timestamp without time zone, + info json +); + + +ALTER TABLE repromon.study_data OWNER TO postgres; + +-- +-- TOC entry 226 (class 1259 OID 16568) +-- Name: study_data_id_seq; Type: SEQUENCE; Schema: repromon; Owner: postgres +-- + +CREATE SEQUENCE repromon.study_data_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE repromon.study_data_id_seq OWNER TO postgres; + +-- +-- TOC entry 3688 (class 0 OID 0) +-- Dependencies: 226 +-- Name: study_data_id_seq; Type: SEQUENCE OWNED BY; Schema: repromon; Owner: postgres +-- + +ALTER SEQUENCE repromon.study_data_id_seq OWNED BY repromon.study_data.id; + + +-- +-- TOC entry 219 (class 1259 OID 16511) +-- Name: study_status; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon.study_status ( + id integer NOT NULL, + status character varying(45) NOT NULL +); + + +ALTER TABLE repromon.study_status OWNER TO postgres; + +-- +-- TOC entry 229 (class 1259 OID 16578) +-- Name: user; Type: TABLE; Schema: repromon; Owner: postgres +-- + +CREATE TABLE repromon."user" ( + id integer NOT NULL, + username character varying(15) NOT NULL, + is_active character(1) DEFAULT 'N'::bpchar NOT NULL, + is_system character(1) DEFAULT 'N'::bpchar NOT NULL, + first_name character varying(45), + last_name character varying(45), + email character varying(128), + phone character varying(16), + description character varying(128), + password character varying(128), + password_changed_on timestamp without time zone, + last_login timestamp without time zone +); + + +ALTER TABLE repromon."user" OWNER TO postgres; + +-- +-- TOC entry 228 (class 1259 OID 16577) +-- Name: user_id_seq; Type: SEQUENCE; Schema: repromon; Owner: postgres +-- + +CREATE SEQUENCE repromon.user_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE repromon.user_id_seq OWNER TO postgres; + +-- +-- TOC entry 3689 (class 0 OID 0) +-- Dependencies: 228 +-- Name: user_id_seq; Type: SEQUENCE OWNED BY; Schema: repromon; Owner: postgres +-- + +ALTER SEQUENCE repromon.user_id_seq OWNED BY repromon."user".id; + + +-- +-- TOC entry 3483 (class 2604 OID 16539) +-- Name: message_log id; Type: DEFAULT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.message_log ALTER COLUMN id SET DEFAULT nextval('repromon.message_log_id_seq'::regclass); + + +-- +-- TOC entry 3485 (class 2604 OID 16554) +-- Name: sec_user_device id; Type: DEFAULT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.sec_user_device ALTER COLUMN id SET DEFAULT nextval('repromon.sec_user_device_id_seq'::regclass); + + +-- +-- TOC entry 3486 (class 2604 OID 16563) +-- Name: sec_user_role id; Type: DEFAULT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.sec_user_role ALTER COLUMN id SET DEFAULT nextval('repromon.sec_user_role_id_seq'::regclass); + + +-- +-- TOC entry 3487 (class 2604 OID 16572) +-- Name: study_data id; Type: DEFAULT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.study_data ALTER COLUMN id SET DEFAULT nextval('repromon.study_data_id_seq'::regclass); + + +-- +-- TOC entry 3488 (class 2604 OID 16581) +-- Name: user id; Type: DEFAULT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon."user" ALTER COLUMN id SET DEFAULT nextval('repromon.user_id_seq'::regclass); + + +-- +-- TOC entry 3663 (class 0 OID 16470) +-- Dependencies: 214 +-- Data for Name: data_provider; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.data_provider (id, provider) FROM stdin; +1 ReproIn +2 ReproStim +3 ReproEvents +4 PACS +5 Noisseur +6 DICOM/QA +7 MRI +\. + + +-- +-- TOC entry 3664 (class 0 OID 16473) +-- Dependencies: 215 +-- Data for Name: device; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.device (id, kind, description) FROM stdin; +1 MRI DBIC 3T Siemens Prisma MRI +\. + + +-- +-- TOC entry 3665 (class 0 OID 16476) +-- Dependencies: 216 +-- Data for Name: message_category; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.message_category (id, category) FROM stdin; +1 Feedback +\. + + +-- +-- TOC entry 3666 (class 0 OID 16479) +-- Dependencies: 217 +-- Data for Name: message_level; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.message_level (id, level) FROM stdin; +1 INFO +2 WARNING +3 ERROR +\. + + +-- +-- TOC entry 3670 (class 0 OID 16536) +-- Dependencies: 221 +-- Data for Name: message_log; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.message_log (id, level_id, category_id, device_id, provider_id, study_id, study_name, is_visible, visible_updated_on, visible_updated_by, description, payload, event_on, registered_on, recorded_on, recorded_by) FROM stdin; +1 1 1 1 2 \N \N Y \N \N stimuli display dis-connected \N 2023-06-07 10:49:31 2023-06-07 10:50:01 2023-06-07 10:50:31 reprostim +2 1 1 1 2 \N \N Y \N \N stimuli display connected(1024x768, …)\n \N 2023-06-07 10:50:22 2023-06-07 10:51:02 2023-06-07 10:51:22 reprostim +3 3 1 1 5 1 Halchenko/Horea/1020_animal_mri Y \N \N subject “John” is not conformant, must match [0-9]{6} regular expression. [link to screen with highlight]\n \N 2023-06-07 10:51:44 2023-06-07 10:52:04 2023-06-07 10:52:44 noisseur +4 1 1 1 5 1 Halchenko/Horea/1020_animal_mri Y \N \N proceeded with compliant data on study Halchenko/Horea/1020_animal_mri \N 2023-06-07 10:54:17 2023-06-07 10:55:07 2023-06-07 10:55:17 noisseur +5 1 1 1 3 \N \N Y \N \N MRI trigger event received \N 2023-06-07 10:55:45 2023-06-07 10:56:05 2023-06-07 10:56:45 reproevt +6 3 1 1 6 \N \N Y \N \N MRI data lacks rear head coils data [link to PACS recording to review] \N 2023-06-07 10:58:01 2023-06-07 10:59:00 2023-06-07 10:59:01 dicomqa +\. + + +-- +-- TOC entry 3667 (class 0 OID 16487) +-- Dependencies: 218 +-- Data for Name: role; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.role (id, rolename, description) FROM stdin; +1 admin Administrator +2 data_collector Data Collector +3 mri_operator MRI Operator +4 participant Participant +5 sys_data_entry System Data Entry +6 tester Automatic System Tester +\. + + +-- +-- TOC entry 3672 (class 0 OID 16551) +-- Dependencies: 223 +-- Data for Name: sec_user_device; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.sec_user_device (id, user_id, device_id) FROM stdin; +1 1 1 +2 2 1 +3 3 1 +4 4 1 +5 5 1 +6 6 1 +\. + + +-- +-- TOC entry 3674 (class 0 OID 16560) +-- Dependencies: 225 +-- Data for Name: sec_user_role; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.sec_user_role (id, user_id, role_id) FROM stdin; +1 4 1 +2 1 2 +3 2 3 +4 3 4 +5 5 4 +6 5 2 +7 5 3 +8 6 5 +9 1 1 +10 5 5 +\. + + +-- +-- TOC entry 3676 (class 0 OID 16569) +-- Dependencies: 227 +-- Data for Name: study_data; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.study_data (id, name, device_id, status_id, start_ts, end_ts, info) FROM stdin; +1 Halchenko/Horea/1020_animal_mri 1 101 2023-06-07 10:41:25 \N {\n\t"data_collector": ["user1", "poweruser"],\n\t"mri_operator": ["user2"],\n\t"participant": ["user3"]\n} +\. + + +-- +-- TOC entry 3668 (class 0 OID 16511) +-- Dependencies: 219 +-- Data for Name: study_status; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon.study_status (id, status) FROM stdin; +100 New +101 Collecting MRI Data +190 Completed +191 Failed +200 Entering Participant Data +300 Designing Sequences +\. + + +-- +-- TOC entry 3678 (class 0 OID 16578) +-- Dependencies: 229 +-- Data for Name: user; Type: TABLE DATA; Schema: repromon; Owner: postgres +-- + +COPY repromon."user" (id, username, is_active, is_system, first_name, last_name, email, phone, description, password, password_changed_on, last_login) FROM stdin; +1 user1 Y N John Smith user1@repromon.com 321 \N $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +2 user2 Y N Dave Cooper user2@repromon.com 231 \N $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +3 user3 Y N Lucy Nelson user3@repromon.com 111 \N $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +4 admin Y N Admin Admin admin@repromon.com \N Administrator $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +5 poweruser Y N Power User poweruser@repromon.com \N Power user $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +6 noisseur Y Y noisseur noisseur \N System con/noisseur user $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +7 reprostim Y Y reprostim reprostim \N \N ReproStim Screen Capture\n $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +8 reproevt Y Y reproevt reproevt \N \N ReproEvents Capture\n $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +9 dicomqa Y Y dicomqa dicomqa \N \N DICOMS/QA $2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq \N \N +\. + + +-- +-- TOC entry 3690 (class 0 OID 0) +-- Dependencies: 220 +-- Name: message_log_id_seq; Type: SEQUENCE SET; Schema: repromon; Owner: postgres +-- + +SELECT pg_catalog.setval('repromon.message_log_id_seq', 6, true); + + +-- +-- TOC entry 3691 (class 0 OID 0) +-- Dependencies: 222 +-- Name: sec_user_device_id_seq; Type: SEQUENCE SET; Schema: repromon; Owner: postgres +-- + +SELECT pg_catalog.setval('repromon.sec_user_device_id_seq', 6, true); + + +-- +-- TOC entry 3692 (class 0 OID 0) +-- Dependencies: 224 +-- Name: sec_user_role_id_seq; Type: SEQUENCE SET; Schema: repromon; Owner: postgres +-- + +SELECT pg_catalog.setval('repromon.sec_user_role_id_seq', 10, true); + + +-- +-- TOC entry 3693 (class 0 OID 0) +-- Dependencies: 226 +-- Name: study_data_id_seq; Type: SEQUENCE SET; Schema: repromon; Owner: postgres +-- + +SELECT pg_catalog.setval('repromon.study_data_id_seq', 1, false); + + +-- +-- TOC entry 3694 (class 0 OID 0) +-- Dependencies: 228 +-- Name: user_id_seq; Type: SEQUENCE SET; Schema: repromon; Owner: postgres +-- + +SELECT pg_catalog.setval('repromon.user_id_seq', 9, true); + + +-- +-- TOC entry 3492 (class 2606 OID 16526) +-- Name: data_provider data_provider_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.data_provider + ADD CONSTRAINT data_provider_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3494 (class 2606 OID 16528) +-- Name: device device_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.device + ADD CONSTRAINT device_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3516 (class 2606 OID 16587) +-- Name: user idx_user_name; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon."user" + ADD CONSTRAINT idx_user_name UNIQUE (username); + + +-- +-- TOC entry 3496 (class 2606 OID 16530) +-- Name: message_category message_category_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.message_category + ADD CONSTRAINT message_category_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3498 (class 2606 OID 16532) +-- Name: message_level message_level_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.message_level + ADD CONSTRAINT message_level_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3504 (class 2606 OID 16544) +-- Name: message_log message_log_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.message_log + ADD CONSTRAINT message_log_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3500 (class 2606 OID 16547) +-- Name: role role_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.role + ADD CONSTRAINT role_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3506 (class 2606 OID 16556) +-- Name: sec_user_device sec_user_device_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.sec_user_device + ADD CONSTRAINT sec_user_device_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3508 (class 2606 OID 16558) +-- Name: sec_user_device sec_user_device_unique_constraint; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.sec_user_device + ADD CONSTRAINT sec_user_device_unique_constraint UNIQUE (user_id, device_id); + + +-- +-- TOC entry 3510 (class 2606 OID 16565) +-- Name: sec_user_role sec_user_role_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.sec_user_role + ADD CONSTRAINT sec_user_role_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3512 (class 2606 OID 16567) +-- Name: sec_user_role sec_user_role_unique_constraint; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.sec_user_role + ADD CONSTRAINT sec_user_role_unique_constraint UNIQUE (user_id, role_id); + + +-- +-- TOC entry 3514 (class 2606 OID 16576) +-- Name: study_data study_data_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.study_data + ADD CONSTRAINT study_data_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3502 (class 2606 OID 16549) +-- Name: study_status study_status_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon.study_status + ADD CONSTRAINT study_status_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 3518 (class 2606 OID 16589) +-- Name: user user_email_unique_constraint; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon."user" + ADD CONSTRAINT user_email_unique_constraint UNIQUE (email); + + +-- +-- TOC entry 3520 (class 2606 OID 16585) +-- Name: user user_pkey; Type: CONSTRAINT; Schema: repromon; Owner: postgres +-- + +ALTER TABLE ONLY repromon."user" + ADD CONSTRAINT user_pkey PRIMARY KEY (id); + + +-- Completed on 2023-09-05 23:33:29 EEST + +-- +-- PostgreSQL database dump complete +-- diff --git a/db/db_dev_sqlite3.sql b/db/db_dev_sqlite3.sql new file mode 100644 index 0000000..50c1cbf --- /dev/null +++ b/db/db_dev_sqlite3.sql @@ -0,0 +1,175 @@ +PRAGMA foreign_keys=OFF; +BEGIN TRANSACTION; +CREATE TABLE IF NOT EXISTS "sec_user_role" ( + "id" INTEGER NOT NULL UNIQUE, + "user_id" INTEGER NOT NULL, + "role_id" INTEGER NOT NULL, + PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("user_id","role_id") +); +INSERT INTO sec_user_role VALUES(1,4,1); +INSERT INTO sec_user_role VALUES(2,1,2); +INSERT INTO sec_user_role VALUES(3,2,3); +INSERT INTO sec_user_role VALUES(4,3,4); +INSERT INTO sec_user_role VALUES(5,5,4); +INSERT INTO sec_user_role VALUES(6,5,2); +INSERT INTO sec_user_role VALUES(7,5,3); +INSERT INTO sec_user_role VALUES(8,6,5); +INSERT INTO sec_user_role VALUES(9,1,1); +INSERT INTO sec_user_role VALUES(10,5,5); +CREATE TABLE IF NOT EXISTS "message_category" ( + "id" INTEGER NOT NULL UNIQUE, + "category" VARCHAR(45), + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO message_category VALUES(1,'Feedback'); +CREATE TABLE IF NOT EXISTS "data_provider" ( + "id" INTEGER NOT NULL UNIQUE, + "provider" VARCHAR(15) NOT NULL UNIQUE, + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO data_provider VALUES(1,'ReproIn'); +INSERT INTO data_provider VALUES(2,'ReproStim'); +INSERT INTO data_provider VALUES(3,'ReproEvents'); +INSERT INTO data_provider VALUES(4,'PACS'); +INSERT INTO data_provider VALUES(5,'Noisseur'); +INSERT INTO data_provider VALUES(6,'DICOM/QA'); +INSERT INTO data_provider VALUES(7,'MRI'); +CREATE TABLE IF NOT EXISTS "study_status" ( + "id" INTEGER NOT NULL UNIQUE, + "status" VARCHAR(45), + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO study_status VALUES(100,'New'); +INSERT INTO study_status VALUES(101,'Collecting MRI Data'); +INSERT INTO study_status VALUES(190,'Completed'); +INSERT INTO study_status VALUES(191,'Failed'); +INSERT INTO study_status VALUES(200,'Entering Participant Data'); +INSERT INTO study_status VALUES(300,'Designing Sequences'); +CREATE TABLE IF NOT EXISTS "todo_study_session" ( + "id" INTEGER NOT NULL UNIQUE, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "todo_study_screen" ( + "id" INTEGER NOT NULL UNIQUE, + PRIMARY KEY("id" AUTOINCREMENT) +); +CREATE TABLE IF NOT EXISTS "message_level" ( + "id" INTEGER NOT NULL UNIQUE, + "level" VARCHAR(8), + PRIMARY KEY("id") +); +INSERT INTO message_level VALUES(1,'INFO'); +INSERT INTO message_level VALUES(2,'WARNING'); +INSERT INTO message_level VALUES(3,'ERROR'); +CREATE TABLE IF NOT EXISTS "device" ( + "id" INTEGER NOT NULL UNIQUE, + "kind" VARCHAR(15), + "description" VARCHAR(128), + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO device VALUES(1,'MRI','DBIC 3T Siemens Prisma MRI'); +CREATE TABLE IF NOT EXISTS "sec_user_device" ( + "id" INTEGER NOT NULL UNIQUE, + "user_id" INTEGER NOT NULL, + "device_id" INTEGER NOT NULL, + UNIQUE("user_id","device_id"), + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO sec_user_device VALUES(1,1,1); +INSERT INTO sec_user_device VALUES(2,2,1); +INSERT INTO sec_user_device VALUES(3,3,1); +INSERT INTO sec_user_device VALUES(4,4,1); +INSERT INTO sec_user_device VALUES(5,5,1); +INSERT INTO sec_user_device VALUES(6,6,1); +CREATE TABLE IF NOT EXISTS "user" ( + "id" INTEGER NOT NULL UNIQUE, + "username" VARCHAR(15) NOT NULL UNIQUE, + "is_active" CHAR(1) DEFAULT 'N', + "is_system" CHAR(1) NOT NULL DEFAULT 'N', + "first_name" VARCHAR(45), + "last_name" VARCHAR(45), + "email" VARCHAR(128) UNIQUE, + "phone" VARCHAR(16), + "description" VARCHAR(128), + "password" VARCHAR(45), + "password_changed_on" TIMESTAMP, + "last_login" TIMESTAMP, + PRIMARY KEY("id" AUTOINCREMENT), + UNIQUE("email") +); +INSERT INTO user VALUES(1,'user1','Y','N','John','Smith','user1@repromon.com','321',NULL,'$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(2,'user2','Y','N','Dave','Cooper','user2@repromon.com','231',NULL,'$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(3,'user3','Y','N','Lucy','Nelson','user3@repromon.com','111',NULL,'$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(4,'admin','Y','N','Admin','Admin','admin@repromon.com',NULL,'Administrator','$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(5,'poweruser','Y','N','Power','User','poweruser@repromon.com',NULL,'Power user','$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(6,'noisseur','Y','Y','noisseur','noisseur','',NULL,'System con/noisseur user','$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(7,'reprostim','Y','Y','reprostim','reprostim',NULL,NULL,replace('ReproStim Screen Capture\n','\n',char(10)),'$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(8,'reproevt','Y','Y','reproevt','reproevt',NULL,NULL,replace('ReproEvents Capture\n','\n',char(10)),'$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +INSERT INTO user VALUES(9,'dicomqa','Y','Y','dicomqa','dicomqa',NULL,NULL,'DICOMS/QA','$2b$12$ShaYDykDo9yJe0sLxBCMqe0f4OhUXD9iZf4FYFhQIwNTt9/WNBkfq',NULL,NULL); +CREATE TABLE IF NOT EXISTS "role" ( + "id" INTEGER NOT NULL UNIQUE, + "rolename" VARCHAR(45) NOT NULL, + "description" VARCHAR(128), + UNIQUE("rolename"), + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO role VALUES(1,'admin','Administrator'); +INSERT INTO role VALUES(2,'data_collector','Data Collector'); +INSERT INTO role VALUES(3,'mri_operator','MRI Operator'); +INSERT INTO role VALUES(4,'participant','Participant'); +INSERT INTO role VALUES(5,'sys_data_entry','System Data Entry'); +INSERT INTO role VALUES(6,'tester','Automatic System Tester'); +CREATE TABLE IF NOT EXISTS "study_data" ( + "id" INTEGER NOT NULL UNIQUE, + "name" VARCHAR(128), + "device_id" INTEGER, + "status_id" INTEGER, + "start_ts" TIMESTAMP, + "end_ts" TIMESTAMP, + "info" JSON, + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO study_data VALUES(1,'Halchenko/Horea/1020_animal_mri',1,101,'2023-06-07 10:41:25',NULL,replace('{\n "data_collector": ["user1", "poweruser"],\n "mri_operator": ["user2"],\n "participant": ["user3"]\n}','\n',char(10))); +CREATE TABLE IF NOT EXISTS "message_log" ( + "id" INTEGER NOT NULL UNIQUE, + "level_id" INTEGER, + "category_id" INTEGER, + "device_id" INTEGER, + "provider_id" INTEGER, + "study_id" INTEGER, + "study_name" VARCHAR(128), + "is_visible" INTEGER NOT NULL DEFAULT 'Y', + "visible_updated_on" TIMESTAMP, + "visible_updated_by" VARCHAR(15), + "description" VARCHAR(255), + "payload" JSON, + "event_on" TIMESTAMP, + "registered_on" TIMESTAMP, + "recorded_on" TIMESTAMP, + "recorded_by" VARCHAR(15), + PRIMARY KEY("id" AUTOINCREMENT) +); +INSERT INTO message_log VALUES(1,1,1,1,2,NULL,NULL,'Y',NULL,NULL,'stimuli display dis-connected',NULL,'2023-06-07 10:49:31','2023-06-07 10:50:01','2023-06-07 10:50:31','reprostim'); +INSERT INTO message_log VALUES(2,1,1,1,2,NULL,NULL,'Y',NULL,NULL,replace('stimuli display connected(1024x768, …)\n','\n',char(10)),NULL,'2023-06-07 10:50:22','2023-06-07 10:51:02','2023-06-07 10:51:22','reprostim'); +INSERT INTO message_log VALUES(3,3,1,1,5,1,'Halchenko/Horea/1020_animal_mri','Y',NULL,NULL,replace('subject “John” is not conformant, must match [0-9]{6} regular expression. [link to screen with highlight]\n','\n',char(10)),NULL,'2023-06-07 10:51:44','2023-06-07 10:52:04','2023-06-07 10:52:44','noisseur'); +INSERT INTO message_log VALUES(4,1,1,1,5,1,'Halchenko/Horea/1020_animal_mri','Y',NULL,NULL,'proceeded with compliant data on study Halchenko/Horea/1020_animal_mri',NULL,'2023-06-07 10:54:17','2023-06-07 10:55:07','2023-06-07 10:55:17','noisseur'); +INSERT INTO message_log VALUES(5,1,1,1,3,NULL,NULL,'Y',NULL,NULL,'MRI trigger event received',NULL,'2023-06-07 10:55:45','2023-06-07 10:56:05','2023-06-07 10:56:45','reproevt'); +INSERT INTO message_log VALUES(6,3,1,1,6,NULL,NULL,'Y',NULL,NULL,'MRI data lacks rear head coils data [link to PACS recording to review]',NULL,'2023-06-07 10:58:01','2023-06-07 10:59:00','2023-06-07 10:59:01','dicomqa'); +DELETE FROM sqlite_sequence; +INSERT INTO sqlite_sequence VALUES('sec_user_role',10); +INSERT INTO sqlite_sequence VALUES('message_category',1); +INSERT INTO sqlite_sequence VALUES('data_provider',7); +INSERT INTO sqlite_sequence VALUES('study_status',300); +INSERT INTO sqlite_sequence VALUES('todo_study_session',0); +INSERT INTO sqlite_sequence VALUES('todo_study_screen',0); +INSERT INTO sqlite_sequence VALUES('device',1); +INSERT INTO sqlite_sequence VALUES('sec_user_device',6); +INSERT INTO sqlite_sequence VALUES('user',9); +INSERT INTO sqlite_sequence VALUES('role',6); +INSERT INTO sqlite_sequence VALUES('study_data',1); +INSERT INTO sqlite_sequence VALUES('message_log',6); +CREATE INDEX "idx_user_name" ON "user" ( + "username" +); +COMMIT; diff --git a/docs/install.txt b/docs/install.txt index 2243d63..b5df070 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -55,3 +55,7 @@ poetry run srv 1) Generate TOKEN_SECRET_KEY: openssl rand -hex 32 + +[PostgreSQL on MacOS] + + export PATH=/Library/PostgreSQL/15/bin:$PATH diff --git a/poetry.lock b/poetry.lock index 0102248..cce071f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,24 +2,24 @@ [[package]] name = "anyio" -version = "3.7.1" +version = "4.0.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.0.0-py3-none-any.whl", hash = "sha256:cfdb2b588b9fc25ede96d8db56ed50848b0b649dca3dd1df0b11f683bb9e0b5f"}, + {file = "anyio-4.0.0.tar.gz", hash = "sha256:f7ed51751b2c2add651e5747c891b47e26d2a21be5d32d9311dfe9692f3e5d7a"}, ] [package.dependencies] -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.22)"] [[package]] name = "bcrypt" @@ -57,13 +57,13 @@ typecheck = ["mypy"] [[package]] name = "click" -version = "8.1.6" +version = "8.1.7" description = "Composable command line interface toolkit" optional = false python-versions = ">=3.7" files = [ - {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, - {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] [package.dependencies] @@ -346,19 +346,108 @@ totp = ["cryptography"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "psycopg2" +version = "2.9.7" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.6" +files = [ + {file = "psycopg2-2.9.7-cp310-cp310-win32.whl", hash = "sha256:1a6a2d609bce44f78af4556bea0c62a5e7f05c23e5ea9c599e07678995609084"}, + {file = "psycopg2-2.9.7-cp310-cp310-win_amd64.whl", hash = "sha256:b22ed9c66da2589a664e0f1ca2465c29b75aaab36fa209d4fb916025fb9119e5"}, + {file = "psycopg2-2.9.7-cp311-cp311-win32.whl", hash = "sha256:44d93a0109dfdf22fe399b419bcd7fa589d86895d3931b01fb321d74dadc68f1"}, + {file = "psycopg2-2.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:91e81a8333a0037babfc9fe6d11e997a9d4dac0f38c43074886b0d9dead94fe9"}, + {file = "psycopg2-2.9.7-cp37-cp37m-win32.whl", hash = "sha256:d1210fcf99aae6f728812d1d2240afc1dc44b9e6cba526a06fb8134f969957c2"}, + {file = "psycopg2-2.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:e9b04cbef584310a1ac0f0d55bb623ca3244c87c51187645432e342de9ae81a8"}, + {file = "psycopg2-2.9.7-cp38-cp38-win32.whl", hash = "sha256:d5c5297e2fbc8068d4255f1e606bfc9291f06f91ec31b2a0d4c536210ac5c0a2"}, + {file = "psycopg2-2.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:8275abf628c6dc7ec834ea63f6f3846bf33518907a2b9b693d41fd063767a866"}, + {file = "psycopg2-2.9.7-cp39-cp39-win32.whl", hash = "sha256:c7949770cafbd2f12cecc97dea410c514368908a103acf519f2a346134caa4d5"}, + {file = "psycopg2-2.9.7-cp39-cp39-win_amd64.whl", hash = "sha256:b6bd7d9d3a7a63faae6edf365f0ed0e9b0a1aaf1da3ca146e6b043fb3eb5d723"}, + {file = "psycopg2-2.9.7.tar.gz", hash = "sha256:f00cc35bd7119f1fed17b85bd1007855194dde2cbd8de01ab8ebb17487440ad8"}, +] + +[[package]] +name = "psycopg2-binary" +version = "2.9.7" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.6" +files = [ + {file = "psycopg2-binary-2.9.7.tar.gz", hash = "sha256:1b918f64a51ffe19cd2e230b3240ba481330ce1d4b7875ae67305bd1d37b041c"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ea5f8ee87f1eddc818fc04649d952c526db4426d26bab16efbe5a0c52b27d6ab"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2993ccb2b7e80844d534e55e0f12534c2871952f78e0da33c35e648bf002bbff"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbbc3c5d15ed76b0d9db7753c0db40899136ecfe97d50cbde918f630c5eb857a"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:692df8763b71d42eb8343f54091368f6f6c9cfc56dc391858cdb3c3ef1e3e584"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dcfd5d37e027ec393a303cc0a216be564b96c80ba532f3d1e0d2b5e5e4b1e6e"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17cc17a70dfb295a240db7f65b6d8153c3d81efb145d76da1e4a096e9c5c0e63"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e5666632ba2b0d9757b38fc17337d84bdf932d38563c5234f5f8c54fd01349c9"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7db7b9b701974c96a88997d458b38ccb110eba8f805d4b4f74944aac48639b42"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c82986635a16fb1fa15cd5436035c88bc65c3d5ced1cfaac7f357ee9e9deddd4"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4fe13712357d802080cfccbf8c6266a3121dc0e27e2144819029095ccf708372"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-win32.whl", hash = "sha256:122641b7fab18ef76b18860dd0c772290566b6fb30cc08e923ad73d17461dc63"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-win_amd64.whl", hash = "sha256:f8651cf1f144f9ee0fa7d1a1df61a9184ab72962531ca99f077bbdcba3947c58"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4ecc15666f16f97709106d87284c136cdc82647e1c3f8392a672616aed3c7151"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fbb1184c7e9d28d67671992970718c05af5f77fc88e26fd7136613c4ece1f89"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7968fd20bd550431837656872c19575b687f3f6f98120046228e451e4064df"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:094af2e77a1976efd4956a031028774b827029729725e136514aae3cdf49b87b"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26484e913d472ecb6b45937ea55ce29c57c662066d222fb0fbdc1fab457f18c5"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f309b77a7c716e6ed9891b9b42953c3ff7d533dc548c1e33fddc73d2f5e21f9"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d92e139ca388ccfe8c04aacc163756e55ba4c623c6ba13d5d1595ed97523e4b"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2df562bb2e4e00ee064779902d721223cfa9f8f58e7e52318c97d139cf7f012d"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4eec5d36dbcfc076caab61a2114c12094c0b7027d57e9e4387b634e8ab36fd44"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1011eeb0c51e5b9ea1016f0f45fa23aca63966a4c0afcf0340ccabe85a9f65bd"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-win32.whl", hash = "sha256:ded8e15f7550db9e75c60b3d9fcbc7737fea258a0f10032cdb7edc26c2a671fd"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:8a136c8aaf6615653450817a7abe0fc01e4ea720ae41dfb2823eccae4b9062a3"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2dec5a75a3a5d42b120e88e6ed3e3b37b46459202bb8e36cd67591b6e5feebc1"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc10da7e7df3380426521e8c1ed975d22df678639da2ed0ec3244c3dc2ab54c8"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee919b676da28f78f91b464fb3e12238bd7474483352a59c8a16c39dfc59f0c5"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb1c0e682138f9067a58fc3c9a9bf1c83d8e08cfbee380d858e63196466d5c86"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00d8db270afb76f48a499f7bb8fa70297e66da67288471ca873db88382850bf4"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b0c2b466b2f4d89ccc33784c4ebb1627989bd84a39b79092e560e937a11d4ac"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:51d1b42d44f4ffb93188f9b39e6d1c82aa758fdb8d9de65e1ddfe7a7d250d7ad"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:11abdbfc6f7f7dea4a524b5f4117369b0d757725798f1593796be6ece20266cb"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f02f4a72cc3ab2565c6d9720f0343cb840fb2dc01a2e9ecb8bc58ccf95dc5c06"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-win32.whl", hash = "sha256:81d5dd2dd9ab78d31a451e357315f201d976c131ca7d43870a0e8063b6b7a1ec"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:62cb6de84d7767164a87ca97e22e5e0a134856ebcb08f21b621c6125baf61f16"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:59f7e9109a59dfa31efa022e94a244736ae401526682de504e87bd11ce870c22"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:95a7a747bdc3b010bb6a980f053233e7610276d55f3ca506afff4ad7749ab58a"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c721ee464e45ecf609ff8c0a555018764974114f671815a0a7152aedb9f3343"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4f37bbc6588d402980ffbd1f3338c871368fb4b1cfa091debe13c68bb3852b3"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac83ab05e25354dad798401babaa6daa9577462136ba215694865394840e31f8"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:024eaeb2a08c9a65cd5f94b31ace1ee3bb3f978cd4d079406aef85169ba01f08"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1c31c2606ac500dbd26381145684d87730a2fac9a62ebcfbaa2b119f8d6c19f4"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:42a62ef0e5abb55bf6ffb050eb2b0fcd767261fa3faf943a4267539168807522"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7952807f95c8eba6a8ccb14e00bf170bb700cafcec3924d565235dffc7dc4ae8"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e02bc4f2966475a7393bd0f098e1165d470d3fa816264054359ed4f10f6914ea"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-win32.whl", hash = "sha256:fdca0511458d26cf39b827a663d7d87db6f32b93efc22442a742035728603d5f"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:d0b16e5bb0ab78583f0ed7ab16378a0f8a89a27256bb5560402749dbe8a164d7"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6822c9c63308d650db201ba22fe6648bd6786ca6d14fdaf273b17e15608d0852"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f94cb12150d57ea433e3e02aabd072205648e86f1d5a0a692d60242f7809b15"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5ee89587696d808c9a00876065d725d4ae606f5f7853b961cdbc348b0f7c9a1"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad5ec10b53cbb57e9a2e77b67e4e4368df56b54d6b00cc86398578f1c635f329"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:642df77484b2dcaf87d4237792246d8068653f9e0f5c025e2c692fc56b0dda70"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6a8b575ac45af1eaccbbcdcf710ab984fd50af048fe130672377f78aaff6fc1"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f955aa50d7d5220fcb6e38f69ea126eafecd812d96aeed5d5f3597f33fad43bb"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad26d4eeaa0d722b25814cce97335ecf1b707630258f14ac4d2ed3d1d8415265"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ced63c054bdaf0298f62681d5dcae3afe60cbae332390bfb1acf0e23dcd25fc8"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b04da24cbde33292ad34a40db9832a80ad12de26486ffeda883413c9e1b1d5e"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-win32.whl", hash = "sha256:18f12632ab516c47c1ac4841a78fddea6508a8284c7cf0f292cb1a523f2e2379"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb3b8d55924a6058a26db69fb1d3e7e32695ff8b491835ba9f479537e14dcf9f"}, +] + [[package]] name = "pyasn1" version = "0.5.0" @@ -424,13 +513,13 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pytest" -version = "7.4.0" +version = "7.4.1" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" files = [ - {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, - {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, + {file = "pytest-7.4.1-py3-none-any.whl", hash = "sha256:460c9a59b14e27c602eb5ece2e47bec99dc5fc5f6513cf924a7d03a578991b1f"}, + {file = "pytest-7.4.1.tar.gz", hash = "sha256:2f2301e797521b23e4d2585a0a3d7b5e50fdddaaf7e7d6773ea26ddb17c213ab"}, ] [package.dependencies] @@ -732,4 +821,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "7b4d86ba928ec9a6fe93faa7470d675e64cee54dbb4d4ac433a293cbb9e7c2e7" +content-hash = "9b803cf8de58d29eb4baeb516451179796f613d2860d0d79b46f740b1f020210" diff --git a/pyproject.toml b/pyproject.toml index dbf8b41..2ceeb04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,8 @@ Jinja2 = "^3.1.2" uvicorn = "^0.22.0" python-multipart = "^0.0.6" sqlalchemy = "^2.0.16" -#psycopg2 = "^2.9.6" +psycopg2 = "^2.9.7" +psycopg2-binary = "^2.9.7" pydantic = "^1.10.9" websockets = "^11.0.3" python-jose = "^3.3.0" diff --git a/repromon.ini b/repromon.ini index 3415c47..cfabdc9 100644 --- a/repromon.ini +++ b/repromon.ini @@ -23,6 +23,8 @@ UI2_APP_PATH=${ROOT_PATH}/repromon_vue/build #SQLAlchemy DB engine configuration url=sqlite:///${ROOT_PATH}/db/db_dev.sqlite3 +#url=postgresql://user:password@somehost:5432/rmdb?options=-csearch_path=repromon +#arg_schema=repromon echo=True pool_size=20 pool_recycle=3600 diff --git a/repromon_app/config.py b/repromon_app/config.py index 2c69af3..f982c41 100644 --- a/repromon_app/config.py +++ b/repromon_app/config.py @@ -22,6 +22,7 @@ class DbConfig(BaseSectionConfig): """Database configuration under [db***] sections for SQLAlchemy""" url: str = None + arg_schema: str = None echo: bool = False pool_size: int = 5 pool_recycle: int = 3600 diff --git a/repromon_app/dao.py b/repromon_app/dao.py index 441b606..9012c35 100644 --- a/repromon_app/dao.py +++ b/repromon_app/dao.py @@ -42,10 +42,14 @@ def _scalar(cls, proxy): return None +_prefix_: str = '' + + class BaseDAO: """Base class for all DAO objects""" default_session = None + default_schema = None def __init__(self): pass @@ -59,6 +63,12 @@ def commit(self): def flush(self): self.session().flush() + @classmethod + def set_default_schema(cls, db_schema: str): + BaseDAO.default_schema = db_schema + global _prefix_ + _prefix_ = f"{BaseDAO.default_schema}." if BaseDAO.default_schema else '' + def session(self): return BaseDAO.default_session @@ -113,13 +123,13 @@ def get_role_infos(self) -> list[RoleInfoDTO]: self.session() .execute( text( - """ + f""" select id, rolename, description from - role + {_prefix_} role """ ) ) @@ -143,14 +153,14 @@ def get_user_info(self, username: str) -> UserInfoDTO: self.session() .execute( text( - """ + f""" select u.id, u.username, u.first_name, u.last_name from - user u + {_prefix_} user u where u.username = :username """ @@ -207,7 +217,7 @@ def get_message_log_infos(self, category_id: int, self.session() .execute( text( - """ + f""" select ml.id, ml.study_id, @@ -223,11 +233,11 @@ def get_message_log_infos(self, category_id: int, dp.provider, ml.description from - message_log ml - left join message_category mc on ml.category_id = mc.id - left join message_level ll on ml.level_id = ll.id - left join device dv on ml.device_id = dv.id - left join data_provider dp on ml.provider_id = dp.id + {_prefix_} message_log ml + left join {_prefix_} message_category mc on ml.category_id = mc.id + left join {_prefix_} message_level ll on ml.level_id = ll.id + left join {_prefix_} device dv on ml.device_id = dv.id + left join {_prefix_} data_provider dp on ml.provider_id = dp.id where (:study_id is null or ml.study_id = :study_id) and (:category_id is null or ml.category_id = :category_id) and @@ -253,7 +263,7 @@ def get_message_log_info(self, message_id: int) -> MessageLogInfoDTO: self.session() .execute( text( - """ + f""" select ml.id, ml.study_id, @@ -269,11 +279,11 @@ def get_message_log_info(self, message_id: int) -> MessageLogInfoDTO: dp.provider, ml.description from - message_log ml - left join message_category mc on ml.category_id = mc.id - left join message_level ll on ml.level_id = ll.id - left join device dv on ml.device_id = dv.id - left join data_provider dp on ml.provider_id = dp.id + {_prefix_} message_log ml + left join {_prefix_} message_category mc on ml.category_id = mc.id + left join {_prefix_} message_level ll on ml.level_id = ll.id + left join {_prefix_} device dv on ml.device_id = dv.id + left join {_prefix_} data_provider dp on ml.provider_id = dp.id where ml.id = :message_id """ @@ -358,11 +368,11 @@ def get_device_id_by_username(self, username: str) -> list[str]: self.session() .execute( text( - """ + f""" select ud.device_id from - user u, sec_user_device ud + {_prefix_} user u, {_prefix_} sec_user_device ud where u.username = :username and u.id = ud.user_id @@ -395,11 +405,13 @@ def get_username_by_rolename(self, rolename: str) -> list[str]: self.session() .execute( text( - """ + f""" select u.username from - role r, user u, sec_user_role ur + {_prefix_} role r, + {_prefix_} user u, + {_prefix_} sec_user_role ur where r.rolename = :rolename and ur.role_id = r.id and @@ -417,11 +429,13 @@ def get_rolename_by_username(self, username: str) -> list[str]: self.session() .execute( text( - """ + f""" select r.rolename from - user u, role r, sec_user_role ur + {_prefix_} user u, + {_prefix_} role r, + {_prefix_} sec_user_role ur where u.username = :username and u.id = ur.user_id and @@ -448,7 +462,7 @@ def get_study_info(self, study_id: int) -> StudyInfoDTO: self.session() .execute( text( - """ + f""" select sd.id, md.description as device, @@ -457,9 +471,9 @@ def get_study_info(self, study_id: int) -> StudyInfoDTO: sd.start_ts, sd.end_ts from - study_data sd - left join study_status ss on sd.status_id = ss.id - left join device md on sd.device_id = md.id + {_prefix_} study_data sd + left join {_prefix_} study_status ss on sd.status_id = ss.id + left join {_prefix_} device md on sd.device_id = md.id where sd.id = :study_id """ diff --git a/repromon_app/db.py b/repromon_app/db.py index d1a5228..4ce3ba5 100644 --- a/repromon_app/db.py +++ b/repromon_app/db.py @@ -20,12 +20,18 @@ def db_init(params: dict, session_scopefunc=None): :return: """ logger.debug("db_init(...)") - engine = create_engine(**params) + valid_params = {key: value for key, value in params.items() + if not key.startswith("arg_")} + + engine = create_engine(**valid_params) logger.debug(f"created DB engine={str(engine)}") BaseDAO.default_session = scoped_session( sessionmaker(bind=engine), scopefunc=session_scopefunc ) + + BaseDAO.set_default_schema(params["arg_schema"]) + logger.debug(f"set default schema to {params['arg_schema']}") logger.debug("done") diff --git a/repromon_app/model.py b/repromon_app/model.py index 4b1a8a5..11ae895 100644 --- a/repromon_app/model.py +++ b/repromon_app/model.py @@ -390,7 +390,7 @@ class UserEntity(BaseEntity): email = Column(String(128), unique=True) phone = Column(String(16)) description = Column(String(128)) - password = Column(String(45)) + password = Column(String(128)) password_changed_on = Column(TIMESTAMP) last_login = Column(TIMESTAMP)