44161

Универсальная программно – аппаратная тестирующая система тестирования знаний с разделяемыми правами доступа по оптимальным методам

Дипломная

Информатика, кибернетика и программирование

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

Русский

2013-11-10

2.28 MB

13 чел.

118

ВВЕДЕНИЕ.

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

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

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

Опыт применения ЭВМ для построения прикладных систем обработки данных показывает, что самым эффективным инструментом здесь являются не универсальные алгоритмические языки высокого уровня, а специализированные языки для создания систем управления данными. Такие средства обычно включаются в состав систем управления базами данных (СУБД) , но они могут существовать и отдельно. СУБД дают возможность пользователям осуществлять непосредственное управление данными, а программистам быстро разрабатывать более со вершенные программные средства их обработки. Характеристики готовых прикладных пакетов определяются прежде всего принятой в СУБД организацией данных и типом используемого транслятора.

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

СУБД в полном варианте может содержать следующие компоненты: 1) среда пользователя, дающая возможность непосредственного управления данными с клавиатуры; 2) алгоритмический язык для программирования прикладных систем обработки данных, реализованный как интерпретатор. Последнее позволяет быстро создавать и отлаживать программы; 3) компилятор для придания завершенной программе вида готового коммерческого продукта в форме независимого EXE-файла; 4) программы-утилиты быстрого программирования  рутинных операций ( генераторы отчетов, экранов, меню и других приложений).

    Собственно СУБД - это, конечно, оболочка пользователя.

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

Наличие в СУБД языка программирования позволяет создавать сложные системы обработки данных, ориентированные под конкретные задачи и даже под конкретного пользователя. Есть СУБД, которые имеют только язык и не имеют оболочки пользователя. Они предназначены исключительно для программистов, и это системы компилирующего типа. Среда C++ Builder имеет

множество встроенных компонентов для работы с СУБД один из них это ADO (ActiveX Data Objects) в связке с программным драйвером BDE Administrator который может быть совмещен с такими СУБД как Microsoft Sql server,Mysql .


1  ПОСТАНОВКА ЗАДАЧИ

       Задача создания  универсального программно – аппаратного тестирующего комплекса заключается в разработке продукта для удобной работы пользователей (студентов).

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

      При входе в систему должна осуществляться идентификация пользователя и поддержка

двух режимов работы:

1) администратора - для создания баз знаний (сценарии тестирования, вопроссы с вариантами ответов из определенной области знаний, ведения конроля над тестирующимся (установка критериев тестирования – времени прохождения теста, дату и факультет), создание получения твердой копии отчета.

2) пользователя - для прохождения тестирования (выбор правильного ответа из списка предложенных)

Разрабатываемый комплекс должен взаимодействовать с базой знаний содержашей исходные данные о тестировании пользователей.

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

        По функциональным    характеристикам определяются следующие требования:

  •  разграничение прав пользователей системы;
  •  создание шифрованного канала на базе VPN (Virtual Private Network).
  •  возможность одновременной работы нескольких пользователей;
  •  возможность загрузки разных вариантов теста для нескольких пользователей;
  •  внесение и редактирование данных в тестирующую систему;
  •  поиск основных объектов;
  •  возможность извлечения разноформатного результата (вывод на экран, распечатка ведомости на принтере, передача результатов по сети);
  •  создание безотказной и надежной работы системы в целом с помощью с учетом топологических особенностей локально- вычислительной сети.

2 ОБЗОР ЛИТЕРАТУРНЫХ ИСТОЧНИКОВ

          

           В последнее время популярным методом непрерывного и интенсивного контроля знаний является тестирование. Тесты позволяют в кратчайший срок проверить знания больших групп учащихся, выявить пробелы при изложении учебного материала и т.п. Опыт показывает, что и сами ученики предпочитают тестирование, другим методам контроля знаний, считая его наиболее объективным.[5]

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

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

            К достоинствам тестирования следует отнести:

Большая объективность и, как следствие, большее позитивное стимулирующее воздействие на познавательную деятельность студента, учащегося;

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

ориентированность на современные технические средства на использование в среде компьютерных (автоматизированных) обучающих систем;

универсальность, охват всех стадий процесса обучения.

Отметим другие достоинства. Тестированный опрос многофункционален. Он позволяет быстрее понять, как дальше работать с данным студентом, а также помогает лектору скорректировать курс [7].

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

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

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

Одним из недостатков тестового метода контроля знаний студентов является то, что создание тестов, их унификация и анализ - это большая кропотливая работа [8].

Чтобы довести тест до полной готовности к применению необходимо несколько лет собирать статистические данные, хотя бы с потоком студентов 100-120 человек [7].

Возможно возникновение и других трудностей [9]. Довольно часто встречается значительный субъективизм в формировании содержания самих тестов, в отборе и формулировке тестовых вопросов, многое также зависит от конкретной тестовой системы, от того, сколько времени отводится на контроль знаний, от структуры включенных в тестовое задание вопросов и т.д.

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

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

VPN гарантирует надежную защиту информации, передаваемой через Интернет[11].

Исторически сложилось так, что для защиты передаваемой на большие расстояния информации компании прокладывали собственные линии связи. Этот способ имеет ряд существенных недостатков - не гарантирует надежной защиты коммуникаций, значительно ограничен в применении и требует больших затрат средств и времени. К примеру, как протянуть линию за сотрудником, если он периодически меняет место дислокации или постоянно разъезжает по всей стране?

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

Благодаря развитию криптографических технологий появился способ преодолеть эти ограничения - использовать технологию защищенных виртуальных частных сетей (Virtual Private Network - VPN), надежно шифрующих информацию, передаваемую по дешевым открытым сетям, включая Интернет.

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

Потребительская сущность VPN - "виртуальный защищенный туннель, или путь", с помощью которого можно организовать удаленный защищенный доступ через открытые каналы Интернета к серверам баз данных, Web, FTP и почтовым серверам. Физическая сущность технологии VPN определяется тем, что она может защитить трафик любых информационных интранет - и экстранет-систем, аудио - видеоконференций, систем электронной коммерции и т. п.

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

вы получаете защищенные каналы связи и защищенный трафик отдельных
приложений по цене доступа в Интернет, что в несколько раз дешевле собственных
линий;

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

обеспечивается масштабирование: VPN не создаст проблем роста, что сохранит уже сделанные раньше инвестиции в инфраструктуру безопасности.

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

* защита всего трафика между многочисленными офисами корпорации, когда шифрование выполняется только на выходе из офисов во внешние сети; такая топология образует "защищенный периметр" вокруг локальных сетей корпорации (рис.2.1);

                         

      Рис. 2.1

*защита трафика ряда приложений внутри корпоративных сетей (это также важно, поскольку большинство атак осуществляется из внутренних сетей), при этом образуются отдельные, непересекающиеся VPN для выделенных групп пользователей или приложений (рис. 2.2).

                                                               

Рис.2.2


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

Основная задача VPN - защита трафика. Эта задача исключительно сложна уже на криптографическом уровне, поскольку VPN должна удовлетворять большому числу требований. И в первую очередь обладать надежной криптографией, гарантирующей от прослушивания, изменения, отказа от авторства (это определяется протоколом IPsec), иметь надежную систему управления ключами, защищать от «подыгрывающих» (replay) атак и проверять, "жив" ли абонент в данный момент (это обеспечивается принятым в 1998 году протоколом IKE). Применение стандартных протоколов IPsec/IKE в VPN-системах сегодня практически обязательно. В противном случае ни один заказчик не сможет быть уверенным, что поставщик VPN создал криптографически целостную и надежную систему. Кроме того, в будущем она окажется несовместима с VPN, применяемыми контрагентами корпорации, что в конце концов приведет к проблеме "вавилонской башни".

Следующим требованием является обеспечение масштабируемости конкретной VPN. Многолетний опыт показывает, что наиболее успешно для этого применяются программные VPN-агенты, которые:

•    могут обеспечить защиту трафика на всех типах компьютеров — рабочих станциях, серверах и шлюзах (на выходе из локальных сетей в открытые сети);

•    работают на всех популярных ОС.

Вторая составляющая масштабируемости - централизованное, целостное и оперативное управление VPN. Необходимо определиться со значениями этих понятий в данном контексте:

централизованное - конфигурирование VPN происходит в одном месте на одной рабочей станции;

целостное - вся VPN создается как единое целое, поскольку совершенно недопустима ситуация, когда разные узлы имеют несовместимую политику безопасности или включаются в VPN не одновременно;

оперативное - созданная в центре «конфигурация VPN» должна автоматически за считанные секунды быть разослана на все узлы VPN. Для больших систем неприемлемо, чтобы оператор последовательно, пусть и удаленно, конфигурировал все 300 VPN-узлов или передавал им конфигурации на дискетах.

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

Для предоставления удаленного доступа мобильным пользователям центр управления должен допускать подключение компьютеров, IP-адрес которых ему заранее неизвестен. Участники информационного обмена опознаются по их криптографическим сертификатам. Так как криптографический сертификат пользователя является электронным паспортом, он, как и любой паспорт, должен соответствовать определенным стандартам. В криптографии это Х.509.

Требование к поддержке стандарта Х.509 далеко не случайно. Не секрет, что ни одна криптозащита, построенная на открытой криптографии, не может существовать без инфраструктуры открытых ключей - PKI (Public Key Infrastructure), в задачу которой входит:

создание и подпись сертификатов, что требует наличия иерархической системы
нотариусов, так как пользователь
VPN должен получать свой сертификат по месту работы, а не ездить за ним, например, в центральный офис или в какую-то иную организацию;

передача сертификатов на электронный носитель пользователя (смарт-карта, e-token, дискета) и публикация их на сервере сертификатов с тем, чтобы любой участник VPN мог легко получить сертификат своего партнера;

регистрация фактов компрометации и публикация "черных" списков отозванныхсертификатов.

VPN должна взаимодействовать с системой PKI в целом ряде точек (передача сертификата на подпись, получение сертификата и "черного" списка при установлении взаимодействия и т. п.). Очевидно, что это взаимодействие с чуждой по отношению к VPN системой может осуществляться только при условии полно поддержки международных стандартов, которым удовлетворяет большинство современных PKI-систем (например, Baltimore, Entrust, Verisign ).

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

Обеспечение безопасности - задача построения множества линий обороны и наблюдения за ними. Как бы вы ни осуществляли это наблюдение - ручной разборкой регистрационной информации или с помощью изощренных систем intrusion detection (обнаружения вторжения), вы должны сначала получить эту информацию для "разбора полетов". Чтобы это было возможно, VPN должна создавать на всех своих агентах:

LOG-файлы с регистрационной информацией;

SNMP-сообщения о текущих атаках, сбоях и проблемах.

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

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

Все необходимые для этого данные (ключи, сертификаты, конфигурация) находятся на его смарт-карте, электронном ключе или дискете. Если корпорация использует так называемые серверы доступа (технология single-sign-on), то VPN должна работать совместно с такой системой, не подключая VPN тем пользователям, которые не прошли авторизацию в системе аутентификации.[11]

 VPN образует "непроницаемые" каналы связи поверх открытых сетей. В реальной жизни организации всегда требуется, чтобы сотрудники имели доступ из VPN в открытые сети и Интернет. Контроль в критичной точке контакта с открытой сетью должен осуществляться межсетевыми экранами. Более правильная ситуация - VPN обеспечивает функции межсетевого экрана в каждой точке, где есть ее агент. Такой "распределенный" межсетевой экран контролируется из того же центра безопасности. Межсетевой экран и VPN являются комплементарными системами, решая две связанные задачи:

использование открытых сетей в качестве канала недорогой связи (VPN);

обеспечение защиты от атак из открытых сетей при работе с открытой информацией, содержащейся в этих сетях (межсетевой экран).

           Гарантируя защиту передаваемой информации, VPN не обеспечивает ее защиту во время хранения на конечных компьютерах. Эта задача решается целым рядом специальных средств:

систем криптозащиты файлов и дисков (а также почты);

систем защиты от несанкционированного доступа к компьютерам;

антивирусных систем и т. п.

            Необходимо обратить внимание на сложную взаимосвязь продуктов защиты информации. Например, система защиты компьютера от несанкционированного доступа должна работать с теми же смарт-картами, что и VPN, а это требует реализации в обеих системах единого интерфейса доступа к смарт-карте (например, PKCS#11 фирмы RSA)."Правильные" средства защиты информации должны обладать следующим набором характеристик:

соответствие открытым международным стандартам;

открытые интерфейсы к другим средствам защиты информации;

способность взаимодействовать с одними и теми же "интегрирующими"
элементами системы;

способность к масштабированию.

      Продукты, создаваемые для защиты информации, должны быть совместимы с системами PKI, известными на российском рынке. И прежде всего с сервером сертификатов - программным средством управления VPN. Сервер сертификатов предназначен для хранения в виде базы данных открытых сертификатов всех пользователей VPN. Он осуществляет автоматическую раздачу сертификатов VPN-устройствам и взаимодействие с внешними системами PKI.[11]

  Итак, начав с отдельного средства, обеспечивающего оперативное решение проблемы защиты информации, - VPN, мы рассмотрели процесс наращивания системы, добавив сначала необходимые компоненты, без которых VPN не может функционировать вообще (PKI, криптоплагины, межсетевые экраны), и дополнили затем общую картину более полным спектром продуктов.[11]

        Виртуальные частные сети (Virtual Private Network) - по своей сути довольно широкий термин, не определяющий определенного типа сетей или протоколов, но в целом, предполагающий использование «туннельной» пересылки конфиденциальных данных по сетям общего пользования. В настоящей главе рассмотрен метод формирования туннелей по протоколу IP Sec (Internet Protocol Security); [4]

                Протокол IPSec, реализованный в рамках проекта FreeS/WAN, обычно используют для объединения фрагментов корпоративных сетей с помощью сетей общего пользования. При этом данные, передаваемые из одной сети в другую, инкапсулируются в пакеты другого типа, шифруются исходящим шлюзом при передаче через сети общего пользования и преобразуются в исходный вид принимающим шлюзом. В настоящее время нет достоверной информации о безопасности этого протокола. [4]

                Для хранения и передачи информации в настоящие время применяется технология клиент-сервер базирующаяся на визуальных компонентах ADO (ActiveX Data Objects) в связке с программным драйвером BDE Administrator и сервером баз данных Mysql. [1]   

                В настоящее время базы данных находят широкое применение в практически любых информационных системах, начиная с простейшего любительского сайта и заканчивая корпоративными базами данных. Для организации интерфейса между конечными пользователями (клиентскими программами) и базами данных, а также администрирования баз данных используется определенный класс программного обеспечения, называемый серверами баз данных. MySQL - многопользовательский, много поточный SQL-сервер баз данных, разработанный компанией MySQL AB. К числу достоинств этого сервера следует отнести высокую производительность, устойчивость к ошибкам и простоту в использовании, а также невысокую стоимость. [4]

3 СИСТЕМОТЕХНИЧЕСКИЙ И ВАРИАНТНЫЙ АНАЛИЗ

                  В данной работе в качестве объекта проектирования выступает программно – аппаратный комплекс тестирования знаний с разделяемыми правами доступа. Ниже будут рассмотрены основные принципы системного подхода применительно к объекту проектирования.

3.1  Принцип конечной цели

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

       3.2  Принцип единства и связности

На рисунке 3.2 показано общее представление проектируемого комплекса как единого целого.

Рисунок 3.2 – «внешний» взгляд на комплекс

где   

S – проектируемый комплекс;

Х – поток входных данных для осуществления тестирования;

Z – действия комплекса, определяющие способ обработки потока входных данных;

Y – результат обработки потока входных данных(поток выходных данных-результат тестирования).

При рассмотрении проектируемого комплекса изнутри его можно представить как совокупность элементов, реализующих следующие действия:

- ввод данных пользователем системы;

- определение метода тестирования по введенным данным (занесение пользователя в нужную категорию тестирования);

- определение параметров тестирования (загрузка нужных тестов);

- обработка исходных данных по требуемому алгоритму.

3.3   Принцип модульного построения

             На рисунке 3.3 проектируемый комплекс представлен в виде совокупности модулей, реализующих основные элементы комплекса.

рисунок 3.3 Проектируемый комплекс.

                                    3.4   Принцип иерархии

          На рисунке 3.4 представлена проектируемая система в виде иерархии по функциональному принципу разделения.

Рисунок 3.4 – иерархия по функциональному принципу разделения.

3.5 Принцип функциональности

Ниже приведено краткое описание элементов системы, определенных в пункте 3.2

- ввод данных пользователем системы:

Информация о пользователе, которая необходима для дальнейшей работы в тестирующем комплексе - фамилия, имя, специальность, курс, вариант теста.   По этой информации будет проводиться анализ выявления результата.   

- определение метода тестирования по введенным данным (занесение пользователя в нужную категорию тестирования):

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

-определение параметров тестирования (загрузка нужных тестов):

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

- Обработка исходных данных по требуемому алгоритму:

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

3.6  Принцип развития

           Разрабатываемый программно – аппаратный комплекс тестирования знаний с разделяемыми правами доступа проектируется на основе архитектуры клиент сервер и использует СУБД Mysql.

Сравнение MySQL с другими СУБД.

         Существует ряд критериев, по которым можно выбрать СУБД - платформу MySQL . Одним из них является перечень операционных систем, под управлением которых способна работать СУБД. В этом разделе, безусловно, лидирует MySQL, которая способна работать на большинстве из имеющихся на настоящее время операционных систем. Некоторые из них имеют значительно более низкую стоимость, чем продукты фирмы Microsoft, что, конечно, ведет к снижению затрат при внедрении в систему тестирования.

Таблица 3.1 - Поддерживаемые операционные системы.

Показатель

ОС

MS SQL Server

Windows NT,2000 (Intel и Alpha)

MySQL

Linux (x86, libc6,S/390,IA64, Alpha, Sparc), Windows 95/98/NT/2000/XP, Solaris 2.9 (Sparc, 64-bit, 32-bit), FreeBSD 4.x ELF (x86), Mac OS X v10.2, HP-UX 10.20 (RISC 1.0), HP-UX 11.11 (PA-RISC 1.1 или 2.0), AIX 5.1 (RS6000), QNX 6.2.0 (x86), Novell NetWare 6 (x86), SCO OpenUnix 8.0 (x86), м SGI Irix 6.5, Dec OSF 5.1 (Alpha)

 

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

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

Таблица 3.2 -Минимальные требования к серверу.

Показатель

ОС

MS SQL Server

Pentium II 350 MHz , ОЗУ – 128 Мбайт, HDD - 250 Мбайт

MySQL

Pentium 100 MHz , ОЗУ - 64 Мбайт (минимум), 100 Мбайт свободного места на диске

         Для изучения стоимости лицензий приведем ориентировочные цены на указанные СУБД с целью их установки на сервер с 2 процессорами и 25-30 клиентских подключений (типичная для многих ЛВС минимальная конфигурация сети, использующаяся на начальном этапе внедрения  в систему тестирования). Как  видно из приведенных данных, стоимость начальных затрат на лицензии Microsoft SQL Server в 35,6 раза выше, чем у MySQL. Фактически, стоимость MySQL не зависит от количества подключаемых рабочих мест, поэтому с ростом количества необходимых лицензий это преимущество увеличивается пропорционально количеству рабочих мест в  системе тестирования.

Таблица 3.3 - Примерная стоимость СУБД для 30 подключений

Название СУБД

Цена, $

Кол-во

Стоимость,  $

SQL Server 2000 Enterprise Edition English OpenLicensePack B *

6643,97

1

6643,97

SQL Server 2000 ClientAccessLicense English OpenLicensePack B*

151,94

30

4558,2

Всего на использование SQL Server 2000

 

 

11202,17

MySQL Pro 10..49 licenses**

315

1

315

* - по данным softline на апрель 2003 г .

** - по данным сайта http :// www . mysql . com на апрель 2003 г .

        Визуальная среда использует полный класс API компонентов C++ Builder6. Такой подход позволяет создать гибкую, легко изменяемую систему, которую можно без особых затрат дополнять новыми компонентами и классами  отвечающими самым современным стандартам дизайна  и безопасности. При этом отпадает необходимость изменять уже существующее программное обеспечение, что позволяет добиться еще большей экономии ресурсов и времени разработки.

  1.  ОПИСАНИЕ АЛГОРИТМА

       Блок-схема взаимодействия программ в программно-аппаратном комплексе тестирования знаний с разделяемыми правами доступа приведена на рисунке 4.1

Рисунок 4.1

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

          Блок схема главной тестирующей программы Test.exe приведена на

        рисунке 4.2

                          

рисунке 4.2 Блок схема главной тестирующей программы Test.exe.

 

 

 

  1.  ОПИСАНИЕ ПРОГРАММ

Test.exe - главная тестирующая форма.

            Основная программа, без которой невозможно эксплуатировать программно-аппаратный комплекс тестирования знаний с разделяемыми правами доступа, – это Test.exe , которая состоит из следующих компонентов языка программирования C++Builder 6.0 - форм: RegForm.cpp (изображена на рисунке 5.1), ResForm.cpp (изображена на рисунке 5.2), QuestForm.cpp (изображена на рисунке 5.3).

 

                                        Рисунок 5.1. форма RegForm.cpp

          На рисунке 5.1 видно, что форма RegForm.cpp состоит из стандартного компонента Form, на который помещены компоненты: 1) Eedit  (3 шт.) - для ввода данных в форму и вывода текущего логина на экран; 2)MaskEdit - для сопоставления номера тестирующегося; 3) DBLookupComboBox (2 шт.) - для выбора из базы данных нужного значения; 4) CspinEdit – для выбора нужного значения факультета, используя прокрутку вверх и вниз; 5) кнопки BitBtn (2шт.) - создающие событие по нажатию на них (начало тестирования и отмена – закрытие окна формы).

                                                                                                            

