Проектирование системы дистанционного обучения

Введение

1. Постановка задачи
2. Анализ систем дистанционного обучения.
2.1. Преимущества дистанционного обучения
2.2. Недостатки дистанционного обучения
2.3. Аспекты технической реализации системы дистанционного обучения
3. Проектирование модулей системы дистанционного обучения
3.1. Разработка модулей наполнения и обзора лекционных материалов
3.2. Разработка модуля тестирования
4. Разработка программного обеспечения
4.1. Выбор программных средств разработки
4.1.1. Выбор операционной системы для сервера
4.1.2. Выбор Web-сервера
    4.1.3. Выбор средств управления базами данных и языковых средств разработки
4.2. Проектирование БД системы дистанционного обучения
4.2.1. Представление модели данных в виде ER – диаграмм
4.2.1. Логическое проектирование БД
4.2.2. Приведение БД к табличной форме
4.2.3. Проектирование схемы БД
4.3. Разработка программных модулей
4.3.1. Разработка модели
4.3.2. Разработка контроллеров действия

4.3.3. Разработка шаблонов (view в паттерне MVC)

Разработка контроллеров действия

Контроллер Zend_Controller_Front  является ядром системы MVC в Zend Framework. В MVC все запросы перехватываются фронт-контроллером и перенаправляются отдельным контроллерам действий, выбор которых производится по запрошенному URL[7].
Загрузке необходимого контроллера способствуют входные переменные, которые были посланы браузером. Выбор нужного контроллера осуществляется путем разбора HTTP заголовка. Первые два параметра указывают на целевой хост и URL. Например, для административно обзора видео лекций запрос будет следующего содержания:
GET /admin/viewvideo/id/9 HTTP/1.1
Host: 172.25.137.25
         Где «admin» контроллер, «viewvideo» действие,  «id» параметр  со значением «9».
Для того чтоб все запросы попадали на базовый класс Zend_Controller_Front, в корневом каталоге сервера был помещен файл переадресации .htaccess следующего содержания:
RewriteEngine on
RewriteRule !\.(js|ico|gif|jpg|png|css|flv|mpg|mpg4|avi|mov|swf)$ index.php
         Таким образом все обращения к URL(кроме тех, которые имеют разрешения указанные в .htaccess) адресуются на index.php.
Загрузка контроллеров в index.php осуществляется следующим образом(листинг 4.4):
Листинг 4.4. – Загрузка фронт контроллера
Zend_Loader::loadClass ( 'Zend_Controller_Front' );
$frontController = Zend_Controller_Front::getInstance ();
$frontController->throwExceptions ( true );
$frontController->setControllerDirectory ( './application/controllers' );

Настройка контроллера осуществляется за счет вызова методов и функций.
В системе разработаны четыре контроллера действия, отвечающих за входные переменные. indexController- контроллер по умолчанию, в нем реализованы следующие действия: авторизация, регистрация, навигация по основным страницам сайта.  С помощью subjectController происходит навигация по курсам, лекциям. adminController обеспечивает навигацию по страницам администрирования. testController создан для прохождения тестов.
В каждом контроллере существует действие по умолчанию indexAction, это действие выполняется, если не передается никаких параметров(листинг 4.5.).
Листинг 4.5 – Действие по умолчанию в контроллере subjectController.
class subjectController extends Zend_Controller_Action
{
function indexAction()
{
$subject = new Subject();                 
$this->view->subjectList = $subject->subjectList();
}
.  .  .  .  .  .  .
}
Для обращения к БД, необходимо создать объект класса Subject, после чего можно выполнять инициализацию методов в объекте view. Объект view использует шаблонизатор для вывода информации на страницу.
На рисунке 4.11. изображен вывод список курсов, доступных для обзора.

Внешний вид верстки страницы
Рисунок 4.11. – Вывод на шаблон списка курсов.
При переходе по ссылке, указывающей на название курса, активируется действие viewsubjectAction контроллера subjecController. В этом случае контроллеру subjecController передаются параметры (листинг 4.6).
Листинг 4.6. – Выполнение действия viewsubject
function viewsubjectAction()
{
$subject = new Subject();                 
$id = (int)$this->_request->getParam('subject');
$author = (int)$this->_request->getParam('author');

$this->view->lectionList = $subject->getAllLectionPrintBySubjectId($id);
$this->view->subject = $subject->getSubjNameByLecId($id);
$this->view->subjectId = $subject->getSubjIdByLectId($id);
}

