-- $Id: postgresql_datamining_database.sql,v 1.1 2015/03/10 12:32:23 sofc Exp $
--
-- Note all leaks are supposed to be in mA/cm^2 !!!
--
-- Note that in function load_users() the string DB_W_USERINFO has to be changed
-- to a database name from which user information can be loaded!
-- 
-- It must contain the table 'users' with the following columns (may contain more, but these must be there):
-- id - integer (PK)
-- rid - varchar(16 or less)
-- name - varchar(64 or less)
-- email - varchar(64 or less)
-- active - integer
-- admin - integer
--
-- Note that it may be a good idea to make sure that user id's (rid) of the users in this table match the ones 
-- logging in and controling the rigs

BEGIN;

SELECT id,rid,name,email,active,admin FROM users')

CREATE TABLE users (
        id integer PRIMARY KEY,
        rid VARCHAR(16) NOT NULL,
        name VARCHAR(64) NOT NULL,
        email VARCHAR(64) NOT NULL,
        active int2 NOT NULL,
        admin int2 NOT NULL
);
GRANT SELECT,INSERT,UPDATE ON users TO sofc;
GRANT SELECT ON users TO PUBLIC;

CREATE VIEW actusers AS SELECT * FROM users WHERE active = 1;

CREATE OR REPLACE FUNCTION load_users() RETURNS BOOL AS
$$
DECLARE
        r RECORD;
        old_path TEXT;
BEGIN
        -- Save old search_path; notice we must qualify current_setting
        -- to ensure we invoke the right function
        old_path := pg_catalog.current_setting('search_path');

        -- Set a secure search_path: trusted schemas, then 'pg_temp'.
        -- We set is_local = true so that the old value will be restored
        -- in event of an error before we reach the function end.
        PERFORM pg_catalog.set_config('search_path', 'public', true);
        -- do work;
        FOR r IN SELECT * FROM dblink('dbname=DB_W_USERINFO','SELECT id,rid,name,email,active,admin FROM users')
        AS t1(id integer ,rid varchar(16),name varchar(64),email varchar(64),active int2,admin int2) LOOP
                IF EXISTS (SELECT 1 FROM users WHERE id = r.id) THEN
                        UPDATE users SET rid = r.rid, name = r.name, email = r.email, active = r.active, admin = r.admin WHERE id = r.id;
                ELSE
                        INSERT INTO users (id,rid,name,email,active,admin) VALUES (r.id,r.rid,r.name,r.email,r.active,r.admin);
                END IF;
        END LOOP;

        -- Restore caller's search_path
        PERFORM pg_catalog.set_config('search_path', old_path, true);

        RETURN TRUE;
END;
$$
LANGUAGE plpgsql SECURITY DEFINER;

ALTER FUNCTION load_users() OWNER TO postgres;

GRANT EXECUTE ON FUNCTION load_users() TO PUBLIC;

-- end usertables


CREATE TABLE cells_info (
	id serial PRIMARY KEY,
        date_new TIMESTAMP DEFAULT now() NOT NULL,
        created_by integer REFERENCES users NOT NULL,
        last_modified TIMESTAMP DEFAULT now() NOT NULL,
        modified_by integer REFERENCES users NOT NULL,
        version int2 DEFAULT 0 NOT NULL,
	rig integer NOT NULL,
	test integer NOT NULL,
	cell_code VARCHAR(32) NOT NULL,
	cell_type VARCHAR(32) NOT NULL,
	cell_size VARCHAR(32) NOT NULL,
	cell_active_size VARCHAR(32) NOT NULL,
	stack_element VARCHAR(32),
	stack_element_code VARCHAR(32),
	stack_element_info TEXT,
	stack VARCHAR(32),
	stack_code VARCHAR(32),
	stack_info TEXT,
	production_date TIMESTAMP,
	production_info TEXT,
	FS_type VARCHAR(32),
	FS_materials VARCHAR(32),
	FS_materials_details TEXT,
	FS_thickness float4,
	FS_porosity float4,
	FS_sintering_temp integer,
	FA_materials VARCHAR(32),
	FA_materials_details TEXT,
	FA_thickness float4,
	FA_porosity float4,
	FA_sintering_temp integer,
	FB_materials VARCHAR(32),
	FB_materials_details TEXT,
	FB_thickness float4,
	FB_porosity float4,
	FB_sintering_temp integer,
	EL_materials VARCHAR(32),
	EL_materials_details TEXT,
	EL_thickness float4,
	EL_porosity float4,
	EL_sintering_temp integer,
	OB_type VARCHAR(32),
	OB_materials VARCHAR(32),
	OB_materials_details TEXT,
	OB_thickness float4,
	OB_porosity float4,
	OB_sintering_temp integer,
	OS_materials VARCHAR(32),
	OS_materials_details TEXT,
	OS_thickness float4,
	OS_porosity float4,
	OS_sintering_temp integer,
	search tsvector,
	unique (rig,test)
);