Рисунок 5.2. форма ResForm.cpp

           На рисунке 5.2 видно, что форма ResForm.cpp состоит из стандартного компонента Form, на которой помещены компоненты: 1) Image - для вывода рисунка на экран, предварительно загрузив картинку в формате *.WMF; 2)Label  (4 шт.) - для вывода информации на метки (результат, количество процентов, результат сохранен, крестик закрытия окна результата).

 

Рисунок 5.3. форма QuestForm.cpp

         На рисунке 5.3 видно, что форма QuestForm.cpp состоит из стандартного компонента Form, на которой помещены компоненты: 1) Timer - для учета времени тестирования,  2) StatusBar - для вывода на экран номера текущего вопроса, а также  количества вопросов; 3) ToolBar - для размещения на нем элементов переключения вопросов; 4) кнопки - ToolButton (2 шт.) - для перехода по вопросу с начального до конечного и обратно; 5) набор переключателей - RadioGroup - для указания нужного варианта ответа в тесте; 6) NMMsg - для вывода на экран списка вариантов ответов; 7) ImageList - для вывода изображения в область вопроса; 8) Table - для работы с базой данных, определяя основные поля ее таблицы; 9) Query (4 шт.) - для  создания запросов на вывод вопросов теста,  удаления вариантов, сохранения вариантов  и создания запроса на  удаление списка вопросов.    

 

5.1 Описание переменных, процедур и функций программы Test.exe

Глобальные переменные, доступные из любого места программы:

 

int NeedDept; //переменная служит для требования в программе ввода кода факультета;

int DefaultDept; //переменная служит для ввода факультет по умолчанию;

int NeedFinalPercent; //переменная служит для вывода на экран финальной формы с процентом ответов;

int DefaultScenario;//c помощью переменной задается сценарий по умолчанию

int CanSelectScenario;//c помощью переменной разрешается выбор сценария

int AttemptsAllowed; //c помощью переменной задается число попыток прохождения теста в один день;

int MinPercentToSave; //c помощью переменной задается минимальный процент сохранения;

int UpTo;//можно поднять процент ответов до  параметра, переданного в этой переменной;

int ReceptMode;//переменная задает режим тестирования поступающих;

AnsiString PServer;//хост на котором располагается сервер печати

void __fastcall TQuest::FormShow(TObject *Sender)

{

conTest->Connected=true; //с помощью этой функции открываем соединение с Базой данных Mysql;

QryQuest->Close();  //функция остановки запроса;

QryQuest->SQL->Clear(); //функция очистки  текста запроса;

//функции создания  нового текста запроса;

QryQuest->SQL->Add("SELECT Quest.No, PrintNo, Theme, Quest, Ans, Quest.Right ");

QryQuest->SQL->Add("FROM Quest ");

QryQuest->SQL->Add("WHERE  Quest.No IN ("+QuestList+")");

QryQuest->Open(); //функция создания запроса;

//функции создания глобальных свойств формы;

QuestNums=new TStringList();

QuestNums->CommaText=QuestList;

Answers=new TStringList();//создание  списка ответов на вопросы;

Correct=new TStringList();//создание списков номера корректных ответов;

Saved=new TStringList();//создание  списков маркеров - сохранен ли ответ;

AnswersAmount=new TStringList();//создание списков -количество вопросов;

//функция заполнения  глобальных свойств формы;

for (QryQuest->First();!QryQuest->Eof;QryQuest->Next()) {

   Answers->Add("0");

   Saved->Add("");

   Correct->Add("0");

   AnswersAmount->Add("5");

}

TStringList *answers = new TStringList();

for (QuestNum=1;QuestNum<=Correct->Count;QuestNum++) {

    FindQuest();

    Correct->Strings[QuestNum-1]=QryQuest->FieldByName("Right")->AsString;

    answers->Text=QryQuest->FieldByName("Ans")->AsString;

    AnswersAmount->Strings[QuestNum-1]=answers->Count;

}

delete answers;

QuestNum=1;

Caption="Тестирование: "+WindowHeader;

QryQuest->Last();//к последнему вопросу

QuestCount=QryQuest->RecordCount;//перемещение к номеру последнего вопроса (начиная с первого и до конца);

QryQuest->First();//перемещение к первому вопросу с конца;

Testing=true;//разрешаем  тестирование!

FindQuest();//переходим к вопросу который числится в списке первым

LoadRec(); //загружаем вопрос в форму

if (Time>0) Timer1->Enabled=true;//если  время на тест отлично от 0  - запускаем таймер!

}

//---------------------------------------------------------------------------

//функция загрузки нового вопроса

void TQuest::LoadRec()

{ TLocateOptions opt;

//1 загружаем текст вопроса

 TStream *Stream;

 TStringStream *Str=new TStringStream("");

 Stream =QryQuest->CreateBlobStream(QryQuest->FieldByName("Quest")  , bmRead);

 try {

     Str->CopyFrom(Stream,Stream->Size);

     Str->Position=0;

     RichText1->Lines->LoadFromStream(Str);

 }

 __finally

 {

     delete Stream;

     delete Str;

 }

   // выгружаем в специальную переменную ответы на вопрос;

   TStringList *answers = new TStringList();

   answers->Text=QryQuest->FieldByName("Ans")->AsString;

   AnsCount=answers->Count;//cчетчик количества ответов;

   if (StrToInt(Correct->Strings[QuestNum-1])>0)//если есть корректный ответ тогда...

   //пройдемся циклом по списук возможных ответов;

   for (int row=0; row<AnsCount; row++) {

   //для каждого ответа создадим объект TRadioButton

       ans[row]=new TRadioButton(GroupBox1);

       ans[row]->Parent=GroupBox1;//разместим его на GroupBox1

       ans[row]->Top=((QuestNum+row)%AnsCount+1)*15;

       ans[row]->Left=5;

       ans[row]->Width=780;

       ((TRadioButton *)ans[row])->Caption=answers->Strings[row];

       //если на вопрос уже ответили, то нужно поставить указатель напротив выбранного //ответа;

       if (row==StrToInt(Answers->Strings[QuestNum-1])-1)

             ((TRadioButton *)ans[row])->Checked=true;

   }

   else { //если ответа на вопрос нет, то

       int StudAnswer=-StrToInt(Answers->Strings[QuestNum-1]);//записываем ответ (если //не было, то записываем 0) и преобразуем его в положительное число

       //пройдемся циклом по списук возможных ответов

       for (int row=0; row<AnsCount; row++) {

       //для каждого ответа создадим объект TRadioButton

           ans[row]=new TCheckBox(GroupBox1);

           ans[row]->Parent=GroupBox1;

           ans[row]->Top=((QuestNum+row)%AnsCount+1)*15;

           ans[row]->Left=5;

           ans[row]->Width=780;

           ((TCheckBox *)ans[row])->Caption=answers->Strings[row];

           ((TCheckBox *)ans[row])->Checked=StudAnswer%2;//разделим по модулю 2

           StudAnswer=StudAnswer/2;

         }

   }

   delete answers;

if (Testing)

  StatusBar1->Panels->Items[1]->Text="Вопрос "+IntToStr(QuestNum)+" из "+IntToStr(QuestCount);

}

//---------------------------------------------------------------------------

//функция обработки нажатия клавиши <= (предыдущий вопрос);

void __fastcall TQuest::ToolButton1Click(TObject *Sender)

{

  if (QuestNum>1) {SaveRec(); QuestNum--; FindQuest(); LoadRec(); }

}

//---------------------------------------------------------------------------

//функция обработки нажатия клавиши => (cледующий вопрос);

void __fastcall TQuest::ToolButton2Click(TObject *Sender)

{

  if (QuestNum<QuestCount) { SaveRec(); QuestNum++; FindQuest(); LoadRec(); }

}

//---------------------------------------------------------------------------

//функция поиска вопроса

void TQuest::FindQuest()

{

for (QryQuest->First();QryQuest->FieldByName("No")->AsString!=QuestNums->Strings[QuestNum-1]; QryQuest->Next());

}

//---------------------------------------------------------------------------

//функция обработки выбора ответа;

void __fastcall TQuest::RadioGroup1Click(TObject *Sender)

{

AnsUpdated=true;

}

//---------------------------------------------------------------------------

//функция сохранения ответа на вопрос;

void TQuest::SaveRec()

{

int StudAnswer=0;

//если корректный ответ есть, то

if (StrToInt(Correct->Strings[QuestNum-1])>0) {

   //пройдемся циклом по элементу управления - выбор правильного ответа

   //удалим все ответы, а заодно выясним какой ответ выбран;

   for (int row=0; row<AnsCount; row++) {

      if (((TRadioButton *)ans[row])->Checked) StudAnswer=row+1;

      delete ans[row];

   }

}

//если корректного ответа нет

else {

   //пройдемся циклом по элементу управления - выбор правильного ответа

   //удалим все ответы, а заодно выясним, какой ответ выбран, и запишем число равное 2 в степени номера ответа (начиная с нуля);

   int Pow=1;

   for (int row=0; row<AnsCount; row++) {

      if (((TCheckBox *)ans[row])->Checked) StudAnswer+=Pow;

      Pow*=2;

      delete ans[row];

   }

   StudAnswer=-StudAnswer; //инвентируем получиное число

}

if (Answers->Strings[QuestNum-1]!=IntToStr(StudAnswer)) AnsUpdated=true;//выясняем //нужно ли обновлять ответ

Answers->Strings[QuestNum-1]=IntToStr(StudAnswer); //сохраняем код ответа

QrySave->Close();//останавливаем наш запрос;

if (Testing&&AnsUpdated) {

 QrySave->SQL->Clear();//удаляем текст запроса;

  if (Saved->Strings[QuestNum-1]=="Saved") {//вопрос уже задавался, значит проводим //обновление ранее сохраненного ответа

     QrySave->SQL->Add("UPDATE Exam");

     QrySave->SQL->Add("SET Answer="+IntToStr(StudAnswer));

     QrySave->SQL->Add("WHERE Student="+IntToStr(StudentCode)+" AND Question="+QryQuest->FieldByName("No")->AsInteger);

     }

  else {

     //если на вопрос отвечают впервые, потому добавляем ответ в СУБД Mysql

     QrySave->SQL->Add("INSERT INTO Exam");

     QrySave->SQL->Add(Format ("VALUES (%d,%d,%d,%d)", ARRAYOFCONST(((int)StudentCode,QryQuest->FieldByName("No")->AsInteger,StudAnswer,QryQuest->FieldByName("Right")->AsInteger))));

     Saved->Strings[QuestNum-1]="Saved";

     }

  QrySave->ExecSQL(); //выполняем запрос;

  AnsUpdated=false; //

}

}

//---------------------------------------------------------------------------

//процедура обработки таймера, таймер заведен на 1000 мк.сек;

//таймер запускается  в том случае, если пользователь ограничен временными рамками;

void __fastcall TQuest::Timer1Timer(TObject *Sender)

{

Time=Time-(double)1/86400;// сек= 1/86400  - уменьшаем отведенное время на 1 сек.

StatusBar1->Panels->Items[0]->Text=TimeToStr(Time); //в строку состояния печатаем //оставшееся время;

if (Time<0) Quest->Close();//если время вышло, закрываем форму – конец тестирования;

}

//---------------------------------------------------------------------------

//процедкура закрытия формы;

void __fastcall TQuest::FormCloseQuery(TObject *Sender, bool &CanClose)

{

Testing=false;

Timer1->Enabled=false;//отключаем таймер;

SaveRec();//сохраняем последний ответ;

//считаем количество правильных ответов;

float RightAnswers=0;

int RightPercent=0,i;

int ans, corr;

//создаем цикл и проводим его по ответам;

for (QuestNum=1;QuestNum<=QuestCount;QuestNum++) {

   ans=StrToInt(Answers->Strings[QuestNum-1]); //код ответа на вопрос

   corr=StrToInt(Correct->Strings[QuestNum-1]);//код правильного ответа

   if (corr>0) {

       if (ans==corr) RightAnswers=RightAnswers+1;

   }

   else {

       ans=-ans; corr=-corr;

       int errors=0;

       for (int e=ans^corr;e>0;e/=2) errors+=(e%2);

       RightAnswers=RightAnswers+1-(float)errors/StrToInt(AnswersAmount->Strings[QuestNum-1]);

   }

}

RightPercent=100*RightAnswers/Answers->Count;//считаем процент правильных ответов

Quest->Hide(); //скрываем форму;

//для режима вступительных экзаменов его нужно проверить;

if (RightPercent<UpTo) {

// придется подправить

int diff=ceil((float)(UpTo-RightPercent)/100*Answers->Count);

for (QuestNum=1;(QuestNum<=QuestCount)&&(diff>0);QuestNum++) {

   ans=StrToInt(Answers->Strings[QuestNum-1]);

   corr=StrToInt(Correct->Strings[QuestNum-1]);

   if (ans!=corr) {

      QrySave->SQL->Clear();

      if (Saved->Strings[QuestNum-1]=="Saved") {//если уже задавался;

        QrySave->SQL->Add("UPDATE Exam");

        QrySave->SQL->Add("SET Answer="+IntToStr(corr));

        QrySave->SQL->Add("WHERE Student="+IntToStr(StudentCode)+" AND Question="+QuestNums->Strings[QuestNum-1]);

        QrySave->ExecSQL();

      }

      else {

        QrySave->SQL->Add("INSERT INTO Exam");

        QrySave->SQL->Add(Format ("VALUES (%d,%d,%d,%d)", ARRAYOFCONST(((int)StudentCode,StrToInt(QuestNums->Strings[QuestNum-1]),corr,corr))));

        QrySave->ExecSQL();

        Saved->Strings[QuestNum-1]="Saved";

      }

      diff--;

  }

}

}

QryQuest->Last();

QryQuest->Close();

//если все - таки нужна форма с результатом, то покажем ее;

if (NeedFinalPercent==1) {

   ResForm->Show();

   if (RightPercent>=MinPercentToSave)

           ResForm->Label2->Caption=FormatFloat("##0%",RightPercent);

//            ResForm->Label2->Caption=FormatFloat("##0.0 ,баллов",RightPercent);

   else

           ResForm->Label2->Caption="Плохо!";

  /* AnsiString strBal;

   if (RightPercent>20)

     {

      if (RightPercent>56)

       {

         if (RightPercent>81)strBal="Отлично!";

         else strBal="Хорошо!";

       }

      else strBal="Удовлетворительно!";

     }

   else strBal="Плохо!";

   ResForm->BalWord->Caption=strBal;  */

   ResForm->Update();

}

//если полученый процент больше или равен минимальному;

if (RightPercent>=MinPercentToSave) {

   if (ResForm!=NULL) {

       ResForm->Label3->Caption="Результат сохраняется...";

       ResForm->Update();

       //сохранить неотмеченные вопросы;

       for (int i=0; i<Saved->Count; i++)

           if (Saved->Strings[i]!="Saved"){

               QrySave->Close();

               QrySave->SQL->Clear();

               QrySave->SQL->Add("INSERT INTO Exam");

               QrySave->SQL->Add(Format ("VALUES (%d,%s,%d,%s)", ARRAYOFCONST(((int)StudentCode,QuestNums->Strings[i],0,Correct->Strings[i]))));

               QrySave->ExecSQL();

       }

       ResForm->Label3->Font->Color=clGreen;

       ResForm->Label3->Caption="Результат сохранен";

   }

}

else { //если результат очень плохой, то можно удалить и фамилию студента;

   QrySave->Close();

   QrySave->SQL->Clear();

   QrySave->SQL->Add("DELETE FROM Exam WHERE Student="+IntToStr(StudentCode));  //удаляем все ответы плохого тестирования;

   QrySave->ExecSQL();

   QrySave->Close();

   QrySave->SQL->Clear();

   QrySave->SQL->Add("DELETE FROM Students WHERE Code="+IntToStr(StudentCode)); //удаляем даже код  и прочие реквизиты тестируемого

   QrySave->ExecSQL();

}

ResForm->Label4->Visible=true;

ResForm->Label4->Enabled=true;

//удалить списки

delete Answers;

delete AnswersAmount;

delete Correct;

delete Saved;

delete QuestNums;

//если нужно выводить результаты теста, то приостановим выполнение кода таким вот циклом;

if (NeedFinalPercent) {

   while (ResForm->Visible) Application->ProcessMessages();

}

//delete ResForm;

//отправить задание печати на сервер

if (PServer.Trim().Length()>0){

   NMMsg1->Host=PServer.Trim();

   try {

       NMMsg1->PostIt(IntToStr(StudentCode));

   }

   catch(...)

   {

   }

}

CanClose = true;

}

