-- drop all objects;

CREATE TABLE Assessment (
	Name   VARCHAR PRIMARY KEY,
	Out_of NUMERIC(7,3) NOT NULL
);

CREATE TABLE Category (
	Category   VARCHAR PRIMARY KEY,
	Sort_Order REAL NOT NULL
);

CREATE TABLE Editor_Type (
	Editor_Type VARCHAR PRIMARY KEY
);

INSERT INTO Editor_Type VALUES ('Scale'),('Numeric');

CREATE TABLE Criterion_Type (
	Criterion_Type VARCHAR PRIMARY KEY
);

INSERT INTO Criterion_Type VALUES ('Normal'),('Special'),('Penalty');

CREATE TABLE Criterion (
	Criterion      VARCHAR PRIMARY KEY,
	Category       VARCHAR NOT NULL,
	Description    VARCHAR NOT NULL,
	Sort_order     REAL NOT NULL,
	Weight         REAL NOT NULL,
	Min            SMALLINT NOT NULL,
	Max            SMALLINT NOT NULL,
	Editor_Type    VARCHAR NOT NULL,
	Criterion_Type VARCHAR NOT NULL,
	CONSTRAINT FK_Criterion_Category FOREIGN KEY (Category) REFERENCES Category (Category),
	CONSTRAINT FK_Criterion_Editor_Type FOREIGN KEY (Editor_Type) REFERENCES Editor_type (Editor_Type),
	CONSTRAINT FK_Criterion_Criterion_Type FOREIGN KEY (Criterion_Type) REFERENCES Criterion_type (Criterion_Type)
);

CREATE TABLE Student (
	Submission_ID VARCHAR UNIQUE NOT NULL,
	Student_ID    VARCHAR UNIQUE NOT NULL,
	Username      VARCHAR UNIQUE NOT NULL,
	First_Name    VARCHAR NOT NULL,
	Last_Name     VARCHAR NOT NULL,
	Email         VARCHAR UNIQUE NOT NULL,
	Send_Feedback BOOLEAN NOT NULL DEFAULT TRUE,
	Email_Sent    BOOLEAN NOT NULL DEFAULT FALSE,
	CONSTRAINT PK_Student PRIMARY KEY (Submission_ID)
);

CREATE TABLE Submission (
	Submission_ID VARCHAR PRIMARY KEY,
	CONSTRAINT FK_Submission_Student FOREIGN KEY (Submission_ID) REFERENCES Student (Submission_ID)
);

CREATE TABLE Result (
	Submission_ID VARCHAR NOT NULL,
	Criterion     VARCHAR NOT NULL,
	Result        NUMERIC(10,6) NOT NULL,
	Comment       VARCHAR,
	CONSTRAINT PK_Result PRIMARY KEY (Submission_ID, Criterion),
	CONSTRAINT FK_Result_Student FOREIGN KEY (Submission_ID) REFERENCES Submission (Submission_ID),
	CONSTRAINT FK_Result_Criterion FOREIGN KEY (Criterion) REFERENCES Criterion (Criterion)
);

CREATE OR REPLACE VIEW Weighted_Normal AS
	SELECT
		Submission_ID,
		Cast(Sum(Weighted) * (
			SELECT
				Out_Of
			FROM
				Assessment) as NUMERIC(10,6)) AS Mark
	FROM (
		SELECT
			Submission_ID,
			Result,
			Weight,
			Max,
			(Result / Max * (Weight / (
						SELECT
							Sum(Weight)
						FROM
							Criterion
						WHERE
							Criterion_Type = 'Normal'))) AS Weighted
			FROM
				Result
				INNER JOIN Criterion ON Result.Criterion = Criterion.Criterion
			WHERE
				Criterion_Type = 'Normal')
	GROUP BY
		Submission_ID;

-- special marks are multiplied by the criterion weight and added to the mark- use
-- negative weight for penalties
CREATE OR REPLACE VIEW Weighted_Special AS
	SELECT Submission_id,
			Cast(Sum(Special) as NUMERIC(10,6)) AS special
		FROM
			(
				SELECT Submission_id, ( Result * Weight ) AS special
					FROM Result
						INNER JOIN Criterion ON Result.Criterion = Criterion.Criterion
					WHERE Criterion_Type = 'Special'

				UNION ALL

					SELECT DISTINCT Submission_id, 0
						FROM Result
			)
		GROUP BY Submission_id;

CREATE OR REPLACE VIEW Weighted_Penalty AS
	SELECT Submission_ID,
			Cast(Sum(Penalty) as NUMERIC(10,6))  AS Penalty
		FROM
			(
				SELECT Submission_ID, ( Result * Weight ) AS Penalty
					FROM Result
						INNER JOIN Criterion ON Result.Criterion = Criterion.Criterion
					WHERE Criterion_Type = 'Penalty'

				UNION ALL

					SELECT DISTINCT Submission_ID, 0
						FROM Result
			)
		GROUP BY Submission_id;

