Skip to content
Snippets Groups Projects
functions.sql 69.1 KiB
Newer Older
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

PavelBegunkov's avatar
PavelBegunkov committed
DELIMITER //
DROP FUNCTION IF EXISTS SaveSession //
DROP FUNCTION IF EXISTS DeattachAllStudents//
DROP FUNCTION IF EXISTS GetGroup//
DROP FUNCTION IF EXISTS CreateDepartment //
PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------
# Label: internals
# -------------------------------------------------------------------------------------------

# actually check for first scoring, in this case you cannot yet edit discipline
# "SetRate" stored procedure can change isLocked flag
DROP FUNCTION IF EXISTS InternalIsMapLocked//
CREATE FUNCTION InternalIsMapLocked (pDisciplineID INT) RETURNS BOOLEAN
READS SQL DATA
RETURN (
    SELECT disciplines.IsLocked FROM disciplines
    WHERE disciplines.ID = pDisciplineID LIMIT 1
) <=> TRUE//
PavelBegunkov's avatar
PavelBegunkov committed


# check, that student really take this course
DROP FUNCTION IF EXISTS InternalIsStudentAttached//
CREATE FUNCTION InternalIsStudentAttached (
    pStudentID INT,
    pDisciplineID INT
) RETURNS BOOLEAN
READS SQL DATA
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vAttachType enum('attach','detach') DEFAULT NULL;
    DECLARE vSemesterID, vGroupID INT DEFAULT 0;

    SET vSemesterID = GetDisciplineProperty(pDisciplineID, 'semester');
    SELECT disciplines_students.Type INTO vAttachType
        FROM disciplines_students
        WHERE   disciplines_students.StudentID = pStudentID AND
                disciplines_students.DisciplineID = pDisciplineID
        LIMIT 1;
    IF vAttachType IS NOT NULL THEN # attached or detached
        RETURN (vAttachType <=> 'attach');
    END IF;
    SET vGroupID = GetStudentGroup(pStudentID, vSemesterID);
    RETURN EXISTS(SELECT * FROM disciplines_groups
        WHERE disciplines_groups.GroupID = vGroupID
PavelBegunkov's avatar
PavelBegunkov committed
END //


# check, that teacher teach this course
DROP FUNCTION IF EXISTS InternalIsTeacherBound//
CREATE FUNCTION InternalIsTeacherBound (
    pTeacherID INT,
    pDisciplineID INT
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS BOOLEAN
READS SQL DATA
RETURN EXISTS (
    SELECT * FROM disciplines_teachers
    WHERE disciplines_teachers.TeacherID = pTeacherID AND disciplines_teachers.DisciplineID = pDisciplineID
    LIMIT 1
)//
PavelBegunkov's avatar
PavelBegunkov committed


DROP FUNCTION IF EXISTS InternalIsTeacherAuthor//
CREATE FUNCTION InternalIsTeacherAuthor (
    pTeacherID INT,
    pDisciplineID INT
READS SQL DATA
RETURN (
    SELECT disciplines.AuthorID FROM disciplines
    WHERE disciplines.ID = pDisciplineID LIMIT 1
) <=> pTeacherID //
DROP FUNCTION IF EXISTS SetExamPeriodOption//
DROP FUNCTION IF EXISTS Discipline_SetExamPeriodOption//
CREATE FUNCTION Discipline_SetExamPeriodOption (
    pStudentID INT,
    pSubmoduleID INT,
    pType enum('absence', 'pass', 'null')
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
    DECLARE vType INT DEFAULT NULL;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    IF pType != 'null' THEN
        SET vType = pType;
    INSERT INTO exam_period_options (StudentID, SubmoduleID, Type) VALUES(pStudentID, pSubmoduleID, vType)
        ON DUPLICATE KEY UPDATE
            exam_period_options.Type = vType;
    RETURN  0;
# check, regular + exam rate == 100
PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS InternalIsMapCreated//
CREATE FUNCTION InternalIsMapCreated (pDisciplineID INT
READS SQL DATA
RETURN (
    SELECT disciplines.MaxRate FROM disciplines
    WHERE disciplines.ID = pDisciplineID LIMIT 1
) <=> 100//
# ordering helper
PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS InternalOrderModuleTypesForSession//
CREATE FUNCTION InternalOrderModuleTypesForSession (pModuleType INT) RETURNS INT(3)
DETERMINISTIC CONTAINS SQL
RETURN (CASE pModuleType
    WHEN 4 THEN 1 # extra
    WHEN 2 THEN 2 # exam
    WHEN 3 THEN 3 # bonus
    ELSE 4
END)//
PavelBegunkov's avatar
PavelBegunkov committed


# -------------------------------------------------------------------------------------------
PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------

-- todo: search by name
DROP FUNCTION IF EXISTS GetUserRole//
CREATE FUNCTION GetUserRole(
    pType enum('student', 'teacher', 'admin', 'dean')
DETERMINISTIC CONTAINS SQL
RETURN (CASE pType
    WHEN 'dean' THEN 4
    WHEN 'teacher' THEN 2
    WHEN 'admin' THEN 3
    ELSE 1
END)//
PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------
# Label: faculties
# -------------------------------------------------------------------------------------------

DROP FUNCTION IF EXISTS CreateFaculty //
CREATE FUNCTION CreateFaculty (
    pFacultyName TEXT CHARSET utf8,
    pFacultyAbbr TEXT CHARSET utf8
) RETURNS INT(11) # -1 or id
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    INSERT INTO faculties (Name, Abbr) VALUES (pFacultyName, pFacultyAbbr);
    RETURN LAST_INSERT_ID();
PavelBegunkov's avatar
PavelBegunkov committed

# -------------------------------------------------------------------------------------------
# Label: departments
# -------------------------------------------------------------------------------------------

# create department or return existing
DROP FUNCTION IF EXISTS Department_Create //
CREATE FUNCTION Department_Create (
    pName VARCHAR(200) CHARSET utf8,
    pFacultyID INT(11)
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS INT(11) # department id or -1 if failed
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;
PavelBegunkov's avatar
PavelBegunkov committed

    INSERT INTO departments (Name, FacultyID) VALUES (pName, pFacultyID)
        ON DUPLICATE KEY UPDATE
            departments.ID = LAST_INSERT_ID(departments.ID);
    RETURN LAST_INSERT_ID();
END //

DROP FUNCTION IF EXISTS Department_Search //
CREATE FUNCTION Department_Search (
    pName VARCHAR(200) CHARSET utf8,
    pFacultyID INT(11)
) RETURNS INT(11) # department id of -1 if not found
READS SQL DATA
RETURN COALESCE((
    SELECT departments.ID FROM departments
    WHERE   departments.Name = pName AND departments.FacultyID = pFacultyID LIMIT 1
), -1)//
PavelBegunkov's avatar
PavelBegunkov committed

PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------
# Label: grades
PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------

DROP FUNCTION IF EXISTS CreateGrade//
CREATE FUNCTION CreateGrade (
    pGradeNum INT,
    pDegree enum('bachelor', 'master', 'specialist')
) RETURNS int(11) # groupID or -1 if failed
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;
    IF pDegree IS NULL OR NOT (pDegree BETWEEN 'bachelor' AND 'specialist') THEN
        RETURN -1;
    END IF;
    INSERT INTO grades (Num, Degree) VALUES (pGradeNum, pDegree)
        ON DUPLICATE KEY UPDATE
            grades.ID = LAST_INSERT_ID(grades.ID);
    RETURN LAST_INSERT_ID();
END //
PavelBegunkov's avatar
PavelBegunkov committed

# -------------------------------------------------------------------------------------------
# Label: groups
PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------

# negative int, if already exists
DROP FUNCTION IF EXISTS CreateGroup//
CREATE FUNCTION CreateGroup (
    pGradeID INT,
    pGroupNum INT,
    pSpecializationID INT,
    pGroupName VARCHAR(50) CHARSET utf8,
    pYear INT
) RETURNS int(11) # group id
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vGroupID, vFacultyID, vGroupYear, vIsSpecMatch INT DEFAULT -1;
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    SET vFacultyID = (SELECT FacultyID FROM specializations WHERE specializations.ID = pSpecializationID LIMIT 1);

PavelBegunkov's avatar
PavelBegunkov committed
    # create discipline
    INSERT INTO study_groups (GradeID, GroupNum, FacultyID) VALUES (pGradeID, pGroupNum, vFacultyID)
        ON DUPLICATE KEY UPDATE
            study_groups.ID = LAST_INSERT_ID(study_groups.ID);
    SELECT groups_years.GroupID, (groups_years.SpecializationID = pSpecializationID AND groups_years.Name <=> pGroupName)
        INTO vGroupYear, vIsSpecMatch
        FROM groups_years
        WHERE groups_years.GroupID = vGroupID AND groups_years.Year = pYear
        LIMIT 1;
    IF vGroupYear = -1 THEN
        INSERT INTO groups_years (GroupID, Year, SpecializationID, Name)
        VALUES (vGroupID, pYear, pSpecializationID, pGroupName);
    ELSEIF NOT vIsSpecMatch THEN
        RETURN -1;
    END IF;

    RETURN vGroupID;
END //

DROP FUNCTION IF EXISTS FindGroup//
CREATE FUNCTION FindGroup (
    pGradeID INT,
    pGroupNum INT,
    pFacultyID INT
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11) # groupID or -1 if failed
READS SQL DATA
RETURN COALESCE((
    SELECT study_groups.ID FROM study_groups
    WHERE   study_groups.GradeID = pGradeID
            AND study_groups.GroupNum = pGroupNum
DROP FUNCTION IF EXISTS GetStudentGroup//
CREATE FUNCTION GetStudentGroup (
    pStudentID INT,
    pSemesterID INT
) RETURNS int(11) # groupID or -1 if failed
READS SQL DATA
RETURN COALESCE((
    SELECT students_groups.GroupID FROM students_groups
        WHERE   students_groups.StudentID = pStudentID
                AND students_groups.SemesterID = pSemesterID
                AND students_groups.State != 'expulsion'
        LIMIT 1
), -1)//
PavelBegunkov's avatar
PavelBegunkov committed
# -------------------------------------------------------------------------------------------
# Label: subjects
# -------------------------------------------------------------------------------------------

DROP FUNCTION IF EXISTS CreateSubject//
CREATE FUNCTION CreateSubject (
    pFacultyID INT,
    pSubjectName TEXT CHARSET utf8,
    pSubjectAbbr VARCHAR(20) CHARSET utf8
)   RETURNS int(11) -- subjectID or negative value
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vSubjectID INT DEFAULT -1;
xamgore's avatar
xamgore committed
    IF pSubjectName = '' THEN RETURN -1; END IF;
    # create/get subject (subject name is unique key)
    INSERT INTO subjects (Name, Abbr) VALUES (pSubjectName, pSubjectAbbr)
        ON DUPLICATE KEY UPDATE
            subjects.ID = LAST_INSERT_ID(subjects.ID);
    SET vSubjectID = LAST_INSERT_ID();
PavelBegunkov's avatar
PavelBegunkov committed

    BEGIN # handler block
        DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;
        INSERT INTO subjects_faculties (SubjectID, FacultyID) VALUES (vSubjectID, pFacultyID)
PavelBegunkov's avatar
PavelBegunkov committed
            ON DUPLICATE KEY UPDATE # just stub
                subjects_faculties.ID = LAST_INSERT_ID(subjects_faculties.ID);
PavelBegunkov's avatar
PavelBegunkov committed
    END;
PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS DeleteSubject //
CREATE FUNCTION DeleteSubject (pSubjectID INT) RETURNS INT(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE vSubjectUsage BOOLEAN DEFAULT FALSE;
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

PavelBegunkov's avatar
PavelBegunkov committed
    SET vSubjectUsage = EXISTS(
        SELECT * FROM disciplines
        WHERE disciplines.SubjectID = pSubjectID LIMIT 1
PavelBegunkov's avatar
PavelBegunkov committed
    IF vSubjectUsage THEN
PavelBegunkov's avatar
PavelBegunkov committed
        RETURN -1; # Удаляемый предмет используется в disciplines.
    END IF;

    DELETE FROM subjects_faculties
PavelBegunkov's avatar
PavelBegunkov committed
        WHERE subjects_faculties.SubjectID = pSubjectID;
PavelBegunkov's avatar
PavelBegunkov committed
        WHERE subjects.ID = pSubjectID
        LIMIT 1;

    RETURN 0; # Успешно удалено;
END //

PavelBegunkov's avatar
PavelBegunkov committed

# -------------------------------------------------------------------------------------------
# Label: accounts
# -------------------------------------------------------------------------------------------

DROP FUNCTION IF EXISTS CheckAccountExistence//
CREATE FUNCTION CheckAccountExistence (
    pData TEXT CHARSET utf8,
    pType enum('login','email', 'code')
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS BOOLEAN # TRUE - exist, FALSE - doesn't
READS SQL DATA
RETURN EXISTS(
    SELECT * FROM accounts
        WHERE CASE pType
            WHEN 'login' THEN pData = accounts.Login
            WHEN 'email' THEN pData = accounts.EMail
            WHEN 'code' THEN pData = accounts.ActivationCode
            ELSE FALSE
        END
    LIMIT 1
)//


DROP FUNCTION IF EXISTS CreateAccount//
CREATE FUNCTION CreateAccount (
    pCode varchar(40) CHARSET utf8,
    pUserRoleID int
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    INSERT INTO accounts (Login , Password , EMail, UserRoleID, ActivationCode)
        VALUES  ( NULL, NULL, NULL, pUserRoleID, pCode);
    RETURN LAST_INSERT_ID();
END//
# return:
#   -1 - unknown error.
#   -2 - code doesn't exists.
#   -3 - email already registered.
#   -4 - login already registered.
PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS ActivateAccount//
CREATE FUNCTION ActivateAccount (
    pCode VARCHAR(40) CHARSET utf8,
    pLogin VARCHAR(50) CHARSET utf8,
    pEMail VARCHAR(50) CHARSET utf8,
    pPassword VARCHAR(255) CHARSET utf8
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    # check for matching with existing accounts (note: Login & E-Mail are unique)
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    IF CheckAccountExistence(pEMail, 'email') THEN
        RETURN -3;
    END IF;
    IF CheckAccountExistence(pLogin, 'login') THEN
        RETURN -4;
    END IF;

PavelBegunkov's avatar
PavelBegunkov committed
    # activate account
PavelBegunkov's avatar
PavelBegunkov committed
        SET accounts.Login = pLogin,
            accounts.Password = SHA1(pPassword),
PavelBegunkov's avatar
PavelBegunkov committed
            accounts.EMail = pEMail,
            accounts.ActivationCode = NULL
        WHERE accounts.ActivationCode = pCode AND ( @vAccountID := accounts.ID ) # save accountID
PavelBegunkov's avatar
PavelBegunkov committed
        LIMIT 1;

    RETURN IF(ROW_COUNT() = 0, -2, @vAccountID); # account with this Code not found
DROP FUNCTION IF EXISTS ChangeAccountData//
CREATE FUNCTION ChangeAccountData (
    pUserID INT,
    pData TEXT CHARSET utf8,
    pType enum('login', 'email', 'password')
) RETURNS int(11) # 1 - success, 0 - failed
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN 0;
    CASE pType
        WHEN 'login' THEN
                SET accounts.Login = pData
                WHERE accounts.ID = pUserID
                LIMIT 1;
        WHEN 'email' THEN
                SET accounts.EMail = pData
                WHERE accounts.ID = pUserID
                LIMIT 1;
        WHEN 'password' THEN
                SET accounts.Password = SHA1(pData)
                WHERE accounts.ID = pUserID
                LIMIT 1;
    END CASE;
PavelBegunkov's avatar
PavelBegunkov committed
END //


# -------------------------------------------------------------------------------------------
# Label: teachers
# -------------------------------------------------------------------------------------------

DROP FUNCTION IF EXISTS ChangeTeacherInfo//
CREATE FUNCTION ChangeTeacherInfo (
    pTeacherID INT,
    pLastName VARCHAR(30) CHARSET utf8,
    pFirstName VARCHAR(30) CHARSET utf8,
    pSecondName VARCHAR(30) CHARSET utf8,
    pJobPositionID INT,
    pDepartmentID INT
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11) # -1 if teacher doesn't exists, otherwise 0
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    # set new info
PavelBegunkov's avatar
PavelBegunkov committed
        SET teachers.LastName = pLastName,
            teachers.FirstName = pFirstName,
            teachers.SecondName = pSecondName,
            teachers.JobPositionID = pJobPositionID,
            teachers.DepartmentID = pDepartmentID
        WHERE teachers.ID = pTeacherID
        LIMIT 1;
    RETURN ROW_COUNT()-1;
DROP FUNCTION IF EXISTS Teacher_Create//
CREATE FUNCTION Teacher_Create (
    pLastName VARCHAR(30) CHARSET utf8,
    pFirstName VARCHAR(30) CHARSET utf8,
    pSecondName VARCHAR(30) CHARSET utf8,
    pJobPositionID INT,
    pDepartmentID  INT,
    pActivationCode VARCHAR(40) CHARSET utf8
) RETURNS int(11) # 0 - success, <0 failed
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vAccountID INT DEFAULT -1;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    SET vAccountID = CreateAccount(pActivationCode, GetUserRole('teacher'));
    # add new teacher
    INSERT INTO teachers (AccountID, LastName, FirstName, SecondName, JobPositionID, DepartmentID) VALUES
        (vAccountID, pLastName, pFirstName, pSecondName, pJobPositionID, pDepartmentID);
    RETURN LAST_INSERT_ID();
PavelBegunkov's avatar
PavelBegunkov committed
END //

PavelBegunkov's avatar
PavelBegunkov committed
-- -1 - не сотрудник деканата и не преподаватель дисциплины
-- 0 - только чтение
-- 1 - редактирование
DROP FUNCTION IF EXISTS GetEditRightsForTeacher//
CREATE FUNCTION GetEditRightsForTeacher (
    pTeacherID INT,
    pDisciplineID INT
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11)
READS SQL DATA
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vUserRole INT DEFAULT -1;
    IF InternalIsTeacherBound(pTeacherID, pDisciplineID) > 0 THEN
PavelBegunkov's avatar
PavelBegunkov committed
        RETURN 1;
    END IF;

    SELECT accounts.UserRoleID INTO vUserRole
        FROM teachers
        INNER JOIN accounts ON teachers.AccountID = accounts.ID
        WHERE teachers.ID = pTeacherID
        LIMIT 1;
    RETURN (vUserRole = GetUserRole('dean'), 0, -1);
PavelBegunkov's avatar
PavelBegunkov committed

# -------------------------------------------------------------------------------------------
# Label: students
# -------------------------------------------------------------------------------------------

PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS CreateStudent//
CREATE FUNCTION CreateStudent (
    pLastName VARCHAR(30) CHARSET utf8,
    pFirstName VARCHAR(30) CHARSET utf8,
    pSecondName VARCHAR(30) CHARSET utf8,
    pGroupID INT,
    pActivationCode VARCHAR(40) CHARSET utf8,
    pSemesterID INT
PavelBegunkov's avatar
PavelBegunkov committed
)   RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vAccountID, vStudentID INT DEFAULT -1;
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    SET vAccountID = CreateAccount(pActivationCode, GetUserRole('student'));
PavelBegunkov's avatar
PavelBegunkov committed

    # create student
    INSERT INTO students (AccountID, LastName, FirstName, SecondName)
        VALUES  (vAccountID, pLastName, pFirstName, pSecondName);
    SET vStudentID = LAST_INSERT_ID();
    RETURN ControlStudentGroup(vStudentID, pGroupID, 'common', pSemesterID);
END //

DROP FUNCTION IF EXISTS CreateStudentGroupSearch//
CREATE FUNCTION CreateStudentGroupSearch (
    pLastName VARCHAR(30) CHARSET utf8,
    pFirstName VARCHAR(30) CHARSET utf8,
    pSecondName VARCHAR(30) CHARSET utf8,
    pGradeID INT,
    pGroupNum INT,
    pFacultyID INT,
    pActivationCode VARCHAR(40) CHARSET utf8,
    pSemesterID INT
)   RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE vGroupID INT DEFAULT -1;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    SET vGroupID = FindGroup(pGradeID, pGroupNum, pFacultyID);
    IF vGroupID <= 0 THEN
        RETURN -1;
    END IF;
    RETURN CreateStudent(pLastName, pFirstName, pSecondName, vGroupID, pActivationCode, pSemesterID);
# unlike fn CreateStudent, this can create all missing records (group, grade, specialization)
PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS CreateStudentEx//
CREATE FUNCTION CreateStudentEx (
    pLastName VARCHAR(30) CHARSET utf8,
    pFirstName VARCHAR(30) CHARSET utf8,
    pSecondName VARCHAR(30) CHARSET utf8,
    pGradeNum INT,
    pGroupNum INT,
    pDegree enum('bachelor', 'master', 'specialist'),
    pSpecName VARCHAR(50) CHARSET utf8,
    pFacultyID INT,
    pActivationCode VARCHAR(40) CHARSET utf8,
    pSemesterID INT
PavelBegunkov's avatar
PavelBegunkov committed
)   RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vAccountID, vGradeID, vSpecID, vGroupID, vYear INT DEFAULT -1;
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    # get specialization id
    INSERT INTO specializations (Name, Abbr, FacultyID) VALUES  (pSpecName, NULL, pFacultyID)
        ON DUPLICATE KEY UPDATE
            specializations.ID = LAST_INSERT_ID(specializations.ID);
    SET vSpecID = LAST_INSERT_ID();
    SET vYear = COALESCE((SELECT Year FROM semesters WHERE semesters.ID = pSemesterID LIMIT 1), -1);
    SET vGradeID = CreateGrade(pGradeNum, pDegree);
    SET vGroupID = CreateGroup(vGradeID, pGroupNum, vSpecID, NULL, vYear);
    RETURN CreateStudent(pLastName, pFirstName, pSecondName, vGroupID, pActivationCode, pSemesterID);
xamgore's avatar
xamgore committed
# Give a student an academic leave or attach him to group.
#   params:
#      StudentID (int)
#      GroupID (int)   : -1, to update all appropriate 'common' records
#      State (enum)
#      SemesterID (int)
DROP FUNCTION IF EXISTS ControlStudentGroup//
CREATE FUNCTION ControlStudentGroup (
    pStudentID INT,
    pGroupID INT,
    pState enum('common', 'outlet', 'expulsion', 'leave'),
    pSemesterID INT
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE vChecker INT DEFAULT 0;
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    INSERT INTO students_groups (StudentID, GroupID, SemesterID, State)
        VALUES (pStudentID, pGroupID, pSemesterID, pState)
        ON DUPLICATE KEY UPDATE
            students_groups.GroupID = pGroupID,
            students_groups.State = pState,
            students_groups.Date = CURDATE();

    RETURN ROW_COUNT()-1;
END //

DROP FUNCTION IF EXISTS RemoveFromGroupInSemester//
CREATE FUNCTION RemoveFromGroupInSemester (
    pStudentID INT,
    pGroupID INT,
    pSemesterID INT
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    DELETE FROM students_groups
        WHERE   students_groups.GroupID = pGroupID
                and students_groups.StudentID = pStudentID
                and students_groups.SemesterID = pSemesterID
                and students_groups.State = 'common'
        LIMIT 1;
    RETURN ROW_COUNT()-1;
END //

PavelBegunkov's avatar
PavelBegunkov committed

# -------------------------------------------------------------------------------------------
# Label: disciplines
# -------------------------------------------------------------------------------------------

PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS GetDisciplineProperty//
CREATE FUNCTION GetDisciplineProperty (
    pDisciplineID INT,
    pType enum('grade', 'subject', 'author', 'semester', 'milestone', 'type', 'maxrate')
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11)
READS SQL DATA
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vRes INT DEFAULT -1;

    SELECT CASE pType
            WHEN 'semester' THEN disciplines.SemesterID
            WHEN 'author' THEN disciplines.AuthorID
            WHEN 'grade' THEN disciplines.GradeID
            WHEN 'subject' THEN disciplines.SubjectID
            WHEN 'milestone' THEN disciplines.Milestone
            WHEN 'type' THEN disciplines.ExamType + 0
            WHEN 'maxrate' THEN disciplines.MaxRate
PavelBegunkov's avatar
PavelBegunkov committed
        END INTO vRes
PavelBegunkov's avatar
PavelBegunkov committed
        WHERE disciplines.ID = pDisciplineID
        LIMIT 1;

    RETURN vRes;
END //

DROP FUNCTION IF EXISTS Discipline_Create//
CREATE FUNCTION Discipline_Create (
    pTeacherID INT,
    pGradeID INT,
    pSubjectID INT,
    pExamType enum('exam', 'credit', 'grading_credit'),
    pLectureCount INT,
    pPracticeCount INT,
    pLabCount INT,
    pFacultyID INT,
    pSemesterID INT,
    pSubtype enum('scientific_coursework', 'disciplinary_coursework')
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vDisciplineID INT DEFAULT -1;
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    # todo: make more flexible creation of coursework
    # I mean, while creating scientific coursework
    #    we don't have the SubjectID field, but we must.
    #    346 is used as a default id for scientific courseworks.
    #    This constant is duplicated in Model_Helper_CourseWorkBuilder

    IF pSubtype IS NOT NULL THEN
        SET pSubjectID = 346;
    END IF;

    # create discipline
    INSERT INTO disciplines
        ( AuthorID, GradeID, SubjectID, ExamType, LectureCount, PracticeCount,LabCount, SemesterID, FacultyID, Subtype) VALUES
        (pTeacherID, pGradeID, pSubjectID, pExamType, pLectureCount, pPracticeCount, pLabCount, pSemesterID, pFacultyID, pSubtype);
PavelBegunkov's avatar
PavelBegunkov committed
    SET vDisciplineID = LAST_INSERT_ID();

    SELECT Discipline_BindTeacher(vDisciplineID, pTeacherID) INTO @vTemp;
    # add exam and extra modules
PavelBegunkov's avatar
PavelBegunkov committed
    IF pExamType = 'exam' THEN
        SELECT AddModuleExamUnsafe(vDisciplineID) INTO @vTemp;
PavelBegunkov's avatar
PavelBegunkov committed
    END IF;
    SELECT AddModuleExtra(pTeacherID, vDisciplineID) INTO @vTemp;
PavelBegunkov's avatar
PavelBegunkov committed
    RETURN vDisciplineID;
END //




DROP FUNCTION IF EXISTS ChangeDisciplineSubjectUnsafe//
CREATE FUNCTION ChangeDisciplineSubjectUnsafe (
    pDisciplineID INT,
    pSubjectID INT
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    UPDATE disciplines
        SET disciplines.SubjectID = pSubjectID
        WHERE disciplines.ID = pDisciplineID
        LIMIT 1;
    RETURN ROW_COUNT()-1;
END //


DROP FUNCTION IF EXISTS ChangeDisciplineSubject//
CREATE FUNCTION ChangeDisciplineSubject (
    pTeacherID INT,
    pDisciplineID INT,
    pSubjectID INT
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
    # TODO: move to php
    IF  NOT InternalIsTeacherAuthor(pTeacherID, pDisciplineID) OR
PavelBegunkov's avatar
PavelBegunkov committed
        InternalIsMapLocked(pDisciplineID)
    THEN
        RETURN -1;
    END IF;
    RETURN ChangeDisciplineSubjectUnsafe(pDisciplineID, pSubjectID);
PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS GetMilestone//
CREATE FUNCTION GetMilestone (
    pFacultyID INT,
    pSemesterID INT
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11)
READS SQL DATA
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vMilestone, vCounter INT DEFAULT 0;

    # get most frequent milestone
    SELECT COUNT(*) AS 'cnt', disciplines.Milestone INTO vCounter, vMilestone
        FROM disciplines
        WHERE disciplines.SemesterID = pSemesterID AND disciplines.FacultyID = pFacultyID
PavelBegunkov's avatar
PavelBegunkov committed
        GROUP BY disciplines.Milestone
        ORDER BY cnt DESC
        LIMIT 1;

    RETURN vMilestone;
END //

-- remove all students from discipline (separate bound & in general groups)
DROP FUNCTION IF EXISTS DetachAllStudents//
CREATE FUNCTION DetachAllStudents (pDisciplineID INT) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DELETE FROM disciplines_groups
        WHERE disciplines_groups.DisciplineID = pDisciplineID;
    DELETE FROM disciplines_students
        WHERE disciplines_students.DisciplineID = pDisciplineID;
    RETURN 0;
END //
PavelBegunkov's avatar
PavelBegunkov committed

DROP FUNCTION IF EXISTS ChangeDisciplineGradeUnsafe//
CREATE FUNCTION ChangeDisciplineGradeUnsafe (
    pDisciplineID INT,
    pGradeID INT
PavelBegunkov's avatar
PavelBegunkov committed
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

PavelBegunkov's avatar
PavelBegunkov committed
    # TODO: move to php
    IF  InternalIsMapLocked(pDisciplineID) THEN
PavelBegunkov's avatar
PavelBegunkov committed
        RETURN -1;
    END IF;
    IF GetDisciplineProperty(pDisciplineID, 'grade') <=> pGradeID THEN
PavelBegunkov's avatar
PavelBegunkov committed
        RETURN 0;
PavelBegunkov's avatar
PavelBegunkov committed
    END IF;

    SELECT DetachAllStudents(pDisciplineID) INTO @vRes;
PavelBegunkov's avatar
PavelBegunkov committed
    # set grade
PavelBegunkov's avatar
PavelBegunkov committed
        SET disciplines.GradeID = pGradeID
        WHERE disciplines.ID = pDisciplineID
        LIMIT 1;
    RETURN ROW_COUNT()-1;
END //

DROP FUNCTION IF EXISTS ChangeDisciplineGrade//
CREATE FUNCTION ChangeDisciplineGrade (
    pTeacherID INT,
    pDisciplineID INT,
    pGradeID INT
) RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    # TODO: move to php
    IF  NOT InternalIsTeacherAuthor(pTeacherID, pDisciplineID) THEN
        RETURN -1;
    END IF;
    RETURN ChangeDisciplineGradeUnsafe(pDisciplineID, pGradeID);
END //

PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS ChangeDisciplineControl//
CREATE FUNCTION ChangeDisciplineControl (
    pTeacherID INT,
    pDisciplineID INT,
    pExamType enum('exam', 'credit', 'grading_credit')
PavelBegunkov's avatar
PavelBegunkov committed
)   RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    DECLARE vChecker, vExtraMax, vExtraID INT DEFAULT -1;
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE vOldExamType enum('exam', 'credit', 'grading_credit');
PavelBegunkov's avatar
PavelBegunkov committed
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;

    # todo: move to php
    IF  InternalIsMapLocked(pDisciplineID) OR NOT InternalIsTeacherAuthor(pTeacherID, pDisciplineID) THEN
PavelBegunkov's avatar
PavelBegunkov committed
        RETURN -1;
    END IF;

    # get exam type and extra module ID
    SELECT disciplines.ExamType, modules.ID INTO vOldExamType, vExtraID
        FROM modules
        INNER JOIN disciplines ON disciplines.ID = modules.DisciplineID
        WHERE   modules.DisciplineID = pDisciplineID AND modules.Type = 'extra'
PavelBegunkov's avatar
PavelBegunkov committed
        LIMIT 1;
    IF vExtraID <= 0 THEN
        RETURN -1;
    END IF;
PavelBegunkov's avatar
PavelBegunkov committed
    # check type changing: exam <-> credit/grading_credit
    IF NOT (vOldExamType = 'exam' XOR pExamType != 'exam') THEN

        # TODO: extract method addExtraModule
        IF pExamType = 'exam' THEN # change to exam
            # count discipline's current max rate
            SET vChecker = GetDisciplineProperty(pDisciplineID, 'maxrate');
PavelBegunkov's avatar
PavelBegunkov committed
            IF vChecker >= 61 THEN # can't add exam module ( > 100 points)
                RETURN 1;
            END IF;
            SET vChecker = AddModuleExamUnsafe(pDisciplineID);
PavelBegunkov's avatar
PavelBegunkov committed

            # delete extra submodules(only 1 extra for exam)
PavelBegunkov's avatar
PavelBegunkov committed
                WHERE submodules.OrderNum > 1 AND submodules.ModuleID = vExtraID;
PavelBegunkov's avatar
PavelBegunkov committed

            SET vExtraMax = 7;
PavelBegunkov's avatar
PavelBegunkov committed
        ELSE # change to credit
            SET vExtraMax = 29;
            SET vChecker = DeleteModuleExam(pTeacherID, pDisciplineID);
            # 2 extra submodules (1 already created for exam)
            SET vChecker = AddSubmodule(pTeacherID, vExtraID, vExtraMax, '', NULL, 'LandmarkControl');
        END IF;
    END IF;

    # set new exam type
        SET disciplines.ExamType = pExamType
        WHERE disciplines.ID = pDisciplineID
        LIMIT 1;

    # set max rate for extra
PavelBegunkov's avatar
PavelBegunkov committed
        SET submodules.MaxRate = vExtraMax
        WHERE submodules.ModuleID = vExtraID;
    RETURN 0;
END //

DROP FUNCTION IF EXISTS ChangeDisciplineHours//
CREATE FUNCTION ChangeDisciplineHours (
    pTeacherID INT,
    pDisciplineID INT,
    pHours INT,
    pType INT
PavelBegunkov's avatar
PavelBegunkov committed
    # Type: 0 - Practice Hours, 1 - Lecture Hours, 2 - Lab Hours
)   RETURNS int(11)
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
PavelBegunkov's avatar
PavelBegunkov committed
BEGIN
    IF NOT InternalIsTeacherAuthor(pTeacherID, pDisciplineID) THEN
        RETURN -1;
    END IF;

    CASE pType
    WHEN 0 THEN
PavelBegunkov's avatar
PavelBegunkov committed
            SET disciplines.PracticeCount = pHours
            WHERE disciplines.ID = pDisciplineID
            LIMIT 1;
    WHEN 1 THEN
PavelBegunkov's avatar
PavelBegunkov committed
            SET disciplines.LectureCount = pHours
            WHERE disciplines.ID = pDisciplineID
            LIMIT 1;
    WHEN 2 THEN
PavelBegunkov's avatar
PavelBegunkov committed
            SET disciplines.LabCount = pHours
            WHERE disciplines.ID = pDisciplineID
            LIMIT 1;
    END CASE;
    RETURN ROW_COUNT()-1;
END //

DROP FUNCTION IF EXISTS LogBind//
CREATE FUNCTION LogBind (
    pDisciplineID INT,
    pTeacherID INT,
    EntityID INT,
    pAttach enum('attach', 'detach'),
    pType enum('group', 'student')
) RETURNS int(11) # 0 - success, < 0 - failed
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1;
    IF pType = 'group' THEN
        INSERT INTO logs_binds_groups
            (DisciplineID, TeacherID, GroupID, Type)
            VALUES (pDisciplineID, pTeacherID, EntityID, pAttach);
    ELSEIF pType = 'student' THEN
        INSERT INTO logs_binds_students
            (DisciplineID, TeacherID, StudentID, Type)
            VALUES (pDisciplineID, pTeacherID, EntityID, pAttach);
    END IF;
    RETURN ROW_COUNT()-1;
END //


DROP FUNCTION IF EXISTS RemoveStudentsAttach//
CREATE FUNCTION RemoveStudentsAttach (pDisciplineID INT, pGroupID INT) RETURNS INT
PavelBegunkov's avatar
PavelBegunkov committed
NO SQL
BEGIN
    DECLARE vSemesterID INT DEFAULT GetDisciplineProperty(pDisciplineID, 'semester');

    DELETE FROM disciplines_students
        WHERE   disciplines_students.DisciplineID = pDisciplineID AND
                disciplines_students.StudentID IN (
                    SELECT students_groups.StudentID FROM students_groups
                    WHERE   students_groups.GroupID = pGroupID AND students_groups.SemesterID = vSemesterID
                );
        RETURN 0;
END //

PavelBegunkov's avatar
PavelBegunkov committed
DROP FUNCTION IF EXISTS BindGroup//
CREATE FUNCTION BindGroup (
    pTeacherID INT,
    pDisciplineID INT,