cloned from code.mipt.ru

This commit is contained in:
2025-07-15 16:59:24 +05:00
commit d7ab972517
35 changed files with 3261 additions and 0 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

+1257
View File
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
CREATE OR REPLACE FUNCTION fn_low_enrollment(threshold INTEGER)
RETURNS TABLE(
discipline_id INTEGER,
discipline_name VARCHAR,
department_name VARCHAR,
enrolled BIGINT
) AS $$
BEGIN
RETURN QUERY
SELECT d.id,
d.name AS discipline_name,
dept.name AS department_name,
COUNT(st.id) AS enrolled
FROM Disciplines d
LEFT JOIN Departments dept ON d.department_id = dept.id
LEFT JOIN Statements st ON st.discipline_id = d.id
GROUP BY d.id, d.name, dept.name
HAVING COUNT(st.id) < threshold;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION fn_generate_transcript(p_student INTEGER)
RETURNS TEXT
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
student_info RECORD;
transcript TEXT;
BEGIN
SELECT p.name || ' ' || p.surname AS full_name,
s.group_id,
s.status
INTO student_info
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
WHERE s.id = p_student;
transcript := 'Transcript for student ' || p_student || E'\n'
|| 'Name: ' || student_info.full_name || E'\n'
|| 'Group: ' || student_info.group_id || E'\n'
|| 'Status: ' || student_info.status || E'\n' || E'\n';
FOR rec IN
SELECT d.name AS discipline_name,
st.grade,
to_char(st.conducted_at,'YYYY-MM-DD') AS date
FROM Statements st
JOIN Disciplines d ON st.discipline_id = d.id
WHERE st.student_id = p_student
ORDER BY st.conducted_at
LOOP
transcript := transcript || rec.date || ' - ' || rec.discipline_name || ': ' || rec.grade || E'\n';
END LOOP;
RETURN transcript;
END;
$$;
CREATE OR REPLACE PROCEDURE sp_graduate_by_admission_year(p_year INTEGER)
LANGUAGE plpgsql
AS $$
DECLARE
rec RECORD;
BEGIN
FOR rec IN
SELECT s.id, g.study_starts
FROM Students s
JOIN Groups g ON s.group_id = g.group_id
WHERE EXTRACT(YEAR FROM g.study_starts) = p_year
AND s.status = 'УЧИТСЯ'
LOOP
INSERT INTO Movement(student_id, type, new_status, issued_at)
VALUES (rec.id, 'ОТЧИСЛЕН', 'ОТЧИСЛЕН', NOW());
END LOOP;
END;
$$;
+9
View File
@@ -0,0 +1,9 @@
CREATE INDEX idx_physicals_fulltext_name ON Physicals
USING GIN (to_tsvector('russian', name || ' ' || surname));
-- GIN (Generalized Inverted Index) хранит для каждой лексемы список указателей на строки,
-- в которых она встречается. Это ускоряет полнотекстовый поиск, так как позволяет быстро
-- находить все документы, содержащие нужные слова, без полного сканирования таблицы.
CREATE INDEX idx_movement_recent ON Movement(issued_at)
WHERE issued_at >= '2024-09-01'::timestamp;
+192
View File
@@ -0,0 +1,192 @@
-- #
-- # "contingent-movement" project;
-- # author: gazakbayev.net
-- # ver: 1.0
-- #
-- 1 Студенты с одинаковыми фамилиями
WITH student_info AS (
SELECT
s.id as student_id,
p.passport_no,
p.surname,
p.name,
g.group_id,
f.name as faculty_name
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
JOIN Groups g ON s.group_id = g.group_id
JOIN Faculties f ON g.faculty_id = f.id
)
SELECT
a.surname,
a.name as student1_name,
b.name as student2_name,
a.group_id as group1,
b.group_id as group2,
a.faculty_name as faculty1,
b.faculty_name as faculty2
FROM student_info a
JOIN student_info b ON a.surname = b.surname
AND a.student_id < b.student_id
ORDER BY a.surname, a.name;
-- 2 Средний балл студентов
SELECT
s.id AS student_id,
p.name,
p.surname,
ROUND(AVG(st.grade)::numeric, 2) AS average_grade
FROM
Students s
JOIN
Physicals p ON s.person = p.passport_no
LEFT JOIN
Statements st ON s.id = st.student_id
GROUP BY
s.id, p.name, p.surname
ORDER BY
average_grade DESC NULLS LAST;
-- 3 Топ 5 лучших
SELECT
s.id,
p.surname,
p.name,
ROUND(AVG(st.grade)::numeric, 2) AS avg_grade,
DENSE_RANK() OVER (ORDER BY AVG(st.grade) DESC) AS rank
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
JOIN Statements st ON s.id = st.student_id
GROUP BY s.id, p.surname, p.name
ORDER BY avg_grade DESC
LIMIT 5;
-- 4 Количества у кафедры дисциплин и студентов
SELECT
d.name AS department,
COUNT(DISTINCT disc.id) AS discipline_count,
COUNT(DISTINCT s.id) AS student_count
FROM Departments d
LEFT JOIN Disciplines disc ON d.id = disc.department_id
LEFT JOIN Groups g ON d.id = g.department_id
LEFT JOIN Students s ON g.group_id = s.group_id
GROUP BY d.id
HAVING COUNT(DISTINCT s.id) > 0
ORDER BY student_count DESC;
-- 5 ФИ обучающихся (статус Учится)
SELECT
p.surname,
p.name
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
WHERE EXISTS (
SELECT 1 FROM Statements st
WHERE st.student_id = s.id
)
AND s.status = 'УЧИТСЯ';
-- 6 Прогресс изменения среднего балла по студентам
SELECT
s.id,
p.surname,
p.name,
g.group_id,
st.conducted_at,
st.grade,
AVG(st.grade) OVER (PARTITION BY s.id ORDER BY st.conducted_at
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_avg
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
JOIN Groups g ON s.group_id = g.group_id
JOIN Statements st ON s.id = st.student_id
ORDER BY s.id, st.conducted_at;
-- 7 Список группы с обучающимися, с указанием оценки успеваемости студента в группе "Ниже среднего" или "Выше среднего"
WITH group_stats AS (
SELECT
s.group_id,
AVG(st.grade) AS group_avg
FROM Students s
JOIN Statements st ON s.id = st.student_id
GROUP BY s.group_id
)
SELECT
s.id,
p.surname,
p.name,
g.group_id,
ROUND(AVG(st.grade)::numeric, 2) AS student_avg,
gs.group_avg,
CASE
WHEN AVG(st.grade) > gs.group_avg THEN 'Выше среднего'
WHEN AVG(st.grade) < gs.group_avg THEN 'Ниже среднего'
ELSE 'Средний'
END AS comparison
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
JOIN Groups g ON s.group_id = g.group_id
JOIN Statements st ON s.id = st.student_id
JOIN group_stats gs ON g.group_id = gs.group_id
GROUP BY s.id, p.surname, p.name, g.group_id, gs.group_avg
ORDER BY g.group_id, comparison;
-- 8 Статистика отчислений по месяцам, основываясь на движении контингента (приказах)
SELECT
DATE_TRUNC('month', m.issued_at) AS month,
COUNT(*) AS dropouts,
SUM(COUNT(*)) OVER (ORDER BY DATE_TRUNC('month', m.issued_at)) AS cumulative_total
FROM Movement m
WHERE m.type = 'ОТЧИСЛЕН'
GROUP BY DATE_TRUNC('month', m.issued_at)
ORDER BY month;
-- 9 Научные руководители студентов
SELECT
s.id AS student_id,
p.name AS student_name,
p.surname AS student_surname,
s.supervisor AS supervisor_id,
ph.name AS supervisor_name,
ph.surname AS supervisor_surname
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
LEFT JOIN Supervisors sup ON s.supervisor = sup.person
LEFT JOIN Physicals ph ON sup.person = ph.passport_no
WHERE s.supervisor IS NOT NULL
ORDER BY student_surname;
-- 10 Вывод преподавателей из ведомости по ФИО, принимающим предметам, средней оценки и уровня халявности:
SELECT
p.surname AS "Фамилия",
p.name AS "Имя",
STRING_AGG(DISTINCT d.name, ', ' ORDER BY d.name) AS "Принимает дисциплины",
ROUND(AVG(st.grade), 2) AS "Средняя оценка",
CASE
WHEN AVG(st.grade) < 3 THEN 'Не халявный'
WHEN AVG(st.grade) < 5 THEN 'Хороший'
WHEN AVG(st.grade) < 8 THEN 'Халявный'
ELSE 'Ультра халявный'
END AS "Уровень халявности"
FROM
Statements st
JOIN
Physicals p ON st.examiner = p.passport_no
JOIN
Disciplines d ON st.discipline_id = d.id
GROUP BY
p.passport_no, p.surname, p.name
HAVING
COUNT(*) >= 5
ORDER BY
"Средняя оценка" DESC;
+126
View File
@@ -0,0 +1,126 @@
-- #
-- # "contingent-movement" project;
-- # author: gazakbayev.net
-- # ver: 1.0
-- #
CREATE TABLE Physicals (
passport_no VARCHAR(30) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
surname VARCHAR(255) NOT NULL,
birthday TIMESTAMP NOT NULL,
phone VARCHAR(20) NOT NULL,
mail VARCHAR(255) NOT NULL,
citizenship VARCHAR(50) NOT NULL,
address VARCHAR(255) NOT NULL,
access_card VARCHAR(20) NOT NULL CHECK (access_card ~ '^[A-F0-9]{2}(:[A-F0-9]{2}){5}$'),
access_level VARCHAR(10) NOT NULL CHECK (access_level IN ('КАМПУС', 'ОБЩЕЖИТИЯ', 'ПОЛНЫЙ'))
);
CREATE TABLE Supervisors (
id SERIAL PRIMARY KEY,
person VARCHAR(32) REFERENCES Physicals(passport_no),
experience INTEGER NOT NULL CHECK (experience >= 0),
defended_ratio REAL NOT NULL CHECK (defended_ratio >= 0 AND defended_ratio <= 1),
qualification VARCHAR(30) NOT NULL CHECK (qualification IN ('МЛАДШИЙ НАУЧНЫЙ СОТРУДНИК', 'СТАРШИЙ НАУЧНЫЙ СОТРУДНИК', 'ВЕДУЩИЙ НАУЧНЫЙ СОТРУДНИК', 'ГЛАВНЫЙ НАУЧНЫЙ СОТРУДНИК', 'НАУЧНЫЙ СОТРУДНИК')),
valid_from TIMESTAMP NOT NULL,
valid_to TIMESTAMP
);
CREATE TABLE Faculties (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
acronym VARCHAR(10) NOT NULL,
head VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
vice VARCHAR(32) REFERENCES Physicals(passport_no),
address VARCHAR(255) NOT NULL
);
CREATE TABLE Departments (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
acronym VARCHAR(10) NOT NULL,
founded TIMESTAMP NOT NULL,
head VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
vice VARCHAR(32) REFERENCES Physicals(passport_no),
secretary VARCHAR(32) REFERENCES Physicals(passport_no),
faculty_id INTEGER REFERENCES Faculties(id)
);
CREATE TABLE Programs (
id SERIAL PRIMARY KEY,
specification VARCHAR(20) NOT NULL,
degree VARCHAR(30) NOT NULL CHECK (degree IN ('BACHELOR', 'MAGISTER', 'ASPIRANT')),
name VARCHAR(255) NOT NULL,
parent_id INTEGER REFERENCES Programs(id)
);
CREATE TABLE Groups (
group_id VARCHAR(10) PRIMARY KEY CHECK (group_id ~ '^[АБМ]\d{2}-\d{3}[а-я]?$'),
faculty_id INTEGER NOT NULL REFERENCES Faculties(id),
program_id INTEGER NOT NULL REFERENCES Programs(id),
department_id INTEGER REFERENCES Departments(id),
study_starts TIMESTAMP NOT NULL DEFAULT NOW(),
study_ends TIMESTAMP NOT NULL DEFAULT NOW() + INTERVAL '4 years'
);
CREATE TABLE Students (
id SERIAL PRIMARY KEY,
person VARCHAR(32) NOT NULL UNIQUE REFERENCES Physicals(passport_no),
group_id VARCHAR(10) NOT NULL REFERENCES Groups(group_id),
supervisor_id INTEGER REFERENCES Supervisors(id),
education_form VARCHAR(255) NOT NULL CHECK (education_form IN ('ОЧНАЯ', 'ЗАОЧНАЯ', 'ВЕЧЕРНЯЯ')),
status VARCHAR(30) NOT NULL CHECK (status IN ('УЧИТСЯ', 'В АКАДЕМИЧЕСКОМ ОТПУСКЕ', 'ОТЧИСЛЕН'))
);
CREATE TABLE Family (
id SERIAL PRIMARY KEY,
person VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
name VARCHAR(255) NOT NULL,
surname VARCHAR(255) NOT NULL,
kinship VARCHAR(10) NOT NULL CHECK (kinship IN ('MOTHER', 'FATHER', 'BROTHER', 'SISTER', 'ANOTHER')),
phone VARCHAR(20) NOT NULL UNIQUE,
address VARCHAR(255) NOT NULL,
CONSTRAINT unique_student_kinship UNIQUE (person, kinship)
);
CREATE TABLE Files (
id SERIAL PRIMARY KEY,
student_id INTEGER NOT NULL REFERENCES Students(id),
name VARCHAR(255) NOT NULL,
description VARCHAR(255) NOT NULL,
extension VARCHAR(5) NOT NULL CHECK (extension IN ('PNG', 'JPEG', 'PDF')),
size NUMERIC(10,2) NOT NULL CHECK (size <= 20),
path VARCHAR(255) NOT NULL UNIQUE,
loaded_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE Disciplines (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
department_id INTEGER NOT NULL REFERENCES Departments(id),
credit_units INTEGER NOT NULL,
academic_hours INTEGER NOT NULL,
general_hours INTEGER NOT NULL CHECK (general_hours > academic_hours),
is_annual BOOLEAN NOT NULL
);
CREATE TABLE Statements (
id SERIAL PRIMARY KEY,
student_id INTEGER NOT NULL REFERENCES Students(id),
discipline_id INTEGER NOT NULL REFERENCES Disciplines(id),
examiner VARCHAR(32) NOT NULL REFERENCES Physicals(passport_no),
try_no INTEGER NOT NULL CHECK (try_no BETWEEN 0 AND 2),
grade INTEGER NOT NULL CHECK (grade BETWEEN 1 AND 10),
conducted_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE TABLE Movement (
id SERIAL PRIMARY KEY,
student_id INTEGER NOT NULL REFERENCES Students(id),
type VARCHAR(30) NOT NULL CHECK (type IN ('ЗАЧИСЛЕН', 'ВОССТАНОВЛЕН', 'ОТЧИСЛЕН', 'В АКАДЕМИЧЕСКИЙ ОТПУСК', 'ПЕРЕВОД В ДРУГУЮ ГРУППУ')),
new_group VARCHAR(10) REFERENCES Groups(group_id),
new_status VARCHAR(30) CHECK (new_status IN ('УЧИТСЯ', 'В АКАДЕМИЧЕСКОМ ОТПУСКЕ', 'ОТЧИСЛЕН')),
issued_at TIMESTAMP NOT NULL DEFAULT NOW()
);
+101
View File
@@ -0,0 +1,101 @@
-- Trigger 1
CREATE OR REPLACE FUNCTION trg_prevent_group_delete()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
DECLARE cnt INTEGER;
BEGIN
SELECT COUNT(*) INTO cnt FROM Students WHERE group_id = OLD.group_id AND status = 'УЧИТСЯ';
IF cnt > 0 THEN
RAISE EXCEPTION 'Cannot delete group %: % active students exist', OLD.group_id, cnt;
END IF;
RETURN OLD;
END;
$$;
CREATE TRIGGER prevent_group_delete
BEFORE DELETE ON Groups
FOR EACH ROW
EXECUTE PROCEDURE trg_prevent_group_delete();
-- Trigger 2
CREATE OR REPLACE FUNCTION trg_lowercase_mail()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
NEW.mail := lower(NEW.mail);
RETURN NEW;
END;
$$;
CREATE TRIGGER lowercase_mail
BEFORE INSERT OR UPDATE ON Physicals
FOR EACH ROW
EXECUTE PROCEDURE trg_lowercase_mail();
-- Trigger 3
CREATE OR REPLACE FUNCTION trg_sync_student_status()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
UPDATE Students
SET status = NEW.new_status
WHERE id = NEW.student_id;
RETURN NEW;
END;
$$;
CREATE TRIGGER sync_student_status
AFTER INSERT ON Movement
FOR EACH ROW
EXECUTE PROCEDURE trg_sync_student_status();
-- Trigger SCD 2
CREATE OR REPLACE FUNCTION trg_supervisors_history()
RETURNS trigger AS $$
BEGIN
NEW.valid_from = NOW();
INSERT INTO Supervisors (
person,
experience,
defended_ratio,
qualification,
valid_from,
valid_to
) VALUES (
OLD.person,
OLD.experience,
OLD.defended_ratio,
OLD.qualification,
OLD.valid_from,
NOW()
);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE TRIGGER trg_supervisors_scd
BEFORE UPDATE ON Supervisors
FOR EACH ROW
EXECUTE PROCEDURE trg_supervisors_history();
CREATE OR REPLACE FUNCTION check_unique_supervisor()
RETURNS TRIGGER AS $$
BEGIN
IF EXISTS (
SELECT 1 FROM Supervisors
WHERE person = NEW.person
AND experience = NEW.experience
AND defended_ratio = NEW.defended_ratio
AND qualification = NEW.qualification
AND valid_from = NEW.valid_from
AND (valid_to = NEW.valid_to OR (valid_to IS NULL AND NEW.valid_to IS NULL))
) THEN
RAISE EXCEPTION 'Дублирующая запись в таблице Supervisors. Все поля (кроме id) должны быть уникальными.';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER unique_supervisor_trigger
BEFORE INSERT ON Supervisors
FOR EACH ROW
EXECUTE FUNCTION check_unique_supervisor();
+22
View File
@@ -0,0 +1,22 @@
-- STUDENT PERFORMANCE --
CREATE OR REPLACE VIEW vw_student_performance AS
SELECT s.id AS student_id,
p.name || ' ' || p.surname AS full_name,
s.group_id,
COUNT(st.id) FILTER (WHERE st.grade >= 3) AS passes,
COUNT(st.id) FILTER (WHERE st.grade < 3) AS fails,
ROUND(AVG(st.grade)::numeric, 2) AS avg_grade
FROM Students s
JOIN Physicals p ON s.person = p.passport_no
LEFT JOIN Statements st ON s.id = st.student_id
GROUP BY s.id, p.name, p.surname, s.group_id;
-- BIRTHDAYS ON THIS MONTH --
CREATE VIEW vw_birthdays_month AS
SELECT passport_no,
name || ' ' || surname AS full_name,
birthday,
EXTRACT(DAY FROM birthday) AS day_of_month
FROM Physicals
WHERE EXTRACT(MONTH FROM birthday) = EXTRACT(MONTH FROM NOW())
ORDER BY day_of_month;
@@ -0,0 +1,110 @@
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 YaBrowser/25.2.0.0 Safari/537.36" version="26.1.1">
<diagram name="Концептуальная модель" id="uXQN3Tqoo_ou7SFrJxsO">
<mxGraphModel dx="954" dy="603" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="M9otMMDKzzFARfVN8Q2O-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-10" target="M9otMMDKzzFARfVN8Q2O-22" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-29" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=circle;endFill=0;endSize=3;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-10" target="M9otMMDKzzFARfVN8Q2O-16" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;startArrow=ERone;startFill=0;endArrow=ERone;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-10" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-10" value="Students" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="200" y="440" width="120" height="100" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-23" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-11" target="M9otMMDKzzFARfVN8Q2O-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-11" value="Family" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="10" y="530" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-25" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=none;" parent="1" source="M9otMMDKzzFARfVN8Q2O-13" target="M9otMMDKzzFARfVN8Q2O-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-13" value="Files" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="10" y="590" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-41" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;startFill=0;startArrow=ERmany;" parent="1" source="M9otMMDKzzFARfVN8Q2O-14" target="M9otMMDKzzFARfVN8Q2O-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-14" value="Movement" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="90" y="230" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-15" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-42" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;endArrow=none;startFill=0;startArrow=ERmany;" parent="1" source="M9otMMDKzzFARfVN8Q2O-15" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="200" y="490" as="targetPoint" />
<Array as="points">
<mxPoint x="70" y="200" />
<mxPoint x="70" y="490" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-44" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=ERone;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-15" target="M9otMMDKzzFARfVN8Q2O-17" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-15" value="Statements" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="90" y="180" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;endArrow=none;startFill=0;startArrow=ERone;" parent="1" source="M9otMMDKzzFARfVN8Q2O-16" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-16" value="Supervisors" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="200" y="360" width="120" height="40" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.75;entryY=0;entryDx=0;entryDy=0;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-17" target="M9otMMDKzzFARfVN8Q2O-19" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-17" value="Disciplines" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="550" y="150" width="120" height="100" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-18" value="Physicals" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="330" y="160" width="150" height="180" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-38" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=ERmany;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-19" target="M9otMMDKzzFARfVN8Q2O-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-19" value="Departments" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="640" y="290" width="120" height="80" as="geometry" />
</mxCell>
<mxCell id="xU2I2qLbYURmXa2A43uc-1" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=oval;startFill=0;endArrow=ERmany;endFill=0;" edge="1" parent="1" target="M9otMMDKzzFARfVN8Q2O-19">
<mxGeometry relative="1" as="geometry">
<mxPoint x="700" y="406" as="sourcePoint" />
<Array as="points">
<mxPoint x="700" y="380" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-20" value="Faculties" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="640" y="410" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;endArrow=ERone;endFill=0;endSize=6;startArrow=ERone;startFill=0;startSize=6;" parent="1" source="M9otMMDKzzFARfVN8Q2O-21" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="650" y="600.4000000000001" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-21" value="Programs" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="560" y="550" width="120" height="50" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;endArrow=ERone;endFill=0;startArrow=ERmany;startFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-22" target="M9otMMDKzzFARfVN8Q2O-21" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-36" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=ERone;endFill=0;" parent="1" source="M9otMMDKzzFARfVN8Q2O-22" target="M9otMMDKzzFARfVN8Q2O-20" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-37" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;startArrow=ERmany;startFill=0;endArrow=circle;endFill=0;endSize=3;" parent="1" source="M9otMMDKzzFARfVN8Q2O-22" target="M9otMMDKzzFARfVN8Q2O-19" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="M9otMMDKzzFARfVN8Q2O-22" value="Groups" style="rounded=1;arcSize=10;whiteSpace=wrap;html=1;align=center;" parent="1" vertex="1">
<mxGeometry x="390" y="420" width="150" height="100" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

+193
View File
@@ -0,0 +1,193 @@
Table Students {
id integer [pk, increment, not null, unique, note: 'Номер студенческого билета']
person_id integer [not null, unique, note: 'Номер физического лица']
group varchar [not null, note: 'Номер учебной группы']
supervisor integer [note: 'Номер научного руководителя']
education_form varchar [not null, default: 'Очная', note: 'Форма обучения студента']
status varchar [not null, default: 'Учится', note: 'Статус обучающегося']
}
Table Persons {
id integer [pk, increment, not null, unique]
name varchar [not null]
surname varchar [not null]
birthday timestamp [not null]
phone varchar [not null]
mail varchar [default: null]
passport_no varchar [not null]
citizenship varchar [not null]
address varchar [not null]
access_card varchar [not null]
access_level varchar [not null]
}
Table Groups {
group varchar [pk, not null, unique]
faculty_id integer [not null]
program_id integer [not null]
department_id integer [not null]
study_starts timestamp [not null]
study_ends timestamp [not null]
}
Table Family {
id integer [pk, increment, not null, unique]
student_id integer [not null]
name varchar [not null]
surname varchar [not null]
kinship varchar [not null, note: 'Степень родства: mthr, fthr, brth, sstr']
phone varchar [not null]
address varchar [not null]
}
Table Files {
id integer [pk, increment, not null, unique]
student_id integer [not null]
name varchar [not null]
description varchar [not null]
extension varchar [not null, note: 'available: png, jpeg, pdf']
size numeric [not null, note: 'size in mb']
path varchar [not null, note: 'path to file /var/www/student_files/<file_id>.<ext>']
loaded_at timestamp [not null]
}
Table Supervisors {
id integer [pk, increment, not null, unique]
person_id integer [not null]
experience integer [not null]
defended_ratio real [not null]
qualification varchar [not null]
}
Table Movement {
id integer [pk, increment, not null, unique]
student_id integer [not null]
type varchar [not null]
new_group varchar
new_status varchar
issued_at timestamp [not null]
}
Table Departments {
id integer [pk, increment, not null, unique]
name varchar [not null]
acronym varchar [not null]
founded timestamp [not null]
head integer [not null]
vice integer
secretary integer
faculty_id integer
}
Table Programs {
id integer [pk, increment, not null, unique]
specification varchar [not null, note: 'Key from Russian register']
degree varchar [not null]
name varchar [not null]
parent_id integer [not null]
}
Table Faculties {
id integer [pk, increment, not null, unique]
name varchar [not null]
acronym varchar [not null]
head integer [not null]
vice integer
address varchar [not null]
}
Table Statements {
id integer [pk, increment, not null, unique]
student_id integer [not null]
discipline_id integer [not null]
examiner_id integer [not null]
try_no integer [not null]
grade integer [not null]
conducted_at timestamp [not null]
}
Table Disciplines {
id integer [pk, increment, not null, unique]
name varchar [not null]
department_id integer [not null]
credit_units integer [not null]
academic_hours integer [not null]
general_hours integer [not null]
is_annual boolean [not null]
}
Ref "Студент к физлицу" {
Persons.id - Students.person_id [delete: no action]
}
Ref "Загружен для" {
Files.student_id > Students.id [delete: no action]
}
Ref "Руководитель к физлицу" {
Departments.head - Persons.id [delete: no action]
}
Ref "Заместитель к физлицу" {
Departments.vice - Persons.id [delete: no action]
}
Ref "Секретарь к физлицу" {
Departments.secretary - Persons.id [delete: no action]
}
Ref "К научному руководителю" {
Students.supervisor > Supervisors.id [delete: no action]
}
Ref "В учебной группе" {
Students.group > Groups.group [delete: no action]
}
Ref "План/программа группы" {
Groups.program_id > Programs.id [delete: no action]
}
Ref "Группа факультета" {
Groups.faculty_id > Faculties.id [delete: no action]
}
Ref "Имеет базовую кафедру" {
Groups.department_id > Departments.id [delete: no action]
}
Ref "Ведомость по студенту" {
Statements.student_id > Students.id [delete: no action]
}
Ref "Ведомость по дисциплине" {
Statements.discipline_id > Disciplines.id [delete: no action]
}
Ref "Экзаменатор к физлицу" {
Statements.examiner_id > Persons.id [delete: no action]
}
Ref "Дисциплина к кафедре" {
Disciplines.department_id > Departments.id [delete: no action]
}
Ref "Подразделение факультета" {
Departments.faculty_id > Faculties.id [delete: no action]
}
Ref "Приказ по студенту" {
Movement.student_id > Students.id [delete: no action]
}
Ref "Научный руководитель к физлицу" {
Supervisors.person_id - Persons.id [delete: no action]
}
Ref "Дочерний к программе" {
Programs.parent_id - Programs.id [delete: no action]
}
Ref "Родственник студента" {
Family.student_id > Students.id [delete: no action]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.