-- version to use when bonus contributes to other assessment (maximum mark CAN be exceeded)
CREATE OR REPLACE VIEW Mark AS
	SELECT
		Weighted_Normal.Submission_ID,
		case when mark - penalty > 0
			then cast(round(Mark - Penalty,3) as NUMERIC(7,3))
			else 0
			end as Mark
	FROM Weighted_Normal
		INNER JOIN Weighted_Penalty USING (Submission_ID)

-- version to use when bonus contributes only to this assessment (maximum mark can not
-- be exceeded)
-- CREATE VIEW mark
-- AS
-- SELECT weighted_mark.submission_id,
-- Round(
-- Casewhen(
-- mark + special <= (SELECT out_of FROM   assessment),
-- mark + special,
-- (SELECT out_of FROM assessment)
-- ),
-- 5) AS MARK
-- FROM weighted_mark
-- INNER JOIN weighted_special
-- ON weighted_mark.submission_id = weighted_special.submission_id;

CREATE VIEW marking_schedule AS
SELECT
	criterion.category,
	description,
	weight,
	max AS OUTOF
FROM
	criterion
	INNER JOIN category ON category.category = criterion.category
ORDER BY
	category.sort_order,
	criterion.sort_order;

CREATE OR REPLACE VIEW All_Marks ("Student", "Mark", "Penalty", "Special", "Complete", "Email Sent") AS
	SELECT
		Submission.Submission_ID,
		COALESCE(Cast(Round(Mark, 3) AS VARCHAR), ''),
		COALESCE(Cast(Round(Penalty, 3) AS VARCHAR), ''),
		COALESCE(Cast(Round(Special, 3) AS VARCHAR), ''),
		COALESCE(Cast(CASE WHEN Complete THEN 'Yes' ELSE 'No' END AS varchar), ''),
		COALESCE(Cast(CASE WHEN Email_Sent THEN 'Yes' ELSE 'No' END AS varchar), '')
	FROM
		Submission
		LEFT JOIN Mark USING (Submission_ID)
		LEFT JOIN Weighted_Special USING (Submission_ID)
		LEFT JOIN Weighted_Penalty USING (Submission_ID)
		LEFT JOIN (
			SELECT
				Submission_ID,
				Count(Criterion) = (
					SELECT
						Count(Criterion)
					FROM
						Criterion) AS Complete
				FROM
					Result
				GROUP BY
					Submission_ID)
	USING (Submission_ID)
		LEFT JOIN student USING (Submission_ID);

-- view for showing relative weightings as percentages of total mark
CREATE OR REPLACE VIEW weighted_schedule AS
SELECT
	cat.category,
	criterion,
	description,
	weight,
	round(weight / total_weight * 100, 2) AS relative_weight,
	round(weight / total_weight * (
			SELECT
				out_of
			FROM assessment), 2) AS out_of
FROM
	criterion crit,
	(
		SELECT
			sum(weight) AS total_weight
		FROM
			criterion
		WHERE
			Criterion_Type = 'Normal')
		INNER JOIN category cat ON crit.category = cat.category
	WHERE Criterion_Type = 'Normal'
	ORDER BY
		cat.sort_order,
		crit.sort_order;

CREATE OR REPLACE VIEW weighted_categories AS
SELECT
	category,
	round(cast(sum(relative_weight) as real), 2) AS weight,
	round(cast(sum(relative_weight) / 100 * (select out_of from assessment) as real), 2) AS out_of,
FROM
	WEIGHTED_SCHEDULE
GROUP BY
	category;

CREATE OR REPLACE VIEW report AS
SELECT
	a.name AS assessment,
	a.out_of,
	s.student_id,
	s.first_name || ' ' || s.last_name AS name,
	s.username,
	cat.category,
	cat.sort_order AS cat_order,
	cr.description AS criterion,
	cr.max AS max,
	cr.sort_order AS cr_order,
	r.result,
	r.comment,
	round(m.mark, 3) AS mark,
	round(penalty, 3) as penalty,
	round(special, 3) as special

FROM
	assessment a
	JOIN category cat
	INNER JOIN criterion cr ON cat.category = cr.category
	INNER JOIN result r ON r.criterion = cr.criterion
	INNER JOIN Student s ON s.Submission_ID = r.Submission_ID
	INNER JOIN mark m ON m.submission_id = r.Submission_ID
	INNER JOIN weighted_special ws on ws.submission_id = r.Submission_ID
	INNER JOIN Weighted_Penalty wp on wp.submission_id = r.Submission_ID
ORDER BY
	student_id,
	cat_order,
	cr_order;

-- use this if you want to produce useful category totals (and you don't care
-- about weighting for individual criterion)
-- UPDATE criterion
-- SET	weight = Cast(max AS DOUBLE) / (SELECT Count(*)
-- FROM   criterion);


create view unmarked as select * from student where submission_id not in (select distinct submission_id from result)
