У вас вопросы?
У нас ответы:) SamZan.net

СОЗДАНИЕ СИСТЕМЫ МОНИТОРИНГА УРОВНЯ ЗНАНИЙ

Работа добавлена на сайт samzan.net:

Поможем написать учебную работу

Если у вас возникли сложности с курсовой, контрольной, дипломной, рефератом, отчетом по практике, научно-исследовательской и любой другой работой - мы готовы помочь.

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 28.12.2024

МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ

Федеральное государственное бюджетное образовательное учреждение

высшего профессионального образования

«КУБАНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»

(ФГБОУ ВПО «КубГУ»)

Физико-технический факультет

Кафедра теоретической физики и компьютерных технологий

Допустить к защите в ГАК

_____ . ____ . 2013 г.

Заведующий кафедрой

д-р физ. мат. наук, профессор

_______________Е. Н. Тумаев

ДИПЛОМНАЯ РАБОТА

СОЗДАНИЕ СИСТЕМЫ МОНИТОРИНГА УРОВНЯ ЗНАНИЙ

Работу выполнил   Елькин Михаил Николаевич

Специальность 230201 – Информационные системы и технологии

Научный руководитель        Значко В.Н.

Нормоконтролер

канд. физ.-мат. наук, доцент  А. А. Мартынов

Краснодар 2013


РЕФЕРАТ

Елькин М.Н. СОЗДАНИЕ СИСТЕМЫ МОНИТОРИНГА УРОВНЯ ЗНАНИЙ. Дипломная работа: 32 с., 11 рис., 13 источников, 1 приложение.

СИСТЕМЫ СОЗДАНИЯ И ПРОХОЖДЕНИЯ ТЕСТОВ, СИСТЕМЫ УПРАВЛЕНИЯ БАЗАМИ ДАННЫХ, WEB-СЕРВЕР

Объектом разработки дипломной работы является система, предназначенная для сбора данных о результатах тестирования, состоящая из модулей создания и прохождения тестов, модуля назначения и модуля просмотра статистики.

Целью работы является разработка системы, способной хранить и обрабатывать большие объемы статистических данных с результатами тестов.

В результате выполнения дипломной работы спроектирована и разработана база данных для хранения статистики. Так же разработаны модули прохождения и создания тестов, и модуль просмотра статистических данных  по результатам тестирования.


Содержание

[1]
Введение

[2]
1. Постановка задачи. Анализ существующих решений

[2.1] 1.1 Постановка задачи

[2.2] 1.2 Анализ существующих решений

[2.2.1] 1.2.1 UniTest System 4

[2.2.2] 1.2.2 MyTest

[2.2.3] 1.2.3 Moodle

[2.3] 1.3 Требования к создаваемой системе

[3] 2. Выбор и обоснование решения

[3.1] 2.1 Описание общей архитектуры системы

[4]
3. Описание инструментов и методов реализации

[4.1] 3.1 Конвенции программирования для системы

[4.2] 3.2 Конвенции разработки Firebird

[4.3] 3.3 Конвенции разработки ZOPE

[4.4] 3.4 Описание архитектуры базы

[4.5] 3.5 Методы разработки веб-интерфейсов

[4.6] 3.6 Обработка данных при помощи python скриптов

[4.7] 3.7 Создание хранимых процедур в Firebid

[5]
4. Описание интерфейса разработанной системы

[5.1] 4.1 Модуль создания тестов

[5.2] 4.2 Модуль прохождения тестов

[5.3] 4.3 Модуль назначения тестов

[5.4] 4.4 Модуль просмотра статистики

[6]
Заключение.

[7]
Список используемых источников

[8]
Приложение А Исходный код системы


Введение

Системы, используемые для создания и прохождения тестов, обладают отличными инструментами для создания и прохождения созданных тестов. Но сбор статистики в таких системах является дополнительным функционалом, возможности которого не развиваются. Работа направлена на создание системы для сбора и дальнейшего анализа результатов тестирований. Использование системы позволит получать расширенные наборы статистических данных, и применять к ним методы системного анализа.

Работа содержит описание  и анализ существующих систем для мониторинга уровня знаний. Так же в работе присутствует обоснование архитектурного решения, для разрабатываемой системы, и само решение, краткое описание инструментов разработки. В работе описана структура приложения, а так же основные приемы программирования используемые при реализации системы.


1. Постановка задачи. Анализ существующих решений

1.1 Постановка задачи

Сейчас существует множество программ для создания и прохождения тестов, но большинство из них не обладает полноценными инструментами по сбору, хранению, и обработке статистики, полученной при прохождении тестов.

Создание системы мониторинга уровня знаний позволит собирать подробную статистику по результатам тестирований студентов за длительный период. Помимо основных статистических данных, таких как результат тестирования и время затраченное на прохождение теста, система будет содержать данные, о времени ответа на каждый вопрос, средние баллы получаемые за тест и процент правильных ответов на вопрос. Все вопросы и тесты в базе будут разбиты по тематике, что позволит при анализе результатов эффективно выделять темы с которыми у учащихся возникают проблемы. При использовании такой системы длительное время на основе собранных ею данных, можно проводить анализ уровня подготовки студентов разных годов выпуска.

Система состоит из четырех основных модулей.

  •  Модуля для создания и редактирования тестов.
  •  Модуля для назначения тестов учащимся.
  •  Модуля прохождения тестов.
  •  Модуля получения статистики по результатам тестов.

1.2 Анализ существующих решений

Сейчас на рынке программного обеспечения существует множество программных продуктов для мониторинга уровня знаний с различной архитектурой и сферой применения. Для анализа выберем три наиболее распространенные системы, такие как UniTest System 4, MyTest, Moodle. И подробнее познакомимся с этими системами.

1.2.1 UniTest System 4

UniTest System 4 решение для создания компьютерных тестов, проведения тестирования (как локально, так и по сети), детального анализа результатов тестирований и составления отчетов.

Система состоит из нескольких утилит Editor, Monitor, Report, Setting, Test.

Утилита Editor предназначен для создания и редактирования тестов, используемых для проведения тестирований в утилите Test. [1]

Утилита Test предназначена для прохождения тестов в созданных через утилиту Editor.

Утилита Report служит для получения информации по тестам, на рисунке 1 показан пример отчета созданного через Reporter

Утилита Monitor предназначен для администрирования и мониторинга процесса тестирования в сетевой версии пакета. В сетевой версии утилиты Test являются клиентами, которые подключаются к утилите Monitor и через нее проводят тестирование. Monitor контролирует все действия Test'ов и может разрешать или запрещать подключение отдельных клиентов (Test'ов) или целых групп, отключать или блокировать подключение уже подключенных клиентов. По каждому подключенному клиенту можно посмотреть подробную информацию (статус подключения, информация о тестируемом и о состоянии тестирования на текущий момент).

Settings позволяют настроить работу пакета UniTest System в целом и его отдельных компонентов. Можно установить пути к базам данных собст, используемым по умолчанию, и указать пароли к ним. Можно задать поля ввода данных о тестируемом, которые будут отображаться при тестировании. Здесь же можно настроить разбалловку тестов и внешний вид Test. [1]

Недостатком системы является хранение данных бинарном файле, что замедляет доступ к ним при работе с большими объемами данных, также это делает невозможным получение группированных по различным параметрам данных. Так же недостатком является отсутствие целостности статистики. Каждый запущенный модуль Monitor пишет информацию в свой отдельный файл, который в дальнейшем придется компоновать с другими файлами результатов, для получения целостной картины. В противном случае это означает, что модуль Monitor  одновременно может быть запущен только на одном компьютере и что только один человек сможет быть администратором.

Рисунок 1 - Пример созданного отчета в системе

UniTest System через модуль Reporter

1.2.2 MyTest

MyTest система программ для создания и проведения компьютерного тестирования, сбора и анализа их результатов, аналогичная UniTest System 4 состоит из трех модулей: Модуль тестирования (MyTestStudent), Редактор тестов (MyTestEditor) и Журнал тестирования (MyTestServer).

Редактор тестов (MyTestEditor) модуль для создания и редактирования тестов, для каждого теста создается бинарный файл

Модуль тестирования (MyTestStudent) предназначен для работы с бинарными файлами, созданными в редакторе тестов.

Журнал тестирования (MyTestServer) модуль программы MyTest, позволяющий централизовано принимать и обрабатывать результаты тестирования, раздавать тесты посредством компьютерной сети. Для отправки и получения результатов, отправки файлов с тестами используется протокол Интернета TCP/IP. На рисунке 2 форма для просмотра результатов в журнале тестирования MyTestServer.

Недостатком системы является хранение данных бинарном файле, что замедляет доступ к ним при работе с большими объемами данных, также это делает невозможным получение группированных по различным параметрам данных. Так же недостатком является отсутствие целостности статистики. Каждый запущенный модуль Monitor пишет информацию в свой отдельный файл, который в дальнейшем придется компоновать с другими файлами результатов, для получения целостной картины. В противном случае это означает, что модуль Monitor  одновременно может быть запущен только на одном компьютере и что только один человек сможет быть администратором.

Рисунок 2 - Результаты  тестов в MyTestServer.

1.2.3 Moodle

Moodle — система управления курсами (электронное обучение), также известная как система управления обучением или виртуальная обучающая среда. Представляет собой свободное (распространяющееся по лицензии GNU GPL) веб-приложение, предоставляющее возможность создавать сайты для онлайн-обучения. [7]

В Moodle так же есть возможность создавать тесты и получать статистику. При прохождении тестов статистика пишется в подключенную СУБД, но записываются не весь набор данных, который нас интересует. Так же у Moodle сложный, относительно других рассмотренных систем, процесс создания тестов. На рисунке 3 изображен модуль отчетов moodle

Рисунок 3 - Модуль отчета в moodle

1.3 Требования к создаваемой системе

После анализа существующих систем можно сделать вывод о том, какими свойствами должна обладать создаваемая система.

  1.  Кроссплатформенность. Система должна работать под управлением с различных ОС. И не должны вызывать сложностей для работы с системой с различных устройств.
  2.  Работа по сети. Система должна поддерживать работу с сетью, для сбора всех данных в единственный банк данных, так позволит работать в системе удаленно, к примеру находясь у себя дома.
  3.  Совместимость с уже используемой в КубГУ системой Moodle. Система должна быть легко встраиваемой в существующую архитектуру и не должна вызывать дополнительных затрат на интеграцию.
  4.  Обеспечение быстрого доступа к статистическим данным. Для хранения и получения данных следует использовать СУБД, что позволит хранить и обрабатывать большие объемы жанных.
  5.  Обеспечение безопасности доступа к данным.