Результат выполнения действия – присваивание методов объекта view. На рисунке 4.12. Изображен внешний вид страницы со списком тем.
Внешний вид верстки страницы
Рисунок 4.12 – Вывод на шаблон списка тем
На этой странице доступны темы, которые существуют в выбранном курсе. Для перехода к теме необходимо перейти по ссылке.    
В связи с тем, что реализовано три типа представлений лекций, было принято решение отображать изначально текстовый вариант с наивысшим рейтингом, поскольку видео и аудио лекции обладают намного большим объемом.
В листинге 4.7. отображено действие viewprintAction, которое выводит содержание текстовой лекции, а также дополнительную информацию.
Листинг 4.7. – Метод viewprintAction контроллера subjectController
function viewprintAction()
{
$subject = new Subject();                 
$id = (int)$this->_request->getParam('id');
$author = (int)$this->_request->getParam('author');

$this->view->lectionList = $subject->getAllLectionPrintBySubjectId($id);

$this->view->author = $subject->getAuthorByLectId($author);
$this->view->subject = $subject->getSubjNameByLecId($id);
$this->view->subjectId = $subject->getSubjIdByLectId($id);
$this->view->lectionId = $id;
$this->view->lection = $subject->getLectNameByLectId($id);
$this->view->printId = $author;
$this->view->rating = $subject->getRatingByPrintId($author);

$this->view->authorListPrint = $subject->getAuthorListByPrintId($id);
$this->view->authorListVideo = $subject->getAuthorListByVideoId($id);
$this->view->authorListAudio = $subject->getAuthorListByAudioId($id);              
$this->view->authorListAddition = $subject->getAuthorListByAdditionId($id);      
}
На странице сайта также присутствуют ссылки для перехода на другие варианты лекций, а также представлен интерфейс для повышения рейтинга открытой лекции(рисунок 4.13).
Внешний вид верстки страницы
Рисунок 4.13. – Фрагмент страницы обзора лекции текстового типа

Любой вариант лекции идентифицируется автором, рейтинг экземпляра указан в квадратных скобках.
Для скачивания прикрепленных к лекции файлов необходимо перейти по ссылке, указывающей на название файла. Прикрепляемые файлы хранятся в специально отведенном файловом хранилище, их вывод осуществляется посредством перехода на действие downloadAction контроллера subjectController.
Проигрыватель видео и аудио лекций также использует действие downloadAction, поскольку все файлы хранятся в едином хранилище и имеют общий интерфейс для вывода. В БД каждый тип файла имеет свой идентификатор, который соответствует физическому файлу(листинг 4.8.).


Листинг 4.8 – Вывода файлов из файлового хранилища
function downloadAction()
{
$subject = new Subject();
/* установка директории дампа памяти */
$patchToDump = "D:\\DUMP\\";

if ($this->_request->getParam('download'))
{
$exp = "";
switch($this->_request->getParam('type'))
{
case 'video' :
/*  загрузка идентификатора файла видео формата /*
$type = 'video';
$id = (int) $this->_request->getParam('download');
$lectionId = $subject->getLectIdByVideoId($id);                                                                             break;
/*  загрузка идентификатора файла аудио формата */
case 'audio' :
$type = 'audio';
$result = strpos($this->_request->getParam('download'), ".");
$id =(int)substr($this->_request->getParam('download'),0, $result);
$lectionId = $subject->getLectIdByAudioId($id);
/* извлечение разрешения файла(необходимо только для аудио форматов) */
$exp = $subject->getExpByAudioId($id);;
break;
case 'addition' :

                                    $type = 'addition';
$id = (int) $this->_request->getParam('download');
$lectionId = $subject->getLectIdByAdditionId($id);                                                                         break;
default: exit;
}
$subjectId = $subject->getSubjIdByLectId($lectionId);

$filename = $patchToDump.$subjectId."_".$lectionId."_".$type."_".$id;
$contents = "";
/* открытие файла из дампа памяти */
$f = fopen($filename, "r");

             while (!feof($f))
{
$contents .= fread($f, 2048);
}
fclose ($f);
if ($type = 'addition')
{
/* присваивание переменной $id имени файла */
$id = $subject->getNameAdditionByAdditionId($id);                      
}
/* изменение заголовка пакета HTTP протокола */
header("Content-Type: ".$type."; charset=\"windows-1251\";\r\n");
header("Content-Disposition: attachment; filename=".$id.".".$exp.";\r\n");
/* вывод содержимого файла */
echo $contents;
}                 
exit;
}

         Для отображения видео и аудио лекций  был разработан проигрыватель видео файлов в программной среде AdobeFlashCS3. При этом использовались средства стандартных компонентов, которые включены в дистрибутив: FLV Playback, StopButton, PlayButton, MuteButton, VolumeButton и StreamControl.