GRANT SELECT,INSERT,UPDATE ON cells_info TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON cells_info TO sofc;
GRANT SELECT,UPDATE ON cells_info_id_seq TO PUBLIC;

CREATE OR REPLACE FUNCTION upd_cells_info() RETURNS TRIGGER AS
$$
DECLARE
	dummy BOOL;
	info TEXT;
BEGIN
	-- Load user table --
	dummy := load_users();
	IF (TG_OP = 'UPDATE') THEN
	        NEW.id = OLD.id;
	        NEW.date_new = OLD.date_new;
	        NEW.created_by = OLD.created_by;
	        NEW.last_modified = now();
	        NEW.version = OLD.version + 1;
	END IF;
	info := coalesce(NEW.cell_code,'') 
	|| ' ' || coalesce(NEW.cell_type,'') 
	|| ' ' || coalesce(NEW.stack_element,'') 
	|| ' ' || coalesce(NEW.stack_element_code,'')  
	|| ' ' || coalesce(NEW.stack_element_info,'') 
	|| ' ' || coalesce(NEW.stack,'') 
	|| ' ' || coalesce(NEW.stack_code,'')
	|| ' ' || coalesce(NEW.stack_info,'')
        || ' ' || coalesce(NEW.production_info,'')
        || ' ' || coalesce(NEW.FS_type,'')
        || ' ' || coalesce(NEW.FS_materials,'')
        || ' ' || coalesce(NEW.FS_materials_details,'')
        || ' ' || coalesce(NEW.FA_materials,'')
        || ' ' || coalesce(NEW.FA_materials_details,'')
        || ' ' || coalesce(NEW.FB_materials,'')
        || ' ' || coalesce(NEW.FB_materials_details,'')
        || ' ' || coalesce(NEW.EL_materials,'')
        || ' ' || coalesce(NEW.EL_materials_details,'')
        || ' ' || coalesce(NEW.OB_type,'')
        || ' ' || coalesce(NEW.OB_materials,'')
        || ' ' || coalesce(NEW.OB_materials_details,'')
        || ' ' || coalesce(NEW.OS_materials,'')
        || ' ' || coalesce(NEW.OS_materials_details,'');
	NEW.search := setweight(to_tsvector('pg_catalog.english',info),'A'); 
	RETURN NEW;
END
$$
LANGUAGE plpgsql;

CREATE TRIGGER cells_info_ins
	BEFORE INSERT OR UPDATE ON cells_info
	FOR EACH ROW
	EXECUTE PROCEDURE upd_cells_info();



CREATE TABLE test_main (
	id serial PRIMARY KEY,
	rig integer NOT NULL,
	test integer NOT NULL,
	date_new TIMESTAMP default now() NOT NULL,
	date_modify TIMESTAMP default now() NOT NULL,
	start_date TIMESTAMP NOT NULL,
	end_date TIMESTAMP NOT NULL,
	area float4 NOT NULL,
	h2_leak_intercept float4,
	h2_leak_slope float4,
	h2_leak_variance float4,
	h2_leak_n integer,
	air_leak_intercept float4,
	air_leak_slope float4,
	air_leak_variance float4,
	air_leak_n integer,
	alt_test_nr TEXT,
	cell_code VARCHAR(32) NOT NULL,
	project VARCHAR(64),
	keywords TEXT,
	testplan TEXT,
	testresult TEXT,
	comments TEXT,
	info TEXT,
	proglog TEXT,
	url VARCHAR(1024) NOT NULL,
	search tsvector,
	unique (rig,test)
);

GRANT SELECT,UPDATE ON test_main TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON test_main TO sofc;
GRANT SELECT,UPDATE on test_main_id_seq TO sofc;