2. Выбор и обоснование решения

Рассмотрим подробнее способы достижения требований изложенных в прошлой главе. Первые три пункта кроссплатформенность, работу по сети и совместимость с moodle можно выполнить, реализовав систему в виде web-приложение. Система будет доступна с любого устройства, у которого есть доступ к сети. Для четвертого пункта необходимо выбрать СУБД. Среди некоммерческих СУБД, можно выделить две это PostgreSQL и Firebird. Для своей системы я выбирал Firebird т.к. это простая в эксплуатации и разработке СУБД.

Firebird -  реляционная СУДБ, обладает многоверсионной архитектурой, обеспечивающей параллельную обработку оперативных и аналитических запросов (это возможно потому, что читающие пользователи не блокируют пишущих), компактность (дистрибутив 5Mb), высокую эффективность и мощную языковую поддержку для хранимых процедур и триггеров. [3][4] СУДБ легко работает с большими базами данных. Среди минусов можно отметить отсутствие кеша результатов запросов и отсутствие полнотекстовых индексов.

Основные характеристики Firebird

  •  Полная поодержка хранимых процедур и триггеров.
  •  Транзакции, полностью совместимые с концепцией ACID.
  •  Ссылочная целостность.
  •  Версионная архитектура.
  •  Очень небольшой размер.
  •  Мощный внутренний язык для написания хранимых процедур и триггеров (PSQL).
  •  Поддержка внешних пользовательских функций (UDF).
  •  Firebird практически не требует работы системного администратора или позволяет свести ее к минимуму.
  •  Почти не требует настройки - использовать СУБД можно сразу же после ее установки!
  •  Огромное интернет-сообщество пользователей и разработчиков, множество мест, где вы можете получить быструю и бесплатную помощь.
  •  Возможность распространения встроенной в приложение (embedded) версии - замечательно подходит для создания каталогов на CD-ROM, однопользовательских и пробных версий программ.
  •  Десятки специализированных приложений от сторонних разработчиков, включая средства администрирования, репликации, и так далее.
  •  Безопасная запись данных (careful write) - быстрое восстановление после сбоев, отсутствие необходимости в журналировании транзакций!
  •  Большое количество средств доступа к базе данных: native/API, драйверы dbExpress, ODBC, OLEDB, .Net provider, JDBC-драйвер, модули для Python, PHP, Perl, и так далее.
  •  Поддержка большинства распространенных операционных систем, включая Windows, Linux, Solaris, MacOS.
  •  Инкрементные бэкапы
  •  Билды для 32- и 64-разрядных ОС
  •  Полная реализация курсоров в PSQL
  •  Таблицы мониторинга
  •  Триггеры на коннект и транзакции
  •  Временные таблицы [9]

В качестве web-сервера будет использоваться Zope 2, это объектно-ориентированная платформа, сервер приложений, написанный на языке python, предназначенный для создания динамических web-приложений и интерактивных сайтов.

Он был выбран мной в качестве web-сервера так как протоколы WWW (HTTP, CGI и т.д.) часто неадекватны задачам и могут делать публикацию динамических данных неоправданно сложной. Их низкий уровень недостаточен для непосредственного создания многих классов web-приложений на их основе. Zope создает объектно-ориентированную оболочку вокруг этих низкоуровневых средств. С его помощью решение задачи происходит обычным путем - программист пишет набор иерархий классов, являющийся абстракцией предметной области, а Zope берет на себя труд по предоставлению доступа к экземплярам этих классов. [5]

Zope предоставляет программистам и администраторам простые, и в то же время мощные и гибкие механизмы управления безопасностью. Безопасность в Zope стоит на трех китах, трех базовых понятиях - пользователь, роль, и вид доступа, что удовлетворяет последнему пункту требований.

Вид, или тип доступа определяет программист при создании компонента. Каждому классу в компоненте определяется полномочие "Add" ("Добавить экземпляр класса в дерево объектов"), каждому методу класса можно определить свои собственные полномочия, которые определят, кому и какой вид доступа предоставлен к этому методу класса. Например, методу index_html (который вызывается при обращении к объекту, а не к конкретному методу) обычно дается вид доступа View. Но это дело программиста, как назвать свои полномочия, и какие методы какими полномочиями защитить. Обычно методы объекта объединяются в группы, предоставляющие один сервис. Например, класс Новостная Лента может иметь сервисы (группы методов) "показ новостей", "добавление новостей", "редактирование новостей", "удаление новостей", "добавление/редактирование/удаление рубрик". И каждый из сервисов можно защитить (дав ему отдельный вид доступа) - с точностью до одного метода. Для более тонкого управления, уже внутри метода, программист может запросить Security Manager - "имеет ли текущий пользователь права на создание DTML Методов в Папке Razdel?"

Роли создает администратор сайта через менеджерский web-интерфейс Zope. Понятие роли распространяется не на весь сайт, не на ZODB, а на часть дерева. Администратор создает роль в какой-то папке, и дальше благодаря механизму acquisition эта роль распространяется вниз по поддереву.

Zope, поставленная из дистрибутива, имеет 3 роли, определенные в корне ZODB - Anonymous, Owner и Manager. Manager - это такой всесильный администратор, аналог рута. Owner - владелец тех ресурсов, которые он создал. Анонимный пользователь - просто посетитель сайта; ему изначально доступны типы доступа: Access content, View, Use SQL Methods (это для того, чтобы позволить вызывать SQL Методы из DTML Методов) и Search ZCatalog.   Администратор сайта в дальнейшем может создавать новые роли, как в корне, если у самого администратора есть права на редактирование корня, так и в любых поддеревьях, на которые у администратора есть права.

Большинство кода Zope реализована на языке python, за исключением небольшого числа модулей написанных на C для повышения производительности. Python является многоцелевым, объектно-ориентированным языком программирования. Python представляет собой полноценный язык программирования, используемый для создания приложений любого типа. В отличии от PHP который ориентирован на разработку web-приложений. Богатый набор дополнительных модулей может быть использован для создания веб-приложений на основе Python. Python является интерпретируемым языком и ориентирована на выполнение "сценариев" в первую очередь. Это не означает, что Python приложение не может быть большими и сложными. Используя Python можно создавать сложные приложения. Установочный пакет Python содержит богатый набор модулей, которые охватывают большую часть функционала, необходимого типичному разработчику. Дополнительные модули могут быть загружены и установлены из хранилища индекса PyPI. PyPI содержащего более 14 000 дополнительных сторонних модулей (по состоянию на май 2011 г.).

Python прост в освоении. Синтаксис и концепции Python очень легко узнать и понять. Средний разработчика, как правило, в состоянии изучить основы в течение дня или двух. Код python обычно  читаемый и понятный (например, по сравнению с Perl).[10]

Для динамического построения страниц из шаблонов в Zope применяется язык TAL

TAL является системой шаблонов Zope Page Template (ZPT). TAL поддерживает пространства имен TAL, METAL и I18N.

TALES является эквивалентом TALES, Template Attribute Language Expression Syntax (синтаксис атрибутивного языка шаблонов). Он определяет, как обрабатываются значения атрибутов XML.

Так как PTALES подобен TALES, TAL шаблоны python и PHP могут одинаково использоваться и транспортироваться из одного языка в другой.

Чтобы быть совместимым с PHPTAL, TAL реализует XPath-подобный доступ к данным.[6]

Для подключения к Firebird используется продукт kinterbasDA. Продукты - компоненты, написанные программистом на Питоне - позволяют дополнять Zope новыми типами объектов. В нашем случае это драйвер для работы с СУБД. Он позволяет создавать параметризированные SQL методы. Вызывать методы можно непосредственно на странице, используя TAL шаблон, или в скрипте, написанном на языке Python.

2.1 Описание общей архитектуры системы

Система состоит из формы авторизации и четырех модулей.

  •  Модуль создания тестов
  •  Модуль назначения тестов
  •  Модуль прохождения тестов
  •  Модуль просмотра статистики

Каждый из модулей использует свой собственный коннект под отделенной для модуля ролью, с определенным набором прав и разрешений.

На следующем рисунке представлено схематическое строение модулей.

Рисунок 4 - Общая архитектура модулей.

Архитектура всех модулей однотипная, модуль состоит из одного или нескольких форм-шаблонов, передающих данные скриптам, написанным на языке python. Скрипты обрабатывают данные, после чего обращаются в базу, вызывая хранимые процедуры, в которых содержится логика модуля. В зависимости от результатов возвращаемых из базы в скрипте принимается решение о переходе на ту или иную форму. Данные для формы так же подгружаются из базы.


3. Описание инструментов и методов реализации

3.1 Конвенции программирования для системы 

В высококачественном программировании должна быть очевидна связь между концептуальной целостностью архитектуры и её низкоуровневой реализации. Реализация должна соответствовать высокоуровневой архитектуре и обладать внутренней согласованностью. В этом заключается смысл принципов конструирования, определяющих конвенции именования переменных, классов, методов, а так еж форматирования кола и оформления переменных.

При разработке сложной программы архитектурные принципы вносят в программу структурный баланс, а принципы конструирования – низкоуровневую гармонию, при наличии каторой каждый класс определяется как неотъемлемая часть общего плана. Любая крупная программа требует применения контролирующей структуры, унифицирующей аспекты языка программирования. Красота крупной структуры частично заключается в том, как в её отдельных компонентах выражены особенности архитектуры. Без унификации программа будет смесью небрежных вариаций стиля, заставляющих прилагать дополнительные усилия только для того, чтобы различия в стиле кодирования, которые вполне можно было избежать. Одно из условий успешного программирования – устранение ненужных вариаций, позволяющее сосредоточиться на днйствительно необходимых вариациях. [11]