__fastcall TRegister::TRegister(TComponent* Owner)

       : TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TRegister::FormShow(TObject *Sender)

{

StudentCode=0;

ReadConfig(); //процедура считывания значений глобальных переменных из .ini файла

conTest->Connected=true; //устанавливаем соединение с сервером БД MySQL

//seTest->Active=true;

/*

try {

TblExam->Open();

}

catch(...) {};

if (TblExam->Active) {

   // если это режим продолжается, то можно сразу переходить к вопросам

   //TQuest* Quest=new TQuest(0);

   Quest->StudentCode=TblExam->FieldByName("Student")->AsInteger;

   TblExam->Active=false;

   Quest->WindowHeader="";

   Quest->ShowModal();

   Close();

   return;

}

TblExam->Active=false;

*/

QDepts->Open();//открываем запрос - запрашиваем у сервера специальности

QScTitle->Open();//открываем запрос - запрашиваем у сервера допустимые варианты //теста

SelectDefaultScenario(); //выбираем сценарий по умолчанию

DBLookupComboBox2->Enabled=CanSelectScenario;

//считываем при помощи API имя пользователя в системе

char UserName[20];

DWORD UserNameSize=20;

LPDWORD p=&UserNameSize;

GetUserName(UserName,p);

//strcpy(UserName,"test");

Edit3->Text=UserName; //показываем считанное имя пользователя в системе;

//Блок для перевода теста в режим "Вступительный Тест"

//Все поступающие заходят в систему под именем "test" 

// Тут перекрываются настройки из Megatest.ini

if (Edit3->Text=="test") ReceptMode=1;      //

//else ReceptMode=0;                          //

//ReceptMode=1;                                           

if (ReceptMode==1) {                        // Если режим тестирования "Вступительный Тест",

       MinPercentToSave=0;                 // то глобальные переменные восстанавливаем

       NeedFinalPercent=0;                 // так, чтоб при любых условиях сохранялся результат

       AttemptsAllowed=99;                 //

}                                           //

else                                        //

       UpTo=0;

       NeedFinalPercent=1;                 //

//--------------------------------------------

//для вступительного или поточного теста немного меняем

//вид формы и доступность некоторых элементов;

if (ReceptMode==1) {

   Register->ActiveControl=MaskEdit1;

   Edit1->Enabled=false;

   Edit2->Enabled=false;

   DBLookupComboBox1->Enabled=false;

   DBLookupComboBox2->Enabled=false;

   CSpinEdit1->Enabled=false;

   CSpinEdit1->Color=clBtnFace;

   return;

}

else

{

   MaskEdit1->Enabled=false;

   MaskEdit1->Visible=false;

   Label5->Caption="Для прохождения теста необходимо заполнить все поля формы!";

   //попробуем определить факультет;

   TLocateOptions opt;

   opt<<loPartialKey;

   if (QDepts->Locate("LoginLetters",AnsiString(UserName).SubString(1,2),opt))

           DBLookupComboBox1->KeyValue=QDepts->FieldByName("DeptNo")->AsInteger;

   else

           DBLookupComboBox1->KeyValue=-1;

   //попробуем определить курс;

   if (DBLookupComboBox1->KeyValue!=-1) {

       int year=FormatDateTime("yyyy",Now()).ToInt();

       int month=FormatDateTime("mm",Now()).ToInt();

       if (month>8) year++;  //после сентября уже идет следующий набор

       int StudentInYear=-1;

       try {StudentInYear=AnsiString(UserName).SubString(3,2).ToInt();}

       catch (...) {};

       StudentInYear+=2000;

       CSpinEdit1->Value=year-StudentInYear;

   }

}

}

//--------------------------------------------------------------------------

//обработка нажатия клавиши BitBtn2 - "Отмена"

void __fastcall TRegister::BitBtn2Click(TObject *Sender)

{

Close(); //закрываем форму, а поскольку она главная, то и все приложение;

}

//---------------------------------------------------------------------------

//процедура считывания и установки глобальных параметров приложения

void TRegister::ReadConfig()

{

//в начале определим имя и путь .ini файла

//для этого возьмем название нашего приложения (под каким именем запущено)

//и сменим расширение на ini;

AnsiString IniFileName=ParamStr(0).SubString(1,ParamStr(0).LastDelimiter("."))+"ini";

TIniFile *pIniFile = new TIniFile(IniFileName); //создали класс для работы с ini и инициализировали его задав имя .ini файла;

//теперь осталось считать соответствующие параметры из файла

DefaultScenario=pIniFile->ReadInteger("Login", "DefaultScenario", 0);

CanSelectScenario=pIniFile->ReadInteger("Login", "CanSelectScenario", 1);

AttemptsAllowed=pIniFile->ReadInteger("Login", "AttemptsAllowed", 99);

MinPercentToSave=pIniFile->ReadInteger("Result", "MinPercentToSave", 0);

NeedFinalPercent=pIniFile->ReadInteger("Result", "NeedFinalPercent", 1);

UpTo=pIniFile->ReadInteger("Result", "UpTo", 0);

PServer=pIniFile->ReadString("Result","PServer","");

ReceptMode=pIniFile->ReadInteger("Login","ReceptMode",0);

delete pIniFile;//освобождаем память, занятую классом;

}

//---------------------------------------------------------------------------

//Обработка нажатия клавиши  "ОК";

void __fastcall TRegister::BitBtn1Click(TObject *Sender)

{

nDept=DBLookupComboBox1->KeyValue;//код факультета

nScenar=DBLookupComboBox2->KeyValue;//код сценария

 //вначале проверим заполнил ли пользователь фамилию и имя

if (Edit1->Text.Length()==0||Edit2->Text.Length()==0) {

       MessageBox(0,"Обязательно нужно указать фамилию и имя тестирующегося","Ошибка",MB_OK+MB_ICONERROR);

       return;

}

//если программа требует ввода кода факультета и код факультета не задан - предупреждаем

if (NeedDept&&nDept<0) {

       MessageBox(0,"Обязательно нужно указать факультет","Ошибка",MB_OK+MB_ICONERROR);

       return;

}

// выясняем не превышено ли допустимое количество попыток?

//правда если = или > 99 то выяснения опускаются

if (AttemptsAllowed<99) {

// qry - запрос который вернет нам лиш одну запись

// количество попыток которые пользователь предпринял

// после начала сегодняшнего дня по выбранной теме теста

TADOQuery* qry = new TADOQuery(this);//создаем объект Запрос;

qry->Connection=conTest;//показываем соединение;

qry->SQL->Add("Select count(*) as WasAttempts FROM Students WHERE Login='"+Edit3->Text.Trim()+"' ");

qry->SQL->Add("AND Scenario = "+IntToStr(nScenar));

qry->SQL->Add("AND TestDate > '"+ FormatDateTime("yy-mm-dd",Now())+"'");

qry->Open(); //открываем запрос;

int WasAttempts=qry->FieldByName("WasAttempts")->AsInteger;// выводим  //количество вопросов;

qry->Close(); //теперь можно закрыть запрос;

delete qry;//освобождаем память;

//если пользователь за сегодня исчерпал ресурс попыток  - сообщаем ему об этом

 if (WasAttempts>=AttemptsAllowed)  {

       MessageBox(0,("Администратором ограничено число попыток в один день - "+IntToStr(AttemptsAllowed)+"\nВы можете попробовать другой вариант теста").c_str(),"Доступ закрыт",MB_OK+MB_ICONERROR);

       return;

 }

}

//Quest - это форма которая представляет вопросы и сохраняет ответы

Quest->WindowHeader=Edit1->Text+" "+Edit2->Text+"        Тема: "+DBLookupComboBox2->Text; //это свойство - заголовок окна

Quest->Time=QScTitle->FieldByName("Time")->AsDateTime;  //время теста

QDepts->Close();

QScTitle->Close();

// Записываем данные о студенте в БД Mysql;

TADOQuery* qry = new TADOQuery(this);

qry->Connection=conTest;

qry->SQL->Add("INSERT INTO Students ");

qry->SQL->Add("(Surname, Name, Login, Dept, Course, Students.Group, TestDate, Scenario) ");

qry->SQL->Add(" VALUES ");

qry->SQL->Add(" ( :Surname, :Name, :Login, :Dept, :Course, :Group, '"+ FormatDateTime("yy-mm-dd hh:mm:ss",Now())+"' , "+IntToStr(nScenar)+" )");

qry->Parameters->ParamValues["Surname"]=Edit1->Text; //Фамилия

qry->Parameters->ParamValues["Name"]=Edit2->Text;//Имя

qry->Parameters->ParamValues["Login"]=Edit3->Text; //Логин

if (DBLookupComboBox1->KeyValue>=0)

       qry->Parameters->ParamValues["Dept"]=nDept; //Специальность

else

       qry->Parameters->ParamValues["Dept"]=1;

qry->Parameters->ParamValues["Course"]=CSpinEdit1->Value;

if (ReceptMode==1)

   qry->Parameters->ParamValues["Group"]=StrToInt(MaskEdit1->Text);  //номер экзаменационного листа

else

   qry->Parameters->ParamValues["Group"]=0;

//qry->Parameters->ParamValues["TestDate"]=FormatDateTime("mm/dd/yyyy",Now());

//qry->Parameters->ParamValues["Scenario"]=DBLookupComboBox2->KeyValue;

qry->ExecSQL(); //выполняем получившийся запрос;

delete qry;//очищаем память;

//теперь нужно получить код данного сеанса тестирования;

//так как предыдущим запросом мы создали запись, соответствующую данному сеансу,

//то нам остается считать значение последнего идентификатора вставленной записи,

//конечно, это может привести к логической ошибке в том случае, если перед //выполнением текущего запроса

//другой пользователь выполнит предыдущий,

//но такая ситуация вероятна в случае, когда нагрузка на сервер очень высокая - скорее //всего что Mysql

//выдаст ошибку об этом;

qry = new TADOQuery(this);

qry->Connection=conTest;

qry->SQL->Add("SELECT LAST_INSERT_ID()");

qry->Open();

StudentCode=qry->FieldByName("last_insert_id()")->AsInteger;

qry->Close();

delete qry;

// Подготавливаем вопросы, для этого нужно:

// выбрать все вопросы по нужным темам.

Query1->Close();

Query1->SQL->Clear();

Query1->SQL->Add("SELECT Quest.Theme, Quest.No ");

Query1->SQL->Add("FROM Quest,Scenar WHERE  Quest.Theme=Scenar.Theme ");

Query1->SQL->Add("AND Scenar.ScNo = "+IntToStr(nScenar)+" AND Scenar.Needed > 0 ");

Query1->Open();

// Выбираем нужное количество вопросов по каждой теме,

//для этого проходим по таблице сценариев

//и дополняем список вопросов из темы, относящейся к варианту теста,

//количеством, определенным свойством темы "TblScenarNeeded";

Dest= new TStringList();//создаем список вопросов студенту, инициализируем //глобальную переменную;

TblScenar->Active=true;//открываем таблицу сценариев;

for (TblScenar->First();!TblScenar->Eof;TblScenar->Next())

  if ((TblScenarScNo->AsInteger==nScenar)&&(TblScenarNeeded->AsInteger>0))

     PrepareQuestions(StudentCode, TblScenarTheme->AsInteger, TblScenarNeeded->AsInteger);//процедура дополнения;

//так как вопросы сгруппированы по темам,

//нужно еще раз перемешать вопросы;

//для этого создадим еще один список,

//и в него будем переносить случайные темы из  Dest;

if (Dest->Count>0) {

 TStringList* Dest2= new TStringList();

 int n;

 while (Dest->Count>0) {

     n=random(Dest->Count);

     Dest2->Add(Dest->Strings[n]);

     Dest->Delete(n);

 }

 Quest->QuestList=Dest2->CommaText; //заполняем свойство в форме Quest, согласно //этому свойству и будет выводиться список вопросов;

 delete Dest2;

}

delete Dest;

Quest->StudentCode=StudentCode; //передаем  в Quest идентификатор теста;

TblScenar->Active=false;

//TblExam->Active=false;

Query1->Close();

Register->Hide();//скрываем форму;

Quest->ShowModal(); //открываем форму для проведения теста;

Close();

}

//---------------------------------------------------------------------------

//функция приготовления вопросов;

//принимаемые параметры: StudentCode - код студента;  Theme - вариант теста(тема); //Needed - количество необходимых вопросов;

void TRegister::PrepareQuestions(int StudentCode, int Theme, int Needed)

{

randomize();

TStringList* Source= new TStringList();//объект для хранения всех вопросов по //выбранному варианту теста;

Source->Clear();

//Query1 – запрос, содержащий все вопросы по выбранному варианту теста;

//перед вызовом процедуры текст запроса должен быть сформирован,

//а запрос выполнен;

//проходим циклом от первой до последней записи и в Source копируем номера вопросов;

for (Query1->First();!Query1->Eof;Query1->Next())

  if (Query1->FieldByName("Theme")->AsInteger==Theme)

      Source->Add(IntToStr(Query1->FieldByName("No")->AsInteger));

//если после формирования полного списка вопросов

//их количество меньше требуемого - сообщаем об этом пользователю;

if (Source->Count<Needed) {

  MessageBox(0,("Тема: "+IntToStr(Theme)+"\nВопросов необходимо по сценарию: "+IntToStr(Needed)+"\nВопросов имеется в наличии: "+IntToStr(Source->Count)+"\nБудет подготовлено вопросов: "+IntToStr(Source->Count)).c_str(),"Ошибка",MB_OK+MB_ICONERROR);

  Needed=Source->Count;

}

//Dest->Clear();

//теперь, когда у нас имеются все вопросы, выбираем необходимое их количество;

//для этого запустим цикл продолжительностью в  Needed - нужное количество вопросов

//и на каждой итерации будем выбирать случайный вопрос из полного списка вопросов в //Dest;

//во избежание повторов вопросов - после выбора  вопрос будет удаляться из полного //списка;

int n;//дополнительная переменная - для хранения случайного номера строки;

int ICount=Source->Count;//переменная, в которой хранится максимальное значение //случайного числа

for (int i=0;i<Needed;i++){

  n=random(ICount);//заносим номер строки в полный список строки;

  Dest->Add(Source->Strings[n]);//добавляем вопрос в итоговый список;

  Source->Delete(n);//удаляем вопрос из полного списка вопросов;

  ICount--;//уменьшаем макcимальное значение случайного числа на 1;

}

delete Source;

}

//---------------------------------------------------------------------------

//обработка события - закрытие формы, закрываем все открытые запросы и разрываем //соединение;

void __fastcall TRegister::FormClose(TObject *Sender, TCloseAction &Action)

{

QDepts->Close();

QScTitle->Close();

TblScenar->Active=false;

}

//---------------------------------------------------------------------------

void __fastcall TRegister::FormCreate(TObject *Sender)

{

/*

AnsiString DomainName="";

 TRegistry *Reg = new TRegistry;

 try

 {

   Reg->Access=KEY_READ;

   Reg->RootKey = HKEY_LOCAL_MACHINE;

   if (Reg->OpenKey("\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", false))

   {

     DomainName=Reg->ReadString("DefaultDomainName");

     if (DomainName!="TEI") ExitWindowsEx(EWX_FORCE+EWX_LOGOFF,0);

     Reg->CloseKey();

   }

 }

 __finally

 {

   delete Reg;

 }

*/

}

//---------------------------------------------------------------------------

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

//меняются данные в поле MaskEdit1 - номер экзаменационного листа;

void __fastcall TRegister::MaskEdit1Change(TObject *Sender)

{

//если пользователь ввел весь номер - 4 цифры, можно начинать анализ;

if (MaskEdit1->Text.Trim().Length()==4) {

       // qryFindInfo – запрос на сервер Олимп, в котором хранятся данные регистрации //поступающих (функция находится в стадии разработки);

       qryFindInfo->Close();//на всякий случай, закрываем запрос;

       qryFindInfo->Parameters->ParamValues["PersonID"]=StrToInt(MaskEdit1->Text);//передаем в запрос параметр - номер экз. листа

       qryFindInfo->Open();//открываем запрос;

       //если запрос вернул хотя бы одну запись => пользователь существует;

       if (qryFindInfo->RecordCount>=1) {

//            Edit1->Enabled=true;

//            Edit2->Enabled=true;

//            DBLookupComboBox1->Enabled=true;

           DBLookupComboBox2->Enabled=true;//разрешаем выбор варианта теста,

              //показываем регистрационные данные;

             Edit1->Text=qryFindInfo->FieldByName("LastName")->AsString; //фамилия;

             Edit2->Text=qryFindInfo->FieldByName("FirstName")->AsString+" "+qryFindInfo->FieldByName("SecondName")->AsString;//имя, отчество;

             DBLookupComboBox1->KeyValue=qryFindInfo->FieldByName("FacID")->AsInteger; //факультет

             Register->Update();//обновляем данные формы;

       }

       //если запрос не вернул записей - собщаем пользователю о неудачи;

       else {

          MessageBox(0, "Вы указали неправильный номер экзаменационного листа\nПопробуйте еще раз!","Ошибка!",MB_OK);

          MaskEdit1->Text="";

       }

 

5.2 TestResults.exe - программа поиска и вывода результата.

       Вспомогательная программа TestResults.exe, с помощью которой производится поиск и вывод результата тестирования, состоит из следующих компонентов языка программирования C++Builder 6.0 - форм: SelectGroup.cpp (изображена на рисунке 5.4), EntranceUnit.cpp (изображена на рисунке 5.5), ExportUnit.cpp (изображена на рисунке 5.6).

Рисунок 5.4. форма SelectGroup.cpp

             На рисунке 5.4 видно, что форма SelectGroup.cpp состоит из стандартного компонента Form, на который помещены компоненты: 1) ComboBox (3 шт.) – для выбора в форму по условию отбора факультета, курса, а также вида тестирования; 2) WordApplication – контейнер activeX для передачи информации в файл microsoft Word; 3) DateTimePicker (2 шт.) – для создания интервала отбора по временной дате (число, месяц, год); 4)Label (4 шт.) – для отображения надписей (факультет, курс, дата тестирования, вид тестирования); 5) GroupBox – для помещения компонентов ComboBox, WordApplication, DateTimePicker, Label; 6) DataSource - источник данных осуществляющий обмен информацией между  компонентами Table, Query, StoredProc, которые непосредственно связываются с базой данных и компонентами  визуализации и управления данными; 7)DBGrid –компонент визуализации и управления данными, имеющий ряд кнопок: nbFirst –перемещение к первой записи, nbPrior – перемещение к предыдущей записи, nbNext –перемещение к следующей записи, nbLast –перемещение к последней записи, nbInsert –вставить новую запись перед текущей, nbDelete –удалить текущую запись, nbEedit –редактировать текущую запись, nbPost –послать отредактированную информацию  в базу данных, nbCancel –отменить результаты редактирования  или добавления новой записи, nbRefresh –очистить буфер, связанный с набором данных; 8)Button (3 шт.) – кнопки, по событию которых выполняются определенные действия (создается ведомость, экзаменационный лист, осуществляется экспорт в другую СУБД – эволюционная функция в данной программе пока не активна); 9) ListBox  (2 шт.) – для просмотра результатов, посланных на программу  TestResults.exe,  для последующей распечатки и импортирования в другую СУБД (эволюционная функция); 10) Timer (2 шт.) – для создания задержки при передачи информации в титульную форму и при распечатке результатов. 11)NMMSGServ – сервер стороннего разработчика http://www.netmastersllc.com/,   находящийся в свободной разработке и использовании, служит для приема информации с тестирующих форм MegaTest.exe во время крупномасштабного тестирования.

  

           Рисунок 5.5. форма EntranceUnit.cpp

          На рисунке 5.5 видно, что форма SelectGroup.cpp состоит из стандартного компонента Form, на который помещены компоненты: 1)QuickRep – для создания поля ведомости; 2)QRImage – для помещения логотипа вуза в ведомость в формате *.bmp; 3)QRLable (21 шт.) – для написания в ведомости основных данных об учащихся (ф.и.о., специальность, вид тестирования, дата тестирования и т.д. см. рис 6.5) и вывода результата из базы данных.