На рисунке 4.14. изображен внешний вид проигрывателя. За счет своей универсальности проигрыватель также используется для воспроизведения аудио лекций.

Внешний вид верстки страницы
Рисунок 4.14. - Отображение видео лекций
Действия для отображения аудио лекций представлены в листинге 4.9.
Листинг 4.9. – Метод viewaudioAction контроллера subjectController
function viewaudioAction()
{
$subject = new Subject();            
$id = (int)$this->_request->getParam('id');
$author = (int)$this->_request->getParam('author');
/* получение списка лекций */
$this->view->lectionList = $subject->getAllLectionPrintBySubjectId($id);

$this->view->author = $subject->getAuthorByAudioId($author); $this->view->subject = $subject->getSubjNameByLecId($id);
$this->view->subjectId = $subject->getSubjIdByLectId($id);
$this->view->lectionId = $id;
$this->view->lection = $subject->getLectNameByLectId($id);
$this->view->printId = $author;
$this->view->rating = $subject->getRatingByVideoId($author);
/* получение списка авторов */
$this->view->authorListPrint = $subject->getAuthorListByPrintId($id);
$this->view->authorListVideo = $subject->getAuthorListByVideoId($id);
$this->view->authorListAudio = $subject->getAuthorListByAudioId($id);
$this->view->authorListAddition =
$subject->getAuthorListByAdditionId($id);                                          
}          

Для обеспечения возможности повышения рейтинга для каждого экземпляра лекции был разработан механизм отправки данных без перегрузки страницы. При этом использовалась библиотека «Protorype.js», с помощью которой на языке JavaScript создавался xml документ и отправлялся на сервер.
Для предотвращения многократного «голосования» за один и тот же экземпляр лекции одним клиентом системы, был разработан механизм занесения информации в кэш-память браузера(с использованием механизма cookies) с последующей проверкой на соответствующую запись при повторном голосовании.
В браузере клиента при вызове функции plus(), происходит создание XMLHttpRequest запроса, который отправляется контроллеру subjectController, методу subjectAction(листинг 4.10).
В том  случае если в Интернет браузере отключены cookies, пользователю системы выдастся соответствующее сообщение с просьбой активировать cookies.


Листинг 4.10. – Динамическое создание XMLHttpRequest запроса.
/*  функция отправки xmlHttpRequest запроса*/
function plus()
{
/* проверка: включены ли в браузере coockie */
If (document.cookieEnabled ==  false)
(
brouser = document.appName;
alert(‘Извините, но в Вашем браузере ’+ brouser+’ отключены cookies.\nДля того чтоб проголосовать включите cookies в настройках браузера. ’);
return;
)      
/* путь к контроллеру рейтинга
на этот относительный адрес будет послан запрос */
var requestUrl = '/subject/rating/';
var printId = $F('rating');

try
{
var request = new Ajax.Request(
requestUrl,
{
/* параметры xmlHttpRequest запроса*/
method: 'get',
parameters: printId,
/* вызов JavaScript функции, при получении ответа на запрос*/
onComplete: updateRating
}
);
}
catch (e)
{
/* обработка исключительных ситуаций, если
xmlHttpRequest не был отправлен или
небыл получен ответ*/
alert('Error: ' + e.toString());
}
}
/* функции  обновления текущего рейтинга лекции*/
function updateRating(responce)
{
if (responce.responseText)
{
curReting.innerHTML = responce.responseText;
}
}

Запрос обрабатывается на сервере действием ratingAction, где происходит занесение в БД результатов и выполняется проверка на наличие в cookie уже существующей записи(листинг 4.11).