3.2 Конвенции разработки Firebird

  1.  Имена всех объектов должны отражать способ их использования.
    1.  Имена генераторов состоят из буквы «G» нижнего подчеркивания и наименования первичного ключа для генерации чьего значения используется генератор. Имена генераторов используемых для других целей начинаются с «GEN_» далее используется любое имя отражающее назначение генератора.
    2.  Имена триггеров имею формат «TR_<ИМЯ_ТАБЛИЦЫ>_<Время срабатывания>_<Доплнительная_информация>». Где Время срабатывания буквы BI (перед вставкой), AI (после вставки), BU(до обновления), AU(после обновления), BD(до удалени), AD(после удаления).
    3.  Имена таблиц, используемых для хранения данных, начинаются с префикса «S_», тем самым обозначая, что таблицы используются для статического хранения данных. Таблицы, используемые для хранения справочников, маркируются префиксом «L_». Таблицы, хранящие часто изменяемые данные, обозначаются префиксом «D_».
    4.  Имена хранимых процедур начинаются с префиксов «REP_» и «W_».
  2.  Префикс «REP_» обозначает, что процедура производит лишь чтение данных. Префиксом «W_» маркируются процедуры производящие изменения в БД.
  3.  Типы данных в таблицах описываются строго прописанными доменами.
  4.  Записи из таблиц не удаляются, для пометки записи как удаленная используется поле статус, которое заполняется подходящим значением из таблицы статусов записей.
  5.  В каждой таблице в качестве первичного ключа используется целочисленное поле.
  6.  В каждой таблице существуют два поля, хранящие информацию о дате последнего изменения записи и о пользователе, который последним производил изменение, поля имеют фиксированные имена LASTDATE и USRNAME.
  7.  Всем триггерам, работающим с одной таблицей и имеющим одинаковое время срабатывания, присваивается уникальный приоритет кратный 100.
  8.  Наименования входных переменных должно начинаться с префикса «IN_» выходных - «OUT_». Внутренние переменные начинаются с префикса «V_».

3.3 Конвенции разработки ZOPE

  1.  Все ZSQL методы должны именоваться как процедуры, которые они вызывают. Если процедура не вызывается, или запрос включает в себя подзапросы или связи то имя метода начинается с префикса «SQL_» после чего следует имя метода, описывающее суть метода.
  2.  Каждый шаблон (PageTamplate), кроме шаблонов расположенных в корне, имеет уникальный префикс каталога, в котором он находится, шаблоны из корневого каталога имею префикс «r_».
  3.  Скрипты языка python, вызываемые по отправке данных с формы должны иметь имя «action_» + <имя формы, с которой был вызван скрипт>. Остальные скрипты должны иметь префикс «S_»
  4.  Каждый модуль использует свой собственный коннект к базе.
  5.  Имена переменных передаваемых в скрипт, с формы и наоборот должны совпадать. Однотипность имен позволяет быстрее вести разработку и уменьшает вероятность ошибок, связанных с путаницей в именах переменных.

3.4 Описание архитектуры базы

База состоит из 10 основных таблиц.

Таблица пользователей L_USERS в которой храниться информация о пользователях. Таблица состоит из следующих полей, целочисленного идентификатор записи (первичный ключ), ФИО сотрудника, статус записи, внешний ключ связывающий таблицу с таблицей групп пользователей L_USER_GROUP, логин, зашифрованный пароль, даты последнего изменения записи, имени последнего пользователя изменявшего запись.

Таблица групп пользователей L_USER_GROUP состоит из целочисленного идентификатора записи (первичный ключ), наименования группы, уникального символьного кода группы, полей с разрешениями на модули для каждой группы, даты последнего изменения записи и имени последнего пользователя редактировавшего запись.

Таблица сессий L_SESSION, хранит информацию о сессиях пользователей (времени входа и выхода) в системе. Состоит из целочисленного идентификатора (первичного ключа), идентификатора пользователя (внешний ключ на таблицу L_USERS), времени входа и выхода из системы.

Таблица тем (список тем из различных областей знаний) L_THEME. Состоит из целочисленного идентификатора записи (первичного ключа), наименования темы, текстового уникального кода темы, поля с описанием темы, статуса записи в таблице, идентификатора родительской записи (внешний ключ, связывание таблицы собой же), даты последнего изменения записи и имени последнего пользователя редактировавшего запись.

Таблица закрепления пользователя за темой L_USER_THEMS. Состоит из целочисленного идентификатора записи (первичный ключ), идентификатора темы (внешний ключ), идентификатора пользователя (внешний ключ) даты последнего изменения записи и имени последнего пользователя редактировавшего запись.

Таблица содержащая реестр тестов L_TESTS. Состоит из целочисленного идентификатора записи (первичный ключ), идентификатора темы (внешний ключ на таблицу L_THEME), наименования теста, описания теста, статуса записи, идентификатора пользователя создавшего сайт (внешний ключ на таблицу L_USER), поля с временем, за которое нужно выполнить тест, даты последнего изменения записи и имени последнего пользователя редактировавшего запись.

Таблица L_QUESTION список вопросов в тестах. Состоит из целочисленного идентификатора записи (первичного ключа), идентификатора теста (внешний ключ на таблицу L_TESTS), текста вопроса, идентификатора типа теста (внешний ключ на таблицу L_QUEST_TYPE), статуса записи, порядкового номера вопроса в тесте, даты последнего изменения записи и имени последнего пользователя редактировавшего запись.

Таблица L_QUEST_TYPE список типов вопросов, и их параметров.

Состоит из целочисленного идентификатора записи ( первичного ключа), уникального символьного кода, наименования типа, флага текстового ответа, флага выбора одного, максимальное кол-во вариантов ответа, даты последнего изменения записи и имени последнего пользователя редактировавшего запись.  В зависимости от типа вопроса, в интерфейсе задаются правила отображения вопроса и формата ответа на него.

Таблица вариантов ответов L_ANSWER по вопросу. Состоит из целочисленного идентификатора записи (первичный ключ), текста ответа, отображаемого в интерфесе, идентификатора вопроса (внешний ключ на таблицу L_QUESTION), статус записи в таблице, флаг корректности ответа, для вопросов с выбором правильного ответа, количества балов, начисляемое за ответ, даты последнего изменения записи и имени последнего пользователя редактировавшего запись

Таблицы D_RESULT и S_RESULT таблицы результатов прохождения тестов, относительно набора атрибуов имеют одинаковую структуру. Состоят из целочисленного идентификатора записи (первичного ключа), идентификатора теста, идентификатора вопроса, идентификатора выбранного ответа, идентификтаора сессии, кол-ва заработанных балов за ответ, статуса записи в таблице, даты последнего изменения записи и имени последнего пользователя редактировавшего запись.

На рисунке 5 изображена схематическая структура базы.

Рисунок 5 - Схематическая связь таблиц в базе.

3.5 Методы разработки веб-интерфейсов

Одним из методов создания страниц в Zope является панель администратора Zope (Рисунок 6). Расположенная по адресу http://zopehost:zopeport/manage, где zopehostip-адрес сервера, на котором установлен Zope сервер, а zopeport – порт который прослушивает сервер, по умолчанию это 8080. Так же вести разрботку можно через FTP или WebDAV.

Рисунок 6 Панель администратора в Zope2.

Создание html страниц в Zope

Для создания html страницы в выпадающем списке выберем элемент Page Template, представляющий из себя html шаблон. Шаблоны страниц должны удовлетворять трем основным правилам:

  •  Хорошее согласование с инструментами редактирования.
  •  Что вы видите, то вы и получаете.
  •  Хранение кода отдельно от шаблона, за исключением структурной логики.

Шаблон страницы подобен модели страниц, которые он генерирует. В частности, он является правильной HTML страницей [8].

В качестве обработчика данных с формы используются скрипты написанные на языке python.

Рассмотрим создание простого шаблона (рисунки 7, 8) с использованием языка TAL.

Рисунок 7 - Форма авторизации при входе в систему

без сообщения.

Рисунок 8 - Форма авторизации при входе в систему

с сообщением.

Форма является шаблоном, написанным на обычном html, а появляющееся сообщение на красном фоне является результатом использования TAL шаблонизатора, рассмотрим эту часть кода.

<span tal:condition="options/err">

       <p align="center" style="background-color:#FF0000">

           <font size=4 color=#FFFFFF tal:content="python: options.get('err','')">

           </font>

   </p>

</span>

Здесь использованы две конструкции tal, это condition и content.

Condition – условие, при наличии значения err передаваемого на форму тег в котором установлено условие и все вложенные в него отображаются, иначе не отображаются.

Content – описывает содержимое того тега, в котором оно находиться. В нашем случае это текст сообщения содержащийся в переменной err. Конструкции языка tal могут использовать как стандартные конструкции, так и встроенные конструкции языка python как в примере с конструкцией content.

Tal шаблоны так же позволяют строить динамические списки.

Например построение списка типов вопросов на форме создания тестов (рисунок 9).

Рисунок 9 Список типов вопросов.

Код описывающий построение списка.

<span tal:repeat="item python: container.SQL_QUEST_TYPE()">

       <input type="radio" name="type" tal:attributes="id item/ID_QUESTTYPE"> <span tal:content="item/NAME" /> <br>

</span>

Tal:repeat – конструкция определяющая повторяющийся тег, со всем своим содержимым. Повторяться он будет столько раз, сколько строк вернет Z SQL метод SQL_QUEST_TYPE. Z SQL методы – объекты Zope для создания параметризированных запросов к базе данных.

Пример SQL метода:

SELECT OUT_ID_QUESTION, OUT_STATUS, OUT_NUM,

               OUT_QUESTION_DESCRIPTION, OUT_ID_TEST

FROM REP_GET_QUESTION_LIST(<DTML-SQLVAR ID_TEST TYPE="INT"OPTIONAL >)

Запрос вызывающий хранимую процедуру и передающий в неё идентификатор теста для получения списка вопросов по этому тесту.

3.6 Обработка данных при помощи python скриптов

Рассмотрим обработку данных на примере формы регистрации

Объявим методы request и RESPONSE.

request = container.REQUEST

RESPONSE = request.RESPONSE

Запросим из полученного набора присланных данных значения полей с логином и паролем

login= request.get('login','')

passw = request.get('passw','')

Удостоверимся, что значения не являются пустыми

if (login==''):

Если значение пустое, то выведем снова форму логина, дополнительно передав на нее сообщение о пустом логине или пароде

 return container.Login(err='Вы не ввели логин')