CREATE OR REPLACE FUNCTION upd_test_main() RETURNS TRIGGER AS
$$
BEGIN
	IF (TG_OP = 'DELETE') THEN
		IF (OLD.date_new != OLD.date_new) THEN
			RAISE EXCEPTION 'Record for rig % test % modified after upload, record can not be deleted!',OLD.rig,OLD.test;
			RETURN NULL;
		END IF;
		RETURN OLD;
	END IF;
	IF (TG_OP = 'UPDATE') THEN
		NEW.date_new = OLD.date_new;
		NEW.date_modify = now();
	END IF;
	NEW.search := setweight(to_tsvector('pg_catalog.english',coalesce(NEW.cell_code,'')),'A') ||
                setweight(to_tsvector('pg_catalog.english',coalesce(NEW.alt_test_nr,'')),'A') ||
                setweight(to_tsvector('pg_catalog.english',coalesce(NEW.info,'')),'B') ||
                setweight(to_tsvector('pg_catalog.english',coalesce(NEW.proglog,'')),'C');
	RETURN NEW;
END
$$
LANGUAGE plpgsql;




CREATE TRIGGER test_main_ins
	BEFORE INSERT OR UPDATE OR DELETE ON test_main
	FOR EACH ROW
	EXECUTE PROCEDURE upd_test_main();


CREATE TABLE test_users (
	id serial PRIMARY KEY,
	testid integer REFERENCES test_main ON DELETE CASCADE NOT NULL,
	username VARCHAR (32) NOT NULL CHECK (username <> ''),
	rank integer NOT NULL CHECK (rank > 0)
);

GRANT SELECT ON test_users TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON test_users TO sofc;
GRANT SELECT,UPDATE on test_users_id_seq TO sofc;


CREATE TABLE test_gas_use (
	id serial PRIMARY KEY,
	testid integer REFERENCES test_main ON DELETE CASCADE NOT NULL,
	gas VARCHAR(32) NOT NULL,
	use integer NOT NULL,
	unique(testid,gas)
);

GRANT SELECT ON test_gas_use TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON test_gas_use TO sofc;
GRANT SELECT,UPDATE on test_gas_use_id_seq TO sofc;


CREATE OR REPLACE FUNCTION get_users_test(_testid int) RETURNS TEXT AS
$$
DECLARE
	r record;
	_result TEXT;
BEGIN
	FOR r IN SELECT rank,username FROM test_users WHERE testid = _testid ORDER BY rank DESC LIMIT 3 LOOP
		IF _result IS NULL THEN
			_result := r.username;
		ELSE
			_result := _result || ', ' || r.username;
		END IF;
	END LOOP;
	RETURN _result;
END
$$
LANGUAGE plpgsql;

GRANT EXECUTE ON FUNCTION get_users_test(int) TO PUBLIC;

CREATE TABLE ivcurves (
	id serial PRIMARY KEY,
	testid integer REFERENCES test_main ON DELETE CASCADE NOT NULL,
	ivnr integer NOT NULL,
	date_start TIMESTAMP NOT NULL,
	date_end TIMESTAMP NOT NULL,
	temperature integer NOT NULL,
	temperature_2 integer,
	bottle_temp_anode float4,
	bottle_temp_cathode float4,
	bottle_temp_anode_enabled BOOL,
	bottle_temp_cathode_enabled BOOL,
	leak_current float4 NOT NULL,
	OCV float4 NOT NULL,
	OCV_theory float4 NOT NULL,
	pO2_in float4,
	pO2_out float4,
	Imax float4 NOT NULL,
	url VARCHAR(1024),
	comments TEXT,
	unique (testid,ivnr)
);


GRANT SELECT ON ivcurves TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON ivcurves TO sofc;
GRANT SELECT,UPDATE ON ivcurves_id_seq TO sofc;

CREATE TABLE ivgasflow (
	id serial PRIMARY KEY,
	ivid integer REFERENCES ivcurves ON DELETE CASCADE NOT NULL,
	gasname VARCHAR(64) NOT NULL CHECK (gasname <> ''),
	flow float4 NOT NULL
);

CREATE INDEX ivgasflow_gasname_lower ON ivgasflow ((lower(gasname)));
CREATE INDEX ivgasflow_ivid ON ivgasflow (ivid);

GRANT SELECT ON ivgasflow TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON ivgasflow TO sofc;
GRANT SELECT,UPDATE ON ivgasflow_id_seq TO sofc;

CREATE TABLE iv_res (
	id serial PRIMARY KEY,
	ivid integer REFERENCES ivcurves ON DELETE CASCADE NOT NULL,
	type CHAR(1) NOT NULL,
	value float4 NOT NULL,
	secant float4,
	fu_corr float4
);

CREATE INDEX iv_res_type_lower ON iv_res ((lower(type)));
CREATE INDEX iv_res_value ON iv_res (value);