Листинг 4.11. – Обработка XMLHttpRequest запроса
function ratingAction()
{
$subject = new Subject();
/*  обработка  входных переменных */                
$id = (int) $_REQUEST['id'][1];
$resp = false;
switch ($_REQUEST['id'][0])
{
case 'print':
/*  если cookie лекции не установлены */
if (!isset($_COOKIE["ratingPrint"][$id]))
{
/*  занесение в БД и cookie результатов */
$resp = $subject->updateRaytingPrint($id);
setcookie("ratingPrint[".$id."]", 1);
}
break;                        
}
/* возврат тело результата*/
echo $resp;
}
Контроллер adminControllerразработан для административных действий, т.е. выполнение функций добавление, удаление редактирование курсов, лекций, тестов.
Действие по умолчанию indexAction для adminController позволяет выбрать раздел администрирования(рисунок 4.15.).

Внешний вид верстки страницы
Рисунок 4.15. – Выбор разделов администрирования.

При переходе по ссылке  «Курсы»  администратору выводится список существующих курсов. В этом же окне возможно добавить новый курс(рисунок 4.16).

Внешний вид верстки страницы
Рисунок 4.16. – Форма обзора и ввода курсов.
Обработка формы и вывод контента происходит в subjectAction. Программа обработки формы представлена в листинге 4.12.
Листинг 4.12. -  Добавление нового курса.

function subjectAction()
{
$subject = new Subject();
if ($this->_request->getPost('addSubject'))
{          
$id = $subject->insertSubject($this->_request->getPost('addSubject'));
}                     

            $this->view->subjectList = $subject->subjectList();
}

         Обзор курса предоставляет администратору возможность добавить, изменить курс, лекцию,  действие viewsubjectAction контроллера adminController обрабатывает все эти действия. Все действия представлены в листинге 4.13.