elif (passw==''):

 return container.Login(err='Вы не ввели пароль')

else:

Если же поля заполнены, то вызываем через параметризованный ZSQL метод процедуру для проверки правильности введенных логина и пароля. Значения возращенные процедурой запишем в переменную log.

 log = container.P_LOGIN(login=login, passw=passw)[0]

Если пароль и логин подтвердились, то переходим на форму со списком модулей и передаем на неё идентификатор сессии под которым будет работать пользователь.

 if (log.OUT_SUCCESS=='1'):

   return RESPONSE.redirect('Iface?id_session=%s' % (log.OUT_ID_SESSION))

 else:

Иначе вернемся на форму регистрации с сообщением об ошибке.

   return container.Login(err = log.OUT_MESSAGE).

3.7 Создание хранимых процедур в Firebid

Для написания процедур используется PSQL – расширение языка SQL для СУБД Firebird. Основными элементами являются циклы и логические операторы.

Каждая процедура состоит из следующих обязательных выражений

Объявление действия совершаемого над процедурой, в скобках перечисляются входные параметры процедуры.

CREATE OR ALTER PROCEDURE PROCEDURE_NAME ()

В конструкции в скобках RETURNS () перечисляются выходные параметры которые будут выведены процедурой после вызова команды SUSPEND. Команды BEGIN и END обозначают конец и начало блока с текстом процедуры. Ниже приведен пример простейшего цикла, который выбирает все варианты ответов по вопросу, присваивая каждому варианту ответа свой номер начиная с одного.

OUT_NUMBER = 0;

Объявляем массив, который будем обрабатывать.

 FOR

   SELECT la.ID_ANSWER, la.IS_CORRECT, la.TEXT, la.SCORE

     FROM L_ANSWER la

    WHERE la.ID_QUERY = :IN_QUESTION_NUMBER

Переменные, которым будут присваиваться значения из массива.

     INTO :OUt_ID_ANSWER, :OUT_IS_CORRECT, :OUT_TEXT, :OUT_SCORE

 DO BEGIN

Тело цикла с инкрементом счетчика и выводом информации.

   OUT_NUMBER = OUT_NUMBER +1;

   SUSPEND;

END


4. Описание интерфейса разработанной системы

4.1 Модуль создания тестов

Модуль создания тестов предназначен для создания новых и редактирования существующих тестов. Модуль состоит из трёх форм, формы выбора теста для редактирования, непосредственно формы редактирования и формы создания нового теста. Права на модуль имеют только пользователи, входящие в группу преподавателей. Остальные пользователи не имеют возможности зайти в этот модуль. На рисунке 10 изображена форма редактирования тестов, модуля создания тестов.

Рисунок 10 – Форма редактирования теста.

4.2 Модуль прохождения тестов

Модуль прохождения тестов предназначен для прохождения тестов. Модуль состоит из двух форм, формы выбора теста для прохождения и формы и непосредственно формы прохождения тестов, подгружающего данные в различном формате, в зависимости от типа вопроса. Права на модуль есть у всех пользователей, а возможностью проходить все тесты без назначения, обладают только пользователи из группы администратор и из группы преподаватели. На рисунке 11 изображен пример интерфейса.

Рисунок 11 – Форма прохождения тестов.

4.3 Модуль назначения тестов

Модуль назначения тестов предназначен для назначения пользователям тестов на прохождение. Состоит из формы со списком пользователей и тестов и формы для просмотра привязанных к пользователю тестов.. Модуль доступен для пользователей из группы администратор и из группы преподаватели.

4.4 Модуль просмотра статистики

Модуль предназначен для просмотра статистики по пройденным тестам. Модуль доступен всем пользователям, но внутри модуля существуют ограничения на запуск тех или иных отчетов. Модуль состоит из формы со списком отчетов, и множества форм самих отчетов.


Заключение.

Основные результаты дипломной (курсовой) работы состоят в следующем:

1) Проведен анализ существующих систем для тестирования и сбора результатов. Выявлены достоинства и недостатки каждой из рассмотренных систем.

2) На основе анализа существующих систем сформулированы концепции разрабатываемой системы, на основе была спроектирована архитектура разрабатываемой системы.

3) Была спроектирована структура базы данных, с учетом специфики использования системы, для хранения больших объемов данных.

4) Спроектированы интерфейсы для системы мониторинга уровня знаний.

5) Была разработана система мониторинга уровня знаний учащихся.

Практическая значимость проведенной разработки состоит в возможности комплексного оценивания уровня знаний учащихся. Система может применяться в любых учебных заведениях, для сбора статистики по результатам тестов и проведения анализ собранных данных.


Список используемых источников

1. Основные возможности UniTest System. URL: http://sight2k.com/rus/unitest/qtour/  [2 сентября 2012]

2. MyTest - Компьютерное тестирование знаний UPL: http://mytest.klyaksa.net/  [26 сентября 2012]

3. Firebird: About Firebird 25 января 2013 URL: http://www.firebirdsql.org/en/about-firebird/  [4 февраля 2013]

4. iBase.ru | Firebird  18 марта 2012 URL: http://www.ibase.ru/firebird.htm [15 марта 2013]

5. Zope - The Object Publishing Environment URL: http://lib.misto.kiev.ua/WEBMASTER/zope.txt [20 октября 2012]

6. PHPTAL URL: http://phptal.org/manual/ru/#introduction [12 декабря 2012]

7. Moodle.org: open-source community-based tools for learning URL: http://moodle.org [30 сентября 2012]

8. Plone.org.ru: wikipage - Сайт поддержки систем управления сайтами Plone, Zope, языка python 19 августа 2006. URL: http://plone.org.ru/docs/howto/ZPT [16 ноября 2012]

9. Узнайте Firebird 23 сентября 2011 URL: http://www.firebirdnews.org/docs/fb2min_ru.html [29 октября 2012]

10 Zope uses Python — The Zope 2 Application Server URL: http://zope2.zope.org/about-zope-2/six-reasons-for-using-zope/zope-uses-python [20 декабря 2012]

11 Макконелл С. Совершенный код. Мастер-класс / Пер. с англ. – М. : Русская редакция, 2012 – 896 стр. : ил.

12 Марк Лутц Изучаем Python. / Пер. с англ. – М. : Символ-Плюс 2011 – 1280 с.

13. Хелен Борри. Firebird: руководство разработчика баз данных / Пер. с англ. – СПб «БХВ-Петербург» 2007 – 1104 с.


Приложение А Исходный код системы

/******************************************************************************/

/***          Generated by IBExpert 2012.9.2.1 18.06.2013 23:24:13          ***/

/******************************************************************************/

SET SQL DIALECT 3;

SET NAMES WIN1251;

CREATE DATABASE '127.0.0.1:C:\Documents and Settings\Admin\Рабочий стол\SMUZ2.FDB'

USER 'SYSDBA' PASSWORD 'masterkey'

PAGE_SIZE 16384

DEFAULT CHARACTER SET WIN1251;

/******************************************************************************/

/***                                Domains                                 ***/

/******************************************************************************/

CREATE DOMAIN D1024VARCHAR AS

VARCHAR(1024);

CREATE DOMAIN D10CHAR AS

CHAR(10);

CREATE DOMAIN D128VARCHAR AS

VARCHAR(128);

CREATE DOMAIN D512VARCHAR AS

VARCHAR(512);

CREATE DOMAIN D60CHAR AS

CHAR(60);

CREATE DOMAIN D60VARCHAR AS

VARCHAR(60);

CREATE DOMAIN DATES AS

DATE;

CREATE DOMAIN DOUBLE_PREC AS

DOUBLE PRECISION;

CREATE DOMAIN INTT AS

INTEGER;

CREATE DOMAIN PRK AS

INTEGER

NOT NULL;

CREATE DOMAIN TIMEST AS

TIMESTAMP;

/******************************************************************************/

/***                               Generators                               ***/

/******************************************************************************/

CREATE GENERATOR G_ID_ANSWER;

SET GENERATOR G_ID_ANSWER TO 21;

CREATE GENERATOR G_ID_GROUP;

SET GENERATOR G_ID_GROUP TO 0;

CREATE GENERATOR G_ID_QUERY;

SET GENERATOR G_ID_QUERY TO 3;

CREATE GENERATOR G_ID_QUERY_TYPE;

SET GENERATOR G_ID_QUERY_TYPE TO 10;

CREATE GENERATOR G_ID_RES_ANSWER;

SET GENERATOR G_ID_RES_ANSWER TO 0;

CREATE GENERATOR G_ID_RES_TEST;

SET GENERATOR G_ID_RES_TEST TO 0;

CREATE GENERATOR G_ID_SESSION;

SET GENERATOR G_ID_SESSION TO 0;

CREATE GENERATOR G_ID_TEST;

SET GENERATOR G_ID_TEST TO 1;

CREATE GENERATOR G_ID_THEM;

SET GENERATOR G_ID_THEM TO 0;

CREATE GENERATOR G_ID_USER;

SET GENERATOR G_ID_USER TO 0;

CREATE GENERATOR G_ID_USER_GROUP;

SET GENERATOR G_ID_USER_GROUP TO 0;

SET TERM ^ ;

/******************************************************************************/

/***                           Stored Procedures                            ***/

/******************************************************************************/

CREATE PROCEDURE ADD_VARIANT (

   IN_ID_QUESTION INTEGER,

   IN_TEXT VARCHAR(128),

   IN_IS_CORRECT INTEGER,

   IN_SCORE DOUBLE PRECISION)

RETURNS (

   I INTEGER)

AS

BEGIN

 SUSPEND;

END^

CREATE PROCEDURE GENERATE_TRIGGER

RETURNS (

   OUT_TRIG_TEXT VARCHAR(10000))

AS

BEGIN

 SUSPEND;

END^

CREATE PROCEDURE REP_GET_QUERY_TEXT (

   IN_ID_QUERY INTEGER)

RETURNS (

   TEXT VARCHAR(512))

AS

BEGIN

 SUSPEND;

END^

CREATE PROCEDURE REP_GET_QUESTION_LIST (

   IN_ID_TEST INTEGER)