GRANT SELECT ON iv_res TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON iv_res TO sofc;
GRANT SELECT,UPDATE ON iv_res_id_seq TO sofc;

CREATE VIEW iv_curve_data AS 
SELECT
	tm.id AS testid,
	tm.rig || 'test' || tm.test AS testnr,
	'iV-curve'::TEXT AS type,
	iv.id AS id,
	ivnr AS nr,
	iv.date_start,
	EXTRACT('EPOCH' FROM iv.date_start - tm.start_date) / 3600 AS hours,
	temperature,
	temperature_2,
	bottle_temp_anode,
	bottle_temp_anode_enabled,
	bottle_temp_cathode,
	bottle_temp_cathode_enabled,
	COALESCE(h2.flow,0.0) AS h2,
	COALESCE(o2.flow,0.0) AS o2,
	COALESCE(air.flow,0.0) AS air,
	COALESCE(co.flow,0.0) AS co,
	COALESCE(co2.flow,0.0) AS co2,
	COALESCE(ch4.flow,0.0) AS ch4,
	COALESCE(o2_cathode.flow,0.0) AS o2_cathode,
	COALESCE(n2_cathode.flow,0.0) AS n2_cathode,
	fuel.flow AS total_anode,
	oxygen.flow AS total_cathode,
	leak_current,
	OCV,
	OCV_theory,
	pO2_in,
	pO2_out,
	Imax,
	iv.url,
	COALESCE(fu_07.secant,fu_12.secant) AS R_secant,
	COALESCE(fu_07.fu_corr,fu_12.fu_corr) AS ASR_FU,
	COALESCE(fu_07.fu_corr,fu_12.fu_corr) AS rtotal,
	abs(leak_current) AS abs_leak,
	iv.comments
FROM ivcurves iv 
	INNER JOIN test_main tm ON (iv.testid = tm.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'h2') AS h2 ON (h2.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'o2') AS o2 ON (o2.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'air') AS air ON (air.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'ch4') AS ch4 ON (ch4.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'co2') AS co2 ON (co2.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'co') AS co ON (co.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'o2_cathode') AS o2_cathode ON (o2_cathode.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'n2_cathode') AS n2_cathode ON (n2_cathode.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'fuel_total') AS fuel ON (fuel.ivid = iv.id) 
	LEFT OUTER JOIN (SELECT ivid,flow FROM ivgasflow WHERE lower(gasname) = 'oxygen_total') AS oxygen ON (oxygen.ivid = iv.id)
	LEFT OUTER JOIN (SELECT ivid,fu_corr,secant FROM iv_res WHERE type = 'U' AND value > 0.695 AND value < 0.705) AS fu_07 ON (fu_07.ivid = iv.id)
	LEFT OUTER JOIN (SELECT ivid,fu_corr,secant FROM iv_res WHERE type = 'U' AND value > 1.195 AND value < 1.205) AS fu_12 ON (fu_12.ivid = iv.id)
;


GRANT SELECT ON iv_curve_data TO PUBLIC;

CREATE TABLE impedance (
	id serial PRIMARY KEY,
	testid integer REFERENCES test_main ON DELETE CASCADE NOT NULL,
	impnr integer NOT NULL,
	filename VARCHAR(256) NOT NULL,
	date TIMESTAMP NOT NULL,
	temperature integer,
	temperature_2 integer,
	bottle_temp_anode float4,
	bottle_temp_cathode float4,
	bottle_temp_anode_enabled BOOL,
	bottle_temp_cathode_enabled BOOL,
	leak_current float4,
	voltage float4,
	current float4,
	O2_in float4,
	O2_out float4,
	OCV_theory float4,
	xH2O_theory float4,
	xH2O_out float4,
	rs float4 NOT NULL,
	rp float4 NOT NULL,
	url VARCHAR(1024),
	comments TEXT,
	unique (testid,impnr)
);

GRANT SELECT ON impedance TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON impedance TO sofc;
GRANT SELECT,UPDATE ON impedance_id_seq TO sofc;

CREATE TABLE impgasflow (
	id serial PRIMARY KEY,
	impid integer REFERENCES impedance ON DELETE CASCADE NOT NULL,
	gasname VARCHAR(64) NOT NULL CHECK (gasname <> ''),
	flow float4 NOT NULL
);

CREATE INDEX impgasflow_gasname_lower ON impgasflow ((lower(gasname)));
CREATE INDEX impgasflow_impid ON impgasflow ((impid));