рисунок 5.6. форма ExportUnit.cpp

           На рисунке 5.6 видно, что форма ExportUnit.cpp (эволюционная форма, находящаяся в стадии разработки), состоит из стандартного компонента Form, на который помещены компоненты: 1)ADOQuery (2 шт.) - элемент для соединения с базой данных Microsoft Sql, используя класс ADO; 2) ProgressBar - для размещения данных на форме; 3)Label (2шт.) – для создания отчета в другую СУБД.

Текст программы TestResults.exe состоит, в основном, из готовых компонентов и приведен в приложении.

 

 

5.3 estQuestions.exe – программа создания сценариев тестирования и добавления вопросов в базу данных.

     Программа создания сценариев тестирования и добавления вопросов в базу данных  TestQuestions.exe, с помощью которой создаются сценарии тестирования и добавляются вопросы в базу данных Mysql, состоит из следующих компонентов языка программирования C++Builder 6.0 - форм: QuestUnit.cpp(изображена на рисунке 5.7),Scenar.cpp(изображена на рисунке 5.8).

    

рисунок 5.7. форма QuestUnit.cpp

        На рисунке 5.7 видно, что форма QuestUnit.cpp состоит из стандартного компонента Form, на который помещены компоненты: 1) ToolBar – для помещения на форму основных кнопок управления; 2) ToolButton (11 штук) – основные элементы управления формы создания вопросов (новый вопрос, удалить вопрос, сохранить изменения, предыдущий вопрос, следующий вопрос, перенести в другую тему, выход, разрешить множественный выбор, редактирование сценариев и т.д. см. рис. 7); 3) ImageList  - элемент для добавления рисунка в формате *.bmp в вопрос теста; 4) StatusBar – для вывода информации о текущем номере вопроса, а также для просмотра количества вопросов; 5) Memo – поле для создания ответов; 6) TrxRichEdit – поле для введения вопроса; 6) GroupBox – для переключения введенных вариантов ответов.      

рисунок 5.8. форма Scenar.cpp

        На рисунке 5.8 видно, что форма Scenar.cpp состоит из стандартного компонента Form, на который помещены компоненты: 1)ToolBar – для размещения основных элементов управления формой для создания сценариев; 2) ToolButton (3шт.) – основные элементы управления (новый сценарий, удалить сценарий, сохранить изменения); 3) ComboBox – для выбора сценария из базы данных; 4) DBGrid (2шт) – для визуализации и управления данными (список тем, список сценариев); 5)DataSource (2шт.) - источник данных, осуществляющий  обмен информацией между  компонентами Table, Query, StoredProc, которые непосредственно связываются с базой данных и компонентами  визуализации и управления данными; 6)ImageList – для вывода на экран изображений кнопок управления формой сценариев в формате *.bmp.

Текст программы TestQuestions.exe состоит, в основном, из готовых компонентов и приведен в приложени

 

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

5.4.1 Обоснование выбора реляционной модели базы данных.

При выборе модели данных необходимо учитывать следующие требования:

-гибкость и адаптивность структуры базы данных;

-целостность базы данных;

-динамичность базы данных;

-способность к расширению.

В основе реляционной модели лежит математическое понятие теоретико-множественного отношения. Теоретико-множественное отношение представляет собой декартово подмножество произведения доменов. Элементами отношения являются кортежи.

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

Такие таблицы обладают следующими свойствами:

а) каждый элемент таблицы представляет собой только один элемент данных, повторяющиеся группы отсутствуют;

б) все столбцы в таблице однородные, то есть элементы столбца имеют одинаковую природу;

в) в таблице нет двух одинаковых строк;

г) в операциях с такой таблицей ее строки и столбцы могут просматриваться в любом порядке и в любой последовательности безотносительно к исходному смыслу.

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

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

5.4.2. Разработка структуры базы данных

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

n              n

1              n

1              n

 

n             1

n              1

n              n

Рисунок 5.9 - ER-диаграммы сущностей базы данных.

База данных представляет собой набор из семи таблиц. Перейдём к описанию полученных в ходе разработки таблиц.

Описание таблиц базы данных test.

     Типы данных, которые используются в СУБД Mysql, очень различные и отображают значения, занесенные как программой, так и оператором.

Таблица Students предназначена для хранения информации о студентах. Также в ней содержится  информация, которую вводит тестируемый во время прохождения теста. Структура таблицы Students приведена в таблице 5.1.

Таблица 5.1-Students имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

Code

Int

11

Код студента

Surname

Varchar

30

Фамилия студента

Name

Varchar

40

Имя студента

Login

Varchar

10

Логин студента

Dept

Int

11

Код факультета

Course

Smallint

6

Номер курса

Group

Smallint

6

Номер группы поступающего

TestDate

Datetime

Дата и время тестирования

Scenario

int

11

Номер сценария, по которому проходило тестирование

Таблица SсTitle предназначена для хранения информации о сценариях тестирования. В ней содержится  информация о готовности сценария и его типе и времени тести-рования. Структура таблицы SсTitle приведена в таблице 5.2.

Таблица 5.2-SсTitle имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

No

int

11

Идентификатор таблицы (первичный ключ)

Title

Varchar

50

Название сценария

Dept

int

11

Факультет

Teacher

Varchar

30

Имя ведущего преподавателя

Active

Tinyint

1

Активность сценария

Time

Time

Время, отведенное на тестирование

ReceptExam

int

11

Поле заведено для связи  с программами бухгалтерии (эволюционная функция)

Таблица Themes предназначена для хранения информации о темах тестирования. В ней содержится  названия тем и их порядковый номер . Структура таблицы Themes приведена в таблице 5.3.

Таблица 5.3-Themes имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

No

Int

11

Номер темы (ключевое поле)

Theme

Varchar

30

Название темы

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

Таблица 5.4-Quest имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

No

Int

11

Номер вопроса

PrintNo

Int

11

Варианты печати

Theme

Int

11

Номер темы

Quest

Mediumtext

Вопрос в формате *.rtf

Ans

Text

Список ответов

Right

Smallint

6

Правильный ответ

Таблица Scenar предназначена для хранения взаимосвязи сценария с нужным количеством вопросов определенной темы. Структура таблицы Scenar приведена в таблице 5.5.

Таблица 5.5-Scenar имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

ScNo

Int

11

Номер сценария

Theme

Int

11

Номер темы

Availeble

Int

11

Количество доступных вопросов

Needed

Int

11

Необходимое количество ответов

       Таблица Depts предназначена для хранения информации о факультетах. Также в ней содержится  соответствие между логином  и факультетом. Структура таблицы Depts приведена в таблице 5.6.

Таблица 5.6-Depts имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

DeptsNo

Int

11

Ключ факультета (генерируется автоматически, инкрементально – значение формируется по возрастающей)

Dept

varchar

30

Название факультета

LoginLetters

Char

3

Буква логина

       Таблица Exam предназначена для хранения информации о тестируемом который закончил тестирование для последующей распечатки результата или сохранения в Базе данных.По этим данным вычисляется результат тестирования и формируется оценка. Структура таблицы Exam приведена в таблице 5.7.

Таблица 5.7-Exam имеет следующие поля:

Поле

Тип поля

Длина

Описание поля

Student

Int

11

Код тестируемого присваивается, когда начинается сессия тестирующегося

Questions

Int

11

Код вопроса

Answer

smallint

6

Ответ тестирующегося

Correct

smallint

6

Корректный ответ

Рассмотрим структуру взаимодействия таблиц базы данных test в СУБД Mysql (Рисунок 5.10.).

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

рисунок 5.10. Структура  логических связей между таблицами.

Лист утверждения

Студент группы М – 62з

_________В.В. Храмов

“____”__________2005 г.

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

__________М.А. Лебедева

“____”___________2005 г.

2005

  1.  РУКОВОДСТВО ПО ОБСЛУЖИВАНИЮ КОМПЛЕКСА

6.1 Руководство пользователя тестирующего комплекса.

Для начала работы с тестирующей программой необходимо нажать на  кнопку ПУСК/ Программы /TEST. Этим действием мы запустим файл главной формы test.exe.    После запуска программы на экране появляется окно главной формы, изображенной на рисунке 6.1.

          Рисунок 6.1 – Главная форма тестирующей программы.

        После этого нужно заполнить основные поля формы: фамилия тестирующегося, имя тестирующегося, а также с помощью элементов списков: специальность и вариант теста    и  компонента, благодаря которому  можно выбирать номер курса (указав значение стрелками). Это все изображено на  рисунках  6.2,6.3,6.4,6.5.  

 

Рисунок 6.2 Заполнение полей: фамилия, имя, а также выбор специальности.

Рисунок 6.4 Выбор элементов списка: вариант теста.

Особое внимание нужно уделить полосе прокрутки справа, с помощью которой можно просматривать весь список.

 

                                  Рисунок 6.4 Выбор курса.

               После того, как форма будет заполнена, нужно нажать на кнопку   и перед вами  появится окно с вопросами. Это изображено на рисунке 6.5.   

                               Рисунок 6.5   Окно с вопросами.

            

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

                               

                         Рисунок 6.6 Выбор последующих вопросов

          После перемещения по всем вопросам и указав варианты ответов, дойдя до последнего вопроса, нужно нажать на значок закрытия окна, нажав в правом верхнем углу крестик . И на экране появится результат тестирования в процентах или в количестве баллов - (это зависит от настроек теста). Это мы видим на рисунке 6.7

                                          

                      Рисунок 6.7 Окно результата тестирования.

6.2 Руководство администратора тестирующего комплекса.

Создание вопросов и сценариев теста с помощью программы TestQuestions.

Для добавления вопросов и создания сценариев в тестирующий комплекс нужно запустить программу  TestQuestions.exe, предварительно настроив безопасное соединение через туннель ipsec(VPN).

Настройка VPN-соединения под Windows2000/ XP

Нажать: Пуск->Подключения->Отобразить все подключения


Далее выберите "Создание нового подключения"


Нажимаем "Далее"


Выбираем "Подключить к сети на рабочем месте"


Ставим точку на "Подключении к виртуальной частной сети" и жмем "Далее"


Пишем название нашего соединения, например "Provider" и жмем "Далее"


Для успешного соединения вписываем IP адрес сервера: "1.1.1.2" и жмем "Далее"


Ставим для удобства галку и жмем "Готово"


В поле "пользователь" пишем наш "login"  В поле "пароль" пишем наш "пароль"


В итоге мы получили настроенное подключение к виртуальной частной сети и можем смело жать на "Подключение"


"Рабочий стол после настройки подключения выглядит так"

Настраиваем драйвер mysqlODBC в ос windows, указав через /Панель управления / Администрирование / Источники данных (ODBC). Этот механизм применяется для соблюдения строгой безопасности при работе с базой данных. Это изображено на рисунке 6.8.   

 

                                      

                           рисунок 6.8 Системный источник ODBC

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

                                  Рисунок 6.9 Настройка системных параметров

После этих действий можно переходить к интерфейсу программы TestQuestions, который показан на рисунке 6.10

               

                            

                       рисунок 6.10 Интерфейс программы TestQuestions.

Для добавления новой темы и соответственно сценария, по которому будет проходить тестирование пользователя нужно выбрать закладку “сценарии”, далее в окне программы сценариев нужно выбрать и указать новый сценарий. Затем добавить новую тему в поле, где список тем. Это можно увидеть на рисунках 6.11,6.12.

 

рисунок 6.11 Создание нового сценария.

рисунок 6.12 Создание новой темы для сценария.

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

После  этого нужно нажать значок - новый вопрос  и перейти ко второму вопросу с помощью верхних стрелок, а также вернуться назад и т д.  Это можно увидеть на рисунках 6.13,6.14

                         рисунок 6.13 Создание нового вопроса с вариантами ответа  для темы сценария.

рисунок 6.14 Создание последующего вопроса для новой темы и сценария тем.

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

рисунок 6.15 Поиск тестирующегося в базе данных для распечатки его  результата.

рисунок 6.16 Распечатка результата тестирования студента.

      7 ТРЕБОВАНИЯ К АППАРАТНОЙ ЧАСТИ СЕРВЕРА И СЕТИ

      

      7.1 Аппаратно – программные требования.

         7.1.1 Требования к уровню аппаратной части сервера:

- оперативная память 64mb;  

- сетевые интерфейсы Ethernet 10/100 mbit;

- центральный процессор min 486/dx4;

- HDD (жесткий диск) 1,5 Gb;

         7.1.2 Требования к уровню аппаратной части клиента:

- оперативная память 32mb;

- центральный процессор pentium166;

- сетевые интерфейсы Ethernet 10/100 mbit;

         7.1.3 Программные требования к уровню сервера:

- операционная система: Сервер(win98/Me/2000/XP/2003/linux/FreeBSD)

- установленная СУБД Mysql с настроенными базами данных; дамп создания базы данных приведен в приложении к дипломной работе;

- сервер VPN(виртуальной  частной сети) – сценарий создания приведен в приложении.

          7.1.4 Программные требования к уровню клиента:

  •  набор программ тестирующего комплекса: test.exe, TestQuesttions, TestResults;
  •  драйвер для работы с СУБД Mysql. (www,mysql.ru);
  •  Установленное VPN соединение (описано в руководстве по обслуживанию программного-аппаратного  комплекса тестирования знаний  с разделяемыми правами доступа).   

7.2 Расчет топологии сети для аппаратно-программного комплекса тестирования знаний с разделяемыми правами доступа.

          Для того, чтобы сеть Ethernet, состоящая из сегментов различной физической природы, работала корректно, необходимо, чтобы выполнялись три основных условия:

  •  Количество станций в сети не превышает 1024 (с учетом ограничений для коаксиальных сегментов).
  •  Удвоенная задержка распространения сигнала (Path Delay Value, PDV) между двумя самыми удаленными друг от друга станциями сети не превышает 575 битовых интервалов.
  •  Сокращение межкадрового расстояния (Interpacket Gap Shrinkage) при прохождении последовательности кадров через все повторители не более, чем на 49 битовых интервалов (напомним, что при отправке кадров станция обеспечивает начальное межкадровое расстояние в 96 битовых интервалов).

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

        Физический смысл ограничения задержки распространения сигнала по сети уже пояснялся - соблюдение этого требования обеспечивает своевременное обнаружение коллизий.

       Требование на минимальное межкадровое расстояние связано с тем, что при прохождении кадра через повторитель это расстояние уменьшается. Каждый пакет, принимаемый повторителем, ресинхронизируется для исключения дрожания сигналов, накопленного при прохождении последовательности импульсов по кабелю и через интерфейсные схемы. Процесс ресинхронизации обычно увеличивает длину преамбулы, что уменьшает межкадровый интервал. При прохождении кадров через несколько повторителей межкадровый интервал может уменьшиться настолько, что сетевым адаптерам в последнем сегменте не хватит времени на обработку предыдущего кадра, в результате чего кадр будет просто потерян. Поэтому не допускается суммарное уменьшение межкадрового интервала более чем на 49 битовых интервалов. Величину уменьшения межкадрового расстояния при переходе между соседними сегментами обычно называют в англоязычной литературе Segment Variability Value, SVV, а суммарную величину уменьшения межкадрового интервала при прохождении всех повторителей - Path Variability Value, PVV. Очевидно, что величина PVV равна сумме SVV всех сегментов, кроме последнего.

Расчет PDV

      Для упрощения расчетов обычно используются справочные данные, содержащие значения задержек распространения сигналов в повторителях, приемопередатчиках и в различных физических средах. В таблице 7.1 приведены данные, необходимые для расчета значения PDV для всех физических стандартов сетей Ethernet, взятые из справочника Technical Reference Pocket Guide (Volume 4, Number 4) компании Bay Networks.

Таблица 7.1 

Тип сегмента

База левого сегмента

База промежуточного сегмента

База правого сегмента

Задержка среды на 1 м

Максимальная длина сегмента

10Base-T

15.3

42.0

165.0

0.113

100

          Поясним терминологию, использованную в этой таблице, на примере нашей сети, изображенной на рисунке 7.1

Рис. 7.1 Сеть Ethernet, состоящая из сегментов

         Левым сегментом называется сегмент, в котором начинается путь сигнала от выхода передатчика (выход Tx) конечного узла. Затем сигнал проходит через промежуточные сегменты и доходит до приемника (вход Rx) наиболее удаленного узла наиболее удаленного сегмента, который называется правым. С каждым сегментом связана постоянная задержка, названная базой, которая зависит только от типа сегмента и от положения сегмента на пути сигнала (левый, промежуточный или правый). Кроме этого, с каждым сегментом связана задержка распространения сигнала вдоль кабеля сегмента, которая зависит от длины сегмента и вычисляется путем умножения времени распространения сигнала по одному метру кабеля (в битовых интервалах) на длину кабеля в метрах.

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

Рассчитаем значение PDV для нашей сети

Левый сегмент: 15.3 bt (база) + 5 м * 0.113 /м = 15.865 bt 

Промежуточный сегмент: 42 bt(база) + 25 м * 0.113 /м =44.825 bt  

Правый сегмент: 165 bt (база) + 5м * 0.113/м = 165.565 bt

Сумма всех составляющих дает значение PDV, равное 226.256 bt 

          Так как значение PDV меньше максимально допустимой величины 575, то эта сеть проходит по величине максимально возможной задержки оборота сигнала.  

Расчет PVV

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

Таблица 7.2

Тип сегмента

Передающий сегмент

Промежуточный сегмент

10Base-T

10.5

8

           В соответствии с этими данными рассчитаем значение PVV для нашей сети

Левый сегмент 10Base-T: дает сокращение в 10.5 битовых интервалов