RETURNS (

   OUT_ID_TEST INTEGER,

   OUT_ID_QUESTION INTEGER,

   OUT_NUM INTEGER,

   OUT_QUESTION_DESCRIPTION VARCHAR(128))

AS

BEGIN

 SUSPEND;

END^

CREATE PROCEDURE REP_GET_VARIANT (

   IN_QUESTION_NUMBER INTEGER)

RETURNS (

   OUT_ID_ANSWER INTEGER,

   OUT_IS_CORRECT INTEGER,

   OUT_TEXT VARCHAR(128),

   OUT_SCORE DOUBLE PRECISION,

   OUT_NUMBER INTEGER,

   OUT_AUERY_TEXT VARCHAR(512))

AS

BEGIN

 SUSPEND;

END^

CREATE PROCEDURE W_ADD_QUERY (

   IN_ID_TEST INTEGER)

RETURNS (

   OUT_ID_QUERY INTEGER)

AS

BEGIN

 SUSPEND;

END^

CREATE PROCEDURE W_SAVE_QUERY (

   IN_ID_QUERY INTEGER,

   IN_QUERY_TEXT VARCHAR(512),

   IN_TEXT_1 VARCHAR(128),

   IN_TEXT_2 VARCHAR(128),

   IN_TEXT_3 VARCHAR(128),

   IN_TEXT_4 VARCHAR(128),

   IN_TEXT_5 VARCHAR(128),

   IN_CORRECT_1 VARCHAR(5),

   IN_CORRECT_2 VARCHAR(5),

   IN_CORRECT_3 VARCHAR(5),

   IN_CORRECT_4 VARCHAR(5),

   IN_CORRECT_5 VARCHAR(5),

   IN_SCORE_1 DOUBLE PRECISION,

   IN_SCORE_2 DOUBLE PRECISION,

   IN_SCORE_3 DOUBLE PRECISION,

   IN_SCORE_4 DOUBLE PRECISION,

   IN_SCORE_5 DOUBLE PRECISION)

RETURNS (

   OUT_SUCCESS INTEGER,

   OUT_MESSAGE VARCHAR(128))

AS

BEGIN

 SUSPEND;

END^

SET TERM ; ^

/******************************************************************************/

/***                                 Tables                                 ***/

/******************************************************************************/

CREATE TABLE D_RES_ANSWER (

   ID_RES_ANSWER  PRK,

   ID_RES_TEST    INTT,

   ID_TEST        INTT,

   ID_ANSWER      INTT,

   ID_SESSION     INTT,

   BEGINTIME      TIMEST,

   ENDTIME        TIMEST,

   IS_CORRECT     INTT,

   SCORE          DOUBLE_PREC,

   PROCENT        DOUBLE_PREC,

   STATUS         INTT,

   LASTDATE       TIMEST,

   USERNAME       D10CHAR

);

CREATE TABLE D_RES_TEST (

   ID_RES_TEST  PRK,

   ID_TEST      INTT,

   ID_SESSION   INTT,

   BEGINTIME    TIMEST,

   ENDTIME      TIMEST,

   RESULT       DOUBLE_PREC,

   PROCENT      DOUBLE_PREC,

   STATUS       INTT,

   LASTDATE     TIMEST,

   USERNAME     D10CHAR

);

CREATE TABLE HEL (

   TAB  VARCHAR(100),

   ATR  VARCHAR(100)

);

CREATE TABLE L_ANSWER (

   ID_ANSWER   PRK,

   ID_QUERY    INTT,

   NUM         INTT,

   TEXT        D512VARCHAR,

   IS_CORRECT  INTT,

   SCORE       DOUBLE_PREC,

   STATUS      INTT,

   LASTDATE    TIMEST,

   USERNAME    D10CHAR

);

CREATE TABLE L_GROUP (

   ID_GROUP        PRK,

   NAME            D128VARCHAR,

   CODE            D10CHAR,

   ALLOW_EDIT      INTT,

   ALLOW_ASSEMBLE  INTT,

   ALLOW_STAT      INTT,

   ALLOW_TEST      INTT,

   PARAMS          D1024VARCHAR,

   STATUS          INTT,

   LASTDATE        TIMEST,

   USERNAME        D10CHAR

);

CREATE TABLE L_QUERY (

   ID_QUERY       PRK,

   ID_QUERY_TYPE  INTT,

   ID_TEST        INTT,

   ID_THEM        INTT,

   NUM            INTT,

   TEXT           D512VARCHAR,

   STATUS         INTT,

   LASTDATE       TIMEST,

   USERNAME       D10CHAR

);

CREATE TABLE L_SESSION (

   ID_SESSION      PRK,

   ID_USER         INTT,

   CONNECTTIME     TIMEST,

   DISCONNECTTIME  TIMEST,

   STATUS          INTT,

   LASTDATE        TIMEST,

   USERNAME        D10CHAR

);

CREATE TABLE L_TEST (

   ID_TEST   PRK,

   ID_THEM   INTT,

   NAME      D128VARCHAR,

   STATUS    INTT,

   LASTDATE  TIMEST,

   USERNAME  D10CHAR

);

CREATE TABLE L_THEM (

   ID_THEM   PRK,

   NAME      D128VARCHAR,

   STATUS    INTT,

   LASTDATE  TIMEST,

   USERNAME  D10CHAR

);

CREATE TABLE L_USER (

   ID_USER    PRK,

   NAME       D128VARCHAR,

   FIRSTNAME  D128VARCHAR,

   LASTNAME   D128VARCHAR,

   STATUS     INTT,

   LASTDATE   TIMEST,

   USERNAME   D10CHAR

);

CREATE TABLE L_USER_GROUP (

   ID_USER_GROUP  PRK,

   ID_USER        INTT,

   ID_GROUP       INTT,

   STATUS         INTT,

   LASTDATE       TIMEST,

   USERNAME       D10CHAR

);

CREATE TABLE S_QUERY_TYPE (

   ID_QUERY_TYPE  PRK,

   NAME           D128VARCHAR,

   CODE           D10CHAR,

   STATUS         INTT,

   LASTDATE       TIMEST,

   USERNAME       D10CHAR

);

/******************************************************************************/

/***                              Primary Keys                              ***/

/******************************************************************************/

ALTER TABLE D_RES_ANSWER ADD CONSTRAINT PK_D_RES_ANSWER PRIMARY KEY (ID_RES_ANSWER);

ALTER TABLE D_RES_TEST ADD CONSTRAINT PK_D_RES_TEST PRIMARY KEY (ID_RES_TEST);

ALTER TABLE L_ANSWER ADD CONSTRAINT PK_L_ANSWER PRIMARY KEY (ID_ANSWER);

ALTER TABLE L_GROUP ADD CONSTRAINT PK_L_GROUP PRIMARY KEY (ID_GROUP);

ALTER TABLE L_QUERY ADD CONSTRAINT PK_L_QUERY PRIMARY KEY (ID_QUERY);

ALTER TABLE L_SESSION ADD CONSTRAINT PK_L_SESSION PRIMARY KEY (ID_SESSION);

ALTER TABLE L_TEST ADD CONSTRAINT PK_L_TEST PRIMARY KEY (ID_TEST);

ALTER TABLE L_THEM ADD CONSTRAINT PK_L_THEM PRIMARY KEY (ID_THEM);

ALTER TABLE L_USER ADD CONSTRAINT PK_L_USER PRIMARY KEY (ID_USER);

ALTER TABLE L_USER_GROUP ADD CONSTRAINT PK_L_USER_GROUP PRIMARY KEY (ID_USER_GROUP);

ALTER TABLE S_QUERY_TYPE ADD CONSTRAINT PK_S_QUERY_TYPE PRIMARY KEY (ID_QUERY_TYPE);

/******************************************************************************/

/***                                Triggers                                ***/

/******************************************************************************/

SET TERM ^ ;

/******************************************************************************/

/***                          Triggers for tables                           ***/

/******************************************************************************/

/* Trigger: TR_D_RES_ANSWER_BI */

CREATE TRIGGER TR_D_RES_ANSWER_BI FOR D_RES_ANSWER

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.id_res_answer IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_id_res_answer,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.id_res_answer = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_D_RES_ANSWER_BU */

CREATE TRIGGER TR_D_RES_ANSWER_BU FOR D_RES_ANSWER

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_D_RES_TEST_BI */

CREATE TRIGGER TR_D_RES_TEST_BI FOR D_RES_TEST

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_RES_TEST IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_RES_TEST,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_RES_TEST = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_D_RES_TEST_BU */

CREATE TRIGGER TR_D_RES_TEST_BU FOR D_RES_TEST

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_ANSWER_BI */

CREATE TRIGGER TR_L_ANSWER_BI FOR L_ANSWER

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_ANSWER IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_ANSWER,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_ANSWER = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_ANSWER_BU */

CREATE TRIGGER TR_L_ANSWER_BU FOR L_ANSWER

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_GROUP_BI */

CREATE TRIGGER TR_L_GROUP_BI FOR L_GROUP

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_GROUP IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_GROUP,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_GROUP = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_GROUP_BU */

CREATE TRIGGER TR_L_GROUP_BU FOR L_GROUP

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_QUERY_BI */

CREATE TRIGGER TR_L_QUERY_BI FOR L_QUERY

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_QUERY IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_QUERY,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_QUERY = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_QUERY_BU */

CREATE TRIGGER TR_L_QUERY_BU FOR L_QUERY

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_SESSION_BI */

CREATE TRIGGER TR_L_SESSION_BI FOR L_SESSION

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_SESSION IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_SESSION,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_SESSION = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_SESSION_BU */

CREATE TRIGGER TR_L_SESSION_BU FOR L_SESSION

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_TEST_BI */

CREATE TRIGGER TR_L_TEST_BI FOR L_TEST

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_TEST IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_TEST,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_TEST = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_TEST_BU */

CREATE TRIGGER TR_L_TEST_BU FOR L_TEST

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_THEM_BI */

CREATE TRIGGER TR_L_THEM_BI FOR L_THEM

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_THEM IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_THEM,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_THEM = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_THEM_BU */

CREATE TRIGGER TR_L_THEM_BU FOR L_THEM

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_USER_BI */

CREATE TRIGGER TR_L_USER_BI FOR L_USER

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_USER IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_USER,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_USER = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_USER_BU */