GRANT SELECT ON impgasflow TO PUBLIC;
GRANT SELECT,INSERT,UPDATE,DELETE ON impgasflow TO sofc;
GRANT SELECT,UPDATE ON impgasflow_id_seq TO sofc;


CREATE VIEW imp_data AS 
SELECT
	tm.id AS testid,
	tm.rig || 'test' || tm.test AS testnr,
	'impedance'::TEXT AS type,
	iv.id AS id,
	impnr AS nr,
	iv.date AS date_start,
	EXTRACT('EPOCH' FROM iv.date - tm.start_date) / 3600 AS hours,
	temperature,
	temperature_2,
	bottle_temp_anode,
	bottle_temp_anode_enabled,
	bottle_temp_cathode,
	bottle_temp_cathode_enabled,
	COALESCE(h2.flow,0.0) AS h2,
	COALESCE(o2.flow,0.0) AS o2,
	COALESCE(air.flow,0.0) AS air,
	COALESCE(co.flow,0.0) AS co,
	COALESCE(co2.flow,0.0) AS co2,
	COALESCE(ch4.flow,0.0) AS ch4,
	COALESCE(o2_cathode.flow,0.0) AS o2_cathode,
	COALESCE(n2_cathode.flow,0.0) AS n2_cathode,
	fuel.flow AS total_anode,
	oxygen.flow AS total_cathode,
	leak_current,
	voltage AS OCV,
	OCV_theory,
	O2_in AS pO2_in,
	O2_out AS pO2_out,
	current AS Imax,
	iv.url,
	rs,
	rp,
	COALESCE(rs,0) + COALESCE(rp,0) AS rtotal,
	abs(leak_current) AS abs_leak,
	iv.comments
FROM impedance iv 
	INNER JOIN test_main tm ON (iv.testid = tm.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'h2') AS h2 ON (h2.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'o2') AS o2 ON (o2.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'air') AS air ON (air.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'ch4') AS ch4 ON (ch4.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'co2') AS co2 ON (co2.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'co') AS co ON (co.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'o2_cathode') AS o2_cathode ON (o2_cathode.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'n2_cathode') AS n2_cathode ON (n2_cathode.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'fuel_total') AS fuel ON (fuel.impid = iv.id) 
	LEFT OUTER JOIN (SELECT impid,flow FROM impgasflow WHERE lower(gasname) = 'oxygen_total') AS oxygen ON (oxygen.impid = iv.id)
;

GRANT SELECT ON imp_data TO PUBLIC;

CREATE TABLE tags (
	id serial PRIMARY KEY,
	userid integer REFERENCES users NOT NULL,
	name VARCHAR(32),
	unique(userid,name)
);

GRANT SELECT,INSERT,UPDATE,DELETE ON tags TO public;
GRANT SELECT,UPDATE ON tags_id_seq TO public;

CREATE TABLE tag_iv (
	id serial PRIMARY KEY,
	tagid integer REFERENCES tags ON DELETE CASCADE NOT NULL,
	ivid integer REFERENCES ivcurves ON DELETE CASCADE NOT NULL,
	unique(tagid,ivid)
);

GRANT SELECT,INSERT,UPDATE,DELETE ON tag_iv TO public;
GRANT SELECT,UPDATE ON tag_iv_id_seq TO public;

CREATE TABLE tag_imp (
	id serial PRIMARY KEY,
	tagid integer REFERENCES tags ON DELETE CASCADE NOT NULL,
	impid integer REFERENCES impedance ON DELETE CASCADE NOT NULL,
	unique(tagid,impid)
);

GRANT SELECT,INSERT,UPDATE,DELETE ON tag_imp TO public;
GRANT SELECT,UPDATE ON tag_imp_id_seq TO public;

CREATE VIEW tags_iv_test AS
	SELECT Distinct name, userid,rig,test 
	FROM tags 
	INNER JOIN tag_iv ON (tagid = tags.id) 
	INNER JOIN ivcurves ON (ivid = ivcurves.id) 
	INNER JOIN test_main ON (testid = test_main.id);

GRANT SELECT ON tags_iv_test TO PUBLIC;

CREATE VIEW tags_imp_test AS
	SELECT Distinct name, userid,rig,test 
	FROM tags 
	INNER JOIN tag_imp ON (tagid = tags.id) 
	INNER JOIN impedance ON (impid = impedance.id) 
	INNER JOIN test_main ON (testid = test_main.id);

GRANT SELECT ON tags_imp_test TO PUBLIC;

COMMIT;