Листинг 4.13. – Действие viewsubjectAction
function viewsubjectAction()
{
$subject = new Subject();
if ($this->_request->getPost('subject'))
{
/* изменение названия лекции */
if ($this->_request->getPost('lection'))
{
$subject->LectionUpdate(this->_request->getPost('lection'),
$this->_request->getPost('name));
}

            /* добавление лекции */
if ($this->_request->getPost('addLection'))
{          
$subject->insertLection($this->_request->getPost('id'),
$this->_request->getPost('addLection'));    
}
/* добавление предмета */
if ($this->_request->getPost('addSubject'))
{          
$id = $subject->insertSubject($this->_request->getPost('addSubject'));
header('location: /admin/subject/id/'.$id);     
}          

/*  получение списка предметов */           
$this->view->subjectList = $subject->subjectList();

/* если инициализированы входные переменных
значит пользователь выбрал курс */   
$id = (int)$this->_request->getParam('id');
if ($id != 0)
{
/* присваивание данных класса view*/
$sub = $subject->subjectId($id);
$this->view->title = "Администрирование > Курсы > " . $sub->subject;
$this->view->subject = $sub->subject;
$this->view->subjectId = $id;
$this->view->lections = $subject->subjectAllLection($id);
$this->view->prints = $ subject ->printAllLection($id);
$this->view->video = $ subject ->printAllVideo($id);
$this->view->audio = $ subject ->printAllAudio($id);
$this->view->addition = $ subject ->printAllAddition($id);                                                   }
}
}
Класс view использует шаблон viewsubject.phtml, данные класса будут выведены в форме (рисунок 4.17).
Внешний вид верстки страницы
Рисунок 4.17. – Представление шаблона viewsubject.phtml

Каждое поле редактируется прямо из страницы, остылая форму действию viewsubjectAction.
Для перехода к лекции автора необходимо перейти по соответствующей ссылке. Обзор экземпляра лекции выполняет действие viewprintAction(листинг 4.14).

Листинг 4.14 – Редактирование экземпляра лекции.
function viewlectAction()
{
/*  установка директории к используемому
дампу памяти и к конвертору текстовых файлов */
$patchToConverter = "D:\\SERVER\\converter\\";
$patchToDump = "D:\\DUMP\\";
$patchToDumpUpl = "D:/DUMP/";

            $subject = new Subject();
if($this->_request->isPost())
{
/*  форма отображения экзмемпляра лекции  отправляет
запрос на viewlectAction, в форме action – интерфейсный
элемент  типа hidden, указывающий на тип действий     */              
switch ($this->_request->getPost('action'))
{
/*  переименование автора лекции */
case 'rename':
if ($this->_request->getPost('author') == '')
{
$author = "Гость";
}
else
{
$author = $this->_request->getPost('author');
}
$subject->updateAuthorPrint($this->_request->getPost('printId'),
$this->_request->getPost('author'));
break;

/*  удаление экземпляра лекции */
case 'delete':
$id=$subject->getSubjIdByLectPrintId(
$this->_request->getPost('printId'));
$subject->deleteLectionPrint($this->_request->getPost('printId'));                                                 exit;                                                   
break;

/*  замена файла с последующим конвертированием*/
case 'replace':
/*  создание объекта класса  supportFile */
$supportFile = new supportFile();
$i = false;
foreach ($supportFile->print as $key => $val)
{
/*  проверка условия: входит ли разрешение загруженного
файла   в список поддерживаемых файлов                 */
if (in_array($_FILES['file']['type'], $val))
{
$i = $key;                  
}
}
/*  если загруженный файл входит в поддерживаемый тип    выполняется
процесса конвертирования и  записи изменений в БД                       */
if ($i !== false)
{
$id = $this->_request->getPost('printId');
$ext = $supportFile->print[$i]['ext'];
$subjectId = $subject->getSubjIdByLectId(
$this->_request->getParam('lectionId'));
/*  установка директории и названия файла */
$uploadfile = $patchToDumpUpl.$subjectId.'_'.$this->
_request->getParam('lectionId').'_print_'.$id.'.'.$ext;
/*  перемещение загруженного файла из директории временного
хранения   файлов(указанного в настройках PHP) в директорию
значение которой проинициализировано переменной $uploadfile */
if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile))
{
/* инициализация двух выходных параметров
программы «Minetext»                            */
$in = " ".$uploadfile;
$out = ' '.$patchToDumpUpl.$subjectId.'_'.$this->
_request->getParam('lectionId').'_print_'.$id.'.txt';
/* запуск приложения «minetext» */
exec($patchToConverter."minetext.exe".$in.$out, $error);
$subject->updateTypePrint($id, $_FILES['file']['type']);
}
}
break;
}
}
Редактирование текстового варианта лекции предполагает загрузку клиенту исходного варианта лекции с последующим редактированием и повторной загрузки файла на сервер.
Исходный вид шаблона для отображения текстового варианта лекций представлен на рисунке 4.18.
Внешний вид верстки страницы
Рисунок 4.18. – Редактирование лекции текстового варианта.

Для предоставления исходных файлов в текстовый вид был применен консольный конвертор «Minetext». Программа «Minetext» бесплатна для распространения, имеет открытые исходные коды, существуют дистрибутивы для ОС Linux и Windows. Программа имеет два параметра, директории входного и выходного файлов(см. листинг 4.14). Средствами PHP запускается системным вызовом exec().
В связи с тем что «Minetext» работает не со всеми форматами файлов и разработанный проигрыватель мультимедийных файлов воспроизводит только определенные форматы, был разработан класс supportFile , который на момент загрузки файлов определял форматы, поддерживаемые системой. В листинге 4.15 определены форматы для текстовых файлов(Microsoft office, Adobe Acrobat), для видео файлов(Flash Vidoe), аудио(mpeg Layer 3), а для прикрепляемых файлов доступны любые разрешения.
Листинг 4.15 – Поддерживаемые типы файлов.
class supportFile
{
public $text = array(
array(  'description' => 'Microsoft office',
'type' => 'application/msword',
'ext' => 'doc'),
array(  'description' => 'Adobe Acrobat',
'type' => 'application/pdf',
'ext' => 'pdf')
);
public $video = array(
array(  'description' => 'Flash Vidoe',
'type' => 'octet-stream',
'ext' => 'flv')
);

public $audio = array(
array(  'description' => 'mpeg Layer 3',
'type' => 'audio/mpeg',
'ext' => 'mp3')
);

public $addition = array(
array(  'description' => 'Все форматы',
'type' => '*',
'ext' => '*')
);                                            
}
В случае необходимости добавления новых поддерживаемых типов файлов(модернизация конвертора, добавления новых кодеков воспроизведения), достаточно будет добавить новый массив в метод объекта supportFile.
Редактирование видео, аудио, прикрепленных файлов выполнено по аналогии. Администратору предоставляется возможность переименовать поле «Автор», скачать, заменить, а также удалить файл.
Внешний вид верстки страницы
Рисунок 4.19. – Исходный вид страницы редактирования видео лекции.

В разделе администрирования тестовых вопросов представлен список курсов(действие testAction контроллера adminController). Выбрав нужный курс, управление передастся viewtestAction(листинг 4.15).
Листинг 4.15. – действие viewtestAction
            function viewtestAction()
            {
                        $subject = new Subject();
                        $this->view->subjectList = $subject->subjectList();
/*  обработка входных переменных  */
                        $id = (int)$this->_request->getParam('id');
                        if ($id != 0)
                        {
/* вывод из бД вопроса и вариантов ответа
   согласно   идентификатору курса $id      */
                                   $sub = $subject->subjectId($id);
                                   $this->view->title = "Администрирование > Тесты > " .
$sub->subject;
                                   $this->view->subject = $sub->subject;
                                   $this->view->subjectId = $id;
                                   $this->view->questions = $subject->getAllQuestionBySubjectId($id);
                                   $this->view->questions_variants =
$subject->getAllQuestionVariantBySubjectId($id);
                        }
                        else
                        {
                                   $this->view->title = "Администрирование > Тесты";                       
                        }
            }
        
Редактирование вопросов осуществляется за счет событий JavaScript.
Для редактирования названия вопроса разработан следующий механизм:
При нажатии курсором на название вопроса, средствами JavaScript текст заменяется на текстовое поле для ввода с содержанием названия вопроса.
Внешний вид страницы
При нажатии на название вопроса, текст заменяется на поле для ввода.
Внешний вид страницы
Рисунок 4.20 – Редактирование названия вопроса

Редактирование вариантов ответа происходит по аналогии с редактированием названия вопроса(рисунок 4.21). Выбор правильного варианта ответа (HTML тег input тип элемента radio) и выбор сложности  вопроса (HTML ниспадающее меню select) сопровождается отправкой xmlHttpRequest запроса, вносящего изменение на сервере. Таким образом, редактирования происходит прозрачно для пользователя.
Внешний вид страницы
При нажатии на текст варианта ответа, текст заменяется на поле для ввода.
Внешний вид страницы
Рисунок 4.21 – Редактирование вариантов ответа

На событие «onBlur»(потеря фокуса) происходит отправление  серверу внесенных изменений. Поле ввода опять принимает прежний текстовый вид.
Текст используемых JavaScript функций, обеспечивающих редактирование вопросов, представлен в листинге 4.16.

 Листинг 4.16. – JavaScript функции, обеспечивающие редактирование тестов.
/* функция замены текстовой надписи на поле для ввода,
параметр функции – идентификатор  вопроса             */
function updateQest(i)
{
document.getElementById('question'+i).innerHTML =
'<input type=text size=70 onBlur=updateQuestionReturn('+i+',this) value="'+document.getElementById('questionspan'+i).innerHTML+'">'
}
/* функция замены текстовой надписи на поле для ввода,
параметры функции – идентификатор  вопроса и значение поля ввода */
function updateQuestionReturn(i, t)
{
s = t.value
document.getElementById('question'+i).innerHTML = '<span id="questionspan'+i+'" onClick="updateQest('+i+')">'+t.value+'</span>';
/*  отправка внесенных изменений на сервер */
updateQuestion('uq', s, i);
}
/*  функция, обеспечивающая появление формы ввода для нового варианта ответа */
function setField(i)
{
/*  проверка  текущего состояния поля ввода */
if ( document.getElementById('addAnswer'+i).innerHTML == '')
{
document.getElementById('addAnswer'+i).innerHTML = '<input type=text size=85
id=\'field'+i+'\' onBlur=addVariant('+i+')>';
document.getElementById('addAnswerBut'+i).value = '-';
}
else
{
document.getElementById('addAnswer'+i).innerHTML = '';
document.getElementById('addAnswerBut'+i).value = '+';
}
}

/*  функция создания нового варианта ответа */
function addVariant(i)
{
s = document.getElementById('field'+i).value;
if (s != '')
{
updateQuestion('a', s, i);
}
}

/* функция установки правильного ответа */
function setAnsw(s, i)
{
id = document.getElementById('version'+i);
id.innerHTML = s;
updateQuestion('u', s, i);
}

/* функция отправки xmlHttpRequest  запроса на сервер входные
данные: тип  изменения, номер вопроса, номер ответа           */
function updateQuestion(t, s, i)
{
/* контроллер обработки Ajax запроса */
var requestUrl = '/admin/updatequstion/';
try
{
var request = new Ajax.Request(
requestUrl,
{
method: 'post',
parameters: 't='+t+'&i='+i+'&s='+s
}
);
}
catch (e)
{
alert('Error: ' + e.toString());
}
}
</script>        

Добавление нового варианта ответа происходит после нажатия на соответствующую кнопку «+», после чего появляется форма для ввода нового варианта ответа(рисунок 4.22).
Внешний вид страницы
Рисунок 4.22 – Добавление нового варианта ответа
Все отсылаемые запросы на сервер с помощью xmlHttpRequest
На рисунке 4.23 изображен пример списка тестов.
Внешний вид страницы
Рисунок 4.23.  – Шаблон редактирования списка вопросов.
После потери фокуса функция updateQuestion отсылает на сервер внесенные изменения.
Обработка переданного запроса на сервере осуществляется посредством передачи переменных на updatequstionAction().

Листинг 4.17. Обработка Ajax запросов редактирования тестов.
function updatequstionAction()
{
$subject = new Subject();
/*  инициализация входных переменных */
$id = (int) $_REQUEST['i'];
$s = $_REQUEST['s'];
$t = $_REQUEST['t'];
if ($t == 'aq')
{
if ($s != "")
{
/* добавление  вопроса  */
$subject->insertQuestion($s,$id);
/* возврат true обозначает, что страницу необходимо
перегрузить  после отправленного запроса            */
echo 'true';
}
}                     

}

Возвращаемый результат «true» и «false» указывают на необходимость перегрузки страницы, поскольку в некоторых случаях перестроить страницу на JavaScript трудоемко с технической точки зрения.
Контроллер testController предназначен для предоставления возможности прохождения тестов. После того как пользователь авторизировался ему становятся доступны тесты. Перейдя по ссылке тестов
пользователь попадет на страницу с тестами, в конце всех тестов предусмотрена   кнопка отправки формы на сервер (рисунок 4.24).
Внешний вид страницы
Рисунок 4.24. – Шаблон test.phtml
Данные отправляются на testController, управление передается checkresultAction(листинг ).
Листинг 4.18. – функция сопоставление проверки результата тестирования
function checkresultAction()
{
$subject = new Subject();                 
$id = (int)$this->_request->getParam('subject');
$userid = (int)$this->_request->getParam('userid');
$user = (int)$this->_request->getFIObyUserId('userid');

$index = $subject->getIndex ($testId);
$result = 0;
foreach ($this->_request->getParam(arrayResult) as $val)
{
If ($p = $subject->getIsTrue ($val))
{
$result += $p;
}
}
$subject->saveResult ($testId, $userId, $result);
$this->view->result= $subject->getResultTestIdByUserId ($id, $userid);
}          

         Создание тестов подразумевает выбор предмета и выбор лекций, вопросы которых будут включены в тест. Связи с этим был разработан модуль создания тестов, интерфейс которого представлен на рисунке 4.25.
Внешний вид страницы
Рисунок 4.25. – Интерфейс вывода списка тестов в теме модуля организации тестов
В модуле организации тестов существует три действия, одно из которых (testlistAction) выводит список созданных тестов в курсе, а второе (testeditAction) выполняет создание нового теста и прикрепление к нему тем(где в теме содержатся вопросы), третье действие(resulttestAction) выводит статистику прохождения тестов.
Действие testlistAction контроллера adminController() выводит список тем в выбранном курсе(листинг 4.19).
function testlistAction()
{
$subject = new Subject();
$this->view->subjectList = $subject->subjectList();
$id = (int)$this->_request->getParam('subject');
if ($id != 0)
{
$sub = $subject->subjectId($id);
$this->view->title="Администрирование > Создание тестов > " . $sub->subject;
$this->view->subject = $sub->subject;
$this->view->subjectId = $id;
$this->view->testlist = $subject->getTestListBySubjectId($id);
}
else
{
$this->view->title = "Администрирование > Создание тестов";
$this->view->noSubject = "Выберите курс";
}
}
Листинг 4.19 – Действие testlistAction выводит список тестов согласно выбранному предмету.

Выбрав нужный тест активируется действие testeditAction(), которое выполняет обзор  выбранных тем в тесте. В появившейся шаблоне, который соответствует этому действию представлена форма, на которой с помощью HTML элементов ввода типа checkbox можно выбрать те лекции, вопросы которых будут включены в тест. Также можно переименовать название, или удалить тест (рисунок 4.26).
Внешний вид страницы
Рисунок 4.26. – Шаблон edittest.phtml действия edittest.

Все действия над тестами выполняет testeditAction(). При удалении теста также происходит удаление тем из таблицы(test_lection) в БД, хранящей список лекций в удаляемом курсе. Действие testedittAction() не содержит шаблона.
function testeditAction()
{
$subject = new Subject();  
if($this->_request->isPost())
{
/* удаление теста */
$id = $this->_request->getPost('testId');
if ($this->_request->getPost('deleteTest') == true)
{
$subject->deleteTestById($id);
header("location: /admin/testlist/");
exit;
}
/* обновление названия теста*/
$subject->updateTestById($id, $this->_request->getPost('value'));

/* удаление всех тем, прикрепленных к тесту*/
$subject->removeAllLection($id);
$i = false;
foreach ($supportFile->print as $key => $val)
{
/*проверка – выбрана ли тема в курсе*/
if (($this->_request->getPost('lection')+$i) == true)
{

/* добавление выбранных тем */
$subject->addTestLection($id, ($this->_request->getPost('lection')+$i));
}
}
header('location: /admin/testlist/subject/'.$id);
}
}
Листинг 4.23 – Действие testeditAction, выполняющее основные операции над тестом.

Вывод результатов тестирования выполняет действие testresultAction. Число баллов оцениваются как сумма, произведений вопросов на их индекс сложности, деленная на сумму правильных ответов умноженных на индекс сложности (рисунок 4.27).
Внешний вид страницы
Рисунок 4.27 – Отображение результата тестирования
В листинге 4.24 представлено действие testresultAction, которое выполняет выборку из БД на шаблон testresult.phtml.

 

Листинг 4.24. – Вывод результата тестирования.
function testresultAction()
{
$subject = new Subject();
$this->view->subjectList = $subject->subjectList();

$id = (int)$this->_request->getParam('testId');
if ($id != 0)
{
$test = $subject->test($id);
$sub = $subject->subjectId($id);
$this->view->title = "Администрирование >  " . $sub->subject . " > " . $sub->test;                      $this->view->subject = $sub->subject;
$this->view->subjectId = $id;
/*извлечение массива результатов*/
$this->view->resultTest = $subject->getTestResult($id);
}
}
Для пользователя Администратор предусмотрено действие adminAction контроллера adminController. В этом действии происходит проверка метода перехода на страницу, если метод GET, значит нужно вывести список пользователей, зарегистрированных в системе, если POST, то произвести выбранное действие над пользователем. На рисунке  4.28 изображен шаблон admin.phtml.
Внешний вид страницы
Рисунок 4.28. – Шаблон вывода списка пользователей
В листинге 4.25. выполняется удаление, перемещение групп пользователей.
Листинг 4.25. – действие adminAction контроллера adminController.
function adminAction()
{
$subject = new Subject();
if($this->_request->isPost())
{
$id = $this->_request->getPost('userId');
if ($this->_request->getPost('deleteUser') == true)
{
$subject->deleteTestById($id);
header("location: /admin/");
exit;
}
if ($this->_request->getPost('changeGruopUser') == true)
{
$subject->addUserToPrepod($id);
}
header('location: /admin/');
}
else
{
$this->view->title = "Список пользователей";
$this->view->userlistPrepod = $subject->getUserListPrepod();
$this->view->userlistStud = $subject->getUserListStud();
}
}

СевНТУ 2008г. автор работы Прищак Сергей
Hosted by uCoz