CREATE TRIGGER TR_L_USER_BU FOR L_USER

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_L_USER_GROUP_BI */

CREATE TRIGGER TR_L_USER_GROUP_BI FOR L_USER_GROUP

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_USER_GROUP IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_USER_GROUP,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_USER_GROUP = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_L_USER_GROUP_BU */

CREATE TRIGGER TR_L_USER_GROUP_BU FOR L_USER_GROUP

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

/* Trigger: TR_S_QUERY_TYPE_BI */

CREATE TRIGGER TR_S_QUERY_TYPE_BI FOR S_QUERY_TYPE

ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.ID_QUERY_TYPE IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_ID_QUERY_TYPE,1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.ID_QUERY_TYPE = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END

^

/* Trigger: TR_S_QUERY_TYPE_BU */

CREATE TRIGGER TR_S_QUERY_TYPE_BU FOR S_QUERY_TYPE

ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END

^

SET TERM ; ^

/******************************************************************************/

/***                           Stored Procedures                            ***/

/******************************************************************************/

SET TERM ^ ;

ALTER PROCEDURE ADD_VARIANT (

   IN_ID_QUESTION INTEGER,

   IN_TEXT VARCHAR(128),

   IN_IS_CORRECT INTEGER,

   IN_SCORE DOUBLE PRECISION)

RETURNS (

   I INTEGER)

AS

BEGIN

 IF (in_ID_QUESTION IS NULL) THEN

 EXIT;

 IF (IN_TEXT IS NULL ) THEN IN_TEXT = '';

 IF (IN_IS_CORRECT IS NULL) THEN IN_IS_CORRECT = 0;

 IF (IN_SCORE IS NULL ) THEN IN_SCORE = 0.0;

SELECT COUNT(*) FROM L_ANSWER WHERE ID_QUERY = :IN_ID_QUESTION

INTO i;

IF (i<5 ) THEN

 INSERT INTO L_ANSWER (ID_QUERY, TEXT,IS_CORRECT, SCORE )

      VALUES  (:IN_ID_QUESTION, :IN_TEXT, :IN_IS_CORRECT, :IN_SCORE);

SUSPEND;

END^

ALTER PROCEDURE GENERATE_TRIGGER

RETURNS (

   OUT_TRIG_TEXT VARCHAR(10000))

AS

DECLARE VARIABLE V_TAB VARCHAR(50);

DECLARE VARIABLE V_ATR VARCHAR(50);

BEGIN

 FOR

   SELECT TRIM(TAB), TRIM(ATR)

     FROM HEL

     INTO :V_TAB, :V_ATR

 DO BEGIN

OUT_TRIG_TEXT = 'CREATE OR ALTER TRIGGER TR_'|| V_TAB ||'_BI FOR ' || V_TAB ||

' ACTIVE BEFORE INSERT POSITION 0

AS

DECLARE VARIABLE v_ID integer;

BEGIN

 IF (NEW.'|| V_ATR || ' IS NULL) THEN

 BEGIN

   SELECT GEN_ID(G_'|| V_ATR ||',1)

     FROM RDB$DATABASE

     INTO :v_ID;

   NEW.'|| V_ATR || ' = :V_ID;

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

 END

END;';

EXECUTE STATEMENT  :OUT_TRIG_TEXT;

   SUSPEND;

OUT_TRIG_TEXT = 'CREATE OR ALTER TRIGGER TR_'|| V_TAB ||'_BU FOR ' || V_TAB ||

' ACTIVE BEFORE UPDATE POSITION 0

AS

BEGIN

   new.lastdate = CURRENT_TIMESTAMP;

   new.username = user;

END;';

EXECUTE STATEMENT  :OUT_TRIG_TEXT;

SUSPEND;

 END

END^

ALTER PROCEDURE REP_GET_QUERY_TEXT (

   IN_ID_QUERY INTEGER)

RETURNS (

   TEXT VARCHAR(512))

AS

BEGIN

 SELECT TEXT

 FROM L_QUERY

 WHERE ID_QUERY = :in_ID_QUERY

 into :text;

 IF (:text is null) THEN

 text='';

       SUSPEND;

END^

ALTER PROCEDURE REP_GET_QUESTION_LIST (

   IN_ID_TEST INTEGER)

RETURNS (

   OUT_ID_TEST INTEGER,

   OUT_ID_QUESTION INTEGER,

   OUT_NUM INTEGER,

   OUT_QUESTION_DESCRIPTION VARCHAR(128))

AS

BEGIN

 FOR

   /*получаем по ид-теста список вопросов для прорисовки на странице*/

   SELECT LQ.ID_QUERY, LQ.ID_TEST, LQ.NUM, 'Вопрос № '|| INTTOSTR(LQ.NUM, '%d')

     FROM L_QUERY LQ

    WHERE LQ.ID_TEST = :IN_ID_TEST

     INTO :OUT_ID_QUESTION, :OUT_ID_TEST, :OUT_NUM, :OUT_QUESTION_DESCRIPTION

 DO SUSPEND;

END^

ALTER PROCEDURE REP_GET_VARIANT (

   IN_QUESTION_NUMBER INTEGER)

RETURNS (

   OUT_ID_ANSWER INTEGER,

   OUT_IS_CORRECT INTEGER,

   OUT_TEXT VARCHAR(128),

   OUT_SCORE DOUBLE PRECISION,

   OUT_NUMBER INTEGER,

   OUT_AUERY_TEXT VARCHAR(512))

AS

BEGIN

 /*Получаем список вариантов ответа по вопросу*/

 SELECT TEXT

   FROM L_QUERY

  WHERE ID_QUERY = :IN_QUESTION_NUMBER

   INTO :OUT_AUERY_TEXT;

 out_number = 0;

 FOR

   SELECT la.ID_ANSWER, la.IS_CORRECT, la.TEXT, la.SCORE

     FROM L_ANSWER la

    WHERE la.ID_QUERY = :IN_QUESTION_NUMBER

    ORDER BY ID_ANSWER

     INTO :OUt_ID_ANSWER, :OUT_IS_CORRECT, :OUT_TEXT, :OUT_SCORE

 DO BEGIN

   OUT_NUMBER = OUT_NUMBER +1;

   SUSPEND;

 END

END^

ALTER PROCEDURE W_ADD_QUERY (

   IN_ID_TEST INTEGER)

RETURNS (

   OUT_ID_QUERY INTEGER)

AS

DECLARE VARIABLE V_NUM INTEGER;

BEGIN

 /*ДОБАВЛЯЕМ ЗАПИСЬ В СПИСОК ВОПРОСОВ ТЕСТА*/

 SELECT MAX(Q.NUM)+1

   FROM L_QUERY Q

  WHERE Q.ID_TEST = :IN_ID_TEST

   INTO :V_NUM;

 SELECT GEN_ID(G_ID_QUERY_TYPE,1)

   FROM RDB$DATABASE

   INTO :OUT_ID_QUERY;

 INSERT INTO L_QUERY (ID_QUERY, ID_TEST, NUM)

      VALUES (:OUT_ID_QUERY, :IN_ID_TEST, :V_NUM);

 SUSPEND;

END^

ALTER PROCEDURE W_SAVE_QUERY (

   IN_ID_QUERY INTEGER,

   IN_QUERY_TEXT VARCHAR(512),

   IN_TEXT_1 VARCHAR(128),

   IN_TEXT_2 VARCHAR(128),

   IN_TEXT_3 VARCHAR(128),

   IN_TEXT_4 VARCHAR(128),

   IN_TEXT_5 VARCHAR(128),

   IN_CORRECT_1 VARCHAR(5),

   IN_CORRECT_2 VARCHAR(5),

   IN_CORRECT_3 VARCHAR(5),

   IN_CORRECT_4 VARCHAR(5),

   IN_CORRECT_5 VARCHAR(5),

   IN_SCORE_1 DOUBLE PRECISION,

   IN_SCORE_2 DOUBLE PRECISION,

   IN_SCORE_3 DOUBLE PRECISION,

   IN_SCORE_4 DOUBLE PRECISION,

   IN_SCORE_5 DOUBLE PRECISION)

RETURNS (

   OUT_SUCCESS INTEGER,

   OUT_MESSAGE VARCHAR(128))

AS

DECLARE VARIABLE V_ID_ANSWER INTEGER;

DECLARE VARIABLE V_CORRECT INTEGER;

DECLARE VARIABLE v_num INTEGER;

BEGIN

 /*Процедура сохраняет данные с формы*/

 OUT_MESSAGE = 'Вопрос сохранен.';

 /*запишем параметры вопроса*/

 FOR

   SELECT ID_ANSWER, NUM

     FROM L_ANSWER

    WHERE ID_QUERY = :IN_ID_QUERY

    ORDER BY NUM

     INTO :V_ID_ANSWER, :V_NUM

 DO BEGIN

/*---------------------------------------------------------------------------*/

   IF (V_NUM = 1 ) THEN

   BEGIN

     IF (IN_TEXT_1 = '') THEN

       DELETE FROM L_ANSWER

             WHERE ID_ANSWER =:V_ID_ANSWER;

     ELSE

     BEGIN

       IF (IN_CORRECT_1= 'on')

         THEN V_CORRECT = 1;

       ELSE V_CORRECT = 0;

       UPDATE L_ANSWER

          SET TEXT = :IN_TEXT_1,

              IS_CORRECT = :V_CORRECT,

              SCORE = :IN_SCORE_1

        WHERE ID_ANSWER =:V_ID_ANSWER;

     END

   END

/*---------------------------------------------------------------------------*/

   IF (V_NUM = 2 ) THEN

   BEGIN

     IF (IN_TEXT_2 = '') THEN

       DELETE FROM L_ANSWER

             WHERE ID_ANSWER =:V_ID_ANSWER;

     ELSE

     BEGIN

       IF (IN_CORRECT_2= 'on')

         THEN V_CORRECT = 1;

       ELSE V_CORRECT = 0;

       UPDATE L_ANSWER

          SET TEXT = :IN_TEXT_2,

              IS_CORRECT = :V_CORRECT,

              SCORE = :IN_SCORE_2

        WHERE ID_ANSWER =:V_ID_ANSWER;

     END

   END

