Skip to content
Snippets Groups Projects
rating.js 14.9 KiB
Newer Older
const singleNumRegexp = /\w_(\d+)/;
const doubleNumReg = /.*_(\d+)_(\d+)$/;

class Base {
    static parseid($cell) {
        singleNumRegexp.lastIndex = 0;
        let found = singleNumRegexp.exec($cell.attr('id'));
        return found ? parseInt(found[1]) : 0;
    }

    static parseClass($cell) {
        singleNumRegexp.lastIndex = 0;
        let found = singleNumRegexp.exec($cell.attr('class'));
        return found ? parseInt(found[1]) : 0;
    }

    static parsePosition($cell) {
        doubleNumReg.lastIndex = 0;
        let [col, row] = doubleNumReg.exec($cell.attr('id')).splice(1, 2);
        return [ parseInt(col), parseInt(row) ];
    }

    static parseSettings() {
        let $discipline = $('#json_discipline');
        settings.discipline = {};
        let disciplineRaw = $.parseJSON($discipline.html() || '{}');
        Object.keys(disciplineRaw).forEach((keyRaw) => {
            let key = keyRaw.slice(0, 1).toLowerCase() + keyRaw.slice(1);
            if (keyRaw == "ID") key = 'id';

            let value = disciplineRaw[keyRaw];

            settings.discipline[key] = value;
        });

        $discipline.remove();

    static parseRate($cell, server) {
        let def = server ? '-1' : '0';
        if ($cell.hasClass('static')) {
            return parseInt($cell.text() || def);
            return parseInt($cell.children('input').val() || def);

    static filterNumbers($input) {
        let scoreInputVal = $input.val();
        let numbers = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
        scoreInputVal = Array.prototype.filter.call(scoreInputVal, chr => chr in numbers).join('');
        $input.val(scoreInputVal);
    }
}

class Cursor {
    constructor() {
        this.col = 0;
        this.row = 0;
    }

    update(col, row) {
        this.col = col;
        this.row = row;
    }

    get() {
        return [this.col, this.row];
    }

    parseLocation($cell) {
        let [col, row] = Base.parsePosition($cell);
        this.update(col, row);
    }

    isEmpty() {
        return (this.col == 0) && (this.row == 0);
    }
}

class Cell {
    constructor() {
        this.update(0, 0, 0);
    }

    update(student, submodule, maxRate) {
        this.student = student;
        this.submodule = submodule;
        this.maxRate = maxRate;
    }
}

class CellInfo {
    constructor(cursor, cell) {
        this.cursor = cursor;
        this.cell = cell;

        this.$modulesHead = $('#modulesHead');
        this.$submodulesHead = $('#submodulesHead');
        this.$submodulesMaxRate = $('#submodulesHeadMaxRate');
    }

    select($cell) {
        this.cursor.parseLocation($cell);

        let [col] = this.cursor.get();

        let $curModuleHead = this.$modulesHead.children(`.col_${col}`);
        let $curSubmoduleHead = this.$submodulesHead.children(`.col_${col}`);
        let $curSubmoduleMaxRate = this.$submodulesMaxRate.children(`.col_${col}`);

        let student = Base.parseid($cell.siblings('.name'));
        let submodule = Base.parseid($curSubmoduleMaxRate);
        let maxRate = parseInt($curSubmoduleMaxRate.text());
        if ($cell.hasClass('extra')) {
            maxRate -= Base.parseRate($cell.siblings('.semester'));
            maxRate -= Base.parseRate($cell.prev('.extra'));
            maxRate = Math.max(0, maxRate);
        }
        this.cell.update(student, submodule, maxRate);
    }

    reset() {
        this.cell.update(0, 0, 0);
    }
}

class RateInfo {
    constructor(cursor, cell) {
        this.cursor = cursor;
        this.cell = cell;

        this.$modulesHead = $('#modulesHead');
        this.$submodulesHead = $('#submodulesHead');
        this.$submodulesMaxRate = $('#submodulesHeadMaxRate');

        this.$cellInfo = $('#cellInfo');
        this.$studentInfo = this.$cellInfo.find('#student .value');
        this.$submoduleInfo = this.$cellInfo.find('#submodule .value');
        this.$rateInfo = this.$cellInfo.find('#maxRate .value');
    }

        let [col] = this.cursor.get();

        let studentName = $cell.siblings('.name').text();
        let submoduleTitle = this.$submodulesHead.children(`.col_${col}`).text();

        this.$studentInfo.html(studentName);
        this.$submoduleInfo.html(submoduleTitle);
        this.$rateInfo.html(this.cell.maxRate);
        this.$cellInfo.show();
    }

    hide() {
        this.$cellInfo.hide();
    }
}

class GroupFilter {
    constructor(settings, $table, $selector) {
        this.settings = settings;
        this.$table = $table.children('tbody');

        let groupFilter = JSON.parse(localStorage.groupFilter || "{}");
        let groupid = groupFilter[this.settings.discipline.id] || 0;
        $selector.children('[value=' + groupid + ']').prop('selected', true);
        this.filterGroups(groupid);
    filterGroups(groupid) {
        let $trs = this.$table.children();
        $trs.show();

        if (groupid) {
            $trs.not(`.group_${groupid}`).hide();
        }
    }

    listen($selector) {
        $selector.change(() => {
            let groupid = parseInt($selector.val());

            let groupFilter = JSON.parse(localStorage.groupFilter || "{}");
            groupFilter[this.settings.discipline.id] = groupid;
            localStorage.groupFilter = JSON.stringify(groupFilter);

            this.filterGroups(groupid);
        });
    }
}

class Rating {
    constructor() {
        this.settings = Base.parseSettings();
        this.cell = new Cell();
        this.cursor = new Cursor();
        this.cellInfo = new CellInfo(this.cursor, this.cell);
        this.rateInfo = new RateInfo(this.cursor, this.cell);
        this.filter = new GroupFilter(this.settings, $('#studentsRate'), $('#groupSelector'));

        this.Direction = {
            Up: 0,
            Right: 1,
            Down: 2,
            Left: 3
        };

        let keyDirs = {
            13: this.Direction.Down,  // enter
            40: this.Direction.Down,  // down arrow
            38: this.Direction.Up,    // up arrow
            39: this.Direction.Right, // right arrow
            37: this.Direction.Left   // left arrow
        };

        let self = this;
        let oldInputVal = '';
        $('#studentsRate tbody').on('focusin', '.rate', function () {
            $(this).children('input').select();

            self.cellInfo.select($(this));
            self.rateInfo.show($(this));
            $(this).parents('tr').addClass('focus');

            oldInputVal = $(this).children('input').val();
        }).on('focusout', '.rate', function () {
            self.rateInfo.hide($(this));
            self.cellInfo.reset();
            $(this).parents('tr').removeClass('focus');
        }).on('keydown', '.rate', function (event) {
            // Разрешаем нажимать только цифры
            keyDownOnlyNumber(event);

            let key = event.keyCode;

            if (key === 27) {
                $(this).children('input').val(oldInputVal);
                $(this).children('input').blur();
            }

            if (!(key in keyDirs)) {
                return;
            }

            let direction = keyDirs[key];
            let $cell = self.getDesiredCell(direction);
            if ($cell.length !== 0) { // Если ячейка найдена
                self.selectCell($cell);
                $cell = $(this);
                let scoreInputVal = $cell.children('input').val();
                if (scoreInputVal !== oldInputVal) {
                    $cell.one('turnOn', () => self.selectCell($cell));
                    $cell.chlidren('input').blur();
                } else {
                    self.selectCell($cell);
                }
            }
        }).on('change', '.rate', function () {
            Base.filterNumbers($(this).children('input'));
            self.setRate($(this), oldInputVal);
        }).on('change', '.checkbox', function () {
            self.cellInfo.select($(this));
            self.setExam($(this));
            self.cellInfo.reset();
        }).on('click', '.download button', function () {
            $.fileDownload(URLdir + 'handler/FileCreator/GenerateFinalForm', {
                httpMethod: 'POST',
                data: {
                    'disciplineID': self.settings.discipline.id,
                    'groupID': Base.parseid($(this)),
                    'stage': parseInt($(this).siblings('select').val()),
                },
            });
        });
    }

    getDesiredCell(direction) {
        let [col, row] = this.cursor.get();

        let $cell;
        do {
            if (direction == this.Direction.Left)  col--;
            if (direction == this.Direction.Right) col++;
            if (direction == this.Direction.Up)    row--;
            if (direction == this.Direction.Down)  row++;
            $cell = $(`#col_row_${col}_${row}`);
        } while ($cell.length && !$cell.hasClass('rate'));
    selectCell($cell) {
        $cell.children('input').focus();
        // Выбрать весь текст в ячейке
        setTimeout(() => $cell.children('input').select(), 0);
    }


    setRate($cell, oldInputVal) {
        let scoreInputVal = $cell.children('input').val();
        if (scoreInputVal == oldInputVal) return;

        let rate = Base.parseRate($cell, true);

        let successText;
        let errorText;
        if (scoreInputVal != '' && oldInputVal != '') {
            successText = 'Балл обновлен';
            errorText = 'Не удалось обновить балл';
        } else if (scoreInputVal != '' && oldInputVal == '') {
            successText = 'Балл добавлен';
            errorText = 'Не удалось добавить балл';
        } else {
            successText = 'Балл удален';
            errorText = 'Не удалось удалить балл';
        }

        this.sendRate($cell, 'handler/rating/SetRate', { rate }, $input => $input.val(oldInputVal),
            successText, errorText);
    setExam($cell) {
        let optionVal;
        if ($cell.hasClass('absence')) optionVal = 'absence';
        if ($cell.hasClass('autopass')) optionVal = 'pass';
        if (!$cell.children('input').prop('checked')) optionVal = `drop_${optionVal}`;

        let successText;
        let errorText;
        let option;
        switch (optionVal) {
            case 'absence':
                successText = 'Неявка установлена';
                errorText = 'Не удалось установить неявку';
                option = 'absence';
                break;

            case 'drop_absence':
                successText = 'Неявка удалена';
                errorText = 'Не удалось удалить неявку';
                option = 'null';
                break;

            case 'pass':
                successText = 'Автомат установлен';
                errorText = 'Не удалось установить автомат';
                option = 'pass';
                break;

            case 'drop_pass':
                successText = 'Автомат удален';
                errorText = 'Не удалось удалить автомат';
                option = 'null';
                break;
        }

        this.sendRate($cell, 'handler/rating/SetExamPeriodOption', { option },
            $input => $input.prop("checked", !$input.prop("checked")), successText, errorText);
    }

    sendRate($cell, url, data, reset, successText, errorText) {
        let $input = $cell.children('input');
        data.studentID = this.cell.student;
        data.disciplineID = this.settings.discipline.id;
        data.submoduleID = this.cell.submodule;
        $.postJSON(URLdir + url, data).success(() => {
            this.recountScores($cell, data.submoduleID);
            Popup.success(successText);
            let status = parseInt(jqXHR.status);
                if (status != 400) throw null;
                let message = JSON.parse(jqXHR.responseText).message;
                if (!message) throw null;
                Popup.error(message);
            } catch (error) {
                Popup.error(errorText);
        }).always(() => $input.turnOn());
    recountScores($cell, submoduleID) {
        let $сells = $cell.parent().children();
        let [col, row] = Base.parsePosition($cell);
        let examCol = Base.parseClass($(`#exam_${submoduleID}`));
        let absenceCol = Base.parseClass($(`#absence_${submoduleID}`));
        let autopassCol = Base.parseClass($(`#autopass_${submoduleID}`));

        if ($cell.hasClass('exam')) {
            $сells.filter(`#col_row_${absenceCol}_${row}`).children('input').prop('checked', false);
            $сells.filter(`#col_row_${autopassCol}_${row}`).children('input').prop('checked', false);
        }

        if ($cell.hasClass('absence')) {
            $сells.filter(`#col_row_${examCol}_${row}`).children('input').val('');
            $сells.filter(`#col_row_${autopassCol}_${row}`).children('input').prop('checked', false);
        }

        if ($cell.hasClass('autopass')) {
            $сells.filter(`#col_row_${examCol}_${row}`).children('input').val('');
            $сells.filter(`#col_row_${absenceCol}_${row}`).children('input').prop('checked', false);
        }


        let $regularCells = $сells.filter('.regular');
        if ($regularCells.length) {
            let semesterRate = $regularCells.aggregate((init, elem) => init + Base.parseRate($(elem)));
            $сells.filter('.semester').text(semesterRate);
        }


        let semesterRate = Base.parseRate($сells.filter('.semester'));
        let bonusRate = Base.parseRate($сells.filter('.bonus'));
        let extraRate = $сells.filter('.extra').aggregate((init, elem) => init + Base.parseRate($(elem)));
        let examRate = $сells.filter('.exam').aggregate((init, elem) => Math.max(init, Base.parseRate($(elem))));
        let rateResult = semesterRate + bonusRate + extraRate + examRate;


        let rateResultText = (rateResult > 100) ? '100+' : rateResult;
        $сells.filter('.result').text(rateResultText);


        if ($cell.hasClass('extra')) {
            let $nextExtraCell = $cell.next('.extra');
            if ($nextExtraCell.length) {
                let maxExtraRate = this.settings.discipline.type == 'exam' ? 38 : 60;
                let curRate = Base.parseRate($cell, true);
                let maxVal = maxExtraRate - semesterRate - curRate;
                if (maxVal > 0 && curRate != -1) {
                    $nextExtraCell.find('input').attr('placeholder', 'макс. ' + maxVal);
                } else {
                    $nextExtraCell.find('input').attr('placeholder', '').val('');
                }
            }
$(() => new Rating());