diff --git a/db/StoredFunctions.sql b/db/StoredFunctions.sql index fad97d560710b7f28b88ccdcfeac00cdd410b1d9..c01515d5c266592740cd0a08df51bd3a3a0bce34 100644 --- a/db/StoredFunctions.sql +++ b/db/StoredFunctions.sql @@ -238,7 +238,8 @@ END// # create department or return existing DROP FUNCTION IF EXISTS CreateDepartment // -CREATE FUNCTION CreateDepartment ( +DROP FUNCTION IF EXISTS Department_Create // +CREATE FUNCTION Department_Create ( `pName` VARCHAR(200) CHARSET utf8, `pFacultyID` INT(11) ) RETURNS INT(11) # department id or -1 if failed @@ -253,6 +254,24 @@ BEGIN 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 +NO SQL +BEGIN + DECLARE vID INT DEFAULT -1; + + SELECT departments.ID INTO vID + FROM departments + WHERE departments.Name = pName AND + departments.FacultyID = pFacultyID + LIMIT 1; + + RETURN vID; +END // + # ------------------------------------------------------------------------------------------- # Label: specializations # ------------------------------------------------------------------------------------------- @@ -577,7 +596,8 @@ END // DROP FUNCTION IF EXISTS CreateTeacher// -CREATE FUNCTION `CreateTeacher` ( +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, @@ -601,33 +621,9 @@ BEGIN INSERT INTO `teachers` (AccountID, LastName, FirstName, SecondName, JobPositionID, DepartmentID) VALUES (vAccountID, pLastName, pFirstName, pSecondName, pJobPositionID, pDepartmentID); - RETURN ROW_COUNT()-1; -END // - - -DROP FUNCTION IF EXISTS CreateTeacherByDepName// -CREATE FUNCTION `CreateTeacherByDepName` ( - `pLastName` VARCHAR(30) CHARSET utf8, - `pFirstName` VARCHAR(30) CHARSET utf8, - `pSecondName` VARCHAR(30) CHARSET utf8, - `pDepartmentName` VARCHAR(200) CHARSET utf8, - `pFacultyID` INT, - `pActivationCode` VARCHAR(40) CHARSET utf8 - ) RETURNS int(11) # 0 - success, < 0 - failed - NO SQL -BEGIN - DECLARE vAccountID, vRoleID, vDepID INT DEFAULT -1; - DECLARE EXIT HANDLER FOR SQLEXCEPTION RETURN -1; - - SET vDepID = CreateDepartment(pDepartmentName, pFacultyID); - IF vDepID < 0 THEN - RETURN -1; - END IF; - - RETURN CreateTeacher(pLastName, pFirstName, pSecondName, 12, vDepID, pActivationCode); + RETURN LAST_INSERT_ID(); END // - -- -1 - РЅРµ сотрудник деканата Рё РЅРµ преподаватель дисциплины -- 0 - только чтение -- 1 - редактирование diff --git a/db/StoredProcedures.sql b/db/StoredProcedures.sql index cb6b058f6a6d34d29ca7370e3a34ef0cc409c40c..d128a636264a4a8388d95366a27dc49aa9ece2c3 100644 --- a/db/StoredProcedures.sql +++ b/db/StoredProcedures.sql @@ -104,6 +104,12 @@ BEGIN ORDER BY departments.Name ASC; END // +DROP PROCEDURE IF EXISTS Departments_LoadAll// +CREATE PROCEDURE `Departments_LoadAll` ( +) NO SQL +BEGIN + SELECT * FROM `departments`; +END // # ------------------------------------------------------------------------------------------- # Label: specializations diff --git a/media/components/office/dropzone/dz.js b/media/components/office/dropzone/dz.js index 1b8854e281f29f7a7e3f887eb12713cf46e71d54..ecc936c34c36d26f64977e8fad8b65cecd4ec344 100644 --- a/media/components/office/dropzone/dz.js +++ b/media/components/office/dropzone/dz.js @@ -1,5 +1,5 @@ var Dropzone = (function () { - var $dropzone, readerClbks = []; + var $dropzone, $input, readerClbks = []; var handlerOf = { dragOver: function (e) { @@ -20,9 +20,13 @@ var Dropzone = (function () { $dropzone.removeClass('hover'); processFiles(e.dataTransfer.files); }, - - fileSelect: function (evt) { - processFiles(evt.target.files); + + inputChange: function (e) { + if (e.value == '') + return; + + processFiles(e.target.files); + $input.value = ''; } }; @@ -46,8 +50,8 @@ var Dropzone = (function () { init: function (listeners) { readerClbks = listeners || []; - var $input = document.getElementById('files'); - $input.addEventListener('change', handlerOf.fileSelect, false); + $input = document.getElementById('files'); + $input.addEventListener('change', handlerOf.inputChange, false); $dropzone = document.getElementsByClassName('b-dropzone')[0]; $dropzone.addEventListener('dragover', handlerOf.dragOver, false); diff --git a/media/components/office/teachers-upload/page.css b/media/components/office/teachers-upload/page.css index 4968d16bf4df9e34ca4daf72c1bebe51c9a5f709..530bbb034b224cee30add5fc3788a32176d604ef 100644 --- a/media/components/office/teachers-upload/page.css +++ b/media/components/office/teachers-upload/page.css @@ -1,7 +1,3 @@ -pre { - margin-bottom: 20px; -} - code { padding: .1em .5em; font-size: 85%; diff --git a/~dev_rating/application/classes/Controller/Handler/Teachers.php b/~dev_rating/application/classes/Controller/Handler/Teachers.php index 10f924696db1247bc11097a80aa47d5a31ff3d68..bdebe75d7665f90ece789626d6fa67ad2917eb26 100644 --- a/~dev_rating/application/classes/Controller/Handler/Teachers.php +++ b/~dev_rating/application/classes/Controller/Handler/Teachers.php @@ -5,7 +5,7 @@ class Controller_Handler_Teachers extends Controller_Handler public function before() { parent::before(); - $this->user->checkAccess(User::RIGHTS_ADMIN); + $this->user->checkAccess(User::RIGHTS_ADMIN | User::RIGHTS_DEAN); } protected function action_createTeacher() { @@ -39,4 +39,40 @@ class Controller_Handler_Teachers extends Controller_Handler $this->response->body(json_encode($list)); } + + public function action_upload() { + $faculty = $this->user->Faculty; + if ($this->user->isAdmin() && $_POST['facultyID']) + $faculty = Model_Faculty::with($_POST['facultyID']); + + $departments = Arr::groupByUniqueKey('Name', $faculty->getDepartments()); + + foreach ($_POST['departments'] as $dep) { + if ($dep['name'] === 'Без кафедры') + $dep['name'] = ''; + + $dep['ID'] = !isset($departments[$dep['name']]) + ? self::createDepartment($dep['name'], $faculty) + : $departments[$dep['name']]['ID']; + + foreach ($dep['people'] as $name) { + list($lastName, $firstName, $secondName) = Text::parseFullName($name); + + Model_Teacher::make() + ->department($dep['ID']) + ->firstName($firstName) + ->lastName($lastName) + ->secondName($secondName) + ->create(); + } + } + } + + private static function createDepartment($name, Model_Faculty $faculty) { + $sql = 'SELECT Department_Create(:name, :faculty) AS `id`'; + return DB::query(Database::SELECT, $sql) + ->param(':name', $name) + ->param(':faculty', $faculty->ID) + ->execute()->get('id'); + } } diff --git a/~dev_rating/application/classes/FileParser.php b/~dev_rating/application/classes/FileParser.php index 791dcb2b462fb9c8be18847fd40d09e9f3886ddd..267c42b587ef850d2bf9713dd39fcf2b9964bff7 100644 --- a/~dev_rating/application/classes/FileParser.php +++ b/~dev_rating/application/classes/FileParser.php @@ -22,7 +22,7 @@ class FileParser list($name, $gradeNum, $groupNum, $degree, $spec) = UTF8::clear($line); // Фамилия, РёРјСЏ, отчество - list($lastName, $firstName, $secondName) = self::parseFullName($name); + list($lastName, $firstName, $secondName) = Text::parseFullName($name); $degree = self::getDegreeType($degree); @@ -87,53 +87,4 @@ class FileParser 'RecordsExistsCount' => $exists, ]; } - - - /** - * Parse teachers' info & synchronize it with database. - * @param $filename string File with source data - * @param $facultyID int - * @return array - */ - public static function uploadTeachers($filename, $facultyID) { - if (File::mime($filename) != 'text/plain') - return []; - -// $faculties = []; -// foreach (Model_Faculties::load() as $faculty) { -// $name = trim($faculty['Name']); -// $faculties[$name] = $faculty['ID']; -// } - - $count = 0; - $errors = []; - $file = fopen($filename, "r"); - - while ($line = fgetcsv($file, 0, ";")) { - list($name, $department, $facultyName) = UTF8::clear($line); - - // Фамилия, РёРјСЏ, отчество - list($lastName, $firstName, $secondName) = self::parseFullName($name); - -// if ($facultyName) -// $facultyID = $faculties[trim($facultyName)]; - - $attempt = Model_Account::createTeacherByDepName($lastName, $firstName, $secondName, $department, $facultyID); - - if ($attempt == -1) { - $errors[] = ['Row' => $count, 'Info' => implode(';', $line)]; - } - - $count++; - } - - return $errors; - } - - private static function parseFullName($name) { - // Енотова (Сенченко) Наталья Лин Чу Геннадьевна - $name = preg_replace('/\(.*\)\s+/', '', $name); - $res = explode(' ', $name, 3); - return Arr::map('trim', $res); - } -} \ No newline at end of file +} diff --git a/~dev_rating/application/classes/Model/Account.php b/~dev_rating/application/classes/Model/Account.php index c61c229303795d9ac09a7255b4b06419db733e17..fe583f1f9ee1e0f818779699d8c7fff18f35ae95 100644 --- a/~dev_rating/application/classes/Model/Account.php +++ b/~dev_rating/application/classes/Model/Account.php @@ -28,25 +28,6 @@ class Model_Account extends Model return UTF8::strtoupper($activationCode); } - public static function createTeacherByDepName($lastName, $firstName, $secondName, $department, $facultyID) { - if ($department == '') return -1; - - $code = self::generateActivationCode(); - - $sql = "SELECT `CreateTeacherByDepName`(:last, :first, :second, :department, :faculty, :code) AS `UserID`;"; - $response = DB::query(Database::SELECT, $sql) - ->parameters([ - ':last' => trim($lastName), - ':first' => trim($firstName), - ':second' => trim($secondName), - ':department' => $department, - ':faculty' => $facultyID, - ':code' => $code, - ])->execute()->get('UserID'); - - return $response == -1 ? -1 : $code; - } - public static function createStudentEx($lastName, $firstName, $secondName, $gradeNum, $groupNum, $degree, $specialization, $facultyID, $semesterID = null) { $code = self::generateActivationCode(); diff --git a/~dev_rating/application/classes/Model/Faculties.php b/~dev_rating/application/classes/Model/Faculties.php index d3b081c2be9c9f408cbb2576dda58edf4abae512..293270be64c14bb260733d8609d4a56f8ad404f7 100644 --- a/~dev_rating/application/classes/Model/Faculties.php +++ b/~dev_rating/application/classes/Model/Faculties.php @@ -8,4 +8,19 @@ class Model_Faculties extends Model return DB::query(Database::SELECT, $sql) ->execute()->as_array(); } + + public static function getDepartments() { + $sql = 'CALL `Departments_LoadAll`()'; + $result = DB::query(Database::SELECT, $sql)->execute(); + + $list = []; + foreach ($result as $row) { + if (empty($row['Name'])) + $row['Name'] = 'Без кафедры'; + + $list[] = $row; + } + + return $list; + } } diff --git a/~dev_rating/application/classes/Model/Helper/TeacherBuilder.php b/~dev_rating/application/classes/Model/Helper/TeacherBuilder.php index 9439373cecd41d1230519cbe2b509ac65b739659..46e9fd6b8d2253cb84fba8db1300345f1e1e59a5 100644 --- a/~dev_rating/application/classes/Model/Helper/TeacherBuilder.php +++ b/~dev_rating/application/classes/Model/Helper/TeacherBuilder.php @@ -4,8 +4,9 @@ class Model_Helper_TeacherBuilder extends Model_Helper_Builder { public function create() { $this->data += [ - 'SecondName' => '', + 'SecondName' => '', 'ActivationCode' => Model_Account::generateActivationCode(), + 'JobPositionID' => Model_Teacher::JOB_TEACHER, ]; $required = [ @@ -23,7 +24,7 @@ class Model_Helper_TeacherBuilder extends Model_Helper_Builder $string = trim($string); # todo: remove empty symbols if (!strlen($string)) throw new InvalidArgumentException('Last name can\'t be empty'); - $this->data['LastName'] = $string; + $this->data['LastName'] = UTF8::clear($string); return $this; } @@ -31,12 +32,12 @@ class Model_Helper_TeacherBuilder extends Model_Helper_Builder $string = trim($string); if (!strlen($string)) throw new InvalidArgumentException('First name can\'t be empty'); - $this->data['FirstName'] = $string; + $this->data['FirstName'] = UTF8::clear($string); return $this; } function & secondName($string) { - $this->data['SecondName'] = trim($string); + $this->data['SecondName'] = UTF8::clear($string); return $this; } diff --git a/~dev_rating/application/classes/Model/Teacher.php b/~dev_rating/application/classes/Model/Teacher.php index 52d91ed317b7e0ff6a9ebe2ce67448c3d13180f2..8630a4b1f99dc9211f46c98a1092342130f1a438 100644 --- a/~dev_rating/application/classes/Model/Teacher.php +++ b/~dev_rating/application/classes/Model/Teacher.php @@ -20,6 +20,12 @@ */ class Model_Teacher extends Model_Container { + // see `job_positions` table + const JOB_TEACHER = 9; + const JOB_PROFESSOR = 10; + const JOB_SENIOR_TEACHER = 11; + + protected function getRawData($id) { $sql = 'CALL `Teacher_GetInfo`(:id)'; $info = DB::query(Database::SELECT, $sql) @@ -40,7 +46,7 @@ class Model_Teacher extends Model_Container } protected function create() { - $sql = 'SELECT `CreateTeacher`(LastName, FirstName, SecondName, JobPositionID, DepID, ActivationCode) AS `ID`'; + $sql = 'SELECT `Teacher_Create`(LastName, FirstName, SecondName, JobPositionID, DepID, ActivationCode) AS `ID`'; $this->data[self::$ID_FIELD] = DB::query(Database::SELECT, $sql) ->parameters($this->data) diff --git a/~dev_rating/application/classes/User.php b/~dev_rating/application/classes/User.php index 8a7f40bee0c0d5154dd1cb70b7bcb0459fb056a7..53ea6e0271ff122ff5c3387618589103550b3b67 100644 --- a/~dev_rating/application/classes/User.php +++ b/~dev_rating/application/classes/User.php @@ -77,10 +77,10 @@ class User implements ArrayAccess $session = $this->_session = Session::instance(); - if ( !isset($session->RoleMark) ) - $session->RoleMark = 1; + if (!isset($session['RoleMark'])) + $session['RoleMark'] = 1; - if ( self::$_updateSession ) { + if (self::$_updateSession) { $session->regenerate(); $session->set('start_time', time()); } diff --git a/~dev_rating/application/views/office/teachers/upload.twig b/~dev_rating/application/views/office/teachers/upload.twig index 866d68d66c1d8fea0c2c5c2753e6f33e52abb3b6..0a6c72d0ec170e3b34138cce7e9c69f6ea41c99e 100644 --- a/~dev_rating/application/views/office/teachers/upload.twig +++ b/~dev_rating/application/views/office/teachers/upload.twig @@ -12,16 +12,41 @@ <script> $(function () { + var data = []; + Dropzone.init([ function (file, content) { if (file.type !== 'text/plain') { return EventInspector.error('Неверный формат файла!'); } + $('.b-upload-first-step').hide(); + $('.b-upload-second-step').show(); + var list = content.split('\n'); - console.log(parseTeachers(list)); + data = data.concat(parseTeachers(list)); }]); - }) + + $('#cancel').click(function () { + $('.b-upload-second-step').hide(); + $('.b-upload-first-step').show(); + $('#save').removeAttr('disabled').text('Сохранить'); + data = []; + }); + + $('#save').click(function () { + $(this).attr('disabled', 'disabled') + .html('<i class="fa fa-circle-o-notch fa-spin"></i>'); + + $.post(g_URLdir + 'handler/teachers/upload', { + facultyID: $('#faculty').val(), + departments: data + }, function (res) { + EventInspector.success('Успешно сохранено!'); + $('#cancel').trigger('click'); + }); + }); + }); </script> {% endblock %} @@ -30,12 +55,12 @@ {% block office_content %} <h2 class="defaultForm">Пакетная загрузка преподавателей</h2> - <div class="b-upload-file"> + <div class="b-upload-first-step"> <p> Рнструмент пакетной загрузки преподавателей предоставляет возможность регистрировать РІ системе множество преподавателей. Файл СЃРѕ СЃРїРёСЃРєРѕРј преподавателей представляет СЃРѕР±РѕР№ <code>.txt</code>-файл, который должен иметь следующий формат: - <pre> + <pre style="margin-bottom:20px"> {% for field in ['Кафедра <название>', '', 'Р¤РРћ', 'Р¤РРћ'] %} {{ field }}<br/> {% endfor %} @@ -52,23 +77,34 @@ {{ HTML.component('office/dropzone/dz.twig')|raw }} </div> + <div class="b-upload-second-step" style="display:none"> + {% if User.isAdmin %} + <p>Выберите факультет, РЅР° который нужно загрузить преподавателей:</p> + + <select id="faculty" class="defaultForm"> + {% for row in Faculties %} + {% set selected = (row.ID == User.FacultyID ? 'selected="selected"') %} + <option value="{{ row.ID }}" {{ selected }}> + {{ row.Name }} ({{ row.Abbr }}) + </option> + {% endfor %} + </select> + {% endif %} + + {# todo: show some results & errors #} + {#<div id="problems" style="margin-top: 20px; display:none">#} + {#<p>Р’ базе данных отсутствуют следующие кафедры, создать РёС…?#} + + {#{#todo: make editable area #}#} + {#<pre></pre>#} + + {#<p>Следующие имена некорректны:</p>#} + {#</div>#} + + <div style="float:right;margin-top:5px"> + <button id="save" class="defaultForm BlueButton">Сохранить</button> + или <a id="cancel" href="#">отменить</a> + </div> + </div> - {#<div class="dialogTopText">#} - - {#</div>#} - - {#<form enctype="multipart/form-data" action="" method="POST">#} - {#<div class="goodClearFix defaultForm marginBetween">#} - {#<select id="facultySelect" name="facultyID" class="defaultForm">#} - {#<option value="0" selected="selected">— Выберите подразделение ЮФУ —</option>#} - {#{% for row in Faculties %}#} - {#<option value="{{ row.ID }}">{{ row.Name }} ({{ row.Abbr }})</option>#} - {#{% endfor %}#} - {#</select>#} - {#</div>#} - {#<div class="goodClearFix">#} - {#<div class="defaultForm FLeft"><input name="students" class="defaultForm FullWidth" type="file"></div>#} - {#<div class="defaultForm FRight"><input type="submit" class="defaultForm GreenButton P2Width noMargin" value="Загрузить"></div>#} - {#</div>#} - {#</form>#} {% endblock %} diff --git a/~dev_rating/static/other/teachers.txt b/~dev_rating/static/other/teachers.txt index 66750fdb68c92e3773212c4b45de3be534c97718..f470b234a957177f45a3319d5c63c2b4e84b9df5 100644 --- a/~dev_rating/static/other/teachers.txt +++ b/~dev_rating/static/other/teachers.txt @@ -16,4 +16,4 @@ Рнакин Скайуокер РћР±Рё-Ван Кеноби -Йода +Мастер Йода diff --git a/~dev_rating/system/classes/Kohana/Text.php b/~dev_rating/system/classes/Kohana/Text.php index d9f36a63ac0720cf095b7faeba7dc8b314c587ff..dda030f6a6751da45a0346db758b335900511f7d 100644 --- a/~dev_rating/system/classes/Kohana/Text.php +++ b/~dev_rating/system/classes/Kohana/Text.php @@ -10,6 +10,13 @@ */ class Kohana_Text { + public static function parseFullName($name) { + // Енотова (Сенченко) Наталья Лин Чу Геннадьевна ♥ + $name = preg_replace('/\(.*\)\s+/', '', $name); + $res = explode(' ', $name, 3); + return Arr::map('trim', $res); + } + public static function dump($data) { var_dump($data); } // take array with (at least) LastName & FirstName keys