/*---------------------------------------------------------------------------*/

   IF (V_NUM = 3 ) THEN

   BEGIN

     IF (IN_TEXT_3 = '') THEN

       DELETE FROM L_ANSWER

             WHERE ID_ANSWER =:V_ID_ANSWER;

     ELSE

     BEGIN

       IF (IN_CORRECT_3= 'on')

         THEN V_CORRECT = 1;

       ELSE V_CORRECT = 0;

       UPDATE L_ANSWER

          SET TEXT = :IN_TEXT_3,

              IS_CORRECT = :V_CORRECT,

              SCORE = :IN_SCORE_3

        WHERE ID_ANSWER =:V_ID_ANSWER;

     END

   END

/*---------------------------------------------------------------------------*/

   IF (V_NUM = 4 ) THEN

   BEGIN

     IF (IN_TEXT_4 = '') THEN

       DELETE FROM L_ANSWER

             WHERE ID_ANSWER =:V_ID_ANSWER;

     ELSE

     BEGIN

       IF (IN_CORRECT_4= 'on')

         THEN V_CORRECT = 1;

       ELSE V_CORRECT = 0;

       UPDATE L_ANSWER

          SET TEXT = :IN_TEXT_4,

              IS_CORRECT = :V_CORRECT,

              SCORE = :IN_SCORE_4

        WHERE ID_ANSWER =:V_ID_ANSWER;

     END

   END

/*---------------------------------------------------------------------------*/

   IF (V_NUM = 5 ) THEN

   BEGIN

     IF (IN_TEXT_5 = '') THEN

       DELETE FROM L_ANSWER

             WHERE ID_ANSWER =:V_ID_ANSWER;

     ELSE

     BEGIN

       IF (IN_CORRECT_5= 'on')

         THEN V_CORRECT = 1;

       ELSE V_CORRECT = 0;

       UPDATE L_ANSWER

          SET TEXT = :IN_TEXT_5,

              IS_CORRECT = :V_CORRECT,

              SCORE = :IN_SCORE_5

        WHERE ID_ANSWER =:V_ID_ANSWER;

     END

   END

/*---------------------------------------------------------------------------*/

 END

 /*сохраним текст вопроса*/

 IF (C(IN_QUERY_TEXT)='') THEN

 BEGIN

   DELETE FROM L_QUERY

         WHERE ID_QUERY = :IN_ID_QUERY;

   DELETE FROM L_ANSWER

         WHERE ID_QUERY = :IN_ID_QUERY;

   OUT_MESSAGE = 'Вопрос удален.';

 END

 ELSE

 UPDATE L_QUERY

   SET TEXT = :IN_QUERY_TEXT

 WHERE ID_QUERY = :IN_ID_QUERY;

 /*исправим нумерацию если её сбили*/

 V_NUM = 1;

 FOR

   SELECT ID_ANSWER

     FROM L_ANSWER

    WHERE ID_QUERY = :IN_ID_QUERY

    ORDER BY NUM

     INTO :V_ID_ANSWER

 DO BEGIN

   UPDATE L_ANSWER

      SET NUM = :V_NUM

    WHERE ID_ANSWER = :V_ID_ANSWER;

   V_NUM = V_NUM + 1;

 END

 OUT_SUCCESS = 1;

 SUSPEND;

END^

SET TERM ; ^

## Script (Python) "Action_create"

##bind container=container

##bind context=context

##bind namespace=

##bind script=script

##bind subpath=traverse_subpath

##parameters=

##title=

##

request = container.REQUEST

RESPONSE =  request.RESPONSE

""" Здесь мы сохраняем параматры """

if ( request.get('save')):

 id_test = request.get('id_test')

 id_query = request.get('id_quest')

 var1 = request.get('q1','')

 var2 = request.get('q2','')

 var3 = request.get('q3','')

 var4 = request.get('q4','')

 var5 = request.get('q5','')

 score1 = request.get('s1','')

 score2 = request.get('s2','')

 score3 = request.get('s3','')

 score4 = request.get('s4','')

 score5 = request.get('s5','')

 correct1 = request.get('c1','')

 correct2 = request.get('c2','')

 correct3 = request.get('c3','')

 correct4 = request.get('c4','')

 correct5 = request.get('c5','')   

 

 sav = container.save_query(id_query = id_query, query_text = request.get('quest_name','') ,  t1=var1, t2=var2, t3=var3, t4 = var5, t5=var5, cor1=correct1, cor2=correct2, cor3 = correct3, cor4 = correct4, cor5 = correct5, score1 = score1, score2 = score2, score3 = score3, score4=score4 , score5= score5)

 return RESPONSE.redirect('create?id_session=%s&id_test=%s&id_quest=%s' %(request.get('id_session','' ), request.get('id_test',''), request.get('id_quest','') ))

 

""" Добавляем новыйй незаполненный вариант """

if ( request.get('add')):

 container.ADD_VARIANT(id_quest=(request.get('id_quest','')))

 return RESPONSE.redirect('create?id_session=%s&id_test=%s&id_quest=%s' %(request.get('id_session','' ), request.get('id_test',''), request.get('id_quest','') ))

""" Добавляем новый вопрос пока без вариантов """

if ( request.get('new')):  

 add_query = container.ADD_QUERY(id_test=request.get('id_test',''))

 return RESPONSE.redirect('create?id_session=%s&id_test=%s&id_quest=%s' %(request.get('id_session','' ), request.get('id_test',''), add_query[0].OUT_ID_QUERY ))

ADD_QUERY  

 <params>id_test</params>

select * from W_ADD_QUERY(<dtml-sqlvar id_test type="int">)

ADD_VARIANT

<params>id_quest</params>

SELECT * from ADD_VARIANT (

<dtml-sqlvar id_quest type="int" optional >,

null,

0,

0)

create

<html>

 <head>

   <title>

     The title

   </title>

   <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">

 </head>

 <body bgcolor=#faeedd >

     <table width=95% align="center" border=1 style="background-color:#F0FFF0" >

       <tr>

         <td>

           <p align="center"  ><font size="6">Система мониторинга уровня знаний - "СМУЗ"</font> </p>            

           <form action="Action_create">

     <input type="hidden" name="id_test" id="id_test"  tal:attributes="value request/id_test">

     <input type="hidden" name="id_quest" id="id_quest"  tal:attributes="value request/id_quest|nothing">

     <input type="hidden" name="id_questtype" id="id_questtype"  tal:attributes="value request/id_questtype|nothing">

             <table  width=100%>  

               <tr>

               <td width="20%" style="vertical-align: top;">

               <DIV style="background:yellow " >

               <B> ВОПРОСЫ </B><br>

                 <span tal:condition="python: request.get('id_test')"

                       tal:define="list python: container.REP_GET_QUESTION_LIST(id_test=request.get('id_test'))">

                 <span tal:repeat="item list">

                   <a tal:content="python: str(item.OUT_QUESTION_DESCRIPTION)"

                      tal:attributes="href python: 'create?id_quest='+ str(item.OUT_ID_QUESTION) +'&id_test=' + str(item.OUT_ID_TEST) ">

                   </a><br>                

               </span>

                 </span>

                 <br>

                  <input type="submit" name="new" value="Добавить вопрос"><br><br>

               <!-- ТУТ БУДЕТ ШАБЛОН -->

               </DIV>

               </td>

               <td width="80%" style=" vertical-align: top;">

               <DIV style="background:#FFF ">

               <B> РЕДАКТОР ВОПРОСОВ</B>

               <!-- И ТУТ ТОЖЕ --><BR><BR>

               <span tal:repeat="item python: container.SQL_QUEST_TYPE()">

               <input type="radio" name="type" tal:attributes="id item/ID_QUERY_TYPE"> <span tal:content="item/NAME" /> <br>

               </span>

               <hr><br >

     Вопрос: <br>

     <textarea id="quest_name" name="quest_name" tal:content="python: container.SQL_QUERY_TEXT(id_quest= request.get('id_quest',''))[0].TEXT" >  

     </textarea>  <br>

     <span tal:repeat="item python: container.REP_GET_VARIANT(id_quest= request.get('id_quest',''))">

     <input type="text" tal:attributes="id python: 'q' + str(item.out_number) ;name python: 'q' + str(item.out_number); value python: item.OUT_TEXT">

     <input type="text" size=4 tal:attributes="id python: 's' + str(item.out_number) ;name python: 's' + str(item.out_number); value python: item.OUT_SCORE" >

     <input type="checkbox" tal:attributes="id python: 'c' + str(item.out_number) ;name python: 'c' + str(item.out_number); checked python: item.OUT_IS_CORRECT">

      <br>

     </span>

     <input type="submit" name="add" value="+"><br><br>

     <input type="submit" name="save" value="Сохранить">

               

               </DIV>

               </td></tr>

             </table>

             <br>

           </form>

         </td>

       </tr>

     </table>

 </body>

</html>

REP_GET_QUESTION_LIST

<params>id_test</params>

select *

 from REP_GET_QUESTION_LIST(<dtml-sqlvar id_test type="int"optional >)

 

REP_GET_VARIANT  

 

<params>id_quest</params>

select * from REP_GET_VARIANT (<dtml-sqlvar id_quest type="int" optional >)

SAVE_QUERY

<params>id_query query_text t1 t2 t3 t4 t5 cor1 cor2 cor3 cor4 cor5 score1 score2 score3 score4 score5</params>

select * from W_SAVE_QUERY

( <dtml-sqlvar id_query type="int" >,

 <dtml-sqlvar query_text type="string" optional >,

 <dtml-sqlvar t1 type="string" optional >,

 <dtml-sqlvar t2 type="string" optional >,

 <dtml-sqlvar t3 type="string" optional >,

 <dtml-sqlvar t4 type="string" optional >,

 <dtml-sqlvar t5 type="string" optional >,

 <dtml-sqlvar cor1 type="string" optional >,

 <dtml-sqlvar cor2 type="string" optional >,

 <dtml-sqlvar cor3 type="string" optional >,

 <dtml-sqlvar cor4 type="string" optional >,

 <dtml-sqlvar cor5 type="string" optional >,

 <dtml-sqlvar score1 type="float" optional >,

 <dtml-sqlvar score2 type="float" optional >,

 <dtml-sqlvar score3 type="float" optional >,

 <dtml-sqlvar score4 type="float" optional >,

 <dtml-sqlvar score5 type="float" optional >)