Промежуточный сегмент  10Base-T: дает сокращение в 8 битовых интервалов

Правый сегмент 10Base-T: дает сокращение в 10.5 битовых интервалов

PVV=10.5 bt+8 bt+10.5 bt=29 bt

         Сумма этих величин дает значение PVV, равное 29 битовых интервалов, что меньше предельного значения в 49 битовых интервалов.

         В результате, наша  сеть по всем параметрам соответствует стандартам Ethernet.

 

8 РЕЗУЛЬТАТЫ  ИСПЫТАНИЯ ПРОГРАММНОГО КОМПЛЕКСА

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

        Тестирование - процесс выполнения программы с намерением найти ошибку, а не подтвердить правильность программы.

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

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

Сократить количество ошибок можно несколькими путями:

  •  применить надежные, многократно протестированные компоненты и библиотеки;
  •  строго соблюдать и главное контролировать соответствие создаваемых     программ проектной документации.

Еще одним, достаточно эффективным, но трудоемким методом сокращения ошибок является тестирование.

Обычно ресурсов для полного тестирования не хватает. Поэтому полное тестирование с проверкой системы во всех режимах и со всеми параметрами трудно реализуемо.

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

       Выбранный метод тестирования — метод черного ящика. Проверяются исходы выполнения по сравнению с точно установленными исходами.

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

    Оценка должна быть выполнена на уровне поведения системы. Цель - рассмотреть все разнообразие возможных ситуаций.

Общий список вопросов, которые будут обработаны в ходе тестирования программного продукта:

Вся ли функциональность реализована в приложении?

Возможно ли предсказать поведение системы по модели вариантов использования?

Наличие ошибок в сообщениях приложения?

     Тестирование можно считать успешно завершенным, только если все критерии были успешно протестированы.

Была разработана проверочная таблица соответстивия для тестировапния программного комплекса.

Тестовые параметры

Положительный результат

Отрицательный результат, или требующий доработки.

1.Введение основных данных  тестирования (имя, фамилия студента, курс, факультет и т.д.).

Все поля заполнены.

Не заполненено какое - то поле.

2.Режим тестирования.

Выбор всего количества вопросов.

Не учитывался последний вопрос.

3.Сервер печати. 

Запуск 50 экземпляров.

Отказ печати 3 экземпляров из - за неправильной передачи значений в форму.

4.Несоответствие драйвера MysqlDBC.

Версия 3.25.

Выше версии 3.25.

5.Проверка на ограничение

подключений к серверу. 

Меньше 50 подключений.

Больше 50 одновременных подключений.

6.Установка дополнительной библиотеки Rxlib(наличие правильных связей в компиляторе).

Успешная компиляция.

Ошибки при компиляции.

7. Зависание тестовой формы.

Все вопросы заполнены в базе данных.

Если хоть один вопрос

пустой.

8.Запуск тестовой формы с файлом параметров test.ini.

Если совпадают имена

Test.exe и test.ini.

Если имена различны.

9.Использование не-стандартных шрифтов при наборе тестов.

Набор со шрифтами Arial и Times.

Набор с нестандартными шрифтами.

10.Передача параметра сервера печати. 

Реальный ip адрес сервера или доменное имя.

Несуществующие имя сервера или ip адрес.

В данном проекте при тестировании разработанного аппаратно -  программного тестирующего комплекса были получены следующие результаты.

Программа работала в вузе почти 4 месяца. За это время с ее помощью было протестировано около 800 студентов, которые в начале были протестированы эмпирически на своих факультетах (на бумаге), и после этого при тестировании их программой  отклонение было не более +/- 1 процента.         

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

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

 

ЗАКЛЮЧЕНИЕ

             В ходе выполнения данной работы были изучены средства объектного программирования в среде C++ Builder 6 фирмы Borland, основные методы создания приложений клиент-сервер на базе СУБД Mysql, также были использованы алгоритмы создания серверов на базе операционной системы linux, виртуальной частной сети с защитой передоваемых данных (VPN), была разработана топология широкополосной сети ethernet для работы СУБД и межсетевого экрана VPN. В результате был построен программно-аппаратный комплекс тестирования знаний с разделяемыми правами доступа.

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

В дальнейшем рекомендуется разработать интерфейс  для связи комплекса с другими  программами (приемная комиссия, бухгалтерия), для сравнения и занесения информации в другие СУБД, например, MicrosoftSQL и Oracle, для осуществления  быстрого тестирования пользователей вуза или другого учебного заведения. Также необходимо написать руководство по эксплуатации новых возможнойстей комплекса.

БИБЛИОГРАФИЧЕСКИЙ СПИСОК.

  1.  А.Я. Архангельский Программирование в С++Builder 5. – М.: ЗАО “Издательство

БИНОМ”, 2000г. – 1152 c.: ил.

  1.  Дерк Луис Borland C++ 5. Справочник. Пер. c нем. – М.: “Издательство БИНОМ”,

1997. – 560 c.: ил.

  1.  И.Г Захарова Информационные технологии в образовании: Учеб. пособие для студ.

высш. пед. Учеб. заведений. – М.: Издательский центр “Академия”, 2003. – 192 c.

4. Бруй В. В. , Карлов С. В.  LINUX-сервер: пошаговые инструкции инсталляции и настройки. - М.: Изд-во СИП РИА, 2003. - 572 с.

5. Журнал «Вестник образования»:№2 2001 г. - 9 класс, №5 2001 г. - 11 класс.

6. Методические указания по организационно – экономическому обоснованию

дипломных проектов по специальности 7.091501 (M).           

7.Аллахвердиева Д.Т. Опыт применения тестов для дидактической экспертизы обучения // Высшее образование в России. №2, 1993. С. 102-104.

8.Щапов А., Тихомирова Н., Ершиков С., Лобова Т. Тестовый контроль в системе рейтинга//Высшее образование в России. №3, 1995. С. 100-102.

9.Хубаев Г. О построении шкалы оценок в системах тестирования//Высшее образование в России. №1, 1996. С. 122-125.

10.А.Ф. Чернявский, А.М. Мухарский, А.В. Орлов и др. Автоматизированные обучающие системы на базе ЭВМ //Под ред. А. Ф. Чернявского. - Мн.: Изд-во БГУ, 1980.- 176с.

11.Елена Турская, VPN как средство "неотложной помощи" ОАО" Элвис+ " Cетевой журнал №11.2000

12.ГОСТ 12.1.003-83.ССБТ. Шум. Общие требования безопасности.

13.ГОСТ 12.1.004-76.ССБТ. Пожарная безопасность. Общие требования.

14.ГОСТ 12.1.005-88.ССБТ. Воздух рабочей зоны. Общие санитарно – гигиеническиетребования.

15.ГОСТ 12.1.012-78.ССБТ. Вибрация. Общие требования безопасности.

16.ГОСТ 12.1.038-82.ССБТ. Электробезопасность. Предельно допустимые уровни

напряжений прикосновения и токов.

17.ГОСТ 12.2.032-78.ССБТ. Рабочее место при выполнении работ сидя. Общие

эргономические требования.

18.СН и П II-2 80. Противопожарные нормы проектирования зданий и сооружений.

19.СН и П II-4-79. Строительные нормы и правила. Нормы проектирования. Естественное и искусственное освещение.

20.СН и П II-33-75. Отопление, вентиляция и кондиционирование воздуха.

21.СН 512-78. Инструкция по проектированию зданий и помещений для ЭВМ.22.СН и П II-4 79. Естественное и искусственное освещение

23.Методические указания для выполнения раздела "Охрана труда " в дипломных проектах. Ч6 Расчеты естественного и искусственного освещения.

 24.ДНАОП 0.00-1.31-99. Правила охраны труда при эксплуатации электронно-вычислительных машин.

25.Методические указания по выполнению раздела "Охрана труда и окружающей среды" в дипломных проектах специальностей 0608 “Электронные вычислительные машины” и 0604 “Автоматизация и механизация процессов обработки и выдачи информации”.

Министерство образования  и науки Украины

Севастопольский национальный технический университет

СОГЛАСОВАНО:      УТВЕРЖДАЮ:

Руководитель дипломного     Заведующий кафедрой

проекта, доцент      кибернетики и вычислительной

техники, д.т.н., профессор

_____________М.А. Лебедева    _____________А.В. Скатков

“___”_____________2005 г.    “____”______________2003 г.

Программно-аппаратный комплекс тестирования знаний с разделяемыми правами доступа

ТЕКСТ ПРОГРАММЫ

Листов - 30

2005

Приложение 1

Исходный текст программ тестирующего комплекса.

Test.exe (главная тестирующая форма)

                                                  Test.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

USERES("MegaTest.res");

USEFORM("RegForm.cpp", Register);

USE("MegaTest.todo", ToDo);

USEFORM("QuestForm.cpp", Quest);

USE("MegaTest.todo", ToDo);

USE("Globals.h", File);

USEFORM("ResUnit.cpp", ResForm);

//---------------------------------------------------------------------------

int NeedMark;

int NeedDept;

int DefaultDept;

int NeedCourse;

int NeedGroup;

int NeedFinalPercent;

int DefaultScenario;

int CanSelectScenario;

int AttemptsAllowed;

int MinPercentToSave;

int ReceptMode;

AnsiString PServer;

//---------------------------------------------------------------------------

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

{

       try

       {

                Application->Initialize();

                Application->CreateForm(__classid(TRegister), &Register);

                Application->CreateForm(__classid(TQuest), &Quest);

                Application->CreateForm(__classid(TResForm), &ResForm);

                Application->Run();

       }

       catch (Exception &exception)

       {

                Application->ShowException(&exception);

       }

       return 0;

}

//---------------------------------------------------------------------------

Globals.h

extern int NeedDept;

extern int DefaultDept;

extern int NeedCourse;

extern int NeedGroup;

extern int NeedFinalPercent;

extern int DefaultScenario;

extern int CanSelectScenario;

extern int AttemptsAllowed;

extern int MinPercentToSave;

extern int UpTo;

extern int ReceptMode;

extern AnsiString PServer;

Test.bpr

<?xml version='1.0' encoding='utf-8' ?>

<!-- C++Builder XML Project -->

<PROJECT>

 <MACROS>

   <VERSION value="BCB.06.00"/>

   <PROJECT value="MegaTest.exe"/>

   <OBJFILES value="MegaTest.obj RegForm.obj QuestForm.obj ResUnit.obj"/>

   <RESFILES value="MegaTest.res"/>

   <IDLFILES value=""/>

   <IDLGENFILES value=""/>

   <DEFFILE value=""/>

   <RESDEPEN value="$(RESFILES) RegForm.dfm QuestForm.dfm ResUnit.dfm"/>

   <LIBFILES value=""/>

   <LIBRARIES value="RxCtl6.lib adortl.lib nmfast.lib vclsmp.lib bdertl.lib vclx.lib bcbsmp.lib

     vcldb.lib dbrtl.lib vcl.lib rtl.lib"/>

   <SPARELIBS value="rtl.lib vcl.lib dbrtl.lib vcldb.lib bcbsmp.lib vclx.lib bdertl.lib

     vclsmp.lib nmfast.lib adortl.lib RxCtl6.lib"/>

   <PACKAGES value="rtl.bpi vcl.bpi vclx.bpi bcbsmp.bpi dbrtl.bpi vcldb.bpi adortl.bpi

     ibsmp.bpi bdertl.bpi vcldbx.bpi qrpt.bpi teeui.bpi teedb.bpi tee.bpi

     dss.bpi teeqr.bpi ibxpress.bpi dsnap.bpi vclie.bpi inetdb.bpi inet.bpi

     nmfast.bpi webdsnap.bpi bcbie.bpi dclocx.bpi bcb97axserver.bpi DCLUSR.bpi

     RxCtl6.bpi"/>

   <PATHCPP value=".;"/>

   <PATHPAS value=".;"/>

   <PATHRC value=".;"/>

   <PATHASM value=".;"/>

   <DEBUGLIBPATH value="$(BCB)\lib\debug"/>

   <RELEASELIBPATH value="$(BCB)\lib\release"/>

   <LINKER value="ilink32"/>

   <USERDEFINES value=""/>

   <SYSDEFINES value="NO_STRICT"/>

   <MAINSOURCE value="MegaTest.cpp"/>

   <INCLUDEPATH value="$(BCB)\Imports;$(BCB)\include;$(BCB)\include\vcl;&quot;C:\Program Files\Borland\CBuilder6\Lib\RX&quot;"/>

   <LIBPATH value="$(BCB)\Imports;&quot;C:\Program Files\Borland\CBuilder6\Lib\RX&quot;;$(BCB)\Projects\Lib;$(BCB)\lib\obj;$(BCB)\lib"/>

   <WARNINGS value="-w-par"/>

   <OTHERFILES value=""/>

 </MACROS>

 <OPTIONS>

   <IDLCFLAGS value="-I$(BCB)\Imports -I$(BCB)\include -I$(BCB)\include\vcl

     -I&quot;C:\Program Files\Borland\CBuilder6\Lib\RX&quot; -src_suffix cpp -boa"/>

   <CFLAG1 value="-O2 -H=c:\PROGRA~1\borland\CBUILD~1\lib\vcl60.csm -Hc -Vx -Ve -X- -a8 -b-

     -k- -vi -c -tW -tWM"/>

   <PFLAGS value="-$Y- -$L- -$D- -$A8 -v -JPHNE -M"/>

   <RFLAGS value=""/>

   <AFLAGS value="/mx /w2 /zn"/>

   <LFLAGS value="-D&quot;&quot; -aa -Tpe -x -Gn"/>

   <OTHERFILES value=""/>

 </OPTIONS>

 <LINKER>

   <ALLOBJ value="c0w32.obj sysinit.obj $(OBJFILES)"/>

   <ALLRES value="$(RESFILES)"/>

   <ALLLIB value="$(LIBFILES) $(LIBRARIES) import32.lib cp32mt.lib"/>

   <OTHERFILES value=""/>

 </LINKER>

 <FILELIST>

     <FILE FILENAME="MegaTest.cpp" FORMNAME="" UNITNAME="MegaTest" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="MegaTest.res" FORMNAME="" UNITNAME="MegaTest" CONTAINERID="ResTool" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="RegForm.cpp" FORMNAME="Register" UNITNAME="RegForm" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="MegaTest.todo" FORMNAME="" UNITNAME="MegaTest" CONTAINERID="ToDo" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="QuestForm.cpp" FORMNAME="Quest" UNITNAME="QuestForm" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="MegaTest.todo" FORMNAME="" UNITNAME="MegaTest" CONTAINERID="ToDo" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="Globals.h" FORMNAME="" UNITNAME="Globals" CONTAINERID="" DESIGNCLASS="" LOCALCOMMAND=""/>

     <FILE FILENAME="ResUnit.cpp" FORMNAME="ResForm" UNITNAME="ResUnit" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>

 </FILELIST>

 <BUILDTOOLS>

 </BUILDTOOLS>

 <IDEOPTIONS>

[Version Info]

IncludeVerInfo=0

AutoIncBuild=0

MajorVer=1

MinorVer=0

Release=0

Build=0

Debug=0

PreRelease=0

Special=0

Private=0

DLL=0

Locale=1049

CodePage=1251

[Version Info Keys]

CompanyName=

FileDescription=

FileVersion=1.0.0.0

InternalName=

LegalCopyright=

LegalTrademarks=

OriginalFilename=

ProductName=

ProductVersion=1.0.0.0

Comments=

[HistoryLists\hlIncludePath]

Count=5

Item0=$(BCB)\Imports;$(BCB)\include;$(BCB)\include\vcl;C:\Program Files\Borland\CBuilder6\Lib\RX

Item1=$(BCB)\Imports;C:\Program Files\Borland\CBuilder5\Projects\;$(BCB)\include;$(BCB)\include\vcl;C:\Program Files\Borland\CBuilder5\RX\Units\

Item2=$(BCB)\Imports;C:\Program Files\Borland\CBuilder5\Projects\;$(BCB)\include;$(BCB)\include\vcl;C:\Program Files\Borland\RX\Units\

Item3=$(BCB)\Imports;C:\Program Files\Borland\CBuilder5\Projects\;$(BCB)\include;$(BCB)\include\vcl;C:\Program Files\Borland\RX\Units

Item4=$(BCB)\Imports;C:\Program Files\Borland\CBuilder5\Projects\;$(BCB)\include;$(BCB)\include\vcl

[HistoryLists\hlLibraryPath]

Count=2

Item0=$(BCB)\Imports;C:\Program Files\Borland\CBuilder6\Lib\RX;$(BCB)\Projects\Lib;$(BCB)\lib\obj;$(BCB)\lib

Item1=$(BCB)\Imports;C:\Program Files\Borland\CBuilder5\Projects\;$(BCB)\Projects\Lib;$(BCB)\lib\obj;$(BCB)\lib

[HistoryLists\hlDebugSourcePath]

Count=1

Item0=$(BCB)\source\vcl

[HistoryLists\hlConditionals]

Count=1

Item0=_DEBUG

[Debugging]

DebugSourceDirs=$(BCB)\source\vcl

[Parameters]

RunParams=

Launcher=

UseLauncher=0

DebugCWD=

HostApplication=

RemoteHost=

RemotePath=

RemoteLauncher=

RemoteCWD=

RemoteDebug=0

[Compiler]

ShowInfoMsgs=0

LinkDebugVcl=0

LinkCGLIB=0

[CORBA]

AddServerUnit=1

AddClientUnit=1

PrecompiledHeaders=1

[Language]

ActiveLang=

ProjectLang=

RootDir=

 </IDEOPTIONS>

</PROJECT>

Test.ini

[Login]

NeedDept=1

DefaultDept=1

NeedCourse=1

NeedGroup=1

DefaultScenario=10

CanSelectScenario=1

AttemptsAllowed=99

ReceptMode=0

[Result]

NeedFinalPercent=1

MinPercentToSave=30

UpTo=50

PServer=localhost

QuestForm.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "QuestForm.h"

#include "Globals.h"

#include "ResUnit.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

#pragma link "RxRichEd"

#pragma resource "*.dfm"

TQuest *Quest;

//---------------------------------------------------------------------------

__fastcall TQuest::TQuest(TComponent* Owner)

       : TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TQuest::FormShow(TObject *Sender)

{

TblExam->Active=true;

TblExam->First();

FirstRecordNumber=1;

StatusBar1->Panels->Items[1]->Text="Вопрос "+IntToStr(TblExam->RecNo-FirstRecordNumber+1)+" из "+IntToStr(TblExam->RecordCount);

AnsiString quest="";

for (TblExam->First();!TblExam->Eof;TblExam->Next())

   quest=quest+IntToStr(TblExamQuestion->AsInteger)+",";

quest=quest.SubString(1,quest.Length()-1);

TblExam->First();

QryQuest->Close();

QryQuest->SQL->Clear();

QryQuest->SQL->Add("SELECT Quest.'No', PrintNo, Theme, Quest, Ans, Quest.'Right' ");

QryQuest->SQL->Add("FROM Quest ");

QryQuest->SQL->Add("WHERE  Quest.'No' IN ("+quest+")");

QryQuest->Open();

Caption="Тестирование: "+WindowHeader;

LoadRec();

Testing=true;

if (Time>0) Timer1->Enabled=true;

//CheckTBState();

}