SQL_QUERY_TEXT

<params>id_quest</params>

select text from REP_GET_QUERY_TEXT (<dtml-sqlvar id_quest type="int" optional >)

SQL_QUEST_TYPE

<params></params>

select * from s_QUery_TYPE

LOGIN

<html>

 <head>

   <title tal:content="template/title">

     Добро пожаловать в СМУЗ

   </title>

   <meta http-equiv="Content-Type" content="text/html; charset=windows-1251">

 </head>

 <body bgcolor=#faeedd >

   <form action="Login_action" >

     <table align="center" border=0 style="background-color:#F0FFF0" >

       <tr>

         <td>

           <br>

           <p align="center" style="background-color:#white" >

             <font size="7">

               Система мониторинга уровня знаний

             </font>

                             

           </p>

           <br>

<span tal:condition="python: options.get('err', '')">

  <p align="center" style="background-color:#FF0000"> <b> <font size=4 color=#FFFFFF > <span tal:content="python: options.get('err','')"/>

</font> </b> </p>

</span>

           Login: <br>

           <input type="text" id="login" name="login"> <br>

           Password:<br>

           <input type="password" id="passw" name="passw" ><br>

           <pre>          <input type="submit" value="Войти"></pre>

         </td>

       </tr>

       <tr>

       <td>

         <a href="add_user">

           Зарегистрироваться

         </a >  

         </td>

       </tr>

     </table>

   </form>

 </body>

</html>

LOGIN_ACTION

## Script (Python) "Login_action"

##bind container=container

##bind context=context

##bind namespace=

##bind script=script

##bind subpath=traverse_subpath

##parameters=

##title=

##

request = container.REQUEST

RESPONSE =  request.RESPONSE

login= request.get('login','')

password = request.get('passw','')

if (login==''):

 return container.Login(err='Вы не ввели логин')

elif (password==''):

 return container.Login(err='Вы не ввели пароль')

else:

 log = container.P_LOGIN(login=login, password=password)[0]

 if (log.OUT_SUCCESS=='1'):

   return RESPONSE.redirect('Iface?id_session=%s' % (log.OUT_ID_SESSION))

 else:

   return container.Login(err = log.OUT_MESSAGE)

Парсер для Zexp файлов

#!/usr/bin/python

from cStringIO import StringIO

from pickle import Pickler, Unpickler

from struct import pack, unpack

class Dummy:

 def __call__(self,*a,**b):

   print '-->',self.__dict__['xmodule'], '>>call', a,b

   self.__dict__['xmodule']=self.__dict__['xmodule']+(', instance(%s,%s)' % (a,b))

   return self

 def __setattr__(self,name,value):

   print '-->',self.__dict__['xmodule'], 'setattr', name,value

 def __getattr__(self,name):

   print '-->',self.__dict__['xmodule'], 'getattr', name

 def __repr__(self):

   return self.__dict__['xmodule']

class Dummy2:

   def __init__(self,*a,**b):

       #print a,b

       self.argmt=[a,b]   

       

   def __repr__(self):

       return '<%s dict=%s>' % (self.__class__.__name__, self.__dict__)

 

class CustomUnpickler(Unpickler):

   def find_class(self, module, name):

       #print '>>find_class', module,name

       clname=('%s.%s' % (module,name)).replace('.','_')

       exec('class %s(Dummy2):pass' % clname)

       return eval(clname)

       

       cls=Dummy()

       cls.__dict__['xmodule']='%s.%s' % (module,name)

       return cls

_oid = '\0'*8

def FileToNodes(fpath):

   #Copyright (c) Zope

   

   f=file(fpath,'rb')

   f.read(4)

   oids = {}

   export_end_marker = '\377'*16

   

   def new_oid():

       global _oid

       last = _oid

       d = ord(last[-1])

       if d < 255:  # fast path for the usual case

           last = last[:-1] + chr(d+1)

       else:        # there's a carry out of the last byte

           last_as_long, = unpack(">Q", last)

           last = pack(">Q", last_as_long + 1)

       _oid = last

       return last

   

   def u64(v):

       """Unpack an 8-byte string into a 64-bit long integer."""

       return unpack(">Q", v)[0]

   class Ghost(object):

       __slots__ = ("oid",)

       def __init__(self, oid):

           self.oid = oid

       def __repr__(self):

           return '<%s dict=%s>' % (self.__class__.__name__, self.oid)

   def persistent_id(obj):

       if isinstance(obj, Ghost):

           return obj.oid

   def persistent_load(ooid):

       """Remap a persistent id to a new ID and create a ghost for it."""

       klass = None

       if isinstance(ooid, tuple):

           ooid, klass = ooid

       if ooid in oids:

           oid = oids[ooid]

       else:

           if klass is None:

               oid = new_oid()

           else:

               oid = new_oid(), klass

           oids[ooid] = oid

       return Ghost(oid)

   

   out=[]

   while 1:

       h = f.read(16)

       if h == export_end_marker:

           break

       if len(h) != 16:

           raise Exception("Truncated export file")

       l = u64(h[8:16])

       p = f.read(l)

       if len(p) != l:

           raise Exception("Truncated export file")

       ooid = h[:8]

       if oids:

           oid = oids[ooid]

           if isinstance(oid, tuple):

               oid = oid[0]

       else:

           oids[ooid] = oid = new_oid()

       pfile = StringIO(p)

       unpickler = CustomUnpickler(pfile)

       unpickler.persistent_load = persistent_load

       a=unpickler.load()

       b=unpickler.load()

       #

       out.append([oid,a,b])

   return out        

def make_filelist(node,path,flist,index):

 objs=node.get('_objects',[])

 xobjs={}

 for i in objs:

    xobjs[i['id']]=i['meta_type']

 keys=xobjs.keys()

 keys.sort()  

 for i in keys:

    oid=node[i].oid

    if xobjs[i] in ('Folder','Folder (Ordered)'):

       flist=make_filelist(index[oid],path+[i],flist,index)

    flist.append({'node':index[oid],'path':path+[i],'meta_type':xobjs[i]})

 return flist

 

 

def parseZobj(objs):

 index={}

 for i in objs:

   index[i[0]]=i[2]

 root=objs[0][2]

 flist=make_filelist(root,[root['id']],[],index)

 

 out=""

 for item in flist:

    out+='%s(%s):\n\n' % ('/'.join(item['path']),item['meta_type'])

    if item['meta_type']=='File':

       out+=item['node']['data']

    elif item['meta_type']=='Script (Python)':

       out+='params:%s\n' % item['node'].get('_params','')

       out+=item['node']['_body']

    elif item['meta_type']=='Page Template':

       out+=item['node']['_text']

    elif item['meta_type']=='Z SQL Method':

       out+='params:%s\n' % item['node']['arguments_src']

       out+= item['node']['src']

    out+='\n\n'

 return out      

def get_plain_content(fpath):

 """ return content of zexp-file """

 objs = FileToNodes(fpath)

 return parseZobj(objs)  

 

def compareRevisions(fpath,r1,r2):

 import os,tempfile

 

 x, fileR1 = tempfile.mkstemp()

 x, fileR2 = tempfile.mkstemp()

 #print fileR1,fileR2

 os.system('svn cat \'%s\'@%s > %s' % (fpath,r1,fileR1))

 os.system('svn cat \'%s\'@%s > %s' % (fpath,r2,fileR2))

 

 x, out1 = tempfile.mkstemp()

 x, out2 = tempfile.mkstemp()

 #print out1,out2

 file(out1,'wb').write(get_plain_content(fileR1))

 file(out2,'wb').write(get_plain_content(fileR2))

 

 os.system('C:\Program Files\KDiff3\kdiff3.exe %s %s' % (out1,out2))

 os.remove(fileR1)

 os.remove(fileR2)

 os.remove(out1)

 os.remove(out2)

 

def compare_files(path1,path2):

 import os,tempfile

 x, out1 = tempfile.mkstemp()

 x, out2 = tempfile.mkstemp()

 file(out1,'wb').write(get_plain_content(path1))

 file(out2,'wb').write(get_plain_content(path2))

 os.system('"C:\Program Files\KDiff3\kdiff3.exe" %s %s' % (out1,out2))

 os.remove(out1)

 os.remove(out2)

 

if __name__=='__main__':  

 import sys

 if len(sys.argv)==1:

   print 'Usage: %s COMMAND [ARGUMENTS]' % __file__

   print 'Available commands:\n\t compare_svn - compare revisions zexp file\n\t compare_file - compare files'

   sys.exit(1)

 command=sys.argv[1]

 if command == 'compare_svn':

   if len(sys.argv)!=5:

     print 'Usage: %s %s FILEPATH REVISION1 REVISION2' % (__file__, command)

   else:

     compareRevisions(sys.argv[2],sys.argv[3],sys.argv[4])

 elif command == 'compare_file':

   if len(sys.argv)!=4:

     print 'Usage: %s %s FILEPATH1 FILEPATH2' % (__file__, command)

   else:

     compare_files(sys.argv[2],sys.argv[3])

     

 




1. сімей первісних людей
2. Принимаются Министерством финансов РФ и Центральным банком РФ
3. История инквизиции
4. Сделано в Японии В наши дни нельзя представить себе мировой рынок изделий бытовой радиоэлектроники без т
5. по теме 7 Многомерный статистический анализ 1 При каком значении линейного коэффициента корреляции с
6. Кредитування підприємств
7. Экспертиза проектов 1
8. Под игом ПВежинов
9. ВАРИАНТ 2 1. Какой сплав используется при изготовлении штампов и контрштампов
10. Тарханы край где все напоминает о поэте
11. Квантитативная лексикология романских языков введение в проблематику
12. 11 курса 1
13. чьо Дослухай спершу
14. . В каком слове верно выделена буква обозначающая ударный гласный звук позвОним 2 бантЫ
15. Оборудование в офи се сдается по журналу приема оборудования в ремонт надо завести-разработать форму Л
16. Железо
17. Нотаріальне оформлення документів
18. Башкирский государственный медицинский университет Министерства здравоохранения Российской Федерации1
19. Вариант 1 Спишите вставляя пропущенные буквы и раскрывая скобки обозначьте орфограммы
20. Проблемы управления персоналом организации