//---------------------------------------------------------------------------

void TQuest::LoadRec()

{ TLocateOptions opt;

QryQuest->Locate("No",TblExamQuestion->AsInteger,opt);

//  Changed=false;

 TBlobStream *Stream;

 TStringStream *Str=new TStringStream("");

 Stream = new TBlobStream(QryQuestQuest, bmRead);

 try {

     Str->CopyFrom(Stream,Stream->Size);

     Str->Position=0;

     RichText1->Lines->LoadFromStream(Str);

 }

 __finally

 {

     delete Stream;

     delete Str;

 }

 RadioGroup1->Items->Clear();

 RadioGroup1->Items->Text=QryQuestAns->AsString;

 if (TblExam->FieldByName("Answer")->AsInteger!=0) RadioGroup1->ItemIndex=TblExamAnswer->AsInteger-1;

 else RadioGroup1->ItemIndex=-1;

}

void __fastcall TQuest::ToolButton1Click(TObject *Sender)

{

  TblExam->Prior();

  LoadRec();

}

//---------------------------------------------------------------------------

void __fastcall TQuest::ToolButton2Click(TObject *Sender)

{

  TblExam->Next();

  LoadRec();

}

//---------------------------------------------------------------------------

void __fastcall TQuest::RadioGroup1Click(TObject *Sender)

{

AnsUpdated=true;

}

//---------------------------------------------------------------------------

void __fastcall TQuest::TblExamAfterScroll(TDataSet *DataSet)

{

if (Testing)

StatusBar1->Panels->Items[1]->Text="Вопрос "+IntToStr(TblExam->RecNo-FirstRecordNumber+1)+" из "+IntToStr(TblExam->RecordCount);

}

//---------------------------------------------------------------------------

void __fastcall TQuest::TblExamBeforeScroll(TDataSet *DataSet)

{

if (Testing&&AnsUpdated) {

   TblExam->Edit();

   TblExam->FieldByName("Answer")->AsInteger=RadioGroup1->ItemIndex+1;

   TblExam->FieldByName("Correct")->AsInteger=QryQuestRight->AsInteger;

   TblExam->Post();

   //DbiApplyDelayedUpdates (TblExam->Handle,(DBIDelayedUpdCmd)0);   //commit

}

AnsUpdated=false;

}

//---------------------------------------------------------------------------

void __fastcall TQuest::Timer1Timer(TObject *Sender)

{

Time=Time-(double)1/86400;

StatusBar1->Panels->Items[0]->Text=TimeToStr(Time);

if (Time<0) Quest->Close();

}

//---------------------------------------------------------------------------

void __fastcall TQuest::FormCloseQuery(TObject *Sender, bool &CanClose)

{

if (TblExam->State==dsEdit)

   TblExam->Post();

TblExam->Prior();

Testing=false;

Timer1->Enabled=false;

QryQuest->Close();

//посчитать количество правильных ответов

int RightAnswers=0,RightPercent=0;;

for (TblExam->First();!TblExam->Eof;TblExam->Next())

  if (TblExamAnswer->AsInteger==TblExamCorrect->AsInteger) RightAnswers++;

RightPercent=100*RightAnswers/TblExam->RecordCount;

TblExam->Close();

Quest->Hide();

//if (NeedFinalPercent) MessageBox(0,("Ваш результат: "+FormatFloat("##0%",RightPercent)).c_str(),"Результат",MB_OK+MB_ICONINFORMATION);

if (NeedFinalPercent) {

//    TResForm* ResForm = new TResForm(this);

   ResForm->Show();

   if (RightPercent>=MinPercentToSave)

           ResForm->Label2->Caption=FormatFloat("##0%",RightPercent);

   else

           ResForm->Label2->Caption="Плохо!";

   ResForm->Update();

}

if (RightPercent>=MinPercentToSave) {

   if (ResForm!=NULL) {

       ResForm->Label3->Caption="Результат сохраняется...";

       ResForm->Update();

   }

   //на случай режима продолжения зачистить ранее сохраненные ответы

//*********** а нужно ли это??? **********************************

//    QryDel->ParamByName("StudentCode")->AsInteger=StudentCode;

//    QryDel->ExecSQL();

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

   //перебрость результат в общую базу

   QrySave->ExecSQL();

   if (ResForm!=NULL) {

           ResForm->Label3->Font->Color=clGreen;

           ResForm->Label3->Caption="Результат сохранен";

   }

}

else { //если результат очень плохой, то можно удалить и фамилию студента.

   QryDelName->ParamByName("StudentCode")->AsInteger=StudentCode;

   QryDelName->ExecSQL();

}

ResForm->Label4->Visible=true;

ResForm->Label4->Enabled=true;

//удалить временную таблицу ExamLocal

TblExam->DeleteTable();

if (NeedFinalPercent) {

   while (ResForm->Visible) Application->ProcessMessages();

}

//delete ResForm;

//отправить задание печати на сервер

if (PServer.Trim().Length()>0){

   NMMsg1->Host=PServer.Trim();

   try {

       NMMsg1->PostIt(IntToStr(StudentCode));

   }

   catch(...)

   {

   }

}

CanClose = true;

}

//---------------------------------------------------------------------------

QuestForm.dfm

TestQuestions.exe (программа создания и добавления вопросов)

QuestUnit.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#include <math.h>

#pragma hdrstop

#include "QuestUnit.h"

#include "Scenar.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

//#pragma link "mySQLDbTables"

#pragma link "RxRichEd"

#pragma resource "*.dfm"

TfrmQuest *frmQuest;

TStream *Stream;

//---------------------------------------------------------------------------

__fastcall TfrmQuest::TfrmQuest(TComponent* Owner)

       : TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::FormShow(TObject *Sender)

{

Database1->Connected=true;

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select No, Theme from Themes order by theme");

qry->Open();

for (qry->First(); !qry->Eof; qry->Next())

  cbThemes->Items->AddObject(qry->FieldByName("Theme")->AsString, (TObject *)qry->FieldByName("No")->AsInteger);

qry->Close();

cbThemes->ItemIndex=0;

QuestNums = new TStringList();

cbThemesCloseUp(this);

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::cbThemesCloseUp(TObject *Sender)

{

if (Changed) SaveRec(QuestNums->Strings[QuestNum]);

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select No from Quest where Theme="+IntToStr((int)cbThemes->Items->Objects[cbThemes->ItemIndex])+" ORDER BY No");

qry->Open();

QuestNums->Clear();

for (qry->First(); !qry->Eof; qry->Next())

       QuestNums->Add(qry->FieldByName("No")->AsString);

qry->Close();

QuestNum=0;

if (QuestNums->Count>0) LoadRec(QuestNums->Strings[QuestNum]);

else {

 ToolButton1Click(this); //добавить вопрос

 cbThemesCloseUp(this);

}

StatusBar1->Panels->Items[0]->Text="Вопрос "+IntToStr(QuestNum+1)+" из "+IntToStr(QuestNums->Count);

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton5Click(TObject *Sender)

{

if (Changed) SaveRec(QuestNums->Strings[QuestNum]);

if (QuestNum<QuestNums->Count-1) {

   QuestNum++;

   LoadRec(QuestNums->Strings[QuestNum]);

   StatusBar1->Panels->Items[0]->Text="Вопрос "+IntToStr(QuestNum+1)+" из "+IntToStr(QuestNums->Count);

}

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton4Click(TObject *Sender)

{

if (Changed) SaveRec(QuestNums->Strings[QuestNum]);

if (QuestNum>0) {

   QuestNum--;

   LoadRec(QuestNums->Strings[QuestNum]);

   StatusBar1->Panels->Items[0]->Text="Вопрос "+IntToStr(QuestNum+1)+" из "+IntToStr(QuestNums->Count);

}

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton2Click(TObject *Sender)

{

SaveRec(QuestNums->Strings[QuestNum]);

}

//---------------------------------------------------------------------------

void TfrmQuest::LoadRec(AnsiString QuestNo)

{

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select * from Quest where No="+QuestNo);

qry->Open();

TStream *Stream;

Stream=qry->CreateBlobStream(qry->FieldByName("Quest"),bmRead);

RichText1->Lines->LoadFromStream(Stream);

delete Stream;

Correct = qry->FieldByName("Right")->AsInteger;

Multi=(Correct<0);

ToolButton11->Down=Multi;

if (Multi)  Correct=-Correct;

answers->Text=qry->FieldByName("Ans")->AsString;

qry->Close();

Changed=false;

ShowAnswers();

}

//---------------------------------------------------------------------------

void TfrmQuest::ShowAnswers()

{ int Correct=TfrmQuest::Correct;

for (int row=0; row<8; row++)

  if (ans[row]!=NULL) {delete ans[row]; ans[row]=NULL;}

  

AnsCount=answers->Lines->Count;

if (AnsCount>7) GroupBox1->Height=16*(AnsCount+1);

else GroupBox1->Height=125;

if (Multi)

   for (int row=0; row<AnsCount; row++) {

       ans[row]=new TCheckBox(GroupBox1);

       ans[row]->Top=(row%AnsCount+1)*13+2;

       ans[row]->Left=5;

       ans[row]->Width=15;//780;

       ans[row]->Parent=GroupBox1;

//        ((TCheckBox *)ans[row])->Caption=answers->Strings[row];

       ((TCheckBox *)ans[row])->Checked=Correct%2;

       Correct=Correct/2;

     }

else

for (int row=0; row<AnsCount; row++) {

   ans[row]=new TRadioButton(GroupBox1);

   ans[row]->Top=(row%AnsCount+1)*13+2;

   ans[row]->Left=5;

   ans[row]->Width=15;//780;

   ans[row]->Parent=GroupBox1;

//    ((TRadioButton *)ans[row])->Caption=answers->Strings[row];

   if (row==(Correct-1))

         ((TRadioButton *)ans[row])->Checked=true;

}

}

//---------------------------------------------------------------------------

void TfrmQuest::SaveRec(AnsiString QuestNo)

{

TStringStream *Str = new TStringStream("");

TStringList *Ans = new TStringList();

//qry->Close();

//qry->SQL->Clear();

//qry->SQL->Add("update Quest set Quest.Quest=:QuestText, Quest.Ans=:Ans, Quest.Right=:Correct where No="+QuestNo);

ADOCommand1->CommandText="update Quest set Quest.Quest=:QuestText, Quest.Ans=:Ans, Quest.Right=:Correct where No="+QuestNo;

RichText1->Lines->SaveToStream(Str);

ADOCommand1->Parameters->ParamByName("QuestText")->Value=Str->DataString;

//qry->ParamByName("QuestText")->AsBlob=Str->DataString;

ADOCommand1->Parameters->ParamByName("Ans")->Value=answers->Text;

ADOCommand1->Parameters->ParamByName("Correct")->Value=CorrectToNumber();

//qry->ExecSQL();

ADOCommand1->Execute();

//answers->Text

delete Str;

}

//---------------------------------------------------------------------------

int TfrmQuest::CorrectToNumber()

{int Correct=0;

if (Multi) {

//  Correct=-TfrmQuest::Correct;

 for (int row=0; row<AnsCount; row++)

   if (((TCheckBox *)ans[row])->Checked) Correct=Correct+pow(2,row);

 Correct=-Correct;

}

else

 for (int row=0; row<AnsCount; row++)

   if (((TRadioButton *)ans[row])->Checked) Correct=row+1;

return Correct;

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::answersChange(TObject *Sender)

{

Changed=true;

if (AnsCount!=answers->Lines->Count) {

   AnsCount=answers->Lines->Count;

   ShowAnswers();

}

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::RichText1Change(TObject *Sender)

{

Changed=true;

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton1Click(TObject *Sender)

{

qry->Close();

//qry->SQL->Clear();

//qry->SQL->Add("INSERT INTO Quest (theme) VALUES ("+IntToStr((int)cbThemes->Items->Objects[cbThemes->ItemIndex])+")");

//qry->ExecSQL();

ADOCommand1->CommandText="INSERT INTO Quest (theme) VALUES ("+IntToStr((int)cbThemes->Items->Objects[cbThemes->ItemIndex])+")";

ADOCommand1->Execute();

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("SELECT LAST_INSERT_ID()");

qry->Open();

AnsiString QuestCode=qry->FieldByName("last_insert_id()")->AsString;

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select No from Quest where Theme="+IntToStr((int)cbThemes->Items->Objects[cbThemes->ItemIndex]));

qry->Open();

QuestNums->Clear();

for (qry->First(); !qry->Eof; qry->Next())

       QuestNums->Add(qry->FieldByName("No")->AsString);

qry->Close();

for (int i=0; i<QuestNums->Count; i++)

   if (QuestNums->Strings[i]==QuestCode) QuestNum=i;

LoadRec(QuestNums->Strings[QuestNum]);

StatusBar1->Panels->Items[0]->Text="Вопрос "+IntToStr(QuestNum+1)+" из "+IntToStr(QuestNums->Count);

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton7Click(TObject *Sender)

{

if (frmMoveTo==NULL) {

   frmMoveTo=new TForm(this);

   frmMoveTo->Top=frmQuest->Top; frmMoveTo->Left=frmQuest->Left+frmQuest->Width-300;

   frmMoveTo->Width=300; frmMoveTo->Height=frmQuest->Height;

   frmMoveTo->Caption="Перенести в...";

   lbThemes=new TListBox(frmMoveTo);

   lbThemes->Parent=frmMoveTo;

   lbThemes->Align=alClient;

   lbThemes->Items->Assign(cbThemes->Items);

   lbThemes->OnDblClick = SelectTheme;

}

frmMoveTo->ShowModal();

//delete lbThemes;

//delete frmMoveTo;

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::SelectTheme(TObject *Sender)

{

//frmQuest->StatusBar1->Panels->Items[1]->Text=(int)lbThemes->Items->Objects[lbThemes->ItemIndex];

frmMoveTo->Close();

qry->Close();

//qry->SQL->Clear();

//qry->SQL->Add("UPDATE Quest SET Theme="+IntToStr((int)lbThemes->Items->Objects[lbThemes->ItemIndex]));

//qry->SQL->Add("WHERE Quest.No="+QuestNums->Strings[QuestNum]);

ADOCommand1->CommandText="UPDATE Quest SET Theme="+IntToStr((int)lbThemes->Items->Objects[lbThemes->ItemIndex]);

ADOCommand1->CommandText=ADOCommand1->CommandText+" WHERE Quest.No="+QuestNums->Strings[QuestNum];

ADOCommand1->Execute();

//qry->ExecSQL();

int SaveQuestNum=QuestNum;

cbThemesCloseUp(this);

QuestNum=SaveQuestNum;

if (QuestNum>=QuestNums->Count) QuestNum--;

LoadRec(QuestNums->Strings[QuestNum]);

StatusBar1->Panels->Items[0]->Text="Вопрос "+IntToStr(QuestNum+1)+" из "+IntToStr(QuestNums->Count);

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton9Click(TObject *Sender)

{

frmScenar->ShowModal();

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton10Click(TObject *Sender)

{

if (Application->MessageBox("Вы действительно хотите удалить вопрос ?", "Внимание!", MB_OKCANCEL|MB_ICONWARNING)==IDOK){

 qry->Close();

/*  qry->SQL->Clear();

 qry->SQL->Add("DELETE FROM Quest ");

 qry->SQL->Add("WHERE No="+QuestNums->Strings[QuestNum]);

 qry->ExecSQL();

*/

ADOCommand1->CommandText="DELETE FROM Quest WHERE No="+QuestNums->Strings[QuestNum];

ADOCommand1->Execute();

 qry->Close();

 qry->SQL->Clear();

 qry->SQL->Add("Select No from Quest where Theme="+IntToStr((int)cbThemes->Items->Objects[cbThemes->ItemIndex]));

 qry->Open();

 QuestNums->Clear();

 for (qry->First(); !qry->Eof; qry->Next())

         QuestNums->Add(qry->FieldByName("No")->AsString);

 qry->Close();

 if (QuestNum>=QuestNums->Count) QuestNum=QuestNums->Count-1;

LoadRec(QuestNums->Strings[QuestNum]);

StatusBar1->Panels->Items[0]->Text="Вопрос "+IntToStr(QuestNum+1)+" из "+IntToStr(QuestNums->Count);

}

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::answersDblClick(TObject *Sender)

{

for (int row=0; row<answers->Lines->Count; row++)

   answers->Lines->Strings[row]=answers->Lines->Strings[row].Trim();

//правильный ответ может быть помечен звездочкой

int pos=0;

for (int i=0; i<answers->Lines->Count; i++)

  if (pos=answers->Lines->Strings[i].SubString(1,5).Pos("*")>0) {

        Correct=i+1;

        answers->Lines->Strings[i]=answers->Lines->Strings[i].SubString(pos+1,answers->Lines->Strings[i].Length()-pos+1).Trim();

        ShowAnswers();

}

for (int row=0; row<answers->Lines->Count; row++)

   answers->Lines->Strings[row]=answers->Lines->Strings[row].Trim();

AnsiString letters="1),2),3),4),5),6),1.,2.,3.,4.,5.,6.,a.),b.),c.),d.),e.),f.),A.),B.),C.),D.),E.),F.),a).,b).,c).,d).,e).,f).,A).,B).,C).,D).,E).,F).,a),b),c),d),e),f),A),B),C),D),E),F),a.,b.,c.,d.,e.,f.,A.,B.,C.,D.,E.,F.,а).,б).,в).,г).,д).,е).,А).,Б).,В).,Г).,Д).,Е).,а.),б.),в.),г.),д.),е.),А.),Б.),В.),Г.),Д.),Е.),а),б),в),г),д),е),А),Б),В),Г),Д),Е),а.,б.,в.,г.,д.,е.,А.,Б.,В.,Г.,Д.,Е.,";

TStringList *tmp=new TStringList();

tmp->CommaText=letters;

int len;

AnsiString str;

for (int row=0; row<answers->Lines->Count; row++) {

   for (int i=0;i<tmp->Count;i++) {

      len = tmp->Strings[i].Length();

      str=answers->Lines->Strings[row].SubString(1,len);

      if (str==tmp->Strings[i])

         answers->Lines->Strings[row]=answers->Lines->Strings[row].SubString(len+1,answers->Lines->Strings[row].Length()-len+1).Trim();

   }

}

delete tmp;

for (int row=0; row<answers->Lines->Count; row++)

   answers->Lines->Strings[row]=answers->Lines->Strings[row].Trim();

}

//---------------------------------------------------------------------------

void __fastcall TfrmQuest::ToolButton11Click(TObject *Sender)

{

Multi=ToolButton11->Down;

ShowAnswers();    

}

//---------------------------------------------------------------------------

TestQuestions.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

//---------------------------------------------------------------------------

USEFORM("QuestUnit.cpp", frmQuest);

USEFORM("Scenar.cpp", frmScenar);

//---------------------------------------------------------------------------

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

{

       try

       {

                Application->Initialize();

                Application->CreateForm(__classid(TfrmQuest), &frmQuest);

                Application->CreateForm(__classid(TfrmScenar), &frmScenar);

                Application->Run();

       }

       catch (Exception &exception)

       {

                Application->ShowException(&exception);

       }

       catch (...)

       {

                try

                {

                        throw Exception("");

                }

                catch (Exception &exception)

                {

                        Application->ShowException(&exception);

                }

       }

       return 0;

}

//---------------------------------------------------------------------------

Программа TestResults.exe (вывод на экран информации о тестировании и распечатка результата).

EntranceUnit.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "EntranceUnit.h"

#include "SelectGroup.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

//#pragma link "mySQLDbTables"

#pragma resource "*.dfm"

TEntranceForm *EntranceForm;

//---------------------------------------------------------------------------

__fastcall TEntranceForm::TEntranceForm(TComponent* Owner)

       : TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TEntranceForm::EntranceReportBeforePrint(

     TCustomQuickRep *Sender, bool &PrintReport)

{

Query1->Close();

Query1->Parameters->FindParam("StudentCode")->Value=StudentCode;

//Query1->ParamByName("StudentCode")->Value=StudentCode;

Query1->Open();

Query1->First();

right=wrong=0;

RightString=WrongString="";

for (Query1->First();!Query1->Eof;Query1->Next()){

  if (Query1->FieldByName("Answer")->AsInteger==Query1->FieldByName("Correct")->AsInteger) {

       right++;

       RightString+=IntToStr(Query1->FieldByName("Question")->AsInteger)+"-"+IntToStr(Query1->FieldByName("Answer")->AsInteger)+", ";

  }

  else {

       wrong++;

       WrongString+=IntToStr(Query1->FieldByName("Question")->AsInteger)+"-"+IntToStr(Query1->FieldByName("Answer")->AsInteger)+", ";

  }

}

if (right+wrong>0) {

  QRLabel20->Caption=IntToStr((int)((float)right/(right+wrong)*100))+"% правильных ответов";

  QRLabel22->Caption="/"+Query1->FieldByName("Surname")->AsString+" "+Query1->FieldByName("Name")->AsString+"/";

//   QRLabel24->Caption=GetMark((float)right/(right+wrong)*100);

  QRLabel24->Caption=IntToStr((int)((float)right/(right+wrong)*10+1))+" баллов";

  QRLabel26->Caption=Query1->FieldByName("ExList")->AsString;

}

else {

  QRLabel20->Caption="Нет данных";

  QRLabel22->Caption="Нет данных";

  QRLabel24->Caption="Нет данных";

}

//if (!NeedMark) {

//        QRLabel23->Enabled=false;

//        QRLabel24->Enabled=false;

//}

}

//---------------------------------------------------------------------------

void __fastcall TEntranceForm::QRBand1BeforePrint(TQRCustomBand *Sender,

     bool &PrintBand)

{

     QRLabel4->Caption=Query1->FieldByName("Surname")->AsString+" "+Query1->FieldByName("Name")->AsString;

     QRLabel7->Caption=Query1->FieldByName("Dept")->AsString;

//      if (!NeedDept) QRLabel7->Enabled=false;

     QRLabel8->Caption=Query1->FieldByName("Title")->AsString;

//      QRLabel18->Caption=FormatDateTime("dd.mm.yyyy",Query1->FieldByName("TestDate")->AsDateTime);

     QRLabel12->Caption=IntToStr(right+wrong);

     QRLabel13->Caption=IntToStr(right);

     QRLabel14->Caption=IntToStr(wrong);

     QRLabel15->Caption=RightString;

     QRLabel16->Caption=WrongString;

}

//---------------------------------------------------------------------------

AnsiString TEntranceForm::GetMark(int Percent)

{

int Mark3=0, Mark4=0, Mark5=0;

AnsiString tmp="неудовлетворительно";

Mark3=Query1->FieldByName("Mark3")->AsInteger;

Mark4=Query1->FieldByName("Mark4")->AsInteger;

Mark5=Query1->FieldByName("Mark5")->AsInteger;

if (Mark3==0||Mark4==0||Mark5==0) return "";

if (Percent>=Mark3) tmp="удовлетворительно";

if (Percent>=Mark4) tmp="хорошо";

if (Percent>=Mark5) tmp="отлично";

return tmp;

}

//---------------------------------------------------------------------------

void __fastcall TEntranceForm::EntranceReportAfterPrint(TObject *Sender)

{

Query1->Close();

}

//---------------------------------------------------------------------------

EntranceUnit.cpp

//---------------------------------------------------------------------------

#ifndef ExportUnitH

#define ExportUnitH

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <Controls.hpp>

#include <StdCtrls.hpp>

#include <Forms.hpp>

#include <ComCtrls.hpp>

//#include "mySQLDbTables.hpp"

#include <DB.hpp>

#include <ADODB.hpp>

//---------------------------------------------------------------------------

class TExport : public TForm

{

__published: // IDE-managed Components

       TProgressBar *ProgressBar1;

       //TmySQLQuery *Query1;

       //TmySQLQuery *Query2;

       TADOQuery *qryCheck;

       TADOQuery *qryInsert;

       TLabel *Label1;

       TListBox *ListBox1;

       TLabel *Label2;

  TADOQuery *Query1;

  TADOQuery *Query2;

private: // User declarations

         AnsiString FFilterString;

         float CalcPercent(int StudentCode);

         int GetMark(int Percent);

public:  // User declarations

      __fastcall TExport(TComponent* Owner);

      __property AnsiString FilterString={read=FFilterString, write=FFilterString};

      void Export(void);

};

//---------------------------------------------------------------------------

extern PACKAGE TExport *Export;

//---------------------------------------------------------------------------

#endif

SelectGroup.cpp

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "SelectGroup.h"

#include "EntranceUnit.h"

#include "ExportUnit.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

//#pragma link "CSPIN"

//#pragma link "ToolEdit"

//#pragma link "mySQLDbTables"

#pragma link "Word_2K_SRVR"

#pragma resource "*.dfm"

TfrmSelectGroup *frmSelectGroup;

//---------------------------------------------------------------------------

__fastcall TfrmSelectGroup::TfrmSelectGroup(TComponent* Owner)

       : TForm(Owner)

{

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::FormShow(TObject *Sender)

{

Connection1->Connected=true;

cbDepts->Items->AddObject("┬ёх", NULL);

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select DeptNo, Dept from Depts order by Dept");

qry->Open();

for (qry->First(); !qry->Eof; qry->Next())

  cbDepts->Items->AddObject(qry->FieldByName("Dept")->AsString, (TObject *)qry->FieldByName("DeptNo")->AsInteger);

cbDepts->ItemIndex=0;

cbCourse->ItemIndex=0;

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select No, Title from ScTitle order by Title");

qry->Open();

for (qry->First(); !qry->Eof; qry->Next())

  cbScTitles->Items->AddObject(qry->FieldByName("Title")->AsString, (TObject *)qry->FieldByName("No")->AsInteger);

cbScTitles->ItemIndex=0;

qry->Close();

dtDate1->Date=Now();

dtDate2->Date=Now();

SelectList();

busy=0;

char UserName[20];

DWORD UserNameSize=20;

LPDWORD p=&UserNameSize;

GetUserName(UserName,p);

if (AnsiString(UserName)!="vge") Button3->Enabled=false;

}

//---------------------------------------------------------------------------

void TfrmSelectGroup::SelectList()

{

qry->Close();

qry->SQL->Clear();

qry->SQL->Add("Select * from Students ");

qry->SQL->Add("WHERE Scenario="+IntToStr((int)cbScTitles->Items->Objects[cbScTitles->ItemIndex]));

qry->SQL->Add("AND TestDate BETWEEN '"+FormatDateTime("yyyy-mm-dd",dtDate1->Date)+" 00:00:00' AND '"+FormatDateTime("yyyy-mm-dd",dtDate2->Date)+" 23:59:59'" );

if (cbDepts->ItemIndex>0)

 qry->SQL->Add("AND Dept="+IntToStr((int)cbDepts->Items->Objects[cbDepts->ItemIndex]));

if (cbCourse->ItemIndex>0)

 qry->SQL->Add("AND Course="+cbCourse->Items->Strings[cbCourse->ItemIndex]);

qry->SQL->Add("Order by Name");

qry->Open();

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::dtDate1Change(TObject *Sender)

{

SelectList();

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::Button1Click(TObject *Sender)

{

try

 {

   W->Connect();

 }

catch (Exception &exception)

 {

   MessageDlg("┬хЁю Єэю эр ъюья№■ЄхЁх эх єёЄрэютыхэ Word", mtError, TMsgDlgButtons() << mbYes, 0);

   Abort;

 }

//W->Visible = True;

W->set_Visible(1);

W->Documents->Add();

W->Selection->Font->Bold = true; // ╙ёЄрэютшЄ№ цшЁэ√щ °ЁшЇЄ

W->Selection->Font->Size = 14;

W->Selection->ParagraphFormat->Alignment = wdAlignParagraphCenter;

W->Selection->TypeText(StringToOleStr("┬хфюьюёЄ№"));

W->Selection->Font->Bold = false; // ╙ёЄрэютшЄ№ цшЁэ√щ °ЁшЇЄ

W->Selection->Font->Size = 12;

W->Selection->EndKey((OleVariant)wdLine,(OleVariant)wdMove);

W->Selection->TypeParagraph();

W->Selection->TypeParagraph();

W->Selection->TypeParagraph();

W->Selection->ParagraphFormat->Alignment = wdAlignParagraphLeft;

W->ActiveDocument->Tables->Add(W->Selection->Range,1,4);

W->Selection->Tables->Item(1)->Columns->Item(1)->PreferredWidthType = wdPreferredWidthPoints;

W->Selection->Tables->Item(1)->Columns->Item(1)->PreferredWidth = W->CentimetersToPoints(2);

W->Selection->Tables->Item(1)->Columns->Item(2)->PreferredWidth = W->CentimetersToPoints(8);

W->Selection->Tables->Item(1)->Columns->Item(3)->PreferredWidth = W->CentimetersToPoints(3);

W->Selection->Tables->Item(1)->Columns->Item(4)->PreferredWidth = W->CentimetersToPoints(3);

W->Selection->HomeKey((OleVariant)wdRow);

W->Selection->TypeText(StringToOleStr("╣ я/я"));

W->Selection->MoveRight();

W->Selection->TypeText(StringToOleStr("╘рьшыш , ╚.╬."));

W->Selection->MoveRight();

W->Selection->TypeText(StringToOleStr("╧Ёртшы№э√ї"));

W->Selection->MoveRight();

W->Selection->TypeText(StringToOleStr("╬Ўхэър"));

W->Selection->HomeKey((OleVariant)wdLine,(OleVariant)wdMove);

int Row=1;

Query1->Close();

Query1->SQL->Clear();

Query1->SQL->Add("SELECT S.Code, concat(Surname,' ',Name) AS Name, S.Scenario, St.Mark3, St.Mark4, St.Mark5");

Query1->SQL->Add("FROM  Students S, ScTitle St ");

Query1->SQL->Add("WHERE S.Scenario=St.No ");

Query1->SQL->Add("AND S.Code IN ("+PrepareFilterString()+") ");

Query1->SQL->Add("ORDER BY Name");

Query1->Open();

Query2->Close();

Query2->SQL->Clear();

Query2->SQL->Add("SELECT Student, Answer, Correct");

Query2->SQL->Add("FROM Exam");

Query2->SQL->Add("WHERE Student IN ("+PrepareFilterString()+")");

Query2->Open();

int Percent;

for(Query1->First();!Query1->Eof;Query1->Next()){

       W->Selection->InsertRowsBelow((OleVariant)1);

       W->Selection->HomeKey((OleVariant)wdLine,(OleVariant)wdMove);

       W->Selection->TypeText(StringToOleStr(Row++));

       W->Selection->MoveRight();

       W->Selection->HomeKey((OleVariant)wdLine,(OleVariant)wdMove);

       W->Selection->TypeText(StringToOleStr(Query1->FieldByName("Name")->AsString));

       W->Selection->MoveRight();

       Percent=CalcPercent(Query1->FieldByName("Code")->AsInteger);

       W->Selection->TypeText(StringToOleStr(IntToStr(Percent)+"%"));

       W->Selection->MoveRight();

//       W->Selection->TypeText(StringToOleStr(GetMark(Percent)));

}

Query1->Close();

Query2->Close();

//W=Unassigned;

W->Disconnect();

}

//---------------------------------------------------------------------------

AnsiString TfrmSelectGroup::PrepareFilterString()

{

TStringList* str=new TStringList();

for (qry->First();!qry->Eof;qry->Next())

 str->Add(qry->FieldByName("Code")->AsString);

return str->CommaText;

}

//-----------------------------------------------------------------------

AnsiString TfrmSelectGroup::GetMark(int Percent)

{

int Mark3=0, Mark4=0, Mark5=0;

AnsiString tmp="эхєфюты.";

Mark3=Query1->FieldByName("Mark3")->AsInteger;

Mark4=Query1->FieldByName("Mark4")->AsInteger;

Mark5=Query1->FieldByName("Mark5")->AsInteger;

if (Mark3==0||Mark4==0||Mark5==0) return "";

if (Percent>=Mark3) tmp="єфюты.";

if (Percent>=Mark4) tmp="їюЁю°ю";

if (Percent>=Mark5) tmp="юЄышўэю";

return tmp;

}

//------------------------------------------------------------------------

float TfrmSelectGroup::CalcPercent(int StudentCode)

{

int right=0,total=0;

for (Query2->First();!Query2->Eof;Query2->Next()){

 if (Query2->FieldByName("Student")->AsInteger==StudentCode) {

   total++;

   if (Query2->FieldByName("Answer")->AsInteger==Query2->FieldByName("Correct")->AsInteger)

        right++;

 }

}

if (total>0)

       return 100.0*(float)right/total;

else

       return 0;

}

void __fastcall TfrmSelectGroup::Button2Click(TObject *Sender)

{

EntranceForm->StudentCode=qry->FieldByName("Code")->AsInteger;

EntranceForm->EntranceReport->Preview();

       

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::NMMSGServ1MSG(TComponent *Sender,

     const AnsiString sFrom, const AnsiString sMsg)

{

ListBox1->Items->Add(sMsg);

ListBox2->Items->Add(sMsg);

ListBox2->ItemIndex=ListBox2->Items->Count-1;

ListBox2->TopIndex=ListBox2->Items->Count-1;

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::Timer1Timer(TObject *Sender)

{

  if (ListBox1->Items->Count>0&&!busy)

  {

       busy=1;

       EntranceForm->StudentCode=StrToInt(ListBox1->Items->Strings[0]);

       EntranceForm->EntranceReport->Print();

       Timer2->Enabled=true;

       ListBox1->Items->Delete(0);

  }

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::Timer2Timer(TObject *Sender)

{

       busy=0;

       Timer2->Enabled=false;

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::Button3Click(TObject *Sender)

{

Export->FilterString=PrepareFilterString();

Export->Export();

}

//---------------------------------------------------------------------------

void __fastcall TfrmSelectGroup::NMMSGServ1Status(TComponent *Sender,

     AnsiString Status)

{

ListBox2->Items->Add(Status);

}

//---------------------------------------------------------------------------

SelectGroup.dfm

object frmSelectGroup: TfrmSelectGroup

 Left = 226

 Top = 104

 Width = 468

 Height = 601

 Caption = #1054#1090#1073#1086#1088' '#1089#1090#1091#1076#1077#1085#1090#1086#1074

 Color = clBtnFace

 Font.Charset = DEFAULT_CHARSET

 Font.Color = clWindowText

 Font.Height = -11

 Font.Name = 'MS Sans Serif'

 Font.Style = []

 OldCreateOrder = False

 OnShow = FormShow

 PixelsPerInch = 96

 TextHeight = 13

 object Splitter1: TSplitter

   Left = 343

   Top = 0

   Width = 3

   Height = 574

   Cursor = crHSplit

   Align = alRight

 end

 object Panel3: TPanel

   Left = 0

   Top = 0

   Width = 343

   Height = 574

   Align = alClient

   TabOrder = 1

   object GroupBox1: TGroupBox

     Left = 1

     Top = 1

     Width = 341

     Height = 129

     Align = alTop

     Caption = #1059#1089#1083#1086#1074#1080#1103' '#1086#1090#1073#1086#1088#1072

     TabOrder = 0

     object Label1: TLabel

       Left = 8

       Top = 24

       Width = 56

       Height = 13

       Caption = #1060#1072#1082#1091#1083#1100#1090#1077#1090

     end

     object Label2: TLabel

       Left = 232

       Top = 24

       Width = 24

       Height = 13

       Caption = #1050#1091#1088#1089

     end

     object Label3: TLabel

       Left = 8

       Top = 56

       Width = 111

       Height = 13

       Caption = #1044#1072#1090#1072' '#1090#1077#1089#1090#1080#1088#1086#1074#1072#1085#1080#1103'  '#1089

     end

     object Label4: TLabel

       Left = 216

       Top = 56

       Width = 12

       Height = 13

       Caption = #1087#1086

     end

     object Label5: TLabel

       Left = 8

       Top = 96

       Width = 92

       Height = 13

       Caption = #1042#1080#1076' '#1090#1077#1089#1090#1080#1088#1086#1074#1072#1085#1080#1103

     end

     object cbDepts: TComboBox

       Left = 72

       Top = 16

       Width = 153

       Height = 21

       DropDownCount = 11

       ItemHeight = 13

       TabOrder = 0

       Text = 'cbDepts'

       OnCloseUp = dtDate1Change

     end

     object cbCourse: TComboBox

       Left = 264

       Top = 16

       Width = 57

       Height = 21

       ItemHeight = 13

       TabOrder = 1

       Text = 'cbCourse'

       OnCloseUp = dtDate1Change

       Items.Strings = (

         #1042#1089#1077

         '1'

         '2'

         '3'

         '4'

         '5')

     end

     object cbScTitles: TComboBox

       Left = 112

       Top = 88

       Width = 209

       Height = 21

       DropDownCount = 35

       ItemHeight = 13

       TabOrder = 2

       Text = 'cbScTitles'

       OnCloseUp = dtDate1Change

     end

     object dtDate1: TDateTimePicker

       Left = 128

       Top = 48

       Width = 81

       Height = 21

       CalAlignment = dtaLeft

       Date = 37728.7607546296

       Time = 37728.7607546296

       DateFormat = dfShort

       DateMode = dmComboBox

       Kind = dtkDate

       ParseInput = False

       TabOrder = 3

       OnChange = dtDate1Change

     end

     object dtDate2: TDateTimePicker

       Left = 240

       Top = 48

       Width = 81

       Height = 21

       CalAlignment = dtaLeft

       Date = 37728.7610600463

       Time = 37728.7610600463

       DateFormat = dfShort

       DateMode = dmComboBox

       Kind = dtkDate

       ParseInput = False

       TabOrder = 4

       OnChange = dtDate1Change

     end

   end

   object Panel1: TPanel

     Left = 1

     Top = 545

     Width = 341

     Height = 28

     Align = alBottom

     TabOrder = 1

     object DBNavigator1: TDBNavigator

       Left = 3

       Top = 1

       Width = 240

       Height = 25

       DataSource = DataSource1

       TabOrder = 0

     end

   end

   object DBGrid1: TDBGrid

     Left = 1

     Top = 130

     Width = 341

     Height = 415

     Align = alClient

     Ctl3D = False

     DataSource = DataSource1

     Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgConfirmDelete, dgCancelOnExit]

     ParentCtl3D = False

     TabOrder = 2

     TitleFont.Charset = DEFAULT_CHARSET

     TitleFont.Color = clWindowText

     TitleFont.Height = -11

     TitleFont.Name = 'MS Sans Serif'

     TitleFont.Style = []

   end

 end

 object Panel2: TPanel

   Left = 346

   Top = 0

   Width = 114

   Height = 574

   Align = alRight

   TabOrder = 0

   DesignSize = (

     114

     574)

   object Button1: TButton

     Left = 8

     Top = 8

     Width = 97

     Height = 25

     Anchors = [akTop, akRight]

     Caption = #1042#1077#1076#1086#1084#1086#1089#1090#1100

     TabOrder = 0

     OnClick = Button1Click

   end

   object Button2: TButton

     Left = 8

     Top = 40

     Width = 97

     Height = 25

     Anchors = [akTop, akRight]

     Caption = #1069#1082#1079#1072#1084'. '#1083#1080#1089#1090

     TabOrder = 1

     OnClick = Button2Click

   end

   object ListBox1: TListBox

     Left = 1

     Top = 72

     Width = 112

     Height = 57

     Anchors = [akLeft, akTop, akRight]

     ItemHeight = 13

     TabOrder = 2

   end

   object ListBox2: TListBox

     Left = 1

     Top = 130

     Width = 111

     Height = 414

     Anchors = [akLeft, akTop, akRight, akBottom]

     ItemHeight = 13

     TabOrder = 3

   end

   object Button3: TButton

     Left = 13

     Top = 546

     Width = 97

     Height = 25

     Anchors = [akRight, akBottom]

     Caption = #1069#1082#1089#1087#1086#1088#1090

     TabOrder = 4

     OnClick = Button3Click

   end

 end

 object DataSource1: TDataSource

   DataSet = qry

   Left = 8

   Top = 120

 end

 object W: TWordApplication

   AutoConnect = True

   ConnectKind = ckRunningOrNew

   AutoQuit = True

   Left = 488

   Top = 24

 end

 object NMMSGServ1: TNMMSGServ

   Port = 6711

   ReportLevel = 8

   OnStatus = NMMSGServ1Status

   OnMSG = NMMSGServ1MSG

   Left = 344

   Top = 104

 end

 object Timer1: TTimer

   Interval = 5000

   OnTimer = Timer1Timer

   Left = 376

   Top = 104

 end

 object Timer2: TTimer

   Enabled = False

   Interval = 20000

   OnTimer = Timer2Timer

   Left = 408

   Top = 104

 end

 object Connection1: TADOConnection

   ConnectionString =

     'Provider=MSDASQL.1;Persist Security Info=False;Data Source=Admin' +

     'Con'

   LoginPrompt = False

   Provider = 'MSDASQL.1'

   Left = 32

   Top = 72

 end

 object qry: TADOQuery

   Connection = Connection1

   CursorType = ctDynamic

   Parameters = <>

   Left = 64

   Top = 120

 end

 object Query1: TADOQuery

   Connection = Connection1

   Parameters = <>

   Left = 120

   Top = 104

 end

 object Query2: TADOQuery

   Connection = Connection1

   Parameters = <>

   Left = 176

   Top = 104

 end

end

SelectGroup.h

//---------------------------------------------------------------------------

#ifndef SelectGroupH

#define SelectGroupH

//---------------------------------------------------------------------------

#include <Classes.hpp>

#include <Controls.hpp>

#include <StdCtrls.hpp>

#include <Forms.hpp>

//#include "CSPIN.h"

//#include "ToolEdit.hpp"

#include <Mask.hpp>

//#include "mySQLDbTables.hpp"

//#include <DB.hpp>

#include <DBGrids.hpp>

#include <Grids.hpp>

#include <ComCtrls.hpp>

#include "Word_2K_SRVR.h"

//#include <OleServer.hpp>

#include <NMMSG.hpp>

#include <Psock.hpp>

#include <ExtCtrls.hpp>

#include <ADODB.hpp>

#include <DB.hpp>

#include <OleServer.hpp>

#include <DBCtrls.hpp>

//---------------------------------------------------------------------------

class TfrmSelectGroup : public TForm

{

__published: // IDE-managed Components

       TGroupBox *GroupBox1;

       TLabel *Label1;

       TComboBox *cbDepts;

       TLabel *Label2;

       TComboBox *cbCourse;

       TLabel *Label3;

       TLabel *Label4;

       TLabel *Label5;

       TComboBox *cbScTitles;

       //TmySQLDatabase *Connection1;//-----------

      // TmySQLQuery *qry; //-----------

       TDataSource *DataSource1;

       TDBGrid *DBGrid1;

       TDateTimePicker *dtDate1;

       TDateTimePicker *dtDate2;

       TButton *Button1;

       TWordApplication *W;

       //TmySQLQuery *Query1; //----------

      // TmySQLQuery *Query2; //----------

       TButton *Button2;

       TNMMSGServ *NMMSGServ1;

       TListBox *ListBox1;

       TTimer *Timer1;

       TTimer *Timer2;

       TButton *Button3;

       TListBox *ListBox2;

  TADOConnection *Connection1;

  TADOQuery *qry;

  TADOQuery *Query1;

  TADOQuery *Query2;

  TDBNavigator *DBNavigator1;

  TPanel *Panel1;

  TPanel *Panel2;

  TPanel *Panel3;

  TSplitter *Splitter1;

       void __fastcall FormShow(TObject *Sender);

       void __fastcall dtDate1Change(TObject *Sender);

       void __fastcall Button1Click(TObject *Sender);

       void __fastcall Button2Click(TObject *Sender);

       void __fastcall NMMSGServ1MSG(TComponent *Sender,

         const AnsiString sFrom, const AnsiString sMsg);

       void __fastcall Timer1Timer(TObject *Sender);

       void __fastcall Timer2Timer(TObject *Sender);

       void __fastcall Button3Click(TObject *Sender);

       void __fastcall NMMSGServ1Status(TComponent *Sender,

         AnsiString Status);

private: // User declarations

       int busy;

public:  // User declarations

       __fastcall TfrmSelectGroup(TComponent* Owner);

       void SelectList();

       AnsiString PrepareFilterString();

       AnsiString GetMark(int Percent);

       float CalcPercent(int StudentCode);

};

//---------------------------------------------------------------------------

extern PACKAGE TfrmSelectGroup *frmSelectGroup;

//---------------------------------------------------------------------------

#endif

TestResults.bpr

//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

//---------------------------------------------------------------------------

USEFORM("SelectGroup.cpp", frmSelectGroup);

USEFORM("EntranceUnit.cpp", EntranceForm);

USEFORM("ExportUnit.cpp", Export);

//---------------------------------------------------------------------------

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

{

       try

       {

                Application->Initialize();

                Application->CreateForm(__classid(TfrmSelectGroup), &frmSelectGroup);

                Application->CreateForm(__classid(TEntranceForm), &EntranceForm);

                Application->CreateForm(__classid(TExport), &Export);

                Application->Run();

       }

       catch (Exception &exception)

       {

                Application->ShowException(&exception);

       }

       catch (...)

       {

                try

                {

                        throw Exception("");

                }

                catch (Exception &exception)

                {

                        Application->ShowException(&exception);

                }

       }

       return 0;

}

//---------------------------------------------------------------------------

Приложение 2

                                      Установка VPN Linux-сервера.


          Требования: создать линукс-сервер, который должен работать как vpn (
virtual, privat, network), MySQL сервер для безопасных соединений с базой данных.


          В качестве дистрибутива выбран ASPLinux 9.2

          

                                      Этап 1. Установка сервера.


          Делим жесткий диск на два раздела. Один - 256Мб, под раздел swap, все оставшееся - под /. Выбираем серверную установку и выбор пакетов. Отключаем все ненужное, расставляем адреса для сетевых карт, устанавливаем загрузчик. В конце этого этапа мы должны получить установленный Linux сервер, который смотрит одной сетевой картой в интернет, другой - в локальную сеть.

                                    

                                      Этап 2. Настройка системы.


        Первым делом мы правим файл /etc/aliases, чтобы вся почта на root присылалась на учетную запись
root. Затем, чтобы изменения вступили в силу, запускаю newaliases.
       Затем правим файл /etc/hosts, и файл /etc/sysconfig/network на соответствие реальным значениям.
       Затем
 правим файл /etc/resolv.conf

[root@vpn root]# cat /etc/resolv.conf
nameserver 10.0.0.1
nameserver 127.0.0.1
       

       Первый сервер в списке - это DNS сервер в локальной сети, который по умолчанию разбирается со всеми именами. Вторая строчка - это локальный DNS, поднятый в кэширующем режиме. Это нужно для того, что хоть DNS трафик и не большой, но лишнюю информацию передавать не желательно. Таким образом, все DNS запросы собираются в одном месте. На тот же случай, если по какой-то причине не будет связи, будет использоваться локальный DNS.
       Затем мы поправили файл /etc/named.conf на предмет того, что бы bind не принимал запросы со всех запущенных интерфейсов. Это делается просто: в секции options просто добавляем одну строчку:

listen-on { 127.0.0.1; };

Теперь при перезагрузке bind будет сидеть только на lo.
       Все. Можно для полной уверенности перезагрузить сервер, чтобы поглядеть, что все, что надо, запускается так, как надо и в нужной последовательности.
В нашем случае все загрузилось хорошо и замечательно.
Смотрим:
[
root@vpn root]# netstat -npl|grep LIST
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 688/named
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 709/sshd
tcp 0 0 127.0.0.1:953 0.0.0.0:* LISTEN 688/named 

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

Этап 3. Установка VPN сервера.


      Берем и ставим pptpd-1.1.2, входящий в комплект ASPLinux. Проверяем по ntsysv, что сервис pptpd запускается по умолчанию.
      Внимательно смотрим на файл /etc/pptpd.conf и редактируем его.
Проверяем следующие строки

option /etc/ppp/options.pptpd

Здесь будут храниться настройки PPP для PPTP

localip 10.0.0.10
remoteip 10.0.0.20-80

Это мы указываем диапазон адресов, который будет назначаться VPN клиентам.
Больше ничего не меняем.
Теперь возьмем файл /etc/ppp/options.pptpd

lock
mtu 1490
mru 1490
ipcp-accept-local
ipcp-accept-remote
lcp-echo-failure 3
lcp-echo-interval 5
deflate 0
auth
+chap
-pap
proxyarp
ms-dns 10.0.0.1
+chapms
+chapms-v2
nobsdcomp
nodeflate
nodefaultroute
+mppe-40
+mppe-128
+mppe-stateless

Строчки mppe-40 и mppe-128 включают вообще какое-либо шифрование (mppe-40) и разрешают 128 битное шифрование (mppe-128).
       Ну вот теперь можно и запустить pptpd: /etc/init.d/pptpd start
Все должно запуститься без проблем. Если что-то не то, то включаем debug в обеих файлах и разбираем ошибки. У меня в  проекте практически все сразу с первого раза заработало.
       Пишем в файле /etc/ppp/chap-secrets логины и пароли в виде логин * пароль *_или_конкретный_ip_для_клиента.
       Тут необходимо сделать маленькое лирическое отступление: как VPN клиенты будут выходить в интернет? Потому, что после поднятия VPN реальное соединение с интернет "замаскируется" и просто так посмотреть страничку уже не получится ...
Здесь мы уходим в область настройки файрволла и роутинга.

      Добавляем единицу в файл, отвечающий за файрволл.
echo 1 > /proc/sys/net/ipv4/ip_forward
      Эта строчка отвечает за включение пересылки пакетов. Без нее клиент получит адрес но не сможет выйти в интернет.

Случай 1. Клиент выходит наружу через VPN сервер. Тогда даем строчку:
iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -j MASQUERADE
(0.0 - это внутренняя подсеть). Этой командой мы включаем маскарадинг ip адресов. Подробнее - читайте iptables-tutorial и другую документацию

Случай 2. VPN сервер находится отдельно от главного роутера.
Здесь мы воспользуемся так называемым source based policy routing.
echo 200
server >> /etc/iproute2/rt_tables
Добавляем таблицу роутинга

ip rule add from 10.0.0.10 table
server

добавляем ip адрес в таблицу роутинга (10.0.0.10 назначаем для нас в chap-secrets)

ip route add default via 10.0.0.1 dev eth1 table
server

Говорим, чтобы все, что попадает в таблицу роутинга
server, отправлялось не по обычному маршруту, а на 10.0.0.1 (роутинг-сервер в нашей сети).

ip route flush cache

Принимаем изменения в силу. За разъяснениями нужно обращаться на
http://www.linuxguruz.org/iptables/howto/2.4routing-4.html.


После этого, подключаемся в
VPN серверу через windows 2000/XP.

Приложение 3

Скрипт для создания базы данных test СУБД Mysql.

# Host: 127.0.0.1

# Database: test

# Table: 'Depts'

#

CREATE TABLE `depts` (

 `DeptNo` int(11) NOT NULL auto_increment,

 `Dept` varchar(30) default NULL,

 `LoginLetters` char(3) default NULL,

 PRIMARY KEY  (`DeptNo`)

) TYPE=MyISAM;

# Host: 127.0.0.1

# Database: test

# Table: 'Exam'

#

CREATE TABLE `exam` (

 `Student` int(11) default NULL,

 `Question` int(11) default NULL,

 `Answer` smallint(6) default NULL,

 `Correct` smallint(6) default NULL

) TYPE=MyISAM;

# Host: 127.0.0.1

# Database: test

# Table: 'Quest'

#

CREATE TABLE `quest` (

 `No` int(11) NOT NULL auto_increment,

 `PrintNo` int(11) default '0',

 `Theme` int(11) default '0',

 `Quest` mediumtext,

 `Ans` text,

 `Right` smallint(6) default '0',

 PRIMARY KEY  (`No`)

) TYPE=MyISAM;

# Host: 127.0.0.1

# Database: test

# Table: 'ScTitle'

#

CREATE TABLE `sctitle` (

 `No` int(11) NOT NULL auto_increment,

 `Title` varchar(50) default NULL,

 `Dept` int(11) default NULL,

 `Teacher` varchar(30) default NULL,

 `Active` tinyint(1) default NULL,

 `Time` time default NULL,

 `Mark3` int(11) default NULL,

 `Mark4` int(11) default NULL,

 `Mark5` int(11) default NULL,

 `ReceptExam` int(11) default NULL,

 PRIMARY KEY  (`No`)

) TYPE=MyISAM;

# Host: 127.0.0.1

# Database: test

# Table: 'Scenar'

#

CREATE TABLE `scenar` (

 `ScNo` int(11) default NULL,

 `Theme` int(11) default NULL,

 `Available` int(11) default NULL,

 `Needed` int(11) default NULL

) TYPE=MyISAM;

# Host: 127.0.0.1

# Database: test

# Table: 'Students'

#

CREATE TABLE `students` (

 `Code` int(11) NOT NULL auto_increment,

 `Surname` varchar(30) default NULL,

 `Name` varchar(40) default NULL,

 `Login` varchar(10) default NULL,

 `Dept` int(11) default NULL,

 `Course` smallint(6) default NULL,

 `Group` smallint(6) default NULL,

 `TestDate` datetime default '0000-00-00 00:00:00',

 `Scenario` int(11) default NULL,

 PRIMARY KEY  (`Code`)

) TYPE=MyISAM;

# Host: 127.0.0.1

# Database: test

# Table: 'Themes'

#

CREATE TABLE `themes` (

 `No` int(11) NOT NULL auto_increment,

 `Theme` varchar(30) default NULL,

 PRIMARY KEY  (`No`)

) TYPE=MyISAM;

 

 


 

А также другие работы, которые могут Вас заинтересовать

69360. Автоматизація внутрібанківських розрахункових 105 KB
  Обслуговування клієнтів банку організовується у відповідності з його організаційною структурою. Депозитний відділ. Його основною задачею є залучення засобів у банк, а у функції входять: облік депозитних засобів банку по їхній терміновості й окремих депонентах...
69361. Інформаційні системи фондового ринку 104 KB
  Учасниками фондового ринку є: емітенти цінних паперів юридичні й у деяких випадках передбачених законодавством фізичні особи що від свого імені випускають цінні папери і зобов’язуються виконувати обов’язки що випливають з умов їхнього випуску.
69362. Міжнародна електронна мережа та система електронних платежів НБУ 225.5 KB
  Для забезпечення організації і прискорення розрахунків на міжнародному рівні в НБУ застосований Центр міждержавних розрахунків. Згідно затвердженого Положення про Центр міждержавних розрахунків його основними завданнями є: прискорення міжнародних...
69363. Характеристика автоматизованих інформаційних систем 70.5 KB
  Існують інформаційна промисловість і національні інформаційні ресурси відбувається перехід від індустріальної економіки до економіки що ґрунтується на інформації. Під інформаційною технологією розуміють комплекс методів і процедур які реалізують функції збору...
69364. Економічна інформація та засоби її формалізації 70.5 KB
  Характеристика засобів формалізованого опису економічної інформації До числа основних засобів формалізованого опису елементів економічної інформації в ІС відносяться класифікація і кодування означених номенклатур по яким здійснюється упорядкування пошук та логічна обробка...
69365. Характеристика технологічних операцій та технологічних процесів оброблення економічної інформації 56.5 KB
  Методи контролю достовірності набору інформації. Головне для розподілу дій на окремі операції це їх логічне завершення яке веде до конкретного результату: нового носія інформації нового масиву файлу змінах у значеннях окремих атрибутів і т.
69366. Логика, краткий конспект лекций 863 KB
  Мышление неразрывно связано с познанием, которое представляет собой процесс приобретения человеком в ходе общественно-исторической практики истинных знаний об объективном мире. Познание как отражение действительности - весьма сложное многогранное явление.
69367. Безопасность жизнедеятельности, краткий курс лекций 1.15 MB
  Изучение дисциплины «Безопасность жизнедеятельности» обусловлено наличием непрерывного воздействия на человека внешних потоков веществ, энергии и информации, которые часто превышают допустимые уровни. Каждый специалист должен уметь идентифицировать опасные и вредные факторы, знать нормативную базу и возможные средства защиты.
69368. Учение о наказании, курс лекций 1.01 MB
  В работе изложены основные определения и категории Общей части уголовного права, относящиеся к понятиям наказания и системы наказаний, целям наказания, отдельным видам наказаний, видам исправительных учреждений для лишенных свободы, общим началам и отдельным правилам назначения наказания